tsl2550: driver for the TSL2550 light sensor
Change-Id: I9e1c87132404509ffec8bf22a8c6cc993df1aa73
This commit is contained in:
parent
3161c603a6
commit
845aabfe65
8 changed files with 647 additions and 2 deletions
|
@ -30,7 +30,8 @@ case $#:$1 in
|
||||||
eepromb2s50 eepromb2s51 eepromb2s52 eepromb2s53 \
|
eepromb2s50 eepromb2s51 eepromb2s52 eepromb2s53 \
|
||||||
eepromb2s54 eepromb2s55 eepromb2s56 eepromb2s57 \
|
eepromb2s54 eepromb2s55 eepromb2s56 eepromb2s57 \
|
||||||
eepromb3s50 eepromb3s51 eepromb3s52 eepromb3s53 \
|
eepromb3s50 eepromb3s51 eepromb3s52 eepromb3s53 \
|
||||||
eepromb3s54 eepromb3s55 eepromb3s56 eepromb3s57
|
eepromb3s54 eepromb3s55 eepromb3s56 eepromb3s57 \
|
||||||
|
tsl2550b1s39 tsl2550b2s39 tsl2550b3s39
|
||||||
;;
|
;;
|
||||||
0:|1:-\?)
|
0:|1:-\?)
|
||||||
cat >&2 <<EOF
|
cat >&2 <<EOF
|
||||||
|
@ -39,6 +40,7 @@ Where key is one of the following:
|
||||||
ram mem kmem null boot zero # One of these makes all these memory devices
|
ram mem kmem null boot zero # One of these makes all these memory devices
|
||||||
fb0 # Make /dev/fb0
|
fb0 # Make /dev/fb0
|
||||||
i2c-1 i2c-2 i2c-3 # Make /dev/i2c-[1-3]
|
i2c-1 i2c-2 i2c-3 # Make /dev/i2c-[1-3]
|
||||||
|
tsl2550b{1,3}s39 # TSL2550 Ambient Light Sensors
|
||||||
fd0 fd1 ... # Floppy devices for drive 0, 1, ...
|
fd0 fd1 ... # Floppy devices for drive 0, 1, ...
|
||||||
fd0p0 fd1p0 ... # Make floppy partitions fd0p[0-3], fd1p[0-3], ...
|
fd0p0 fd1p0 ... # Make floppy partitions fd0p[0-3], fd1p[0-3], ...
|
||||||
c0d0 c0d1 ... # Make disks c0d0, c0d1, ...
|
c0d0 c0d1 ... # Make disks c0d0, c0d1, ...
|
||||||
|
@ -307,6 +309,12 @@ do
|
||||||
$e mknod eepromb${b}s5${s} b ${m} 0
|
$e mknod eepromb${b}s5${s} b ${m} 0
|
||||||
$e chmod 600 eepromb${b}s5${s}
|
$e chmod 600 eepromb${b}s5${s}
|
||||||
;;
|
;;
|
||||||
|
tsl2550b[1-3]s39)
|
||||||
|
b=`expr $dev : 'tsl2550b\\(.*\\)s39'` #bus number
|
||||||
|
m=`expr ${b} + 46`
|
||||||
|
$e mknod tsl2550b${b}s39 c ${m} 0
|
||||||
|
$e chmod 444 tsl2550b${b}s39
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "$0: don't know about $dev" >&2
|
echo "$0: don't know about $dev" >&2
|
||||||
ex=1
|
ex=1
|
||||||
|
|
|
@ -114,5 +114,6 @@
|
||||||
./usr/sbin/tda19988 minix-sys
|
./usr/sbin/tda19988 minix-sys
|
||||||
./usr/sbin/tps65217 minix-sys
|
./usr/sbin/tps65217 minix-sys
|
||||||
./usr/sbin/tps65950 minix-sys
|
./usr/sbin/tps65950 minix-sys
|
||||||
|
./usr/sbin/tsl2550 minix-sys
|
||||||
./usr/tests/minix-posix/mod minix-sys
|
./usr/tests/minix-posix/mod minix-sys
|
||||||
./usr/tests/minix-posix/test63 minix-sys
|
./usr/tests/minix-posix/test63 minix-sys
|
||||||
|
|
|
@ -24,7 +24,7 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \
|
||||||
|
|
||||||
.if ${MACHINE_ARCH} == "earm"
|
.if ${MACHINE_ARCH} == "earm"
|
||||||
SUBDIR= cat24c256 fb gpio i2c mmc lan8710a log readclock \
|
SUBDIR= cat24c256 fb gpio i2c mmc lan8710a log readclock \
|
||||||
tda19988 tps65217 tps65950 tty random
|
tda19988 tps65217 tps65950 tsl2550 tty random
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
.endif # ${MKIMAGEONLY} != "yes"
|
.endif # ${MKIMAGEONLY} != "yes"
|
||||||
|
|
14
drivers/tsl2550/Makefile
Normal file
14
drivers/tsl2550/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Makefile for the tsl2550 ambient light sensor found on the Weather Cape.
|
||||||
|
PROG= tsl2550
|
||||||
|
SRCS= tsl2550.c
|
||||||
|
|
||||||
|
DPADD+= ${LIBI2CDRIVER} ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS}
|
||||||
|
LDADD+= -li2cdriver -lchardriver -lsys -ltimers
|
||||||
|
|
||||||
|
MAN=
|
||||||
|
|
||||||
|
BINDIR?= /usr/sbin
|
||||||
|
|
||||||
|
CPPFLAGS+= -I${NETBSDSRCDIR}
|
||||||
|
|
||||||
|
.include <minix.service.mk>
|
46
drivers/tsl2550/README.txt
Normal file
46
drivers/tsl2550/README.txt
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
TSL2550 Driver (Ambient Light Sensor)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
This is the driver for the ambient light sensor commonly found on the
|
||||||
|
WeatherCape expansion board for the BeagleBone.
|
||||||
|
|
||||||
|
Interface
|
||||||
|
---------
|
||||||
|
|
||||||
|
This driver implements the character device interface. It supports reading
|
||||||
|
through /dev/tsl2550b{1,3}s39. When read from, it returns a string containing
|
||||||
|
a data label, a colon, and the sensor value.
|
||||||
|
|
||||||
|
Example output of `cat /dev/tsl2550b3s39`:
|
||||||
|
|
||||||
|
ILLUMINANCE : 830
|
||||||
|
|
||||||
|
Illuminance is expressed in lux. Valid values are 0 to 1846.
|
||||||
|
|
||||||
|
Limitations
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Extended mode isn't implemented. Normal mode should be sufficient for most
|
||||||
|
applications.
|
||||||
|
|
||||||
|
Testing the Code
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The driver should have been started by a script in /etc/rc.capes/ If not,
|
||||||
|
this is how you start up an instance:
|
||||||
|
|
||||||
|
cd /dev && MAKEDEV tsl2550b3s39
|
||||||
|
/bin/service up /usr/sbin/tsl2550 -label tsl2550.3.39 -dev /dev/tsl2550b3s39 \
|
||||||
|
-args 'bus=3 address=0x39'
|
||||||
|
|
||||||
|
Getting the sensor value:
|
||||||
|
|
||||||
|
cat /dev/tsl2550b3s39
|
||||||
|
|
||||||
|
Killing an instance:
|
||||||
|
|
||||||
|
/bin/service down tsl2550.3.39
|
||||||
|
|
567
drivers/tsl2550/tsl2550.c
Normal file
567
drivers/tsl2550/tsl2550.c
Normal file
|
@ -0,0 +1,567 @@
|
||||||
|
/* Driver for the TSL2550 Ambient Light Sensor */
|
||||||
|
|
||||||
|
#include <minix/ds.h>
|
||||||
|
#include <minix/drivers.h>
|
||||||
|
#include <minix/i2c.h>
|
||||||
|
#include <minix/i2cdriver.h>
|
||||||
|
#include <minix/chardriver.h>
|
||||||
|
#include <minix/log.h>
|
||||||
|
#include <minix/type.h>
|
||||||
|
#include <minix/spin.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Device Commands
|
||||||
|
*/
|
||||||
|
#define CMD_PWR_DOWN 0x00
|
||||||
|
#define CMD_PWR_UP 0x03
|
||||||
|
#define CMD_EXT_RANGE 0x1d
|
||||||
|
#define CMD_NORM_RANGE 0x18
|
||||||
|
#define CMD_READ_ADC0 0x43
|
||||||
|
#define CMD_READ_ADC1 0x83
|
||||||
|
|
||||||
|
/* When powered up and communicating, the register should have this value */
|
||||||
|
#define EXPECTED_PWR_UP_TEST_VAL 0x03
|
||||||
|
|
||||||
|
/* Maximum Lux value in Standard Mode */
|
||||||
|
#define MAX_LUX_STD_MODE 1846
|
||||||
|
|
||||||
|
/* Bit Masks for ADC Data */
|
||||||
|
#define ADC_VALID_MASK (1<<7)
|
||||||
|
#define ADC_CHORD_MASK ((1<<6)|(1<<5)|(1<<4))
|
||||||
|
#define ADC_STEP_MASK ((1<<3)|(1<<2)|(1<<1)|(1<<0))
|
||||||
|
|
||||||
|
#define ADC_VAL_IS_VALID(x) ((x & ADC_VALID_MASK) == ADC_VALID_MASK)
|
||||||
|
#define ADC_VAL_TO_CHORD_BITS(x) ((x & ADC_CHORD_MASK) >> 4)
|
||||||
|
#define ADC_VAL_TO_STEP_BITS(x) (x & ADC_STEP_MASK)
|
||||||
|
|
||||||
|
/* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
|
||||||
|
static struct log log = {
|
||||||
|
.name = "tsl2550",
|
||||||
|
.log_level = LEVEL_INFO,
|
||||||
|
.log_func = default_log
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The slave address is hardwired to 0x39 and cannot be changed. */
|
||||||
|
static i2c_addr_t valid_addrs[2] = {
|
||||||
|
0x39, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Buffer to store output string returned when reading from device file. */
|
||||||
|
#define BUFFER_LEN 32
|
||||||
|
char buffer[BUFFER_LEN + 1];
|
||||||
|
|
||||||
|
/* the bus that this device is on (counting starting at 1) */
|
||||||
|
static uint32_t bus;
|
||||||
|
|
||||||
|
/* slave address of the device */
|
||||||
|
static i2c_addr_t address;
|
||||||
|
|
||||||
|
/* endpoint for the driver for the bus itself. */
|
||||||
|
static endpoint_t bus_endpoint;
|
||||||
|
|
||||||
|
/* register access functions */
|
||||||
|
static int reg_read(uint8_t * val);
|
||||||
|
static int reg_write(uint8_t val);
|
||||||
|
|
||||||
|
/* main driver functions */
|
||||||
|
static int tsl2550_init(void);
|
||||||
|
static int adc_read(int adc, uint8_t * val);
|
||||||
|
static int measure_lux(uint32_t * lux);
|
||||||
|
|
||||||
|
/* libchardriver callbacks */
|
||||||
|
static struct device *tsl2550_prepare(dev_t UNUSED(dev));
|
||||||
|
static int tsl2550_transfer(endpoint_t endpt, int opcode, u64_t position,
|
||||||
|
iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
|
||||||
|
unsigned int UNUSED(flags));
|
||||||
|
static int tsl2550_other(message * m);
|
||||||
|
|
||||||
|
/* SEF functions */
|
||||||
|
static int sef_cb_lu_state_save(int);
|
||||||
|
static int lu_state_restore(void);
|
||||||
|
static int sef_cb_init(int type, sef_init_info_t * info);
|
||||||
|
static void sef_local_startup(void);
|
||||||
|
|
||||||
|
/* Entry points to this driver from libchardriver. */
|
||||||
|
static struct chardriver tsl2550_tab = {
|
||||||
|
.cdr_open = do_nop,
|
||||||
|
.cdr_close = do_nop,
|
||||||
|
.cdr_ioctl = nop_ioctl,
|
||||||
|
.cdr_prepare = tsl2550_prepare,
|
||||||
|
.cdr_transfer = tsl2550_transfer,
|
||||||
|
.cdr_cleanup = nop_cleanup,
|
||||||
|
.cdr_alarm = nop_alarm,
|
||||||
|
.cdr_cancel = nop_cancel,
|
||||||
|
.cdr_select = nop_select,
|
||||||
|
.cdr_other = tsl2550_other
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device tsl2550_device = {
|
||||||
|
.dv_base = 0,
|
||||||
|
.dv_size = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These two lookup tables and the formulas used in measure_lux() are from
|
||||||
|
* 'TAOS INTELLIGENT OPTO SENSOR DESIGNER'S NOTEBOOK' Number 9
|
||||||
|
* 'Simplified TSL2550 Lux Calculation for Embedded and Micro Controllers'.
|
||||||
|
*
|
||||||
|
* The tables and formulas eliminate the need for floating point math and
|
||||||
|
* functions from libm. It also speeds up the calculations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Look up table for converting ADC values to ADC counts */
|
||||||
|
static const uint32_t adc_counts_lut[128] = {
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7,
|
||||||
|
8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 18, 20, 22, 24, 26, 28, 30,
|
||||||
|
32, 34, 36, 38, 40, 42, 44, 46,
|
||||||
|
49, 53, 57, 61, 65, 69, 73, 77,
|
||||||
|
81, 85, 89, 93, 97, 101, 105, 109,
|
||||||
|
115, 123, 131, 139, 147, 155, 163, 171,
|
||||||
|
179, 187, 195, 203, 211, 219, 227, 235,
|
||||||
|
247, 263, 279, 295, 311, 327, 343, 359,
|
||||||
|
375, 391, 407, 423, 439, 455, 471, 487,
|
||||||
|
511, 543, 575, 607, 639, 671, 703, 735,
|
||||||
|
767, 799, 831, 863, 895, 927, 959, 991,
|
||||||
|
1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
|
||||||
|
1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
|
||||||
|
2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
|
||||||
|
3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Look up table of scaling factors */
|
||||||
|
static const uint32_t ratio_lut[129] = {
|
||||||
|
100, 100, 100, 100, 100, 100, 100, 100,
|
||||||
|
100, 100, 100, 100, 100, 100, 99, 99,
|
||||||
|
99, 99, 99, 99, 99, 99, 99, 99,
|
||||||
|
99, 99, 99, 98, 98, 98, 98, 98,
|
||||||
|
98, 98, 97, 97, 97, 97, 97, 96,
|
||||||
|
96, 96, 96, 95, 95, 95, 94, 94,
|
||||||
|
93, 93, 93, 92, 92, 91, 91, 90,
|
||||||
|
89, 89, 88, 87, 87, 86, 85, 84,
|
||||||
|
83, 82, 81, 80, 79, 78, 77, 75,
|
||||||
|
74, 73, 71, 69, 68, 66, 64, 62,
|
||||||
|
60, 58, 56, 54, 52, 49, 47, 44,
|
||||||
|
42, 41, 40, 40, 39, 39, 38, 38,
|
||||||
|
37, 37, 37, 36, 36, 36, 35, 35,
|
||||||
|
35, 35, 34, 34, 34, 34, 33, 33,
|
||||||
|
33, 33, 32, 32, 32, 32, 32, 31,
|
||||||
|
31, 31, 31, 31, 30, 30, 30, 30,
|
||||||
|
30
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
measure_lux(uint32_t * lux)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
uint8_t adc0_val, adc1_val;
|
||||||
|
uint32_t adc0_cnt, adc1_cnt;
|
||||||
|
uint32_t ratio;
|
||||||
|
|
||||||
|
r = adc_read(0, &adc0_val);
|
||||||
|
if (r != OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = adc_read(1, &adc1_val);
|
||||||
|
if (r != OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look up the adc count, drop the MSB to put in range 0-127. */
|
||||||
|
adc0_cnt = adc_counts_lut[adc0_val & ~ADC_VALID_MASK];
|
||||||
|
adc1_cnt = adc_counts_lut[adc1_val & ~ADC_VALID_MASK];
|
||||||
|
|
||||||
|
/* default scaling factor */
|
||||||
|
ratio = 128;
|
||||||
|
|
||||||
|
/* calculate ratio - avoid div by 0, ensure cnt1 <= cnt0 */
|
||||||
|
if ((adc0_cnt != 0) && (adc1_cnt <= adc0_cnt)) {
|
||||||
|
ratio = (adc1_cnt * 128 / adc0_cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ensure ratio isn't outside ratio_lut[] */
|
||||||
|
if (ratio > 128) {
|
||||||
|
ratio = 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate lux */
|
||||||
|
*lux = ((adc0_cnt - adc1_cnt) * ratio_lut[ratio]) / 256;
|
||||||
|
|
||||||
|
/* range check */
|
||||||
|
if (*lux > MAX_LUX_STD_MODE) {
|
||||||
|
*lux = MAX_LUX_STD_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
adc_read(int adc, uint8_t * val)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
spin_t spin;
|
||||||
|
|
||||||
|
if (adc != 0 && adc != 1) {
|
||||||
|
log_warn(&log, "Invalid ADC number %d, expected 0 or 1.\n",
|
||||||
|
adc);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val == NULL) {
|
||||||
|
log_warn(&log, "Read called with a NULL pointer.\n");
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*val = (adc == 0) ? CMD_READ_ADC0 : CMD_READ_ADC1;
|
||||||
|
|
||||||
|
/* Select the ADC to read from */
|
||||||
|
r = reg_write(*val);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Failed to write ADC read command.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*val = 0;
|
||||||
|
|
||||||
|
/* Repeatedly read until the value is valid (i.e. the conversion
|
||||||
|
* finishes). Depending on the timing, the data sheet says this
|
||||||
|
* could take up to 400ms.
|
||||||
|
*/
|
||||||
|
spin_init(&spin, 400000);
|
||||||
|
do {
|
||||||
|
r = reg_read(val);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Failed to read ADC%d value.\n", adc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ADC_VAL_IS_VALID(*val)) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
} while (spin_check(&spin));
|
||||||
|
|
||||||
|
/* Final read attempt. If the bus was really busy with other requests
|
||||||
|
* and the timing of things happened in the worst possible case,
|
||||||
|
* there is a chance that the loop above only did 1 read (slightly
|
||||||
|
* before 400 ms) and left the loop. To ensure there is a final read
|
||||||
|
* at or after the 400 ms mark, we try one last time here.
|
||||||
|
*/
|
||||||
|
r = reg_read(val);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Failed to read ADC%d value.\n", adc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ADC_VAL_IS_VALID(*val)) {
|
||||||
|
return OK;
|
||||||
|
} else {
|
||||||
|
log_warn(&log, "ADC%d never returned a valid result.\n", adc);
|
||||||
|
return EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
reg_read(uint8_t * val)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
minix_i2c_ioctl_exec_t ioctl_exec;
|
||||||
|
|
||||||
|
if (val == NULL) {
|
||||||
|
log_warn(&log, "Read called with a NULL pointer.\n");
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
|
||||||
|
|
||||||
|
/* Read from chip */
|
||||||
|
ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP;
|
||||||
|
ioctl_exec.iie_addr = address;
|
||||||
|
|
||||||
|
/* No register address to write */
|
||||||
|
ioctl_exec.iie_cmdlen = 0;
|
||||||
|
|
||||||
|
/* Read one byte */
|
||||||
|
ioctl_exec.iie_buflen = 1;
|
||||||
|
|
||||||
|
r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "reg_read() failed (r=%d)\n", r);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*val = ioctl_exec.iie_buf[0];
|
||||||
|
|
||||||
|
log_trace(&log, "Read 0x%x from reg\n", *val);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
reg_write(uint8_t val)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
minix_i2c_ioctl_exec_t ioctl_exec;
|
||||||
|
|
||||||
|
switch (val) {
|
||||||
|
case CMD_PWR_DOWN:
|
||||||
|
case CMD_PWR_UP:
|
||||||
|
case CMD_EXT_RANGE:
|
||||||
|
case CMD_NORM_RANGE:
|
||||||
|
case CMD_READ_ADC0:
|
||||||
|
case CMD_READ_ADC1:
|
||||||
|
/* Command is valid */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_warn(&log,
|
||||||
|
"reg_write() called with invalid command 0x%x\n", val);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
|
||||||
|
|
||||||
|
/* Write to chip */
|
||||||
|
ioctl_exec.iie_op = I2C_OP_WRITE_WITH_STOP;
|
||||||
|
ioctl_exec.iie_addr = address;
|
||||||
|
|
||||||
|
/* No command bytes for writing to this chip */
|
||||||
|
ioctl_exec.iie_cmdlen = 0;
|
||||||
|
|
||||||
|
/* Set the byte to write */
|
||||||
|
ioctl_exec.iie_buf[0] = val;
|
||||||
|
ioctl_exec.iie_buflen = 1;
|
||||||
|
|
||||||
|
r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "reg_write() failed (r=%d)\n", r);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_trace(&log, "Wrote 0x%x to reg\n", val);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tsl2550_init(void)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
uint8_t val;
|
||||||
|
|
||||||
|
/* Power on the device */
|
||||||
|
r = reg_write(CMD_PWR_UP);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Power-up command failed.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read power on test value */
|
||||||
|
r = reg_read(&val);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Failed to read power on test value.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check power on test value */
|
||||||
|
if (val != EXPECTED_PWR_UP_TEST_VAL) {
|
||||||
|
log_warn(&log, "Bad test value. Got 0x%x, expected 0x%x\n",
|
||||||
|
val, EXPECTED_PWR_UP_TEST_VAL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set range to normal */
|
||||||
|
r = reg_write(CMD_NORM_RANGE);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Normal range command failed.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device *
|
||||||
|
tsl2550_prepare(dev_t UNUSED(dev))
|
||||||
|
{
|
||||||
|
return &tsl2550_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tsl2550_transfer(endpoint_t endpt, int opcode, u64_t position,
|
||||||
|
iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
|
||||||
|
unsigned int UNUSED(flags))
|
||||||
|
{
|
||||||
|
int bytes, r;
|
||||||
|
uint32_t lux;
|
||||||
|
|
||||||
|
r = measure_lux(&lux);
|
||||||
|
if (r != OK) {
|
||||||
|
return EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(buffer, '\0', BUFFER_LEN + 1);
|
||||||
|
snprintf(buffer, BUFFER_LEN, "%-16s: %d\n", "ILLUMINANCE", lux);
|
||||||
|
|
||||||
|
bytes = strlen(buffer) - position < iov->iov_size ?
|
||||||
|
strlen(buffer) - position : iov->iov_size;
|
||||||
|
|
||||||
|
if (bytes <= 0) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
case DEV_GATHER_S:
|
||||||
|
r = sys_safecopyto(endpt, (cp_grant_id_t) iov->iov_addr, 0,
|
||||||
|
(vir_bytes) (buffer + position), bytes);
|
||||||
|
iov->iov_size -= bytes;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tsl2550_other(message * m)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
switch (m->m_type) {
|
||||||
|
case NOTIFY_MESSAGE:
|
||||||
|
if (m->m_source == DS_PROC_NR) {
|
||||||
|
log_debug(&log,
|
||||||
|
"bus driver changed state, update endpoint\n");
|
||||||
|
i2cdriver_handle_bus_update(&bus_endpoint, bus,
|
||||||
|
address);
|
||||||
|
}
|
||||||
|
r = OK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
|
||||||
|
r = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sef_cb_lu_state_save(int UNUSED(state))
|
||||||
|
{
|
||||||
|
ds_publish_u32("bus", bus, DSF_OVERWRITE);
|
||||||
|
ds_publish_u32("address", address, DSF_OVERWRITE);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
lu_state_restore(void)
|
||||||
|
{
|
||||||
|
/* Restore the state. */
|
||||||
|
u32_t value;
|
||||||
|
|
||||||
|
ds_retrieve_u32("bus", &value);
|
||||||
|
ds_delete_u32("bus");
|
||||||
|
bus = (int) value;
|
||||||
|
|
||||||
|
ds_retrieve_u32("address", &value);
|
||||||
|
ds_delete_u32("address");
|
||||||
|
address = (int) value;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sef_cb_init(int type, sef_init_info_t * UNUSED(info))
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (type == SEF_INIT_LU) {
|
||||||
|
/* Restore the state. */
|
||||||
|
lu_state_restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look-up the endpoint for the bus driver */
|
||||||
|
bus_endpoint = i2cdriver_bus_endpoint(bus);
|
||||||
|
if (bus_endpoint == 0) {
|
||||||
|
log_warn(&log, "Couldn't find bus driver.\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* claim the device */
|
||||||
|
r = i2cdriver_reserve_device(bus_endpoint, address);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
|
||||||
|
address, r);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = tsl2550_init();
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Device Init Failed\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type != SEF_INIT_LU) {
|
||||||
|
|
||||||
|
/* sign up for updates about the i2c bus going down/up */
|
||||||
|
r = i2cdriver_subscribe_bus_updates(bus);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Couldn't subscribe to bus updates\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2cdriver_announce(bus);
|
||||||
|
log_debug(&log, "announced\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sef_local_startup(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Register init callbacks. Use the same function for all event types
|
||||||
|
*/
|
||||||
|
sef_setcb_init_fresh(sef_cb_init);
|
||||||
|
sef_setcb_init_lu(sef_cb_init);
|
||||||
|
sef_setcb_init_restart(sef_cb_init);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register live update callbacks.
|
||||||
|
*/
|
||||||
|
/* Agree to update immediately when LU is requested in a valid state. */
|
||||||
|
sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
|
||||||
|
/* Support live update starting from any standard state. */
|
||||||
|
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
|
||||||
|
/* Register a custom routine to save the state. */
|
||||||
|
sef_setcb_lu_state_save(sef_cb_lu_state_save);
|
||||||
|
|
||||||
|
/* Let SEF perform startup. */
|
||||||
|
sef_startup();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
env_setargs(argc, argv);
|
||||||
|
|
||||||
|
r = i2cdriver_env_parse(&bus, &address, valid_addrs);
|
||||||
|
if (r < 0) {
|
||||||
|
log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
|
||||||
|
log_warn(&log, "Example -args 'bus=1 address=0x39'\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else if (r > 0) {
|
||||||
|
log_warn(&log,
|
||||||
|
"Invalid slave address for device, expecting 0x39\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sef_local_startup();
|
||||||
|
|
||||||
|
chardriver_task(&tsl2550_tab, CHARDRIVER_SYNC);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -636,6 +636,11 @@ service tps65950
|
||||||
ipc SYSTEM RS DS i2c readclock.drv;
|
ipc SYSTEM RS DS i2c readclock.drv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
service tsl2550
|
||||||
|
{
|
||||||
|
ipc SYSTEM RS DS i2c;
|
||||||
|
};
|
||||||
|
|
||||||
service vbox
|
service vbox
|
||||||
{
|
{
|
||||||
system
|
system
|
||||||
|
|
|
@ -66,6 +66,10 @@ enum dev_style { STYLE_NDEV, STYLE_DEV, STYLE_DEVA, STYLE_TTY, STYLE_CTTY,
|
||||||
#define EEPROMB3S55_MAJOR 44 /* 44 = /dev/eepromb3s55 (cat24c256) */
|
#define EEPROMB3S55_MAJOR 44 /* 44 = /dev/eepromb3s55 (cat24c256) */
|
||||||
#define EEPROMB3S56_MAJOR 45 /* 45 = /dev/eepromb3s56 (cat24c256) */
|
#define EEPROMB3S56_MAJOR 45 /* 45 = /dev/eepromb3s56 (cat24c256) */
|
||||||
#define EEPROMB3S57_MAJOR 46 /* 46 = /dev/eepromb3s57 (cat24c256) */
|
#define EEPROMB3S57_MAJOR 46 /* 46 = /dev/eepromb3s57 (cat24c256) */
|
||||||
|
#define TSL2550B1S39_MAJOR 47 /* 47 = /dev/tsl2550b1s39 (tsl2550) */
|
||||||
|
#define TSL2550B2S39_MAJOR 48 /* 48 = /dev/tsl2550b2s39 (tsl2550) */
|
||||||
|
#define TSL2550B3S39_MAJOR 49 /* 49 = /dev/tsl2550b3s39 (tsl2550) */
|
||||||
|
|
||||||
|
|
||||||
/* Minor device numbers for memory driver. */
|
/* Minor device numbers for memory driver. */
|
||||||
# define RAM_DEV_OLD 0 /* minor device for /dev/ram */
|
# define RAM_DEV_OLD 0 /* minor device for /dev/ram */
|
||||||
|
|
Loading…
Reference in a new issue