Commit 4c1dde53 authored by Andrey Filippov's avatar Andrey Filippov
Browse files

continue with sensor port i2c

parents 50663cc1 bc970067
Loading
Loading
Loading
Loading
+3 −2
Original line number Original line Diff line number Diff line
@@ -8,5 +8,6 @@ obj-$(CONFIG_ELPHELDRVONMICROZED) += elphel393-mem.o
obj-$(CONFIG_ELPHEL393_INIT)      += elphel393-init.o   
obj-$(CONFIG_ELPHEL393_INIT)      += elphel393-init.o   
obj-$(CONFIG_ELPHEL393)           += x393.o
obj-$(CONFIG_ELPHEL393)           += x393.o
obj-$(CONFIG_ELPHEL393)           += sensor_i2c.o
obj-$(CONFIG_ELPHEL393)           += sensor_i2c.o
fpgajtag-y := fpgajtag353.o x393.o 
obj-$(CONFIG_ELPHEL393)           += fpgajtag353.o
obj-$(CONFIG_ELPHEL393_EXTERNAL)  += fpgajtag.o
#fpgajtag-y := fpgajtag353.o x393.o 
 No newline at end of file
#obj-$(CONFIG_ELPHEL393_EXTERNAL)  += fpgajtag.o
 No newline at end of file
+28 −25
Original line number Original line Diff line number Diff line
@@ -18,6 +18,10 @@
 *!  You should have received a copy of the GNU General Public License
 *!  You should have received a copy of the GNU General Public License
 *!  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *!  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *!****************************************************************************/
 *!****************************************************************************/

#define DRV_NAME "elphel393-mem"
#define pr_fmt(fmt) DRV_NAME": " fmt

#include <linux/module.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of.h>
@@ -74,7 +78,7 @@ static int __init elphelmem_init(void)
	node = of_find_node_by_name(NULL, "elphel393-mem");
	node = of_find_node_by_name(NULL, "elphel393-mem");
	if (!node)
	if (!node)
	{
	{
		printk("DMA buffer allocation ERROR: No device tree node found\n");
		pr_err("DMA buffer allocation ERROR: No device tree node found\n");
		return -ENODEV;
		return -ENODEV;
	}
	}


@@ -116,34 +120,33 @@ static int __init elphelmem_init(void)
	// allocate continuous virtual memory range
	// allocate continuous virtual memory range
//	_elphel_buf.vaddr = kmalloc((_elphel_buf.size*PAGE_SIZE) ,GFP_KERNEL);
//	_elphel_buf.vaddr = kmalloc((_elphel_buf.size*PAGE_SIZE) ,GFP_KERNEL);
	_elphel_buf.vaddr = dma_alloc_coherent(NULL,(_elphel_buf.size*PAGE_SIZE),&(_elphel_buf.paddr),GFP_KERNEL);
	_elphel_buf.vaddr = dma_alloc_coherent(NULL,(_elphel_buf.size*PAGE_SIZE),&(_elphel_buf.paddr),GFP_KERNEL);
    if(_elphel_buf.paddr) {
    if(_elphel_buf.vaddr) {
    	printk("Allocated %u pages for DMA at address 0x%x\n", (u32)_elphel_buf.size, (u32)_elphel_buf.paddr);
    	pr_info("Allocated %u pages for DMA at address 0x%x\n", (u32)_elphel_buf.size, (u32)_elphel_buf.paddr);
    } else {
    } else {
    	printk("ERROR allocating coherent DMA memory buffer");
    	pr_err("ERROR allocating coherent DMA memory buffer\n");
    }
    }


    _elphel_buf.h2d_vaddr = kzalloc((_elphel_buf.h2d_size*PAGE_SIZE) ,GFP_KERNEL);
    _elphel_buf.h2d_vaddr = kzalloc((_elphel_buf.h2d_size*PAGE_SIZE) ,GFP_KERNEL);
    if (!_elphel_buf.h2d_vaddr){
    if (!_elphel_buf.h2d_vaddr){
    	_elphel_buf.h2d_size = 0;
    	_elphel_buf.h2d_size = 0;
    	printk("ERROR allocating H2D DMA memory buffer");
    	pr_err("ERROR allocating H2D DMA memory buffer\n");
    }
    }


    _elphel_buf.d2h_vaddr = kzalloc((_elphel_buf.d2h_size*PAGE_SIZE) ,GFP_KERNEL);
    _elphel_buf.d2h_vaddr = kzalloc((_elphel_buf.d2h_size*PAGE_SIZE) ,GFP_KERNEL);
    if (!_elphel_buf.d2h_vaddr){
    if (!_elphel_buf.d2h_vaddr){
    	_elphel_buf.d2h_size = 0;
    	_elphel_buf.d2h_size = 0;
    	printk("ERROR allocating D2H DMA memory buffer");
    	pr_err("ERROR allocating D2H DMA memory buffer\n");
    }
    }


    _elphel_buf.bidir_vaddr = kzalloc((_elphel_buf.bidir_size*PAGE_SIZE) ,GFP_KERNEL);
    _elphel_buf.bidir_vaddr = kzalloc((_elphel_buf.bidir_size*PAGE_SIZE) ,GFP_KERNEL);
    if (!_elphel_buf.bidir_vaddr){
    if (!_elphel_buf.bidir_vaddr){
    	_elphel_buf.bidir_size = 0;
    	_elphel_buf.bidir_size = 0;
    	printk("ERROR allocating Bidirectional DMA memory buffer");
    	pr_err("ERROR allocating Bidirectional DMA memory buffer\n");
    }
    }



    pr_info("Coherent buffer vaddr:              0x%08X\n",(u32) pElphel_buf -> vaddr);
    printk("Coherent buffer vaddr:              0x%08X\n",(u32) pElphel_buf -> vaddr);
    pr_info("Coherent buffer paddr:              0x%08X\n",(u32) pElphel_buf -> paddr);
    printk("Coherent buffer paddr:              0x%08X\n",(u32) pElphel_buf -> paddr);
    pr_info("Coherent buffer length:             0x%08X\n",(u32) pElphel_buf -> size * PAGE_SIZE);
    printk("Coherent buffer length:             0x%08X\n",(u32) pElphel_buf -> size * PAGE_SIZE);


    return 0;
    return 0;
}
}
@@ -155,7 +158,7 @@ dma_map_single(struct device *dev, void *cpu_addr, size_t size,
 */
 */
static void __exit elphelmem_exit(void)
static void __exit elphelmem_exit(void)
{
{
    printk("DMA buffer disabled\n");
	pr_info("DMA buffer disabled\n");
}
}




@@ -240,7 +243,7 @@ static ssize_t sync_for_device_h2d(struct device *dev, struct device_attribute *
		paddr =  _elphel_buf.h2d_paddr;
		paddr =  _elphel_buf.h2d_paddr;
		len = _elphel_buf.h2d_size * PAGE_SIZE;
		len = _elphel_buf.h2d_size * PAGE_SIZE;
	}
	}
    printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
    pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
    dma_sync_single_for_device(dev, paddr, len, DMA_TO_DEVICE);
    dma_sync_single_for_device(dev, paddr, len, DMA_TO_DEVICE);


    return count;
    return count;
@@ -255,7 +258,7 @@ static ssize_t sync_for_cpu_d2h(struct device *dev, struct device_attribute *att
		paddr =  _elphel_buf.d2h_paddr;
		paddr =  _elphel_buf.d2h_paddr;
		len = _elphel_buf.d2h_size * PAGE_SIZE;
		len = _elphel_buf.d2h_size * PAGE_SIZE;
	}
	}
    printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
	pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
    dma_sync_single_for_cpu(dev, paddr, len, DMA_FROM_DEVICE);
    dma_sync_single_for_cpu(dev, paddr, len, DMA_FROM_DEVICE);


    return count;
    return count;
@@ -271,7 +274,7 @@ static ssize_t sync_for_device_d2h(struct device *dev, struct device_attribute *
		paddr =  _elphel_buf.d2h_paddr;
		paddr =  _elphel_buf.d2h_paddr;
		len = _elphel_buf.d2h_size * PAGE_SIZE;
		len = _elphel_buf.d2h_size * PAGE_SIZE;
	}
	}
    printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
	pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
    dma_sync_single_for_device(dev, paddr, len, DMA_FROM_DEVICE);
    dma_sync_single_for_device(dev, paddr, len, DMA_FROM_DEVICE);


    return count;
    return count;
@@ -286,7 +289,7 @@ static ssize_t sync_for_cpu_bidir(struct device *dev, struct device_attribute *a
		paddr =  _elphel_buf.bidir_paddr;
		paddr =  _elphel_buf.bidir_paddr;
		len = _elphel_buf.bidir_size * PAGE_SIZE;
		len = _elphel_buf.bidir_size * PAGE_SIZE;
	}
	}
    printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
	pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
    dma_sync_single_for_cpu(dev, paddr, len, DMA_BIDIRECTIONAL);
    dma_sync_single_for_cpu(dev, paddr, len, DMA_BIDIRECTIONAL);


    return count;
    return count;
@@ -302,7 +305,7 @@ static ssize_t sync_for_device_bidir(struct device *dev, struct device_attribute
		paddr =  _elphel_buf.bidir_paddr;
		paddr =  _elphel_buf.bidir_paddr;
		len = _elphel_buf.bidir_size * PAGE_SIZE;
		len = _elphel_buf.bidir_size * PAGE_SIZE;
	}
	}
    printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
	pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
    dma_sync_single_for_device(dev, paddr, len, DMA_BIDIRECTIONAL);
    dma_sync_single_for_device(dev, paddr, len, DMA_BIDIRECTIONAL);


    return count;
    return count;
@@ -386,13 +389,13 @@ static int elphel393_mem_sysfs_register(struct platform_device *pdev)
static int elphel393_mem_probe(struct platform_device *pdev)
static int elphel393_mem_probe(struct platform_device *pdev)
{
{
	elphel393_mem_sysfs_register(pdev);
	elphel393_mem_sysfs_register(pdev);
	dev_info(&pdev->dev,"Probing elphel393-mem\n");
	pr_info("Probing elphel393-mem\n");


	if (_elphel_buf.h2d_vaddr){
	if (_elphel_buf.h2d_vaddr){
		// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
		// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
		pElphel_buf->h2d_paddr = dma_map_single(&pdev->dev, _elphel_buf.h2d_vaddr, (_elphel_buf.h2d_size*PAGE_SIZE), DMA_TO_DEVICE);
		pElphel_buf->h2d_paddr = dma_map_single(&pdev->dev, _elphel_buf.h2d_vaddr, (_elphel_buf.h2d_size*PAGE_SIZE), DMA_TO_DEVICE);
		if (!pElphel_buf->h2d_paddr){
		if (!pElphel_buf->h2d_paddr){
			printk("ERROR in dma_map_single() for bidirectional buffer");
			pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
			return 0;
			return 0;
		}
		}
//	    printk("H2D DMA buffer location:\t\t0x%08X\n", pElphel_buf->h2d_paddr);
//	    printk("H2D DMA buffer location:\t\t0x%08X\n", pElphel_buf->h2d_paddr);
@@ -402,7 +405,7 @@ static int elphel393_mem_probe(struct platform_device *pdev)
		// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
		// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
		pElphel_buf->d2h_paddr = dma_map_single(&pdev->dev, _elphel_buf.d2h_vaddr, (_elphel_buf.d2h_size*PAGE_SIZE), DMA_FROM_DEVICE);
		pElphel_buf->d2h_paddr = dma_map_single(&pdev->dev, _elphel_buf.d2h_vaddr, (_elphel_buf.d2h_size*PAGE_SIZE), DMA_FROM_DEVICE);
		if (!pElphel_buf->d2h_paddr){
		if (!pElphel_buf->d2h_paddr){
			printk("ERROR in dma_map_single() for bidirectional buffer");
			pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
			return 0;
			return 0;
		}
		}
//	    printk("D2H DMA buffer location:\t\t0x%08X\n", pElphel_buf->d2h_paddr);
//	    printk("D2H DMA buffer location:\t\t0x%08X\n", pElphel_buf->d2h_paddr);
@@ -413,7 +416,7 @@ static int elphel393_mem_probe(struct platform_device *pdev)
		// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
		// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
		pElphel_buf->bidir_paddr = dma_map_single(&pdev->dev, _elphel_buf.bidir_vaddr, (_elphel_buf.bidir_size*PAGE_SIZE), DMA_BIDIRECTIONAL);
		pElphel_buf->bidir_paddr = dma_map_single(&pdev->dev, _elphel_buf.bidir_vaddr, (_elphel_buf.bidir_size*PAGE_SIZE), DMA_BIDIRECTIONAL);
		if (!pElphel_buf->bidir_paddr){
		if (!pElphel_buf->bidir_paddr){
			printk("ERROR in dma_map_single() for bidirectional buffer");
			pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
			return 0;
			return 0;
		}
		}
//	    printk("Bidirectional DMA buffer location:\t0x%08X\n", pElphel_buf->bidir_paddr);
//	    printk("Bidirectional DMA buffer location:\t0x%08X\n", pElphel_buf->bidir_paddr);
@@ -438,7 +441,7 @@ static int elphel393_mem_probe(struct platform_device *pdev)


static int elphel393_mem_remove(struct platform_device *pdev)
static int elphel393_mem_remove(struct platform_device *pdev)
{
{
	dev_info(&pdev->dev,"Removing elphel393-mem");
	pr_info("Removing elphel393-mem");
	return 0;
	return 0;
}
}


@@ -452,7 +455,7 @@ static struct platform_driver elphel393_mem = {
	.probe   = elphel393_mem_probe,
	.probe   = elphel393_mem_probe,
	.remove  = elphel393_mem_remove,
	.remove  = elphel393_mem_remove,
	.driver  = {
	.driver  = {
		.name  = "elphel393-mem",
		.name  = DRV_NAME,
		.owner = THIS_MODULE,
		.owner = THIS_MODULE,
		.of_match_table = elphel393_mem_of_match,
		.of_match_table = elphel393_mem_of_match,
		.pm = NULL, /* power management */
		.pm = NULL, /* power management */
@@ -463,5 +466,5 @@ module_platform_driver(elphel393_mem);
module_init(elphelmem_init);
module_init(elphelmem_init);
module_exit(elphelmem_exit);
module_exit(elphelmem_exit);
MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");

MODULE_AUTHOR("Elphel, Inc.");
MODULE_DESCRIPTION("Reserve a large chunk of contiguous memory at boot");
+166 −31
Original line number Original line Diff line number Diff line
@@ -289,8 +289,8 @@ static ssize_t get_i2c_page_alloc(struct device *dev, struct device_attribute *a
{
{
	int chn = get_channel_from_name(attr) ;
	int chn = get_channel_from_name(attr) ;
	int page;
	int page;
	if (sysfs_page[chn]>=0)
//	if (sysfs_page[chn]>=0)
		return -EBUSY;
//		return -EBUSY;
	page= i2c_page_alloc(chn);
	page= i2c_page_alloc(chn);
	if (page <0)
	if (page <0)
		return -ENOMEM;
		return -ENOMEM;
@@ -303,17 +303,30 @@ static ssize_t free_i2c_page(struct device *dev, struct device_attribute *attr,
	int page;
	int page;
    sscanf(buf, "%i", &page);
    sscanf(buf, "%i", &page);
    page &= 0xff;
    page &= 0xff;
	if (sysfs_page[chn] >= 0)
//	if (sysfs_page[chn] >= 0)
//		i2c_page_free(chn, page);
//	sysfs_page[chn] = -1; // free
    i2c_page_free(chn, page);
    i2c_page_free(chn, page);
	    sysfs_page[chn] = -1; // free
	return count;
	return count;
}
}


/* Set/get page number for reading */
static ssize_t set_i2c_page_inuse(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int chn = get_channel_from_name(attr) ;
	int page;
    sscanf(buf, "%i", &page);
    sysfs_page[chn] = page & 0xff;
	return count;
}
static ssize_t get_i2c_page_inuse(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t get_i2c_page_inuse(struct device *dev, struct device_attribute *attr, char *buf)
{
{
	int chn = get_channel_from_name(attr) ;
	int chn = get_channel_from_name(attr) ;
	return sprintf(buf,"%d\n",sysfs_page[chn]);
	return sprintf(buf,"%d\n",sysfs_page[chn]);
}
}



// Get i2c table data as raw data (hex)
// Get i2c table data as raw data (hex)
static ssize_t get_i2c_tbl_raw(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t get_i2c_tbl_raw(struct device *dev, struct device_attribute *attr, char *buf)
{
{
@@ -322,49 +335,170 @@ static ssize_t get_i2c_tbl_raw(struct device *dev, struct device_attribute *attr
	if (page < 0)
	if (page < 0)
		return -ENXIO;   /* No such device or address */
		return -ENXIO;   /* No such device or address */


	return sprintf(buf,"%0x08x\n",i2c_pages_shadow[(chn << 8) + (page &0xff)]);
	return sprintf(buf,"0x%08x\n",i2c_pages_shadow[(chn << 8) + (page &0xff)]);
}
}


static ssize_t set_i2c_tbl_raw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
static ssize_t set_i2c_tbl_raw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
{
	int chn = get_channel_from_name(attr) ;
	int chn = get_channel_from_name(attr) ;
	int page, data;
	int ni, page, data;
    sscanf(buf, "%i %i", &page, &data);
    ni = sscanf(buf, "%i %i", &page, &data);
    page &= 0xff;
    if (ni < 2)
    	dev_err(dev, "Requires 2 parameters: page, data\n");
        return -EINVAL;

    set_sensor_i2c_raw(chn,
    set_sensor_i2c_raw(chn,
    		           page,   // index in lookup table
    		           page & 0xff,   // index in lookup table
    				   (u32) data); // Bit delay - number of mclk periods in 1/4 of the SCL period
    				   (u32) data); // Bit delay - number of mclk periods in 1/4 of the SCL period
	return count;
	return count;
}
}


// Sysfs top
// Get/parse i2c table (hex)
static ssize_t get_i2c_tbl_human(struct device *dev, struct device_attribute *attr, char *buf)
{
	x393_i2c_ctltbl_t tb_data;
	int chn = get_channel_from_name(attr) ;
	int page = sysfs_page[chn]; // currently selected page for sysfs reads
	if (page < 0)
		return -ENXIO;   /* No such device or address */
	tb_data.d32 = i2c_pages_shadow[(chn << 8) + (page &0xff)];
	if (tb_data.rnw){
		return sprintf(buf,"Read entry: chn=%d page=%d(0x%x) two_byte_addr=%d number bytes to read=%d bit_duration=%d\n",
				                chn,   page,page,            tb_data.nabrd,tb_data.nabrd,tb_data.nbrd,  tb_data.dly);
	} else {
		return sprintf(buf,"Write entry: chn=%d page=%d(0x%x) sa=0x%02x rah=0x%02x nbw=%d bit_duration=%d\n",
				                chn,   page,page,tb_data.sa,tb_data.rah,tb_data.nbwr,  tb_data.dly);
	}
}

static ssize_t set_i2c_tbl_wr_human(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int chn = get_channel_from_name(attr) ;
	int ni, page, rah,sa7,nbwr,dly;
    ni = sscanf(buf, "%i %i %i %i %i", &page, &sa7, &rah, &nbwr, &dly);
    if (ni < 2)
    	dev_err(dev, "Requires 5 parameters: page, slave address (7 bit), high reg address byte, bytes to write (1..10), 1/4 scl period in mclk\n");
        return -EINVAL;
    set_sensor_i2c_wr(chn,
					  page & 0xff, // index in lookup table
					  sa7 &  0x7f,  // slave address (7 bit)
					  rah &  0xff,  // High byte of the i2c register address
					  nbwr & 0xf, // Number of bytes to write (1..10)
					  dly &  0xff); // Bit delay - number of mclk periods in 1/4 of the SCL period
	return count;
}


static DEVICE_ATTR(alloc0  ,       SYSFS_PERMISSIONS & SYSFS_READONLY,    get_i2c_page_alloc ,   NULL);
static ssize_t set_i2c_tbl_rd_human(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
static DEVICE_ATTR(alloc1  ,       SYSFS_PERMISSIONS & SYSFS_READONLY,    get_i2c_page_alloc ,   NULL);
{
static DEVICE_ATTR(alloc2  ,       SYSFS_PERMISSIONS & SYSFS_READONLY,    get_i2c_page_alloc ,   NULL);
	int chn = get_channel_from_name(attr) ;
static DEVICE_ATTR(alloc3  ,       SYSFS_PERMISSIONS & SYSFS_READONLY,    get_i2c_page_alloc ,   NULL);
	int ni, page, two_byte_addr, num_bytes,bit_delay;
static DEVICE_ATTR(page_in_use0 ,  SYSFS_PERMISSIONS                 ,    get_i2c_page_inuse,   free_i2c_page);
    ni = sscanf(buf, "%i %i %i %i %i", &page, &two_byte_addr, &num_bytes, &bit_delay);
static DEVICE_ATTR(page_in_use1 ,  SYSFS_PERMISSIONS                 ,    get_i2c_page_inuse,   free_i2c_page);
    if (ni < 2)
static DEVICE_ATTR(page_in_use2 ,  SYSFS_PERMISSIONS                 ,    get_i2c_page_inuse,   free_i2c_page);
    	dev_err(dev, "Requires 4 parameters: page, two byte addr (0 - 8bit,1 - 16 bit reg. addr), number of bytes to read, bytes to write (1..10), 1/4 scl period in mclk\n");
static DEVICE_ATTR(page_in_use3 ,  SYSFS_PERMISSIONS                 ,    get_i2c_page_inuse,   free_i2c_page);
        return -EINVAL;
    set_sensor_i2c_rd( chn,
					   page & 0xff,          // index in lookup table
					   two_byte_addr & 1,    // Number of address bytes (0 - one byte, 1 - two bytes)
					   num_bytes & 7,        // Number of bytes to read (1..8, 0 means 8)
					   bit_delay & 0xff);    // Bit delay - number of mclk periods in 1/4 of the SCL period
	return count;
}


static ssize_t set_i2c_read(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int chn = get_channel_from_name(attr) ;
	int ni, page, sa7, addr;
    ni = sscanf(buf, "%i %i %i", &page, &sa7, addr);
    if (ni <3)
    	dev_err(dev, "Requires 3 parameters: page, sa7, reg_addr\n");
        return -EINVAL;
    page &= 0xff;
    read_sensor_i2c (chn,
    		         page & 0xff,    // page (8 bits)
    				 sa7 & 0x7f,     // 7-bit i2c slave address
                     addr & 0xffff); // 8/16 bit address	return count;
}

// Get i2c read data from fifo
static ssize_t get_i2c_read(struct device *dev, struct device_attribute *attr, char *buf)
{
	int chn = get_channel_from_name(attr) ;
	int page = sysfs_page[chn]; // currently selected page for sysfs reads
	if (page < 0)
		return -ENXIO;   /* No such device or address */
	return sprintf(buf,"%d\n",read_sensor_i2c_fifo(chn)); // <0 - not ready, 0..255 - data
}

// Get i2c read data from fifo
static ssize_t get_i2c_help(struct device *dev, struct device_attribute *attr, char *buf)
{
	return sprintf(buf,"Numeric suffix in file names selects sensor port\n"
			           "alloc*:   read - allocate and return page, write <page> (any data) - free page\n"
			           "rd_page*: read/write page number used in read operations (-1 if none)\n"
			           "tbl_raw*: read - raw hex table value (for current rd_page), write <page> <data32> set page\n"
			           "tbl_wr*:  read - decoded table entry for current rd_page, write <page> <sa7> <high_addr_byte> <bytes_to_write> <dly>\n"
			           "tbl_rd*:  read - decoded table entry for current rd_page, write <page> <2-byte addr> <bytes_to_read> <dly>\n"
			           "tbl_rd* and tbl_wr* return same result when read. Delay is 8 bit, 250 - 200KHz SCL\n");

}


// Sysfs top
/* alloc*: read - allocate and return page, write (any data) - free page */
static DEVICE_ATTR(alloc0  ,       SYSFS_PERMISSIONS                 ,    get_i2c_page_alloc ,   free_i2c_page);
static DEVICE_ATTR(alloc1  ,       SYSFS_PERMISSIONS                 ,    get_i2c_page_alloc ,   free_i2c_page);
static DEVICE_ATTR(alloc2  ,       SYSFS_PERMISSIONS                 ,    get_i2c_page_alloc ,   free_i2c_page);
static DEVICE_ATTR(alloc3  ,       SYSFS_PERMISSIONS                 ,    get_i2c_page_alloc ,   free_i2c_page);
/* rd_page*: read/write page number used in read operations */
static DEVICE_ATTR(rd_page0 ,      SYSFS_PERMISSIONS                 ,    get_i2c_page_inuse,    set_i2c_page_inuse);
static DEVICE_ATTR(rd_page1 ,      SYSFS_PERMISSIONS                 ,    get_i2c_page_inuse,    set_i2c_page_inuse);
static DEVICE_ATTR(rd_page2 ,      SYSFS_PERMISSIONS                 ,    get_i2c_page_inuse,    set_i2c_page_inuse);
static DEVICE_ATTR(rd_page3 ,      SYSFS_PERMISSIONS                 ,    get_i2c_page_inuse,    set_i2c_page_inuse);
static DEVICE_ATTR(tbl_raw0 ,      SYSFS_PERMISSIONS                 ,    get_i2c_tbl_raw,       set_i2c_tbl_raw);
static DEVICE_ATTR(tbl_raw0 ,      SYSFS_PERMISSIONS                 ,    get_i2c_tbl_raw,       set_i2c_tbl_raw);
static DEVICE_ATTR(tbl_raw1 ,      SYSFS_PERMISSIONS                 ,    get_i2c_tbl_raw,       set_i2c_tbl_raw);
static DEVICE_ATTR(tbl_raw1 ,      SYSFS_PERMISSIONS                 ,    get_i2c_tbl_raw,       set_i2c_tbl_raw);
static DEVICE_ATTR(tbl_raw2 ,      SYSFS_PERMISSIONS                 ,    get_i2c_tbl_raw,       set_i2c_tbl_raw);
static DEVICE_ATTR(tbl_raw2 ,      SYSFS_PERMISSIONS                 ,    get_i2c_tbl_raw,       set_i2c_tbl_raw);
static DEVICE_ATTR(tbl_raw3 ,      SYSFS_PERMISSIONS                 ,    get_i2c_tbl_raw,       set_i2c_tbl_raw);
static DEVICE_ATTR(tbl_raw3 ,      SYSFS_PERMISSIONS                 ,    get_i2c_tbl_raw,       set_i2c_tbl_raw);
static DEVICE_ATTR(tbl_wr0 ,       SYSFS_PERMISSIONS                 ,    get_i2c_tbl_human,     set_i2c_tbl_wr_human);
static DEVICE_ATTR(tbl_wr1 ,       SYSFS_PERMISSIONS                 ,    get_i2c_tbl_human,     set_i2c_tbl_wr_human);
static DEVICE_ATTR(tbl_wr2 ,       SYSFS_PERMISSIONS                 ,    get_i2c_tbl_human,     set_i2c_tbl_wr_human);
static DEVICE_ATTR(tbl_wr3 ,       SYSFS_PERMISSIONS                 ,    get_i2c_tbl_human,     set_i2c_tbl_wr_human);
static DEVICE_ATTR(tbl_rd0 ,       SYSFS_PERMISSIONS                 ,    get_i2c_tbl_human,     set_i2c_tbl_rd_human);
static DEVICE_ATTR(tbl_rd1 ,       SYSFS_PERMISSIONS                 ,    get_i2c_tbl_human,     set_i2c_tbl_rd_human);
static DEVICE_ATTR(tbl_rd2 ,       SYSFS_PERMISSIONS                 ,    get_i2c_tbl_human,     set_i2c_tbl_rd_human);
static DEVICE_ATTR(tbl_rd3 ,       SYSFS_PERMISSIONS                 ,    get_i2c_tbl_human,     set_i2c_tbl_rd_human);
static DEVICE_ATTR(i2c_rd0 ,       SYSFS_PERMISSIONS                 ,    get_i2c_read,          set_i2c_read);
static DEVICE_ATTR(i2c_rd1 ,       SYSFS_PERMISSIONS                 ,    get_i2c_read,          set_i2c_read);
static DEVICE_ATTR(i2c_rd2 ,       SYSFS_PERMISSIONS                 ,    get_i2c_read,          set_i2c_read);
static DEVICE_ATTR(i2c_rd3 ,       SYSFS_PERMISSIONS                 ,    get_i2c_read,          set_i2c_read);
static DEVICE_ATTR(help,           SYSFS_PERMISSIONS & SYSFS_READONLY,    get_i2c_help,          NULL);


static struct attribute *root_dev_attrs[] = {
static struct attribute *root_dev_attrs[] = {
        &dev_attr_alloc0.attr,
        &dev_attr_alloc0.attr,
        &dev_attr_alloc1.attr,
        &dev_attr_alloc1.attr,
        &dev_attr_alloc2.attr,
        &dev_attr_alloc2.attr,
        &dev_attr_alloc3.attr,
        &dev_attr_alloc3.attr,
        &dev_attr_page_in_use0.attr,
        &dev_attr_rd_page0.attr,
        &dev_attr_page_in_use1.attr,
        &dev_attr_rd_page1.attr,
        &dev_attr_page_in_use2.attr,
        &dev_attr_rd_page2.attr,
        &dev_attr_page_in_use3.attr,
        &dev_attr_rd_page3.attr,
        &dev_attr_tbl_raw0.attr,
        &dev_attr_tbl_raw0.attr,
        &dev_attr_tbl_raw1.attr,
        &dev_attr_tbl_raw1.attr,
        &dev_attr_tbl_raw2.attr,
        &dev_attr_tbl_raw2.attr,
        &dev_attr_tbl_raw3.attr,
        &dev_attr_tbl_raw3.attr,
        &dev_attr_tbl_wr0.attr,
        &dev_attr_tbl_wr1.attr,
        &dev_attr_tbl_wr2.attr,
        &dev_attr_tbl_wr3.attr,
        &dev_attr_tbl_rd0.attr,
        &dev_attr_tbl_rd1.attr,
        &dev_attr_tbl_rd2.attr,
        &dev_attr_tbl_rd3.attr,
		&dev_attr_i2c_rd0.attr,
        &dev_attr_i2c_rd1.attr,
        &dev_attr_i2c_rd2.attr,
        &dev_attr_i2c_rd3.attr,
        &dev_attr_help.attr,
        NULL
        NULL
};
};


@@ -412,13 +546,14 @@ static int elphel393_sensor_i2c_probe(struct platform_device *pdev)
	struct i2c_client *ltc3589_client;
	struct i2c_client *ltc3589_client;
	struct elphel393_pwr_data_t *clientdata = NULL;
	struct elphel393_pwr_data_t *clientdata = NULL;
	*/
	*/
	dev_info(&pdev->dev,"Probing elphel393-sensor-i2c\n");
	pr_info("Probing elphel393-sensor-i2c\n");
	elphel393_sens_i2c_sysfs_register(pdev);
	i2c_page_alloc_init();
	i2c_page_alloc_init();
#if 0
#if 0
	clientdata = devm_kzalloc(&pdev->dev, sizeof(*clientdata), GFP_KERNEL);
	clientdata = devm_kzalloc(&pdev->dev, sizeof(*clientdata), GFP_KERNEL);
#endif
#endif
	elphel393_sensor_i2c_init_of(pdev);
	elphel393_sensor_i2c_init_of(pdev);
	elphel393_sens_i2c_sysfs_register(pdev);
	pr_info("done probing elphel393-sensor-i2c\n");
	return 0;
	return 0;
}
}


@@ -435,7 +570,7 @@ static struct of_device_id elphel393_sensor_i2c_of_match[] = {


MODULE_DEVICE_TABLE(of, elphel393_sensor_i2c_of_match);
MODULE_DEVICE_TABLE(of, elphel393_sensor_i2c_of_match);


static struct platform_driver elphel393_pwr = {
static struct platform_driver elphel393_sensor_i2c = {
	.probe   = elphel393_sensor_i2c_probe,
	.probe   = elphel393_sensor_i2c_probe,
	.remove  = elphel393_sensor_i2c_remove,
	.remove  = elphel393_sensor_i2c_remove,
	.driver  = {
	.driver  = {
@@ -446,7 +581,7 @@ static struct platform_driver elphel393_pwr = {
	},
	},
};
};


module_platform_driver(elphel393_pwr);
module_platform_driver(elphel393_sensor_i2c);


MODULE_AUTHOR("Andrey Filippov  <andrey@elphel.com>");
MODULE_AUTHOR("Andrey Filippov  <andrey@elphel.com>");
MODULE_DESCRIPTION("Elphel 10393 sensor ports i2c");
MODULE_DESCRIPTION("Elphel 10393 sensor ports i2c");