#include "x393.h"
#include "sensor_i2c.h"
#include "clock10359.h"
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
#define DRV_NAME "elphel_clock10359"
static int clock_frequency[16]; // in Hz per port, per clock
static struct device *sdev = NULL; // store this device here
const char CLOCK_NAME[] = "cy22393";
typedef struct {
unsigned int p : 11; // p - 16..1600
unsigned int q : 8; // q - 0.. 255
unsigned int dv: 7; // dv - 1.. 127
unsigned int corr: 3; // 0/1/2/3/4
unsigned int rslt: 3; // 0 - OK, 1 - frequency low, 2 - frequency high, 3 - other
} t_pll_params;
// in Hz
#define CY22393_SCALE 100 // precision will be 100Hz
#define CY22393_PLLMIN (100000000/CY22393_SCALE)
#define CY22393_PLLMAX (400000000/CY22393_SCALE)
#define CY22393_XTAL ( 12000000/CY22393_SCALE)
#define CY22393_OUTMAX (166000000/CY22393_SCALE)
#define CY22393_PMIN 16
#define CY22393_PMAX 1600
int calc_pll_params (unsigned int f, t_pll_params * pars); // f -in Hz
int setCYField (int sensor_port, int addr, int mask, int value); /// bus==1 - FPGA (sensor bus through 10359), 0 - CPU bus
int setClockFreq(int nclock, int freq); // freq now in Hz
int calc_pll_params (unsigned int f, t_pll_params * pars) { // f -in Hz
// t_pll_params pars;
unsigned int f0= CY22393_XTAL;
unsigned int f0_2= CY22393_XTAL/2;
unsigned int fpmn= CY22393_XTAL * (CY22393_PMIN + 6);
unsigned int fpmx= CY22393_XTAL * (CY22393_PMAX + 6);
int divmn, divmx, err1,err, div,q,qmn,qmx,qmx1,fdv,p, e,fdvq;
pars->rslt=3; // other error
dev_dbg(sdev, "f0=%d,f=%d, CY22393_OUTMAX=%d\r\n",f0,f,CY22393_OUTMAX);
f/=CY22393_SCALE; // to fit into 32-bit calculations
dev_dbg(sdev, "f0=%d,f=%d, CY22393_OUTMAX=%d\r\n",f0,f,CY22393_OUTMAX);
if (f>CY22393_OUTMAX) {
pars->rslt=2;
dev_dbg(sdev, "f0=%d,f=%d, CY22393_OUTMAX=%d\r\n",f0,f,CY22393_OUTMAX);
return pars->rslt;
}
if (f <=0 ) {
pars->rslt=1;
dev_dbg(sdev, "f0=%d,f=%d, CY22393_OUTMAX=%d\r\n",f0,f,CY22393_OUTMAX);
return pars->rslt;
}
divmx=CY22393_PLLMAX/f; if (divmx > 127) divmx=127; // could not be <1
divmn=CY22393_PLLMIN/f; if (divmn < 1) divmn=1;
if (divmn >127) {
pars->rslt=1;
dev_dbg(sdev, "f0=%d,f=%d, CY22393_OUTMAX=%d, divmn=%d\r\n",f0,f,CY22393_OUTMAX,divmn);
return pars->rslt;
}
err1=f;
qmx1=0;
for (div=divmn;div<=divmx;div++) {
err=err1*div;
fdv=f*div;
qmn=fpmn/fdv-2; if (qmn < 0) qmn=0;
qmx=fpmx/fdv-2; if (qmx >255) qmx=255;
// recalculate qmn to avoid same div*qmn as already tried with lover div
dev_dbg(sdev, "div=%d,qmn=%d, qmx=%d\r\n",div,qmn,qmx);
if (div==1) qmx1=qmx;
else if ((qmn*div) < qmx1) qmn=qmx1/div;
for (q=qmn+2;q<=qmx+2; q++) {
fdvq=fdv*q;
p= (fdvq+f0_2)/f0; // can p be out of range here?
e= fdvq-f0*p; if (e<0) e=-e;
if (e< (err*q)) { // better solution found
pars->rslt=0;
pars->p=p-6;
pars->q=q-2;
pars->dv=div;
err1=e/q/div;
err=err1*div;
dev_dbg(sdev, "f=%d, div=%d, p=%d,q=%d, err1=%d\r\n", (f0*p)/q/div, div,p, q, err1);
if (err1==0) {
pars->corr=(pars->p<226)?0:((pars->p<621)?1:((pars->p<829)?2:((pars->p<1038)?3:4)));
dev_dbg(sdev, "f=%d, div=%d, p=%d,q=%d, err1=%d, rslt=%d\r\n",
(f0*(pars->p+6))/(pars->q+2)/pars->dv,
pars->dv,
(pars->p+6),
(pars->q+2),
err1,
pars->rslt);
return pars->rslt;
}
}
}
}
dev_dbg(sdev, "f=%d, div=%d, p=%d,q=%d, err1=%d, rslt=%d\r\n",
(f0*(pars->p+6))/(pars->q+2)/pars->dv,
pars->dv,
(pars->p+6),
(pars->q+2),
err1,
pars->rslt);
pars->corr=(pars->p<226)?0:((pars->p<621)?1:((pars->p<829)?2:((pars->p<1038)?3:4)));
return pars->rslt;
}
int setCYField (int sensor_port, int reg_addr, int mask, int value) {
int error;
int reg_data;
if ((error = x393_xi2c_read_reg(CLOCK_NAME, // device class name
sensor_port, // sensor port number
0, // slave address (7-bit) offset from the class defined slave address
reg_addr, // register address (width is defined by class)
®_data)) <0) { // pointer to a data receiver (read data width is defined by class)
dev_err(sdev,"setCYField(%d, 0x%x, 0x%x,0x%x) failed reading i2c register\n",
sensor_port, reg_addr, mask, value);
return error;
}
reg_data ^= (reg_data ^ value) & mask;
if ((error = x393_xi2c_write_reg(CLOCK_NAME, // device class name
sensor_port, // sensor port number
0, // slave address (7-bit) offset from the class defined slave address
reg_addr, // register address (width is defined by class)
reg_data)) <0) { // data to write (width is defined by class)
dev_err(sdev,"setCYField(%d, 0x%x, 0x%x, 0x%x) failed writing 0x%x to i2c register\n",
sensor_port, reg_addr, mask, value,reg_data);
return error;
}
}
int x393_getClockFreq(int sensor_port, int nclock) {
if ((sensor_port < 0) || (sensor_port > 3) || (nclock < 0) || (nclock > 3))return -EINVAL; // bad clock number
else {
return clock_frequency[(sensor_port << 2) || nclock];
}
}
EXPORT_SYMBOL_GPL(x393_getClockFreq);
int x393_setClockFreq(int sensor_port, int nclock, int freq) { // freq now in Hz
int err=0;
sensor_port &= 3;
nclock &= 3;
t_pll_params pll_params;
int i,bp,bq,bdiv,pllc,fact;
bp=0; bq=0; bdiv=0; pllc= 0; // just to make gcc happy
fact=0;
dev_dbg(sdev, "setClockFreq(%d,%d,%d)\r\n",sensor_port,nclock,freq);
if ((freq!=0) && (nclock!=3) ){
if ( (i=calc_pll_params (freq, &pll_params)) !=0) {
dev_err(sdev, "bad frequency for clock %d - %d Hz, err=%d\n",nclock,freq,i);
return -EINVAL;
}
fact=CY22393_SCALE*(CY22393_XTAL*(pll_params.p+6)/(pll_params.q+2)/pll_params.dv);
bp= pll_params.p; // (freqtab[freq]>>20)&0x7ff;
bq= pll_params.q; // (freqtab[freq]>>12)&0xff;
bdiv=pll_params.dv; // (freqtab[freq]>> 4)&0xff;
pllc=pll_params.corr; // freqtab[freq] &0x0f;
}
switch (nclock) {
case 0:
if (freq==0) {
err |= setCYField (sensor_port,0x16, 0x40, 0x00); // turn off pll3
err |= setCYField (sensor_port,0x09, 0x7f, 0x00); // turn off divider- A
err |= setCYField (sensor_port,0x08, 0x7f, 0x00); // turn off divider- A
} else {
err |= setCYField (sensor_port,0x16, 0x7f, 0x40+(pllc<<3)+((bp & 1)<<2)+((bp & 0x600)>>9) );
err |= setCYField (sensor_port,0x15, 0xff, ((bp & 0x1fe)>>1) );
err |= setCYField (sensor_port,0x14, 0xff, bq );
err |= setCYField (sensor_port,0x09, 0x7f, bdiv); // set divider- A
err |= setCYField (sensor_port,0x08, 0x7f, bdiv); // set divider- A
err |= setCYField (sensor_port,0x0e, 0x03, 0x03); // set PLL3 as source for ClkA
}
// fpga_state |= FPGA_STATE_CLOCKS;
break;
case 1:
if (freq==0) {
err |= setCYField (sensor_port,0x0b, 0x7f, 0x00); // turn off divider- B
err |= setCYField (sensor_port,0x0a, 0x7f, 0x00); // turn off divider- B
for (i=0;i<24;i+=3) err |= setCYField (sensor_port,0x42+i, 0x40, 0x00); // turn off pll1
} else {
// progam all variants
for (i=0;i<24;i+=3) {
err |= setCYField (sensor_port,0x42+i, 0x7f, 0x40+(pllc<<3)+((bp & 1)<<2)+((bp & 0x600)>>9) );
err |= setCYField (sensor_port,0x41+i, 0xff, ((bp & 0x1fe)>>1) );
err |= setCYField (sensor_port,0x40+i, 0xff, bq );
}
err |= setCYField (sensor_port,0x0b, 0x7f, bdiv); // set divider- B
err |= setCYField (sensor_port,0x0a, 0x7f, bdiv); // set divider- B
err |= setCYField (sensor_port,0x0e, 0x0c, 0x04); // set PLL1 as source for ClkB
}
break;
case 2:
if (freq==0) {
err |= setCYField (sensor_port,0x13, 0x40, 0x00); // turn off pll2
err |= setCYField (sensor_port,0x0d, 0x7f, 0x00); // turn off divider- D
} else {
err |= setCYField (sensor_port,0x13, 0x7f, 0x40+(pllc<<3)+((bp & 1)<<2)+((bp & 0x600)>>9) );
err |= setCYField (sensor_port,0x12, 0xff, ((bp & 0x1fe)>>1) );
err |= setCYField (sensor_port,0x11, 0xff, bq );
err |= setCYField (sensor_port,0x0d, 0x7f, bdiv); // set divider- D
err |= setCYField (sensor_port,0x0e, 0xc0, 0x80); // set PLL2 as source for ClkD
}
break;
case 3:
if ((freq!=0) && (freq!=CY22393_SCALE*CY22393_XTAL)) {
dev_err(sdev, "Only frequency 0 (off) and %d Hz (xtal) are allowed for channel 3\r\n",CY22393_SCALE*CY22393_XTAL);
return -EINVAL;
} else {
// int setCYField (sensor_port,int devfd, int addr, int mask, int value) O_RDWR
if (freq==0) {
err |= setCYField (sensor_port,0x0f, 0x04, 0x00);
} else {
err |= setCYField (sensor_port,0x0f, 0x04, 0x04);
fact= CY22393_SCALE*CY22393_XTAL;
}
}
break;
default: return -1; // wrong clock number
}
dev_dbg(sdev, "nclock=%d fact=%d\n",nclock,fact);
if (err != 0) {
dev_err(sdev, "Error programming clock %d fact=%d, err=0x%x\n",nclock,fact, err);
return err;
}
clock_frequency[(sensor_port << 2) + nclock] = fact;
return fact;
}
EXPORT_SYMBOL_GPL(x393_setClockFreq);
// dev_warn(dev,"i2c_member_store(): not implemented\n");
/* =========================== sysfs functionality ============================== */
/* Get channel port and channel number from the last 2 character of the attribute name*/
static int get_port_channel_from_name(struct device_attribute *attr){
int reg = 0;
int sport,nclock;
sscanf(attr->attr.name + (strlen(attr->attr.name)-2), "%du", ®);
sport = reg / 10;
nclock = reg - 10 * sport;
return (nclock & 3) + ((sport &3) <<2);
}
static ssize_t clock_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn = get_port_channel_from_name(attr) ;
int sensor_port = chn >> 2;
int nclock = chn & 3;
int ni, freq, err;
ni = sscanf(buf,"%i", &freq);
if (ni < 1) {
dev_err(dev, "Requires clock frequency in Hz");
return -EINVAL;
}
if ((err = x393_setClockFreq(sensor_port, nclock, freq)))
return err;
return count;
}
static ssize_t clock_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn = get_port_channel_from_name(attr);
int sensor_port = chn >> 2;
int nclock = chn & 3;
int freq = x393_getClockFreq(sensor_port, nclock);
if (freq < 0)
return freq;
return sprintf(buf,"%d\n",freq);
}
// Get i2c read data from fifo
static ssize_t clock_help_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Numeric suffix in file names selects sensor port/clock number\n"
"clock: read - get programmed clock frequency in Hz, write: set clock frequency\n"
);
}
//==================================
static DEVICE_ATTR(clock00 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock01 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock02 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock03 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock10 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock11 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock12 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock13 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock20 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock21 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock22 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock23 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock30 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock31 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock32 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(clock33 , SYSFS_PERMISSIONS , clock_show , clock_store);
static DEVICE_ATTR(help, SYSFS_PERMISSIONS & SYSFS_READONLY, clock_help_show, NULL);
static struct attribute *root_dev_attrs[] = {
&dev_attr_clock00.attr,
&dev_attr_clock01.attr,
&dev_attr_clock02.attr,
&dev_attr_clock03.attr,
&dev_attr_clock10.attr,
&dev_attr_clock11.attr,
&dev_attr_clock12.attr,
&dev_attr_clock13.attr,
&dev_attr_clock20.attr,
&dev_attr_clock21.attr,
&dev_attr_clock22.attr,
&dev_attr_clock23.attr,
&dev_attr_clock30.attr,
&dev_attr_clock31.attr,
&dev_attr_clock32.attr,
&dev_attr_clock33.attr,
&dev_attr_help.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int elphel393_clock10359_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
dev_dbg(dev,"sysfs_create_group(dev_attr_root_group) done \n");
}
return retval;
}
// =======================================
static void elphel393_clock10359_init_of(struct platform_device *pdev)
{
pr_info("elphel393_clock10359_init_of()\n");
}
static int elphel393_clock10359_probe(struct platform_device *pdev)
{
sdev =&pdev->dev;
dev_dbg(&pdev->dev,"Probing elphel_clock10359\n");
elphel393_clock10359_sysfs_register(pdev);
dev_dbg(&pdev->dev,"elphel393_clock10359_sysfs_register() done\n");
elphel393_clock10359_init_of(pdev);
dev_dbg(&pdev->dev,"done probing elphel393_clock10359_probe\n");
return 0;
}
static int elphel393_clock10359_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev,"Removing elphel393_clock10359");
return 0;
}
static struct of_device_id elphel393_clock10359_of_match[] = {
{ .compatible = "elphel,elphel393_clock10359-1.00", },
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, elphel393_clock10359_of_match);
static struct platform_driver elphel393_clock10359 = {
.probe = elphel393_clock10359_probe,
.remove = elphel393_clock10359_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = elphel393_clock10359_of_match,
.pm = NULL, /* power management */
},
};
module_platform_driver(elphel393_clock10359);
MODULE_AUTHOR("Andrey Filippov ");
MODULE_DESCRIPTION("Elphel 10393 10359");
MODULE_LICENSE("GPL");
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/clock10359.h 0000664 0000000 0000000 00000002224 12762437450 0026564 0 ustar 00root root 0000000 0000000 /*******************************************************************************
* FILE NAME : clock10359.h
* DESCRIPTION: Control of the CY22393 clock on the 10359 multiplexer connected
* to the sensor port
* Copyright 2002-2016 (C) Elphel, Inc.
* -----------------------------------------------------------------------------*
*
* 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 2 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 .
*******************************************************************************/
int x393_setClockFreq(int sensor_port, int nclock, int freq);
int x393_getClockFreq(int sensor_port, int nclock);
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/elphel393-init.c 0000664 0000000 0000000 00000032562 12762437450 0027543 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : elphel393-init.c
*! DESCRIPTION: * Unlock rootfs NAND flash partition
*! * Read MAC and other useful info from NAND flash OTP area
*! and put to sysfs
*!
*! E-mail: oleg@elphel.com, support-list@elphel.com
*!
*! Copyright (C) 2016 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*!
*! 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 2 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 .
*!****************************************************************************/
#define DRV_NAME "elphel393-init"
#define pr_fmt(fmt) DRV_NAME": " fmt
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* from elphel393-mem.c */
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
#define NAND_FLASH_OTP_PAGE_OFFSET 0*2048
/*
* Read and parse bootargs parameter in the device tree
* This driver is run in the last place - at least after NAND driver is probed
*/
static struct mtd_info *mtd;
//surprise size
static char *bootargs;
//known size, all should be zero filled
static char boardinfo[2048];
static char serial[13];
static char revision[8];
static int setup_mio_pin_and_reset_usb(void);
static int __init elphel393_early_initialize(void){
pr_info("Set up the mio pin 49 and reset USB\n");
setup_mio_pin_and_reset_usb();
return 0;
}
/*
static int __init elphel393_early_initialize(void){
struct device_node *node;
struct property *newproperty;
u8 *macaddr;
pr_info("VERY EARLY CALL TO UPDATE DEVICE TREE");
node = of_find_node_by_name(NULL, "ps7-ethernet");
if (!node){
pr_err("Device tree node 'ps7-ethernet' not found.");
return -ENODEV;
}
newproperty = kzalloc(sizeof(*newproperty) + 6, GFP_KERNEL);
if (!newproperty)
return -ENOMEM;
newproperty->value = newproperty + 1;
newproperty->length = 6;
newproperty->name = kstrdup("local-mac-address", GFP_KERNEL);
if (!newproperty->name) {
kfree(newproperty);
return -ENOMEM;
}
macaddr = newproperty->value;
macaddr[0] = 0x02;
macaddr[1] = 0x03;
macaddr[2] = 0x04;
macaddr[3] = 0x05;
macaddr[4] = 0x06;
macaddr[5] = 0x07;
of_update_property(node,newproperty);
return 0;
}
*/
static int __init elphel393_init_init (void)
{
struct device_node *node;
node = of_find_node_by_name(NULL, "chosen");
//just throw an error
if (!node){
pr_err("Device tree node 'chosen' not found.");
return -ENODEV;
}
of_property_read_string(node, "bootargs", &bootargs);
if (bootargs!=NULL) {
pr_debug("bootargs line from device tree is %s",bootargs);
}
return 0;
}
static void __exit elphel393_init_exit(void)
{
printk("Exit\n");
}
// SYSFS
static ssize_t get_bootargs(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%s\n",bootargs);
}
static ssize_t get_boardinfo(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%s\n",boardinfo);
}
static ssize_t get_revision(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%s\n",revision);
}
static ssize_t get_serial(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%s\n",serial);
}
static int get_factory_info(void);
static ssize_t set_boardinfo(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int ret;
size_t retlen;
char wbuf[2048];
if (IS_ERR(mtd)){
pr_err("Get MTD device error, code:%d\n",-(u32)mtd);
return -ENODEV;
}
//need all 0xff bytes
memset(wbuf,0xff,2048);
//too much of a trouble to read from flash again
if(!strnstr(boardinfo,"",sizeof(boardinfo))){
pr_info("Factory Info record is clean.\n");
pr_info("Data to be written: %s\n",buf);
// I got some buf, unknown size- should be limited to 2048? ok
if (strlen(buf)>2047) {
pr_err("Data > 2KiB. Abort.\n");
return -EFBIG;
}
// Not strict check, just look for opening tags.
if(!strstr(buf,"")||!strstr(buf,"")||!strstr(buf,"")){
pr_err("Bad data format\n");
return -EINVAL;
}
//copy to wbuf, buf is null terminated and is <=2047
strcpy(wbuf,buf);
//pr_info("BUFFER: %s\n",wbuf);
ret = mtd_write_user_prot_reg(mtd, NAND_FLASH_OTP_PAGE_OFFSET, 2048, &retlen, wbuf);
if (ret){
pr_err("Flash page write, code %d\n",ret);
return ret;
}
pr_info("Data is successfully written and cannot be overwritten anymore\n");
get_factory_info();
}else{
pr_err("Factory Info record (serial='%s' revision='%s') can not be overwritten\n",serial,revision);
return -EPERM;
}
return count;
}
static ssize_t perform_usbreset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
pr_info("EMIO pin 49: USB reset\n");
setup_mio_pin_and_reset_usb();
return count;
}
static DEVICE_ATTR(bootargs , SYSFS_PERMISSIONS & SYSFS_READONLY, get_bootargs , NULL);
static DEVICE_ATTR(boardinfo , SYSFS_PERMISSIONS , get_boardinfo, set_boardinfo);
static DEVICE_ATTR(revision , SYSFS_PERMISSIONS & SYSFS_READONLY, get_revision , NULL);
static DEVICE_ATTR(serial , SYSFS_PERMISSIONS & SYSFS_READONLY, get_serial , NULL);
static DEVICE_ATTR(usbreset , SYSFS_PERMISSIONS , NULL, perform_usbreset);
static struct attribute *root_dev_attrs[] = {
&dev_attr_bootargs.attr,
&dev_attr_boardinfo.attr,
&dev_attr_revision.attr,
&dev_attr_serial.attr,
&dev_attr_usbreset.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int elphel393_init_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
}
return retval;
}
static int elphel393_init_probe(struct platform_device *pdev)
{
char *bootargs_copy;
char *token;
char *token_copy;
char *param;
char *value;
//mtd device number to unlock
u8 devnum= 0;
u8 unlock= 0;
pr_debug("Probing\n");
//copy bootargs string
bootargs_copy = kstrdup(bootargs,GFP_KERNEL);
//find out which partition to unlock if any
//parse bootargs
do {
token = strsep(&bootargs_copy," ");
if (token) {
//if '=' is found - split by '='
token_copy = token;
if (strchr(token_copy,'=')){
if (!strcmp(token_copy,"rootfstype=ubifs")){
unlock=1;
}
param = strsep(&token_copy,"=");
//if "ubi.mtd" then get the partition number and unlock /dev/mtdN - if not found then don't care
if (!strcmp(param,"ubi.mtd")){
value = strsep(&token_copy,",");
if (kstrtou8(value,10,&devnum)){
pr_err("Partition number str to u8 conversion.");
}
}
}
}
} while (token);
kfree(bootargs_copy);
// unlock /dev/mtdN partition
// if there's no need to unlock, get /dev/mtd0
if (devnum>=0){
mtd = get_mtd_device(NULL,devnum);
if (IS_ERR(mtd)){
pr_err("Get MTD device error, code:%d\n",-(u32)mtd);
return -ENODEV;
}
if (unlock){
mtd_unlock(mtd,0,mtd->size);
pr_info("/dev/mtd%d: unlocked",devnum);
}
}
// read boardinfo record
// * device number is not important
// * no need to unlock
// page size
BUG_ON(mtd->writesize > 2048);
get_factory_info();
elphel393_init_sysfs_register(pdev);
return 0;
}
static int get_factory_info(void){
char regvalh[]="0000";
char regvall[]="00000000";
u16 hwaddrh0, hwaddrh1;
u32 hwaddrl0, hwaddrl1;
size_t retlen;
//size of nand flash page
char kbuf[2048];
size_t size = mtd->writesize;
size_t min_size;
int ret;
char *ps,*pe;
struct device_node *node;
struct property *newproperty;
u32 *mac32; // = (u32*) mac_address;
u8 *macaddr;
u8 block_factory_mac = 0;
node = of_find_node_by_name(NULL, "ps7-ethernet");
if (!node){
pr_err("Device tree node 'ps7-ethernet' not found.");
return -ENODEV;
}
// check if MAC address was overridden in u-boot
mac32 = (u32 *) of_get_mac_address(node);
hwaddrl0 = cpu_to_le32(mac32[0]);
hwaddrh0 = cpu_to_le16(mac32[1]);
if ((hwaddrh0!=0x0000)||(hwaddrl0!=0x10640E00)){
block_factory_mac = 1;
}
ret = mtd_read_user_prot_reg(mtd, NAND_FLASH_OTP_PAGE_OFFSET, size, &retlen, kbuf);
if (ret){
pr_err("Flash page read, code %d",ret);
return ret;
}
pr_debug("buf: %s\n",kbuf);
// do whatever we like with the kbuf
// search for ""
// expecting to find it somewhere...
if(strnstr(kbuf,"",size)){
//...right in the beginning or error
ps = strnstr(kbuf,"",size);
pe = strnstr(kbuf,"",size);
min_size = min(pe-ps-(sizeof("")-1),sizeof(serial)-1);
strncpy(serial,ps+sizeof("")-1,min_size);
strncpy(regvalh,serial,4);
strncpy(regvall,serial+4,8);
//there is kstrtou64 but it doesn't work?
kstrtou16(regvalh,16,&hwaddrh1);
kstrtou32(regvall,16,&hwaddrl1);
pr_debug("MAC from flash: %02x:%02x:%02x:%02x:%02x:%02x\n",
(hwaddrl1 & 0xff), ((hwaddrl1 >> 8) & 0xff),
((hwaddrl1 >> 16) & 0xff), (hwaddrl1 >> 24),
(hwaddrh1 & 0xff), (hwaddrh1 >> 8));
newproperty = kzalloc(sizeof(*newproperty) + 6, GFP_KERNEL);
if (!newproperty)
return -ENOMEM;
newproperty->value = newproperty + 1;
newproperty->length = 6;
newproperty->name = kstrdup("local-mac-address", GFP_KERNEL);
if (!newproperty->name) {
kfree(newproperty);
return -ENOMEM;
}
macaddr = newproperty->value;
macaddr[0] = (hwaddrh1 >> 8) & 0xff;
macaddr[1] = hwaddrh1 & 0xff;
macaddr[2] = (hwaddrl1 >> 24) & 0xff;
macaddr[3] = (hwaddrl1 >> 16) & 0xff;
macaddr[4] = (hwaddrl1 >> 8) & 0xff;
macaddr[5] = hwaddrl1 & 0xff;
if (!block_factory_mac)
of_update_property(node,newproperty);
else
pr_info("Factory MAC: %02x:%02x:%02x:%02x:%02x:%02x, u-boot overridden MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
(hwaddrl1 & 0xff),((hwaddrl1 >> 8) & 0xff),((hwaddrl1 >> 16) & 0xff),(hwaddrl1 >> 24),(hwaddrh1 & 0xff),(hwaddrh1 >> 8),
(hwaddrl0 & 0xff),((hwaddrl0 >> 8) & 0xff),((hwaddrl0 >> 16) & 0xff),(hwaddrl0 >> 24),(hwaddrh0 & 0xff),(hwaddrh0 >> 8));
mac32 = (u32 *) of_get_mac_address(node);
hwaddrl1 = cpu_to_le32(mac32[0]);
hwaddrh1 = cpu_to_le16(mac32[1]);
pr_debug("new MAC from device tree: %02x:%02x:%02x:%02x:%02x:%02x\n",
(hwaddrl1 & 0xff), ((hwaddrl1 >> 8) & 0xff),
((hwaddrl1 >> 16) & 0xff), (hwaddrl1 >> 24),
(hwaddrh1 & 0xff), (hwaddrh1 >> 8));
//write hwaddr to zynq reg
//kstrtou16(serial,16,®valh);
//serial
ps = strnstr(kbuf,"",size);
pe = strnstr(kbuf,"",size);
min_size = min(pe-ps-(sizeof("")-1),sizeof(revision)-1);
strncpy(revision,ps+sizeof("")-1,min_size);
ps = strnstr(kbuf,"",size);
pe = strnstr(kbuf,"",size);
min_size = min(pe-ps+(sizeof("")-1),sizeof(boardinfo)-1);
strncpy(boardinfo,ps,min_size);
}
return 0;
}
static int setup_mio_pin_and_reset_usb(void){
u32 reg_value;
void __iomem* emio_ptr = ioremap(0xe000a000, 0x00000300);
if (!emio_ptr) {
pr_err("Failed to get a pointer to the EMIO registers");
return -1;
}
const u32 newvalue = 0x1<<17;
reg_value = readl(emio_ptr+0x244);
pr_debug("read DIR reg: 0x%08x\n",reg_value);
writel(reg_value|newvalue,emio_ptr+0x244);
udelay(10);
reg_value = readl(emio_ptr+0x248);
pr_debug("read EN reg: 0x%08x\n",reg_value);
writel(reg_value|newvalue,emio_ptr+0x248);
udelay(10);
reg_value = readl(emio_ptr+0x44);
pr_debug("read OUT reg: 0x%08x\n",reg_value);
writel(reg_value&(~newvalue),emio_ptr+0x44);
udelay(10);
writel(reg_value|newvalue,emio_ptr+0x44);
reg_value = readl(emio_ptr+0x64);
pr_debug("read IN reg (correct value: 0x00020000): 0x%08x\n",reg_value);
iounmap(emio_ptr);
return 0;
}
static int elphel393_init_remove(struct platform_device *pdev)
{
pr_info("Remove");
return 0;
}
static struct of_device_id elphel393_init_of_match[] = {
{ .compatible = "elphel,elphel393-init-1.00", },
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, elphel393_init_of_match);
static struct platform_driver elphel393_initialize = {
.probe = elphel393_init_probe,
.remove = elphel393_init_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = elphel393_init_of_match,
.pm = NULL, /* power management */
},
};
early_initcall(elphel393_early_initialize);
module_platform_driver(elphel393_initialize);
module_init(elphel393_init_init);
module_exit(elphel393_init_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Elphel, Inc.");
MODULE_DESCRIPTION("Unlock rootfs flash partition and read/write board info: serial and revision");
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/elphel393-mem.c 0000664 0000000 0000000 00000040361 12762437450 0027352 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : elphel393-mem.c
*! DESCRIPTION: Reserve large memory range at boot time (when it is available)
*! to use as a circular video buffer
*! Copyright (C) 2015 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*!
*! 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 2 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 .
*!****************************************************************************/
#define DRV_NAME "elphel393-mem"
#define pr_fmt(fmt) DRV_NAME": " fmt
#include
#include
#include
#include
#include // kmalloc
#include // vmalloc
#include // __get_free_pages
#include
#include
#include
#include
#include
#include
#include
#include
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
static ssize_t get_paddr(struct device *dev, struct device_attribute *attr, char *buf);
static struct elphel_buf_t _elphel_buf = {
// Coherent DMA buffer
.vaddr = NULL,
.paddr = 0,
.size = 0,
// Host to device stream DMA buffer
.h2d_vaddr = NULL,
.h2d_paddr = 0,
.h2d_size = 1024, // TODO: add to DT, learn how to allocate more
// Device to host stream DMA buffer
.d2h_vaddr = NULL,
.d2h_paddr = 0,
.d2h_size = 1024,
// Bidirectional stream DMA buffer
.bidir_vaddr = NULL,
.bidir_paddr = 0,
.bidir_size = 1024
};
struct elphel_buf_t *pElphel_buf; // static can not be extern
EXPORT_SYMBOL_GPL(pElphel_buf);
static int __init elphelmem_init(void)
{
struct device_node *node;
const __be32 *bufsize_be;
pElphel_buf = &_elphel_buf;
node = of_find_node_by_name(NULL, "elphel393-mem");
if (!node)
{
pr_err("DMA buffer allocation ERROR: No device tree node found\n");
return -ENODEV;
}
bufsize_be = (__be32 *)of_get_property(node, "memsize", NULL);
_elphel_buf.size = be32_to_cpup(bufsize_be);
/*
// Coherent DMA buffer
void *vaddr;
dma_addr_t paddr;
ssize_t size;
// Host to device stream DMA buffer
void *h2d_vaddr;
dma_addr_t h2d_paddr;
ssize_t h2d_size;
// Device to host stream DMA buffer
void *d2h_vaddr;
dma_addr_t d2h_paddr;
ssize_t d2h_size;
// Bidirectional stream DMA buffer
void *bidir_vaddr;
dma_addr_t bidir_paddr;
ssize_t bidir_size;
_elphel_buf.vaddr = dma_alloc_coherent(NULL,(_elphel_buf.size*PAGE_SIZE),&(_elphel_buf.paddr),GFP_KERNEL);
if(_elphel_buf.paddr)
{
printk("Allocated %u pages for DMA at address 0x%x\n", (u32)_elphel_buf.size, (u32)_elphel_buf.paddr);
}
else printk("ERROR allocating memory buffer");
http://linuxkernelhacker.blogspot.com/2014/07/arm-dma-mapping-explained.html
*/
// Alternative way to allocate memory for DMA
// allocate continuous virtual memory range
// _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);
if(_elphel_buf.vaddr) {
pr_info("Allocated %u pages for DMA at address 0x%x\n", (u32)_elphel_buf.size, (u32)_elphel_buf.paddr);
} else {
pr_err("ERROR allocating coherent DMA memory buffer\n");
}
_elphel_buf.h2d_vaddr = kzalloc((_elphel_buf.h2d_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.h2d_vaddr){
_elphel_buf.h2d_size = 0;
pr_err("ERROR allocating H2D DMA memory buffer\n");
}
_elphel_buf.d2h_vaddr = kzalloc((_elphel_buf.d2h_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.d2h_vaddr){
_elphel_buf.d2h_size = 0;
pr_err("ERROR allocating D2H DMA memory buffer\n");
}
_elphel_buf.bidir_vaddr = kzalloc((_elphel_buf.bidir_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.bidir_vaddr){
_elphel_buf.bidir_size = 0;
pr_err("ERROR allocating Bidirectional DMA memory buffer\n");
}
pr_info("Coherent buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> vaddr);
pr_info("Coherent buffer paddr: 0x%08X\n",(u32) pElphel_buf -> paddr);
pr_info("Coherent buffer length: 0x%08X\n",(u32) pElphel_buf -> size * PAGE_SIZE);
return 0;
}
/*
dma_addr_t
dma_map_single(struct device *dev, void *cpu_addr, size_t size,
enum dma_data_direction direction)
*/
static void __exit elphelmem_exit(void)
{
pr_info("DMA buffer disabled\n");
}
// SYSFS
static ssize_t get_paddr(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.paddr);
}
static ssize_t get_size(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.size);
}
static ssize_t get_paddr_h2d(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.h2d_paddr);
}
static ssize_t get_size_h2d(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.h2d_size);
}
static ssize_t get_paddr_d2h(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.d2h_paddr);
}
static ssize_t get_size_d2h(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.d2h_size);
}
static ssize_t get_paddr_bidir(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.bidir_paddr);
}
static ssize_t get_size_bidir(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.bidir_size);
}
static ssize_t sync_for_cpu_h2d(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.h2d_paddr;
len = _elphel_buf.h2d_size * PAGE_SIZE;
}
printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_cpu(dev, paddr, len, DMA_TO_DEVICE);
return count;
}
static ssize_t sync_for_device_h2d(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.h2d_paddr;
len = _elphel_buf.h2d_size * PAGE_SIZE;
}
pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_device(dev, paddr, len, DMA_TO_DEVICE);
return count;
}
static ssize_t sync_for_cpu_d2h(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.d2h_paddr;
len = _elphel_buf.d2h_size * PAGE_SIZE;
}
pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_cpu(dev, paddr, len, DMA_FROM_DEVICE);
return count;
}
static ssize_t sync_for_device_d2h(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.d2h_paddr;
len = _elphel_buf.d2h_size * PAGE_SIZE;
}
pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_device(dev, paddr, len, DMA_FROM_DEVICE);
return count;
}
static ssize_t sync_for_cpu_bidir(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.bidir_paddr;
len = _elphel_buf.bidir_size * PAGE_SIZE;
}
pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_cpu(dev, paddr, len, DMA_BIDIRECTIONAL);
return count;
}
static ssize_t sync_for_device_bidir(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.bidir_paddr;
len = _elphel_buf.bidir_size * PAGE_SIZE;
}
pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_device(dev, paddr, len, DMA_BIDIRECTIONAL);
return count;
}
static ssize_t get_sync_for_device_h2d(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the host to device DMA buffer to device (after CPU writes).\n");
}
static ssize_t get_sync_for_cpu_h2d(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the host to device DMA buffer to CPU (before CPU reads).\n");
}
static ssize_t get_sync_for_device_d2h(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the device to host DMA buffer to device (after CPU writes).\n");
}
static ssize_t get_sync_for_cpu_d2h(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the device to host DMA buffer to CPU (before CPU reads).\n");
}
static ssize_t get_sync_for_device_bidir(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the bidirectional DMA buffer to device (after CPU writes).\n");
}
static ssize_t get_sync_for_cpu_bidir(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the bidirectional DMA buffer to CPU (before CPU reads).\n");
}
static DEVICE_ATTR(buffer_address, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr, NULL);
static DEVICE_ATTR(buffer_pages, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size, NULL);
static DEVICE_ATTR(buffer_address_h2d, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_h2d, NULL);
static DEVICE_ATTR(buffer_pages_h2d, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_h2d, NULL);
static DEVICE_ATTR(buffer_address_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_d2h, NULL);
static DEVICE_ATTR(buffer_pages_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_d2h, NULL);
static DEVICE_ATTR(buffer_address_bidir, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_bidir, NULL);
static DEVICE_ATTR(buffer_pages_bidir, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_bidir, NULL);
static DEVICE_ATTR(sync_for_cpu_h2d, SYSFS_PERMISSIONS, get_sync_for_cpu_h2d, sync_for_cpu_h2d);
static DEVICE_ATTR(sync_for_device_h2d, SYSFS_PERMISSIONS, get_sync_for_device_h2d, sync_for_device_h2d);
static DEVICE_ATTR(sync_for_cpu_d2h, SYSFS_PERMISSIONS, get_sync_for_cpu_d2h, sync_for_cpu_d2h);
static DEVICE_ATTR(sync_for_device_d2h, SYSFS_PERMISSIONS, get_sync_for_device_d2h, sync_for_device_d2h);
static DEVICE_ATTR(sync_for_cpu_bidir, SYSFS_PERMISSIONS, get_sync_for_cpu_bidir, sync_for_cpu_bidir);
static DEVICE_ATTR(sync_for_device_bidir, SYSFS_PERMISSIONS, get_sync_for_device_bidir, sync_for_device_bidir);
static struct attribute *root_dev_attrs[] = {
&dev_attr_buffer_address.attr,
&dev_attr_buffer_pages.attr,
&dev_attr_buffer_address_h2d.attr,
&dev_attr_buffer_pages_h2d.attr,
&dev_attr_buffer_address_d2h.attr,
&dev_attr_buffer_pages_d2h.attr,
&dev_attr_buffer_address_bidir.attr,
&dev_attr_buffer_pages_bidir.attr,
&dev_attr_sync_for_cpu_h2d.attr,
&dev_attr_sync_for_device_h2d.attr,
&dev_attr_sync_for_cpu_d2h.attr,
&dev_attr_sync_for_device_d2h.attr,
&dev_attr_sync_for_cpu_bidir.attr,
&dev_attr_sync_for_device_bidir.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int elphel393_mem_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
}
return retval;
}
static int elphel393_mem_probe(struct platform_device *pdev)
{
elphel393_mem_sysfs_register(pdev);
pr_info("Probing elphel393-mem\n");
if (_elphel_buf.h2d_vaddr){
// 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);
if (!pElphel_buf->h2d_paddr){
pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
return 0;
}
// printk("H2D DMA buffer location:\t\t0x%08X\n", pElphel_buf->h2d_paddr);
}
if (_elphel_buf.d2h_vaddr){
// 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);
if (!pElphel_buf->d2h_paddr){
pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
return 0;
}
// printk("D2H DMA buffer location:\t\t0x%08X\n", pElphel_buf->d2h_paddr);
}
if (_elphel_buf.bidir_vaddr){
// 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);
if (!pElphel_buf->bidir_paddr){
pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
return 0;
}
// printk("Bidirectional DMA buffer location:\t0x%08X\n", pElphel_buf->bidir_paddr);
}
printk("H2D stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> h2d_vaddr);
printk("H2D stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> h2d_paddr);
printk("H2D stream buffer length: 0x%08X\n",(u32) pElphel_buf -> h2d_size * PAGE_SIZE);
printk("D2H stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> d2h_vaddr);
printk("D2H stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> d2h_paddr);
printk("D2H stream buffer length: 0x%08X\n",(u32) pElphel_buf -> d2h_size * PAGE_SIZE);
printk("Bidirectional stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> bidir_vaddr);
printk("Bidirectional stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> bidir_paddr);
printk("Bidirectional stream buffer length: 0x%08X\n",(u32) pElphel_buf -> bidir_size * PAGE_SIZE);
return 0;
}
static int elphel393_mem_remove(struct platform_device *pdev)
{
pr_info("Removing elphel393-mem");
return 0;
}
static struct of_device_id elphel393_mem_of_match[] = {
{ .compatible = "elphel,elphel393-mem-1.00", },
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, elphel393_mem_of_match);
static struct platform_driver elphel393_mem = {
.probe = elphel393_mem_probe,
.remove = elphel393_mem_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = elphel393_mem_of_match,
.pm = NULL, /* power management */
},
};
module_platform_driver(elphel393_mem);
module_init(elphelmem_init);
module_exit(elphelmem_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Elphel, Inc.");
MODULE_DESCRIPTION("Reserve a large chunk of contiguous memory at boot");
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/elphel393-pwr.c 0000664 0000000 0000000 00000143177 12762437450 0027415 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : elphel393-pwr.c
*! DESCRIPTION: power supplies control on Elphel 10393 board
*! Copyright (C) 2013-2016 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*!
*! 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 2 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 .
*/
#undef DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DRIVER_DESCRIPTION "Elphel 10393 power supply control"
#define DRIVER_VERSION "1.00"
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
/* PCA6408As */
#define GPIO_CHIP1_ADDR 0x20
#define GPIO_CHIP2_ADDR 0x21
/* PCA9571 on 10389, high 4 pins are NC */
#define GPIO_10389_U4_ADDR 0x25
#define LTC3589_ADDR 0x34
/* TODO: set resistors in device tree to accommodate different revisions ( elphel393_pwr,vp15_r1 = <357000>)*/
#define VP15_R1 357000
#define VP15_R2 287000
#define VCC_SENS01_R1 787000
#define VCC_SENS01_R2 287000
#define VCC_SENS23_R1 787000
#define VCC_SENS23_R2 287000
#define VP5_R1 523000
#define VP5_R2 100000
#define VLDO18_R1 357000
#define VLDO18_R2 287000
#define PINSTRAPPED_OVEN 1
#define REF_FIXED_TENTH_MV 8000
#define REF_VAR_0_TENTH_MV 3625
#define REF_VAR_STEP_TENTH_MV 125
#define DEAFULT_TIMEOUT 300 /* number of retries testing pgood before giving up */
static DEFINE_MUTEX(gpio_10389_lock);
struct pwr_gpio_t {
const char * label;
int pin;
int dir; /* direction: 0 - in, 1 - out*/
int out_val; /* output value */
};
struct elphel393_pwr_data_t {
int chip_i2c_addr[4];
struct device * ltc3489_dev;
struct pwr_gpio_t pwr_gpio [24];
int simulate; /* do not perform actual i2c writes */
struct mutex lock;
int pgoot_timeout;
int pinstrapped_oven;
};
struct voltage_reg_t {
const char * name;
int r1; /* resistor in ohms, if <=0 - r2 is voltage in mv */
int r2; /* resistor in ohms, if r1<=0 - voltage in mv */
int awe_ref; /* 0 - no control, -1 - margining VP10, -2 - margining VP18 */
int awe_en; /* 0 - no control, negative - -1-gpio_index */
int awe_pgood; /* 0 - no status , negative - -1-gpio_index */
int mask_pgood; /* 1 - temporarily disable pgood when turning on/changing voltage */
int awe_slew;
};
static struct voltage_reg_t voltage_reg[]={
{
.name="vp15",
.r1=VP15_R1,
.r2=VP15_R2,
.awe_ref=LTC3589_AWE_B1DTV1_REF,
.awe_en=0,
.awe_pgood=LTC3589_AWE_PGSTAT_SD1,
.mask_pgood=1,
.awe_slew=LTC3589_AWE_VCCR_SLEW_SD1
},
{
.name="vcc_sens01",
.r1=VCC_SENS01_R1,
.r2=VCC_SENS01_R2,
.awe_ref=LTC3589_AWE_B2DTV1_REF,
.awe_en=LTC3589_AWE_OVEN_EN_SD2,
.awe_pgood=LTC3589_AWE_PGSTAT_SD2,
.mask_pgood=1,
.awe_slew=LTC3589_AWE_VCCR_SLEW_SD2
},
{
.name="vcc_sens23",
.r1=VCC_SENS23_R1,
.r2=VCC_SENS23_R2,
.awe_ref=LTC3589_AWE_B3DTV1_REF,
.awe_en=LTC3589_AWE_OVEN_EN_SD3,
.awe_pgood=LTC3589_AWE_PGSTAT_SD3,
.mask_pgood=1,
.awe_slew=LTC3589_AWE_VCCR_SLEW_SD3
},
{
.name="vp5",
.r1=VP5_R1,
.r2=VP5_R2,
.awe_ref=0,
.awe_en=LTC3589_AWE_OVEN_EN_BB,
.awe_pgood=LTC3589_AWE_PGSTAT_BB,
.mask_pgood=1,
.awe_slew=0
},
{
.name="vldo18",
.r1=VLDO18_R1,
.r2=VLDO18_R2,
.awe_ref=0,
.awe_en= 0,
.awe_pgood=LTC3589_AWE_PGSTAT_LDO1,
.mask_pgood=1,
.awe_slew=0
},
{
.name="vp33sens01",
.r1=-1,
.r2=33000,
.awe_ref=0,
.awe_en= -7, /* SENSPWREN0 */
.awe_pgood=0,
.mask_pgood=1,
.awe_slew=0
},
{
.name="vp33sens23",
.r1=-1,
.r2=33000,
.awe_ref=0,
.awe_en= -8, /* SENSPWREN1 */
.awe_pgood=0,
.mask_pgood=1,
.awe_slew=0
},
{
.name="mmtavcc10",
.r1=-1,
.r2=10000,
.awe_ref=0,
.awe_en= 0,
.awe_pgood=-15, /* MGTAVTTGOOD */
.mask_pgood=1,
.awe_slew=0
},
{
.name="mmtavtt12",
.r1=-1,
.r2=12000,
.awe_ref=0,
.awe_en= 0,
.awe_pgood=-15, /* MGTAVTTGOOD */
.mask_pgood=1,
.awe_slew=0
},
{
.name="vp10",
.r1=-1,
.r2=10000,
.awe_ref=-1,
.awe_en= 0,
.awe_pgood=-16, /* PGOOD18 */
.mask_pgood=1,
.awe_slew=0
},
{
.name="vp18",
.r1=-1,
.r2=18000,
.awe_ref=-2,
.awe_en= 0,
.awe_pgood=-16, /* PGOOD18 */
.mask_pgood=1,
.awe_slew=0
},
};
static struct pwr_gpio_t pwr_gpio[24]={
/* 0x20: */
{"PWR_MGB1", 0, 0, 0}, /* 1.8V margining magnitude (0 - 5%, 1 - 10%, float - 15%) */
{"PWR_MG1", 1, 0, 0}, /* 1.8V margining enable 0 - negative margining, 1 - positive margining, float - no margining */
{"PWR_MGB0", 2, 0, 0}, /* 1.0V margining magnitude (0 - 5%, 1 - 10%, float - 15%) */
{"PWR_MG0", 3, 0, 0}, /* 1.0V margining enable 0 - negative margining, 1 - positive margining, float - no margining */
{"PWR_FQ0", 4, 0, 0}, /* float - nominal frequency (should float for SS), 0 - 0.67 nominal frequency, 1 - 1.5 nominal frequency */
{"PWR_SS", 5, 0, 0}, /* Spread spectrum, 0 or float - spread spectrum disabled */
{"SENSPWREN0", 6, 0, 0}, /* 1 - enable 3.3 power to sensor connectors J6 and J7 (0 or float - disable) */
{"SENSPWREN1", 7, 0, 0}, /* 1 - enable 3.3 power to sensor connectors J8 and J9 (0 or float - disable) */
/* 0x21: */
{"NSHUTDOWN", 8, 0, 0}, /* (pulled up). 0 - shutdown, 1 normal */
{"DIS_POR", 9, 0, 0}, /* (pulled down). 0 - normal, 1 - disable POR generation on PGOOD deassertion (needed whil changing voltages) */
{ NULL, 10, 0, 0}, /* Not connected */
{ NULL, 11, 0, 0}, /* Not connected */
{ NULL, 12, 0, 0}, /* Not connected */
{ NULL, 13, 0, 0}, /* Not connected */
{"MGTAVTTGOOD",14, 0, 0}, /* (input) 1.2V linear regulator status (generated from 1.8V) */
{"PGOOD18", 15, 0, 0}, /* (input). Combines other voltages, can be monitored when DIS_POR is activated */
/* 0x25: */
{ "FAN_CTL", 16, 1, 0}, /* Fan Control, 1 - on, 0 - off */
{ "DAS_DSS", 17, 1, 0}, /* ? */
{ "DEVSLP", 18, 1, 0}, /* ? */
{ "EPGMA", 19, 1, 0}, /* ? */
{ NULL, 20, 1, 0}, /* Not connected */
{ NULL, 21, 1, 0}, /* Not connected */
{ NULL, 22, 1, 0}, /* Not connected */
{ NULL, 23, 1, 0} /* Not connected */
};
static struct device * shutdown_dev;
static int make_group (struct device *dev, const char * name,
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf),
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count));
static ssize_t simulate_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t simulate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t outputs_all_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t configs_all_show(struct device *dev, struct device_attribute *attr, char *buf);
#if 0
static ssize_t output_state_show(struct device *dev, struct device_attribute *attr, char *buf);
#endif
static ssize_t output_en_output_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_en_output_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t outputs_pgood_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t channels_en_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t channels_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t channels_dis_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t channels_dis_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t output_ref_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_ref_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t pgood_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t pbad_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t enable_por_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t enable_por_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t gpio_10389_get(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t gpio_10389_set(struct device *dev, struct device_attribute *attr, char *buf, size_t count);
static ssize_t gpio_poweroff(struct device *dev, struct device_attribute *attr, char *buf, size_t count);
static int gpio_shutdown(struct device *dev);
static int por_ctrl(struct device *dev, int disable_por);
static int get_and_disable_por(struct device *dev, int chn_bits, int * old_dis_por);
static int reenable_por(struct device *dev);
static int wait_all_pgood(struct device *dev);
static int list_chn_bits(char * buf, int chn_bits);
static int parse_chn_bits(const char * buf);
static int get_enabled_mask(struct device *dev);
static int set_enabled_by_mask(struct device *dev, int chn_bits, int enable);
static int slew_by_mask(struct device *dev, int chn_bits);
static int get_voltage_channel(const char * name);
static int get_gpio_index_by_name(const char * name);
static int gpio_conf_by_index(struct device *dev,int gpio_index, int dir, int val);
static int get_gpio_pwr_mgx_indices(int chn, int * indices); /* chn = 0 (VP10) or 1 (VP18) */
static int get_volt_mv(struct device *dev, int chn);
static int set_volt_mv(struct device *dev, int chn, int v_mv);
static int get_enable(struct device *dev, int chn);
static int set_enable(struct device *dev, int chn, int enable);
static int get_pgood(struct device *dev, int chn);
int gpio_10389_ctrl(struct device *dev, int value);
/*
Voltages:
VP10 (on at power up, nominal 1.0V)
VP18 (on at power up, nomianl 1.8V)
VP15 (SW1, on by pinstrap, nominal 1.5V - may be reduced to 1.35 later)
VCC_SENS01 (SW2, nominal 1.8V, max 2.8V)
VCC_SENS23 (SW3, nominal 1.8V, max 2.8V)
VP5 (nominal 5.0V, not software programmed)
VLDO18 (LDO1 - always on)
VP33SENS0 - 3.3V to sensors J6,J7
VP33SESN1 - 3.3V to sensors J8,J9
MGTAVCC10 - 1.0 V, linear from VP18 (pgood controls MGTAVTT12)
MGTAVTT12 - 1.2 V, linear from VP18 (pgood available, means both)
LTC3589 used channels : LDO1, SW1, SW2, SW3, BB
TODO: Change VCC_SENS01_R1, VCC_SENS23_R1 to 787K (now 487)
*/
/* root directory */
static DEVICE_ATTR(simulate, SYSFS_PERMISSIONS, simulate_show, simulate_store);
static DEVICE_ATTR(output_state, SYSFS_PERMISSIONS & SYSFS_READONLY, outputs_all_show, NULL);
static DEVICE_ATTR(configs, SYSFS_PERMISSIONS & SYSFS_READONLY, configs_all_show, NULL);
static DEVICE_ATTR(channels_en, SYSFS_PERMISSIONS, channels_en_show, channels_en_store);
static DEVICE_ATTR(channels_dis, SYSFS_PERMISSIONS, channels_dis_show, channels_dis_store);
static DEVICE_ATTR(power_good, SYSFS_PERMISSIONS & SYSFS_READONLY, pgood_show, NULL);
static DEVICE_ATTR(power_bad, SYSFS_PERMISSIONS & SYSFS_READONLY, pbad_show, NULL);
static DEVICE_ATTR(enable_por, SYSFS_PERMISSIONS, enable_por_show, enable_por_store);
/*
* Performs power off whtough CHIP2 P0
* examples:
* 1. echo "anything" > power_off - immediate shutdown
* 2. shutdown -hP now - civilized shutdown
*/
static DEVICE_ATTR(power_off, SYSFS_PERMISSIONS , NULL, gpio_poweroff);
/*
* input is hex, set all outputs at once with a mask, 8xMSB - enable mask, 8xLSB - pin values.
* examples:
* 1. echo 0x101 > gpio_10389 - set P0 high, P1-P3 keep
* 2. echo 0xf01 > gpio_10389 - set P0 high, P1-P3 - low
* 3. echo 0x605 > gpio_10389 - P0 keep, P1 low, P2 high, P3 keep
* P7-P4 - NC, 0xX0X0 - not supported even on this driver level.
*/
static DEVICE_ATTR(gpio_10389, SYSFS_PERMISSIONS, gpio_10389_get, gpio_10389_set);
static struct attribute *root_dev_attrs[] = {
&dev_attr_simulate.attr,
&dev_attr_output_state.attr,
&dev_attr_configs.attr,
&dev_attr_channels_en.attr,
&dev_attr_channels_dis.attr,
&dev_attr_power_good.attr,
&dev_attr_power_bad.attr,
&dev_attr_enable_por.attr,
&dev_attr_power_off.attr,
&dev_attr_gpio_10389.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int make_group (struct device *dev, const char * name,
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf),
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count))
{
int retval=-1;
int index;
struct attribute **pattrs; /* array of pointers to attibutes */
struct device_attribute *dev_attrs;
struct attribute_group *attr_group;
pattrs = devm_kzalloc(dev,(ARRAY_SIZE(voltage_reg)+1)*sizeof(pattrs[0]), GFP_KERNEL);
if (!pattrs) return -ENOMEM;
dev_attrs = devm_kzalloc(dev, ARRAY_SIZE(voltage_reg)*sizeof(dev_attrs[0]), GFP_KERNEL);
if (!dev_attrs) return -ENOMEM;
attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL);
if (!attr_group) return -ENOMEM;
memset(dev_attrs, 0, ARRAY_SIZE(voltage_reg)*sizeof(dev_attrs[0]));
memset(attr_group, 0, sizeof(*attr_group));
for (index=0;indexname = name;
attr_group->attrs =pattrs;
dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj));
if (&dev->kobj) {
retval = sysfs_create_group(&dev->kobj, attr_group);
}
return retval;
}
static ssize_t simulate_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
return sprintf(buf, "%d\n",clientdata->simulate);
}
static ssize_t simulate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
sscanf(buf, "%du", &clientdata->simulate);
ltc3589_set_simulate(ltc3589_client, clientdata->simulate);
return count;
}
static ssize_t outputs_all_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, pg;
char * cp = buf;
for (chn=0;chn0)?get_pgood(dev, chn):-1;
buf+=sprintf(buf,"%s: %s %d mV%s\n",
voltage_reg[chn].name,
get_enable(dev, chn)?"ON":"OFF",
get_volt_mv(dev, chn),
(pg==1)?", power good":((pg==0)?", power is NOT good":"")
);
}
return buf-cp;
}
static ssize_t configs_all_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, pg;
char * cp = buf;
for (chn=0;chnattr.name);
if (chn<0) return chn;
pg=get_pgood(dev, chn);
return sprintf(buf,"%s: %s %d mV, %s\n",
voltage_reg[chn].name,
get_enable(dev, chn)?"ON":"OFF",
get_volt_mv(dev, chn),
(pg=1)?"power good":((pg==0)?"power is NOT good":"")
);
}
#endif
static ssize_t output_en_output_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
return sprintf(buf,"%d\n", get_enable(dev, chn));
}
static ssize_t output_en_output_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, enable;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
sscanf(buf, "%du", &enable);
return count;
}
static ssize_t outputs_pgood_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
return sprintf(buf,"%d\n", get_pgood(dev, chn));
}
static ssize_t channels_en_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn_bits;
char * cp=buf;
chn_bits=get_enabled_mask(dev);
if (chn_bits<0) return chn_bits;
buf+=list_chn_bits(buf, chn_bits);
buf+=sprintf(buf,"\n");
return buf-cp;
}
/* also slews DAC(s) if applilcable. Call after changing voltage on enabled channels */
static ssize_t channels_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn_bits,rc,old_dis_por,pre_disabled;
chn_bits=parse_chn_bits(buf);
pre_disabled=get_and_disable_por(dev, chn_bits, &old_dis_por);
if (pre_disabled<0) return pre_disabled;
rc=slew_by_mask(dev, chn_bits); /* slew if needed - before enabling, waits for slew over */
if (rc<0) return rc;
rc=set_enabled_by_mask(dev, chn_bits, 1);
if (rc<0) return rc;
if (pre_disabled && (old_dis_por==0)){
rc=reenable_por(dev); /* will wait pgood */
if (rc<0) return rc;
}
return count;
}
static ssize_t channels_dis_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn_bits;
char * cp=buf;
chn_bits=get_enabled_mask(dev);
if (chn_bits<0) return chn_bits;
chn_bits=~chn_bits;
buf+=list_chn_bits(buf, chn_bits);
buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t channels_dis_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn_bits,rc;
chn_bits=parse_chn_bits(buf);
rc=set_enabled_by_mask(dev, chn_bits, 0);
if (rc<0) return rc;
return count;
}
static ssize_t output_ref_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
return sprintf(buf,"%d\n",get_volt_mv(dev, chn));
}
static ssize_t output_ref_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, v_mv;
int rc,old_dis_por,pre_disabled;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
/* if output was enabled, and pgood negation may cause POR, disable POR (later restore) */
if (get_enable(dev,chn)) pre_disabled=get_and_disable_por(dev, 1<0) pgood_bits |= (1<pwr_gpio[gpio_disable_por_index].out_val)?0:1);
}
/* When enable_por is set to 1, it first waits for PGOOD and does not enable POR on error */
static ssize_t enable_por_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int en_por,rc;
sscanf(buf, "%du", &en_por);
if (en_por) rc=reenable_por(dev); /* will wait pgood, then enable POR */
else rc=por_ctrl(dev, 1); /* disable POR */
if (rc<0) return rc;
return count;
}
static ssize_t gpio_10389_set(struct device *dev, struct device_attribute *attr, char *buf, size_t count)
{
int result;
int value;
sscanf(buf, "%i", &value);
result = gpio_10389_ctrl(dev,value);
if (result<0) return result;
return count;
}
/* hardcoded to be [19:16] in pwr_gpio */
static ssize_t gpio_10389_get(struct device *dev, struct device_attribute *attr, char *buf)
{
int i;
unsigned int res=0;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
// just 4 of them
for (i=16;i<20;i++){
if (pwr_gpio[i].label){
res += ((clientdata->pwr_gpio[i].out_val)?1:0)<<(i-16);
}
}
return sprintf(buf,"%02x\n",res);
}
static ssize_t gpio_poweroff(struct device *dev, struct device_attribute *attr, char *buf, size_t count)
{
int rc=gpio_shutdown(dev);
if (rc<0) return rc;
return count;
}
int gpio_shutdown(struct device *dev)
{
int gpio_shutdown_index=get_gpio_index_by_name("NSHUTDOWN");
if (gpio_shutdown_index<0) return gpio_shutdown_index;
return gpio_conf_by_index(dev, gpio_shutdown_index, 1, 0);
}
//TODO: test mutex_lock/unlock works
int gpio_10389_ctrl(struct device *dev, int value){
int i, res;
int val = 0;
mutex_lock(&gpio_10389_lock);
for(i=16;i<20;i++){
if ((value>>(i-8))&0x1){
val = (value>>(i-16))&0x1;
//res = gpio_conf_by_index(dev, i, 1, ~val);
//if (res<0) return res;
res = gpio_conf_by_index(dev, i, 1, val);
if (res<0) return res;
}
}
mutex_unlock(&gpio_10389_lock);
return 0;
}
int gpio_10389_control(int value){
gpio_10389_ctrl(shutdown_dev,value);
return 0;
}
EXPORT_SYMBOL_GPL(gpio_10389_control);
int por_ctrl(struct device *dev, int disable_por)
{
int gpio_disable_por_index=get_gpio_index_by_name("DIS_POR");
if (gpio_disable_por_index<0) return gpio_disable_por_index;
return gpio_conf_by_index(dev, gpio_disable_por_index, 1, disable_por);
}
/*
* disable POR (if needed) before changing value or enabling one of the voltages
* chn_bits - 1 bit per channel
*/
static int get_and_disable_por(struct device *dev, int chn_bits, int * old_dis_por)
{
int rc,chn;
int gpio_disable_por_index;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
gpio_disable_por_index=get_gpio_index_by_name("DIS_POR");
if (gpio_disable_por_index<0) return gpio_disable_por_index;
old_dis_por[0]=clientdata->pwr_gpio[gpio_disable_por_index].out_val;
for (chn=0;chn=ARRAY_SIZE(voltage_reg)) return 0; /* POR was not required to be disabled */
rc = gpio_conf_by_index(dev, gpio_disable_por_index, 1, 1); /* out turn on "disable_por" */
if (rc<0) return rc;
return 1; /* pgood-based POR was disabled (could already be disabled)*/
}
/* call if POR was diasabled before changing voltage (value or enabling), after waiting for pgood*/
static int reenable_por(struct device *dev)
{
int gpio_disable_por_index, rc;
gpio_disable_por_index=get_gpio_index_by_name("DIS_POR");
if (gpio_disable_por_index<0) return gpio_disable_por_index;
if (((rc=wait_all_pgood(dev)))<0) return rc;
return gpio_conf_by_index(dev, gpio_disable_por_index, 1, 0); /* out turn off "disable_por" */
}
static int wait_all_pgood(struct device *dev)
{
int ntry,chn,all_good=0;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
for (ntry=0;ntrypgoot_timeout;ntry++){
all_good=1;
for (chn=0;chn0) && (get_pgood(dev,chn)!=1)){ /* enabled or always enabled */
all_good=0;
break;
}
}
if (all_good) break; /* all enabled channels that have pgood control are good */
}
if (!all_good) return -EAGAIN;
return 0;
}
static int list_chn_bits(char * buf, int chn_bits)
{
int chn;
char * cp=buf;
for (chn=0;chn0) en_mask|= (1<0){
awe |= voltage_reg[chn].awe_en;
}
}
awe &= 0xff; /* just WE mask */
if (awe){
dev_dbg(dev,"set_enabled_by_mask(), cumulative awe=0x%x\n",awe);
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
oven=ltc3589_read_field (ltc3589_client, LTC3589_AWE_OVEN);
if (oven<0) return oven;
if (enable) oven |= awe;
else oven &= ~awe;
return ltc3589_write_field (ltc3589_client, oven, LTC3589_AWE_OVEN);
}
return 0;
}
static int slew_by_mask(struct device *dev, int chn_bits)
{
/* assuming all slew bits in LTC3589 to be in a single register (LTC3589_AWE_OVEN) */
int chn, slew=0,rc,ntry;
u32 adwe;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client;
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
dev_dbg(dev,"slew_by_mask(dev,0x%x)\n",chn_bits);
for (chn=0;chn%d (slew = 0x%x)\n",adwe,rc,slew);
if (rc<0) return rc;
/* wait slew over */
for (ntry=0;ntrypgoot_timeout;ntry++){
rc=ltc3589_read_field(ltc3589_client, LTC3589_AWE_VCCR);
dev_dbg(dev,"slew_by_mask():ltc3589_read_field(ltc3589_client, 0x%x)->0x%x(%d)\n",LTC3589_AWE_VCCR,rc,rc);
if (rc<0) return rc;
if ((rc & slew) ==0 ) break;
}
if (ntry>=clientdata->pgoot_timeout) return -EAGAIN;
}
return 0;
}
/* name should either completely match, or have "_*" suffix */
static int get_voltage_channel(const char * name)
{
int i;
for (i=0;i=ARRAY_SIZE(clientdata->pwr_gpio))) return -EINVAL;
if ((clientdata->pwr_gpio[gpio_index].dir==dir) && ((clientdata->pwr_gpio[gpio_index].out_val==val) || (dir==0))){
dev_dbg(dev,"GPIO#%d(index=%d) did not change: old dir=%d, new dir=%d, old val = %d, new val=%d\n",
clientdata->pwr_gpio[gpio_index].pin,
gpio_index,
clientdata->pwr_gpio[gpio_index].dir,
dir,
clientdata->pwr_gpio[gpio_index].out_val,
val);
return 0;
}
clientdata->pwr_gpio[gpio_index].dir=dir?1:0;
clientdata->pwr_gpio[gpio_index].out_val=val?1:0;
if (clientdata->pwr_gpio[gpio_index].dir){
if (!clientdata->simulate) rc=gpio_direction_output(clientdata->pwr_gpio[gpio_index].pin, clientdata->pwr_gpio[gpio_index].out_val);
dev_dbg(dev,"gpio_direction_output(%d,%d)->%d\n",clientdata->pwr_gpio[gpio_index].pin, clientdata->pwr_gpio[gpio_index].out_val,rc);
} else {
if (!clientdata->simulate) rc=gpio_direction_input(clientdata->pwr_gpio[gpio_index].pin);
dev_dbg(dev,"gpio_direction_input(%d)->%d\n",clientdata->pwr_gpio[gpio_index].pin,rc);
}
return rc;
}
static int get_gpio_pwr_mgx_indices(int chn, int * indices) /* chn = 0 (VP10) or 1 (VP18) */
{
indices[0]=get_gpio_index_by_name(chn?"PWR_MG1": "PWR_MG0");
indices[1]=get_gpio_index_by_name(chn?"PWR_MGB1":"PWR_MGB0");
return ((indices[0]>=0) && (indices[1]>=0))?0:-EINVAL;
}
/* calculate output voltage in mV */
static int get_volt_mv(struct device *dev, int chn)
{
int v_mv,ref,rc;
int pwr_mg_indices[2];
s64 num;
struct i2c_client *ltc3589_client;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
if (voltage_reg[chn].r1<=0) {
if (voltage_reg[chn].awe_ref<0) { /* vp10, vp18*/
rc= get_gpio_pwr_mgx_indices(-1-voltage_reg[chn].awe_ref,pwr_mg_indices); /* chn = 0 (VP10) or 1 (VP18) */
if (rc<0) return rc;
if (clientdata->pwr_gpio[pwr_mg_indices[0]].dir==0) ref=0;
else if (clientdata->pwr_gpio[pwr_mg_indices[0]].out_val) ref=1;
else ref=-1;
if (ref) {
if (clientdata->pwr_gpio[pwr_mg_indices[1]].dir==0) ref*=15;
else if (clientdata->pwr_gpio[pwr_mg_indices[1]].out_val) ref*=10;
else ref*= 5;
}
v_mv=(voltage_reg[chn].r2*(100+ref)*2+10)/2000;
} else { /* vp33sens01, vp33sens23, mmtavcc10, mmtavtt12 */
v_mv=(voltage_reg[chn].r2+5)/10;
}
} else if (voltage_reg[chn].awe_ref==0){ /* VP5, vldo18 */
#if 0
v_mv=(REF_FIXED_TENTH_MV*(voltage_reg[chn].r1+voltage_reg[chn].r2)+ 5*voltage_reg[chn].r2)/(10*voltage_reg[chn].r2);
#endif
num=((u64) REF_FIXED_TENTH_MV)* (voltage_reg[chn].r1+voltage_reg[chn].r2)+ 5*voltage_reg[chn].r2;
v_mv=(int) div64_u64(num, 10*voltage_reg[chn].r2);
dev_dbg(dev,"chn=%d REF_FIXED_TENTH_MV=%d .r1=%d .r2=%d v_mv=%d\n",chn, REF_FIXED_TENTH_MV,voltage_reg[chn].r1,voltage_reg[chn].r2,v_mv);
} else { /* vp15, vcc_sens01,vcc_sens23 */
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
ref=ltc3589_read_field(ltc3589_client, voltage_reg[chn].awe_ref);
if (ref<0) return ref;
num=(REF_VAR_0_TENTH_MV+ REF_VAR_STEP_TENTH_MV* ref);
num=num*(voltage_reg[chn].r1+voltage_reg[chn].r2)+ 5*voltage_reg[chn].r2;
v_mv=div64_u64(num, 10*voltage_reg[chn].r2);
dev_dbg(dev,"chn=%d ref=%d .r1=%d .r2=%d v_mv=%d\n",chn, ref,voltage_reg[chn].r1,voltage_reg[chn].r2,v_mv);
}
return v_mv;
}
/* 0 - OK, <0 - error */
/* does not iclude disabling/re-enabling PoR */
static int set_volt_mv(struct device *dev, int chn, int v_mv)
{
int rc,index,d;
s64 num;
int pwr_mg_indices[2];
struct i2c_client *ltc3589_client;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
dev_dbg(dev,"set_volt_mv(dev,%d,%d),.r1=%d\n",chn,v_mv,voltage_reg[chn].r1);
if (voltage_reg[chn].r1<=0) {
if (voltage_reg[chn].awe_ref<0) { /* vp10, vp18*/
index=(400*v_mv+voltage_reg[chn].r2)/(2*voltage_reg[chn].r2);
dev_dbg(dev,"chn=%d v_mv=%d index=%d .r1=%d .r2=%d\n",chn, v_mv, index,voltage_reg[chn].r1,voltage_reg[chn].r2);
if ((index<17) || (index>23)) {
dev_err(dev,"specified voltage for %s is not in the range %dmV to %d mV\n", voltage_reg[chn].name,
(17*voltage_reg[chn].r2)/200,(23*voltage_reg[chn].r2)/200);
return -EINVAL;
}
/* disable -> chnage -> enable (if needed) */
rc= get_gpio_pwr_mgx_indices(-1-voltage_reg[chn].awe_ref,pwr_mg_indices); /* chn = 0 (VP10) or 1 (VP18) */
if (rc<0) return rc;
rc = gpio_conf_by_index(dev,pwr_mg_indices[0], 0, 0); /* disable margining */
if (rc < 0)return rc;
if (index !=20){
/* set margining absolute value */
switch (index) {
case 17:
case 23:
rc = gpio_conf_by_index(dev,pwr_mg_indices[1], 0, 0); /* float: +/- 15% */
break;
case 18:
case 22:
rc = gpio_conf_by_index(dev,pwr_mg_indices[1], 1, 1); /* out 1: +/- 10% */
break;
case 19:
case 21:
rc = gpio_conf_by_index(dev,pwr_mg_indices[1], 1, 0); /* out 0: +/- 5% */
break;
}
if (rc < 0)return rc;
/* set margining sign */
if (index >20) rc = gpio_conf_by_index(dev,pwr_mg_indices[0], 1, 1); /* out 1: positive margining */
else rc = gpio_conf_by_index(dev,pwr_mg_indices[0], 1, 0); /* out 0: negative margining */
if (rc < 0)return rc;
}
} else { /* vp33sens01, vp33sens23, mmtavcc10, mmtavtt12 */
return -EINVAL; /* voltage not regulated */
}
} else if (voltage_reg[chn].awe_ref==0){ /* VP5, vldo18 */
return -EINVAL; /* voltage not regulated */
} else { /* vp15, vcc_sens01,vcc_sens23 */
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
#if 0
index=((10*v_mv*voltage_reg[chn].r2) -(REF_VAR_0_TENTH_MV-REF_VAR_STEP_TENTH_MV/2)*(voltage_reg[chn].r1+voltage_reg[chn].r2))/
((voltage_reg[chn].r1+voltage_reg[chn].r2)*REF_VAR_STEP_TENTH_MV);
num=(10*v_mv*voltage_reg[chn].r2) -(REF_VAR_0_TENTH_MV-REF_VAR_STEP_TENTH_MV/2);
num*=(voltage_reg[chn].r1+voltage_reg[chn].r2);
index=div64_u64(num, (voltage_reg[chn].r1+voltage_reg[chn].r2)*REF_VAR_STEP_TENTH_MV);
#endif
num= (10LL*v_mv*voltage_reg[chn].r2) - ((s64) (voltage_reg[chn].r1+voltage_reg[chn].r2))*REF_VAR_0_TENTH_MV;
d= REF_VAR_STEP_TENTH_MV*(voltage_reg[chn].r1+voltage_reg[chn].r2);
index=div64_u64(num +(d>>1), d);
dev_dbg(dev,"chn=%d v_mv=%d index=%d .r1=%d .r2=%d\n",chn, v_mv, index,voltage_reg[chn].r1,voltage_reg[chn].r2);
dev_dbg(dev,"index=%d\n",index);
if ((index<0) || (index>31)){
dev_err(dev,"chn=%d v_mv=%d index=%d .r1=%d .r2=%d\n",chn, v_mv, index,voltage_reg[chn].r1,voltage_reg[chn].r2);
dev_err(dev,"REF_VAR_0_TENTH_MV=%d REF_VAR_STEP_TENTH_MV=%d\n",REF_VAR_0_TENTH_MV,REF_VAR_STEP_TENTH_MV);
dev_err(dev,"specified voltage for %s is not in the range %dmV to %d mV\n", voltage_reg[chn].name,
(int) div64_u64((((u64)(REF_VAR_0_TENTH_MV+REF_VAR_STEP_TENTH_MV* 0))*(voltage_reg[chn].r1+voltage_reg[chn].r2)+5*voltage_reg[chn].r2),
10*voltage_reg[chn].r2),
(int) div64_u64((((u64)(REF_VAR_0_TENTH_MV+REF_VAR_STEP_TENTH_MV*31))*(voltage_reg[chn].r1+voltage_reg[chn].r2)+5*voltage_reg[chn].r2),
10*voltage_reg[chn].r2));
return -EINVAL;
}
dev_dbg(dev,"ltc3589_client->name= %s\n", ltc3589_client->name);
rc=ltc3589_write_field(ltc3589_client, index,voltage_reg[chn].awe_ref);
if (rc<0) return rc;
}
return 0;
}
/* get output enable state */
static int get_enable(struct device *dev, int chn)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
if (voltage_reg[chn].awe_en==0) {
return 2; /* always on */
} else if (voltage_reg[chn].awe_en>0){
if (clientdata->pinstrapped_oven & voltage_reg[chn].awe_en) return 1; /* pin-strapped on bit */
return ltc3589_read_field(ltc3589_client, voltage_reg[chn].awe_en);
} else {
return (clientdata->pwr_gpio[-1-voltage_reg[chn].awe_en].dir && clientdata->pwr_gpio[-1-voltage_reg[chn].awe_en].out_val)?1:0;
}
}
/* set output enable state */
static int set_enable(struct device *dev, int chn, int enable)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
if (voltage_reg[chn].awe_en==0) {
return -EINVAL; /* always on, not controlled */
} else if (voltage_reg[chn].awe_en>0){
return ltc3589_write_field(ltc3589_client, enable, voltage_reg[chn].awe_en);
} else {
return gpio_conf_by_index(dev,-1-voltage_reg[chn].awe_en, 1, enable);
}
}
/* get power good state */
static int get_pgood(struct device *dev, int chn)
{
int rc;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
if (voltage_reg[chn].awe_pgood==0) {
if (((rc=get_enable(dev,chn)))<0) return rc; /* 0 - disabled */
return 2; /* no status available */
} else if (voltage_reg[chn].awe_pgood>0){
return ltc3589_read_field(ltc3589_client, voltage_reg[chn].awe_pgood);
} else {
/* return gpio_get_value(clientdata->pwr_gpio[-1-voltage_reg[chn].awe_pgood].pin); */
return gpio_get_value_cansleep(clientdata->pwr_gpio[-1-voltage_reg[chn].awe_pgood].pin);
}
}
static int elphel393_pwr_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
if (((retval = make_group (dev, "voltages_mv", output_ref_show, output_ref_store)))<0) return retval;
if (((retval = make_group (dev, "outputs_en", output_en_output_show, output_en_output_store)))<0) return retval;
if (((retval = make_group (dev, "outputs_pgood", outputs_pgood_show, NULL)))<0) return retval;
}
return retval;
}
static void elphel393_pwr_init_of_i2caddr(struct platform_device *pdev)
{
const __be32 * config_data;
int len,i;
struct device_node *node = pdev->dev.of_node;
struct elphel393_pwr_data_t *clientdata = platform_get_drvdata(pdev);
if (node) {
config_data = of_get_property(node, "elphel393_pwr,i2c_chips", &len);
if (config_data){
len /= sizeof(*config_data);
dev_dbg(&pdev->dev,"Found %d items in 'elphel393_pwr,i2c_chips' in the Device Tree\n",len);
if (len!= ARRAY_SIZE(clientdata->chip_i2c_addr)){
dev_err(&pdev->dev,"Got %d items in 'elphel393_pwr,i2c_chips', expected %d\n",len,ARRAY_SIZE(clientdata->chip_i2c_addr));
return;
}
for (i=0;ichip_i2c_addr[i]=be32_to_cpup(&config_data[i]);
}
}
}
static void elphel393_pwr_init_of(struct platform_device *pdev)
{
const __be32 * config_data;
const char * config_string;
char str[40];
int len,chn,pre_disabled,old_dis_por,rc,chn_bits;
struct device_node *node = pdev->dev.of_node;
struct elphel393_pwr_data_t *clientdata = platform_get_drvdata(pdev);
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
if (node) {
/* find resistor values */
for (chn=0;chn0)){
dev_dbg(&pdev->dev,"Found %s=<%d>\n",str,be32_to_cpup(&config_data[0]));
voltage_reg[chn].r1=be32_to_cpup(&config_data[0]);
}
sprintf(str,"elphel393_pwr,%s.r2",voltage_reg[chn].name);
config_data = of_get_property(node, str, &len);
if (config_data && (len>0)){
dev_dbg(&pdev->dev,"Found %s=<%d>\n",str,be32_to_cpup(&config_data[0]));
voltage_reg[chn].r2=be32_to_cpup(&config_data[0]);
}
}
/* which channels are enabled by pin-strapping */
config_data = of_get_property(node, "elphel393_pwr,pinstrapped_oven", &len);
if (config_data && (len>0)){
dev_dbg(&pdev->dev,"Found elphel393_pwr,pinstrapped_oven=<%d>\n",be32_to_cpup(&config_data[0]));
clientdata->pinstrapped_oven=be32_to_cpup(&config_data[0]);
}
/* debug mode - simulate only, no actual power supply control */
config_data = of_get_property(node, "elphel393_pwr,simulate", &len);
if (config_data && (len>0)){
dev_dbg(&pdev->dev,"Found elphel393_pwr,simulate=<%d>\n",be32_to_cpup(&config_data[0]));
clientdata->simulate=config_data[0]?1:0;
ltc3589_set_simulate(ltc3589_client, clientdata->simulate);
}
/* disable output voltages (not likely to be needed - maybe for warm reboot) */
config_string = of_get_property(node, "elphel393_pwr,channels_disable", &len);
if (config_string){
dev_dbg(&pdev->dev,"Found elphel393_pwr,channels_disable=\"%s\"\n",config_string);
chn_bits=parse_chn_bits(config_string);
rc=set_enabled_by_mask(&pdev->dev, chn_bits, 0);
if (rc<0) return;
}
/* set output voltages (target voltages, in mV) */
for (chn=0;chn0)){
dev_dbg(&pdev->dev,"Found %s=<%d>\n",str,be32_to_cpup(&config_data[0]));
if (get_enable(&pdev->dev,chn)) pre_disabled=get_and_disable_por(&pdev->dev, 1<dev,"pre_disabled=%d\n",pre_disabled);
rc=set_volt_mv(&pdev->dev, chn,be32_to_cpup(&config_data[0]));
dev_dbg(&pdev->dev,"set_volt_mv()->%d\n",rc);
if (rc<0) return;
if (pre_disabled && (old_dis_por==0)){
rc=reenable_por(&pdev->dev); /* will wait pgood */
if (rc<0){
dev_err(&pdev->dev,"Timeout during wait for power good after chnging voltage for %s before re-enabling POR on power loss\n",\
voltage_reg[chn].name);
return;
}
}
}
}
/* enable output voltages */
config_string = of_get_property(node, "elphel393_pwr,channels_enable", &len);
if (config_string){
dev_dbg(&pdev->dev,"Found elphel393_pwr,channels_enable=\"%s\"\n",config_string);
chn_bits=parse_chn_bits(config_string);
pre_disabled=get_and_disable_por(&pdev->dev, chn_bits, &old_dis_por);
if (pre_disabled<0) return;
rc=slew_by_mask(&pdev->dev, chn_bits); /* slew if needed - before enabling, waits for slew over */
if (rc<0) {
dev_err(&pdev->dev,"Timeout during wait for slew over\n");
return;
}
rc=set_enabled_by_mask(&pdev->dev, chn_bits, 1);
if (rc<0) return;
if (pre_disabled && (old_dis_por==0)){
rc=reenable_por(&pdev->dev); /* will wait pgood */
if (rc<0) {
dev_err(&pdev->dev,"Timeout during wait for power good before re-enabling POR on power loss\n");
return;
}
}
}
}
dev_info(&pdev->dev,"elphel393_pwr configuration done\n");
}
static int device_by_i2c_addr_match(struct device *dev, void *data)
{
struct i2c_client *client = to_i2c_client(dev);
int *addr = (int *)data;
dev_dbg(dev,"addr_given=0x%02x, addr found=0x%02x\n",addr[0],(int) client->addr);
return i2c_verify_client(dev) && (client->addr==addr[0]);
}
static struct device * find_device_by_i2c_addr(int address)
{
return bus_find_device(&i2c_bus_type, NULL, &address, device_by_i2c_addr_match);
}
static int i2c_addr_gpiochip_match(struct gpio_chip *chip, void *data)
{
struct i2c_client *client = to_i2c_client(chip->dev);
int *addr = (int *)data;
dev_dbg(chip->dev,"addr_given=0x%02x, addr found=0x%02x\n",addr[0],(int) client->addr);
return i2c_verify_client(chip->dev) && (client->addr==addr[0]);
}
static void shutdown(void){
gpio_shutdown(shutdown_dev);
}
static int elphel393_pwr_probe(struct platform_device *pdev)
{
struct gpio_chip *chip;
int i,rc;
struct device *tmp_dev;
struct i2c_client *tmp_i2c_client;
int base[3],dir[3],out_val[3];
struct i2c_client *ltc3589_client;
struct elphel393_pwr_data_t *clientdata = NULL;
struct gpio_desc *desc;
shutdown_dev = &pdev->dev;
dev_info(&pdev->dev,"Probing elphel393-pwr\n");
clientdata = devm_kzalloc(&pdev->dev, sizeof(*clientdata), GFP_KERNEL);
clientdata->pgoot_timeout=DEAFULT_TIMEOUT;
clientdata->pinstrapped_oven=PINSTRAPPED_OVEN;
clientdata->chip_i2c_addr[0]=GPIO_CHIP1_ADDR;
clientdata->chip_i2c_addr[1]=GPIO_CHIP2_ADDR;
clientdata->chip_i2c_addr[2]=GPIO_10389_U4_ADDR;
clientdata->chip_i2c_addr[3]=LTC3589_ADDR;
platform_set_drvdata(pdev, clientdata);
elphel393_pwr_sysfs_register(pdev);
// elphel393_pwr_init_of(pdev);
elphel393_pwr_init_of_i2caddr(pdev);
mutex_init(&clientdata->lock);
/* locate GPIO chips by i2c address */
for (i=0;i<3;i++){
chip = gpiochip_find(&clientdata->chip_i2c_addr[i], i2c_addr_gpiochip_match);
if (chip!=NULL) {
base[i]=chip->base;
dev_dbg(&pdev->dev,"Found gpio_chip with i2c_addr=0x%02x, label=%s, base=0x%x\n",clientdata->chip_i2c_addr[i],chip->label,base[i]);
tmp_dev=find_device_by_i2c_addr(clientdata->chip_i2c_addr[i]);
tmp_i2c_client = to_i2c_client(tmp_dev);
//chip0 and chip1 have registers, chip2 - no regs, only outputs
if (i<2){
//need to invert direction register value
dir[i]=i2c_smbus_read_byte_data(tmp_i2c_client, 0x3)^0xff;
out_val[i]=i2c_smbus_read_byte_data(tmp_i2c_client, 0x1)&0xff;
//dir[i]=0x0;
//out_val[i]=0x0;
pr_debug("chip %d: dir=%d val=%d\n",i,dir[i],out_val[i]);
}else{
dir[i]=0xff;
out_val[i]=i2c_smbus_read_byte(tmp_i2c_client)&0xff;
out_val[i]=0x0;
}
}else{
base[i]=NULL;
}
}
if (base[2]==NULL){
device_remove_file(&pdev->dev, &dev_attr_gpio_10389);
}
for (i=0;i>3]!=NULL) if (pwr_gpio[i].label){
clientdata->pwr_gpio[i].label=pwr_gpio[i].label;
clientdata->pwr_gpio[i].pin=base[i>>3]+(i & 7);
pr_debug("setting gpio %d struct to dir=%d val=%d\n",i,(dir[i>>3]>>(i&7))&0x1,(out_val[i>>3]>>(i&7))&0x1);
clientdata->pwr_gpio[i].dir = (dir[i>>3]>>(i&7))&0x1;
clientdata->pwr_gpio[i].out_val = (out_val[i>>3]>>(i&7))&0x1;
//if (i<16) clientdata->pwr_gpio[i].dir=0; /* input */
//else clientdata->pwr_gpio[i].dir=1; /* output */
//if (i<16) clientdata->pwr_gpio[i].out_val=0;
//else clientdata->pwr_gpio[i].out_val=1;
//clientdata->pwr_gpio[i].out_val=0;
rc=gpio_request(clientdata->pwr_gpio[i].pin, clientdata->pwr_gpio[i].label);
if (rc<0){
dev_err(&pdev->dev," Failed to get GPIO[%d] with label %s\n",clientdata->pwr_gpio[i].pin,clientdata->pwr_gpio[i].label);
return rc;
} else {
dev_dbg(&pdev->dev,"Confirmed request GPIO[%d] with label %s\n",clientdata->pwr_gpio[i].pin,clientdata->pwr_gpio[i].label);
}
}
}
/* find ltc3589 */
clientdata->ltc3489_dev=find_device_by_i2c_addr(LTC3589_ADDR);
if (!clientdata->ltc3489_dev){
dev_err(&pdev->dev," Failed to find LTC3489 with i2c address 0x%02x\n",LTC3589_ADDR);
return -EIO;
}
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
dev_dbg(&pdev->dev,"Located %s with i2c address 0x%02x\n",ltc3589_client->name,LTC3589_ADDR);
dev_dbg(&pdev->dev,"LTC3589 status= 0x%02x\n",ltc3589_read_field(ltc3589_client, LTC3589_AWE_PGSTAT));
elphel393_pwr_init_of(pdev);
/*
* 1. pm_power_off - arch/arm/kernel/process.c - called in the end of halt if power off requested
* 2. To perform a proper system shutdown with power off ("shutdown -hP now") this function is set here.
*/
pm_power_off = shutdown;
/*
if (base[2]!=NULL){
//turn off PCA9571
gpio_10389_ctrl(&pdev->dev, 0xf0f);
gpio_10389_ctrl(&pdev->dev, 0xf00);
}
*/
return 0;
}
static int elphel393_pwr_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev,"Removing elphel393-pwr");
return 0;
}
static struct of_device_id elphel393_pwr_of_match[] = {
{ .compatible = "elphel,elphel393-pwr-1.00", },
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, elphel393_pwr_of_match);
static struct platform_driver elphel393_pwr = {
.probe = elphel393_pwr_probe,
.remove = elphel393_pwr_remove,
.driver = {
.name = "elphel393-pwr",
.owner = THIS_MODULE,
.of_match_table = elphel393_pwr_of_match,
.pm = NULL, /* power management */
},
};
module_platform_driver(elphel393_pwr);
MODULE_AUTHOR("Andrey Filippov ");
MODULE_DESCRIPTION("Elphel 10393 power supply control");
MODULE_LICENSE("GPL");
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/exif.h 0000664 0000000 0000000 00000001776 12762437450 0026035 0 ustar 00root root 0000000 0000000 #ifndef __F_EXIF__H_
#define __F_EXIF__H_
extern unsigned char exif_header[];
int exif_header_length(void);
#define EXIF_OFFSET 4
#define EXIF_FIRMWARE 0xC4
#define EXIF_FIRMWARE_LEN 27
//#define EXIF_DATE_TIME 0x7A
#define EXIF_DATE_TIME 0xE0
#define EXIF_DATE_TIME_LEN 20
//#define EXIF_ARTIST 0x8E
#define EXIF_ARTIST 0xF4
#define EXIF_ARTIST_LEN 18
//#define EXIF_DATE_TIME_OR 0xCA
#define EXIF_DATE_TIME_OR 0x0138
#define EXIF_DATE_TIME_OR_LEN 20
//#define EXIF_SUBSEC_OR 0xDE
#define EXIF_SUBSEC_OR 0x014C
#define EXIF_SUBSEC_OR_LEN 7
//#define EXIF_EXP 0xE6
#define EXIF_EXP 0x0130
#define EXIF_EXP_LEN 8
#define EXIF_IMAGE_ID 0x6E
#define EXIF_IMAGE_ID_LEN 64
struct exif_desc_t {
unsigned char date_time[EXIF_DATE_TIME_LEN];
unsigned char date_time_or[EXIF_DATE_TIME_OR_LEN];
unsigned char subsec[EXIF_SUBSEC_OR_LEN];
unsigned char artist[EXIF_ARTIST_LEN];
unsigned char firmware[EXIF_FIRMWARE_LEN];
unsigned long exp[2];
};
extern struct exif_desc_t exif_desc;
#endif //__F_EXIF__H_
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/exif393.c 0000664 0000000 0000000 00000065246 12762437450 0026271 0 ustar 00root root 0000000 0000000 /*!********************************************************************************
*! FILE NAME : exif353.c
*! DESCRIPTION: Drivers for Exif manipulation
*! Copyright (C) 2008 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*!
*! 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 .
*! -----------------------------------------------------------------------------**
*! $Log: exif353.c,v $
*! Revision 1.2 2009/02/18 06:27:34 elphel
*! fixed system date calculation fro Exif (was 2 days ahead in the years after leap)
*!
*! Revision 1.1.1.1 2008/11/27 20:04:00 elphel
*!
*!
*! Revision 1.3 2008/09/20 00:29:49 elphel
*! moved driver major/minor numbers to a single file - include/asm-cris/elphel/driver_numbers.h
*!
*! Revision 1.2 2008/09/12 00:23:59 elphel
*! removed cc353.c, cc353.h
*!
*! Revision 1.4 2008/04/25 21:28:56 elphel
*! added more functions for access from the IRQ, changed them to inline
*!
*! Revision 1.3 2008/04/11 23:16:51 elphel
*! removed unneeded local_irq_disable() after local_irq_save_flags()
*!
*! Revision 1.2 2008/04/07 09:45:30 elphel
*! removed unused variable
*!
*! Revision 1.1 2008/04/07 09:09:07 elphel
*! New driver to handle Exif
*!
*!
*/
//copied freom cxi2c.c - TODO:remove unneeded
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
//#include
//#include
#include // endians
//#include
#include
#include
#include
#include
#include
#include
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//
//#include "x3x3.h"
//#include "cc3x3.h"
#include "exif393.h"
#define D(x)
//#define D(x) printk(">>> %s:%d:",__FILE__,__LINE__);x
//Major
//#define X3X3_EXIF 136
//Minors
//#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
//#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
// control/setup devices
//#define X3X3_EXIF_TEMPLATE 2 // write Exif template
//#define X3X3_EXIF_METADIR 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
// those 2 files will disable exif_enabled and exif_valid, truncate file size to file pointer on release.
//#define X3X3_EXIF_TIME 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
// midnight (00:00:00) in seconds from epoch (long, startting from LSB)
#define X3X3_EXIF_DRIVER_NAME "Elphel (R) model 353 Exif device driver"
static DEFINE_SPINLOCK(lock);
//#define MAX_EXIF_FIELDS 256 // number of Exif tags in the header
//#define MAX_EXIF_SIZE 4096 // Exif data size
static struct exif_dir_table_t dir_table[MAX_EXIF_FIELDS];
static int exif_fields = 0; // total number of the Exif fields in the header
static int exif_template_size = 0; // size of Exif template
static char exif_template[MAX_EXIF_SIZE];
static int aexif_meta_size[SENSOR_PORTS] = {0,0,0,0}; // size of Exif meta data page (is it the same for all ports?) 393: set as individual
static int aexif_wp[SENSOR_PORTS] = {1,1,1,1}; // frame write pointer in the meta_buffer
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 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.
// page 0 - temporary storage, 1..MAX_EXIF_FRAMES - buffer
// Common for all sensor ports
struct exif_time_t {
char tomorrow_date[10]; //!"YYYY:MM:DD"
unsigned long tomorrow_sec; //!seconds from epoch tomorrow at 00:00
char today_date[10]; //!"YYYY:MM:DD"
unsigned long today_sec; //!seconds from epoch today at 00:00
} exif_time;
static struct exif_datetime_t {
char datetime[20]; //!"YYYY:MM:DD HH:MM:SS\0"
char subsec[7]; //!ASCII microseconds (0-padded), ."\0"
} now_datetime;
static int exif_open (struct inode *inode, struct file *filp);
static int exif_release(struct inode *inode, struct file *filp);
static loff_t exif_lseek (struct file * file, loff_t offset, int orig);
static ssize_t exif_write (struct file * file, const char * buf, size_t count, loff_t *off);
static ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *off);
static int __init exif_init(void);
static struct file_operations exif_fops = {
owner: THIS_MODULE,
open: exif_open,
release: exif_release,
read: exif_read,
write: exif_write,
llseek: exif_lseek
};
ssize_t minor_file_size(int minor) { //return current file size for different minors
int sensor_port;
switch (minor) {
case X3X3_EXIF_TEMPLATE:
return exif_template_size;
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
sensor_port = minor - X3X3_EXIF_EXIF_CHN_0;
return aexif_enabled[sensor_port]? (exif_template_size * (MAX_EXIF_FRAMES+1)):0;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
sensor_port = minor - X3X3_EXIF_META_CHN_0;
return aexif_meta_size[sensor_port];
case X3X3_EXIF_METADIR:
return exif_fields * sizeof(struct exif_dir_table_t);
case X3X3_EXIF_TIME:
return sizeof(struct exif_time_t);
default:return 0;
}
}
ssize_t minor_max_size(int minor) { //return max file size for different minors
switch (minor) {
case X3X3_EXIF_TEMPLATE:
return MAX_EXIF_SIZE;
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
return MAX_EXIF_SIZE * (MAX_EXIF_FRAMES+1);
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
return MAX_EXIF_SIZE;
case X3X3_EXIF_METADIR:
return MAX_EXIF_FIELDS * sizeof(struct exif_dir_table_t);
case X3X3_EXIF_TIME:
return sizeof(struct exif_time_t);
default:
return 0;
}
}
void exif_invalidate(void) { // 393: OK, only invalidates all ayt once
int sensor_port;
for (sensor_port =0; sensor_port < SENSOR_PORTS; sensor_port++){
aexif_enabled[sensor_port] = 0;
aexif_valid[sensor_port] = 0;
}
}
//reallocate meta buffer to store per-frame meta data (later output as Exif)
// 393: Make both individual and all at once
int exif_rebuild(int frames) {
int sensor_port,rslt;
for (sensor_port =0; sensor_port < SENSOR_PORTS; sensor_port++){
if ((rslt = exif_rebuild_chn(sensor_port, frames)) <0){
return rslt;
}
}
return 0;
}
int exif_rebuild_chn(int sensor_port, int frames) {
int i,ml;
char * meta_buffer = ameta_buffer[sensor_port];
aexif_enabled[sensor_port] = 0;
aexif_valid[sensor_port] = 0;
aexif_wp[sensor_port] = 1;
// free buffer, if allocated
if (meta_buffer) {
vfree (meta_buffer);
meta_buffer=NULL;
}
// calculate page size
if (exif_fields==0) return 0; // exif_valid==0;
for (i=0; i < exif_fields; i++) {
ml=dir_table[i].src+dir_table[i].len;
if (ml > aexif_meta_size[sensor_port]) aexif_meta_size[sensor_port] = ml;
}
if (aexif_meta_size[sensor_port] > MAX_EXIF_SIZE) {
printk ("%s:%d: Meta frame size (0x%x) is too big (>0x%x)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port], MAX_EXIF_SIZE);
return -1;
}
meta_buffer= vmalloc(aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
if (!meta_buffer) {
printk ("%s:%d: Failed to allocate memory (%d bytes)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
return -1;
}
memset(meta_buffer, 0, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
ameta_buffer[sensor_port] = meta_buffer;
aexif_valid[sensor_port] = 1;
return 0;
}
int exif_enable(int en) {
int sensor_port,rslt;
for (sensor_port =0; sensor_port < SENSOR_PORTS; sensor_port++){
if ((rslt = exif_enable_chn(sensor_port, en)) <0){
return rslt;
}
}
return 0;
}
int exif_enable_chn(int sensor_port, int en) {
int rslt;
if (en) {
if (!aexif_valid[sensor_port]) {
if (((rslt=exif_rebuild_chn(sensor_port, MAX_EXIF_FRAMES))) <0) return rslt;
}
aexif_enabled[sensor_port] = 1;
} else {
aexif_enabled[sensor_port] = 0;
}
return 0;
}
int dir_find_tag (unsigned long tag) { //find location of the tag field in meta page
int indx;
for (indx=0; indx < exif_fields; indx++) if (dir_table[indx].ltag==tag) return (int) dir_table[indx].src;
return -1;
}
inline void write_meta_raw_irq(int sensor_port, char * data, int offset, int len) { //write data to meta, called from IRQ
if (aexif_enabled[sensor_port])
memcpy(&ameta_buffer[sensor_port][offset], data, len);
}
inline int write_meta_irq(int sensor_port, char * data, int * indx, unsigned long ltag, int len) { //write data to meta, called from IRQ(len==0 => use field length)
int i;
if (!aexif_enabled[sensor_port]) return 0;
if (indx && (dir_table[* indx].ltag==ltag)) i=*indx;
else {
for (i=0; i=exif_fields) return -1; //ltag not found
}
if (len==0) len=dir_table[i].len;
memcpy(&ameta_buffer[sensor_port][dir_table[i].src], data, len);
if (indx) * indx = i;
return dir_table[i].src;
}
inline void putlong_meta_raw_irq(int sensor_port, unsigned long data, int offset) { //write data to meta (4 bytes, big endian), called from IRQ
unsigned long bedata=__cpu_to_be32(data);
if (aexif_enabled[sensor_port]) {
memcpy(&ameta_buffer[sensor_port][ offset], &bedata, 4);
}
}
inline int putlong_meta_irq(int sensor_port, unsigned long data, int * indx, unsigned long ltag) { //write data to meta (4 bytes, big endian), from IRQ
int i;
unsigned long bedata=__cpu_to_be32(data);
if (!aexif_enabled[sensor_port]) return -10;
if (indx && (dir_table[* indx].ltag==ltag)) i=*indx;
else {
for (i=0; i=exif_fields) return -1; //ltag not found
}
memcpy(&ameta_buffer[sensor_port][dir_table[i].src], &bedata, 4);
if (indx) * indx=i;
return dir_table[i].src;
}
void write_meta_raw(int sensor_port, char * data, int offset, int len) { //write data to meta, called from outside IRQ (atomic)
unsigned long flags;
if (aexif_enabled[sensor_port]) {
local_irq_save(flags);
//local_irq_disable();
memcpy(&ameta_buffer[sensor_port][ offset], data, len);
local_irq_restore(flags);
}
}
int write_meta(int sensor_port, char * data, int * indx, unsigned long ltag, int len) { //write data to meta, from outside IRQ (atomic) (len==0 => use field length)
int i;
unsigned long flags;
if (!aexif_enabled[sensor_port]) return 0;
if (indx && (dir_table[* indx].ltag==ltag)) i=*indx;
else {
for (i=0; i=exif_fields) return -1; //ltag not found
}
if (len==0) len=dir_table[i].len;
local_irq_save(flags);
memcpy(&ameta_buffer[sensor_port][dir_table[i].src], data, len);
local_irq_restore(flags);
if (indx) * indx=i;
return dir_table[i].src;
}
void putlong_meta_raw(int sensor_port, unsigned long data, int offset) { //write data to meta (4 bytes, big endian), called from outside IRQ (atomic)
unsigned long flags;
unsigned long bedata=__cpu_to_be32(data);
if (aexif_enabled[sensor_port]) {
local_irq_save(flags);
//local_irq_disable();
memcpy(&ameta_buffer[sensor_port][ offset], &bedata, 4);
local_irq_restore(flags);
}
}
int putlong_meta(int sensor_port, unsigned long data, int * indx, unsigned long ltag) { //write data to meta (4 bytes, big endian), from outside IRQ (atomic)
int i;
unsigned long flags;
unsigned long bedata=__cpu_to_be32(data);
if (!aexif_enabled[sensor_port]) return -10;
if (indx && (dir_table[* indx].ltag==ltag)) i=*indx;
else {
for (i=0; i=exif_fields) return -1; //ltag not found
}
local_irq_save(flags);
memcpy(&ameta_buffer[sensor_port][dir_table[i].src], &bedata, 4);
local_irq_restore(flags);
if (indx) * indx=i;
return dir_table[i].src;
}
// The next function is normally called from the interrupt service routine
// Encode time (epoch sec, usec) into static buffer, return pointer to the buffer
// Uses struct exif_time that should be updated from the user space (once a day),
// calculates date/time ignoring leap seconds if not updated in time
/*
* 393: Continue to use same static buffers for exif_time
*/
char * encode_time(char buf[27], unsigned long sec, unsigned long usec) {
unsigned long s,d,m,y,y4,lp,h;
spin_lock(&lock);
if (((sec-exif_time.today_sec)>86400) || (sec < exif_time.today_sec)) {// today's time is not valid, try tomorrow:
memcpy(&exif_time.today_date[0],&exif_time.tomorrow_date[0],sizeof(exif_time.today_date)+sizeof(exif_time.today_sec));
if (((sec-exif_time.today_sec)>86400) || (sec < exif_time.today_sec)) {// today's time is _still_ not valid, has to do it itself :-(
d=sec/86400;
s=d*86400;
y4=d/1461; // number of 4-year periods
d-=1461*y4; //days after 1970, 1974, ...
y=(d- ((d>=1095)?1:0))/365;
d-=y*365+((y>2)?1:0);
lp=(y==2);
y+=4*y4+1970;
if ((!lp) && (d>58)) d++;
// d+=(lp && (d>58))?1:0;
D(printk("d=%ld, y=%ld, y4=%ld, lp=%ld\n",d, y, y4, lp));
if (d>181) {
if (d>273) {
if (d>304) {
if (d>334) {m=12;d-=334;} // December
else {m=11;d-=304;} // November
} else {m=10;d-=273;} // October
} else {
if (d>212) {
if (d>243) {m= 9;d-=243;} // September
else {m= 8;d-=212;} // August
} else {m= 7;d-=181;} // July
}
} else {
if (d>90) {
if (d>120) {
if (d>151) {m=6; d-=151;} // June
else {m=5; d-=120;} // May
} else {m=4; d-= 90;} // April
} else {
if (d>30) {
if (d>59) {m=3; d-= 59;} // March
else {m=2; d-= 30;} // February
} else {m=1; d++; } // January
}
}
D(printk("d=%ld, y=%ld, y4=%ld\n",d, y, y4));
sprintf(exif_time.today_date,"%04ld:%02ld:%02ld",y,m,d);
exif_time.today_sec=s;
}
memcpy (&now_datetime.datetime[0],exif_time.today_date,10);
now_datetime.datetime[10]=' ';
now_datetime.datetime[19]='\0';
now_datetime.subsec[6]='\0';
}
// now we have valid exif_time.today_date, exif_time.today_sec;
s=sec-exif_time.today_sec;
h= s/3600;
s-= 3600*h;
m= s/60;
s-= 60*m;
sprintf(&now_datetime.datetime[11],"%02ld:%02ld:%02ld",h,m,s);
sprintf(&now_datetime.subsec[0],"%06ld",usec);
memcpy(buf,&now_datetime.datetime[0],sizeof(now_datetime));
// return &now_datetime.datetime[0];
spin_unlock(&lock);
return buf;
}
int store_meta(int sensor_port) { //called from IRQ service - put current metadata to meta_buffer, return page index
if (!aexif_enabled[sensor_port]) return 0;
int retval=aexif_wp[sensor_port];
memcpy(&ameta_buffer[sensor_port][aexif_wp[sensor_port] * aexif_meta_size[sensor_port]], ameta_buffer[sensor_port],aexif_meta_size[sensor_port]);
aexif_wp[sensor_port]++;
if (aexif_wp[sensor_port] > MAX_EXIF_FRAMES) aexif_wp[sensor_port] = 1;
return retval;
}
//!++++++++++++++++++++++++++++++++++++ open() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int exif_open(struct inode *inode, struct file *filp) {
int p = MINOR(inode->i_rdev);
int * pd= (int *) &(filp->private_data);
switch (p) {
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
case X3X3_EXIF_TEMPLATE:
case X3X3_EXIF_METADIR:
case X3X3_EXIF_TIME:
break;
default:return -EINVAL;
}
D(printk("exif_open, minor=%d\n",p));
inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number
return 0;
}
//!++++++++++++++++++++++++++++++++++++ release() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int exif_release(struct inode *inode, struct file *filp){
int p = MINOR(inode->i_rdev);
int * pd= (int *) &(filp->private_data);
switch (p) {
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
break;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
break;
case X3X3_EXIF_TEMPLATE:
break;
case X3X3_EXIF_METADIR:
break;
case X3X3_EXIF_TIME:
break;
default:return -EINVAL;
}
D(printk("exif_open, minor=%d\n",p));
inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number
return 0;
}
//!++++++++++++++++++++++++++++++++++++ lseek() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
int p=(int)file->private_data;
int thissize=minor_file_size(p);
int maxsize=minor_max_size(p);
// int sensor_port;
int fp;
switch (orig) {
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
file->f_pos += offset;
break;
case SEEK_END:
//!overload
if (offset<=0) {
file->f_pos = thissize + offset;
} else {
switch (p) {
case X3X3_EXIF_TEMPLATE: //enable/disable
switch (offset) {
case EXIF_LSEEK_DISABLE:
exif_enable(0);
break;
case EXIF_LSEEK_ENABLE:
if (exif_enable(1)<0) return -EOVERFLOW; //TODO: change code
break;
case EXIF_LSEEK_INVALIDATE:
exif_invalidate();
break;
case EXIF_LSEEK_REBUILD:
if (exif_rebuild(MAX_EXIF_FRAMES)<0) return -EOVERFLOW; //TODO: change code
break;
default:return -EINVAL;
}
break;
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
// sensor_port = p - X3X3_EXIF_EXIF_CHN_0;
if (offset > MAX_EXIF_FRAMES) return -EOVERFLOW; //larger than buffer
// file->f_pos=exif_meta_size * offset;
file->f_pos=exif_template_size * offset;
break;
case X3X3_EXIF_META: // iterate
fp= dir_find_tag (offset);
if (fp < 0) return -EOVERFLOW; // tag is not in the directory
file->f_pos=fp;
break;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
file->f_pos=offset*sizeof(struct exif_dir_table_t);
break;
case X3X3_EXIF_TIME:
switch (offset) {
case EXIF_LSEEK_TOMORROW_DATE:
file->f_pos=exif_time.tomorrow_date - ((char *) &exif_time);
break;
case EXIF_LSEEK_TOMORROW_SEC:
file->f_pos=((char *) &exif_time.tomorrow_sec) - ((char *) &exif_time);
break;
case EXIF_LSEEK_TODAY_DATE:
file->f_pos=exif_time.today_date - ((char *) &exif_time);
break;
case EXIF_LSEEK_TODAY_SEC:
file->f_pos=((char *) &exif_time.today_sec) - ((char *) &exif_time);
break;
default:return -EINVAL;
}
break;
default:return -EINVAL;
}
}
break;
default:
return -EINVAL;
}
/* truncate position */
if (file->f_pos < 0) {
file->f_pos = 0;
return (-EOVERFLOW);
}
if (file->f_pos > maxsize) {
file->f_pos = maxsize;
return (-EOVERFLOW);
}
return (file->f_pos);
}
//!++++++++++++++++++++++++++++++++++++ write() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static ssize_t exif_write (struct file * file, const char * buf, size_t count, loff_t *off) {
int p=(int)file->private_data;
int sensor_port;
// int thissize=minor_file_size(p);
int maxsize=minor_max_size(p);
char * cp;
char tmp[MAX_EXIF_SIZE]; //! Or is it possible to disable IRQ while copy_from_user()?
unsigned long flags;
int disabled_err=0;
if ((*off+count)>maxsize) {
printk ("%s:%d: Data (0x%x) does not fit into 0x%x bytes\n",__FILE__,__LINE__, (int) (*off+count), maxsize);
return -EOVERFLOW;
}
switch (p) {
case X3X3_EXIF_TEMPLATE:
exif_invalidate();
if (copy_from_user(&exif_template[*off], buf, count)) return -EFAULT;
exif_template_size=*off+count;
break;
case X3X3_EXIF_METADIR:
exif_invalidate();
cp= (char *) &dir_table;
if (copy_from_user(&cp[*off], buf, count)) return -EFAULT;
exif_fields=(*off+count)/sizeof(struct exif_dir_table_t);
break;
case X3X3_EXIF_TIME: //write date/time first, then - midnight seconds
cp= (char *) &exif_time;
if (copy_from_user(&cp[*off], buf, count)) return -EFAULT;
break;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
sensor_port = p - X3X3_EXIF_META_CHN_0;
if (copy_from_user(tmp, buf, count)) return -EFAULT;
local_irq_save(flags);
//local_irq_disable();
if (aexif_enabled[sensor_port]) memcpy(&ameta_buffer[sensor_port][*off], tmp, count);
else disabled_err=1;
local_irq_restore(flags);
if (disabled_err) {
D(printk("tried to write meta channel %d while disabled\n",sensor_port));
count=0;
}
break;
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
return -EINVAL; // no writing - read only
break;
default:return -EINVAL;
}
*off+=count;
D(printk("count= 0x%x, pos= 0x%x\n", (int) count, (int)*off));
return count;
}
//!++++++++++++++++++++++++++++++++++++ read() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *off) {
int p=(int)file->private_data;
int thissize=minor_file_size(p);
char * cp, * metap;
int start_p, page_p,i;
int sensor_port;
char tmp[MAX_EXIF_SIZE]; //! Or is it possible to disable IRQ while copy_from_user()?
/*
Does not work with PHP - always read 8192 bytes
if ((*off+count)>maxsize) {
printk ("%s:%d: Data (0x%x) does not fit into 0x%x bytes\n",__FILE__,__LINE__, (int) (*off+count), maxsize);
return -EOVERFLOW;
}
*/
if (*off > thissize) {
return 0; // nothing to read
}
if ((*off + count) > thissize) {
count=thissize-*off;
}
switch (p) {
case X3X3_EXIF_TEMPLATE:
if (copy_to_user(buf, &exif_template[*off], count)) return -EFAULT;
break;
case X3X3_EXIF_METADIR:
cp= (char *) &dir_table;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break;
case X3X3_EXIF_TIME:
cp= (char *) &exif_time;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
sensor_port = p - X3X3_EXIF_META_CHN_0;
if (!aexif_enabled[sensor_port]) return 0;
if (copy_to_user(buf, &ameta_buffer[sensor_port][*off], count)) return -EFAULT;
break;
case X3X3_EXIF_EXIF_CHN_0:// generates exif data by merging exif_template with the selected meta_buffer page
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
sensor_port = p - X3X3_EXIF_EXIF_CHN_0;
//will truncate by the end of current page
if (!aexif_enabled[sensor_port]) return 0;
i=((int) *off) / exif_template_size;
D(printk("count= 0x%x, *off= 0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int) i, (int) exif_template_size));
//arch/cris/arch-v32/drivers/elphel/exif353.c:590:count= 0x2000, *off= 0x410, i=0x82, exif_template_size=0x208
start_p=i*exif_template_size;
page_p= *off - start_p;
D(printk("count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p,(int) i, (int) exif_template_size));
//arch/cris/arch-v32/drivers/elphel/exif353.c:591:count= 0x2000, pos= 0x410, start_p=0x10810, page_p=0xfffefc00, i=0x82, exif_template_size=0x208
metap= &ameta_buffer[sensor_port][i*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(tmp,exif_template, exif_template_size);
D(printk("count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p));
for (i=0;i 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() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int __init exif_init(void) {
int res;
res = register_chrdev(X3X3_EXIF_MAJOR, "Exif", &exif_fops);
if(res < 0) {
printk(KERN_ERR "\nexif_init: couldn't get a major number %d.\n",X3X3_EXIF_MAJOR);
return res;
}
printk(X3X3_EXIF_DRIVER_NAME" - %d\n",X3X3_EXIF_MAJOR);
return 0;
}
module_init(exif_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov .");
MODULE_DESCRIPTION(X3X3_EXIF_DRIVER_NAME);
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/exif393.h 0000664 0000000 0000000 00000005537 12762437450 0026273 0 ustar 00root root 0000000 0000000 /*
exif353.h
*/
#ifndef _EXIF_H
#define _EXIF_H
#if 0
struct exif_time_t {
char tomorrow_date[10]; //!"YYYY:MM:DD"
unsigned long tomorrow_sec; //!seconds from epoch tomorrow at 00:00
char today_date[10]; //!"YYYY:MM:DD"
unsigned long today_sec; //!seconds from epoch today at 00:00
} exif_time;
struct exif_datetime_t {
char datetime[20]; //!"YYYY:MM:DD HH:MM:SS\0"
char subsec[7]; //!ASCII microseconds (0-padded), ."\0"
} now_datetime;
#endif
void exif_invalidate(void);
int exif_rebuild_chn(int sensor_port, int frames);// reallocate meta buffer to store per-frame meta data (later output as Exif)
int exif_rebuild(int frames); // rebuild for all sensor ports
int exif_enable_chn(int sensor_port, int en); // enable/disable Exif processing (both W/R)
int exif_enable(int en); // For all sensor ports
int dir_find_tag (unsigned long tag); //!find location of the tag field in meta page using long tag (Exif tag and tag group)
void write_meta_raw_irq(int sensor_port, char * data, int offset, int len); //write data to meta, called from IRQ
int write_meta_irq(int sensor_port, char * data, int * indx, unsigned long ltag, int len); //write data to meta, called from IRQ(len==0 => use field length)
void putlong_meta_raw_irq(int sensor_port, unsigned long data, int offset); //write data to meta (4 bytes, big endian), called from IRQ
int putlong_meta_irq(int sensor_port, unsigned long data, int * indx, unsigned long ltag); //write data to meta (4 bytes, big endian), from IRQ
//void write_meta_raw_irq(char * data, int offset, int len); //write data to meta, called from IRQ
//int write_meta_irq(char * data, int * indx, unsigned long ltag, int len); //write data to meta, called from IRQ(len==0 => use field length). Returns index of the written data, -1 if not written
void write_meta_raw(int sensor_port, char * data, int offset, int len); //write data to meta, called from outside IRQ (atomic)
int write_meta(int sensor_port, char * data, int * indx, unsigned long ltag, int len); //write data to meta, from outside IRQ (atomic) (len==0 => use field length). Returns index of the written data, -1 if not written
void putlong_meta_raw(int sensor_port, unsigned long data, int offset); //write data to meta (4 bytes, big endian), called from outside IRQ (atomic)
int putlong_meta(int sensor_port, unsigned long data, int * indx, unsigned long ltag); //write data to meta (4 bytes, big endian), from outside IRQ (atomic). Returns index of the written data, -1 if not written
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
size_t exif_get_data(int sensor_port, unsigned short meta_index, void * buff, size_t buff_sz);
#endif
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/fpgajtag353.c 0000664 0000000 0000000 00000156766 12762437450 0027125 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : fpgajtag353.c
*! DESCRIPTION: TBD
*! Copyright 2002-20016 (C) Elphel, Inc.
*! -----------------------------------------------------------------------------**
*!
*! 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 2 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 .
*! -----------------------------------------------------------------------------**
*/
#undef DEBUG
/****************** INCLUDE FILES SECTION ***********************************/
#include
#include
#include
#include // needed?
#include
#include
#include
#include
//#include
#include
#include
#include
//#include
//#include
#include
//#include
#include
#include
//#include //defines for fpga_state fields
//#include
#include
//#include "fpgactrl.h" // extern fpga_state, defines port_csp0_addr, port_csp4_addr
//#include "x3x3.h" // FPGA registers and macros
#include "x393.h"
//#include
//#define JTAG_DISABLE_IRQ y
#define D(x)
//#define D(x) printk("%s:%d:",__FILE__,__LINE__);x
#define PARALLEL_JTAG
/*
port C 353:
0 - TDO (in)
1 - TDI (out)
2 - TMS (out)
3 - TCK (out)
4 - NC (was INIT (i/o) )
5 - DONE (in)
6 - RSTBTN
7 - PGM (out)
*/
#define FPGAJTAG_TDO_BIT 0
#define FPGAJTAG_TDI_BIT 1
#define FPGAJTAG_TMS_BIT 2
#define FPGAJTAG_TCK_BIT 3
#define FPGAJTAG_DONE_BIT 5
#define FPGAJTAG_RSTBTN_BIT 6
#define FPGAJTAG_PGM_BIT 7
#ifndef XC2S300E_BITSIZE
#define XC3S1000_BITSIZE 3223488
#define XC3S1200E_BITSIZE 3841189
#define XC3S1200E_BOUNDARY_SIZE 772
// #define XC3S1200E_BOUNDARY_SIZE 812
#define FJTAG_BUF_SIZE 0x77000
#define FJTAG_MAX_HEAD 0x1000
#define FJTAG_RAW_WSIZE 0x40000 // shared with bitstream buffer
#define FJTAG_RAW_RSIZE 0x30000 // shared with bitstream buffer
#define FJTAG_IDSIZE 0x40 // bits - ID and User
#endif
//#define FPGA_JTAG_DRIVER_NAME "Elphel (R) model 353 FPGA (Xilinx (R) XC3S1200E) configuration driver"
#define FPGA_JTAG_DRIVER_NAME "Elphel (R) model 393 FPGA (Xilinx (R) XC3S1200E) configuration driver"
#define FPGA_JTAG_MAXMINOR 16 // 10
#define JTAG_RAW 0 // raw JTAG access to any FPGA
#define JTAG_MAIN_FPGA 1 // main board FPGA access (10353)
#define JTAG_SENSOR_FPGA 2 // sensor board FPGA access (10347, 10359)
#define JTAG_AUX_FPGA 3 //
//#define JTAG_NCHANNELS 4
#define JTAG_NCHANNELS 16 // 4 << 2
#define JTAG_SENSOR_OFFSET 4 // Sensor ports minors start (4..7) - (2 LSB should be 0)
#define JTAG_SENSOR_CHANNELS 4 // Number of sensor ports for JTAG
#define JTAG_MODE_CLOSED 0 // JTAG channel is closed
#define JTAG_MODE_RDID 1 // JTAG channel read ID
#define JTAG_MODE_PGM 2 // JTAG channel PROGRAM
#define JTAG_MODE_BOUNDARY 3 // JTAG channel boundary scan (just opened, will become one of the 2:JTAG_MODE_SAMPLE, JTAG_MODE_EXTEST
#define JTAG_MODE_SAMPLE 4 // JTAG channel boundary scan - sample (the first operation after open is read)
#define JTAG_MODE_EXTEST 5 // JTAG channel boundary scan - EXTEST (the first operation after open is read)
#define JTAG_MODE_RAW 6 // JTAG raw command mode
// configuration and raw minors use whole buffer, ID and boundary can be opened at the same time
struct JTAG_channel_t {
int mode; // 0..5 -JTAG_MODE_CLOSED...JTAG_MODE_EXTEST
unsigned char * dbuf; // data buffer (shared, boundary mode use different parts)
int sizew; // byte size that can be written
int sizer; // byte size that can be read
int bitsw; // bit size to be written
int bitsr; // bit size to be read
int wp; // byte pointer for file write
int rp; // byte pointer for file read
int wdirty; // some data is buffered but not yet sent out in EXTEST mode
};
static unsigned char bitstream_data[FJTAG_BUF_SIZE]; // will fit bitstream and the header (if any). Also used for boundary write
//static unsigned short *raw_fifo_w= (unsigned short) &bitstream_data[0];
static unsigned char *raw_fifo_w= &bitstream_data[0];
static unsigned char *raw_fifo_r= &bitstream_data[FJTAG_RAW_WSIZE];
static struct JTAG_channel_t JTAG_channels[JTAG_NCHANNELS];
// boundary scan is read always at open. written - at close (only if there were any writes)
static int data_modified=0;
//static reg_gio_rw_pc_dout pc_dout;
#define PC_DOUT_INITIAL 0
//static int buf8i=0; // current buffer length (in bytes!)
//static int datastart=0;
//static int prev32;
//static int prev64;
//static int fpga_jtag_state=0;
// inteface functions
static const char fpga_jtag_name[] = "fpga_jtag_loader";
static int minors[FPGA_JTAG_MAXMINOR+1]; // each minor can be opened only once
//static int thisminor;
static int fpga_jtag_open (struct inode *inode, struct file *filp);
static int fpga_jtag_release(struct inode *inode, struct file *filp);
static ssize_t fpga_jtag_write (struct file * file, const char * buf, size_t count, loff_t *off);
static loff_t fpga_jtag_lseek (struct file * file, loff_t offset, int orig);
static ssize_t fpga_jtag_read (struct file * file, char * buf, size_t count, loff_t *off);
static int __init fpga_jtag_init(void);
static struct file_operations fpga_jtag_fops = {
owner: THIS_MODULE,
open: fpga_jtag_open,
release: fpga_jtag_release,
llseek: fpga_jtag_lseek,
read: fpga_jtag_read,
write: fpga_jtag_write
};
//static int sens_num = 0;
// internal functions
//loff_t fjtag_bitsize (int minor);
//loff_t fjtag_bytesize (int minor);
int JTAG_channel(int minor);
void initPortC(void);
void set_pgm_mode (int chn, int en);
void set_pgm (int chn, int pgmon);
int read_done (int chn);
int jtag_send (int chn, int tms, int len, int d);
int jtag_write_bits (int chn,
unsigned char *buf, // data to write
int len, // number of bytes to write
int check, // compare readback data with previously written, abort on mismatch
int last, // output last bit with TMS=1
int prev[2]); // if null - don't use
int JTAG_configure (int chn, unsigned char * buf, int len);
int JTAG_readID (int chn, unsigned char * buf);
int JTAG_openChannel (int chn);
int JTAG_resetChannel (int chn);
int JTAG_CAPTURE (int chn, unsigned char * buf, int len);
int JTAG_EXTEST (int chn, unsigned char * buf, int len);
void JTAG_push_raw (int b);
int JTAG_process_raw(void);
int JTAG_channel(int minor) {
if ((minor >= FPGA_SJTAG_MINOR_OFFSET) && (minor < (FPGA_SJTAG_MINOR_OFFSET + FPGA_SJTAG_CHANNELS)))
return (minor - FPGA_SJTAG_MINOR_OFFSET) + (JTAG_SENSOR_FPGA << 2);
if ((minor >= FPGA_SJTAG_BOUNDARY_OFFSET) && (minor < (FPGA_SJTAG_BOUNDARY_OFFSET + FPGA_SJTAG_CHANNELS)))
return (minor - FPGA_SJTAG_BOUNDARY_OFFSET) + (JTAG_SENSOR_FPGA << 2);
// maybe will never be used
switch (minor) {
case FPGA_JTAG_RESET_MINOR : // same as RAW
return JTAG_RAW << 2;
case FPGA_JTAG_MINOR:
case FPGA_JTAG_BOUNDARY_MINOR:
return JTAG_MAIN_FPGA << 2;
case FPGA_SJTAG_MINOR:
case FPGA_SJTAG_BOUNDARY_MINOR:
return JTAG_SENSOR_FPGA << 2;
case FPGA_AJTAG_MINOR:
case FPGA_AJTAG_BOUNDARY_MINOR:
return JTAG_AUX_FPGA << 2;
}
return 0;
}
static int raw_fifo_w_wp;
static int raw_fifo_w_rp;
static int raw_fifo_r_wp;
static int raw_fifo_r_rp;
static int raw_chn;
/*
* send raw JTAG commands. Each command consists of 2 bytes
* byte 0 - data to send through TDI, lsb aligned (if less than 8 bits - hign bits are not used
* byte 1 - 0001TNNN - send NNN?NNN:8 bits of byte 0 through TDI, keeping TDS at value of T (reads back TDO - before TCL)
* - 00100000 - select JTAG channel from byte 0 (reads back channel)
* - 00100010 - de-activate JTAG access (reads back 0)
* - 00100011 - activate JTAG access (reads back 0)
* - 00100100 - PGM off (reads back ready in bit 0)
* - 00100101 - PGM on (reads back 0xf0)
* - 1TTTTTTT - delay usec byte0+ ((byte1 &0x7f) << 8) (reads back 80)
* - 0??????? (other) - nop (reads back 0xff)
* if no channel is selected, no output is generated
void set_pgm_mode (int chn, int en);
void set_pgm (int chn, int pgmon);
*/
#define JTAG_RAW_SEND 0x10
#define JTAG_RAW_SETCHN 0x20
#define JTAG_RAW_DEACT 0x22
#define JTAG_RAW_ACT 0x23
#define JTAG_RAW_PGMOFF 0x24
#define JTAG_RAW_PGMON 0x25
#define JTAG_RAW_WAIT 0x80
void JTAG_push_raw (int b) {
raw_fifo_r[raw_fifo_r_wp++]=b;
if (raw_fifo_r_wp > FJTAG_RAW_RSIZE) raw_fifo_r_wp-=FJTAG_RAW_RSIZE;
}
// TODO: Not updated for 393. Is it needed?
int JTAG_process_raw(void) {
unsigned char b0, b1;
while (raw_fifo_w_rp != (raw_fifo_w_wp & ~1)) {
b0=raw_fifo_w[raw_fifo_w_rp++];
b1=raw_fifo_w[raw_fifo_w_rp++];
if (raw_fifo_w_rp > FJTAG_RAW_WSIZE) raw_fifo_w_rp-=FJTAG_RAW_WSIZE;
if (b1 == JTAG_RAW_SETCHN) { // set channel number
raw_chn = b0;
if (raw_chn>=JTAG_NCHANNELS) raw_chn=0; //illegal channel
JTAG_push_raw (raw_chn);
} else if (raw_chn) { // ignore commands until the JTAG channel number is specified
if ((b1 & 0xf0) == JTAG_RAW_SEND) { // send JTAG data
JTAG_push_raw (jtag_send(raw_chn, (b1 >> 3) & 1, b1 & 7, (int) b0 ));
} else if ((b1 & 0x80) == JTAG_RAW_WAIT) { // delay
/* possible bug here, udelay is used for delays less then 2 ms */
udelay(((b1 & 0x7f) <<8) + b0);
JTAG_push_raw (0x80);
} else switch (b1) {
case JTAG_RAW_DEACT:
set_pgm_mode (raw_chn, 0);
JTAG_push_raw (0x0);
break;
case JTAG_RAW_ACT:
set_pgm_mode (raw_chn, 1);
JTAG_push_raw (0x0);
break;
case JTAG_RAW_PGMOFF:
set_pgm (raw_chn, 0);
JTAG_push_raw (read_done(raw_chn));
break; // was missing for 353
case JTAG_RAW_PGMON:
set_pgm (raw_chn, 1);
JTAG_push_raw (0xf0);
break;
default:
JTAG_push_raw (0xff);
}
} else { // make output always be 1 byte for 2 bytes input
JTAG_push_raw (0xf0);
} // end of if (raw_chn) /else
} // while (raw_fifo_w_rp != (raw_fifo_w_wp & ~1))
return 0; // will think of return value later
}
//returns 0 if all channels closed
//
int JTAG_whatopen(void) {
int i,r=0;
for (i=0;ii_rdev);
int chn= JTAG_channel(p);
//reg_intr_vect_rw_mask intr_mask;
//D(printk("fpga_jtag_open: minor=%x, channel=%x, buf=%p\r\n",p,chn,bitstream_data ));
dev_dbg(NULL, "fpga_jtag_open: minor=%x, channel=%x, buf=%p\r\n",p ,chn, bitstream_data);
switch ( p ) {
case FPGA_JTAG_RESET_MINOR : // same as RAW
for (i=1; i> 3;
// JTAG_channels[chn].wp = 0;
// JTAG_channels[chn].rp = 0; // will read IDs if actually read
#ifdef TEST_DISABLE_CODE
fpga_state &= ~FPGA_STATE_LOADED; // is it still used?
fpga_state &= ~FPGA_STATE_SDRAM_INIT; // not needed
// disable camera interrupts here (while reprogramming FPGA could generate stray interrupts;
/* Disable external interrupts.. */
intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
intr_mask.ext = 0;
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
#endif /* TEST_DISABLE_CODE */
// printk ("Camera interrupts disabled\r\n");
// break;
// fall through
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3):
case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR :
if ( JTAG_whatopen() & 0x7e) return -EACCES; // none of the channels could be open when opening this file
JTAG_channels[chn].mode = JTAG_MODE_PGM;
JTAG_channels[chn].dbuf = &bitstream_data[0];
JTAG_channels[chn].sizew = FJTAG_BUF_SIZE;
JTAG_channels[chn].sizer = FJTAG_IDSIZE >> 3;
JTAG_channels[chn].wp = 0;
JTAG_channels[chn].rp = 0; // will read IDs if actually read
JTAG_channels[chn].bitsw = XC3S1200E_BITSIZE; // bit size to be written
JTAG_channels[chn].bitsr= FJTAG_IDSIZE;
JTAG_openChannel (chn); // configure channel access, reset JTAG and to RUN-TEST/IDLE state
break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3):
case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR :
if ( JTAG_whatopen() & 0x46) return -EACCES; // none of the channels could be open for program/id/raw when opening this file
if ( JTAG_channels[chn].mode != JTAG_MODE_CLOSED) return -EACCES; // already open
JTAG_channels[chn].mode = JTAG_MODE_BOUNDARY;
JTAG_channels[chn].sizew = (XC3S1200E_BOUNDARY_SIZE+7) >> 3;
JTAG_channels[chn].sizer = (XC3S1200E_BOUNDARY_SIZE+7) >> 3;
JTAG_channels[chn].dbuf = &bitstream_data[JTAG_channels[chn].sizew * chn];
JTAG_channels[chn].wp = 0;
JTAG_channels[chn].rp = 0; // will read IDs if actually read
JTAG_channels[chn].bitsw = XC3S1200E_BOUNDARY_SIZE; // bit size to be written
JTAG_channels[chn].bitsr= XC3S1200E_BOUNDARY_SIZE;
JTAG_openChannel (chn); // configure channel access, reset JTAG and to RUN-TEST/IDLE state
break;
default: return -EINVAL;
}
dev_dbg(NULL, "fpga_jtag_open: chn=%x, JTAG_channels[chn].sizew=%x, JTAG_channels[chn].sizer=%x\r\n", chn, JTAG_channels[chn].sizew, JTAG_channels[chn].sizer);
dev_dbg(NULL, "fpga_jtag_open: chn=%x, JTAG_channels[chn].bitsw=%x, JTAG_channels[chn].bitsr=%x\r\n", chn, JTAG_channels[chn].bitsw, JTAG_channels[chn].bitsr);
JTAG_channels[chn].wdirty=0;
inode->i_size=JTAG_channels[chn].sizer;
minors[p]=p;
filp->private_data = &minors[p];
dev_dbg(NULL, "fpga_jtag_open: inode->i_size=%x, chn=%x\r\n", (int) inode->i_size, chn);
return 0;
}
//++++++++++++++++++++++++++++++++++++ release() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int fpga_jtag_release(struct inode *inode, struct file *filp) {
int res=0;
int p = MINOR(inode->i_rdev);
int chn= JTAG_channel(p);
dev_dbg(NULL, "fpga_jtag_release: p=%x,chn=%x, wp=0x%x, rp=0x%x\r\n", p, chn, JTAG_channels[chn].wp, JTAG_channels[chn].rp);
switch ( p ) {
case FPGA_JTAG_RESET_MINOR : // same as RAW - do nothing, raw code should do it on it's own
break;
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3):
case FPGA_JTAG_MINOR :
case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR :
if (JTAG_channels[chn].wp > 0) { // anything written?
res=JTAG_configure (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].wp);
JTAG_resetChannel (chn);
if ((res >=0) & (chn == JTAG_MAIN_FPGA)) {
// read FPGA model number/revision and OR it with current state
//fpga_state = (fpga_state & ~0xffff) | (port_csp0_addr[X313__RA__MODEL] & 0xffff);
}
} else JTAG_resetChannel (chn); /// reset initializing in any case:
//if (chn == JTAG_MAIN_FPGA) fpga_state &=~FPGA_STATE_INITIALIZED;
break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3):
case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR :
// "dirty"? Send to JTAG
if (JTAG_channels[chn].wp >0) {
// D(printk("fpga_jtag_release(), JTAG_channels[%d].wp = 0x%x",chn,JTAG_channels[chn].wp));
JTAG_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsw); // size in bits
}
JTAG_resetChannel (chn);
break;
default: return -EINVAL;
}
minors[p]=0;
JTAG_channels[chn].mode=JTAG_MODE_CLOSED;
//D(printk("fpga_jtag_release: done\r\n"));
dev_dbg(NULL, "fpga_jtag_release: done\r\n");
dev_info(NULL, "fpga_jtag_release: done, res= %d\n",res);
return (res<0)?res:0;
}
//++++++++++++++++++++++++++++++++++++ write() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
//for boundary scan: writing before wp+1 will start EXTEST cycle (either rollover or lseek)
static ssize_t fpga_jtag_write(struct file * file, const char * buf, size_t count, loff_t *off) {
int p = ((int *)file->private_data)[0];
int chn= JTAG_channel(p);
size_t size = JTAG_channels[chn].sizew;
dev_dbg(NULL, "fpga_jtag_write: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, wp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].wp, (int) size);
switch (p) {
case FPGA_JTAG_RESET_MINOR : // same as RAW - do nothing, raw code should do it on it's own
if (count > size) count= size;
if ((raw_fifo_w_wp+count) > size) { // read tail, then roll over to the head to the total of count
if (copy_from_user(&raw_fifo_w[raw_fifo_w_wp],buf,size-raw_fifo_w_wp)) return -EFAULT; // read tail
if (copy_from_user(&raw_fifo_w[0],&buf[size-raw_fifo_w_wp],count+raw_fifo_w_wp-size)) return -EFAULT; // read head
} else {
if (copy_from_user(&raw_fifo_w[raw_fifo_w_wp],buf,count)) return -EFAULT; // read count
}
raw_fifo_w_wp+=count;
if (raw_fifo_w_wp > size) raw_fifo_w_wp -= size;
JTAG_process_raw(); // send all the received data to JTAG - will cause read fifo to get ~1/2 of the number of bytes written
break;
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3):
case FPGA_JTAG_MINOR :
case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR : // read configuration data to buffer
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
if (copy_from_user(&(JTAG_channels[chn].dbuf[*off]),buf,count)) return -EFAULT;
*off+=count;
if (*off > JTAG_channels[chn].wp) JTAG_channels[chn].wp= *off;
break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3):
case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR :
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
if (*off < JTAG_channels[chn].wp) {
// D(printk("fpga_jtag_write(), JTAG_channels[%d].wp = 0x%x",chn, JTAG_channels[chn].wp));
JTAG_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsw); // writing "before" causes EXTEST to fill in boundary scan register
JTAG_channels[chn].wdirty=0;
}
if (copy_from_user(&(JTAG_channels[chn].dbuf[*off]),buf,count)) return -EFAULT;
*off+=count;
JTAG_channels[chn].wp= *off; // before rolling over
if (*off >= size) {
*off=0; // roll over
}
JTAG_channels[chn].mode=JTAG_MODE_EXTEST; //should write the last byte before reading - or buffer data will be just lost
JTAG_channels[chn].wdirty=1;
break;
default: return -EINVAL;
}
dev_dbg(NULL, "fpga_jtag_write end: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, wp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].wp, (int) size);
//D(printk("fpga_jtag_write end: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, wp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].wp, (int) size));
return count;
}
//++++++++++++++++++++++++++++++++++++ read() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off) {
int p = ((int *)file->private_data)[0];
int chn= JTAG_channel(p);
size_t size = JTAG_channels[chn].sizer;
int size_av; // available data
dev_dbg(NULL, "fpga_jtag_read: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x\r\n", p, chn,(long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size);
switch (p) {
case FPGA_JTAG_RESET_MINOR : // same as RAW - do nothing, raw code should do it on it's own
size_av=(raw_fifo_r_wp >= raw_fifo_r_rp)?(raw_fifo_r_wp - raw_fifo_r_rp):(size+raw_fifo_r_wp - raw_fifo_r_rp);
if (count > size_av) count= size_av;
if ((raw_fifo_r_rp+count) > size) { // read tail, then roll over to the head to the total of count
if (copy_to_user(buf, &raw_fifo_r[raw_fifo_r_rp],size-raw_fifo_r_rp)) return -EFAULT; // read tail
if (copy_to_user(&buf[size-raw_fifo_r_rp],&raw_fifo_r[0],count+raw_fifo_r_rp-size)) return -EFAULT; // read head
} else {
if (copy_to_user(buf,&raw_fifo_w[raw_fifo_w_wp],count)) return -EFAULT; // read count
}
raw_fifo_r_rp+=count;
if (raw_fifo_r_rp > size) raw_fifo_r_rp -= size;
break;
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3):
case FPGA_JTAG_MINOR :
case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR : // read configuration data to buffer
if ((JTAG_channels[chn].wp==0) && (JTAG_channels[chn].rp==0)) { // starting from read - get ID
JTAG_channels[chn].mode=JTAG_MODE_RDID;
JTAG_readID (chn, JTAG_channels[chn].dbuf);
}
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
dev_dbg(NULL, "fpga_jtag_read_01: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size);
if (copy_to_user(buf,&(JTAG_channels[chn].dbuf[*off]),count)) return -EFAULT;
*off+=count;
JTAG_channels[chn].rp= *off;
break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3):
case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR :
if ((JTAG_channels[chn].mode==JTAG_MODE_EXTEST) && (JTAG_channels[chn].wdirty || (*off < JTAG_channels[chn].rp))) {
JTAG_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsr); // writing last byte causes EXTEST to fill in boundary scan register
JTAG_channels[chn].wdirty=0;
}
// (re)-read capture pins if it was a roll-over or the first access after open
if ((JTAG_channels[chn].mode!=JTAG_MODE_EXTEST) && ((*off < JTAG_channels[chn].rp) || (JTAG_channels[chn].mode==JTAG_MODE_BOUNDARY))) {
JTAG_CAPTURE (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsr);
}
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
dev_dbg(NULL, "fpga_jtag_read_01: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size);
if (copy_to_user(buf,&(JTAG_channels[chn].dbuf[*off]),count)) return -EFAULT;
*off+=count;
JTAG_channels[chn].rp= *off; // before rolling over
if (*off >= size) {
*off=0; // roll over
}
if (JTAG_channels[chn].mode == JTAG_MODE_BOUNDARY) JTAG_channels[chn].mode=JTAG_MODE_SAMPLE; //should write the last byte before reading - or buffer data will be just lost
break;
default: return -EINVAL;
}
dev_dbg(NULL, "fpga_jtag_read_end: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x, mode=%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size, JTAG_channels[chn].mode);
return count;
}
//++++++++++++++++++++++++++++++++++++ lseek() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static loff_t fpga_jtag_lseek(struct file * file, loff_t offset, int orig) {
/*
* orig 0: position from begning of
* orig 1: relative from current position
* orig 2: position from last address
*/
int p = ((int *)file->private_data)[0];
int chn= JTAG_channel(p);
size_t size;
if (chn==JTAG_RAW) {
size=raw_fifo_r_wp-raw_fifo_r_rp;
if (size<0) size+=FJTAG_RAW_RSIZE;
} else size = JTAG_channels[chn].sizew;
if (JTAG_channels[chn].mode == JTAG_MODE_RDID) size = JTAG_channels[chn].sizer;
dev_dbg(NULL, "fpga_jtag_lseek, fsize= 0x%x\n", (int) size);
switch (orig) {
case 0:
file->f_pos = offset;
break;
case 1:
file->f_pos += offset;
break;
case 2:
file->f_pos = size + offset;
break;
default:
return -EINVAL;
}
/* truncate position */
if (file->f_pos < 0) {
file->f_pos = 0;
return (-EOVERFLOW);
}
if (file->f_pos > size) {
file->f_pos = size;
return (-EOVERFLOW);
}
dev_dbg(NULL,"fpga_jtag_lseek, file->f_pos= 0x%x\n", (int) file->f_pos);
return (file->f_pos);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Initialize GPIOs of the CPU to access JTAG/programming of the main FPGA
void initPortC(void) {
// connect 8 lower bits of port C to GPIO, disconnect from IOP
unsigned long tmp;
#ifdef TEST_DISABLE_CODE
reg_pinmux_rw_pc_iop pinmux_c_iop;
reg_pinmux_rw_pc_gio pinmux_c_gio;
reg_gio_rw_pc_oe pc_oe;
pinmux_c_iop= REG_RD(pinmux, regi_pinmux, rw_pc_iop);
tmp = REG_TYPE_CONV(unsigned long, reg_pinmux_rw_pc_iop, pinmux_c_iop);
tmp &= ~0xff;
pinmux_c_iop = REG_TYPE_CONV(reg_pinmux_rw_pc_iop, unsigned long, tmp);
REG_WR(pinmux, regi_pinmux, rw_pc_iop, pinmux_c_iop);
pinmux_c_gio= REG_RD(pinmux, regi_pinmux, rw_pc_gio);
tmp = REG_TYPE_CONV(unsigned long, reg_pinmux_rw_pc_gio, pinmux_c_gio);
tmp |= 0xff;
pinmux_c_gio = REG_TYPE_CONV(reg_pinmux_rw_pc_gio, unsigned long, tmp);
REG_WR(pinmux, regi_pinmux, rw_pc_gio, pinmux_c_gio);
// now set data of port C pins (static pc_dout)
pc_dout = REG_RD(gio, regi_gio, rw_pc_dout);
pc_dout.data &= ~0xff;
pc_dout.data |= PC_DOUT_INITIAL;
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
// now set directions of port C pins
pc_oe = REG_RD(gio, regi_gio, rw_pc_oe);
pc_oe.oe &= ~( (1 << FPGAJTAG_TDO_BIT) |
(1 << FPGAJTAG_DONE_BIT) |
(1 << FPGAJTAG_RSTBTN_BIT));
pc_oe.oe |= ( (1 << FPGAJTAG_TDI_BIT) |
(1 << FPGAJTAG_TMS_BIT) |
(1 << FPGAJTAG_TCK_BIT) |
(1 << FPGAJTAG_PGM_BIT));
REG_WR(gio, regi_gio, rw_pc_oe, pc_oe);
#endif /* TEST_DISABLE_CODE */
}
inline u32 prep_sensio_status(int sens_num)
{
x393_status_sens_io_t stat;
x393_status_ctrl_t stat_ctrl;
stat_ctrl.d32 = 0;
stat = x393_sensio_status(sens_num);
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
// dev_dbg(NULL, "set seq_num = %d, chn = %d", stat_ctrl.seq_num, sens_num);
return stat_ctrl.seq_num; // Sequence number to expect (wait for) with return data
}
inline x393_status_sens_io_t wait_sensio_status(int chn, u32 seq_num) // reducing number of hardware reads
{
int i;
int ret = 0;
x393_status_sens_io_t stat;
// dev_dbg(NULL, "waiting for seq_num = %d, chn = %d", seq_num, chn);
for (i = 0; i < 10; i++) {
stat = x393_sensio_status(chn & 3); // sens_num);
if (stat.seq_num == seq_num) {
ret = -1;
if (i)
dev_dbg(NULL, "seq_num = %d received after %d wait cycles", seq_num, i);
break;
}
}
// return ret;
return stat;
}
inline u32 read_tdo(int sens_num)
{
x393_status_sens_io_t stat;
x393_status_ctrl_t stat_ctrl;
int i;
stat_ctrl.d32 = 0;
stat = x393_sensio_status(sens_num);
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
for (i = 0; i < 10; i++) {
stat = x393_sensio_status(sens_num & 3); // sens_num);
if (likely(stat.seq_num == stat_ctrl.seq_num)) {
return stat.xfpgatdo;
}
}
dev_err(NULL,"read_tdo(%d): failed to get expected seq_num in 10 cycles, expected = 0x%x, got 0x%x\n",sens_num,stat_ctrl.seq_num, stat.seq_num);
return stat.xfpgatdo;
}
// read last 8 TDO bits, shifted at rising edge of TCL
inline u32 read_tdo_byte(int sens_num)
{
x393_status_sens_io_t stat;
x393_status_ctrl_t stat_ctrl;
int i;
stat_ctrl.d32 = 0;
stat = x393_sensio_status(sens_num);
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
for (i = 0; i < 10; i++) {
stat = x393_sensio_status(sens_num & 3); // sens_num);
if (likely(stat.seq_num == stat_ctrl.seq_num)) {
return stat.xfpgatdo_byte;
}
}
dev_err(NULL,"read_tdo_byte(%d): failed to get expected seq_num in 10 cycles, expected = 0x%x, got 0x%x\n",sens_num,stat_ctrl.seq_num, stat.seq_num);
return stat.xfpgatdo_byte;
}
// set FPGA in programming/JTAG mode (only for sensor board)
// NOP for the main board FPGA configuration
void set_pgm_mode (int chn, int en) {
u32 seq_num;
x393_sensio_jtag_t data;
dev_dbg(NULL, "set_pgm_mode (%d,%d)\n",chn,en);
switch (chn >> 2) {
case JTAG_SENSOR_FPGA:
//port_csp0_addr[X313_WA_SENSFPGA] = (en ? (3 << SFPGA_PGMEN_BIT): (SFPGA_RD_SENSPGMPIN | (2 << SFPGA_PGMEN_BIT))) | (2 << SFPGA_TCK_BIT); // turn off TCK (if not turned off already)
/* ? SFPGA_RD_SENSPGMPIN */
data.d32 = 0;
data.pgmen = (en) ? 1 : 0;
data.pgmen_set = 1;
data.tck = 0;
data.tck_set = 1;
/* check status register */
x393_sensio_jtag(data, chn & 3); // sens_num);
/* wait for status register update */
wait_sensio_status(chn & 3, prep_sensio_status(chn & 3)) ; // Not needed here
break;
}
udelay (2);
}
void set_pgm (int chn, int pgmon) {
u32 seq_num;
x393_sensio_jtag_t data;
dev_dbg(NULL, "set_pgm (%d,%d)\n",chn,pgmon);
switch (chn >> 2) {
case JTAG_MAIN_FPGA:
#ifdef TEST_DISABLE_CODE
if (pgmon) pc_dout.data &= ~0x80; // set PGM low (active)
else pc_dout.data |= 0x80; // set PGM high (inactive)
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout); // extend low?
#endif /* TEST_DISABLE_CODE */
break;
case JTAG_SENSOR_FPGA:
//port_csp0_addr[X313_WA_SENSFPGA] = (2 | (pgmon & 1)) << SFPGA_PROG_BIT;
data.prog= pgmon & 1;
data.prog_set = 1;
x393_sensio_jtag(data, chn >> 2); // sens_num);
break;
case JTAG_AUX_FPGA:
break;
}
udelay (2);
}
int read_done (int chn) {
x393_status_sens_io_t stat;
x393_sensio_jtag_t data;
switch (chn >> 2) {
#ifdef TEST_DISABLE_CODE
case JTAG_MAIN_FPGA:
return ((((REG_RD(gio, regi_gio, r_pc_din)).data & 0x20)==0) ? 0 : 1 );
#endif //* TEST_DISABLE_CODE */
case JTAG_SENSOR_FPGA:
//port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_DONE;
//udelay (1);
//return (port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1 ;
stat = wait_sensio_status(chn & 3, prep_sensio_status(chn & 3)) ;
return stat.xfpgadone;
case JTAG_AUX_FPGA:
return 0;
}
return 0; // just in case
}
// send 1..8 bits through JTAG
int jtag_send (int chn, int tms, int len, int d) {
int sens_num = chn & 3;
x393_sensio_jtag_t data;
x393_status_sens_io_t stat;
// u32 seq_num;
int i, bm = 0; //,m;
int r=0;
int d0;
i = len & 7;
if (i==0) i=8;
d &= 0xff;
d0=d;
dev_dbg(NULL, "jtag_send(0x%x, 0x%x, 0x%x, 0x%x)\r\n", chn, tms,len,d);
switch (chn >> 2) {
case JTAG_MAIN_FPGA:
#ifdef TEST_DISABLE_CODE
pc_dout.data &= ~0x0e;
pc_dout.data |= (tms & 1) << FPGAJTAG_TMS_BIT;
for (;i>0;i--){
r= (r<<1)+ ((REG_RD(gio, regi_gio, r_pc_din)).data & 1); // read TDO before TCK pulse
pc_dout.data = (pc_dout.data & ~0x0a) | (((d<<=1)>>7) & 2);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data |= (1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data &= ~(1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
}
#endif /* TEST_DISABLE_CODE */
break;
case JTAG_SENSOR_FPGA:
#ifdef TEST_DISABLE_CODE
port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_TDO;
udelay (1); // wait MUX
for (;i>0;i--){
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
port_csp0_addr[X313_WA_SENSFPGA] = ((2 | (tms & 1)) << SFPGA_TMS_BIT) |
(((((d<<=1)>>8) & 1) | 2) << SFPGA_TDI_BIT) |
(2 << SFPGA_TCK_BIT) ;
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
port_csp0_addr[X313_WA_SENSFPGA] = (3 << SFPGA_TCK_BIT); // TCK=1
r= (r<<1)+ ((port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1); // read TDO before TCK pulse
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
}
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0
#endif /* TEST_DISABLE_CODE */
data.d32 = 0;
data.tck_set = 1;
data.tms_set = 1;
data.tdi_set = 1;
dev_dbg(NULL, "jtag_send(0x%x, 0x%x, 0x%x, 0x%x)\n", chn, tms,len,d);
for ( ; i > 0; i--) {
/* TCK = 0 - just a delay; is it really needed? */
data.tck = 0;
data.tms = tms & 1;
data.tdi = ((d <<= 1) >> 8) & 1;
data.tck = 0;
x393_sensio_jtag(data, sens_num);
/* repeat writel - just a delay; is it really needed? */
// x393_sensio_jtag(data, sens_num);
/* read TDO before TCK pulse */
#ifndef PARALLEL_JTAG
r = (r << 1) + read_tdo(sens_num); // may need to read twice to increase delay?
#else
bm = (bm <<1 ) | 1;
#endif
data.tck = 1;
x393_sensio_jtag(data, sens_num); // keep other signals, set TCK == 1
// x393_sensio_jtag(data, sens_num); // repeat if delay will be needed to increase length of the TCK signal
data.tck = 0;
// x393_sensio_jtag(data, sens_num);
}
x393_sensio_jtag(data, sens_num);
#ifdef PARALLEL_JTAG
r = read_tdo_byte(sens_num) & bm;
#endif
// x393_sensio_jtag(data, sens_num);
dev_dbg(NULL, " ---> %02x\n", r);
break;
case JTAG_AUX_FPGA:
break;
}
return r;
}
//====================================
// port_csp0_addr[X313_WA_SENSFPGA] = 0; // nop
// write data data bytes from buffer, read data, optionally compare/abort
// return: 0- OK, !=0 - readback mismatch error
// modified so it reads data in-place of the written one
// send/receive bits, raising TMS during the last one (if last==1). If number of bits are not multiple of 8, lower bits of the last byte will not be used.
int jtag_write_bits (int chn,
unsigned char *buf, // data to write
int len, // number of bits to write
int check, // compare readback data with previously written, abort on mismatch
int last, // output last bit with TMS=1
int prev[2]) // if null - don't use
{
int sens_num = chn & 3;
int i,j;
int r = 0;
int bm = 0;
int d,d0;
// u32 seq_num;
x393_status_sens_io_t stat;
x393_sensio_jtag_t data;
dev_dbg(NULL, "jtag_write_bits(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\r\n", (int) chn, (int) buf, len, check, last);
switch (chn >> 2) {
case JTAG_MAIN_FPGA: //TODO: save some cycles like for FPGA_SJTAG_MINOR
#ifdef TEST_DISABLE_CODE
for (i=0; len>0;i++) {
pc_dout.data &= ~0x0e;
d0=(d=buf[i]);
for (j=0;j<8;j++) {
//D(printk("i=%x, j=%x, len=%x, d=%x ",i,j,len,d));
if (len>0) {
r= (r<<1)+ ((REG_RD(gio, regi_gio, r_pc_din)).data & 1);
if ((len==1) && last) pc_dout.data = (pc_dout.data & ~0x0a) | (((d<<=1)>>7) & 2) | (1 << FPGAJTAG_TMS_BIT);
else pc_dout.data = (pc_dout.data & ~0x0a) | (((d<<=1)>>7) & 2);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data |= (1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data &= ~(1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
} else r= (r<<1);
len--;
//D(printk(", r=%x\r\n",r));
}
buf[i]=r; // read back in-place
if (check && ((r ^ (prev[1] >> 24)) & 0xff)) {
return -((r & 0xff) | ((i+1) << 8)); //readback mismatch
}
if (prev) {
// prev64= (prev64<<8) | ((prev32>>24) & 0xff);
// prev32= (prev32<<8) | (d0 & 0xff);
prev[1]= (prev[1]<<8) | ((prev[0]>>24) & 0xff);
prev[0]= (prev[0]<<8) | (d0 & 0xff);
}
}
#endif /* TEST_DISABLE_CODE */
break;
case JTAG_SENSOR_FPGA:
#ifdef TEST_DISABLE_CODE
port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_TDO; // just in case, it should be in that mode when calling jtag_write_bits()
udelay (1); // wait MUX
for (i=0; len>0;i++) {
d0=(d=buf[i]);
for (j=0;j<8;j++) {
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
if (len>0) {
if ((len==1) && last) port_csp0_addr[X313_WA_SENSFPGA] =
(3 << SFPGA_TMS_BIT) |
(((((d<<=1)>>8) & 1) | 2) << SFPGA_TDI_BIT) |
(2 << SFPGA_TMS_BIT) |
(2 << SFPGA_TCK_BIT) ;
else port_csp0_addr[X313_WA_SENSFPGA] =
(((((d<<=1)>>8) & 1) | 2) << SFPGA_TDI_BIT) |
(2 << SFPGA_TMS_BIT) |
(2 << SFPGA_TCK_BIT) ;
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
port_csp0_addr[X313_WA_SENSFPGA] = (3 << SFPGA_TCK_BIT); // TCK=1
// add delays here if long cable?
r= ((r<<1)+ ((port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1)); // read TDO before TCK pulse
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
} else r= (r<<1);
len--;
}
buf[i]=r; // read back in-place
if (check && ((r ^ (prev[1]>>24)) & 0xff)) {
return -((r & 0xff) | ((i+1) << 8)); //readback mismatch
}
if (prev) {
// prev64= (prev64<<8) | ((prev32>>24) & 0xff);
// prev32= (prev32<<8) | (d0 & 0xff);
prev[1]= (prev[1]<<8) | ((prev[0]>>24) & 0xff);
prev[0]= (prev[0]<<8) | (d0 & 0xff);
}
}
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0
#endif /* TEST_DISABLE_CODE */
// Can be done once
data.d32 = 0;
data.tck_set = 1;
data.tms_set = 1;
data.tdi_set = 1;
for (i = 0; len > 0; i++) {
d0 = (d = buf[i]);
dev_dbg(NULL,"jtag_write_bits(), i=0x%x ", i);
bm = 0;
for (j = 0; j < 8; j++) {
if (len > 0) {
data.tms = (len == 1 && last)? 1:0 ;
data.tdi = ((d <<= 1) >> 8) & 1;
data.tck = 0;
x393_sensio_jtag(data, sens_num);
#ifndef PARALLEL_JTAG
r = (r << 1) + read_tdo(sens_num);
#else
bm = (bm <<1 ) | 1;
#endif
data.tck = 1;
x393_sensio_jtag(data, sens_num);
data.tck = 0;
x393_sensio_jtag(data, sens_num);
} else {
r <<= 1;
}
len--;
}
#ifdef PARALLEL_JTAG
r = read_tdo_byte(sens_num) & bm;
if (unlikely(len < 0)){
r <<= -len;
}
#endif
buf[i] = r;
dev_dbg(NULL," ===> %02x\n", r);
if (check && ((r ^ (prev[1]>>24)) & 0xff)) {
return -((r & 0xff) | ((i+1) << 8)); //readback mismatch
}
if (prev) {
prev[1]= (prev[1]<<8) | ((prev[0]>>24) & 0xff);
prev[0]= (prev[0]<<8) | (d0 & 0xff);
}
}
break;
case JTAG_AUX_FPGA:
break;
}
return 0;
}
int JTAG_configure (int chn, unsigned char * buf, int len) {
int datastart, i, j ,r;
//static int prev32;
//static int prev64;
int prev[2];
#ifdef JTAG_DISABLE_IRQ
unsigned long flags;
#endif
const unsigned char sync[]={0xff,0xff,0xff,0xff,0xaa,0x99,0x55,0x66};
int skipvfy=8;
dev_dbg(NULL, "JTAG_configure: chn=%x, wp=0x%x, rp=0x%x, len=0x%x\r\n",chn, JTAG_channels[chn].wp, JTAG_channels[chn].rp, len);
// all the programming goes here...
// find sync:
datastart=-1;
for (i=0;i<(FJTAG_MAX_HEAD-8);i++) {
j=0;
while ((j<8) && (buf[i+j]==sync[j])) j++;
if (j==8) {
datastart=i;
break;
}
}
if (datastart<0) {
dev_err(NULL,"Bitstream not found - bad file\r\n");
return -EFAULT;
}
// check for right bitstream length
if ((len-datastart)!=(XC3S1200E_BITSIZE>>3)) {
dev_err(NULL,"Wrong bitstream size - XC3S1200E has bitstream of %d bits (%d bytes)\n",XC3S1200E_BITSIZE,XC3S1200E_BITSIZE>>3);
dev_err(NULL,"header size - %d, data size - %d\r\n",datastart, len-datastart);
return -EFAULT;
}
// enable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 1);
// reset device
set_pgm (chn, 1);
//udelay (1000); // needed?
mdelay(1);
set_pgm (chn, 0);
// wait INIT over - no init connected, just wait >2ms
//udelay (2500);
mdelay(3);
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE PROGRAMMING CYCLE ***********************
//D( udelay (100000);printk("JTAG_configure(): IRQ off!\r\n"); udelay (100000););
D( mdelay (100);printk("JTAG_configure(): IRQ off!\r\n"); mdelay (100););
#ifdef JTAG_DISABLE_IRQ
local_irq_save(flags);
//local_irq_disable();
#endif
// prepare JTAG
jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0xa0); //step 5 - start of CFG_IN ***NOW 6 bits ***
jtag_send(chn, 1, 1, 0 ); //step 6 - finish CFG_IN
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set SHIFT-DR state
// write data (first 8 bytes - just fill the buffer, no readback comparison)
jtag_write_bits (chn,
&buf[datastart], // data to write
skipvfy << 3, // number of bytes to write
0, // compare readback data with previously written, abort on mismatch
0, // do not raise TMS at last bit
prev); // 64 bits storage to verify configuration transmission
if ((r=jtag_write_bits (chn,
&buf[datastart+skipvfy],
// (buf8i-(datastart+skipvfy)) << 3,
(len-(datastart+skipvfy)) << 3,
1,
1, prev))<0) {
r= -r;
i= (r>>8) -1 + (datastart+skipvfy);
r &= 0xff;
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
#endif
set_pgm (chn, 1);
set_pgm (chn, 0);
// disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0);
dev_err(NULL,"**** Configuration failed at byte # %d (%x)****\n", (i-datastart),(i-datastart));
dev_err(NULL,"**** r= %x, prev64=%x prev32=%x****\n", r,prev[1], prev[0]);
return -EFAULT;
}
jtag_send(chn, 1, 1, 0 ); //step 11 - set UPDATE-DR state
jtag_send(chn, 1, 2, 0 ); //step 12 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 13 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x30); //step 14 - start of JSTART ***NOW 6 bits ***
jtag_send(chn, 1, 1, 0 ); //step 15 - finish JSTART
jtag_send(chn, 1, 2, 0 ); //step 16 - set SELECT-DR state
jtag_send(chn, 0, 0, 0 ); //step 17 - set SHIFT-DR , clock startup
jtag_send(chn, 0, 0, 0 ); //step 17a - (total >=12 clocks)
jtag_send(chn, 1, 2, 0 ); //step 18 - set UPDATE-DR state
jtag_send(chn, 0, 1, 0 ); //step 19 - set Run-Test-Idle state
jtag_send(chn, 0, 0, 0 ); //step 19a - only here starts the sequence - adding 17b does not help (5 - sets done, 6 - releases outputs, 7 - releases reset)
jtag_send(chn, 0, 0, 0 ); //step 19b - one more?
// ready or not - device should start now
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
#endif
//*************************** END OF NO INTERRUPS ***********************
r=read_done(chn);
// disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0);
if (r==0) {
dev_err(NULL,"*** FPGA did not start after configuration ***\r\n");
return -EFAULT;
}
//D( udelay (100000);printk("\nJTAG_configure() OK!\r\n"));
D( mdelay (100);printk("\nJTAG_configure() OK!\r\n"));
dev_info(NULL,"JTAG_configure() OK!\n");
return 0;
} //int JTAG_configure
//=============================
//
// enable access to JTAG pins. For sensor FPGA that is not possible w/o deprogramming the chip
// leaves in Run-Test-Idle state
int JTAG_openChannel (int chn) {
dev_dbg(NULL, "JTAG_openChannel (%d)\n",chn);
// enable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 1);
// for shared JTAG/data bus we need to de-program the chip to be able to read JTAG :-(
switch (chn >> 2) {
case JTAG_SENSOR_FPGA:
// reset device
set_pgm (chn, 1);
set_pgm (chn, 0);
// wait INIT over - no init connected, just wait >2ms
//udelay (2500);
mdelay(3);
break;
}
jtag_send(chn, 1, 5, 0 ); // set Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); // set Run-Test-Idle state
return 0;
} // int JTAG_openChannel (int chn)
//
int JTAG_resetChannel (int chn) {
dev_dbg(NULL, "JTAG_resetChannel (%d)\n",chn);
jtag_send(chn, 1, 5, 0 ); // set Test-Logic-Reset state
// disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0); // only for sensor FPGA
return 0;
} // int JTAG_resetChannel (int chn)
int JTAG_readID (int chn, unsigned char * buf) {
int i;
unsigned long d1,d2=0;
unsigned long * dp;
#ifdef JTAG_DISABLE_IRQ
unsigned long flags;
#endif
// read dev id, user id
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE ***********************
#ifdef JTAG_DISABLE_IRQ
local_irq_save(flags);
//local_irq_disable();
#endif
// prepare JTAG
jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x90); //step 5 - start of IDCODE
jtag_send(chn, 1, 1, 0 ); //step 6 - finish IDCODE
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
&buf[0], // data to write
32, // number of bits to write/read
0, // don't compare readback data with previously written
1, 0) ; // raise TMS at last bit
jtag_send(chn, 1, 5, 0 ); //reset state machine to Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x10); //step 5 - start of USERCODE
jtag_send(chn, 1, 1, 0 ); //step 6 - finish USERCODE
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
&buf[4], // data to write
32, // number of bits to write/read
0, // don't compare readback data with previously written
1,0) ; // raise TMS at last bit
jtag_send(chn,1, 5, 0 ); //reset state machine to Test-Logic-Reset state
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
#endif
//*************************** END OF NO INTERRUPS ***********************
// swap all bits in ID and user fields
dp = (unsigned long *) &buf[0];
d1= *dp;
for (i=0;i<32;i++){
d2 = (d2 << 1) | (d1 & 1);
d1 >>= 1;
}
*dp=d2;
dp = (unsigned long *) &buf[4];
d1= *dp;
for (i=0;i<32;i++){
d2 = (d2 << 1) | (d1 & 1);
d1 >>= 1;
}
*dp=d2;
D(for (i=0; i<8;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");} );
data_modified=0; //*************************************************
return 0;
} // int JTAG_readID (int chn, unsigned char * buf)
/*
* capture all pins w/o changing functionality
* assuming Run-Test-Idle/UPDATE-DR leaving UPDATE-DR
*/
int JTAG_CAPTURE (int chn, unsigned char * buf, int len) {
int i; // only in debug
#ifdef JTAG_DISABLE_IRQ
unsigned long flags;
#endif
dev_dbg(NULL,"JTAG_CAPTURE(): buf=%p\n",buf);
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE ***********************
#ifdef JTAG_DISABLE_IRQ
local_irq_save(flags);
//local_irq_disable();
#endif
// prepare JTAG
// jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
// jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x80); //step 5 - start of SAMPLE (which bit goes first???)
jtag_send(chn, 1, 1, 0 ); //step 6 - finish SAMPLE
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
buf, // data to write
len, // number of bits to read
0, // don't compare readback data with previously written
1,0) ; // raise TMS at last bit
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
jtag_send(chn, 1, 1, 0 ); //step 9 - set UPDATE-DR state
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// jtag_send(chn,1, 5, 0 ); //reset state machine to Test-Logic-Reset state
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
#endif
//*************************** END OF NO INTERRUPS ***********************
dev_dbg(NULL, "\n");
for (i=0; i<((len+7)>>3) ;i++) {
dev_dbg(NULL, "%3x ",(int) buf[i]);
if ((i & 0xf) == 0xf) dev_dbg(NULL, "\n");
}
dev_dbg(NULL, "\n");
data_modified=0;
return 0;
} // JTAG_CAPTURE (int chn, unsigned char * buf, int len) {
/*
* write new boundary registers (len should match BS register length), read pins in-place
* TAP controller is supposed to be in "UPDATE-DR" state (or RUN-TEST/IDLE after just opening this mode)
* TAP controller will be left in the same "UPDATE-DR" after the command
*/
int JTAG_EXTEST (int chn, unsigned char * buf, int len) {
#ifdef JTAG_DISABLE_IRQ
unsigned long flags;
#endif
int i; // only in debug
#ifdef JTAG_DISABLE_IRQ
local_irq_save(flags);
//local_irq_disable();
#endif
//D(printk("EXTEST: buf=%p, len=0x%x\n",buf,len));
//D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
// jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0xf0); //step 5 - start of EXTEST
jtag_send(chn, 1, 1, 0 ); //step 6 - finish EXTEST
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
buf,
len, // number of bits to write
0, // don't compare readback data with previously written
1,0); // raise TMS at last bit
jtag_send(chn, 1, 1, 0 ); //step 9 - set UPDATE-DR state
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
#endif
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
return 0;
} //int JTAG_EXTEST (int chn, unsigned char * buf, int len)
static int __init fpga_jtag_init(void) {
int i,res;
res = register_chrdev(FPGA_JTAG_MAJOR, fpga_jtag_name, &fpga_jtag_fops);
if(res < 0) {
dev_err(NULL,"\nfpga_jtag_init: couldn't get a major number %d.\n",FPGA_JTAG_MAJOR);
return res;
}
dev_dbg(NULL,FPGA_JTAG_DRIVER_NAME" - %d\n",FPGA_JTAG_MAJOR);
for (i=0;i<=FPGA_JTAG_MAXMINOR;i++) minors[i]=0;
initPortC();
dev_dbg(NULL, "elphel test %s: MAJOR %d", FPGA_JTAG_DRIVER_NAME, FPGA_JTAG_MAJOR);
res = init_mmio_ptr();
if (res < 0)
return -ENOMEM;
return 0;
}
static void __exit fpga_jtag_exit(void)
{
unregister_chrdev(FPGA_JTAG_MAJOR, FPGA_JTAG_DRIVER_NAME);
dev_dbg(NULL, "unregistering driver");
}
module_exit(fpga_jtag_exit);
/* this makes sure that fpga_init is called during boot */
module_init(fpga_jtag_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov ");
MODULE_DESCRIPTION(FPGA_JTAG_DRIVER_NAME);
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/framepars.c 0000664 0000000 0000000 00000177215 12762437450 0027057 0 ustar 00root root 0000000 0000000 /** @file framepars.c
* @brief Handling of frame parameters, making use of FPGA i2c
* and command sequencer that accepts commands up to 6 frames ahead.
* This module includes parameter storage, code called from ISR,
* from other kernel drivers as well as from the user space
* Copyright (C) 2016 Elphel, Inc.
*
* 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 .
*/
//copied from cxi2c.c - TODO:remove unneeded
#include /// div for 64
#include /// div for 64
#include
#include
#include
#include
//#include
#include
#include
#include
#include
//#include
#include
#include
//#include
//#include // endians
//#include
//#include
//#include
#include
#include
#include
#include
//#include "fpgactrl.h" // defines port_csp0_adsensor_common.hdr, port_csp4_addr
//#include "cc3x3.h"
//#include "x3x3.h" // hardware definitions
#include "sensor_common.h"
#include "framepars.h"
#include "param_depend.h" // specifies what functions should be called for different parameters changed
/// needed for lseek commands
//#include "cxdma.h" // x313_dma_init
//#include "cci2c.h" // to use void i2c_reset_wait(void), reset shadow static 'i2c_hardware_on'
#include "x393_macro.h"
#include "x393.h"
//#undef _LINUX_WAIT_H
//#include
/**
* \def MDF1(x) optional debug output
*/
#if ELPHEL_DEBUG
/* 393: program G_DEBUG through sensor port 0 */
#define MDF(x) { printk("%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__); x; }
#define MDF2(x) { if (GLOBALPARS(0,G_DEBUG) & (1 << 2)) { printk("%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__); x; } }
/// setFrameParsAtomic
#define MDF5(x) { if (GLOBALPARS(0,G_DEBUG) & (1 << 5)) { printk("%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__); x; } }
#define D5(x) { if (GLOBALPARS(0,G_DEBUG) & (1 << 5)) { x; } }
/// processPars
#define MDF6(x) { if (GLOBALPARS(0,G_DEBUG) & (1 << 6)) { printk("%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__); x; } }
#define D6(x) { if (GLOBALPARS(0,G_DEBUG) & (1 << 6)) { x; } }
///update FramePars
#define MDF7(x) { if (GLOBALPARS(0,G_DEBUG) & (1 << 7)) { printk("%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__); x; } }
#define D7(x) { if (GLOBALPARS(0,G_DEBUG) & (1 << 7)) { x; } }
/// setFramePar[s]
#define MDF8(x) { if (GLOBALPARS(0,G_DEBUG) & (1 << 8)) { printk("%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__); x; } }
#define D8(x) { if (GLOBALPARS(0,G_DEBUG) & (1 << 8)) { x; } }
#define ELPHEL_DEBUG_THIS 0
// #define ELPHEL_DEBUG_THIS 1
#else
#define MDF(x)
#define MDF2(x)
#define MDF5(x)
#define D5(x)
#define MDF6(x)
#define D6(x)
#define MDF7(x)
#define D7(x)
#define MDF8(x)
#define D8(x)
#define ELPHEL_DEBUG_THIS 0
#endif
#if ELPHEL_DEBUG_THIS
#define MDD1(x) printk("%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__); x; udelay(ELPHEL_DEBUG_DELAY)
#define MDF1(x) printk("%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__); x
#define D1(x) x
#define D1I(x)
#else
#define MDD1(x)
#define MDF1(x)
#define D1(x)
#define D1I(x) x
#endif
/**
* \def FRAMEPARS_DRIVER_NAME driver name to display
*/
#define FRAMEPARS_DRIVER_NAME "Elphel (R) Model 393 Frame Parameters device driver"
/* 393: sFrameParsAll is an array of 4per-port structures */
static struct framepars_all_t sFrameParsAll[SENSOR_PORTS] __attribute__ ((aligned(PAGE_SIZE))); ///< Sensor Parameters, currently 16 pages all and 2048 pages some, static struct
unsigned long frameParsInitialized; /// set to 0 at startup, 1 after initialization that is triggered by setParsAtomic()
#define thisFrameNumber(p) GLOBALPARS(p,G_THIS_FRAME) // Current frame number (may lag from the hardware)
#ifdef NC353
struct framepars_all_t *frameparsall = NULL; /// - will be mmap-ed
struct framepars_t *framepars = NULL; ///< getting rid of static to be able to use extern
struct framepars_past_t *pastpars = NULL; ///< getting rid of static to be able to use extern
unsigned long *funcs2call = NULL; /// sFrameParsAll.func2call.pars; - each parameter has a 32-bit mask of what pgm_function to call - other fields not used
unsigned long *globalPars = NULL; /// parameters that are not frame-related, their changes do not initiate any actions so they can be mmaped for both
unsigned long *multiSensIndex = NULL; /// index for per-sensor alternatives
unsigned long *multiSensRvrsIndex = NULL; /// reverse index (to parent) for the multiSensIndex
wait_queue_head_t framepars_wait_queue; /// used to wait for the frame to be acquired
#endif
/* 393 : Changing to per-port */
struct framepars_all_t *aframeparsall = NULL; /// - will be mmap-ed, in 393 points to an array of structures
struct framepars_t *aframepars[SENSOR_PORTS]; ///< getting rid of static to be able to use extern
struct framepars_past_t *apastpars[SENSOR_PORTS]; ///< getting rid of static to be able to use extern
unsigned long *afuncs2call[SENSOR_PORTS]; /// sFrameParsAll.func2call.pars; - each parameter has a 32-bit mask of what pgm_function to call - other fields not used
unsigned long *aglobalPars[SENSOR_PORTS]; /// parameters that are not frame-related, their changes do not initiate any actions so they can be mmaped for both
unsigned long *amultiSensIndex[SENSOR_PORTS]; /// index for per-sensor alternatives
unsigned long *amultiSensRvrsIndex[SENSOR_PORTS]; /// reverse index (to parent) for the multiSensIndex
wait_queue_head_t aframepars_wait_queue[SENSOR_PORTS];/// used to wait for the frame to be acquired
/* Remove after compilation OK */
struct sensorproc_t * sensorproc = NULL;
//void camera_interrupts (int on) {}
#if 0
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
might_sleep(); \
if (!(condition)) \
__ret = __wait_event_interruptible(wq, condition); \
__ret; \
})
#define __wait_event_interruptible_timeout(wq, condition, timeout) \
___wait_event(wq, ___wait_cond_timeout(condition), \
TASK_INTERRUPTIBLE, 0, timeout, \
__ret = schedule_timeout(__ret))
#define __wait_event_interruptible(wq, condition) \
___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \
schedule())
#endif
//wait_queue_head_t framepars_wait_queue; /// used to wait for the frame to be acquired
/**
* @brief file private data
*/
struct framepars_pd {
int minor; ///< file minor value
struct wait_queue *framepars_wait_queue; ///< wait queue (waiting for file number to increase) ///NOTE: not used at all?
// something else to be added here?
};
/**
* @brief assign non-static pointers to static data to be used as extern
*/
void init_framepars_ptr(int sensor_port)
{
// frameparsall = &sFrameParsAll; /// - will be mmap-ed
aframeparsall = sFrameParsAll; /// - will be mmap-ed
aframepars[sensor_port] = sFrameParsAll[sensor_port].framePars;
apastpars[sensor_port] = sFrameParsAll[sensor_port].pastPars;
afuncs2call[sensor_port] = sFrameParsAll[sensor_port].func2call.pars; /// each parameter has a 32-bit mask of what pgm_function to call - other fields not used
aglobalPars[sensor_port] = sFrameParsAll[sensor_port].globalPars; /// parameters that are not frame-related, their changes do not initiate any actions so they can be mmaped for both
amultiSensIndex[sensor_port] = sFrameParsAll[sensor_port].multiSensIndex; /// indexes of individual sensor register shadows (first of 3) - now for all parameters, not just sensor ones
amultiSensRvrsIndex[sensor_port] = sFrameParsAll[sensor_port].multiSensRvrsIndex; /// reverse index (to parent) for the multiSensIndex
}
int framepars_open(struct inode *inode, struct file *filp);
int framepars_release(struct inode *inode, struct file *filp);
loff_t framepars_lseek(struct file * file, loff_t offset, int orig);
ssize_t framepars_write(struct file * file, const char * buf, size_t count, loff_t *off);
int framepars_mmap(struct file *file, struct vm_area_struct *vma);
/**
* @brief Reset hardware sequencers (i2c, command) and initialize framepars structure
*/
void initSequencers(int sensor_port)
{
unsigned long flags;
MDF2(printk("\n"));
printk("initSequencers:resetting both sequencers\n");
#ifdef TEST_DISABLE_CODE
local_irq_save(flags);
X3X3_SEQ_RESET;
i2c_reset_wait();
local_irq_restore(flags);
initFramePars();
#endif
}
/**
* @brief reset absolute frame number \b thisFrameNumber to \b frame8
* @param sensor_port sensor port number (0..3)
*/
void resetFrameNumber(int sensor_port)
{
int i;
// Setup update mode for
x393_cmdseqmux_status_t stat;
x393_status_ctrl_t stat_ctrl;
/* Check if the status update mode for command sequencer is not 3 (auto), set/wait if needed */
stat_ctrl = get_x393_cmdseqmux_status_ctrl();
if (stat_ctrl.mode !=3) {
stat = x393_cmdseqmux_status();
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 3;
set_x393_cmdseqmux_status_ctrl(stat_ctrl);
for (i = 0; i < 10; i++) {
stat = x393_cmdseqmux_status();
if (likely(stat.seq_num == stat_ctrl.seq_num))
break;
}
} else {
stat = x393_cmdseqmux_status();
}
switch (sensor_port) {
case 0: thisFrameNumber(sensor_port) = stat.frame_num0; break;
case 1: thisFrameNumber(sensor_port) = stat.frame_num1; break;
case 2: thisFrameNumber(sensor_port) = stat.frame_num2; break;
case 3: thisFrameNumber(sensor_port) = stat.frame_num3; break;
}
#ifdef NC353
thisFrameNumber(sensor_port) = X3X3_I2C_FRAME;
#endif
MDF2(printk(" thisFrameNumber=0x%lx\n", thisFrameNumber(sensor_port)));
// write absolute frame numbers
for (i = thisFrameNumber(sensor_port); i < (thisFrameNumber(sensor_port) + PARS_FRAMES); i++) aframepars[sensor_port][i & PARS_FRAMES_MASK].pars[P_FRAME] = i;
}
/**
* @brief initialize all parameters, set \b thisFrameNumber to \b frame number read from hardware hardware ( 0 after resetting i2c and cmd_seq)
* @param sensor_port sensor port number (0..3)
*/
void initFramePars(int sensor_port)
{
int i;
memset(aframepars[sensor_port], 0, sizeof(struct framepars_t) * PARS_FRAMES);
resetFrameNumber(sensor_port);
/// initialize frameParsDeps.pars masks:
for (i = 0; i < (sizeof(param_depend_tab) / 8); i++) {
afuncs2call[sensor_port][param_depend_tab[2 * i] & 0xffff] = param_depend_tab[2 * i + 1]; /// remove possible flags
MDF2(printk("funcs2call[0x%lx]=0x%08lx\n", param_depend_tab[2 * i] & 0xffff, param_depend_tab[2 * i + 1]));
}
for (i = 0; i < P_SENSOR_NUMREGS; i++) afuncs2call[sensor_port][P_SENSOR_REGS + i] = ONCHANGE_SENSORREGS; /// by default each "manual" write to any of 256 registers will trigger pgm_sensorreg function
/// Same for 10359 registers - will not change anything if there is no 10359 - these registers will not be changed, and if will be it wil cause no action
for (i = 0; i < P_M10359_NUMREGS; i++) afuncs2call[sensor_port][P_M10359_REGS + i] = ONCHANGE_SENSORREGS; /// by default each "manual" write to any of 256 registers will trigger pgm_sensorreg function
initMultiPars(sensor_port); /// initialize structures for individual per-sensor parameters. Now only works for sensor registers using G_MULTI_REGSM. Should be called after/during sensor detection
frameParsInitialized = 1;
}
/**
* @brief reset all global parameters, set default for debug mask (if ELPHEL_DEBUG)
* @param sensor_port sensor port number (0..3)
*/
void initGlobalPars(int sensor_port)
{
memset(&aglobalPars[sensor_port][GLOBALS_PRESERVE], 0, (NUM_GPAR - GLOBALS_PRESERVE) * sizeof(unsigned long));
/// MDF(GLOBALPARS(G_DEBUG) = ELPHEL_DEBUG_STARTUP;// removed - add write to fpga init script
MDF(printk("GLOBALPARS(0,G_DEBUG)=%lx\n", GLOBALPARS(0,G_DEBUG)));
}
/**
* @brief initialize structures for individual per-sensor parameters. Now only works for sensor registers using G_MULTI_REGSM. Should be called after/during sensor detection
* @param sensor_port sensor port number (0..3)
* @return number of multi-regs
*/
int initMultiPars(int sensor_port)
{
int i, j, n;
int ireg = P_MULTI_REGS; /// multi-reg shadows start index
unsigned long m;
// memset(amultiSensIndex[sensor_port], 0, sizeof(struct framepars_all_t.multiSensIndex));
memset(amultiSensIndex[sensor_port], 0, P_MAX_PAR_ROUNDUP * sizeof(int));
// memset(multiSensRvrsIndex, 0, sizeof(multiSensRvrsIndex));
memset(amultiSensRvrsIndex[sensor_port], 0, P_MAX_PAR_ROUNDUP * sizeof(int));
GLOBALPARS(sensor_port,G_MULTI_NUM) = 0;
for (i = 0; i < 8; i++) {
m = GLOBALPARS(sensor_port, G_MULTI_REGSM + i); /// 1 bit per register that need individual shadows
// MDF(printk("i=%d, m=0x%lx\n",i,m));
for (j = P_SENSOR_REGS + (i << 5); m && (GLOBALPARS(sensor_port,G_MULTI_NUM) < P_MULTI_NUMREGS); j++, m >>= 1) {
if (m & 1) {
amultiSensIndex[j] = ireg;
// MDF(printk("j=0x%x ireg=0x%x\n",j,ireg));
for (n = 0; n < MAX_SENSORS; n++) {
afuncs2call[sensor_port][ireg] = ONCHANGE_SENSORREGS; /// by default each "manual" write to any of these registers will trigger pgm_sensorreg function
amultiSensRvrsIndex[sensor_port][ireg++] = j | ((n + 1) << 16);
}
GLOBALPARS(sensor_port,G_MULTI_NUM)++;
}
}
}
// remark: the line below is called from initFramePars, consider removing it
for (i = 0; i < P_SENSOR_NUMREGS; i++) afuncs2call[sensor_port][P_SENSOR_REGS + i] = ONCHANGE_SENSORREGS; /// by default each "manual" write to any of 256 registers will trigger pgm_sensorreg function
MDF(printk("GLOBALPARS(G_MULTI_NUM)=%lx\n", GLOBALPARS(sensor_port, G_MULTI_NUM)));
return GLOBALPARS(sensor_port, G_MULTI_NUM);
}
/**
* @brief reads parameters from the current frame (matching hardware index)
* @param sensor_port sensor port number (0..3)
* @param n number of a parameter to read
* @return parameter value (unsigned long)
*/
inline unsigned long get_imageParamsThis(int sensor_port, int n)
{
return aframepars[sensor_port][thisFrameNumber(sensor_port) & PARS_FRAMES_MASK].pars[n];
}
/**
* @brief reads parameters from the previous frame (matching hardware index) - used to determine if historam was needed
* @param sensor_port sensor port number (0..3)
* @param n number of a parameter to read
* @return parameter value (unsigned long)
*/
inline unsigned long get_imageParamsPrev(int sensor_port, int n)
{
return aframepars[sensor_port][(thisFrameNumber(sensor_port) - 1) & PARS_FRAMES_MASK].pars[n];
}
/**
* @brief writes read-only parameter to the current frame (does not propagate to next frames as setFramePar() does)
* In most cases you really need to use setFramePar() instead;
* @param sensor_port sensor port number (0..3)
* @param n number of a parameter to set
* @param d data to write to the selected parameter
*/
inline void set_imageParamsThis(int sensor_port, int n, unsigned long d)
{
aframepars[sensor_port][thisFrameNumber(sensor_port) & PARS_FRAMES_MASK].pars[n] = d;
}
/**
* @brief reads global (not related to particular frames) parameters
* @param sensor_port sensor port number (0..3)
* @param n number of a parameter to read (numbers start from FRAMEPAR_GLOBALS)
* @return parameter value (unsigned long)
*/
inline unsigned long get_globalParam(int sensor_port, int n)
{
return GLOBALPARS(sensor_port, n);
}
/**
* @brief sets global (not related to particular frames) parameters
* @param sensor_port sensor port number (0..3)
* @param n number of a parameter to set (numbers start from FRAMEPAR_GLOBALS)
* @param d data to write to the selected parameter
*/
inline void set_globalParam(int sensor_port, int n, unsigned long d)
{
GLOBALPARS(sensor_port, n) = d;
}
/**
* @brief set same parameters in all frames
* currently used only in compressor reset
* @param sensor_port sensor port number (0..3)
* @param n number of a parameter to set
* @param d data to write to the selected parameter
*/
inline void set_imageParamsR_all(int sensor_port, int n, unsigned long d)
{
int i;
for (i = 0; i < PARS_FRAMES; i++) aframepars[sensor_port][i].pars[n] = d;
}
///++++++++++++++++++++++++++++++++++++++++++
/*!
* @brief called from ISR - advance thisFrameNumber to match hardware frame8, copy parameters as needed.
* before: (thisFrameNumber mod8 pointed to current (for the software) parameters frame (now behind by at least 1, maybe 2)
* (thisFrameNumber-1) mod 8 - oldest with parameters preserved, also containes histograms results (+image timestamp, size?)
* subset of that frame data is copied to pastpars
* (thisFrameNumber-2) mod 8 - farthest in the future frame
* after: thisFrameNumber matches hardware pointer
* @param sensor_port sensor port number (0..3)
* @param interframe_pars pointer to structure (between frames in the frame buffer) to save a pointer to past parameters
* pass NULL if compressor was off (or no time to copy?)
*/
void updateFramePars(int sensor_port, int frame8, struct interframe_params_t * interframe_pars)
{
int findex_this, findex_prev, findex_future, findex_next;
int index, index32;
unsigned long bmask, bmask32;
int pastParsIndex;
struct framepars_t *framepars = aframepars[sensor_port];
/// If interrupt was from compression done (circbuf advanced, interframe_pars!=null), the frame8 (hardware) maybe not yet advanced
/// We can fix it here, but it will not work if some frames were not processed in time
if ((interframe_pars != NULL) && (((frame8 ^ thisFrameNumber(sensor_port)) & PARS_FRAMES_MASK) == 0)) {
findex_this = frame8 & PARS_FRAMES_MASK;
/* 393 TODO: Check what to do with P_IRQ_SMART */
if (framepars[findex_this].pars[P_IRQ_SMART] & 4) frame8 = (frame8 + 1) & PARS_FRAMES_MASK; // verify that this mode is enabled (together with bit0)
}
while ((frame8 ^ thisFrameNumber(sensor_port)) & PARS_FRAMES_MASK) {
/// before update:
/// framepars[findex_prev] holds previous frame data (oldest availble)
/// framepars[findex_future] holds farthest in the future one
/// after update:
/// framepars[findex_prev] holds farthest in the future one ("this" will become "prev")
findex_this = thisFrameNumber(sensor_port) & PARS_FRAMES_MASK;
findex_prev = (findex_this - 1) & PARS_FRAMES_MASK;
findex_future = (findex_this - 2) & PARS_FRAMES_MASK; // farthest in the future
findex_next = (findex_this + 1) & PARS_FRAMES_MASK;
/// copy subset of the parameters to the long buffer of past parameters. TODO: fill Exif also here?
/// TODO:DONE: Change - make pastpars be save for all frames, not just compressed ones
/// With PASTPARS_SAVE_ENTRIES being multiple of PARS_FRAMES - make it possible to calculate past_index from thisFrameNumber
// pastParsIndex= thisFrameNumber & PASTPARS_SAVE_ENTRIES_MASK;
pastParsIndex = (thisFrameNumber(sensor_port) - 1) & PASTPARS_SAVE_ENTRIES_MASK; /// copying from what was past frame that might include histogram data
// memcpy (pastpars[pastParsIndex].past_pars, &framepars[findex_prev].pars[PARS_SAVE_FROM], sizeof(pastpars[0].past_pars));
memcpy(apastpars[sensor_port][pastParsIndex].past_pars, &framepars[findex_prev].pars[PARS_SAVE_FROM], PARS_SAVE_COPY * sizeof(int));
/// Now update interframe_pars (interframe area) used to create JPEG headers. Interframe area survives exactly as long as the frames themselves (not like pastpars)
if (interframe_pars) { /// frame was compressed, not just vsync
///TODO: get rid of *_prev, use it for the future.
memcpy(interframe_pars, &framepars[findex_this].pars[P_GTAB_R], 24); /// will leave some gaps, but copy [P_ACTUAL_WIDTH]
interframe_pars->height = framepars[findex_this].pars[P_ACTUAL_HEIGHT]; /// NOTE: P_ACTUAL_WIDTH,P_QUALITY copied with memcpy
interframe_pars->color = framepars[findex_this].pars[P_COLOR];
interframe_pars->byrshift = framepars[findex_this].pars[P_COMPMOD_BYRSH];
interframe_pars->quality2 |= (framepars[findex_this].pars[P_PORTRAIT] & 1) << 7;
}
/// copy parameters from findex_future (old "farthest in the future") to findex_prev (new "fartherst in the future") if it was changed since
if ((bmask32 = framepars[findex_prev].modsince32)) {
MDF7(printk("framepars[%d].modsince32=0x%lx\n", findex_prev, bmask32));
for (index32 = 0; bmask32; index32++, bmask32 >>= 1) {
if (bmask32 & 1) {
for (index = (index32 << 5), bmask = framepars[findex_prev].modsince[index32]; bmask; index++, bmask >>= 1)
if (bmask & 1) {
framepars[findex_prev].pars[index] = framepars[findex_future].pars[index];
MDF7(printk("hw=%d framepars[%d].pars[%d]=framepars[%d].pars[%d]=0x%lx\n", frame8, findex_prev, index, findex_future, index, framepars[findex_future].pars[index]));
}
framepars[findex_prev].modsince[index32] = 0; /// mark as not "modified since" (yet)
}
}
framepars[findex_prev].modsince32 = 0; /// mark as not "modified since" super index
}
/// clear "modified" and flags on the brand new future frame
// remark: framepars[findex_prev].mod is 31 dwords long abd here 32 dwords are cleared
// explanation: mod32 goes after mod[31] and it is cleared too
if (framepars[findex_prev].mod32) memset(framepars[findex_prev].mod, 0, 32 * 4); /// .mod[0]-.mod[30], .mod32
framepars[findex_prev].functions = 0; /// No functions yet needed on the brand new frame
// remark: replace number 7 with named constant, it should correspond to total frame num (16 in new camera)
framepars[findex_prev].pars[P_FRAME] = thisFrameNumber(sensor_port) + (PARS_FRAMES-1); /// that will be the full frame number
/// NOTE: Handle past due - copy functions, and mod if functions were non-zero
if (framepars[findex_this].functions) { /// Some functions were not yet processed (past due)
if (!(get_globalParam(sensor_port, G_TASKLET_CTL) & (1 << TASKLET_CTL_IGNPAST))) {
framepars[findex_next].functions |= framepars[findex_this].functions;
if ((bmask32 = framepars[findex_this].mod32)) {
for (index32 = 0; bmask32; index32++, bmask32 >>= 1) {
if (bmask32 & 1) {
framepars[findex_next].mod[index32] |= framepars[findex_this].mod[index32];
}
framepars[findex_next].mod32 |= framepars[findex_this].mod32;
}
}
MDF7(printk("resubmitting past due functions = 0x%lx for frame=%ld (0x%x)\n", framepars[findex_this].functions, thisFrameNumber(sensor_port), findex_this));
} else {
MDF(printk("Ignored past due functions = 0x%lx for frame=%ld (0x%x)\n", framepars[findex_this].functions, thisFrameNumber(sensor_port), findex_this));
}
}
thisFrameNumber(sensor_port)++;
}
}
/**
* @brief process parameters that are overdue or due in ASAP mode (not through the sequencer)
* Called twice from processPars - at the beginning and at the end to finish off any derivatives (needed?)
* @param sensor_port sensor port number (0..3)
* @param sensorproc
* @param frame8
*/
inline void processParsASAP(int sensor_port, struct sensorproc_t * sensorproc, int frame8)
{
unsigned long todo, mask, remain;
int pars_ahead; /// considering parameter "pars_ahead" of the (frame8+job_ahead) mod 8
int frame_proc; /// current frame for which parameters are considered
struct framepars_t * procpars;
struct framepars_t * prevpars; /// maybe - drop calculation for each function, move it to pgm_* where needed?
struct framepars_t *framepars = aframepars[sensor_port];
unsigned long * p_nasap = &GLOBALPARS(sensor_port, G_CALLNASAP);
int i;
int rslt;
#if ELPHEL_DEBUG
unsigned long allfunctions = framepars[0].functions | framepars[1].functions | framepars[2].functions | framepars[3].functions |
framepars[4].functions | framepars[5].functions | framepars[6].functions | framepars[7].functions;
if (allfunctions) MDF6(printk("frame8=%d, functions: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", frame8, framepars[0].functions,
framepars[1].functions, framepars[2].functions, framepars[3].functions, framepars[4].functions, framepars[5].functions,
framepars[6].functions, framepars[7].functions));
#endif
/// do all ASAP tasks (they should not be done ahead of the corresponding interrupt!)
/// Now try overdue functions with latencies >=1 and try them in ASAP mode
for (pars_ahead = 0; pars_ahead <= 4; pars_ahead++ ) {
frame_proc = (frame8 + pars_ahead) & PARS_FRAMES_MASK;
procpars = &framepars[frame_proc];
prevpars = &framepars[(frame_proc - 1) & PARS_FRAMES_MASK];
i = 0;
mask = 1;
remain = 0xffffffff;
while ((todo = (pars_ahead) ?
(p_nasap[pars_ahead] & (procpars->functions) & remain) :
(procpars->functions & remain) )) { ///none, *1, *2,*3,*4
while (!(todo & mask)) { /// skip zeros - todo will stay current (.functions will not change
i++;
mask <<= 1;
remain <<= 1;
}
/// now (todo & mask) !=0
MDF6(printk(" todo=0x%08lx (curr=0x%08lx) frame8=%d, pars_ahead=%d, frame_proc=%d i=%d, mask=0x%08lx\n",
todo, procpars->functions, frame8, pars_ahead, frame_proc, i, mask));
MDF6(printk(" %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
framepars[0].functions, framepars[1].functions, framepars[2].functions, framepars[3].functions,
framepars[4].functions, framepars[5].functions, framepars[6].functions, framepars[7].functions));
if (sensorproc->pgm_func[i]) {
rslt = sensorproc->pgm_func[i] ( &(sensorproc->sensor), procpars, prevpars, -1);
} else rslt = 0; /// only sensor-specific function, nothing to do common to all sensors
if ((rslt >= 0) && (sensorproc->pgm_func[i + 32])) { /// sensor - specific functions, called after the main ones
rslt = sensorproc->pgm_func[i + 32] ( &(sensorproc->sensor), procpars, prevpars, -1);
}
/// Nothing to do with errors here - just report?
if (rslt < 0) printk("%s:%d:%s - error=%d", __FILE__, __LINE__, __FUNCTION__, rslt);
procpars->functions &= ~mask;
MDF6(printk(".functions=0x%08lx)\n", procpars->functions));
i++;
mask <<= 1;
remain <<= 1;
}
}
}
/// Next 5 should go in that sequence
//#define G_CALLNASAP 119 // bitmask - what functions can be used not only in the current frame (ASAP) mode
//#define G_CALLNEXT1 120 // bitmask of actions to be one or more frames ahead of the programmed one (OR-ed with G_CALLNEXT2..G_CALLNEXT4)
//#define G_CALLNEXT2 121 // bitmask of actions to be two or more frames ahead of the programmed one (OR-ed with G_CALLNEXT3..G_CALLNEXT4)
//#define G_CALLNEXT3 122 // bitmask of actions to be three or more frames ahead of the programmed one (OR-ed with G_CALLNEXT4)
//#define G_CALLNEXT4 123 // bitmask of actions to be four or more frames ahead of the programmed one
inline void processParsSeq(int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead)
{
unsigned long todo, mask, remain;
int job_ahead; /// doing job "job_ahead" ahead of needed
int pars_ahead; /// considering parameter "pars_ahead" of the (frame8+job_ahead) mod 8
int frame_proc; /// current frame for which parameters are considered
struct framepars_t * procpars;
struct framepars_t * prevpars; /// maybe - drop calculation for each function, move it to pgm_* where needed?
struct framepars_t *framepars = aframepars[sensor_port];
unsigned long * p_nasap = &GLOBALPARS(sensor_port, G_CALLNASAP);
int seq_frame; /// sequencer frame for which pgm_* function should schedule data
int i;
int rslt;
int max_par_ahead;
int this_ahead;
if (maxahead > (PARS_FRAMES - 3)) maxahead = PARS_FRAMES - 3; /// use 5 if maxahead >5
/// commands that use FPGA queues for the i2c/sequencer commands, executed at frame syncs
/// Modifying - as soon as found the frame to process with non-zero masked .functions - process all functions for that
/// frame with appropriate sequencer frame.
/// For now - scan p_nasap[i] to find latency - improve that later
for (job_ahead = 0; job_ahead <= maxahead; job_ahead++ ) {
max_par_ahead = min(5, (PARS_FRAMES - 3) - job_ahead);
for (pars_ahead = 0; pars_ahead < max_par_ahead; pars_ahead++ ) {
frame_proc = (frame8 + job_ahead + pars_ahead + 1) & PARS_FRAMES_MASK; ///
procpars = &framepars[frame_proc];
/// Check if at least one function is needed for frame_proc
if (procpars->functions &
p_nasap[pars_ahead] & ///all, *1, *2,*3,*4 - for all will have G_CALLNASAP twice
p_nasap[0]) {
prevpars = &framepars[(frame_proc - 1) & PARS_FRAMES_MASK];
// seq_frame= (frame8+job_ahead+1) & PARS_FRAMES_MASK;
i = 0;
mask = 1;
remain = 0xffffffff;
while ((todo = procpars->functions &
/// p_nasap[pars_ahead] & ///all, *1, *2,*3,*4 - for all will have G_CALLNASAP twice
p_nasap[0] & remain)) { /// eliminate ASAP-only function
while (!(todo & mask)) { /// skip zeros - todo will stay current (.functions will not change)
i++;
mask <<= 1;
remain <<= 1;
}
/// now (todo & mask) !=0
/// find the right latency
for (this_ahead = 1; (p_nasap[this_ahead] & todo & mask) && (this_ahead <= 4); this_ahead++) ; /// this_ahead==1..5
// seq_frame= (frame8 + job_ahead + this_ahead) & PARS_FRAMES_MASK;
seq_frame = (frame_proc + 1 - this_ahead) & PARS_FRAMES_MASK;
MDF6(printk(" todo=0x%08lx (curr=0x%08lx) frame8=%d, frame_proc=%d, seq_frame=%d, i=%d, mask=0x%08lx\n", todo, procpars->functions, frame8, frame_proc, seq_frame, i, mask));
MDF6(printk(" %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", framepars[0].functions, framepars[1].functions, framepars[2].functions, framepars[3].functions, framepars[4].functions, framepars[5].functions, framepars[6].functions, framepars[7].functions));
if (sensorproc->pgm_func[i]) {
/// NOTE: Was (frame8+job_ahead +1) & PARS_FRAMES_MASK
rslt = sensorproc->pgm_func[i] ( &(sensorproc->sensor), procpars, prevpars, seq_frame);
} else rslt = 0; /// only sensor-specific function, nothing to do common to all sensors
if ((rslt >= 0) && (sensorproc->pgm_func[i + 32])) { /// sensor - specific functions, called after the main ones
rslt = sensorproc->pgm_func[i + 32] ( &(sensorproc->sensor), procpars, prevpars, seq_frame);
}
if (rslt >= 0) {
procpars->functions &= ~mask; /// mark it done
} else {
MDF6(printk("Error - function result was %d\n", rslt));
}
i++;
mask <<= 1;
remain <<= 1;
}
}
}
}
}
/**
* @brief Program image acquisition, according to the parameters changed
* Called from ISR?
* @param sensor_port sensor port number (0..3)
* @param sensorproc pointer to sensor static parameters and functions
* @param frame8 current hardware frame number
* @param maxahead maximal number of frames to program ahead of the current (make it P_* parameter ?)
* @return always 0 ?
*/
//TODO: "Do it later" should be the only reason not to erase todo bit
//#define P_CALLASAP 107 // bitmask - what functions work only in the current frame (ASAP) mode
void processPars(int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead)
{
frame8 &= PARS_FRAMES_MASK;
/// first - do all ASAP tasks (they should not be done ahead of the corresponding interrupt!)
// MDF6(printk("before first processParsASAP\n"));
processParsASAP(sensor_port, sensorproc, frame8);
/// now - the rest commands that use FPGA queues for the i2c/sequencer commands, executed at frame syncs
/// for jobahead =0 it is still possible to have some functions in ASAP mode with non-zero latency
// MDF6(printk("before processParsSeq\n"));
processParsSeq(sensor_port, sensorproc, frame8, maxahead);
/// re-test ASAP tasks - they might appear as a result of other commands executed
// MDF6(printk("before second processParsASAP\n"));
processParsASAP(sensor_port, sensorproc, frame8);
}
/**
* @brief schedule pgm_func to be executed for selected frame (frame8)
* @param sensor_port sensor port number (0..3)
* @param frame8 frame number (3-bit) to schedule a function for
* @param func_num function number to schedule
*/
void schedule_pgm_func(int sensor_port, int frame8, int func_num)
{
MDF1(printk(" frame8=%d, func_num=%d\n", frame8, func_num));
aframepars[sensor_port][frame8 & PARS_FRAMES_MASK].functions |= 1 << func_num;
}
/**
* @brief schedule pgm_func to be executed for this_framepars->pars[P_FRAME] & PARS_FRAMES_MASK
* @param sensor_port sensor port number (0..3)
* @param this_framepars pointer to frame parameters structure
* @param func_num number of function to schedule
*/
void schedule_this_pgm_func(int sensor_port, struct framepars_t * this_framepars, int func_num)
{
int frame8 = this_framepars->pars[P_FRAME] & PARS_FRAMES_MASK;
MDF1(printk(" frame8=%d, func_num=%d\n", frame8, func_num));
aframepars[sensor_port][frame8].functions |= 1 << func_num;
}
/**
* @brief just return current thisFrameNumber
* @param sensor_port sensor port number (0..3)
* @return current value of thisFrameNumber
*/
unsigned long getThisFrameNumber(int sensor_port)
{
return thisFrameNumber(sensor_port);
}
/**
* @brief Set parameters that will never change (usually after sensor discovery)
* @param sensor_port sensor port number (0..3)
* @param numPars number of parameters to set
* @param pars array of parameters (number/value pairs)
* @return always 0
*/
int setFrameParsStatic(int sensor_port, int numPars, struct frameparspair_t * pars)
{
int npar, nframe, index;
struct framepars_t *framepars = aframepars[sensor_port];
for (npar = 0; npar < numPars; npar++) {
index = pars[npar].num;
if (index > P_MAX_PAR) return -ERR_FRAMEPARS_BADINDEX;
for (nframe = 0; nframe < PARS_FRAMES; nframe++) {
framepars[nframe].pars[index] = pars[npar].val;
}
}
return 0;
}
/**
* @brief set parameters for the specified frame (atomic, with interrupts off). Used from applications through driver write
* @param sensor_port sensor port number (0..3)
* @param frameno absolute (full) frame number parameters should be applied to
* @param maxLatency maximal command latency (parameters should be set not less than maxLatency ahead of the current frame)
* maxLatency < 0 - don't check latency (i.e.only commands that are not releted to particular frames)
* @param numPars number of parameters to set (0 is OK to just test if it is too early/too late)
* @param pars array of parameters (number/value pairs). FRAMEPAIR_FORCE_NEW modifier to parameter number
* @return 0 - OK, -ERR_FRAMEPARS_TOOEARLY, -ERR_FRAMEPARS_TOOLATE
*/
///TODO: Check that writes never to the future or past frame (only 6 of 8 are allowed). Have seen just_this to flood all
int setFrameParsAtomic(int sensor_port, unsigned long frameno, int maxLatency, int numPars, struct frameparspair_t * pars)
{
unsigned long flags;
int npar, nframe;
unsigned long val, bmask, bmask32;
int index, bindex;
struct framepars_t *framepars = aframepars[sensor_port];
unsigned long *funcs2call =afuncs2call[sensor_port];
if (!frameParsInitialized) {
initSequencers(sensor_port); /// Will call initFramePars(); and initialize functions
}
int findex_this = thisFrameNumber(sensor_port) & PARS_FRAMES_MASK;
int findex_prev = (findex_this - 1) & PARS_FRAMES_MASK;
int findex_future = (findex_this - 2) & PARS_FRAMES_MASK; /// actually - fartherst in the future??
// int frame8= frameno & PARS_FRAMES_MASK;
int frame8;
MDF2(printk(": frameno=0x%lx, findex_this=%d (0x%lx) maxLatency=%d, numPars=%d\n", frameno, findex_this, thisFrameNumber(sensor_port), maxLatency, numPars));
D1I(local_irq_save(flags));
PROFILE_NOW(6);
if (maxLatency >= 0) {
if (frameno <= (thisFrameNumber(sensor_port) + maxLatency)) {
D1I(local_irq_restore(flags));
return -ERR_FRAMEPARS_TOOLATE;
}else if (frameno >= (thisFrameNumber(sensor_port) + (PARS_FRAMES - 1))) {
D1I(local_irq_restore(flags));
return -ERR_FRAMEPARS_TOOEARLY;
}
}
/// not too late, not too early, go ahead
for (npar = 0; npar < numPars; npar++) {
D5(printk(" --pars[%d].num=0x%lx, pars[%d].val=0x%lx", npar, pars[npar].num, npar, pars[npar].val));
// frame8= (pars[npar].num & FRAMEPAR_GLOBALS)? -1: (frameno & PARS_FRAMES_MASK);
frame8 = frameno & PARS_FRAMES_MASK;
val = pars[npar].val;
index = pars[npar].num & 0xffff;
if (index > ((index >= FRAMEPAR_GLOBALS) ? (P_MAX_GPAR + FRAMEPAR_GLOBALS) : P_MAX_PAR)) {
D1I(local_irq_restore(flags));
return -ERR_FRAMEPARS_BADINDEX;
}
D5(printk(" index=0x%x, val=0x%lx", index, val));
if (index >= FRAMEPAR_GLOBALS) { /// ignore frame logic, set "static" parameters to frame 0
if (pars[npar].num & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val = FRAMEPAIR_FRAME_MASK_NEW(pars[npar].num, GLOBALPARS(sensor_port,index), val);
}
GLOBALPARS(sensor_port, index) = val;
D5(printk(" set GLOBALPARS(0x%x)=0x%lx\n", index, val));
} else if (pars[npar].num & FRAMEPAIR_FRAME_FUNC) {
funcs2call[index] = val;
D5(printk(" set funcs2call[0x%x]=0x%lx\n", index, val));
// } else if ((frameno !=findex_prev) && (frameno != findex_future)) { /// do not write parameters in the future otherwise
} else if ((frame8 != findex_future) || ((pars[npar].num & FRAMEPAIR_JUST_THIS) == 0)) { /// do not write "JUST_THIS" parameters in the future otherwise they'll stick
if (pars[npar].num & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val = FRAMEPAIR_FRAME_MASK_NEW(pars[npar].num, framepars[frame8].pars[index], val);
}
//TODO: optimize to use mask several parameters together
D5(printk(" frame8=0x%x\n", frame8));
if ((framepars[frame8].pars[index] != val) || (pars[npar].num & FRAMEPAIR_FORCE_NEW)) {
bmask = 1 << (index & 31);
bindex = index >> 5;
bmask32 = 1 << bindex;
/// Set this parameter for specified frame
framepars[frame8].pars[index] = val;
framepars[frame8].mod[bindex] |= bmask;
framepars[frame8].mod32 |= bmask32;
framepars[frame8].functions |= funcs2call[index]; ///Mark which functions will be needed to process the parameters
D5(printk(" bindex=0x%x, bmask=0x%08lx, bmask32=0x%08lx, functions=0x%08lx\n", bindex, bmask, bmask32, framepars[frame8].functions));
/// Write parameter to the next frames up to the one that have the same parameter already modified (only if not FRAMEPAIR_JUST_THIS)
if ((pars[npar].num & FRAMEPAIR_JUST_THIS) == 0) {
MDF5(printk(": --- setting next frames"));
for (nframe = (frame8 + 1) & PARS_FRAMES_MASK; (nframe != findex_prev) && (!(framepars[nframe].mod[bindex] & bmask)); nframe = (nframe + 1) & PARS_FRAMES_MASK) {
framepars[nframe].pars[index] = val;
D5(printk(" %d", nframe));
}
frame8 = (frame8 - 1) & PARS_FRAMES_MASK; /// for " regular parameters "modified since" do not include the target frame itself, for "JUST_THIS" - does
D5(printk("\n"));
}
/// Mark this parameter in all previous frames as "modified since"
/// TODO: consider alternative way - first iterate through all parameters, build masks, then apply them
for (nframe = frame8; nframe != findex_future; nframe = (nframe - 1) & PARS_FRAMES_MASK) { ///NOTE: frame8 is modified here
framepars[nframe].modsince[bindex] |= bmask;
framepars[nframe].modsince32 |= bmask32;
}
}
} else { /// error - trying to write "just this" to the "future" - that would stick if allowed
D1I(local_irq_restore(flags));
ELP_KERR(printk("Tried to write JUST_THIS parameter (0x%lx) too far in the future", pars[npar].num));
return -ERR_FRAMEPARS_TOOEARLY;
}
}
/// Try to process parameters immediately after written. If 0, only non-ASAP will be processed to prevent
/// effects of uncertainty of when was it called relative to frame sync
/// Changed to all (don't care about uncertainty - they will trigger only if it is too late or during sensor detection/initialization)
if (!(get_globalParam(sensor_port, G_TASKLET_CTL) & (1 << TASKLET_CTL_NOSAME))) {
// processParsSeq (sensorproc, thisFrameNumber & PARS_FRAMES_MASK, 0); ///maxahead=0, the rest will be processed after frame sync, from the tasklet
MDF5(printk("\n"));
processPars(sensor_port, sensorproc, thisFrameNumber(sensor_port) & PARS_FRAMES_MASK, 0); ///maxahead=0, the rest will be processed after frame sync, from the tasklet
}
PROFILE_NOW(7);
D1I(local_irq_restore(flags));
return 0;
}
//#define FRAMEPAIR_JUST_THIS 0x40000 // write only to this frame, don't propagate
// (like "single frame" - compressor, sensor) first write "stop", then - "single" with FRAMEPAIR_JUST_THIS
/**
* @brief set a single output (calculated) parameter for the frame referenced by this_framepars structure.
* Schedules action only if the FRAMEPAIR_FORCE_PROC modifier bit is set in mindex
* @param sensor_port sensor port number (0..3)
* @param this_framepars pointer to the current parameters structure
* @param mindex parameter number (with optional modifiers in high bits)
* @param val parameter value to set
* @return 0 - OK, -ERR_FRAMEPARS_BADINDEX
*/
int setFramePar(int sensor_port, struct framepars_t * this_framepars, unsigned long mindex, unsigned long val)
{
int frame8 = (this_framepars->pars[P_FRAME]) & PARS_FRAMES_MASK;
unsigned long flags;
int nframe;
unsigned long bmask, bmask32, bindex;
int findex_this = thisFrameNumber(sensor_port) & PARS_FRAMES_MASK;
int findex_prev = (findex_this - 1) & PARS_FRAMES_MASK;
int findex_future = (findex_this - 2) & PARS_FRAMES_MASK;
int index = mindex & 0xffff;
struct framepars_t *framepars = aframepars[sensor_port];
unsigned long *funcs2call =afuncs2call[sensor_port];
MDF8(printk(": thisFrameNumber=0x%lx frame8=%d index= %d (0x%lx), val=0x%lx\n", thisFrameNumber(sensor_port), frame8, index, mindex, val));
D1I(local_irq_save(flags));
// if (index > P_MAX_PAR) {
if (index > ((index >= FRAMEPAR_GLOBALS) ? (P_MAX_GPAR + FRAMEPAR_GLOBALS) : P_MAX_PAR)) {
D1I(local_irq_restore(flags));
return -ERR_FRAMEPARS_BADINDEX;
}
//TODO: optimize to use mask several parameters together
if (index >= FRAMEPAR_GLOBALS) { /// ignore frame logic, set "static" parameters to frame 0
if (mindex & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val = FRAMEPAIR_FRAME_MASK_NEW(mindex, GLOBALPARS(sensor_port,index), val);
}
GLOBALPARS(sensor_port,index) = val;
} else if (mindex & FRAMEPAIR_FRAME_FUNC) { /// write to func_proc[] instead
funcs2call[index] = val;
// } else {
} else if ((frame8 != findex_future) || ((mindex & FRAMEPAIR_JUST_THIS) == 0)) { /// do not write "JUST_THIS" parameters in the future otherwise they'll stick
if (mindex & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val = FRAMEPAIR_FRAME_MASK_NEW(mindex, framepars[frame8].pars[index], val);
}
if ((framepars[frame8].pars[index] != val) || (mindex & (FRAMEPAIR_FORCE_NEW | FRAMEPAIR_FORCE_PROC))) {
bmask = 1 << (index & 31);
bindex = index >> 5;
bmask32 = 1 << bindex;
/// Set this parameter for specified frame, (for now - unconditionally mark as modified, even if the value is the same as it was - CHANGED!
framepars[frame8].pars[index] = val;
framepars[frame8].mod[bindex] |= bmask;
framepars[frame8].mod32 |= bmask32;
if (mindex & FRAMEPAIR_FORCE_PROC) {
framepars[frame8].functions |= funcs2call[index]; ///Mark which functions will be needed to process the parameters
}
MDF8(printk(" bindex=0x%lx, bmask=0x%08lx, bmask32=0x%08lx, functions=0x%08lx\n", bindex, bmask, bmask32, framepars[frame8].functions));
/// Write parameter to the next frames up to the one that have the same parameter already modified
if ((mindex & FRAMEPAIR_JUST_THIS) == 0) {
MDF8(printk(": --- setting next frames"));
// for (nframe=(frame8+1) & PARS_FRAMES_MASK; (nframe != findex_prev) && (!(framepars[frame8].mod[bindex] & bmask)); nframe=(nframe+1) & PARS_FRAMES_MASK) {
for (nframe = (frame8 + 1) & PARS_FRAMES_MASK; (nframe != findex_prev) && (!(framepars[nframe].mod[bindex] & bmask)); nframe = (nframe + 1) & PARS_FRAMES_MASK) {
framepars[nframe].pars[index] = val;
D8(printk(" %d", nframe));
}
frame8 = (frame8 - 1) & PARS_FRAMES_MASK; /// for " regular parameters "modified since" do not include the target frame itself, for "JUST_THIS" - does
}
// MDF1(printk("\n"));
/// Mark this parameter in all previous frames as "modified since"
/// TODO: consider alternative way - first iterate through all parameters, build masks, then apply them
MDF8(printk(": >>> setting modsince"));
// for (nframe=(frame8-1) & PARS_FRAMES_MASK; nframe != findex_future; nframe=(nframe-1) & PARS_FRAMES_MASK) {
for (nframe = frame8; nframe != findex_future; nframe = (nframe - 1) & PARS_FRAMES_MASK) { ///NOTE: frame8 is modified here
framepars[nframe].modsince[bindex] |= bmask;
framepars[nframe].modsince32 |= bmask32;
D8(printk(" %d", nframe));
}
D8(printk("\n"));
}
} else { /// error - trying to write "just this" to the "future" - that would stick if allowed
D1I(local_irq_restore(flags));
ELP_KERR(printk("Tried to write JUST_THIS parameter (0x%lx) too far in the future", mindex));
return -ERR_FRAMEPARS_TOOEARLY;
}
D1I(local_irq_restore(flags));
return 0;
}
/**
* @brief set multiple output (calculated) parameters for the frame referenced by this_framepars structure.
* Schedules action only if the FRAMEPAIR_FORCE_PROC modifier bit is set in the particular parameter index
* @param sensor_port sensor port number (0..3)
* @param this_framepars pointer to the current parameters structure
* @param numPars number of parameters to set
* @param pars array of parameters (number/value pairs). Parameter numbers accept modifiers
* @return 0 - OK, -ERR_FRAMEPARS_BADINDEX
*/
int setFramePars(int sensor_port, struct framepars_t * this_framepars, int numPars, struct frameparspair_t * pars)
{
int frame8;
unsigned long flags;
int npar, nframe;
unsigned long val, bmask, bmask32;
int index, bindex;
int findex_this = thisFrameNumber(sensor_port) & PARS_FRAMES_MASK;
int findex_prev = (findex_this - 1) & PARS_FRAMES_MASK;
int findex_future = (findex_this - 2) & PARS_FRAMES_MASK;
struct framepars_t *framepars = aframepars[sensor_port];
unsigned long *funcs2call =afuncs2call[sensor_port];
MDF8(printk(": this_framepars=0x%x numPars=%d\n", (int)this_framepars, numPars));
D1I(local_irq_save(flags));
for (npar = 0; npar < numPars; npar++) {
frame8 = (this_framepars->pars[P_FRAME]) & PARS_FRAMES_MASK;
val = pars[npar].val;
index = pars[npar].num & 0xffff;
MDF8(printk(": --- frame8=%d index=%d (0x%x) val=0x%x\n", frame8, index, (int)pars[npar].num, (int)val));
// remark: code below looks similar to setFramePar function, call it instead
if (index > ((index >= FRAMEPAR_GLOBALS) ? (P_MAX_GPAR + FRAMEPAR_GLOBALS) : P_MAX_PAR)) {
D1I(local_irq_restore(flags));
ELP_KERR(printk(" bad index=%d > %d\n", index, P_MAX_PAR));
return -ERR_FRAMEPARS_BADINDEX;
}
if (index >= FRAMEPAR_GLOBALS) { /// ignore frame logic, set "static" parameters to frame 0
if (pars[npar].num & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val = FRAMEPAIR_FRAME_MASK_NEW(pars[npar].num, GLOBALPARS(sensor_port,index), val);
}
GLOBALPARS(sensor_port,index) = val;
} else if (pars[npar].num & FRAMEPAIR_FRAME_FUNC) {
funcs2call[index] = val;
// } else {
} else if ((frame8 != findex_future) || ((pars[npar].num & FRAMEPAIR_JUST_THIS) == 0)) { /// do not write "JUST_THIS" parameters in the future otherwise they'll stick
if (pars[npar].num & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val = FRAMEPAIR_FRAME_MASK_NEW(pars[npar].num, framepars[frame8].pars[index], val);
}
//TODO: optimize to use mask several parameters together
if ((framepars[frame8].pars[index] != val) || (pars[npar].num & (FRAMEPAIR_FORCE_NEW | FRAMEPAIR_FORCE_PROC))) {
bmask = 1 << (index & 31);
bindex = index >> 5;
bmask32 = 1 << bindex;
/// Set this parameter for specified frame, (for now - unconditionally mark as modified, even if the value is the same as it was - CHANGED!
framepars[frame8].pars[index] = val;
framepars[frame8].mod[bindex] |= bmask;
framepars[frame8].mod32 |= bmask32;
if (pars[npar].num & FRAMEPAIR_FORCE_PROC) {
framepars[frame8].functions |= funcs2call[index]; ///Mark which functions will be needed to process the parameters
}
/// Write parameter to the next frames up to the one that have the same parameter already modified (only if not FRAMEPAIR_JUST_THIS)
if ((pars[npar].num & FRAMEPAIR_JUST_THIS) == 0) {
MDF8(printk(": --- setting next frames"));
for (nframe = (frame8 + 1) & PARS_FRAMES_MASK; (nframe != findex_prev) && (!(framepars[nframe].mod[bindex] & bmask)); nframe = (nframe + 1) & PARS_FRAMES_MASK) {
D8(printk(" %d", nframe));
framepars[nframe].pars[index] = val;
}
frame8 = (frame8 - 1) & PARS_FRAMES_MASK; /// for " regular parameters "modified since" do not include the target frame itself, for "JUST_THIS" - does
D8(printk("\n"));
}
/// Mark this parameter in all previous frames as "modified since"
/// TODO: consider alternative way - first iterate through all parameters, build masks, then apply them
// for (nframe=(frame8-1) & PARS_FRAMES_MASK; nframe != findex_future; nframe=(nframe-1) & PARS_FRAMES_MASK) {
for (nframe = frame8; nframe != findex_future; nframe = (nframe - 1) & PARS_FRAMES_MASK) { ///NOTE: frame8 is modified here
framepars[nframe].modsince[bindex] |= bmask;
framepars[nframe].modsince32 |= bmask32;
}
}
} else { /// error - trying to write "just this" to the "future" - that would stick if allowed
D1I(local_irq_restore(flags));
ELP_KERR(printk("Tried to write JUST_THIS parameter (0x%lx) too far in the future", pars[npar].num));
return -ERR_FRAMEPARS_TOOEARLY;
}
}
D1I(local_irq_restore(flags));
return 0;
}
///TODO: make some parameters readonly (prohibited from modification from the userland)
/// File operations:
/// open, release - nop
/// read - none
/// write -> setFrameParsAtomic (first 4 bytes - absolute frame number, next 4 bytes - latency, then each 8 bytes - index/value)
/// can use current file pointer or special indexes (0x****ff01 - set frame number, 0x****ff02 - set latency) that should come before actual parameters
/// file pointer - absolute frame number
/// lseek (SEEK_SET, value) - set absolute frame number
/// lseek (SEEK_CUR, value) - set frame number relative to the current frame number (thisFrameNumber),
/// lseek (SEEK_CUR, 0) - (used by ftell()) also modifies file pointer - set it to thisFrameNumber,
/// lseek (SEEK_END, value <= 0) - do nothing?, do not modify file pointer
/// lseek (SEEK_END, value > 0) - execute commands, do not modify file pointer (and actually use it - frame number the command applies to)
/// mmap (should be used read only)
static struct file_operations framepars_fops = {
owner: THIS_MODULE,
llseek: framepars_lseek,
write: framepars_write,
open: framepars_open,
mmap: framepars_mmap,
release: framepars_release
};
/**
* @brief Driver OPEN method
* @param inode inode
* @param filp file pointer
* @return OK - 0, -EINVAL for wrong minor
*/
int framepars_open(struct inode *inode, struct file *filp)
{
int res;
struct framepars_pd * privData;
privData = (struct framepars_pd*)kmalloc(sizeof(struct framepars_pd), GFP_KERNEL);
if (!privData) return -ENOMEM;
filp->private_data = privData;
privData->minor = MINOR(inode->i_rdev);
MDF1(printk(": minor=0x%x\n", privData->minor));
switch (privData->minor) {
case CMOSCAM_MINOR_FRAMEPARS_CHN_0:
case CMOSCAM_MINOR_FRAMEPARS_CHN_1:
case CMOSCAM_MINOR_FRAMEPARS_CHN_2:
case CMOSCAM_MINOR_FRAMEPARS_CHN_3:
inode->i_size = 0; //or return 8 - number of frame pages?
return 0;
default:
kfree(filp->private_data); // already allocated
return -EINVAL;
}
return res;
}
/**
* @brief Driver RELEASE method
* @param inode inode
* @param filp file pointer
* @return OK - 0, -EINVAL for wrong minor
*/
int framepars_release(struct inode *inode, struct file *filp)
{
int res = 0;
int p = MINOR(inode->i_rdev);
MDF1(printk(": minor=0x%x\n", p));
switch ( p ) {
case CMOSCAM_MINOR_FRAMEPARS_CHN_0:
case CMOSCAM_MINOR_FRAMEPARS_CHN_1:
case CMOSCAM_MINOR_FRAMEPARS_CHN_2:
case CMOSCAM_MINOR_FRAMEPARS_CHN_3:
break;
default:
return -EINVAL; //! do not need to free anything - "wrong number"
}
kfree(filp->private_data);
return res;
}
/**
* @brief Driver LSEEK method (and execute commands)
* - lseek (SEEK_SET, value) - set absolute frame number
* - lseek (SEEK_CUR, value) - set frame number relative to the current frame number (thisFrameNumber),
* - lseek (SEEK_CUR, 0) - (used by ftell()) DOES NOT modify file pointer, returns thisFrameNumber,
* - lseek (SEEK_END, value <= 0) - do nothing?, do not modify file pointer
* - lseek (SEEK_END, value > 0) - execute commands, do not modify file pointer (and actually use it - frame number the command applies to)
* - no commands yet
* @param file
* @param offset
* @param orig SEEK_SET, SEEK_CUR or SEEK_SET END
* @return file position (absolute frame number)
*/
loff_t framepars_lseek(struct file * file, loff_t offset, int orig)
{
unsigned long target_frame;
struct framepars_pd * privData = (struct framepars_pd*) file -> private_data;
int sensor_port = privData -> minor - CMOSCAM_MINOR_FRAMEPARS_CHN_0;
struct framepars_t *framepars = aframepars[sensor_port];
MDF1(printk(" offset=0x%x, orig=0x%x, sensor_port = %d\n", (int)offset, (int)orig, sensor_port));
switch (orig) {
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
if (offset == 0) return getThisFrameNumber(sensor_port); /// do not modify frame number
file->f_pos = getThisFrameNumber(sensor_port) + offset; /// modifies frame number, but it is better to use absolute SEEK SET
break;
case SEEK_END:
if (offset <= 0) {
break;
} else if (offset >= LSEEK_FRAME_WAIT_REL) {
if (offset >= LSEEK_FRAME_WAIT_ABS) target_frame = offset - LSEEK_FRAME_WAIT_ABS; /// Wait for absolute frame number
else target_frame = getThisFrameNumber(sensor_port) + offset - LSEEK_FRAME_WAIT_REL;
/// Skip 0..255 frames
// wait_event_interruptible (framepars_wait_queue, getThisFrameNumber()>=target_frame);
wait_event_interruptible(aframepars_wait_queue[sensor_port], getThisFrameNumber(sensor_port) >= target_frame);
// if (getThisFrameNumber()=target_frame);
return getThisFrameNumber(sensor_port); /// Does not modify current frame pointer? lseek (,0,SEEK_CUR) anyway returns getThisFrameNumber()
} else { //! Other lseek commands
switch (offset & ~0x1f) {
case LSEEK_DAEMON_FRAME: /// wait the daemon enabled and a new frame interrupt (sensor frame sync)
wait_event_interruptible(aframepars_wait_queue[sensor_port], get_imageParamsThis(sensor_port, P_DAEMON_EN) & (1 << (offset & 0x1f)));
break;
default:
switch (offset) {
case LSEEK_GET_FPGA_TIME:
//X313_GET_FPGA_TIME( GLOBALPARS(G_SECONDS), GLOBALPARS(G_MICROSECONDS) );
MDF2(printk("X313_GET_FPGA_TIME\n"));
break;
case LSEEK_SET_FPGA_TIME: /// better to use write, not lseek to set FPGA time
//X313_SET_FPGA_TIME( GLOBALPARS(G_SECONDS) , GLOBALPARS(G_MICROSECONDS) );
MDF2(printk("X313_SET_FPGA_TIME\n"));
break;
case LSEEK_FRAMEPARS_INIT: /// reset hardware sequencers, init framepars structure
MDF2(printk("LSEEK_FRAMEPARS_INIT\n"));
initGlobalPars(sensor_port); /// reset all global parameters but the first 32
initSequencers(sensor_port);
break;
case LSEEK_FRAME_RESET: /// reset absolute frame number to avoid integer frame number overflow
MDF2(printk("LSEEK_FRAME_RESET\n"));
resetFrameNumber(sensor_port);
break;
case LSEEK_SENSORPROC: /// process modified parameters in frame 0 (to start sensor detection)
MDF2(printk("LSEEK_SENSORPROC: framepars[0].functions=0x%08lx\n", framepars[0].functions));
processPars(sensor_port, sensorproc, 0, 8); ///frame0, all 8 frames (maxAhead==8)
break;
case LSEEK_DMA_INIT: /// initialize ETRAX DMA (normally done in sensor_common.c at driver init
MDF2(printk("LSEEK_DMA_INIT\n"));
//x313_dma_init();
break;
case LSEEK_DMA_STOP: /// stop DMA
MDF2(printk("LSEEK_DMA_STOP\n"));
//x313_dma_stop(); ///
break;
case LSEEK_DMA_START: /// start DMA
MDF2(printk("LSEEK_DMA_START\n"));
//x313_dma_start(); ///
break;
case LSEEK_COMPRESSOR_RESET: /// reset compressor and buffer pointers
MDF2(printk("LSEEK_COMPRESSOR_RESET\n"));
//reset_compressor();
break;
case LSEEK_INTERRUPT_OFF: /// disable camera interrupts
MDF2(printk("LSEEK_INTERRUPT_OFF\n"));
camera_interrupts(0);
break;
case LSEEK_INTERRUPT_ON: /// enable camera interrupts
MDF2(printk("LSEEK_INTERRUPT_ON\n"));
camera_interrupts(1);
break;
}
}
break;
}
break;
default:
return -EINVAL;
}
return file->f_pos;
}
/**
* @brief Driver WRITE method
* writes all at once, no provisions to continue in the next call
* @param file
* @param buf
* @param count
* @param off
* @return OK - number of bytes written, negative - errors
*/
ssize_t framepars_write(struct file * file, const char * buf, size_t count, loff_t *off)
{
struct frameparspair_t pars_static[256]; /// will be sufficient for most calls
struct frameparspair_t * pars = pars_static;
struct framepars_pd * privData = (struct framepars_pd*)file->private_data;
int sensor_port = privData -> minor - CMOSCAM_MINOR_FRAMEPARS_CHN_0;
// struct framepars_t *framepars = aframepars[sensor_port];
unsigned long frame = *off; /// ************* NOTE: Never use file->f_pos in write() and read() !!!
int latency = -1;
int first = 0;
int last;
int result;
MDF1(printk(": file->f_pos=0x%x, *off=0x%x, count=0x%x\n", (int)file->f_pos, (int)*off, (int)count));
count &= ~7; /// sizeof (struct frameparspair_t)==8
switch (privData->minor) {
case CMOSCAM_MINOR_FRAMEPARS:
if (count > sizeof(pars_static)) /// only allocate if static is not enough
pars = (struct frameparspair_t*)kmalloc(count, GFP_KERNEL);
if (!pars) return -ENOMEM;
count >>= 3; /// divide by sizeof(struct frameparspair_t); // 8
if (count) {
if (copy_from_user((char*)pars, buf, count << 3)) {
if (count > sizeof(pars_static)) kfree(pars);
return -EFAULT;
}
while (first < count) {
while ((first < count) && ((pars[first].num & 0xff00) == 0xff00)) { // process special instructions
switch (pars[first].num & 0xffff) {
case FRAMEPARS_SETFRAME:
frame = pars[first].val;
break;
case FRAMEPARS_SETFRAMEREL:
frame = pars[first].val + getThisFrameNumber(sensor_port);
break;
case FRAMEPARS_SETLATENCY:
latency = (pars[first].val & 0x80000000) ? -1 : pars[first].val;
break;
case FRAMEPARS_SETFPGATIME: ///ignore value (should be already set in G_SECONDS, G_MICROSECONDS frame0)
//X313_SET_FPGA_TIME( GLOBALPARS(G_SECONDS) , GLOBALPARS(G_MICROSECONDS) );
break;
case FRAMEPARS_GETFPGATIME: ///ignore value, set frame 0 G_SECONDS, G_MICROSECONDS to FPGA registers
//X313_GET_FPGA_TIME( GLOBALPARS(G_SECONDS) , GLOBALPARS(G_MICROSECONDS) );
break;
default:
printk("framepars_write: invalid special instruction: 0x%x\n", (int)pars[first].num);
}
first++;
}
last = first + 1;
while ((last < count) && ((pars[last].num & 0xff00) != 0xff00)) last++; // skip to the end or next special instructions
result = setFrameParsAtomic(sensor_port,frame, latency, last - first, &pars[first]);
MDF1(printk("setFrameParsAtomic(%ld, %d, %d)\n", frame, latency, last - first));
if (result < 0) {
if (count > sizeof(pars_static)) kfree(pars);
return -EFAULT;
}
first = last;
}
}
if (count > sizeof(pars_static)) kfree(pars);
return count << 3; /// *sizeof(struct frameparspair_t);
default: return -EINVAL;
}
}
/**
* @brief Driver MMAP method (should be used read only)
* provides access to both 8-frame parameters (including future ones), frame - 0 - some static ones too and
* much longer retained past parameters - subset of all parameters
* @param file
* @param vma
* @return OK - 0, negative - errors
*/
int framepars_mmap(struct file *file, struct vm_area_struct *vma)
{
int result;
struct framepars_pd * privData = (struct framepars_pd*)file->private_data;
int sensor_port = privData -> minor - CMOSCAM_MINOR_FRAMEPARS_CHN_0;
MDF1(printk(": minor=0x%x\n", privData->minor));
switch (privData->minor) {
case CMOSCAM_MINOR_FRAMEPARS:
result = remap_pfn_range(vma,
vma->vm_start,
((unsigned long)virt_to_phys(&aframeparsall[sensor_port])) >> PAGE_SHIFT, // Should be page-aligned
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
MDF1(printk("remap_pfn_range returned=%x\r\n", result));
if (result) return -EAGAIN;
return 0;
default: return -EINVAL;
}
}
/**
* @brief framepars driver probing function
* @param[in] pdev pointer to \b platform_device structure
* @return 0 on success or negative error code otherwise
*/
int framepars_init(struct platform_device *pdev)
{
int res;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
int sensor_port;
for (sensor_port = 0; sensor_port < SENSOR_PORTS; sensor_port++) {
init_framepars_ptr(sensor_port);
initGlobalPars(sensor_port); /// sets default debug if enabled - not anymore. Add here?
initMultiPars(sensor_port); /// just clear - needs to be called again when sensor is recognized
}
frameParsInitialized = 0;
res = register_chrdev(FRAMEPARS_MAJOR, "framepars_operations", &framepars_fops);
if (res < 0) {
printk(KERN_ERR "\nframepars_init: couldn't get a major number %d.\n", FRAMEPARS_MAJOR);
return res;
}
for (sensor_port = 0; sensor_port < SENSOR_PORTS; sensor_port++) {
init_waitqueue_head(&aframepars_wait_queue[sensor_port]);
}
dev_info(dev, "registered MAJOR: %d\n", FRAMEPARS_MAJOR);
return 0;
}
int framepars_remove(struct platform_device *pdev)
{
unregister_chrdev(FRAMEPARS_MAJOR, "framepars_operations");
return 0;
}
//static const struct of_device_id elphel393_framepars_of_match[] = {
// { .compatible = "elphel,elphel393-framepars-1.00" },
// { /* end of list */ }
//};
//MODULE_DEVICE_TABLE(of, elphel393_framepars_of_match);
//
//static struct platform_driver elphel393_framepars = {
// .probe = framepars_init,
// .remove = framepars_remove,
// .driver = {
// .name = FRAMEPARS_DRIVER_NAME,
// .of_match_table = elphel393_framepars_of_match,
// },
//};
//
//module_platform_driver(elphel393_framepars);
//
//MODULE_LICENSE("GPL");
//MODULE_AUTHOR("Andrey Filippov .");
//MODULE_DESCRIPTION(X3X3_FRAMEPARS_DRIVER_NAME);
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/framepars.h 0000664 0000000 0000000 00000011573 12762437450 0027056 0 ustar 00root root 0000000 0000000 #ifndef _FRAMEPARS_H
#define _FRAMEPARS_H
#ifndef SENSOR_PORTS
#include // to get SENSOR_PORTS
#endif
//extern struct framepars_t (*framepars)[PARS_FRAMES];
extern struct framepars_t *aframepars[SENSOR_PORTS];
extern struct framepars_past_t *apastpars[SENSOR_PORTS];
extern unsigned long *aglobalPars[SENSOR_PORTS];
extern unsigned long *amultiSensIndex[SENSOR_PORTS];
extern unsigned long *amultiSensRvrsIndex[SENSOR_PORTS];
extern wait_queue_head_t aframepars_wait_queue[SENSOR_PORTS];
///TODO: init framepars (zero parameters) before initscripts (not when detecting the sensors) - then initscript will be able to overwrite some
void init_framepars_ptr(int sensor_port);
void initSequencers (int sensor_port); ///Move to sensorcommon? currently it is used through frameparsall file (lseek)
void initGlobalPars (int sensor_port); /// resets all global parameters but debug mask (if ELPHEL_DEBUG)
int initMultiPars (int sensor_port); /// initialize structures for individual per-sensor parameters. Now only works for sensor registers using G_MULTI_REGSM. Should be called aftre/during sensor detection
void initFramePars (int sensor_port); ///initialize all parameters, set thisFrameNumber to frame8 (read from hardware, usually 0 after resetting i2c and cmd_seq)
void resetFrameNumber (int sensor_port); /// reset this frame number (called from initFramePars(), also can be used to avoid frame number integer overflow)
unsigned long get_imageParamsThis (int sensor_port, int n);
unsigned long get_imageParamsPrev (int sensor_port, int n);
void set_imageParamsThis (int sensor_port, int n, unsigned long d);
unsigned long get_globalParam (int sensor_port, int n);
void set_globalParam (int sensor_port, int n, unsigned long d);
void set_imageParamsR_all(int sensor_port, int n, unsigned long d);
void updateFramePars (int sensor_port, int frame8, struct interframe_params_t * frame_pars); /// called from ISR - advance thisFrameNumber to match hardware frame8, copy parameters as needed.
/// frame8 usually is just next after thisFrameNumber
/// frame_pars - pointer to structure (between frames in the frame buffer) to save a pointer to past parameters
int setFrameParsStatic (int sensor_port, int numPars, struct frameparspair_t * pars);
unsigned long getThisFrameNumber (int sensor_port); /// just return current thisFrameNumber
/// set parameters for the frame number frameno, knowing that they should be set not less than maxLatency ahead (may be sensor - dependent)
/// Parameters (numPars of them) will be updated all at once, with interrupts disabled
/// Return - 0 if OK, -ERR_FRAMEPARS_TOOEARLY or -ERR_FRAMEPARS_TOOLATE if it is too early or too late to set parameters (numPars may be 0 to just test)
///
/// NOTE: When writing parameter to P_SENSOR_RUN or P_COMPRESSOR_RUN "*_RUN_SINGLE", first write "*SENSOR_RUN_STOP" (it will propagate to all next frames) and then
/// write "*_RUN_SINGLE", to (P_*_RUN | FRAMEPAIR_JUST_THIS) - then this *_RUN_SINGLE will not propagate to the next frames (they will stay stopped)
/// TODO: Make (an "unlimited") future commands que based on lists and a tree frame index
int setFrameParsAtomic (int sensor_port, unsigned long frameno, int maxLatency, int numPars, struct frameparspair_t * pars);
/// set output/calculated parameter and propagate changes - will not trigger any actions
int setFramePar (int sensor_port, struct framepars_t * this_framepars, unsigned long mindex, unsigned long val);
///same for several pars at once
int setFramePars (int sensor_port, struct framepars_t * this_framepars, int numPars, struct frameparspair_t * pars);
/// schedule pgm_func to be executed for selected frame
void schedule_pgm_func (int sensor_port, int frame8, int func_num);
/// schedule pgm_func to be executed for the current frame
void schedule_this_pgm_func (int sensor_port, struct framepars_t * this_framepars, int func_num);
/// program acquisition, according to the parameters changed.
/// maxahead - how many frames ahead of time (start with most urgent, then 1 ahead, ...)
/// make maxahead - P_* parameter?
/* 393: See if sesnor port is needed here */
inline void processParsASAP (int sensor_port, struct sensorproc_t * sensorproc, int frame8);
inline void processParsSeq (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead);
void processPars (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead);
///*** TODO: Add option (flag?) to write "single" (like single compress, single sensor) so it will not make all the next frames "single"
int framepars_init (struct platform_device *pdev);
int framepars_remove (struct platform_device *pdev);
#endif
linux-elphel-ee11ae901b5449dcb64275bb7ade1589a31083bb/src/drivers/elphel/jpeghead.c 0000664 0000000 0000000 00000073306 12762437450 0026642 0 ustar 00root root 0000000 0000000 /** @file jpeghead.c
*
* @brief This file contains methods for JPEG tables and headers generation and
* JPEG files composition from data compressed by FPGA.
*
* Copyright (C) 2016 Elphel, Inc
*
* 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 .
*/
//#include
#include
//#include
#include
//#include
#include
#include
//#include
#include
//#include