Hello Grant It is similar to the crm, because they came from the same manufacturer and they share some commands, but that is all. the crm is a gyroscope and the cma is an accelerometer. I tried to put both drivers together and the result was a bit too messy, I think it is easier to understand as two separate drivers, but if you believe they have to live together and help me making it more understandable, I have to problem in coding it. Regards! On Mon, Oct 24, 2011 at 23:40, Grant Likely <grant.likely@xxxxxxxxxxxx> wrote: > On Mon, Oct 24, 2011 at 10:21:15PM +0200, 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. > > How different is this driver from the cma3000? Is it a cut and paste job? > > g. > >> >> --- >> >> v4: Fixes suggested by Dmitry Torokhov >> -Do not release the input device until the irq has been released >> >> v3: Fixes suggested by Jonathan Cameron >> -Support DT >> -Cleaner spi read/write >> >> v2: Fixes suggested by Jonathan Cameron >> -Code Stype >> -Check pdata!=NULL >> -SPI align Cacheline >> -More clear based on >> -%s/set/write/ >> -%s/accl/gyro/ >> -remove READ/SET macros >> >> 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 | 426 ++++++++++++++++++++++++++++++++++ >> drivers/input/misc/cmr3000_d0x.h | 45 ++++ >> drivers/input/misc/cmr3000_d0x_spi.c | 144 ++++++++++++ >> include/linux/input/cmr3000.h | 54 +++++ >> 6 files changed, 695 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..d046149 >> --- /dev/null >> +++ b/drivers/input/misc/cmr3000_d0x.c >> @@ -0,0 +1,426 @@ >> +/* >> + * VTI CMR3000_D0x Gyroscope driver >> + * >> + * Copyright (C) 2011 Qtechnology >> + * Author: Ricardo Ribalda <ricardo.ribalda@xxxxxxxxx> >> + * >> + * Based on: >> + * drivers/input/misc/cma3000_d0x.c by: Hemanth V >> + * >> + * 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 <linux/of.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 >> + >> +/* >> + * 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, >> + .fuzz_x = 1, >> + .fuzz_y = 1, >> + .fuzz_z = 1, >> + .irqflags = 0, >> +}; >> + >> +struct cmr3000_gyro_data { >> + const struct cmr3000_bus_ops *bus_ops; >> + 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; >> +}; >> + >> +static void decode_dps(struct cmr3000_gyro_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_gyro_data *data = dev_id; >> + int datax, datay, dataz; >> + u8 mode, intr_status; >> + >> + intr_status = data->bus_ops->read(data->dev, CMR3000_STATUS, >> + "irq status"); >> + intr_status = data->bus_ops->read(data->dev, 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 = (data->bus_ops->read(data->dev, CMR3000_X_MSB, "X_MSB")) << 8; >> + datax |= data->bus_ops->read(data->dev, CMR3000_X_LSB, "X_LSB"); >> + datay = (data->bus_ops->read(data->dev, CMR3000_Y_MSB, "Y_MSB")) << 8; >> + datay |= data->bus_ops->read(data->dev, CMR3000_Y_LSB, "Y_LSB"); >> + dataz = (data->bus_ops->read(data->dev, CMR3000_Z_MSB, "Z_MSB")) << 8; >> + dataz |= data->bus_ops->read(data->dev, CMR3000_Z_LSB, "Z_LSB"); >> + >> + /* 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_gyro_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 = data->bus_ops->write(data->dev, CMR3000_CTRL, ctrl, >> + "Mode setting"); >> + if (ret < 0) >> + return -EIO; >> + >> + msleep(CMR3000_SETDELAY); >> + >> + return 0; >> +} >> + >> +static int cmr3000_poweroff(struct cmr3000_gyro_data *data) >> +{ >> + int ret; >> + u8 ctrl = CMRMODE_POFF; >> + >> + ctrl |= data->bus_ops->ctrl_mod; >> + ctrl |= CMR3000_IRQDIS; >> + >> + ret = data->bus_ops->write(data->dev, CMR3000_CTRL, ctrl, >> + "Mode setting"); >> + msleep(CMR3000_SETDELAY); >> + >> + return ret; >> +} >> + >> +static int cmr3000_reset(struct cmr3000_gyro_data *data) >> +{ >> + int val; >> + >> + /* Reset chip */ >> + data->bus_ops->write(data->dev, CMR3000_CTRL, CMR3000_RST, "Reset"); >> + mdelay(2); >> + >> + /* Settling time delay */ >> + val = data->bus_ops->read(data->dev, 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_gyro_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_gyro_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_gyro_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_gyro_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); >> + >> +#ifdef CONFIG_OF >> +void cmr3000_get_pdata_of(struct device *dev, struct cmr3000_gyro_data *data) >> +{ >> + const __be32 *property; >> + int len; >> + >> + property = of_get_property(dev->of_node, "vti,irq_level", &len); >> + if (property && len == sizeof(int)) >> + data->pdata.irq_level = be32_to_cpup(property); >> + >> + property = of_get_property(dev->of_node, "vti,mode", &len); >> + if (property && len == sizeof(int)) >> + data->pdata.mode = be32_to_cpup(property); >> + >> + property = of_get_property(dev->of_node, "vti,fuzz_x", &len); >> + if (property && len == sizeof(int)) >> + data->pdata.fuzz_x = be32_to_cpup(property); >> + >> + property = of_get_property(dev->of_node, "vti,fuzz_y", &len); >> + if (property && len == sizeof(int)) >> + data->pdata.fuzz_y = be32_to_cpup(property); >> + >> + property = of_get_property(dev->of_node, "vti,fuzz_z", &len); >> + if (property && len == sizeof(int)) >> + data->pdata.fuzz_z = be32_to_cpup(property); >> + >> + property = of_get_property(dev->of_node, "vti,irqflags", &len); >> + if (property && len == sizeof(int)) >> + data->pdata.irqflags = be32_to_cpup(property); >> + >> + return; >> +} >> +#endif >> + >> +struct cmr3000_gyro_data *cmr3000_init(struct device *dev, int irq, >> + const struct cmr3000_bus_ops *bops) >> +{ >> + struct cmr3000_platform_data *pdata; >> + struct cmr3000_gyro_data *data; >> + struct input_dev *input_dev; >> + int rev; >> + int error; >> + >> + /* if no IRQ return error */ >> + if (irq == 0) { >> + error = -EINVAL; >> + goto err_out; >> + } >> + >> + data = kzalloc(sizeof(struct cmr3000_gyro_data), GFP_KERNEL); >> + input_dev = input_allocate_device(); >> + if (!data || !input_dev) { >> + error = -ENOMEM; >> + goto err_free_mem; >> + } >> + >> + /*Init platform data*/ >> + if (dev->platform_data != NULL) { >> + memcpy(&data->pdata, dev->platform_data, sizeof(data->pdata)); >> + } else { >> + memcpy(&data->pdata, &cmr3000_default_pdata, >> + sizeof(data->pdata)); >> + #ifdef CONFIG_OF >> + if (dev->of_node != NULL) >> + cmr3000_get_pdata_of(dev, data); >> + else >> + #endif >> + dev_info(dev, "platform data not found, using default\n"); >> + } >> + pdata = &data->pdata; >> + >> + data->dev = dev; >> + data->input_dev = input_dev; >> + data->bus_ops = bops; >> + 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 = data->bus_ops->read(data->dev, 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); >> + >> + 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_gyro_data *data) >> +{ >> + free_irq(data->irq, data); >> + input_unregister_device(data->input_dev); >> + 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..3d0984a >> --- /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: >> + * drivers/input/misc/cma3000_d0x.h by Hemanth V >> + * >> + * 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_gyro_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_gyro_data *cmr3000_init(struct device *dev, int irq, >> + const struct cmr3000_bus_ops *bops); >> +void cmr3000_exit(struct cmr3000_gyro_data *); >> +void cmr3000_suspend(struct cmr3000_gyro_data *); >> +void cmr3000_resume(struct cmr3000_gyro_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..7f27fa3 >> --- /dev/null >> +++ b/drivers/input/misc/cmr3000_d0x_spi.c >> @@ -0,0 +1,144 @@ >> +/* >> + * Implements SPI interface for VTI CMR300_D0x Accelerometer driver >> + * >> + * Copyright (C) 2011 Qtechnology >> + * Author: Ricardo Ribalda <ricardo.ribalda@xxxxxxxxxxxxx> >> + * Based on: >> + * drivers/input/misc/cma3000_d0x_i2c.c by Hemanth V >> + * drivers/input/mis/adxl34x-spi.c by Michael Hennerich >> + * >> + * 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" >> + >> +static int cmr3000_spi_write(struct device *dev, u8 reg, u8 val, char *msg) >> +{ >> + struct spi_device *spi = to_spi_device(dev); >> + int ret; >> + u8 tmp[2]; >> + >> + tmp[0] = (reg << 2) | 2; >> + tmp[1] = val; >> + >> + ret = spi_write_then_read(spi, tmp, sizeof(tmp), NULL, 0); >> + if (ret < 0) { >> + dev_err(dev, "%s failed (%s, %d)\n", __func__, msg, ret); >> + return ret; >> + } >> + return 0; >> +} >> + >> +static int cmr3000_spi_read(struct device *dev, u8 reg, char *msg) >> +{ >> + struct spi_device *spi = to_spi_device(dev); >> + int ret; >> + >> + ret = spi_w8r8(spi, reg << 2); >> + if (ret < 0) >> + dev_err(dev, "%s failed (%s, %d)\n", __func__, msg, ret); >> + >> + return ret; >> +} >> + >> +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_write, >> +}; >> + >> +static int __devinit cmr3000_spi_probe(struct spi_device *spi) >> +{ >> + struct cmr3000_gyro_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_gyro_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_gyro_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_gyro_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..dfcf9e3 >> --- /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> >> + * >> + * Based on: >> + * include/linux/input/cma3000.h by Hemanth V >> + * >> + * 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 >> + >> +/** >> + * struct cmr3000_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 >> -- >> 1.7.7 >> > -- Ricardo Ribalda -- 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