Current-Users archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Driver for the ST LIS302DL MEMS motion sensor
Hi current-users,
I am writing a driver for the ST LIS302DL MEMS motion sensor. This
component can be found on the I2C bus in the Nokia N900 smartphone
hardware, which is where it currently expects to attach:
# Accelerometer
lis302dl0 at iic2 addr 0x1d
It seems to be otherwise available in a number of other hardware
devices, although not always on I2C apparently. The Openmoko Freerunner
features one on the SPI interface for instance:
http://wiki.openmoko.org/wiki/ST_LIS302DL
I am not currently unable to test it besides just attaching, so I will
welcome comments before committing it, and of course suggestions as to
how to make it more portable (eg usable on other buses as well).
HTH,
--
khorben
/* $NetBSD$ */
/*
* Copyright (c) 2013 Pierre Pronchery <khorben%defora.org@localhost>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ST LIS302DL MEMS motion sensor
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");
#include <sys/param.h>
#include <sys/device.h>
#include <dev/i2c/i2cvar.h>
#include <dev/sysmon/sysmonvar.h>
/* registers */
#define LIS302DL_REG_WHO_AM_I 0x0f
#define LIS302DL_REG_CTRL1 0x20
#define LIS302DL_REG_CTRL2 0x21
#define LIS302DL_REG_CTRL3 0x22
#define LIS302DL_REG_FILTER_RESET 0x23
#define LIS302DL_REG_STATUS 0x27
#define LIS302DL_REG_OUTX 0x29
#define LIS302DL_REG_OUTY 0x2b
#define LIS302DL_REG_OUTZ 0x2d
#define LIS302DL_REG_FF_WU_CFG1 0x30
#define LIS302DL_REG_FF_WU_SRC1 0x31
#define LIS302DL_REG_FF_WU_THS1 0x32
#define LIS302DL_REG_FF_WU_DURATION_1 0x33
#define LIS302DL_REG_FF_WU_CFG2 0x34
#define LIS302DL_REG_FF_WU_SRC2 0x35
#define LIS302DL_REG_FF_WU_THS2 0x36
#define LIS302DL_REG_CLICK_CFG 0x38
#define LIS302DL_REG_CLICK_SRC 0x39
#define LIS302DL_REG_CLICK_THSY_X 0x3b
#define LIS302DL_REG_CLICK_THSZ 0x3c
/* constants */
#define LIS302DL_ID 0x3b
#define LIS302DL_CTRL1_XEN __BIT(0)
#define LIS302DL_CTRL1_YEN __BIT(1)
#define LIS302DL_CTRL1_ZEN __BIT(2)
#define LIS302DL_CTRL1_STM __BIT(3)
#define LIS302DL_CTRL1_STP __BIT(4)
#define LIS302DL_CTRL1_FS __BIT(5)
#define LIS302DL_CTRL1_PD __BIT(6)
#define LIS302DL_CTRL1_DR __BIT(7)
#define LIS302DL_CTRL2_HP_COEFF1 __BIT(0)
#define LIS302DL_CTRL2_HP_COEFF2 __BIT(1)
#define LIS302DL_CTRL2_HP_FF_WU1 __BIT(2)
#define LIS302DL_CTRL2_HP_FF_WU2 __BIT(3)
#define LIS302DL_CTRL2_FDS __BIT(4)
#define LIS302DL_CTRL2_BOOT __BIT(6)
#define LIS302DL_CTRL2_SIM __BIT(7)
#define LIS302DL_STATUS_XDA __BIT(0)
#define LIS302DL_STATUS_YDA __BIT(1)
#define LIS302DL_STATUS_ZDA __BIT(2)
#define LIS302DL_STATUS_ZYXDA __BIT(3)
#define LIS302DL_STATUS_XOR __BIT(4)
#define LIS302DL_STATUS_YOR __BIT(5)
#define LIS302DL_STATUS_ZOR __BIT(6)
#define LIS302DL_STATUS_ZXYOR __BIT(7)
#define LIS302DL_FF_WU_CFG1_XLIE __BIT(0)
#define LIS302DL_FF_WU_CFG1_XHIE __BIT(1)
#define LIS302DL_FF_WU_CFG1_YLIE __BIT(2)
#define LIS302DL_FF_WU_CFG1_YHIE __BIT(3)
#define LIS302DL_FF_WU_CFG1_ZLIE __BIT(4)
#define LIS302DL_FF_WU_CFG1_ZHIE __BIT(5)
#define LIS302DL_FF_WU_CFG1_LIR __BIT(6)
#define LIS302DL_FF_WU_CFG1_AOI __BIT(7)
#define LIS302DL_SRC1_XL __BIT(0)
#define LIS302DL_SRC1_XE __BIT(1)
#define LIS302DL_SRC1_YL __BIT(2)
#define LIS302DL_SRC1_YH __BIT(3)
#define LIS302DL_SRC1_ZL __BIT(4)
#define LIS302DL_SRC1_ZH __BIT(5)
#define LIS302DL_SRC1_IA __BIT(6)
#define LIS302DL_FF_WU_CFG2_XLIE __BIT(0)
#define LIS302DL_FF_WU_CFG2_XHIE __BIT(1)
#define LIS302DL_FF_WU_CFG2_YLIE __BIT(2)
#define LIS302DL_FF_WU_CFG2_YHIE __BIT(3)
#define LIS302DL_FF_WU_CFG2_ZLIE __BIT(4)
#define LIS302DL_FF_WU_CFG2_ZHIE __BIT(5)
#define LIS302DL_FF_WU_CFG2_LIR __BIT(6)
#define LIS302DL_FF_WU_CFG2_AOI __BIT(7)
#define LIS302DL_CLICK_CFG_X __BIT(0)
#define LIS302DL_CLICK_CFG_X2 __BIT(1)
#define LIS302DL_CLICK_CFG_Y __BIT(2)
#define LIS302DL_CLICK_CFG_Y2 __BIT(3)
#define LIS302DL_CLICK_CFG_Z __BIT(4)
#define LIS302DL_CLICK_CFG_Z2 __BIT(5)
#define LIS302DL_CLICK_CFG_LIR __BIT(6)
enum lis302dl_sensors {
LIS302DL_SENSOR_XACCEL = 0,
LIS302DL_SENSOR_YACCEL,
LIS302DL_SENSOR_ZACCEL
};
#define LIS302DL_SENSOR_LAST LIS302DL_SENSOR_ZACCEL
#define LIS302DL_SENSOR_CNT (LIS302DL_SENSOR_LAST + 1)
/* variables */
struct lis302dl_softc {
device_t sc_dev;
i2c_tag_t sc_i2c;
i2c_addr_t sc_addr;
struct sysmon_envsys *sc_sme;
envsys_data_t sc_sensor[LIS302DL_SENSOR_CNT];
void * sc_intr;
};
static int lis302dl_match(device_t, cfdata_t, void *);
static void lis302dl_attach(device_t, device_t, void *);
static int lis302dl_detach(device_t, int);
static int lis302dl_reset(struct lis302dl_softc *);
static int lis302dl_intr(void *);
static int lis302dl_read_1(struct lis302dl_softc *, uint8_t, uint8_t *);
static int lis302dl_write_1(struct lis302dl_softc *, uint8_t, uint8_t);
CFATTACH_DECL_NEW(lis302dl, sizeof(struct lis302dl_softc),
lis302dl_match, lis302dl_attach, lis302dl_detach, NULL);
static int
lis302dl_match(device_t parent, cfdata_t match, void *aux)
{
return 1;
}
static void
lis302dl_attach(device_t parent, device_t self, void *aux)
{
struct lis302dl_softc *sc = device_private(self);
struct i2c_attach_args *ia = aux;
uint8_t u8;
int i;
sc->sc_dev = self;
sc->sc_i2c = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
aprint_naive("\n");
aprint_normal(": motion sensor\n");
lis302dl_reset(sc);
sc->sc_intr = iic_smbus_intr_establish(sc->sc_i2c, lis302dl_intr, sc);
if (sc->sc_intr == NULL) {
aprint_error("Could not establish interrupt\n");
return;
}
iic_acquire_bus(sc->sc_i2c, 0);
/* enable free-fall wake up */
u8 = LIS302DL_FF_WU_CFG1_XLIE | LIS302DL_FF_WU_CFG1_XHIE
| LIS302DL_FF_WU_CFG1_YLIE | LIS302DL_FF_WU_CFG1_YHIE
| LIS302DL_FF_WU_CFG1_ZLIE | LIS302DL_FF_WU_CFG1_ZHIE;
lis302dl_write_1(sc, LIS302DL_REG_FF_WU_CFG1, u8);
u8 = LIS302DL_FF_WU_CFG2_XLIE | LIS302DL_FF_WU_CFG2_XHIE
| LIS302DL_FF_WU_CFG2_YLIE | LIS302DL_FF_WU_CFG2_YHIE
| LIS302DL_FF_WU_CFG2_ZLIE | LIS302DL_FF_WU_CFG2_ZHIE;
lis302dl_write_1(sc, LIS302DL_REG_FF_WU_CFG2, u8);
/* enable clicks */
u8 = LIS302DL_CLICK_CFG_X | LIS302DL_CLICK_CFG_X2
| LIS302DL_CLICK_CFG_Y | LIS302DL_CLICK_CFG_Y2
| LIS302DL_CLICK_CFG_Z | LIS302DL_CLICK_CFG_Z2;
lis302dl_write_1(sc, LIS302DL_REG_CLICK_CFG, u8);
iic_release_bus(sc->sc_i2c, 0);
/* initialize the sensors */
#define INITDATA(idx, unit, string) \
sc->sc_sensor[idx].units = unit; \
strlcpy(sc->sc_sensor[idx].desc, string, \
sizeof(sc->sc_sensor[idx].desc));
INITDATA(LIS302DL_SENSOR_XACCEL, ENVSYS_INTEGER, "x-acceleration");
INITDATA(LIS302DL_SENSOR_YACCEL, ENVSYS_INTEGER, "y-acceleration");
INITDATA(LIS302DL_SENSOR_ZACCEL, ENVSYS_INTEGER, "z-acceleration");
sc->sc_sme = sysmon_envsys_create();
for (i = 0; i < LIS302DL_SENSOR_CNT; i++) {
sc->sc_sensor[i].state = ENVSYS_SVALID;
if (sc->sc_sensor[i].units == ENVSYS_INTEGER) {
sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
}
if (sysmon_envsys_sensor_attach(sc->sc_sme,
&sc->sc_sensor[i])) {
sysmon_envsys_destroy(sc->sc_sme);
break;
}
}
/* register with sysmon_envsys(9) */
sc->sc_sme->sme_name = device_xname(self);
sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
if ((i = sysmon_envsys_register(sc->sc_sme))) {
aprint_error_dev(self,
"unable to register with sysmon (%d)\n", i);
sysmon_envsys_destroy(sc->sc_sme);
return;
}
}
static int
lis302dl_detach(device_t self, int flags)
{
struct lis302dl_softc *sc = device_private(self);
if (sc->sc_sme)
sysmon_envsys_unregister(sc->sc_sme);
/* FIXME should probably also disable interrupts */
return 0;
}
static int
lis302dl_reset(struct lis302dl_softc *sc)
{
uint8_t u8;
iic_acquire_bus(sc->sc_i2c, 0);
/* reboot the device */
if (lis302dl_read_1(sc, LIS302DL_REG_CTRL2, &u8) == 0) {
u8 |= LIS302DL_CTRL2_BOOT;
lis302dl_write_1(sc, LIS302DL_REG_CTRL2, u8);
}
/* reset the internal filter */
lis302dl_read_1(sc, LIS302DL_REG_FILTER_RESET, &u8);
/* enable all three axis and power */
u8 = LIS302DL_CTRL1_XEN | LIS302DL_CTRL1_YEN | LIS302DL_CTRL1_ZEN
| LIS302DL_CTRL1_PD;
lis302dl_write_1(sc, LIS302DL_REG_CTRL1, u8);
/* reset the free-fall thresholds */
lis302dl_write_1(sc, LIS302DL_REG_FF_WU_THS1, 0);
lis302dl_write_1(sc, LIS302DL_REG_FF_WU_THS2, 0);
/* reset the click thresholds */
lis302dl_write_1(sc, LIS302DL_REG_CLICK_THSY_X, 0);
lis302dl_write_1(sc, LIS302DL_REG_CLICK_THSZ, 0);
iic_release_bus(sc->sc_i2c, 0);
return 0;
}
static int
lis302dl_intr(void *v)
{
struct lis302dl_softc *sc = v;
uint8_t val;
uint8_t u8;
iic_acquire_bus(sc->sc_i2c, 0);
if (lis302dl_read_1(sc, LIS302DL_REG_STATUS, &val) == 0) {
if(val & LIS302DL_STATUS_XDA) {
lis302dl_read_1(sc, LIS302DL_REG_OUTX, &u8);
sc->sc_sensor[LIS302DL_SENSOR_XACCEL].value_cur = u8;
}
if(val & LIS302DL_STATUS_YDA) {
lis302dl_read_1(sc, LIS302DL_REG_OUTY, &u8);
sc->sc_sensor[LIS302DL_SENSOR_YACCEL].value_cur = u8;
}
if(val & LIS302DL_STATUS_ZDA) {
lis302dl_read_1(sc, LIS302DL_REG_OUTZ, &u8);
sc->sc_sensor[LIS302DL_SENSOR_ZACCEL].value_cur = u8;
}
}
if (lis302dl_read_1(sc, LIS302DL_REG_FF_WU_SRC1, &val) == 0) {
/* FIXME implement */
}
if (lis302dl_read_1(sc, LIS302DL_REG_FF_WU_SRC2, &val) == 0) {
/* FIXME implement */
}
if (lis302dl_read_1(sc, LIS302DL_REG_CLICK_SRC, &val) == 0) {
/* FIXME implement */
}
iic_release_bus(sc->sc_i2c, 0);
return 0;
}
static int
lis302dl_read_1(struct lis302dl_softc *sc, uint8_t reg, uint8_t *val)
{
return iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP, sc->sc_addr,
®, sizeof(reg), val, sizeof(*val), 0);
}
static int
lis302dl_write_1(struct lis302dl_softc *sc, uint8_t reg, uint8_t val)
{
uint8_t data[2] = { reg, val };
return iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
NULL, 0, data, sizeof(data), 0);
}
Home |
Main Index |
Thread Index |
Old Index