commit adae85117cb815730bef435b90e014d7fffd77a7 Author: Job Bolle Date: Tue Apr 21 14:21:05 2009 +0200 raph-gsensor -> kionix-kxsd9 - renamed raph-gsensor to kionix-kxsd9 (the actual part name) - added pedometer function & free-fall detection diff --git a/arch/arm/configs/htcraphael_defconfig b/arch/arm/configs/htcraphael_defconfig index c84f2e8..6e41ae0 100644 --- a/arch/arm/configs/htcraphael_defconfig +++ b/arch/arm/configs/htcraphael_defconfig @@ -235,6 +235,7 @@ CONFIG_HTC_FB_CONSOLE=y # CONFIG_HTC_FB_CONSOLE_DELAY is not set CONFIG_HTC_FB_CONSOLE_BOOT=y # CONFIG_WIFI_CONTROL_FUNC is not set +CONFIG_RAPHAEL_GSENSOR=y # # Processor Type diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index a0387e2..de535f9 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -40,6 +40,6 @@ obj-$(CONFIG_TROUT_H2W) += board-trout-h2w.o obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o obj-$(CONFIG_TROUT_PWRSINK) += htc_pwrsink.o -obj-$(CONFIG_RAPHAEL_GSENSOR) += raph-gsensor.o +obj-$(CONFIG_RAPHAEL_GSENSOR) += kionix-kxsd9.o obj-$(CONFIG_HTC_FB_CONSOLE) += htc_fb_console.o diff --git a/arch/arm/mach-msm/board-htcraphael.c b/arch/arm/mach-msm/board-htcraphael.c index abf8ebd..1dc20e2 100644 --- a/arch/arm/mach-msm/board-htcraphael.c +++ b/arch/arm/mach-msm/board-htcraphael.c @@ -187,6 +187,10 @@ static struct i2c_board_info i2c_devices[] = { // Raphael NaviPad I2C_BOARD_INFO("raph_navi_pad", 0x62), }, + { + // Accelerometer + I2C_BOARD_INFO("kionix-kxsd9", 0x18), + }, }; static struct android_pmem_platform_data android_pmem_pdata = { diff --git a/arch/arm/mach-msm/kionix-kxsd9.c b/arch/arm/mach-msm/kionix-kxsd9.c new file mode 100644 index 0000000..0ce04ee --- /dev/null +++ b/arch/arm/mach-msm/kionix-kxsd9.c @@ -0,0 +1,330 @@ +/* kionix-kxsd9.c + * + * G-Sensor found in HTC Raphael (Touch Pro) and HTC Diamond mobile phones + * + * also acts as pedometer and free-fall detector + * reports g-force as x,y,z + * reports step count + + ^ + +y | + ___ +-x | -'| +x +<--| |--> + |___| / -z + |_O_|/ + -y | / + v / +z + + * TODO: calibration, accuracy/sensitivity, report free fall, .. + * + * Job Bolle + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ANDROID_POWER +#include +#endif + +#define MODULE_NAME "kionix-kxsd9" +#define KXSD9_DEBUG 1 +#define KXSD9_DUMP 0 + +struct kxsd9 { + struct i2c_client *client; + struct input_dev *inputdev; + struct hrtimer timer; + struct delayed_work work; + struct mutex lock; +#ifdef CONFIG_ANDROID_POWER + android_suspend_lock_t suspend_lock; +#endif + int on; + struct kxsd9_seq *seq; + int iseq,nseq; + + int pedo_up,pedo_lim; + unsigned short pedo_count; +}; + +struct kxsd9_seq { + int len; + unsigned char *data; +} kxsd9_enable_seq[] = { + { 2, "\x0d""\xc0" }, + { 1, "\x1e" }, + { 2, "\x0a""\xca" }, + { 2, "\x0b""\x60" }, + { 2, "\x0c""\xe3" }, + { 1, "\x00" }, +}, kxsd9_disable_seq[] = { + { 2, "\x0d""\x00" }, +}; + +static ktime_t kxsd9_poll_time = {.tv.nsec = 100 * NSEC_PER_MSEC }; +static ktime_t kxsd9_seq_time = {.tv.nsec = 5 * NSEC_PER_MSEC }; + + +int kxsd9_enable(struct kxsd9 *kxsd9,int on) +{ +#if KXSD9_DEBUG + printk(KERN_INFO MODULE_NAME ": %s" "abling accelerometer\n", + on ? "En" : "Dis"); +#endif + if (!kxsd9->seq && !kxsd9->on) { + hrtimer_start(&kxsd9->timer, kxsd9_seq_time, + HRTIMER_MODE_REL); + } + kxsd9->on = on; + kxsd9->iseq = 0; + if (on) { + kxsd9->nseq = ARRAY_SIZE(kxsd9_enable_seq); + kxsd9->seq = kxsd9_enable_seq; + } else { + kxsd9->nseq = ARRAY_SIZE(kxsd9_disable_seq); + kxsd9->seq = kxsd9_disable_seq; + } + return 0; +} + +static int kxsd9_i2c_read(struct i2c_client *client, unsigned id, + char *buf, int len) +{ + int r; + char outbuffer[2] = { 0, 0 }; + + outbuffer[0] = id; + // maejrep: Have to separate the "ask" and "read" chunks + r = i2c_master_send(client, outbuffer, 1); + if (r < 0) { + printk(KERN_WARNING "%s: error asking for gsensor data at " + "address %02x,%02x: %d\n", + __func__, client->addr, id, r); + return r; + } + mdelay(1); + r = i2c_master_recv(client, buf, len); + if (r < 0) { + printk(KERN_ERR "%s: error reading gsensor data at " + "address %02x,%02x: %d\n", + __func__, client->addr, id, r); + return r; + } + return 0; +} + +static enum hrtimer_restart kxsd9_poll_timer(struct hrtimer *timer) +{ + struct kxsd9 *kxsd9; + + kxsd9 = container_of(timer, struct kxsd9, timer); +#ifdef CONFIG_ANDROID_POWER + android_lock_suspend(&kxsd9->suspend_lock); +#endif + schedule_work(&kxsd9->work.work); + return HRTIMER_NORESTART; +} + +static void kxsd9_work(struct work_struct *work) +{ + struct kxsd9 *kxsd9; + int err; + char buf[6]; + int x,y,z; + unsigned long long gabs; + + kxsd9 = container_of(work, struct kxsd9, work.work); + mutex_lock(&kxsd9->lock); + if (kxsd9->seq) { +#if KXSD9_DEBUG + printk(KERN_INFO MODULE_NAME ": Sequence iseq %d/%d\n", + kxsd9->iseq, kxsd9->nseq); +#endif + err = i2c_master_send(kxsd9->client, + kxsd9->seq[kxsd9->iseq].data, + kxsd9->seq[kxsd9->iseq].len); + if (err < 0) { + printk(KERN_WARNING MODULE_NAME + ": %s: error %d\n", __func__, err); + } + if (++kxsd9->iseq >= kxsd9->nseq) + kxsd9->seq = 0; // sequence finished + hrtimer_start(&kxsd9->timer, kxsd9_seq_time, + HRTIMER_MODE_REL); + } else { + err = kxsd9_i2c_read(kxsd9->client, 0, buf, 6); + x = 0x8000 - 0x100 * buf[2] - buf[3]; + y = 0x8000 - 0x100 * buf[0] - buf[1]; + z = buf[4] * 0x100 + buf[5] - 0x8000 - 1000; // calib? + gabs = x * x + y * y + z * z; + + if (kxsd9->pedo_up) { + if (gabs > kxsd9->pedo_lim) { + kxsd9->pedo_up = 0; + kxsd9->pedo_lim = gabs / 2; + kxsd9->pedo_count++; + input_report_abs(kxsd9->inputdev, ABS_GAS, + kxsd9->pedo_count); + } else if (kxsd9->pedo_lim > gabs * 2) { + kxsd9->pedo_lim = gabs * 2; + } + } else { + if (gabs < kxsd9->pedo_lim) { + kxsd9->pedo_up = 1; + kxsd9->pedo_lim = gabs * 2; + } else if (kxsd9->pedo_lim < gabs / 2) { + kxsd9->pedo_lim = gabs / 2; + } + } +#if KXSD9_DUMP +#if 1 + printk(KERN_INFO "G=(%6d, %6d, %6d) P=%d %s\n", + x, y, z, kxsd9->pedo_count, + gabs < 0x400000 ? "FF" : ""); // free-fall +#else + printk(KERN_INFO "G=( %02X %02X %02X %02X %02X %02X )\n", + buf[0],buf[1],buf[2],buf[3],buf[4],buf[5]); +#endif +#endif + input_report_abs(kxsd9->inputdev, ABS_X, x); + input_report_abs(kxsd9->inputdev, ABS_Y, y); + input_report_abs(kxsd9->inputdev, ABS_Z, z); + input_sync(kxsd9->inputdev); + if (kxsd9->on) + hrtimer_start(&kxsd9->timer, kxsd9_poll_time, + HRTIMER_MODE_REL); +#ifdef CONFIG_ANDROID_POWER + android_unlock_suspend(&kxsd9->suspend_lock); +#endif + } + mutex_unlock(&kxsd9->lock); +} + +static int kxsd9_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct kxsd9 *kxsd9; + struct input_dev *idev; + + printk(KERN_INFO MODULE_NAME ": Initializing Kionix KXSD9 driver " + "at addr: 0x%02x\n", client->addr); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + printk(KERN_ERR MODULE_NAME ": i2c bus not supported\n"); + return -EINVAL; + } + + kxsd9 = kzalloc(sizeof *kxsd9, GFP_KERNEL); + if (kxsd9 < 0) { + printk(KERN_ERR MODULE_NAME ": Not enough memory\n"); + return -ENOMEM; + } + mutex_init(&kxsd9->lock); + kxsd9->client = client; + i2c_set_clientdata(client, kxsd9); + + idev = input_allocate_device(); + if (idev) { + idev->name = MODULE_NAME; + set_bit(EV_ABS, idev->evbit); + input_set_abs_params(idev, ABS_X, -32768, 32767, 0, 0); + input_set_abs_params(idev, ABS_Y, -32768, 32767, 0, 0); + input_set_abs_params(idev, ABS_Z, -32768, 32767, 0, 0); + input_set_abs_params(idev, ABS_GAS, 0, 65535, 0, 0); + if (!input_register_device(idev)) { + kxsd9->inputdev = idev; + } else { + kxsd9->inputdev = 0; + } + } +#ifdef CONFIG_ANDROID_POWER + kxsd9->suspend_lock.name = MODULE_NAME; + android_init_suspend_lock(&kxsd9->suspend_lock); + android_lock_suspend(&kxsd9->suspend_lock); +#endif + INIT_DELAYED_WORK(&kxsd9->work, kxsd9_work); + hrtimer_init(&kxsd9->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + kxsd9->timer.function = kxsd9_poll_timer; + kxsd9_enable(kxsd9,1); + return 0; +} + +static int kxsd9_remove(struct i2c_client * client) +{ + struct kxsd9 *kxsd9 = i2c_get_clientdata(client); + + + input_unregister_device(kxsd9->inputdev); + input_free_device(kxsd9->inputdev); +#ifdef CONFIG_ANDROID_POWER + android_uninit_suspend_lock(&kxsd9->suspend_lock); +#endif + kfree(kxsd9); + return 0; +} + +#if CONFIG_PM +static int kxsd9_suspend(struct i2c_client * client, pm_message_t mesg) +{ +#if KXSD9_DEBUG + printk(KERN_INFO MODULE_NAME ": suspending device...\n"); +#endif + return 0; +} + +static int kxsd9_resume(struct i2c_client * client) +{ +#if KXSD9_DEBUG + printk(KERN_INFO MODULE_NAME ": resuming device...\n"); +#endif + return 0; +} +#else +#define kxsd9_suspend NULL +#define kxsd9_resume NULL +#endif + +static const struct i2c_device_id kxsd9_ids[] = { + { MODULE_NAME, 0 }, + { } +}; + +static struct i2c_driver kxsd9_driver = { + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, + .id_table = kxsd9_ids, + .probe = kxsd9_probe, + .remove = kxsd9_remove, +#if CONFIG_PM + .suspend = kxsd9_suspend, + .resume = kxsd9_resume, +#endif +}; + +static int __init kxsd9_init(void) +{ + printk(KERN_INFO MODULE_NAME ": Registering Kionix KXSD9 driver\n"); + return i2c_add_driver(&kxsd9_driver); +} + +static void __exit kxsd9_exit(void) +{ + printk(KERN_INFO MODULE_NAME ": Unregistered Kionix KXSD9 driver\n"); + i2c_del_driver(&kxsd9_driver); +} + +MODULE_AUTHOR("Job Bolle"); +MODULE_DESCRIPTION("Kionix-KXSD9 Driver"); +MODULE_LICENSE("GPL"); + +module_init(kxsd9_init); +module_exit(kxsd9_exit); + diff --git a/arch/arm/mach-msm/raph-gsensor.c b/arch/arm/mach-msm/raph-gsensor.c deleted file mode 100644 index e5d51dc..0000000 --- a/arch/arm/mach-msm/raph-gsensor.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * raph-gsensor.c - * G-Sensor found in HTC Raphael (Touch Pro) and HTC Diamond mobile phones - * - * Job Bolle - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_ANDROID_POWER -#include -#endif - -#define MODULE_NAME "raph_gsensor" -#define RAPH_GSENS_DEBUG - -struct raph_gsensor { - struct i2c_client *client; - struct input_dev *inputdev; - struct hrtimer timer; - struct delayed_work work; - struct mutex lock; -#ifdef CONFIG_ANDROID_POWER - android_suspend_lock_t suspend_lock; -#endif - int state; - int step; -}; - -enum { - RAPH_GSENS_OFF, - RAPH_GSENS_ENABLE, - RAPH_GSENS_ON, - RAPH_GSENS_DISABLE, -}; - -struct { - int len; - unsigned char *data; -} raph_gsensor_enable_seq[] = { - { 2, "\x0d""\xc0" }, - { 1, "\x1e" }, - { 2, "\x0a""\xca" }, - { 2, "\x0b""\x60" }, - { 2, "\x0c""\xe3" }, - { 1, "\x00" }, -}, raph_gsensor_disable_seq[] = { - { 2, "\x0d""\x00" }, -}; - -static ktime_t raph_gsensor_poll_time = {.tv.nsec = 100 * NSEC_PER_MSEC }; -static ktime_t raph_gsensor_seq_time = {.tv.nsec = 2 * NSEC_PER_MSEC }; - - -int raph_gsensor_enable(struct raph_gsensor *gsens,int on) -{ - if (on) { - if (gsens->state == RAPH_GSENS_OFF - || gsens->state == RAPH_GSENS_DISABLE) { - on = gsens->state == RAPH_GSENS_OFF; - gsens->state = RAPH_GSENS_ENABLE; - gsens->step = 0; - if (on) { - hrtimer_start(&gsens->timer, - raph_gsensor_seq_time, - HRTIMER_MODE_REL); - } - } - } else { - if (gsens->state == RAPH_GSENS_ON - || gsens->state == RAPH_GSENS_ENABLE) { - gsens->state = RAPH_GSENS_DISABLE; - gsens->step = 0; - } - } - return 0; -} - -static int raph_gsensor_i2c_read(struct i2c_client *client, unsigned id, - char *buf, int len) -{ - int r; - char outbuffer[2] = { 0, 0 }; - - outbuffer[0] = id; - // maejrep: Have to separate the "ask" and "read" chunks - r = i2c_master_send(client, outbuffer, 1); - if (r < 0) { - printk(KERN_WARNING "%s: error asking for gsensor data at " - "address %02x,%02x: %d\n", - __func__, client->addr, id, r); - return r; - } - mdelay(1); - r = i2c_master_recv(client, buf, len); - if (r < 0) { - printk(KERN_ERR "%s: error reading gsensor data at " - "address %02x,%02x: %d\n", - __func__, client->addr, id, r); - return r; - } - return 0; -} - -static enum hrtimer_restart raph_gsensor_poll_timer(struct hrtimer *timer) -{ - struct raph_gsensor *gsens; - - gsens = container_of(timer, struct raph_gsensor, timer); -#ifdef CONFIG_ANDROID_POWER - android_lock_suspend(&gsens->suspend_lock); -#endif - schedule_work(&gsens->work.work); - return HRTIMER_NORESTART; -} - -static void raph_gsensor_work(struct work_struct *work) -{ - struct raph_gsensor *gsens; - int err; - char buf[6]; - int x,y,z; - - gsens = container_of(work, struct raph_gsensor, work.work); - mutex_lock(&gsens->lock); - switch (gsens->state) { - case RAPH_GSENS_ENABLE: - err = i2c_master_send(gsens->client, - raph_gsensor_enable_seq[gsens->step].data, - raph_gsensor_enable_seq[gsens->step].len); - if (err < 0) - printk(KERN_WARNING MODULE_NAME - ": %s: Enable %d error %d\n", - __func__, gsens->step, err); - if (++gsens->step >= ARRAY_SIZE(raph_gsensor_enable_seq)) - gsens->state = RAPH_GSENS_ON; - hrtimer_start(&gsens->timer, raph_gsensor_seq_time, - HRTIMER_MODE_REL); - break; - - case RAPH_GSENS_DISABLE: - err = i2c_master_send(gsens->client, - raph_gsensor_disable_seq[gsens->step].data, - raph_gsensor_disable_seq[gsens->step].len); - if (err < 0) - printk(KERN_WARNING MODULE_NAME - ": %s: Disable %d error %d\n", - __func__, gsens->step, err); - if (++gsens->step >= ARRAY_SIZE(raph_gsensor_disable_seq)) - gsens->state = RAPH_GSENS_OFF; - hrtimer_start(&gsens->timer, raph_gsensor_seq_time, - HRTIMER_MODE_REL); - break; - - case RAPH_GSENS_ON: - err = raph_gsensor_i2c_read(gsens->client, 0, buf, 6); - x = buf[0] * 0x100 + buf[1] - 0x8000; - y = buf[2] * 0x100 + buf[3] - 0x8000; - z = buf[4] * 0x100 + buf[5] - 0x8000; -#ifdef RAPH_GSENS_DEBUG - printk(KERN_INFO "G=( %6d , %6d , %6d )\n", - x, y, z); -#endif - input_report_abs(gsens->inputdev, ABS_X, x); - input_report_abs(gsens->inputdev, ABS_Y, y); - input_report_abs(gsens->inputdev, ABS_Z, z); - input_sync(gsens->inputdev); - hrtimer_start(&gsens->timer, raph_gsensor_poll_time, - HRTIMER_MODE_REL); - /* don't break */ - case RAPH_GSENS_OFF: -#ifdef CONFIG_ANDROID_POWER - android_unlock_suspend(&gsens->suspend_lock); -#endif - break; - } - mutex_unlock(&gsens->lock); -} - -static int raph_gsensor_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct raph_gsensor *gsens; - struct input_dev *idev; - - printk(KERN_INFO MODULE_NAME ": Initializing Raphael G-Sensor driver " - "at addr: 0x%02x\n", client->addr); - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - printk(KERN_ERR MODULE_NAME ": i2c bus not supported\n"); - return -EINVAL; - } - - gsens = kzalloc(sizeof *gsens, GFP_KERNEL); - if (gsens < 0) { - printk(KERN_ERR MODULE_NAME ": Not enough memory\n"); - return -ENOMEM; - } - mutex_init(&gsens->lock); - gsens->client = client; - i2c_set_clientdata(client, gsens); - - idev = input_allocate_device(); - if (idev) { - idev->name = MODULE_NAME; - set_bit(EV_ABS, idev->evbit); - input_set_abs_params(idev, ABS_X, -32768, 32767, 0, 0); - input_set_abs_params(idev, ABS_Y, -32768, 32767, 0, 0); - input_set_abs_params(idev, ABS_Z, -32768, 32767, 0, 0); - if (!input_register_device(idev)) { - gsens->inputdev = idev; - } else { - gsens->inputdev = 0; - } - } -#ifdef CONFIG_ANDROID_POWER - gsens->suspend_lock.name = "raph_gsensor"; - android_init_suspend_lock(&gsens->suspend_lock); - android_lock_suspend(&gsens->suspend_lock); -#endif - INIT_DELAYED_WORK(&gsens->work, raph_gsensor_work); - hrtimer_init(&gsens->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - gsens->timer.function = raph_gsensor_poll_timer; - - gsens->state = RAPH_GSENS_OFF; - raph_gsensor_enable(gsens,1); - return 0; -} - -static int raph_gsensor_remove(struct i2c_client * client) -{ - struct raph_gsensor *gsens = i2c_get_clientdata(client); - - - input_unregister_device(gsens->inputdev); - input_free_device(gsens->inputdev); -#ifdef CONFIG_ANDROID_POWER - android_uninit_suspend_lock(&gsens->suspend_lock); -#endif - kfree(gsens); - return 0; -} - -#if CONFIG_PM -static int raph_gsensor_suspend(struct i2c_client * client, pm_message_t mesg) -{ -#ifdef RAPH_GSENS_DEBUG - printk(KERN_INFO MODULE_NAME ": suspending device...\n"); -#endif - return 0; -} - -static int raph_gsensor_resume(struct i2c_client * client) -{ -#ifdef RAPH_GSENS_DEBUG - printk(KERN_INFO MODULE_NAME ": resuming device...\n"); -#endif - return 0; -} -#else -#define raph_gsensor_suspend NULL -#define raph_gsensor_resume NULL -#endif - -static const struct i2c_device_id raph_gsensor_ids[] = { - { "raph_gsensor", 0 }, - { } -}; - -static struct i2c_driver raph_gsensor_driver = { - .driver = { - .name = MODULE_NAME, - .owner = THIS_MODULE, - }, - .id_table = raph_gsensor_ids, - .probe = raph_gsensor_probe, - .remove = raph_gsensor_remove, -#if CONFIG_PM - .suspend = raph_gsensor_suspend, - .resume = raph_gsensor_resume, -#endif -}; - -static int __init raph_gsensor_init(void) -{ - printk(KERN_INFO MODULE_NAME ": Registering Raphael G-Sensor driver\n"); - return i2c_add_driver(&raph_gsensor_driver); -} - -static void __exit raph_gsensor_exit(void) -{ - printk(KERN_INFO MODULE_NAME ": Unregistered Raphael G-Sensor driver\n"); - i2c_del_driver(&raph_gsensor_driver); -} - -module_init(raph_gsensor_init); -module_exit(raph_gsensor_exit); -