Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-elphel
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
Elphel
linux-elphel
Commits
50663cc1
Commit
50663cc1
authored
Apr 07, 2016
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adding sensor ports i2c driver
parent
3027f36c
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
622 additions
and
86 deletions
+622
-86
Makefile
src/drivers/elphel/Makefile
+2
-1
fpgajtag353.c
src/drivers/elphel/fpgajtag353.c
+60
-66
sensor_i2c.c
src/drivers/elphel/sensor_i2c.c
+453
-0
sensor_i2c.h
src/drivers/elphel/sensor_i2c.h
+89
-0
x393.c
src/drivers/elphel/x393.c
+2
-2
x393.h
src/drivers/elphel/x393.h
+2
-4
x393_defs.h
src/drivers/elphel/x393_defs.h
+2
-3
x393_map.h
src/drivers/elphel/x393_map.h
+5
-5
x393_types.h
src/drivers/elphel/x393_types.h
+7
-5
No files found.
src/drivers/elphel/Makefile
View file @
50663cc1
...
...
@@ -6,6 +6,7 @@ obj-$(CONFIG_ELPHEL393) += elphel393-pwr.o
obj-$(CONFIG_ELPHEL393)
+=
elphel393-mem.o
obj-$(CONFIG_ELPHELDRVONMICROZED)
+=
elphel393-mem.o
obj-$(CONFIG_ELPHEL393_INIT)
+=
elphel393-init.o
obj-$(CONFIG_ELPHEL393)
+=
x393.o
obj-$(CONFIG_ELPHEL393)
+=
sensor_i2c.o
fpgajtag-y
:=
fpgajtag353.o x393.o
obj-$(CONFIG_ELPHEL393_EXTERNAL)
+=
fpgajtag.o
\ No newline at end of file
src/drivers/elphel/fpgajtag353.c
View file @
50663cc1
...
...
@@ -223,8 +223,8 @@ static struct file_operations fpga_jtag_fops = {
//static int sens_num = 0;
// internal functions
loff_t
fjtag_bitsize
(
int
minor
);
loff_t
fjtag_bytesize
(
int
minor
);
//
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
);
...
...
@@ -484,7 +484,7 @@ static int fpga_jtag_release(struct inode *inode, struct file *filp) {
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
));
//
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
);
...
...
@@ -544,7 +544,7 @@ static ssize_t fpga_jtag_write(struct file * file, const char * buf, size_t coun
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
));
//
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
;
}
...
...
@@ -560,7 +560,7 @@ static ssize_t fpga_jtag_write(struct file * file, const char * buf, size_t coun
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
));
//
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
;
}
...
...
@@ -756,11 +756,34 @@ inline x393_status_sens_io_t wait_sensio_status(int chn, u32 seq_num) // reducin
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
;
}
// 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_j
p
ag_t
data
;
x393_sensio_j
t
ag_t
data
;
dev_dbg
(
NULL
,
"set_pgm_mode (%d,%d)
\n
"
,
chn
,
en
);
switch
(
chn
>>
2
)
{
...
...
@@ -768,16 +791,15 @@ void set_pgm_mode (int chn, int en) {
//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 */
// seq_num = prep_sensio_status(sens_num);
x393_sensio_jtag
(
data
,
chn
&
3
);
// sens_num);
/* wait for status register update */
// wait_sensio_status(sens_num, seq_num);
// x393_sensio_jtag(data, sens_num);
wait_sensio_status
(
chn
&
3
,
prep_sensio_status
(
chn
&
3
))
;
// Not needed here
break
;
}
udelay
(
2
);
...
...
@@ -785,7 +807,7 @@ void set_pgm_mode (int chn, int en) {
void
set_pgm
(
int
chn
,
int
pgmon
)
{
u32
seq_num
;
x393_sensio_j
p
ag_t
data
;
x393_sensio_j
t
ag_t
data
;
dev_dbg
(
NULL
,
"set_pgm (%d,%d)
\n
"
,
chn
,
pgmon
);
switch
(
chn
>>
2
)
{
...
...
@@ -800,9 +822,7 @@ void set_pgm (int chn, int pgmon) {
//port_csp0_addr[X313_WA_SENSFPGA] = (2 | (pgmon & 1)) << SFPGA_PROG_BIT;
data
.
prog
=
pgmon
&
1
;
data
.
prog_set
=
1
;
// seq_num = prep_sensio_status(sens_num);
x393_sensio_jtag
(
data
,
chn
>>
2
);
// sens_num);
// wait_sensio_status(sens_num, seq_num);
break
;
case
JTAG_AUX_FPGA
:
break
;
...
...
@@ -812,9 +832,8 @@ void set_pgm (int chn, int pgmon) {
int
read_done
(
int
chn
)
{
u32
seq_num
;
x393_status_sens_io_t
stat
;
x393_sensio_j
p
ag_t
data
;
x393_sensio_j
t
ag_t
data
;
switch
(
chn
>>
2
)
{
#ifdef TEST_DISABLE_CODE
...
...
@@ -825,9 +844,8 @@ int read_done (int chn) {
//port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_DONE;
//udelay (1);
//return (port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1 ;
seq_num
=
prep_sensio_status
(
chn
&
3
)
;
// sens_num);
// stat = x393_sensio_status(chn & 3) ; // sens_num);
stat
=
wait_sensio_status
(
chn
&
3
,
seq_num
)
;
// sens_num);
stat
=
wait_sensio_status
(
chn
&
3
,
prep_sensio_status
(
chn
&
3
))
;
return
stat
.
xfpgadone
;
case
JTAG_AUX_FPGA
:
return
0
;
...
...
@@ -838,7 +856,7 @@ int read_done (int chn) {
// send 1..8 bits through JTAG
int
jtag_send
(
int
chn
,
int
tms
,
int
len
,
int
d
)
{
int
sens_num
=
chn
&
3
;
x393_sensio_j
p
ag_t
data
;
x393_sensio_j
t
ag_t
data
;
x393_status_sens_io_t
stat
;
u32
seq_num
;
int
i
;
//,m;
...
...
@@ -882,11 +900,12 @@ int jtag_send (int chn, int tms, int len, int d) {
}
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
;
printk
(
"jtag_send(0x%x, 0x%x, 0x%x, 0x%x)
\n
"
,
chn
,
tms
,
len
,
d
);
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
;
...
...
@@ -897,31 +916,20 @@ int jtag_send (int chn, int tms, int len, int d) {
x393_sensio_jtag
(
data
,
sens_num
);
/* repeat writel - just a delay; is it really needed? */
x393_sensio_jtag
(
data
,
sens_num
);
x393_sensio_jtag
(
data
,
sens_num
);
x393_sensio_jtag
(
data
,
sens_num
);
// x393_sensio_jtag(data, sens_num);
/* read TDO before TCK pulse */
seq_num
=
prep_sensio_status
(
sens_num
);
stat
=
wait_sensio_status
(
sens_num
,
seq_num
);
// get TDO (only where we need to wait status for here)
// DEBUG: repeating read
seq_num
=
prep_sensio_status
(
sens_num
);
stat
=
wait_sensio_status
(
sens_num
,
seq_num
);
// get TDO (only where we need to wait status for here)
printk
(
" %08x"
,
stat
.
d32
);
r
=
(
r
<<
1
)
+
(
stat
.
xfpgatdo
&
1
);
r
=
(
r
<<
1
)
+
read_tdo
(
sens_num
);
// may to need to read twice to increase delay?
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
//
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
);
// x393_sensio_jtag(data, sens_num);
}
printk
(
" ---> %02x
\n
"
,
r
);
x393_sensio_jtag
(
data
,
sens_num
);
dev_dbg
(
NULL
,
" ---> %02x
\n
"
,
r
);
break
;
case
JTAG_AUX_FPGA
:
...
...
@@ -948,9 +956,9 @@ int jtag_write_bits (int chn,
int
i
,
j
;
int
r
=
0
;
int
d
,
d0
;
u32
seq_num
;
//
u32 seq_num;
x393_status_sens_io_t
stat
;
x393_sensio_j
p
ag_t
data
;
x393_sensio_j
t
ag_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
);
...
...
@@ -1030,51 +1038,36 @@ int jtag_write_bits (int chn,
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
]);
printk
(
"jtag_write_bits(), i=0x%x "
,
i
);
dev_dbg
(
NULL
,
"jtag_write_bits(), i=0x%x "
,
i
);
for
(
j
=
0
;
j
<
8
;
j
++
)
{
/* TCK = 0 - just a delay; is it really needed? */
data
.
tck
=
0
;
if
(
len
>
0
)
{
if
(
len
==
1
&&
last
)
data
.
tms
=
1
;
else
data
.
tms
=
0
;
data
.
tms
=
(
len
==
1
&&
last
)
?
1
:
0
;
data
.
tdi
=
((
d
<<=
1
)
>>
8
)
&
1
;
data
.
tck
=
0
;
x393_sensio_jtag
(
data
,
sens_num
);
x393_sensio_jtag
(
data
,
sens_num
);
// repeat writel() if needed for delay
x393_sensio_jtag
(
data
,
sens_num
);
// repeat writel() if needed for delay
x393_sensio_jtag
(
data
,
sens_num
);
// repeat writel() if needed for delay
seq_num
=
prep_sensio_status
(
sens_num
);
stat
=
wait_sensio_status
(
sens_num
,
seq_num
);
seq_num
=
prep_sensio_status
(
sens_num
);
stat
=
wait_sensio_status
(
sens_num
,
seq_num
);
printk
(
" %d: %08x"
,
j
,
stat
.
d32
);
r
=
(
r
<<
1
)
+
(
stat
.
xfpgatdo
&
1
);
// x393_sensio_jtag(data, sens_num); // repeat writel() if needed for delay
r
=
(
r
<<
1
)
+
read_tdo
(
sens_num
);
data
.
tck
=
1
;
x393_sensio_jtag
(
data
,
sens_num
);
x393_sensio_jtag
(
data
,
sens_num
);
// remove if no delay is needed
//
x393_sensio_jtag(data, sens_num); // remove if no delay is needed
data
.
tck
=
0
;
x393_sensio_jtag
(
data
,
sens_num
);
x393_sensio_jtag
(
data
,
sens_num
);
//
x393_sensio_jtag(data, sens_num);
}
else
{
r
<<=
1
;
}
len
--
;
}
buf
[
i
]
=
r
;
printk
(
" ===> %02x
\n
"
,
r
);
dev_dbg
(
NULL
,
" ===> %02x
\n
"
,
r
);
if
(
check
&&
((
r
^
(
prev
[
1
]
>>
24
))
&
0xff
))
{
return
-
((
r
&
0xff
)
|
((
i
+
1
)
<<
8
));
//readback mismatch
}
...
...
@@ -1241,6 +1234,7 @@ int JTAG_resetChannel (int 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
)
{
...
...
@@ -1319,7 +1313,7 @@ int i; // only in debug
#ifdef JTAG_DISABLE_IRQ
unsigned
long
flags
;
#endif
D
(
printk
(
"JTAG_CAPTURE(): buf=%p
\n
"
,
buf
)
);
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
);
...
...
@@ -1374,8 +1368,8 @@ int i; // only in debug
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
"
);
);
//
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
...
...
@@ -1395,7 +1389,7 @@ D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if
#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
"
);
);
// 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)
...
...
@@ -1430,6 +1424,6 @@ module_exit(fpga_jtag_exit);
module_init
(
fpga_jtag_init
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"Andrey Filippov <andrey@elphel.com>
.
"
);
MODULE_AUTHOR
(
"Andrey Filippov <andrey@elphel.com>"
);
MODULE_DESCRIPTION
(
FPGA_JTAG_DRIVER_NAME
);
src/drivers/elphel/sensor_i2c.c
0 → 100644
View file @
50663cc1
/*******************************************************************************
* FILE NAME : sensor_i2c.c
* DESCRIPTION: Interface to FPGA-based i2c sequencer for sensor ports
* Copyright 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
/****************** INCLUDE FILES SECTION ***********************************/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
//#include <linux/poll.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
//#include <linux/spinlock_types.h>
#include <asm/io.h>
//#include <asm/system.h>
#include <asm/irq.h>
#include <elphel/driver_numbers.h>
#include <asm/uaccess.h>
#include "x393.h"
#include "sensor_i2c.h"
//------------------
#include <asm/page.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_net.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
#define SYSFS_PERMISSIONS 0644
/* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
#define DRV_NAME "elphel_sensor_i2c"
//------------------
// Number of channels is hard-wired to 4
static
u32
free_i2c_groups
[
4
];
static
u32
free_i2c_pages
[
32
];
static
DEFINE_SPINLOCK
(
lock
);
static
u32
i2c_pages_shadow
[
1024
];
// Mostly for debugging to analyze i2c pages allocation
static
int
sysfs_page
[
4
];
// when positive - page locked for exclusive access
/* Mark all i2c pages for each channel as free */
void
i2c_page_alloc_init
(
void
){
int
i
;
for
(
i
=
0
;
i
<
sizeof
(
free_i2c_groups
)
/
sizeof
(
free_i2c_groups
[
0
]);
i
++
)
free_i2c_groups
[
i
]
=
0xff000000
;
for
(
i
=
0
;
i
<
sizeof
(
free_i2c_pages
)
/
sizeof
(
free_i2c_pages
[
0
]);
i
++
)
free_i2c_pages
[
i
]
=
0xffffffff
;
for
(
i
=
0
;
i
<
sizeof
(
i2c_pages_shadow
)
/
sizeof
(
i2c_pages_shadow
[
0
]);
i
++
)
i2c_pages_shadow
[
i
]
=
0
;
for
(
i
=
0
;
i
<
sizeof
(
sysfs_page
)
/
sizeof
(
sysfs_page
[
0
]);
i
++
)
sysfs_page
[
i
]
=
-
1
;
}
/* Reserve i2c page (1 of 256 for a sensor port)*/
int
i2c_page_alloc
(
int
chn
){
int
g
,
b
;
u32
*
fb
;
spin_lock
(
&
lock
);
g
=
__builtin_clz
(
free_i2c_groups
[
chn
]);
if
(
unlikely
(
g
>
7
))
{
spin_unlock
(
&
lock
);
return
-
ENOMEM
;
// no free i2c pages left
}
fb
=
free_i2c_pages
+
((
chn
<<
3
)
+
g
);
b
=
__builtin_clz
(
*
fb
);
*
fb
&=
~
(
1
<<
(
31
-
b
));
if
(
unlikely
(
*
fb
==
0
)){
free_i2c_groups
[
chn
]
&=
~
(
1
<<
(
31
-
g
));
}
spin_unlock
(
&
lock
);
return
(
g
<<
5
)
+
b
;
}
/* Free i2c page */
void
i2c_page_free
(
int
chn
,
int
page
){
int
g
=
page
>>
5
;
int
b
=
page
&
0x1f
;
u32
*
fb
=
free_i2c_pages
+
((
chn
<<
3
)
+
g
);
spin_lock
(
&
lock
);
free_i2c_groups
[
chn
]
|=
1
<<
(
31
-
g
);
*
fb
|=
1
<<
(
31
-
b
);
spin_unlock
(
&
lock
);
}
/* Set i2c table entry to raw data (will just overwrite tbl_mode = 2)*/
void
set_sensor_i2c_raw
(
int
chn
,
int
page
,
// index in lookup table
u32
data
)
{
// Bit delay - number of mclk periods in 1/4 of the SCL period
x393_i2c_ctltbl_t
tb_data
,
tb_addr
;
tb_addr
.
d32
=
0
;
tb_addr
.
tbl_mode
=
3
;
tb_data
.
d32
=
data
;
tb_data
.
tbl_mode
=
2
;
//
/* Table address and data should not interleave with others */
spin_lock
(
&
lock
);
x393_sensi2c_ctrl
(
tb_addr
,
chn
);
x393_sensi2c_ctrl
(
tb_data
,
chn
);
spin_unlock
(
&
lock
);
i2c_pages_shadow
[(
chn
<<
8
)
+
page
]
=
tb_data
.
d32
;
}
/* Set i2c table entry for write operation */
void
set_sensor_i2c_wr
(
int
chn
,
int
page
,
// index in lookup table
int
sa
,
// slave address (7 bit)
int
rah
,
// High byte of the i2c register address
int
num_bytes
,
//Number of bytes to write (1..10)
int
bit_delay
)
{
// Bit delay - number of mclk periods in 1/4 of the SCL period
x393_i2c_ctltbl_t
tb_data
,
tb_addr
;
tb_addr
.
d32
=
0
;
tb_addr
.
tbl_mode
=
3
;
tb_data
.
d32
=
0
;
tb_data
.
rah
=
rah
;
tb_data
.
rnw
=
0
;
tb_data
.
sa
=
sa
;
tb_data
.
nbwr
=
num_bytes
;
tb_data
.
dly
=
bit_delay
;
tb_data
.
tbl_mode
=
2
;
/* Table address and data should not interleave with others */
spin_lock
(
&
lock
);
x393_sensi2c_ctrl
(
tb_addr
,
chn
);
x393_sensi2c_ctrl
(
tb_data
,
chn
);
spin_unlock
(
&
lock
);
i2c_pages_shadow
[(
chn
<<
8
)
+
page
]
=
tb_data
.
d32
;
}
/* Set i2c table entry for read operation */
void
set_sensor_i2c_rd
(
int
chn
,
int
page
,
// index in lookup table
int
two_byte_addr
,
// Number of address bytes (0 - one byte, 1 - two bytes)
int
num_bytes
,
// Number of bytes to read (1..8, 0 means 8)
int
bit_delay
)
{
// Bit delay - number of mclk periods in 1/4 of the SCL period
x393_i2c_ctltbl_t
tb_data
,
tb_addr
;
tb_addr
.
d32
=
0
;
tb_addr
.
tbl_mode
=
3
;
tb_data
.
d32
=
0
;
tb_data
.
rnw
=
1
;
tb_data
.
nabrd
=
two_byte_addr
;
tb_data
.
nbrd
=
num_bytes
;
tb_data
.
dly
=
bit_delay
;
tb_data
.
tbl_mode
=
2
;
/* Table address and data should not interleave with others */
spin_lock
(
&
lock
);
x393_sensi2c_ctrl
(
tb_addr
,
chn
);
x393_sensi2c_ctrl
(
tb_data
,
chn
);
spin_unlock
(
&
lock
);
i2c_pages_shadow
[(
chn
<<
8
)
+
page
]
=
tb_data
.
d32
;
}
/*
// Write i2c command to the i2c command sequencer
// I2C command sequencer, block of 16 DWORD slots for absolute frame numbers (modulo 16) and 15 slots for relative ones
// 0 - ASAP, 1 next frame, 14 -14-th next.
// Data written depends on context:
// 1 - I2C register write: index page (MSB), 3 payload bytes. Payload bytes are used according to table and sent
// after the slave address and optional high address byte. Other bytes are sent in descending order (LSB- last).
// If less than 4 bytes are programmed in the table the high bytes (starting with the one from the table) are
// skipped.
// If more than 4 bytes are programmed in the table for the page (high byte), one or two next 32-bit words
// bypass the index table and all 4 bytes are considered payload ones. If less than 4 extra bytes are to be
// sent for such extra word, only the lower bytes are sent.
//
// 2 - I2C register read: index page, slave address (8-bit, with lower bit 0) and one or 2 address bytes (as programmed
// in the table. Slave address is always in byte 2 (bits 23:16), byte1 (high register address) is skipped if
// read address in the table is programmed to be a single-byte one
*/
/* Write one or multiple DWORDs to i2c relative (modulo16) address. Use offs = 0 for immediate (ASAP) command */
/* Length of data is determined by the page data already preset */
int
write_sensor_i2c_rel
(
int
chn
,
int
offs
,
// 4 bits
u32
*
data
){
x393_i2c_ctltbl_t
tb_data
;
int
len
;
int
i
;
tb_data
.
d32
=
i2c_pages_shadow
[(
chn
<<
8
)
+
(
data
[
0
]
>>
24
)];
if
(
tb_data
.
tbl_mode
!=
2
)
return
-
1
;
len
=
(
tb_data
.
rnw
)
?
1
:
((
tb_data
.
nbwr
+
5
)
>>
2
);
// read mode - always 1 DWORD, write - 1..3
if
(
len
>
1
)
{
spin_lock
(
&
lock
);
for
(
i
=
0
;
i
<
len
;
i
++
){
x393_sensi2c_rel
(
data
[
i
],
chn
,
offs
);
}
}
else
{
x393_sensi2c_rel
(
data
[
0
],
chn
,
offs
);
}
spin_unlock
(
&
lock
);
return
0
;
}
int
write_sensor_i2c_abs
(
int
chn
,
int
offs
,
// 4 bits
u32
*
data
){
x393_i2c_ctltbl_t
tb_data
;
int
len
;
int
i
;
tb_data
.
d32
=
i2c_pages_shadow
[(
chn
<<
8
)
+
(
data
[
0
]
>>
24
)];
if
(
tb_data
.
tbl_mode
!=
2
)
return
-
1
;
len
=
(
tb_data
.
rnw
)
?
1
:
((
tb_data
.
nbwr
+
5
)
>>
2
);
// read mode - always 1 DWORD, write - 1..3
if
(
len
>
1
)
{
spin_lock
(
&
lock
);
for
(
i
=
0
;
i
<
len
;
i
++
){
x393_sensi2c_abs
(
data
[
i
],
chn
,
offs
);
}
}
else
{
x393_sensi2c_abs
(
data
[
0
],
chn
,
offs
);
}
spin_unlock
(
&
lock
);
return
0
;
}
/* Write sensor 16 bit (or 8 bit as programmed in the table) data in immediate mode */
void
write_sensor_reg16
(
int
chn
,
int
page
,
// page (8 bits)
int
addr
,
// low 8 bits
u32
data
){
// 16 or 8-bit data (LSB aligned)
u32
dw
=
((
page
&
0xff
)
<<
24
)
|
((
addr
&
0xff
)
<<
16
)
|
(
data
&
0xffff
);
x393_sensi2c_rel
(
dw
,
chn
,
0
);
}
/* Initiate sensor i2c read in immediate mode (data itself has to be read from FIFO with read_sensor_i2c_fifo)*/
void
read_sensor_i2c
(
int
chn
,
int
page
,
// page (8 bits)
int
sa7
,
// 7-bit i2c slave address
int
addr
){
// 8/16 bit address
u32
dw
=
((
page
&
0xff
)
<<
24
)
|
(
sa7
<<
17
)
|
(
addr
&
0xffff
);
x393_sensi2c_rel
(
dw
,
chn
,
0
);
}
//void x393_sensi2c_rel (u32 d, int sens_num, int offset){writel(d, mmio_ptr + (0x1080 + 0x40 * sens_num + 0x1 * offset));} // Write sensor i2c sequencer
/* Read next byte from the channel i2c FIFO. Return byte or -1 if no data available */
/* Sensor channel status should be in auto update mode (3) */
int
read_sensor_i2c_fifo
(
int
chn
){
int
fifo_lsb
,
rslt
,
i
;
x393_i2c_ctltbl_t
i2c_cmd
;
x393_status_sens_i2c_t
status
=
x393_sensi2c_status
(
chn
);
if
(
!
status
.
i2c_fifo_nempty
)
return
-
1
;
// No data available
fifo_lsb
=
status
.
i2c_fifo_lsb
;
rslt
=
status
.
i2c_fifo_dout
;
// Advance FIFO readout pointer
i2c_cmd
.
d32
=
0
;
i2c_cmd
.
next_fifo_rd
=
1
;
// tbl_mode is 0 already
x393_sensi2c_ctrl
(
i2c_cmd
,
chn
);
for
(
i
=
0
;
i
<
10
;
i
++
)
{
status
=
x393_sensi2c_status
(
chn
);
if
(
likely
(
status
.
i2c_fifo_lsb
!=
fifo_lsb
))
break
;
}
return
rslt
;
}
// ======================================
// SYSFS
/* Get channelo number from the last character of the attribute name*/
static
int
get_channel_from_name
(
struct
device_attribute
*
attr
){
int
reg
=
0
;
sscanf
(
attr
->
attr
.
name
+
(
strlen
(
attr
->
attr
.
name
)
-
1
),
"%du"
,
&
reg
);
return
reg
;
}
static
ssize_t
get_i2c_page_alloc
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
int
chn
=
get_channel_from_name
(
attr
)
;
int
page
;
if
(
sysfs_page
[
chn
]
>=
0
)
return
-
EBUSY
;
page
=
i2c_page_alloc
(
chn
);
if
(
page
<
0
)
return
-
ENOMEM
;
sysfs_page
[
chn
]
=
page
;
return
sprintf
(
buf
,
"%d
\n
"
,
page
);
}
static
ssize_t
free_i2c_page
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
int
chn
=
get_channel_from_name
(
attr
)
;
int
page
;
sscanf
(
buf
,
"%i"
,
&
page
);
page
&=
0xff
;
if
(
sysfs_page
[
chn
]
>=
0
)
i2c_page_free
(
chn
,
page
);
sysfs_page
[
chn
]
=
-
1
;
// free
return
count
;
}
static
ssize_t
get_i2c_page_inuse
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
int
chn
=
get_channel_from_name
(
attr
)
;
return
sprintf
(
buf
,
"%d
\n
"
,
sysfs_page
[
chn
]);
}
// Get i2c table data as raw data (hex)
static
ssize_t
get_i2c_tbl_raw
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
int
chn
=
get_channel_from_name
(
attr
)
;
int
page
=
sysfs_page
[
chn
];
// currently selected page for sysfs reads
if
(
page
<
0
)
return
-
ENXIO
;
/* No such device or address */
return
sprintf
(
buf
,
"%0x08x
\n
"
,
i2c_pages_shadow
[(
chn
<<
8
)
+
(
page
&
0xff
)]);
}
static
ssize_t
set_i2c_tbl_raw
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
int
chn
=
get_channel_from_name
(
attr
)
;
int
page
,
data
;
sscanf
(
buf
,
"%i %i"
,
&
page
,
&
data
);
page
&=
0xff
;
set_sensor_i2c_raw
(
chn
,
page
,
// index in lookup table
(
u32
)
data
);
// Bit delay - number of mclk periods in 1/4 of the SCL period
return
count
;
}
// Sysfs top
static
DEVICE_ATTR
(
alloc0
,
SYSFS_PERMISSIONS
&
SYSFS_READONLY
,
get_i2c_page_alloc
,
NULL
);
static
DEVICE_ATTR
(
alloc1
,
SYSFS_PERMISSIONS
&
SYSFS_READONLY
,
get_i2c_page_alloc
,
NULL
);
static
DEVICE_ATTR
(
alloc2
,
SYSFS_PERMISSIONS
&
SYSFS_READONLY
,
get_i2c_page_alloc
,
NULL
);
static
DEVICE_ATTR
(
alloc3
,
SYSFS_PERMISSIONS
&
SYSFS_READONLY
,
get_i2c_page_alloc
,
NULL
);
static
DEVICE_ATTR
(
page_in_use0
,
SYSFS_PERMISSIONS
,
get_i2c_page_inuse
,
free_i2c_page
);
static
DEVICE_ATTR
(
page_in_use1
,
SYSFS_PERMISSIONS
,
get_i2c_page_inuse
,
free_i2c_page
);
static
DEVICE_ATTR
(
page_in_use2
,
SYSFS_PERMISSIONS
,
get_i2c_page_inuse
,
free_i2c_page
);
static
DEVICE_ATTR
(
page_in_use3
,
SYSFS_PERMISSIONS
,
get_i2c_page_inuse
,
free_i2c_page
);
static
DEVICE_ATTR
(
tbl_raw0
,
SYSFS_PERMISSIONS
,
get_i2c_tbl_raw
,
set_i2c_tbl_raw
);
static
DEVICE_ATTR
(
tbl_raw1
,
SYSFS_PERMISSIONS
,
get_i2c_tbl_raw
,
set_i2c_tbl_raw
);
static
DEVICE_ATTR
(
tbl_raw2
,
SYSFS_PERMISSIONS
,
get_i2c_tbl_raw
,
set_i2c_tbl_raw
);
static
DEVICE_ATTR
(
tbl_raw3
,
SYSFS_PERMISSIONS
,
get_i2c_tbl_raw
,
set_i2c_tbl_raw
);
static
struct
attribute
*
root_dev_attrs
[]
=
{
&
dev_attr_alloc0
.
attr
,
&
dev_attr_alloc1
.
attr
,
&
dev_attr_alloc2
.
attr
,
&
dev_attr_alloc3
.
attr
,
&
dev_attr_page_in_use0
.
attr
,
&
dev_attr_page_in_use1
.
attr
,
&
dev_attr_page_in_use2
.
attr
,
&
dev_attr_page_in_use3
.
attr
,
&
dev_attr_tbl_raw0
.
attr
,
&
dev_attr_tbl_raw1
.
attr
,
&
dev_attr_tbl_raw2
.
attr
,
&
dev_attr_tbl_raw3
.
attr
,
NULL
};
static
const
struct
attribute_group
dev_attr_root_group
=
{
.
attrs
=
root_dev_attrs
,
.
name
=
NULL
,
};
static
int
elphel393_sens_i2c_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
void
elphel393_sensor_i2c_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
)
{
/*TODO: Configure some i2c devices here (slaves, formats, speeds) to be used by names*/
}
dev_info
(
&
pdev
->
dev
,
"elphel393_sensor_i2c configuration done
\n
"
);
}
static
int
elphel393_sensor_i2c_probe
(
struct
platform_device
*
pdev
)
{
/*