Commit ba32b44b authored by Mikhail Karpenko's avatar Mikhail Karpenko

Merge AHCI driver from 'origin/master-initial'

parents 13453273 c9b13370
This diff is collapsed.
/** @file ahci_elphel_ext.h
*
* @brief Elphel AHCI SATA platform driver for Elphel393 camera. This module provides
* additional functions which allows to use a part of a disk (or entire disk) as a
* raw circular buffer.
*
* @copyright Copyright (C) 2016 Elphel, Inc
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <uapi/elphel/ahci_cmd.h>
#include "../elphel/circbuf.h"
#ifndef _AHCI_ELPHEL_EXT
#define _AHCI_ELPHEL_EXT
#define IRQ_SIMPLE (1 << 0) ///< Flag indicating that IRQ corresponds to internal command and should not be
///< processed in ahci_handle_port_interrupt
#define DISK_BUSY (1 << 1) ///< Flag indicating that disk is currently busy. Access to this flag should be protected by
///< spin locks to prevent race conditions
#define PROC_CMD (1 << 2) ///< Processing driver's internal command is in progress
#define LAST_BLOCK (1 << 3) ///< Flag indicating that the remaining chunk of data will be recorded
#define DELAYED_FINISH (1 << 4) ///< Flag indicating that recording should be stopped right after the last chunk of data is written
#define LOCK_TAIL (1 << 5) ///< Lock current command slot until all data buffers are assigned and the frame is aligned
#define CMD_FIS_LEN 5 ///< The length of a command FIS in double words
#define ADDR_MASK_28_BIT ((u64)0xfffffff)///< This is used to get 28-bit address from 64-bit value
#define MAX_PRDT_LEN 0x3fffff ///< A maximum of length of 4MB may exist for PRDT entry
#define MAX_DATA_CHUNKS 9 ///< An array or JPEG frame chunks contains pointers to JPEG leading marker,
///< JPEG header, Exif data if present, stuffing bytes chunk which aligns
///< the frame size to disk sector boundary, JPEG data which
///< can be split into two chunks, align buffers, JPEG
///< trailing marker, and pointer to a buffer containing the remainder of a
///< frame. Nine chunks of data in total.
#define DEFAULT_PORT_NUM 0 ///< Default port number
#define ALIGNMENT_SIZE 32 ///< Align buffers length to this amount of bytes
#define MAX_SGL_LEN 168 ///< Maximum number of entries in PRDT table. HW max is 64k.
///< Set this value the same as AHCI_MAX_SG in ahci.h
#define MAX_CMD_SLOTS 4 ///< Maximum number of frames which will be processed at the same time
#define MAX_LBA_COUNT 0xff ///< Maximum number of sectors for READ DMA or WRITE DMA commands
#define MAX_LBA_COUNT_EXT 0xffff ///< Maximum number of sectors for READ DMA EXT or WRITE_DMA EXT commands
#define PHY_BLOCK_SIZE 512 ///< Physical disk block size
#define JPEG_MARKER_LEN 2 ///< The size in bytes of JPEG marker
#define JPEG_SIZE_LEN 2 ///< The size in bytes of JPEG marker length field
#define INCLUDE_REM 1 ///< Include REM buffer to total size calculation
#define EXCLUDE_REM 0 ///< Exclude REM buffer from total size calculation
/** This structure holds raw device buffer pointers */
struct drv_pointers {
uint64_t lba_start; ///< raw buffer starting LBA
uint64_t lba_end; ///< raw buffer ending LBA
uint64_t lba_write; ///< current write pointer inside raw buffer
uint16_t wr_count; ///< the number of LBA to write next time
};
/** Container structure for frame buffers */
struct frame_buffers {
struct fvec exif_buff; ///< Exif buffer
struct fvec jpheader_buff; ///< JPEG header buffer
struct fvec trailer_buff; ///< buffer for trailing marker
struct fvec common_buff; ///< common buffer where other parts are combined
struct fvec rem_buff; ///< remainder from previous frame
};
/** Symbolic names for slots in buffer pointers. Buffer alignment function relies on the order of these names, so
* new names can be added but the overall order should not be changed */
enum {
CHUNK_LEADER, ///< pointer to JPEG leading marker
CHUNK_EXIF, ///< pointer to Exif buffer
CHUNK_HEADER, ///< pointer to JPEG header data excluding leading marker
CHUNK_COMMON, ///< pointer to common buffer
CHUNK_DATA_0, ///< pointer to JPEG data
CHUNK_DATA_1, ///< pointer to the second half of JPEG data if a frame crosses circbuf boundary
CHUNK_TRAILER, ///< pointer to JPEG trailing marker
CHUNK_ALIGN, ///< pointer to buffer where the second part of JPEG data should be aligned
CHUNK_REM ///< pointer to buffer containing the remainder of current frame. It will be recorded during next transaction
};
/** AHCI driver private structure */
struct elphel_ahci_priv {
u32 clb_offs; ///< CLB offset, received from device tree
u32 fb_offs; ///< FB offset, received from device tree
u32 base_addr; ///< controller base address
u32 flags; ///< flags indicating current state of the driver. Access to #DISK_BUSY flags is protected with
///< a spin lock
int curr_cmd; ///< current ATA command
size_t max_data_sz; ///< maximum data size (in bytes) which can be processed with current ATA command
struct drv_pointers lba_ptr; ///< disk buffer pointers
struct frame_buffers fbuffs[MAX_CMD_SLOTS]; ///< a set of buffers for each command
struct fvec data_chunks[MAX_CMD_SLOTS][MAX_DATA_CHUNKS];///< a set of vectors pointing to data buffers for each command
struct fvec sgl[MAX_SGL_LEN]; ///< an array of data buffers mapped for next transaction
int sg_elems; ///< the number of S/G vectors mapped for next transaction in @e sgl array
int curr_data_chunk; ///< index of a data chunk used during last transaction
size_t curr_data_offset; ///< offset of the last byte in a data chunk pointed to by @e curr_data_chunk
size_t head_ptr; ///< pointer to command slot which will be written next
size_t tail_ptr; ///< pointer to next free command slot
spinlock_t flags_lock; ///< controls access to #DISK_BUSY flag in @e flags variable.
///< This flag controls access to disk write operations either from
///< the the driver itself or from the system. Mutex is not used
///< because this flag is accessed from interrupt context
struct tasklet_struct bh; ///< command processing tasklet
struct device *dev; ///< pointer to parent device structure
};
#endif /* _AHCI_ELPHEL_EXT */
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/uio.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <uapi/elphel/x393_devices.h> #include <uapi/elphel/x393_devices.h>
#include <uapi/elphel/c313a.h> #include <uapi/elphel/c313a.h>
...@@ -108,6 +109,36 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev) ...@@ -108,6 +109,36 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev)
return 0; return 0;
} }
ssize_t circbuf_get_ptr(int sensor_port, size_t offset, size_t len, struct fvec *vect_0, struct fvec *vect_1)
{
int ret = 1;
if (offset > CCAM_DMA_SIZE || sensor_port >= SENSOR_PORTS)
return -EINVAL;
if (offset + len < CCAM_DMA_SIZE) {
// the image is not split
vect_0->iov_base = &circbuf_priv[sensor_port].buf_ptr[BYTE2DW(offset)];
vect_0->iov_dma = circbuf_priv[sensor_port].phys_addr + offset;
vect_0->iov_len = len;
vect_1->iov_base = NULL;
vect_1->iov_len = 0;
vect_1->iov_dma = 0;
} else {
// the image is split into two segments
vect_0->iov_base = &circbuf_priv[sensor_port].buf_ptr[BYTE2DW(offset)];
vect_0->iov_dma = circbuf_priv[sensor_port].phys_addr + offset;
vect_0->iov_len = CCAM_DMA_SIZE - offset;
vect_1->iov_base = circbuf_priv[sensor_port].buf_ptr;
vect_1->iov_dma = circbuf_priv[sensor_port].phys_addr;
vect_1->iov_len = len - vect_0->iov_len;
ret = 2;
}
return ret;
}
EXPORT_SYMBOL_GPL(circbuf_get_ptr);
/** /**
* @brief Process circular buffer file opening and define further action in accordance * @brief Process circular buffer file opening and define further action in accordance
* with minor file number. * with minor file number.
......
...@@ -24,6 +24,12 @@ ...@@ -24,6 +24,12 @@
#include <linux/poll.h> #include <linux/poll.h>
struct fvec {
void *iov_base; ///< pointer to allocated buffer
size_t iov_len; ///< the size (in bytes) of allocated buffer; set after allocation and is not modified during buffer lifetime
dma_addr_t iov_dma; ///< buffer physical address
};
/** @brief Circular buffer private data */ /** @brief Circular buffer private data */
struct circbuf_priv_t { struct circbuf_priv_t {
int minor; ///< device file minor number int minor; ///< device file minor number
...@@ -75,4 +81,6 @@ extern unsigned char circbuf_byrshift; ...@@ -75,4 +81,6 @@ extern unsigned char circbuf_byrshift;
#endif #endif
/* end of debug code */ /* end of debug code */
ssize_t circbuf_get_ptr(int sensor_port, size_t offset, size_t len, struct fvec *vect_0, struct fvec *vect_1);
#endif /* _CIRCBUF_H */ #endif /* _CIRCBUF_H */
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
#include "exif393.h" #include "exif393.h"
#define D(x) #define D(x)
//#define D(x) printk("%s:%d:",__FILE__,__LINE__);x //#define D(x) printk(">>> %s:%d:",__FILE__,__LINE__);x
//Major //Major
...@@ -90,6 +90,7 @@ static int aexif_wp[SENSOR_PORTS] = {1,1,1,1}; // frame write pointer in ...@@ -90,6 +90,7 @@ static int aexif_wp[SENSOR_PORTS] = {1,1,1,1}; // frame write pointer in
static int aexif_enabled[SENSOR_PORTS] = {0,0,0,0}; // enable storing of frame meta data, enable reading Exif data static int aexif_enabled[SENSOR_PORTS] = {0,0,0,0}; // enable storing of frame meta data, enable reading Exif data
static int aexif_valid[SENSOR_PORTS] = {0,0,0,0}; // Exif tables and buffer are valid. static int aexif_valid[SENSOR_PORTS] = {0,0,0,0}; // Exif tables and buffer are valid.
static char * ameta_buffer[SENSOR_PORTS]= {NULL,NULL,NULL,NULL}; // dynamically allocated buffer to store frame meta data. static char * ameta_buffer[SENSOR_PORTS]= {NULL,NULL,NULL,NULL}; // dynamically allocated buffer to store frame meta data.
static char exif_tmp_buff[MAX_EXIF_SIZE];
//static char * meta_buffer=NULL; // dynamically allocated buffer to store frame meta data. //static char * meta_buffer=NULL; // dynamically allocated buffer to store frame meta data.
// page 0 - temporary storage, 1..MAX_EXIF_FRAMES - buffer // page 0 - temporary storage, 1..MAX_EXIF_FRAMES - buffer
...@@ -724,6 +725,35 @@ ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *of ...@@ -724,6 +725,35 @@ ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *of
return count; return count;
} }
/* This code is copied from exif_read, consider replacing it with this function invocation */
size_t exif_get_data(int sensor_port, unsigned short meta_index, void *buff, size_t buff_sz)
{
size_t ret = 0;
size_t count = exif_template_size;
loff_t off;
int start_p, page_p, i;
char *metap;
//will truncate by the end of current page
if (!aexif_enabled[sensor_port])
return 0;
off = meta_index * exif_template_size;
D(printk("%s: count= 0x%x, *off= 0x%x, i=0x%x, exif_template_size=0x%x\n", __func__, (int) count, (int) off, (int) meta_index, (int) exif_template_size));
start_p = meta_index * exif_template_size;
page_p = off - start_p;
D(printk("%s: count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x, i=0x%x, exif_template_size=0x%x\n", __func__, (int) count, (int) off, (int)start_p, (int)page_p,(int) meta_index, (int) exif_template_size));
metap = &ameta_buffer[sensor_port][meta_index * aexif_meta_size[sensor_port]]; // pointer to the start of the selected page in frame meta_buffer
if ((page_p + count) > exif_template_size)
count = exif_template_size - page_p;
memcpy(exif_tmp_buff, exif_template, exif_template_size);
D(printk("%s: count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x\n", __func__, (int) count, (int) off, (int)start_p, (int)page_p));
for (i = 0; i < exif_fields; i++) {
memcpy(&exif_tmp_buff[dir_table[i].dst], &metap[dir_table[i].src], dir_table[i].len);
}
memcpy(buff, &exif_tmp_buff[page_p], count);
return count;
}
EXPORT_SYMBOL_GPL(exif_get_data);
//!++++++++++++++++++++++++++++++++++++ _init() ++++++++++++++++++++++++++++++++++++++++++++++++++++++ //!++++++++++++++++++++++++++++++++++++ _init() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
......
...@@ -53,5 +53,6 @@ int putlong_meta(int sensor_port, unsigned long data, int * indx, unsigned long ...@@ -53,5 +53,6 @@ int putlong_meta(int sensor_port, unsigned long data, int * indx, unsigned long
char * encode_time(char buf[27], unsigned long sec, unsigned long usec); char * encode_time(char buf[27], unsigned long sec, unsigned long usec);
int store_meta(int sensor_port); //called from IRQ service - put current metadata to meta_buffer, return page index int store_meta(int sensor_port); //called from IRQ service - put current metadata to meta_buffer, return page index
size_t exif_get_data(int sensor_port, unsigned short meta_index, void * buff, size_t buff_sz);
#endif #endif
...@@ -412,6 +412,22 @@ ssize_t jpeghead_read(struct file *file, char *buf, size_t count, loff_t *off) ...@@ -412,6 +412,22 @@ ssize_t jpeghead_read(struct file *file, char *buf, size_t count, loff_t *off)
return count; return count;
} }
ssize_t jpeghead_get_data(int sensor_port, void *buff, size_t buff_sz, size_t offset)
{
unsigned long ptr = offset;
size_t count = jpeghead_priv[sensor_port].jpeg_h_sz;
if (ptr >= jpeghead_priv[sensor_port].jpeg_h_sz)
ptr = jpeghead_priv[sensor_port].jpeg_h_sz;
if ((ptr + count) > jpeghead_priv[sensor_port].jpeg_h_sz)
count = jpeghead_priv[sensor_port].jpeg_h_sz - ptr;
if (buff_sz < count)
return -EINVAL;
memcpy(buff, &jpeghead_priv[sensor_port].header[ptr], count);
return count;
}
EXPORT_SYMBOL_GPL(jpeghead_get_data);
/**huffman_* file operations /**huffman_* file operations
* write, read Huffman tables, initialize tables to default ones, program FPGA with the Huffman tables * write, read Huffman tables, initialize tables to default ones, program FPGA with the Huffman tables
......
...@@ -14,6 +14,7 @@ int jpegheader_create(struct interframe_params_t * params, unsigned char * b ...@@ -14,6 +14,7 @@ int jpegheader_create(struct interframe_params_t * params, unsigned char * b
int jpeghead_open (struct inode *inode, struct file *filp); // set filesize int jpeghead_open (struct inode *inode, struct file *filp); // set filesize
loff_t jpeghead_lseek (struct file * file, loff_t offset, int orig, struct interframe_params_t *fp); loff_t jpeghead_lseek (struct file * file, loff_t offset, int orig, struct interframe_params_t *fp);
ssize_t jpeghead_read (struct file * file, char * buf, size_t count, loff_t *off); ssize_t jpeghead_read (struct file * file, char * buf, size_t count, loff_t *off);
ssize_t jpeghead_get_data(int sensor_port, void *buff, size_t buff_sz, size_t offset);
int huffman_open (struct inode *inode, struct file *filp); // set filesize int huffman_open (struct inode *inode, struct file *filp); // set filesize
int huffman_release(struct inode *inode, struct file *filp); int huffman_release(struct inode *inode, struct file *filp);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/module.h>
#include <stddef.h> #include <stddef.h>
#include "x393_helpers.h" #include "x393_helpers.h"
...@@ -47,3 +48,32 @@ u32 get_rtc_usec(void) ...@@ -47,3 +48,32 @@ u32 get_rtc_usec(void)
} }
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(get_rtc_usec);
/**
* @brief Read RTC second counter.
* @return Current value of second counter or 0 in case read sequence was
* not successful.
*/
u32 get_rtc_sec(void)
{
x393_rtc_status_t stat;
x393_status_ctrl_t stat_ctrl;
x393_rtc_sec_t sec;
unsigned int i;
stat = x393_rtc_status();
stat_ctrl.d32 = 0;
stat_ctrl.mode = 1;
stat_ctrl.seq_num = stat.seq_num + 1;
set_x393_rtc_set_status(stat_ctrl);
for (i = 0; i < REPEAT_READ; i++) {
stat = x393_rtc_status();
if (stat.seq_num == stat_ctrl.seq_num) {
sec = x393_rtc_status_sec();
return sec.sec;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(get_rtc_sec);
...@@ -30,5 +30,6 @@ ...@@ -30,5 +30,6 @@
#define REPEAT_READ 10 #define REPEAT_READ 10
u32 get_rtc_usec(void); u32 get_rtc_usec(void);
u32 get_rtc_sec(void);
#endif /* _X393_HELPERS_H */ #endif /* _X393_HELPERS_H */
...@@ -5,4 +5,5 @@ ...@@ -5,4 +5,5 @@
header-y += exifa.h header-y += exifa.h
header-y += c313a.h header-y += c313a.h
header-y += x393_devices.h header-y += x393_devices.h
header-y += ahci_cmd.h
/** @file ahci_cmd.h
*
* @brief Elphel AHCI SATA platform driver for Elphel393 camera. This module provides
* constants and data structures which are used to organize interaction between drivers
* and user space applications during JPEG files recording.
*
* @copyright Copyright (C) 2016 Elphel, Inc
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _AHCI_CMD
#define _AHCI_CMD
#define DRV_CMD_WRITE (1 << 0)
#define DRV_CMD_FINISH (1 << 1)
#define DRV_CMD_EXIF (1 << 2)
#define _NAME_TO_STR(...) #__VA_ARGS__
#define NAME_TO_STR(NAME) _NAME_TO_STR(NAME)
/** The path to Elphel AHCI driver sysfs entry. The trailing slash is mandatory. */
#define SYSFS_AHCI_ENTRY "/sys/devices/soc0/amba@0/80000000.elphel-ahci/"
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_WRITE write
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_START lba_start
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_END lba_end
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_CURR lba_current
/** This file is used to send commands to AHCI driver from user space applications (camogm as for now). */
#define SYSFS_AHCI_WRITE SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_WRITE)
/** This file is used to control starting LBA of a disk buffer (R/W). */
#define SYSFS_AHCI_LBA_START SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_START)
/** This file is used to control ending LBA of a disk buffer (R/W). */
#define SYSFS_AHCI_LBA_END SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_END)
/** This file is used to control current LBA of a disk buffer (R/W). Use this file to set a pointer inside
* [lba_start..lba_end] area where next write operation will begin. */
#define SYSFS_AHCI_LBA_CURRENT SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_CURR)
struct frame_data {
unsigned int sensor_port;
int cirbuf_ptr;
int jpeg_len;
int meta_index;
int cmd;
};
#endif /* _AHCI_CMD */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment