From: Leo Yan <[email protected]> Correctly setup the GPIO pin for lis3dh and pass right configuration data through platform data. Device can report single click/tap event on X, Y, or Z axises (by BTN_X,Y,Z events) if tapped on one of the axises.
Signed-off-by: Leo Yan <[email protected]> Signed-off-by: Hong Liu <[email protected]> --- arch/x86/kernel/mrst.c | 81 +++++++++++++++++++++++++++++++++++++++++ drivers/hwmon/lis3lv02d.c | 78 ++++++++++++++++++++++++++++++++++++---- drivers/hwmon/lis3lv02d.h | 16 ++++++++ drivers/hwmon/lis3lv02d_i2c.c | 10 +++-- include/linux/lis3lv02d.h | 8 +++- 5 files changed, 180 insertions(+), 13 deletions(-) diff --git a/arch/x86/kernel/mrst.c b/arch/x86/kernel/mrst.c index dce79e2..73e0206 100644 --- a/arch/x86/kernel/mrst.c +++ b/arch/x86/kernel/mrst.c @@ -725,6 +725,86 @@ static void *lis331dl_platform_data(void *info) return &intr2nd_pdata; } +static int lis3dh_setup_gpio_irq(int gpio, const char *label) +{ + int ret; + + ret = gpio_request(gpio, label); + if (ret < 0) { + pr_err("GPIO pin %d request failed, error %d\n", gpio, ret); + return ret; + } + + ret = gpio_direction_input(gpio); + if (ret < 0) { + pr_err("GPIO pin %d direction configuration failed, error %d\n", + gpio, ret); + goto error; + } + + ret = gpio_to_irq(gpio); + if (ret < 0) { + pr_err("GPIO pin %d to IRQ failed, error %d\n", gpio, ret); + goto error; + } + pr_info("Got IRQ %d for GPIO pin %d\n", ret, gpio); + return ret; +error: + gpio_free(gpio); + return ret; +} + +static int lis3dh_release_resources(void *p) +{ + struct lis3lv02d_platform_data *pdata = p; + + gpio_free(pdata->gpio1); + gpio_free(pdata->gpio2); + return 0; +} + +static int lis3dh_setup_resources(void *p) +{ + int ret; + struct lis3lv02d_platform_data *pdata = p; + + ret = lis3dh_setup_gpio_irq(pdata->gpio1, "lis3lv02d_int1"); + if (ret < 0) + return ret; + pdata->irq1 = ret; + + ret = lis3dh_setup_gpio_irq(pdata->gpio2, "lis3lv02d_int2"); + if (ret < 0) { + lis3dh_release_resources(pdata); + return ret; + } + pdata->irq2 = ret; + return 0; +} + +static void *lis3dh_platform_data(void *info) +{ + static struct lis3lv02d_platform_data lis3dh_pdata = { + .click_flags = LIS3_CLICK_SINGLE_X | LIS3_CLICK_SINGLE_Y | + LIS3_CLICK_SINGLE_Z, + .click_time_limit = 50, /* 50ms */ + .click_latency = 5, /* 5ms */ + .click_window = 100, /* 100ms */ + .click_thresh_x = 0x10, /* 0.25G: 0.25G/(2G/128) = 0x10 */ + .irq_cfg = LIS3_16B_IRQ1_CLICK, /* CLICK interrupt on INT1 */ + .setup_resources = lis3dh_setup_resources, + .release_resources = lis3dh_release_resources, + }; + + lis3dh_pdata.gpio1 = get_gpio_by_name("accel_int"); + lis3dh_pdata.gpio2 = get_gpio_by_name("accel_2"); + + if (lis3dh_pdata.gpio1 < 0 || lis3dh_pdata.gpio2 < 0) + return NULL; + + return &lis3dh_pdata; +} + #define BH1770GLC_GPIO_INT "bh1770glc_int" void *bh1770glc_platform_data_init(void *info) @@ -1047,6 +1127,7 @@ static const struct devs_id device_ids[] = { {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, {"i2c_thermal", SFI_DEV_TYPE_I2C, 0, &emc1403_platform_data}, {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data}, + {"lis3lv02d", SFI_DEV_TYPE_I2C, 0, &lis3dh_platform_data}, {"cy8ctmg110", SFI_DEV_TYPE_I2C, 0, &cy8ctmg110_platform_data}, {"aava-max3107", SFI_DEV_TYPE_SPI, 1, &no_platform_data}, {"pmic_audio", SFI_DEV_TYPE_SPI, 1, &no_platform_data}, diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 62f91a9..314e7b9 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -159,6 +159,23 @@ static int lis3lv02d_get_odr(void) return lis3_dev.odrs[(ctrl >> shift)]; } +static void lis3lv02d_16b_set_click_time(struct lis3lv02d *dev, + struct lis3lv02d_platform_data *p) +{ + int time; + int odr = lis3lv02d_get_odr(); + + /* 1 LSB equals to 1000/ODR ms */ + time = (p->click_time_limit * odr) / 1000; + dev->write(dev, CLICK_TIME_LIMIT, time < 0x7f ? time : 0x7f); + + time = (p->click_latency * odr) / 1000; + dev->write(dev, CLICK_TIME_LATENCY, time < 0xff ? time : 0xff); + + time = (p->click_window * odr) / 1000; + dev->write(dev, CLICK_TIME_WINDOW, time < 0xff ? time : 0xff); +} + static int lis3lv02d_set_odr(int rate) { u8 ctrl; @@ -173,6 +190,9 @@ static int lis3lv02d_set_odr(int rate) if (lis3_dev.odrs[i] == rate) { lis3_dev.write(&lis3_dev, CTRL_REG1, ctrl | (i << shift)); + if (lis3_dev.whoami == WAI_16B) + lis3lv02d_16b_set_click_time(&lis3_dev, + lis3_dev.pdata); return 0; } return -EINVAL; @@ -272,31 +292,42 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy) wake_up_interruptible(&lis3_dev.misc_wait); kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); out: - if (lis3_dev.whoami == WAI_8B && lis3_dev.idev && - lis3_dev.idev->input->users) + if ((lis3_dev.whoami == WAI_8B || lis3_dev.whoami == WAI_16B) && + lis3_dev.idev && lis3_dev.idev->input->users) return IRQ_WAKE_THREAD; return IRQ_HANDLED; } static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3) { + int x_mask, y_mask, z_mask; struct input_dev *dev = lis3->idev->input; u8 click_src; + if (lis3->whoami == WAI_16B) { + x_mask = CLICK_16B_X; + y_mask = CLICK_16B_Y; + z_mask = CLICK_16B_Z; + } else { + x_mask = CLICK_SINGLE_X; + y_mask = CLICK_SINGLE_Y; + z_mask = CLICK_SINGLE_Z; + } + mutex_lock(&lis3->mutex); lis3->read(lis3, CLICK_SRC, &click_src); - if (click_src & CLICK_SINGLE_X) { + if (click_src & x_mask) { input_report_key(dev, lis3->mapped_btns[0], 1); input_report_key(dev, lis3->mapped_btns[0], 0); } - if (click_src & CLICK_SINGLE_Y) { + if (click_src & y_mask) { input_report_key(dev, lis3->mapped_btns[1], 1); input_report_key(dev, lis3->mapped_btns[1], 0); } - if (click_src & CLICK_SINGLE_Z) { + if (click_src & z_mask) { input_report_key(dev, lis3->mapped_btns[2], 1); input_report_key(dev, lis3->mapped_btns[2], 0); } @@ -348,6 +379,18 @@ static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t lis3lv02d_16b_interrupt_thread(int irq, void *data) +{ + struct lis3lv02d *lis3 = data; + + if (lis3->pdata && + (lis3->pdata->irq_cfg & LIS3_16B_IRQ1_CLICK)) { + lis302dl_interrupt_handle_click(lis3); + } + + return IRQ_HANDLED; +} + static int lis3lv02d_misc_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &lis3_dev.misc_opened)) @@ -462,7 +505,6 @@ static void joystick_close(struct input_polled_dev *dev) pm_runtime_put(lis3_dev.dev); } - int lis3lv02d_joystick_enable(void) { struct input_dev *input_dev; @@ -598,7 +640,6 @@ static struct attribute_group lis3lv02d_attribute_group = { .attrs = lis3lv02d_attributes }; - static int lis3lv02d_add_fs(struct lis3lv02d *lis3) { lis3->pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); @@ -671,6 +712,25 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev, } } + +static void lis3lv02d_16b_configure(struct lis3lv02d *dev, + struct lis3lv02d_platform_data *p) +{ + if (p->click_flags) { + dev->write(dev, CLICK_CFG, p->click_flags); + dev->write(dev, CLICK_THS, p->click_thresh_x); + lis3lv02d_16b_set_click_time(dev, p); + if (dev->idev) { + struct input_dev *input_dev = lis3_dev.idev->input; + input_set_capability(input_dev, EV_KEY, BTN_X); + input_set_capability(input_dev, EV_KEY, BTN_Y); + input_set_capability(input_dev, EV_KEY, BTN_Z); + } + } + /* setup INT1_CFG register, */ + dev->write(dev, INT1_CFG, INT1_ZH | INT1_YH | INT1_XH); +} + static int lis3lv02d_init_8(struct lis3lv02d *lis3) { u8 reg; @@ -787,6 +847,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) if (dev->whoami == WAI_8B) lis3lv02d_8b_configure(dev, p); + else if (dev->whoami == WAI_16B) + lis3lv02d_16b_configure(dev, p); if (p->irq_cfg) dev->write(dev, CTRL_REG3, p->irq_cfg); @@ -812,6 +874,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) */ if (dev->whoami == WAI_8B) thread_fn = lis302dl_interrupt_thread1_8b; + else if (dev->whoami == WAI_16B) + thread_fn = lis3lv02d_16b_interrupt_thread; else thread_fn = NULL; diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 19d7723..fd835e1 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -98,6 +98,22 @@ enum lis3dh_reg { CTRL_REG4 = 0x23, CTRL4_BDU = 0x80, CTRL4_HR = 0x08, + INT1_CFG = 0x30, + INT1_ZH = (1 << 5), + INT1_YH = (1 << 3), + INT1_XH = (1 << 1), + INT1_SRC = 0x31, + INT1_THS = 0x32, + INT1_DURATION = 0x33, + /* CLICK_SRC = 0x39, */ + CLICK_16B_X = (1 << 0), + CLICK_16B_Y = (1 << 1), + CLICK_16B_Z = (1 << 2), + CLICK_16B_IA = (1 << 6), + CLICK_THS = 0x3A, + CLICK_TIME_LIMIT = 0x3B, + CLICK_TIME_LATENCY = 0x3C, + CLICK_TIME_WINDOW = 0x3D, }; enum lis3_who_am_i { diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c index f086cd8..59ef714 100644 --- a/drivers/hwmon/lis3lv02d_i2c.c +++ b/drivers/hwmon/lis3lv02d_i2c.c @@ -68,8 +68,11 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, if (pdata->axis_z) lis3lv02d_axis_map.z = pdata->axis_z; - if (pdata->setup_resources) - ret = pdata->setup_resources(); + if (pdata->setup_resources) { + ret = pdata->setup_resources(pdata); + if (pdata->irq1 > 0) + client->irq = pdata->irq1; + } if (ret) goto fail; @@ -85,7 +88,6 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, &lis3_dev); - ret = lis3lv02d_init_device(&lis3_dev); if (!ret) { pm_runtime_enable(&client->dev); @@ -104,7 +106,7 @@ static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) struct lis3lv02d_platform_data *pdata = client->dev.platform_data; if (pdata && pdata->release_resources) - pdata->release_resources(); + pdata->release_resources(pdata); lis3lv02d_joystick_disable(); lis3lv02d_poweroff(lis3); diff --git a/include/linux/lis3lv02d.h b/include/linux/lis3lv02d.h index 0e8a346..98bddaa 100644 --- a/include/linux/lis3lv02d.h +++ b/include/linux/lis3lv02d.h @@ -35,6 +35,7 @@ struct lis3lv02d_platform_data { #define LIS3_IRQ2_MASK (7 << 3) #define LIS3_IRQ_OPEN_DRAIN (1 << 6) #define LIS3_IRQ_ACTIVE_LOW (1 << 7) +#define LIS3_16B_IRQ1_CLICK (1 << 7) unsigned char irq_cfg; #define LIS3_WAKEUP_X_LO (1 << 0) @@ -64,11 +65,14 @@ struct lis3lv02d_platform_data { s8 axis_x; s8 axis_y; s8 axis_z; - int (*setup_resources)(void); - int (*release_resources)(void); + int (*setup_resources)(void *); + int (*release_resources)(void *); /* Limits for selftest are specified in chip data sheet */ s16 st_min_limits[3]; /* min pass limit x, y, z */ s16 st_max_limits[3]; /* max pass limit x, y, z */ + int gpio1; + int gpio2; + int irq1; int irq2; }; -- 1.7.2.3 _______________________________________________ MeeGo-kernel mailing list [email protected] http://lists.meego.com/listinfo/meego-kernel
