Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
#
# STMicroelectonics LIS3LV02D and similar accelerometers
#
config SENSORS_LIS3_SPI
tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)"
depends on !ACPI && SPI_MASTER && INPUT
select SENSORS_LIS3LV02D
default n
help
This driver provides support for the LIS3LV02Dx accelerometer connected
via SPI. The accelerometer data is readable via
/sys/devices/platform/lis3lv02d.
This driver also provides an absolute input class device, allowing
the laptop to act as a pinball machine-esque joystick.
This driver can also be built as modules. If so, the core module
will be called lis3lv02d and a specific module for the SPI transport
is called lis3lv02d_spi.
config SENSORS_LIS3_I2C
tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (I2C)"
depends on I2C && INPUT
select SENSORS_LIS3LV02D
default n
help
This driver provides support for the LIS3LV02Dx accelerometer connected
via I2C. The accelerometer data is readable via
/sys/devices/platform/lis3lv02d.
This driver also provides an absolute input class device, allowing
the device to act as a pinball machine-esque joystick.
This driver can also be built as modules. If so, the core module
will be called lis3lv02d and a specific module for the I2C transport
is called lis3lv02d_i2c.

View File

@@ -0,0 +1,7 @@
#
# STMicroelectonics LIS3LV02D and similar accelerometers
#
obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o
obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d_spi.o
obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d_i2c.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,331 @@
/*
* lis3lv02d.h - ST LIS3LV02DL accelerometer driver
*
* Copyright (C) 2007-2008 Yan Burman
* Copyright (C) 2008-2009 Eric Piel
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
#include <linux/regulator/consumer.h>
#include <linux/miscdevice.h>
/*
* This driver tries to support the "digital" accelerometer chips from
* STMicroelectronics such as LIS3LV02DL, LIS302DL, LIS3L02DQ, LIS331DL,
* LIS331DLH, LIS35DE, or LIS202DL. They are very similar in terms of
* programming, with almost the same registers. In addition to differing
* on physical properties, they differ on the number of axes (2/3),
* precision (8/12 bits), and special features (freefall detection,
* click...). Unfortunately, not all the differences can be probed via
* a register. They can be connected either via I²C or SPI.
*/
#include <linux/lis3lv02d.h>
enum lis3_reg {
WHO_AM_I = 0x0F,
OFFSET_X = 0x16,
OFFSET_Y = 0x17,
OFFSET_Z = 0x18,
GAIN_X = 0x19,
GAIN_Y = 0x1A,
GAIN_Z = 0x1B,
CTRL_REG1 = 0x20,
CTRL_REG2 = 0x21,
CTRL_REG3 = 0x22,
CTRL_REG4 = 0x23,
HP_FILTER_RESET = 0x23,
STATUS_REG = 0x27,
OUTX_L = 0x28,
OUTX_H = 0x29,
OUTX = 0x29,
OUTY_L = 0x2A,
OUTY_H = 0x2B,
OUTY = 0x2B,
OUTZ_L = 0x2C,
OUTZ_H = 0x2D,
OUTZ = 0x2D,
};
enum lis302d_reg {
FF_WU_CFG_1 = 0x30,
FF_WU_SRC_1 = 0x31,
FF_WU_THS_1 = 0x32,
FF_WU_DURATION_1 = 0x33,
FF_WU_CFG_2 = 0x34,
FF_WU_SRC_2 = 0x35,
FF_WU_THS_2 = 0x36,
FF_WU_DURATION_2 = 0x37,
CLICK_CFG = 0x38,
CLICK_SRC = 0x39,
CLICK_THSY_X = 0x3B,
CLICK_THSZ = 0x3C,
CLICK_TIMELIMIT = 0x3D,
CLICK_LATENCY = 0x3E,
CLICK_WINDOW = 0x3F,
};
enum lis3lv02d_reg {
FF_WU_CFG = 0x30,
FF_WU_SRC = 0x31,
FF_WU_ACK = 0x32,
FF_WU_THS_L = 0x34,
FF_WU_THS_H = 0x35,
FF_WU_DURATION = 0x36,
DD_CFG = 0x38,
DD_SRC = 0x39,
DD_ACK = 0x3A,
DD_THSI_L = 0x3C,
DD_THSI_H = 0x3D,
DD_THSE_L = 0x3E,
DD_THSE_H = 0x3F,
};
enum lis3_who_am_i {
WAI_3DLH = 0x32, /* 16 bits: LIS331DLH */
WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */
WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */
WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */
WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */
};
enum lis3_type {
LIS3LV02D,
LIS3DC,
HP3DC,
LIS2302D,
LIS331DLF,
LIS331DLH,
};
enum lis3lv02d_ctrl1_12b {
CTRL1_Xen = 0x01,
CTRL1_Yen = 0x02,
CTRL1_Zen = 0x04,
CTRL1_ST = 0x08,
CTRL1_DF0 = 0x10,
CTRL1_DF1 = 0x20,
CTRL1_PD0 = 0x40,
CTRL1_PD1 = 0x80,
};
/* Delta to ctrl1_12b version */
enum lis3lv02d_ctrl1_8b {
CTRL1_STM = 0x08,
CTRL1_STP = 0x10,
CTRL1_FS = 0x20,
CTRL1_PD = 0x40,
CTRL1_DR = 0x80,
};
enum lis3lv02d_ctrl1_3dc {
CTRL1_ODR0 = 0x10,
CTRL1_ODR1 = 0x20,
CTRL1_ODR2 = 0x40,
CTRL1_ODR3 = 0x80,
};
enum lis331dlh_ctrl1 {
CTRL1_DR0 = 0x08,
CTRL1_DR1 = 0x10,
CTRL1_PM0 = 0x20,
CTRL1_PM1 = 0x40,
CTRL1_PM2 = 0x80,
};
enum lis331dlh_ctrl2 {
CTRL2_HPEN1 = 0x04,
CTRL2_HPEN2 = 0x08,
CTRL2_FDS_3DLH = 0x10,
CTRL2_BOOT_3DLH = 0x80,
};
enum lis331dlh_ctrl4 {
CTRL4_STSIGN = 0x08,
CTRL4_BLE = 0x40,
CTRL4_BDU = 0x80,
};
enum lis3lv02d_ctrl2 {
CTRL2_DAS = 0x01,
CTRL2_SIM = 0x02,
CTRL2_DRDY = 0x04,
CTRL2_IEN = 0x08,
CTRL2_BOOT = 0x10,
CTRL2_BLE = 0x20,
CTRL2_BDU = 0x40, /* Block Data Update */
CTRL2_FS = 0x80, /* Full Scale selection */
};
enum lis3lv02d_ctrl4_3dc {
CTRL4_SIM = 0x01,
CTRL4_ST0 = 0x02,
CTRL4_ST1 = 0x04,
CTRL4_FS0 = 0x10,
CTRL4_FS1 = 0x20,
};
enum lis302d_ctrl2 {
HP_FF_WU2 = 0x08,
HP_FF_WU1 = 0x04,
CTRL2_BOOT_8B = 0x40,
};
enum lis3lv02d_ctrl3 {
CTRL3_CFS0 = 0x01,
CTRL3_CFS1 = 0x02,
CTRL3_FDS = 0x10,
CTRL3_HPFF = 0x20,
CTRL3_HPDD = 0x40,
CTRL3_ECK = 0x80,
};
enum lis3lv02d_status_reg {
STATUS_XDA = 0x01,
STATUS_YDA = 0x02,
STATUS_ZDA = 0x04,
STATUS_XYZDA = 0x08,
STATUS_XOR = 0x10,
STATUS_YOR = 0x20,
STATUS_ZOR = 0x40,
STATUS_XYZOR = 0x80,
};
enum lis3lv02d_ff_wu_cfg {
FF_WU_CFG_XLIE = 0x01,
FF_WU_CFG_XHIE = 0x02,
FF_WU_CFG_YLIE = 0x04,
FF_WU_CFG_YHIE = 0x08,
FF_WU_CFG_ZLIE = 0x10,
FF_WU_CFG_ZHIE = 0x20,
FF_WU_CFG_LIR = 0x40,
FF_WU_CFG_AOI = 0x80,
};
enum lis3lv02d_ff_wu_src {
FF_WU_SRC_XL = 0x01,
FF_WU_SRC_XH = 0x02,
FF_WU_SRC_YL = 0x04,
FF_WU_SRC_YH = 0x08,
FF_WU_SRC_ZL = 0x10,
FF_WU_SRC_ZH = 0x20,
FF_WU_SRC_IA = 0x40,
};
enum lis3lv02d_dd_cfg {
DD_CFG_XLIE = 0x01,
DD_CFG_XHIE = 0x02,
DD_CFG_YLIE = 0x04,
DD_CFG_YHIE = 0x08,
DD_CFG_ZLIE = 0x10,
DD_CFG_ZHIE = 0x20,
DD_CFG_LIR = 0x40,
DD_CFG_IEND = 0x80,
};
enum lis3lv02d_dd_src {
DD_SRC_XL = 0x01,
DD_SRC_XH = 0x02,
DD_SRC_YL = 0x04,
DD_SRC_YH = 0x08,
DD_SRC_ZL = 0x10,
DD_SRC_ZH = 0x20,
DD_SRC_IA = 0x40,
};
enum lis3lv02d_click_src_8b {
CLICK_SINGLE_X = 0x01,
CLICK_DOUBLE_X = 0x02,
CLICK_SINGLE_Y = 0x04,
CLICK_DOUBLE_Y = 0x08,
CLICK_SINGLE_Z = 0x10,
CLICK_DOUBLE_Z = 0x20,
CLICK_IA = 0x40,
};
enum lis3lv02d_reg_state {
LIS3_REG_OFF = 0x00,
LIS3_REG_ON = 0x01,
};
union axis_conversion {
struct {
int x, y, z;
};
int as_array[3];
};
struct lis3lv02d {
void *bus_priv; /* used by the bus layer only */
struct device *pm_dev; /* for pm_runtime purposes */
int (*init) (struct lis3lv02d *lis3);
int (*write) (struct lis3lv02d *lis3, int reg, u8 val);
int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret);
int (*blkread) (struct lis3lv02d *lis3, int reg, int len, u8 *ret);
int (*reg_ctrl) (struct lis3lv02d *lis3, bool state);
int *odrs; /* Supported output data rates */
u8 *regs; /* Regs to store / restore */
int regs_size;
u8 *reg_cache;
bool regs_stored;
u8 odr_mask; /* ODR bit mask */
u8 whoami; /* indicates measurement precision */
s16 (*read_data) (struct lis3lv02d *lis3, int reg);
int mdps_max_val;
int pwron_delay;
int scale; /*
* relationship between 1 LBS and mG
* (1/1000th of earth gravity)
*/
struct input_polled_dev *idev; /* input device */
struct platform_device *pdev; /* platform device */
struct regulator_bulk_data regulators[2];
atomic_t count; /* interrupt count after last read */
union axis_conversion ac; /* hw -> logical axis */
int mapped_btns[3];
u32 irq; /* IRQ number */
struct fasync_struct *async_queue; /* queue for the misc device */
wait_queue_head_t misc_wait; /* Wait queue for the misc device */
unsigned long misc_opened; /* bit0: whether the device is open */
struct miscdevice miscdev;
int data_ready_count[2];
atomic_t wake_thread;
unsigned char irq_cfg;
unsigned int shift_adj;
struct lis3lv02d_platform_data *pdata; /* for passing board config */
struct mutex mutex; /* Serialize poll and selftest */
#ifdef CONFIG_OF
struct device_node *of_node;
#endif
};
int lis3lv02d_init_device(struct lis3lv02d *lis3);
int lis3lv02d_joystick_enable(struct lis3lv02d *lis3);
void lis3lv02d_joystick_disable(struct lis3lv02d *lis3);
void lis3lv02d_poweroff(struct lis3lv02d *lis3);
int lis3lv02d_poweron(struct lis3lv02d *lis3);
int lis3lv02d_remove_fs(struct lis3lv02d *lis3);
int lis3lv02d_init_dt(struct lis3lv02d *lis3);
extern struct lis3lv02d lis3_dev;

View File

@@ -0,0 +1,291 @@
/*
* drivers/hwmon/lis3lv02d_i2c.c
*
* Implements I2C interface for lis3lv02d (STMicroelectronics) accelerometer.
* Driver is based on corresponding SPI driver written by Daniel Mack
* (lis3lv02d_spi.c (C) 2009 Daniel Mack <daniel@caiaq.de> ).
*
* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
*
* Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include "lis3lv02d.h"
#define DRV_NAME "lis3lv02d_i2c"
static const char reg_vdd[] = "Vdd";
static const char reg_vdd_io[] = "Vdd_IO";
static int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state)
{
int ret;
if (state == LIS3_REG_OFF) {
ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators),
lis3->regulators);
} else {
ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators),
lis3->regulators);
/* Chip needs time to wakeup. Not mentioned in datasheet */
usleep_range(10000, 20000);
}
return ret;
}
static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value)
{
struct i2c_client *c = lis3->bus_priv;
return i2c_smbus_write_byte_data(c, reg, value);
}
static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v)
{
struct i2c_client *c = lis3->bus_priv;
*v = i2c_smbus_read_byte_data(c, reg);
return 0;
}
static inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len,
u8 *v)
{
struct i2c_client *c = lis3->bus_priv;
reg |= (1 << 7); /* 7th bit enables address auto incrementation */
return i2c_smbus_read_i2c_block_data(c, reg, len, v);
}
static int lis3_i2c_init(struct lis3lv02d *lis3)
{
u8 reg;
int ret;
lis3_reg_ctrl(lis3, LIS3_REG_ON);
lis3->read(lis3, WHO_AM_I, &reg);
if (reg != lis3->whoami)
printk(KERN_ERR "lis3: power on failure\n");
/* power up the device */
ret = lis3->read(lis3, CTRL_REG1, &reg);
if (ret < 0)
return ret;
if (lis3->whoami == WAI_3DLH)
reg |= CTRL1_PM0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
else
reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
return lis3->write(lis3, CTRL_REG1, reg);
}
/* Default axis mapping but it can be overwritten by platform data */
static union axis_conversion lis3lv02d_axis_map =
{ .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } };
#ifdef CONFIG_OF
static struct of_device_id lis3lv02d_i2c_dt_ids[] = {
{ .compatible = "st,lis3lv02d" },
{}
};
MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids);
#endif
static int lis3lv02d_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
#ifdef CONFIG_OF
if (of_match_device(lis3lv02d_i2c_dt_ids, &client->dev)) {
lis3_dev.of_node = client->dev.of_node;
ret = lis3lv02d_init_dt(&lis3_dev);
if (ret)
return ret;
pdata = lis3_dev.pdata;
}
#endif
if (pdata) {
if ((pdata->driver_features & LIS3_USE_BLOCK_READ) &&
(i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK)))
lis3_dev.blkread = lis3_i2c_blockread;
if (pdata->axis_x)
lis3lv02d_axis_map.x = pdata->axis_x;
if (pdata->axis_y)
lis3lv02d_axis_map.y = pdata->axis_y;
if (pdata->axis_z)
lis3lv02d_axis_map.z = pdata->axis_z;
if (pdata->setup_resources)
ret = pdata->setup_resources();
if (ret)
goto fail;
}
lis3_dev.regulators[0].supply = reg_vdd;
lis3_dev.regulators[1].supply = reg_vdd_io;
ret = regulator_bulk_get(&client->dev,
ARRAY_SIZE(lis3_dev.regulators),
lis3_dev.regulators);
if (ret < 0)
goto fail;
lis3_dev.pdata = pdata;
lis3_dev.bus_priv = client;
lis3_dev.init = lis3_i2c_init;
lis3_dev.read = lis3_i2c_read;
lis3_dev.write = lis3_i2c_write;
lis3_dev.irq = client->irq;
lis3_dev.ac = lis3lv02d_axis_map;
lis3_dev.pm_dev = &client->dev;
i2c_set_clientdata(client, &lis3_dev);
/* Provide power over the init call */
lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON);
ret = lis3lv02d_init_device(&lis3_dev);
lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF);
if (ret)
goto fail2;
return 0;
fail2:
regulator_bulk_free(ARRAY_SIZE(lis3_dev.regulators),
lis3_dev.regulators);
fail:
if (pdata && pdata->release_resources)
pdata->release_resources();
return ret;
}
static int lis3lv02d_i2c_remove(struct i2c_client *client)
{
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
if (pdata && pdata->release_resources)
pdata->release_resources();
lis3lv02d_joystick_disable(lis3);
lis3lv02d_remove_fs(&lis3_dev);
regulator_bulk_free(ARRAY_SIZE(lis3->regulators),
lis3_dev.regulators);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int lis3lv02d_i2c_suspend(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
if (!lis3->pdata || !lis3->pdata->wakeup_flags)
lis3lv02d_poweroff(lis3);
return 0;
}
static int lis3lv02d_i2c_resume(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
/*
* pm_runtime documentation says that devices should always
* be powered on at resume. Pm_runtime turns them off after system
* wide resume is complete.
*/
if (!lis3->pdata || !lis3->pdata->wakeup_flags ||
pm_runtime_suspended(dev))
lis3lv02d_poweron(lis3);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM_RUNTIME
static int lis3_i2c_runtime_suspend(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
lis3lv02d_poweroff(lis3);
return 0;
}
static int lis3_i2c_runtime_resume(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
lis3lv02d_poweron(lis3);
return 0;
}
#endif /* CONFIG_PM_RUNTIME */
static const struct i2c_device_id lis3lv02d_id[] = {
{"lis3lv02d", LIS3LV02D},
{"lis331dlh", LIS331DLH},
{}
};
MODULE_DEVICE_TABLE(i2c, lis3lv02d_id);
static const struct dev_pm_ops lis3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend,
lis3lv02d_i2c_resume)
SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend,
lis3_i2c_runtime_resume,
NULL)
};
static struct i2c_driver lis3lv02d_i2c_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3_pm_ops,
.of_match_table = of_match_ptr(lis3lv02d_i2c_dt_ids),
},
.probe = lis3lv02d_i2c_probe,
.remove = lis3lv02d_i2c_remove,
.id_table = lis3lv02d_id,
};
module_i2c_driver(lis3lv02d_i2c_driver);
MODULE_AUTHOR("Nokia Corporation");
MODULE_DESCRIPTION("lis3lv02d I2C interface");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,155 @@
/*
* lis3lv02d_spi - SPI glue layer for lis3lv02d
*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* publishhed by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
#include <linux/pm.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include "lis3lv02d.h"
#define DRV_NAME "lis3lv02d_spi"
#define LIS3_SPI_READ 0x80
static int lis3_spi_read(struct lis3lv02d *lis3, int reg, u8 *v)
{
struct spi_device *spi = lis3->bus_priv;
int ret = spi_w8r8(spi, reg | LIS3_SPI_READ);
if (ret < 0)
return -EINVAL;
*v = (u8) ret;
return 0;
}
static int lis3_spi_write(struct lis3lv02d *lis3, int reg, u8 val)
{
u8 tmp[2] = { reg, val };
struct spi_device *spi = lis3->bus_priv;
return spi_write(spi, tmp, sizeof(tmp));
}
static int lis3_spi_init(struct lis3lv02d *lis3)
{
u8 reg;
int ret;
/* power up the device */
ret = lis3->read(lis3, CTRL_REG1, &reg);
if (ret < 0)
return ret;
reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
return lis3->write(lis3, CTRL_REG1, reg);
}
static union axis_conversion lis3lv02d_axis_normal =
{ .as_array = { 1, 2, 3 } };
#ifdef CONFIG_OF
static struct of_device_id lis302dl_spi_dt_ids[] = {
{ .compatible = "st,lis302dl-spi" },
{}
};
MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
#endif
static int lis302dl_spi_probe(struct spi_device *spi)
{
int ret;
spi->bits_per_word = 8;
spi->mode = SPI_MODE_0;
ret = spi_setup(spi);
if (ret < 0)
return ret;
lis3_dev.bus_priv = spi;
lis3_dev.init = lis3_spi_init;
lis3_dev.read = lis3_spi_read;
lis3_dev.write = lis3_spi_write;
lis3_dev.irq = spi->irq;
lis3_dev.ac = lis3lv02d_axis_normal;
lis3_dev.pdata = spi->dev.platform_data;
#ifdef CONFIG_OF
if (of_match_device(lis302dl_spi_dt_ids, &spi->dev)) {
lis3_dev.of_node = spi->dev.of_node;
ret = lis3lv02d_init_dt(&lis3_dev);
if (ret)
return ret;
}
#endif
spi_set_drvdata(spi, &lis3_dev);
return lis3lv02d_init_device(&lis3_dev);
}
static int lis302dl_spi_remove(struct spi_device *spi)
{
struct lis3lv02d *lis3 = spi_get_drvdata(spi);
lis3lv02d_joystick_disable(lis3);
lis3lv02d_poweroff(lis3);
return lis3lv02d_remove_fs(&lis3_dev);
}
#ifdef CONFIG_PM_SLEEP
static int lis3lv02d_spi_suspend(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct lis3lv02d *lis3 = spi_get_drvdata(spi);
if (!lis3->pdata || !lis3->pdata->wakeup_flags)
lis3lv02d_poweroff(&lis3_dev);
return 0;
}
static int lis3lv02d_spi_resume(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct lis3lv02d *lis3 = spi_get_drvdata(spi);
if (!lis3->pdata || !lis3->pdata->wakeup_flags)
lis3lv02d_poweron(lis3);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(lis3lv02d_spi_pm, lis3lv02d_spi_suspend,
lis3lv02d_spi_resume);
static struct spi_driver lis302dl_spi_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3lv02d_spi_pm,
.of_match_table = of_match_ptr(lis302dl_spi_dt_ids),
},
.probe = lis302dl_spi_probe,
.remove = lis302dl_spi_remove,
};
module_spi_driver(lis302dl_spi_driver);
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
MODULE_DESCRIPTION("lis3lv02d SPI glue layer");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:" DRV_NAME);