From e1a1842321813a2fb05aa8833d8b7556772a6d14 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 16 Jul 2010 15:50:47 +0800 Subject: [PATCH] add gsensor mma7660 driver --- drivers/input/Kconfig | 1 + drivers/input/Makefile | 1 + drivers/input/gsensor/Kconfig | 22 ++ drivers/input/gsensor/Makefile | 3 + drivers/input/gsensor/mma7660.c | 641 ++++++++++++++++++++++++++++++++ include/linux/mma7660.h | 92 +++++ 6 files changed, 760 insertions(+) create mode 100644 drivers/input/gsensor/Kconfig create mode 100644 drivers/input/gsensor/Makefile create mode 100644 drivers/input/gsensor/mma7660.c create mode 100644 include/linux/mma7660.h diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index de0fa9930553..a082b068f41d 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -181,6 +181,7 @@ source "drivers/input/touchscreen/Kconfig" source "drivers/input/misc/Kconfig" +source "drivers/input/gsensor/Kconfig" endif menu "Hardware I/O ports" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index eef9e24eab98..05f9813786df 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_INPUT_JOYSTICK) += joystick/ obj-$(CONFIG_INPUT_TABLET) += tablet/ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ +obj-$(CONFIG_G_SENSOR_DEVICE) += gsensor/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o diff --git a/drivers/input/gsensor/Kconfig b/drivers/input/gsensor/Kconfig new file mode 100644 index 000000000000..748d69c17b54 --- /dev/null +++ b/drivers/input/gsensor/Kconfig @@ -0,0 +1,22 @@ +# +# gsensor drivers configuration +# + +menuconfig G_SENSOR_DEVICE + bool "g_sensor device support" + default n + help + Enable this to be able to choose the drivers for controlling the + g_sensor on some platforms, for example on PDAs. + +if G_SENSOR_DEVICE + +config GS_MMA7660 + bool "gs_mma7660" + depends on G_SENSOR_DEVICE + default y + help + To have support for your specific gsesnor you will have to + select the proper drivers which depend on this option. + +endif diff --git a/drivers/input/gsensor/Makefile b/drivers/input/gsensor/Makefile new file mode 100644 index 000000000000..bd0a069ceeb7 --- /dev/null +++ b/drivers/input/gsensor/Makefile @@ -0,0 +1,3 @@ +# gsensor drivers + +obj-$(CONFIG_GS_MMA7660) += mma7660.o diff --git a/drivers/input/gsensor/mma7660.c b/drivers/input/gsensor/mma7660.c new file mode 100644 index 000000000000..61dceedc4798 --- /dev/null +++ b/drivers/input/gsensor/mma7660.c @@ -0,0 +1,641 @@ +/* drivers/i2c/chips/mma7660.c - mma7660 compass driver + * + * Copyright (C) 2007-2008 HTC Corporation. + * Author: Hou-Kun Chen + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ANDROID_POWER +#include +#endif + +//#define RK28_PRINT +//#include + +#define rk28printk(x...) printk(x) + +static int mma7660_probe(struct i2c_client *client, const struct i2c_device_id *id); + + +#define MMA7660_GPIO_INT RK2818_PIN_PE3 + +/* Addresses to scan -- protected by sense_data_mutex */ +static char sense_data[RBUFF_SIZE + 1]; +static struct i2c_client *this_client; + +static DECLARE_WAIT_QUEUE_HEAD(data_ready_wq); +static atomic_t data_ready; +#ifdef CONFIG_ANDROID_POWER +static android_early_suspend_t mma7660_early_suspend; +#endif +static int revision = -1; +/* AKM HW info */ +static ssize_t gsensor_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "%#x\n", revision); + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(vendor, 0444, gsensor_vendor_show, NULL); + +static struct kobject *android_gsensor_kobj; + +static int gsensor_sysfs_init(void) +{ + int ret ; + + android_gsensor_kobj = kobject_create_and_add("android_gsensor", NULL); + if (android_gsensor_kobj == NULL) { + rk28printk(KERN_ERR + "MMA7660 gsensor_sysfs_init:"\ + "subsystem_register failed\n"); + ret = -ENOMEM; + goto err; + } + + ret = sysfs_create_file(android_gsensor_kobj, &dev_attr_vendor.attr); + if (ret) { + rk28printk(KERN_ERR + "MMA7660 gsensor_sysfs_init:"\ + "sysfs_create_group failed\n"); + goto err4; + } + + return 0 ; +err4: + kobject_del(android_gsensor_kobj); +err: + return ret ; +} + +static int mma7660_rx_data(char *rxData, int length) +{ +#if 0 + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = 1, + .len = length, + .buf = rxData, + }, + }; + + if (i2c_transfer(this_client->adapter, msgs, 1) < 0) { + rk28printk(KERN_ERR "MMA7660 mma7660_rx_data: transfer error\n"); + return -EIO; + } else + return 0; +#else + int ret; + struct i2c_adapter *adap = this_client->adapter; + struct i2c_msg msgs[2]; + + msgs[0].addr = this_client->addr; + msgs[0].buf = (char *)rxData; + msgs[0].flags = this_client->flags; + msgs[0].len = 1; + msgs[0].scl_rate = 200*1000; + + msgs[1].addr = this_client->addr; + msgs[1].buf = (char *)rxData; + msgs[1].flags = this_client->flags | I2C_M_RD; + msgs[1].len = 3; + msgs[1].scl_rate = 200*1000; + + ret = i2c_transfer(adap, msgs, 2); + + return ret; +#endif +} + +static int mma7660_tx_data(char *txData, int length) +{ +#if 0 + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + if (i2c_transfer(this_client->adapter, msg, 1) < 0) { + rk28printk(KERN_ERR "MMA7660 mma7660_tx_data: transfer error\n"); + return -EIO; + } else + return 0; +#else + int ret; + struct i2c_adapter *adap = this_client->adapter; + struct i2c_msg msg; + + msg.addr = this_client->addr; + msg.buf = txData; + msg.len = length; + msg.flags = this_client->flags; + msg.scl_rate = 200*1000; + + ret = i2c_transfer(adap, &msg, 1); + + return ret; +#endif +} + +static int mma7660_set_rate(char rate) +{ + char buffer[2]; + int ret = 0; + int i; + + if (rate > 128) + return -EINVAL; + + for (i = 0; i < 7; i++) { + if (rate & (0x1 << i)) + break; + } + + buffer[0] = MMA7660_REG_SR; + buffer[1] = 0xf8 | (0x07 & (~i)); + + ret = mma7660_tx_data(&(buffer[0]), 2); + + return ret; +} + +static int mma7660_start_dev(char rate) +{ + char buffer[MMA7660_REG_LEN]; + int ret = 0; + + buffer[0] = MMA7660_REG_INTSU; + buffer[1] = 0x10; //0x10; modify by zhao + ret = mma7660_tx_data(&buffer[0], 2); + + ret = mma7660_set_rate(rate); + + buffer[0] = MMA7660_REG_MODE; + buffer[1] = 0x81; + ret = mma7660_tx_data(&buffer[0], 2); + + rk28printk("\n----------------------------mma7660_start------------------------\n"); + + return ret; +} + +static int mma7660_start(char rate) +{ + struct mma7660_data *mma = (struct mma7660_data *)i2c_get_clientdata(this_client); + + if (mma->status == MMA7660_OPEN) { + return 0; + } + mma->status = MMA7660_OPEN; + return mma7660_start_dev(rate); +} + +static int mma7660_close_dev(void) +{ + char buffer[2]; + + buffer[0] = MMA7660_REG_MODE; + buffer[1] = 0x00; + + return mma7660_tx_data(buffer, 2); +} + +static int mma7660_close(void) +{ + struct mma7660_data *mma = (struct mma7660_data *)i2c_get_clientdata(this_client); + + mma->status = MMA7660_CLOSE; + + return mma7660_close_dev(); +} + +static int mma7660_reset_rate(char rate) +{ + int ret = 0; + + ret = mma7660_close_dev(); + ret = mma7660_start_dev(rate); + + return ret ; +} + +static inline int mma7660_convert_to_int(char value) +{ + int result; + + if (value < MMA7660_BOUNDARY) { + result = value * MMA7660_GRAVITY_STEP; + } else { + result = ~(((~value & 0x3f) + 1)* MMA7660_GRAVITY_STEP) + 1; + } + + return result; +} + +static void mma7660_report_value(short *rbuf) +{ + struct mma7660_data *data = i2c_get_clientdata(this_client); + struct mma7660_axis *axis = (struct mma7660_axis *)rbuf; + + /* Report acceleration sensor information */ + input_report_abs(data->input_dev, ABS_X, axis->x); + input_report_abs(data->input_dev, ABS_Y, axis->y); + input_report_abs(data->input_dev, ABS_Z, axis->z); + input_sync(data->input_dev); + rk28printk("Gsensor x==%d y==%d z==%d\n",axis->x,axis->y,axis->z); +} + +static int mma7660_get_data(void) +{ + char buffer[3]; + int ret; + struct mma7660_axis axis; + + memset(buffer, 0, 3); + buffer[0] = MMA7660_REG_X_OUT; + ret = mma7660_rx_data(&buffer[0], 3); + //while(mma7660_rx_data(&buffer[0], 3)); + /* + if (!ret) { + rk28printk( "%s: -------------------------------------------gsensor device register = [0]:%d [1]:%d [2]:%d [3]:0x%x [4]:0x%x [5]:0x%x [6]:0x%x [7]:0x%x-----------------------------------------------\n", + __func__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]); + } + */ + if (ret < 0) + return ret; + + axis.x = mma7660_convert_to_int(buffer[MMA7660_REG_X_OUT]); + axis.y = -mma7660_convert_to_int(buffer[MMA7660_REG_Y_OUT]); + axis.z = -mma7660_convert_to_int(buffer[MMA7660_REG_Z_OUT]); + + rk28printk( "%s: -------------------------------------------mma7660_GetData axis = %d %d %d-----------------------------------------------\n", + __func__, axis.x, axis.y, axis.z); + + memcpy(sense_data, &axis, sizeof(axis)); + mma7660_report_value(sense_data); + //atomic_set(&data_ready, 0); + //wake_up(&data_ready_wq); + + return 0; +} + +static int mma7660_trans_buff(char *rbuf, int size) +{ + //wait_event_interruptible_timeout(data_ready_wq, + // atomic_read(&data_ready), 1000); + wait_event_interruptible(data_ready_wq, + atomic_read(&data_ready)); + + atomic_set(&data_ready, 0); + memcpy(rbuf, &sense_data[0], size); + + return 0; +} + +static int mma7660_open(struct inode *inode, struct file *file) +{ + rk28printk("----------------------------mma7660_open------------------------\n"); + return 0;//nonseekable_open(inode, file); +} + +static int mma7660_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int mma7660_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + + void __user *argp = (void __user *)arg; + char msg[RBUFF_SIZE + 1]; + int ret = -1; + char rate; + + rk28printk("----------------------------mma7660_ioctl------------------------cmd: %d\n", cmd); + + switch (cmd) { + case ECS_IOCTL_APP_SET_RATE: + if (copy_from_user(&rate, argp, sizeof(rate))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_START: + ret = mma7660_start(MMA7660_RATE_32); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_CLOSE: + ret = mma7660_close(); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_APP_SET_RATE: + ret = mma7660_reset_rate(rate); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_GETDATA: + ret = mma7660_trans_buff(msg, RBUFF_SIZE); + if (ret < 0) + return ret; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_GETDATA: + if (copy_to_user(argp, &msg, sizeof(msg))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static void mma7660_work_func(struct work_struct *work) +{ + if (mma7660_get_data() < 0) + rk28printk(KERN_ERR "MMA7660 mma_work_func: Get data failed\n"); + + //GPIOClrearInmarkIntr(MMA7660_GPIO_INT); + + rk28printk("---------------------------------------mma7660_work_func----------------------------------\n"); +} + +static void mma7660_delaywork_func(struct work_struct *work) +{ + + if (mma7660_get_data() < 0) + rk28printk(KERN_ERR "MMA7660 mma_work_func: Get data failed\n"); + + //GPIOClrearInmarkIntr(MMA7660_GPIO_INT); + + rk28printk("---------------------------------------mma7660_delaywork_func------------------------------\n"); + +} + +static irqreturn_t mma7660_interrupt(int irq, void *dev_id) +{ + struct mma7660_data *data = dev_id; + + ///GPIOInmarkIntr(MMA7660_GPIO_INT); + //schedule_work(&data->work); + schedule_delayed_work(&data->delaywork,msecs_to_jiffies(30)); + rk28printk("--------------------------------------mma7660_interrupt---------------------------------------\n"); + + return IRQ_HANDLED; +} + +static struct file_operations mma7660_fops = { + .owner = THIS_MODULE, + .open = mma7660_open, + .release = mma7660_release, + .ioctl = mma7660_ioctl, +}; + +static struct miscdevice mma7660_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mma7660_daemon", + .fops = &mma7660_fops, +}; + +static int mma7660_remove(struct i2c_client *client) +{ + struct mma7660_data *mma = i2c_get_clientdata(client); + + misc_deregister(&mma7660_device); + input_unregister_device(mma->input_dev); + input_free_device(mma->input_dev); + free_irq(client->irq, mma); + kfree(mma); +#ifdef CONFIG_ANDROID_POWER + android_unregister_early_suspend(&mma7660_early_suspend); +#endif + this_client = NULL; + return 0; +} + +#ifdef CONFIG_ANDROID_POWER +static int mma7660_suspend(android_early_suspend_t *h) +{ + rk28printk("Gsensor mma7760 enter suspend\n"); + return mma7660_close_dev(); +} + +static int mma7660_resume(android_early_suspend_t *h) +{ + struct mma7660_data *mma = (struct mma7660_data *)i2c_get_clientdata(this_client); + rk28printk("Gsensor mma7760 resume!!\n"); + return mma7660_start_dev(mma->curr_tate); +} +/* +static int suspend(struct i2c_client *client, pm_message_t mesg) +{ + rk28printk("Gsensor mma7760 enter 2 level suspend\n"); + return mma7660_close_dev(); +} +static int resume(struct i2c_client *client) +{ + struct mma7660_data *mma = (struct mma7660_data *)i2c_get_clientdata(this_client); + rk28printk("Gsensor mma7760 2 level resume!!\n"); + return mma7660_start_dev(mma->curr_tate); +} +*/ +#else +#define mma7660_suspend NULL +#define mma7660_resume NULL +#endif + +static const struct i2c_device_id mma7660_id[] = { + {"gs_mma7660", 0}, + { } +}; + +static struct i2c_driver mma7660_driver = { + .driver = { + .name = "gs_mma7660", + }, + .id_table = mma7660_id, + .probe = mma7660_probe, + .remove = __devexit_p(mma7660_remove), + //.suspend = &suspend, + //.resume = &resume, +}; + + +static int mma7660_init_client(struct i2c_client *client) +{ + struct mma7660_data *data; + int ret; + data = i2c_get_clientdata(client); + + rk28printk("gpio_to_irq(%d) is %d\n",client->irq,gpio_to_irq(client->irq)); + if ( !gpio_is_valid(client->irq)) { + rk28printk("+++++++++++gpio_is_invalid\n"); + return -EINVAL; + } + ret = gpio_request(client->irq, "mma7660_int"); + if (ret) { + rk28printk( "failed to request mma7990_trig GPIO%d\n",gpio_to_irq(client->irq)); + return ret; + } + + client->irq = gpio_to_irq(client->irq); + ret = request_irq(client->irq, mma7660_interrupt, IRQF_TRIGGER_RISING, client->dev.driver->name, data); + rk28printk("request irq is %d,ret is 0x%x\n",client->irq,ret); + if (ret ) { + rk28printk(KERN_ERR "mma7660_init_client: request irq failed,ret is %d\n",ret); + return ret; + } + init_waitqueue_head(&data_ready_wq); + + return 0; +} + +static int mma7660_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct mma7660_data *mma; + int err; + + mma = kzalloc(sizeof(struct mma7660_data), GFP_KERNEL); + if (!mma) { + rk28printk("[mma7660]:alloc data failed.\n"); + err = -ENOMEM; + goto exit_alloc_data_failed; + } + + INIT_WORK(&mma->work, mma7660_work_func); + INIT_DELAYED_WORK(&mma->delaywork, mma7660_delaywork_func); + + mma->client = client; + i2c_set_clientdata(client, mma); + + this_client = client; + + err = mma7660_init_client(client); + if (err < 0) { + rk28printk(KERN_ERR + "mma7660_probe: mma7660_init_client failed\n"); + goto exit_request_gpio_irq_failed; + } + + mma->input_dev = input_allocate_device(); + if (!mma->input_dev) { + err = -ENOMEM; + rk28printk(KERN_ERR + "mma7660_probe: Failed to allocate input device\n"); + goto exit_input_allocate_device_failed; + } + + set_bit(EV_ABS, mma->input_dev->evbit); + + /* x-axis acceleration */ + input_set_abs_params(mma->input_dev, ABS_X, -1500, 1500, 0, 0); + /* y-axis acceleration */ + input_set_abs_params(mma->input_dev, ABS_Y, -1500, 1500, 0, 0); + /* z-axis acceleration */ + input_set_abs_params(mma->input_dev, ABS_Z, -1500, 1500, 0, 0); + + mma->input_dev->name = "compass"; + + err = input_register_device(mma->input_dev); + if (err < 0) { + rk28printk(KERN_ERR + "mma7660_probe: Unable to register input device: %s\n", + mma->input_dev->name); + goto exit_input_register_device_failed; + } + + err = misc_register(&mma7660_device); + if (err < 0) { + rk28printk(KERN_ERR + "mma7660_probe: mmad_device register failed\n"); + goto exit_misc_device_register_mma7660_device_failed; + } + + err = gsensor_sysfs_init(); + if (err < 0) { + rk28printk(KERN_ERR + "mma7660_probe: gsensor sysfs init failed\n"); + goto exit_gsensor_sysfs_init_failed; + } + +#ifdef CONFIG_ANDROID_POWER + mma7660_early_suspend.suspend = mma7660_suspend; + mma7660_early_suspend.resume = mma7660_resume; + mma7660_early_suspend.level = 0x2; + android_register_early_suspend(&mma7660_early_suspend); +#endif + + return 0; + +exit_gsensor_sysfs_init_failed: + misc_deregister(&mma7660_device); +exit_misc_device_register_mma7660_device_failed: + input_unregister_device(mma->input_dev); +exit_input_register_device_failed: + input_free_device(mma->input_dev); +exit_input_allocate_device_failed: + free_irq(client->irq, mma); +exit_request_gpio_irq_failed: + kfree(mma); +exit_alloc_data_failed: + ; + return err; +} + + +static int __init mma7660_i2c_init(void) +{ + return i2c_add_driver(&mma7660_driver); +} + +static void __exit mma7660_i2c_exit(void) +{ + i2c_del_driver(&mma7660_driver); +} + +module_init(mma7660_i2c_init); +module_exit(mma7660_i2c_exit); + + + diff --git a/include/linux/mma7660.h b/include/linux/mma7660.h new file mode 100644 index 000000000000..8df0414485e9 --- /dev/null +++ b/include/linux/mma7660.h @@ -0,0 +1,92 @@ +/* + * Definitions for mma7660 compass chip. + */ +#ifndef MMA7660_H +#define MMA7660_H + +#include + + + + +/* Default register settings */ +#define RBUFF_SIZE 12 /* Rx buffer size */ + + +#define MMA7660_REG_X_OUT 0x0 +#define MMA7660_REG_Y_OUT 0x1 +#define MMA7660_REG_Z_OUT 0x2 +#define MMA7660_REG_TILT 0x3 +#define MMA7660_REG_SRST 0x4 +#define MMA7660_REG_SPCNT 0x5 +#define MMA7660_REG_INTSU 0x6 +#define MMA7660_REG_MODE 0x7 +#define MMA7660_REG_SR 0x8 +#define MMA7660_REG_PDET 0x9 +#define MMA7660_REG_PD 0xa + + +#define MMAIO 0xA1 + +/* IOCTLs for MMA7660 library */ +#define ECS_IOCTL_INIT _IO(MMAIO, 0x01) +#define ECS_IOCTL_RESET _IO(MMAIO, 0x04) +#define ECS_IOCTL_CLOSE _IO(MMAIO, 0x02) +#define ECS_IOCTL_START _IO(MMAIO, 0x03) +#define ECS_IOCTL_GETDATA _IOR(MMAIO, 0x08, char[RBUFF_SIZE+1]) + +/* IOCTLs for APPs */ +#define ECS_IOCTL_APP_SET_RATE _IOW(MMAIO, 0x10, char) + + +/*rate*/ +#define MMA7660_RATE_1 1 +#define MMA7660_RATE_2 2 +#define MMA7660_RATE_4 4 +#define MMA7660_RATE_8 8 +#define MMA7660_RATE_16 16 +#define MMA7660_RATE_32 32 +#define MMA7660_RATE_64 64 +#define MMA7660_RATE_120 128 + +/*status*/ +#define MMA7660_OPEN 1 +#define MMA7660_CLOSE 0 + + + +#define MMA7660_IIC_ADDR 0x98 +#define MMA7660_REG_LEN 11 +#define MMA7660_GRAVITY_STEP 47 +#define MMA7660_PRECISION 6 +#define MMA7660_BOUNDARY (0x1 << (MMA7660_PRECISION - 1)) +#define MMA7660_TOTAL_TIME 10 + + + +struct mma7660_platform_data { + int reset; + int clk_on; + int intr; +}; + +struct mma7660_data { + char status; + char curr_tate; + struct input_dev *input_dev; + struct i2c_client *client; + struct work_struct work; + struct delayed_work delaywork; /*report second event*/ +}; + +struct mma7660_axis { + int x; + int y; + int z; +}; + +#define GSENSOR_DEV_PATH "/dev/mma7660_daemon" + + +#endif + -- 2.34.1