On 10/18/11 09:28, Ricardo Ribalda Delgado wrote: > Add support for CMR3000 Tri-axis accelerometer. CMR3000 supports both > I2C/SPI bus communication, currently the driver supports SPI > communication, since I have no hardware to test the I2C communication. > Quick review done below. Quite a bit of overlap with earlier reviews. Sorry I couldn't take a closer look. > Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@xxxxxxxxx> > --- > drivers/input/misc/Kconfig | 24 ++ > drivers/input/misc/Makefile | 2 + > drivers/input/misc/cmr3000_d0x.c | 400 ++++++++++++++++++++++++++++++++++ > drivers/input/misc/cmr3000_d0x.h | 45 ++++ > drivers/input/misc/cmr3000_d0x_spi.c | 178 +++++++++++++++ > include/linux/input/cmr3000.h | 54 +++++ > 6 files changed, 703 insertions(+), 0 deletions(-) > create mode 100644 drivers/input/misc/cmr3000_d0x.c > create mode 100644 drivers/input/misc/cmr3000_d0x.h > create mode 100644 drivers/input/misc/cmr3000_d0x_spi.c > create mode 100644 include/linux/input/cmr3000.h > > diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig > index b9f2e93..7c56f94 100644 > --- a/drivers/input/misc/Kconfig > +++ b/drivers/input/misc/Kconfig > @@ -524,6 +524,30 @@ config INPUT_CMA3000_SPI > To compile this driver as a module, choose M here: the > module will be called cma3000_d0x_spi. > > +config INPUT_CMR3000 > + tristate "VTI CMR3000 Tri-axis gyroscope" > + help > + Say Y here if you want to use VTI CMR3000_D0x Gyroscope > + driver > + > + This driver currently only supports SPI interface to the > + controller. Also select the SPI method. > + > + If unsure, say N > + > + To compile this driver as a module, choose M here: the > + module will be called cmr3000_d0x. > + > +config INPUT_CMR3000_SPI > + tristate "Support SPI bus connection" > + depends on INPUT_CMR3000 && SPI > + help > + Say Y here if you want to use VTI CMR3000_D0x Gyroscope > + through SPI interface. > + > + To compile this driver as a module, choose M here: the > + module will be called cmr3000_d0x_spi. > + > config INPUT_XEN_KBDDEV_FRONTEND > tristate "Xen virtual keyboard and mouse support" > depends on XEN_FBDEV_FRONTEND > diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile > index 7305f6f..c7fe09a 100644 > --- a/drivers/input/misc/Makefile > +++ b/drivers/input/misc/Makefile > @@ -21,6 +21,8 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o > obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o > obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o > obj-$(CONFIG_INPUT_CMA3000_SPI) += cma3000_d0x_spi.o > +obj-$(CONFIG_INPUT_CMR3000) += cmr3000_d0x.o > +obj-$(CONFIG_INPUT_CMR3000_SPI) += cmr3000_d0x_spi.o > obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o > obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o > obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o > diff --git a/drivers/input/misc/cmr3000_d0x.c b/drivers/input/misc/cmr3000_d0x.c > new file mode 100644 > index 0000000..5ffe38b > --- /dev/null > +++ b/drivers/input/misc/cmr3000_d0x.c > @@ -0,0 +1,400 @@ > +/* > + * VTI CMR3000_D0x Gyroscope driver > + * > + * Copyright (C) 2011 Qtechnology > + * Author: Ricardo Ribalda <ricardo.ribalda@xxxxxxxxx> > + * > + * Based on: > + * Author: Hemanth V <hemanthv@xxxxxx> > + * > + * 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, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/types.h> > +#include <linux/interrupt.h> > +#include <linux/delay.h> > +#include <linux/slab.h> > +#include <linux/input.h> > +#include <linux/input/cmr3000.h> > + > +#include "cmr3000_d0x.h" > + > +#define CMR3000_REV 0x21 > + > +#define CMR3000_WHOAMI 0x00 > +#define CMR3000_REVID 0x01 > +#define CMR3000_CTRL 0x02 > +#define CMR3000_STATUS 0x03 > +#define CMR3000_X_LSB 0x0C > +#define CMR3000_X_MSB 0x0D > +#define CMR3000_Y_LSB 0x0E > +#define CMR3000_Y_MSB 0x0F > +#define CMR3000_Z_LSB 0x10 > +#define CMR3000_Z_MSB 0x11 > +#define CMR3000_I2C_ADDR 0x22 > +#define CMR3000_PDR 0x26 > + > +#define CMR3000_IRQDIS (1 << 0) > +#define CMR3000_MODEMASK (3 << 1) > +#define CMR3000_BUSI2C (0 << 4) > +#define CMR3000_BUSSPI (1 << 4) > +#define CMR3000_INTLOW (1 << 6) > +#define CMR3000_INTHIGH (0 << 6) > +#define CMR3000_RST (1 << 7) > + > +#define CMRMODE_SHIFT 1 > +#define CMRIRQLEVEL_SHIFT 6 > + > +#define CMR3000_STATUS_PERR (1 << 0) > +#define CMR3000_STATUS_PORST (1 << 3) > + > +/* Settling time delay in ms */ > +#define CMR3000_SETDELAY 30 > + > +/* Delay for clearing interrupt in us */ > +#define CMR3000_INTDELAY 44 > + > +/* > + * Bit weights mult/div in dps for bit 0, other bits need > + * multipy factor 2^n. 11th bit is the sign bit. > + */ > +#define BIT_TO_DPS_MUL 3 > +#define BIT_TO_DPS_DIV 32 > + > +static struct cmr3000_platform_data cmr3000_default_pdata = { > + .irq_level = CMR3000_INTHIGH, > + .mode = CMRMODE_MEAS80, > + .irqflags = 0, > + .fuzz_x = 1, > + .fuzz_y = 1, > + .fuzz_z = 1, > +}; > + accl? I thought this was a gyro so this seems odd. > +struct cmr3000_accl_data { > + const struct cmr3000_bus_ops *bus_ops; > + const struct cmr3000_platform_data *pdata; > + > + struct device *dev; > + struct input_dev *input_dev; > + > + int irq_level; > + u8 mode; > + > + int bit_to_mg; > + int irq; > + > + struct mutex mutex; > + bool opened; > + bool suspended; > +}; > + I'd much rather see these done explicity in the code. This sort of macro just obscures the works to save a few lines of code. > +#define CMR3000_READ(data, reg, msg) \ > + (data->bus_ops->read(data->dev, reg, msg)) > +#define CMR3000_SET(data, reg, val, msg) \ > + ((data)->bus_ops->write(data->dev, reg, val, msg)) > + > +static void decode_dps(struct cmr3000_accl_data *data, int *datax, > + int *datay, int *dataz) > +{ > + /* Data in 2's complement, convert to dps */ > + *datax = (((s16) ((*datax) << 2)) * BIT_TO_DPS_MUL) / BIT_TO_DPS_DIV; > + *datay = (((s16) ((*datay) << 2)) * BIT_TO_DPS_MUL) / BIT_TO_DPS_DIV; > + *dataz = (((s16) ((*dataz) << 2)) * BIT_TO_DPS_MUL) / BIT_TO_DPS_DIV; > +} > + > +static irqreturn_t cmr3000_thread_irq(int irq, void *dev_id) > +{ > + struct cmr3000_accl_data *data = dev_id; > + int datax, datay, dataz; > + u8 mode, intr_status; > + > + intr_status = CMR3000_READ(data, CMR3000_STATUS, "interrupt status"); > + intr_status = CMR3000_READ(data, CMR3000_CTRL, "control mode"); > + if (intr_status < 0) > + return IRQ_NONE; > + > + /* Interrupt not for this device */ > + if (intr_status & CMR3000_IRQDIS) > + return IRQ_NONE; > + > + mode = (intr_status & CMR3000_MODEMASK) >> CMRMODE_SHIFT; > + if ((mode != CMRMODE_MEAS80) > + && (mode != CMRMODE_MEAS20)) > + return IRQ_NONE; > + > + datax = (CMR3000_READ(data, CMR3000_X_MSB, "X_MSB")) << 8; > + datax |= CMR3000_READ(data, CMR3000_X_LSB, "X_LSB"); > + datay = (CMR3000_READ(data, CMR3000_Y_MSB, "Y_MSB")) << 8; > + datay |= CMR3000_READ(data, CMR3000_Y_LSB, "Y_LSB"); > + dataz = (CMR3000_READ(data, CMR3000_Z_MSB, "Z_MSB")) << 8; > + dataz |= CMR3000_READ(data, CMR3000_Z_LSB, "Z_LSB"); > + Why are you checking this twice? > + /* Device closed */ > + if ((data->mode != CMRMODE_MEAS80) > + && (data->mode != CMRMODE_MEAS20)) > + return IRQ_NONE; > + > + /* Decode register values to dps */ > + decode_dps(data, &datax, &datay, &dataz); > + > + input_report_abs(data->input_dev, ABS_X, datax); > + input_report_abs(data->input_dev, ABS_Y, datay); > + input_report_abs(data->input_dev, ABS_Z, dataz); > + input_sync(data->input_dev); > + > + return IRQ_HANDLED; > +} > + > +static int cmr3000_poweron(struct cmr3000_accl_data *data) > +{ > + const struct cmr3000_platform_data *pdata = data->pdata; > + u8 ctrl; > + int ret; > + > + ctrl = pdata->irq_level << CMRIRQLEVEL_SHIFT; > + ctrl |= data->mode << CMRMODE_SHIFT; > + ctrl |= data->bus_ops->ctrl_mod; > + ret = CMR3000_SET(data, CMR3000_CTRL, ctrl, "Mode setting"); > + if (ret < 0) > + return -EIO; > + > + msleep(CMR3000_SETDELAY); > + > + return 0; > +} > + > +static int cmr3000_poweroff(struct cmr3000_accl_data *data) > +{ > + int ret; > + u8 ctrl = CMRMODE_POFF; > + > + ctrl |= data->bus_ops->ctrl_mod; > + ctrl |= CMR3000_IRQDIS; No real reason not to do the above in one line. > + > + ret = CMR3000_SET(data, CMR3000_CTRL, ctrl, "Mode setting"); > + msleep(CMR3000_SETDELAY); > + > + return ret; > +} > + > +static int cmr3000_reset(struct cmr3000_accl_data *data) > +{ > + int val; > + > + /* Reset chip */ > + CMR3000_SET(data, CMR3000_CTRL, CMR3000_RST, "Reset"); > + mdelay(2); > + > + /* Settling time delay */ > + val = CMR3000_READ(data, CMR3000_STATUS, "Status"); > + if (val < 0) { > + dev_err(data->dev, "Reset failed\n"); > + return val; > + } > + > + if (val & CMR3000_STATUS_PERR) { > + dev_err(data->dev, "Parity Error\n"); > + return -EIO; > + } > + > + return cmr3000_poweroff(data); > +} > + > +static int cmr3000_open(struct input_dev *input_dev) > +{ > + struct cmr3000_accl_data *data = input_get_drvdata(input_dev); > + > + mutex_lock(&data->mutex); > + > + if (!data->suspended) > + cmr3000_poweron(data); > + > + data->opened = true; > + > + mutex_unlock(&data->mutex); > + > + return 0; > +} > + > +static void cmr3000_close(struct input_dev *input_dev) > +{ > + struct cmr3000_accl_data *data = input_get_drvdata(input_dev); > + > + mutex_lock(&data->mutex); > + > + if (!data->suspended) > + cmr3000_poweroff(data); > + > + data->opened = false; > + > + mutex_unlock(&data->mutex); > +} > + > +void cmr3000_suspend(struct cmr3000_accl_data *data) > +{ > + mutex_lock(&data->mutex); > + > + if (!data->suspended && data->opened) > + cmr3000_poweroff(data); > + > + data->suspended = true; > + > + mutex_unlock(&data->mutex); > +} > +EXPORT_SYMBOL(cmr3000_suspend); > + > +void cmr3000_resume(struct cmr3000_accl_data *data) > +{ > + mutex_lock(&data->mutex); > + > + if (data->suspended && data->opened) > + cmr3000_poweron(data); > + > + data->suspended = false; > + > + mutex_unlock(&data->mutex); > +} > +EXPORT_SYMBOL(cmr3000_resume); > + > +struct cmr3000_accl_data *cmr3000_init(struct device *dev, int irq, > + const struct cmr3000_bus_ops *bops) > +{ > + const struct cmr3000_platform_data *pdata; > + struct cmr3000_accl_data *data; > + struct input_dev *input_dev; > + int rev; > + int error; > + > + if (!dev->platform_data) { > + dev_info(dev, "platform data not found, using default\n"); > + pdata = &cmr3000_default_pdata; > + } else > + pdata = dev->platform_data; > + > + if (!pdata) { > + dev_err(dev, "platform data not found\n"); > + error = -EINVAL; > + goto err_out; > + } > + > + /* if no IRQ return error */ > + if (irq == 0) { > + error = -EINVAL; > + goto err_out; > + } > + > + data = kzalloc(sizeof(struct cmr3000_accl_data), GFP_KERNEL); > + input_dev = input_allocate_device(); > + if (!data || !input_dev) { > + error = -ENOMEM; > + goto err_free_mem; > + } > + > + data->dev = dev; > + data->input_dev = input_dev; > + data->bus_ops = bops; > + data->pdata = pdata; > + data->irq = irq; > + mutex_init(&data->mutex); > + > + data->mode = pdata->mode; > + if ((data->mode != CMRMODE_MEAS80) > + && (data->mode != CMRMODE_MEAS20)) { > + data->mode = CMRMODE_MEAS80; > + dev_warn(dev, "Invalid mode specified, assuming 80Hz\n"); > + } > + > + data->irq_level = pdata->irq_level; > + if ((data->irq_level != CMR3000_INTLOW) > + && (data->irq_level != CMR3000_INTHIGH)) { > + data->irq_level = CMR3000_INTHIGH; > + dev_warn(data->dev, > + "Invalid int level specified, assuming high\n"); > + } > + > + input_dev->name = "cmr3000-gyroscope"; > + input_dev->id.bustype = bops->bustype; > + input_dev->open = cmr3000_open; > + input_dev->close = cmr3000_close; > + > + __set_bit(EV_ABS, input_dev->evbit); > + > + input_set_abs_params(input_dev, ABS_X, > + -CMRRANGE, CMRRANGE, pdata->fuzz_x, 0); > + input_set_abs_params(input_dev, ABS_Y, > + -CMRRANGE, CMRRANGE, pdata->fuzz_y, 0); > + input_set_abs_params(input_dev, ABS_Z, > + -CMRRANGE, CMRRANGE, pdata->fuzz_z, 0); > + > + input_set_drvdata(input_dev, data); > + > + error = cmr3000_reset(data); > + if (error) > + goto err_free_mem; > + > + rev = CMR3000_READ(data, CMR3000_REVID, "Revid"); > + if (rev < 0) { > + error = rev; > + goto err_free_mem; > + } > + if (rev != CMR3000_REV) { > + error = -EINVAL; > + pr_err("CMR3000 Gyroscope: Unknown Revision %x\n", rev); > + goto err_free_mem; > + } > + pr_info("CMR3000 Gyroscope: Revision %x\n", rev); > + > + rev = CMR3000_READ(data, CMR3000_WHOAMI, "WhoamI"); > + if (rev < 0) { > + error = rev; > + goto err_free_mem; > + } > + pr_info("CMR3000 Gyroscope: WhoamI %x\n", rev); > + > + error = request_threaded_irq(irq, NULL, cmr3000_thread_irq, > + pdata->irqflags | IRQF_ONESHOT, > + "cmr3000_d0x", data); > + if (error) { > + dev_err(dev, "request_threaded_irq failed\n"); > + goto err_free_mem; > + } > + > + error = input_register_device(data->input_dev); > + if (error) { > + dev_err(dev, "Unable to register input device\n"); > + goto err_free_irq; > + } > + > + return data; > + > +err_free_irq: > + free_irq(irq, data); > +err_free_mem: > + input_free_device(input_dev); > + kfree(data); > +err_out: > + return ERR_PTR(error); > +} > +EXPORT_SYMBOL(cmr3000_init); > + > +void cmr3000_exit(struct cmr3000_accl_data *data) > +{ > + free_irq(data->irq, data); > + input_unregister_device(data->input_dev); cleaner to unwind in reverse order of setup unless you have a good reason and that should be commented. > + kfree(data); > +} > +EXPORT_SYMBOL(cmr3000_exit); > + > +MODULE_DESCRIPTION("CMR3000-D0x Gyroscope Driver"); > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@xxxxxxxxx>"); > diff --git a/drivers/input/misc/cmr3000_d0x.h b/drivers/input/misc/cmr3000_d0x.h > new file mode 100644 > index 0000000..604c7d4 > --- /dev/null > +++ b/drivers/input/misc/cmr3000_d0x.h > @@ -0,0 +1,45 @@ > +/* > + * VTI CMR3000_D0x Gyroscpe driver > + * > + * Copyright (C) 2011 Qtechnology > + * Author: Ricardo Ribalda <ricardo.ribalda@xxxxxxxxx> > + * > + * Based on: > + * Hemanth V <hemanthv@xxxxxx> > + * > + * 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, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef _INPUT_CMR3000_H > +#define _INPUT_CMR3000_H > + > +#include <linux/types.h> > +#include <linux/input.h> > + > +struct device; > +struct cmr3000_accl_data; > + > +struct cmr3000_bus_ops { > + u16 bustype; > + u8 ctrl_mod; > + int (*read) (struct device *, u8, char *); > + int (*write) (struct device *, u8, u8, char *); > +}; > + > +struct cmr3000_accl_data *cmr3000_init(struct device *dev, int irq, > + const struct cmr3000_bus_ops *bops); > +void cmr3000_exit(struct cmr3000_accl_data *); > +void cmr3000_suspend(struct cmr3000_accl_data *); > +void cmr3000_resume(struct cmr3000_accl_data *); > + > +#endif > diff --git a/drivers/input/misc/cmr3000_d0x_spi.c b/drivers/input/misc/cmr3000_d0x_spi.c > new file mode 100644 > index 0000000..4ec139c > --- /dev/null > +++ b/drivers/input/misc/cmr3000_d0x_spi.c > @@ -0,0 +1,178 @@ > +/* > + * Implements SPI interface for VTI CMR300_D0x Accelerometer driver > + * > + * Copyright (C) 2011 Qtechnology > + * Author: Ricardo Ribalda <ricardo.ribalda@xxxxxxxxxxxxx> > + * Based on: > + * Hemanth V <hemanthv@xxxxxx> > + * Michael Hennerich <hennerich@xxxxxxxxxxxxxxxxxxxx> > + * > + * 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, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/delay.h> > +#include <linux/module.h> > +#include <linux/spi/spi.h> > +#include <linux/input/cmr3000.h> > +#include "cmr3000_d0x.h" > + > +enum { DO_READ = 0, DO_WRITE }; > + > +static int cmr3000_spi_cmd(struct spi_device *spi, u8 reg, u8 * val, int cmd, > + char *msg) > +{ > + int ret; Same as with earlier patch on all counts in this function. > + unsigned char tx_buf[2]; > + unsigned char rx_buf[2]; > + struct spi_transfer t = { > + .rx_buf = rx_buf, > + .tx_buf = tx_buf, > + .len = 2, > + }; > + struct spi_message m; > + > + if (cmd == DO_WRITE) { > + tx_buf[0] = (reg << 2) | 2; > + tx_buf[1] = *val; > + } else { > + tx_buf[0] = reg << 2; > + tx_buf[1] = 0; > + } > + spi_message_init(&m); > + spi_message_add_tail(&t, &m); > + ret = spi_sync(spi, &m); > + if (ret < 0) { > + dev_err(&spi->dev, "%s failed (%s, %d)\n", __func__, msg, ret); > + return ret; > + } > + if (cmd == DO_READ) > + *val = rx_buf[1]; > + > + if (rx_buf[0] & 0x41) > + dev_err(&spi->dev, > + "%s Invalid Zero mask(0x%x)\n", __func__, rx_buf[0]); > + > + if ((rx_buf[0] & 0x2) != 0x2) > + dev_err(&spi->dev, > + "%s Invalid One mask (0x%x)\n", __func__, rx_buf[0]); > + > + return 0; > +} > + > +static int cmr3000_spi_set(struct device *dev, u8 reg, u8 val, char *msg) > +{ > + > + struct spi_device *spi = to_spi_device(dev); > + > + return cmr3000_spi_cmd(spi, reg, &val, DO_WRITE, msg); > +} > + > +static int cmr3000_spi_read(struct device *dev, u8 reg, char *msg) > +{ > + struct spi_device *spi = to_spi_device(dev); > + int ret; > + u8 val; > + > + ret = cmr3000_spi_cmd(spi, reg, &val, DO_READ, msg); > + if (ret) > + return ret; > + return val; > +} > + > +static const struct cmr3000_bus_ops cmr3000_spi_bops = { > + .bustype = BUS_SPI, > +#define CMR3000_BUSSPI (1 << 4) > + .ctrl_mod = CMR3000_BUSSPI, > + .read = cmr3000_spi_read, > + .write = cmr3000_spi_set, > +}; > + > +static int __devinit cmr3000_spi_probe(struct spi_device *spi) > +{ > + struct cmr3000_accl_data *data; > + > + data = cmr3000_init(&spi->dev, spi->irq, &cmr3000_spi_bops); > + if (IS_ERR(data)) > + return PTR_ERR(data); > + > + spi_set_drvdata(spi, data); > + > + return 0; > +} > + > +static int __devexit cmr3000_spi_remove(struct spi_device *spi) > +{ > + struct cmr3000_accl_data *data = dev_get_drvdata(&spi->dev); > + > + cmr3000_exit(data); > + > + return 0; > +} > + > +#ifdef CONFIG_PM > +static int cmr3000_spi_suspend(struct device *dev) > +{ > + struct spi_device *spi = to_spi_device(dev); > + struct cmr3000_accl_data *data = dev_get_drvdata(&spi->dev); > + > + cmr3000_suspend(data); > + > + return 0; > +} > + > +static int cmr3000_spi_resume(struct device *dev) > +{ > + struct spi_device *spi = to_spi_device(dev); > + struct cmr3000_accl_data *data = dev_get_drvdata(&spi->dev); > + > + cmr3000_resume(data); > + > + return 0; > +} > + > +static const struct dev_pm_ops cmr3000_spi_pm_ops = { > + .suspend = cmr3000_spi_suspend, > + .resume = cmr3000_spi_resume, > +}; > +#endif > + > +static SIMPLE_DEV_PM_OPS(cmr3000_spi_pm, cmr3000_spi_suspend, > + cmr3000_spi_resume); > + > +static struct spi_driver cmr3000_driver = { > + .driver = { > + .name = "cmr3000_d01", > + .bus = &spi_bus_type, > + .owner = THIS_MODULE, > + .pm = &cmr3000_spi_pm, > + }, > + .probe = cmr3000_spi_probe, > + .remove = __devexit_p(cmr3000_spi_remove), > +}; > + > +static int __init cmr3000_spi_init(void) > +{ > + return spi_register_driver(&cmr3000_driver); > +} > + > +static void __exit cmr3000_spi_exit(void) > +{ > + spi_unregister_driver(&cmr3000_driver); > +} > + > +module_init(cmr3000_spi_init); > +module_exit(cmr3000_spi_exit); > + > +MODULE_DESCRIPTION("CMR3000-D0x Gyroscope SPI Driver"); > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@xxxxxxxxx>"); > diff --git a/include/linux/input/cmr3000.h b/include/linux/input/cmr3000.h > new file mode 100644 > index 0000000..0202100 > --- /dev/null > +++ b/include/linux/input/cmr3000.h > @@ -0,0 +1,54 @@ > +/* > + * VTI CMR3000_Dxx Gyroscope driver > + * > + * Copyright (C) 2011 Qtechnology > + * Author: Ricardo Ribalda <ricardo.ribalda@xxxxxxxxx> > + * > + * Copyright (C) 2010 Texas Instruments > + * Author: Hemanth V <hemanthv@xxxxxx> > + * > + * 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, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef _LINUX_CMR3000_H > +#define _LINUX_CMR3000_H > + > +#define CMRMODE_DEFAULT 0 > +#define CMRMODE_STANDBY 1 > +#define CMRMODE_MEAS20 2 > +#define CMRMODE_MEAS80 3 > +#define CMRMODE_POFF 0 > + > +#define CMRIRQLEVEL_LOW 1 > +#define CMRIRQLEVEL_HIGH 0 > + > +#define CMRRANGE 3072 > + > +/** Match comment to name fo structure. > + * struct cmr3000_i2c_platform_data - CMR3000 Platform data > + * @fuzz_x: Noise on X Axis > + * @fuzz_y: Noise on Y Axis > + * @fuzz_z: Noise on Z Axis > + * @mode: Operating mode > + * @irq_level: Irq level > + */ > +struct cmr3000_platform_data { > + int fuzz_x; > + int fuzz_y; > + int fuzz_z; > + uint8_t irq_level; > + uint8_t mode; > + unsigned long irqflags; > +}; > + > +#endif -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html