diff options
Diffstat (limited to 'drivers')
80 files changed, 4421 insertions, 421 deletions
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index feae88b53fcd..c7710b5c69af 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -524,6 +524,12 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS), + .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index e39dac68063c..36668d1aca8f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1547,6 +1547,9 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, @@ -2189,6 +2192,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c5aea29f164f..ffe4c7ae3340 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -135,6 +135,9 @@ #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b #define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index f0f8928b3c8a..d2b34fbbc42e 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -52,6 +52,82 @@ struct evdev_client { struct input_event buffer[]; }; +/* flush queued events of type @type, caller must hold client->buffer_lock */ +static void __evdev_flush_queue(struct evdev_client *client, unsigned int type) +{ + unsigned int i, head, num; + unsigned int mask = client->bufsize - 1; + bool is_report; + struct input_event *ev; + + BUG_ON(type == EV_SYN); + + head = client->tail; + client->packet_head = client->tail; + + /* init to 1 so a leading SYN_REPORT will not be dropped */ + num = 1; + + for (i = client->tail; i != client->head; i = (i + 1) & mask) { + ev = &client->buffer[i]; + is_report = ev->type == EV_SYN && ev->code == SYN_REPORT; + + if (ev->type == type) { + /* drop matched entry */ + continue; + } else if (is_report && !num) { + /* drop empty SYN_REPORT groups */ + continue; + } else if (head != i) { + /* move entry to fill the gap */ + client->buffer[head].time = ev->time; + client->buffer[head].type = ev->type; + client->buffer[head].code = ev->code; + client->buffer[head].value = ev->value; + } + + num++; + head = (head + 1) & mask; + + if (is_report) { + num = 0; + client->packet_head = head; + } + } + + client->head = head; +} + +/* queue SYN_DROPPED event */ +static void evdev_queue_syn_dropped(struct evdev_client *client) +{ + unsigned long flags; + struct input_event ev; + ktime_t time; + + time = ktime_get(); + if (client->clkid != CLOCK_MONOTONIC) + time = ktime_sub(time, ktime_get_monotonic_offset()); + + ev.time = ktime_to_timeval(time); + ev.type = EV_SYN; + ev.code = SYN_DROPPED; + ev.value = 0; + + spin_lock_irqsave(&client->buffer_lock, flags); + + client->buffer[client->head++] = ev; + client->head &= client->bufsize - 1; + + if (unlikely(client->head == client->tail)) { + /* drop queue but keep our SYN_DROPPED event */ + client->tail = (client->head - 1) & (client->bufsize - 1); + client->packet_head = client->tail; + } + + spin_unlock_irqrestore(&client->buffer_lock, flags); +} + static void __pass_event(struct evdev_client *client, const struct input_event *event) { @@ -650,6 +726,51 @@ static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) return input_set_keycode(dev, &ke); } +/* + * If we transfer state to the user, we should flush all pending events + * of the same type from the client's queue. Otherwise, they might end up + * with duplicate events, which can screw up client's state tracking. + * If bits_to_user fails after flushing the queue, we queue a SYN_DROPPED + * event so user-space will notice missing events. + * + * LOCKING: + * We need to take event_lock before buffer_lock to avoid dead-locks. But we + * need the even_lock only to guarantee consistent state. We can safely release + * it while flushing the queue. This allows input-core to handle filters while + * we flush the queue. + */ +static int evdev_handle_get_val(struct evdev_client *client, + struct input_dev *dev, unsigned int type, + unsigned long *bits, unsigned int max, + unsigned int size, void __user *p, int compat) +{ + int ret; + unsigned long *mem; + + mem = kmalloc(sizeof(unsigned long) * max, GFP_KERNEL); + if (!mem) + return -ENOMEM; + + spin_lock_irq(&dev->event_lock); + spin_lock(&client->buffer_lock); + + memcpy(mem, bits, sizeof(unsigned long) * max); + + spin_unlock(&dev->event_lock); + + __evdev_flush_queue(client, type); + + spin_unlock_irq(&client->buffer_lock); + + ret = bits_to_user(mem, max, size, p, compat); + if (ret < 0) + evdev_queue_syn_dropped(client); + + kfree(mem); + + return ret; +} + static int evdev_handle_mt_request(struct input_dev *dev, unsigned int size, int __user *ip) @@ -771,16 +892,20 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return evdev_handle_mt_request(dev, size, ip); case EVIOCGKEY(0): - return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode); + return evdev_handle_get_val(client, dev, EV_KEY, dev->key, + KEY_MAX, size, p, compat_mode); case EVIOCGLED(0): - return bits_to_user(dev->led, LED_MAX, size, p, compat_mode); + return evdev_handle_get_val(client, dev, EV_LED, dev->led, + LED_MAX, size, p, compat_mode); case EVIOCGSND(0): - return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode); + return evdev_handle_get_val(client, dev, EV_SND, dev->snd, + SND_MAX, size, p, compat_mode); case EVIOCGSW(0): - return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode); + return evdev_handle_get_val(client, dev, EV_SW, dev->sw, + SW_MAX, size, p, compat_mode); case EVIOCGNAME(0): return str_to_user(dev->name, size, p); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 7ac9c9818d55..269d4c3658cb 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -418,6 +418,16 @@ config KEYBOARD_NOMADIK To compile this driver as a module, choose M here: the module will be called nmk-ske-keypad. +config KEYBOARD_NSPIRE + tristate "TI-NSPIRE built-in keyboard" + depends on ARCH_NSPIRE && OF + select INPUT_MATRIXKMAP + help + Say Y here if you want to use the built-in keypad on TI-NSPIRE. + + To compile this driver as a module, choose M here: the + module will be called nspire-keypad. + config KEYBOARD_TEGRA tristate "NVIDIA Tegra internal matrix keyboard controller support" depends on ARCH_TEGRA && OF @@ -442,6 +452,7 @@ config KEYBOARD_OPENCORES config KEYBOARD_PXA27x tristate "PXA27x/PXA3xx keypad support" depends on PXA27x || PXA3xx || ARCH_MMP + select INPUT_MATRIXKMAP help Enable support for PXA27x/PXA3xx keypad controller. diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 0c43e8cf8d0e..a699b6172303 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o +obj-$(CONFIG_KEYBOARD_NSPIRE) += nspire-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c index ba0b36f7daea..096d6067ae1f 100644 --- a/drivers/input/keyboard/amikbd.c +++ b/drivers/input/keyboard/amikbd.c @@ -246,7 +246,6 @@ static int __exit amikbd_remove(struct platform_device *pdev) { struct input_dev *dev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); free_irq(IRQ_AMIGA_CIAA_SP, dev); input_unregister_device(dev); return 0; diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index 20b9fa91fb9e..fc88fb48d70d 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -326,7 +326,6 @@ out0: kfree(bf54x_kpad->keycode); out: kfree(bf54x_kpad); - platform_set_drvdata(pdev, NULL); return error; } @@ -346,7 +345,6 @@ static int bfin_kpad_remove(struct platform_device *pdev) kfree(bf54x_kpad->keycode); kfree(bf54x_kpad); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c index 829753702b62..d15977a8361e 100644 --- a/drivers/input/keyboard/davinci_keyscan.c +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -314,8 +314,6 @@ static int davinci_ks_remove(struct platform_device *pdev) iounmap(davinci_ks->base); release_mem_region(davinci_ks->pbase, davinci_ks->base_size); - platform_set_drvdata(pdev, NULL); - kfree(davinci_ks); return 0; diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index 9857e8fd0987..47206bdba411 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -329,8 +329,7 @@ static int ep93xx_keypad_probe(struct platform_device *pdev) return 0; failed_free_irq: - free_irq(keypad->irq, pdev); - platform_set_drvdata(pdev, NULL); + free_irq(keypad->irq, keypad); failed_free_dev: input_free_device(input_dev); failed_put_clk: @@ -351,9 +350,7 @@ static int ep93xx_keypad_remove(struct platform_device *pdev) struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); struct resource *res; - free_irq(keypad->irq, pdev); - - platform_set_drvdata(pdev, NULL); + free_irq(keypad->irq, keypad); if (keypad->enabled) clk_disable(keypad->clk); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index b29ca651a395..440ce32462ba 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -767,7 +767,6 @@ static int gpio_keys_probe(struct platform_device *pdev) while (--i >= 0) gpio_remove_key(&ddata->data[i]); - platform_set_drvdata(pdev, NULL); fail1: input_free_device(input); kfree(ddata); diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 21147164874d..cd5ed9e22168 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -324,7 +324,6 @@ err_free_gpio: err_free_bdev: kfree(bdev); - platform_set_drvdata(pdev, NULL); err_free_pdata: /* If we have no platform_data, we allocated pdata dynamically. */ @@ -355,7 +354,6 @@ static int gpio_keys_polled_remove(struct platform_device *pdev) kfree(pdata); kfree(bdev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c index 74e75a6e8deb..a2a034c25f0b 100644 --- a/drivers/input/keyboard/jornada680_kbd.c +++ b/drivers/input/keyboard/jornada680_kbd.c @@ -233,7 +233,6 @@ static int jornada680kbd_probe(struct platform_device *pdev) failed: printk(KERN_ERR "Jornadakbd: failed to register driver, error: %d\n", error); - platform_set_drvdata(pdev, NULL); input_free_polled_device(poll_dev); kfree(jornadakbd); return error; @@ -244,7 +243,6 @@ static int jornada680kbd_remove(struct platform_device *pdev) { struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); input_unregister_polled_device(jornadakbd->poll_dev); input_free_polled_device(jornadakbd->poll_dev); kfree(jornadakbd); diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c index 5ceef636df2f..b0ad457ca9d8 100644 --- a/drivers/input/keyboard/jornada720_kbd.c +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -146,7 +146,6 @@ static int jornada720_kbd_probe(struct platform_device *pdev) fail2: /* IRQ, DEVICE, MEMORY */ free_irq(IRQ_GPIO0, pdev); fail1: /* DEVICE, MEMORY */ - platform_set_drvdata(pdev, NULL); input_free_device(input_dev); kfree(jornadakbd); return err; @@ -157,7 +156,6 @@ static int jornada720_kbd_remove(struct platform_device *pdev) struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); free_irq(IRQ_GPIO0, pdev); - platform_set_drvdata(pdev, NULL); input_unregister_device(jornadakbd->input); kfree(jornadakbd); diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 71d77192ac1e..90ff73ace424 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -549,8 +549,6 @@ static int matrix_keypad_remove(struct platform_device *pdev) input_unregister_device(keypad->input_dev); kfree(keypad); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c new file mode 100644 index 000000000000..e0a1339e40e6 --- /dev/null +++ b/drivers/input/keyboard/nspire-keypad.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au> + * + * 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. + */ + +#include <linux/input/matrix_keypad.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> + +#define KEYPAD_SCAN_MODE 0x00 +#define KEYPAD_CNTL 0x04 +#define KEYPAD_INT 0x08 +#define KEYPAD_INTMSK 0x0C + +#define KEYPAD_DATA 0x10 +#define KEYPAD_GPIO 0x30 + +#define KEYPAD_UNKNOWN_INT 0x40 +#define KEYPAD_UNKNOWN_INT_STS 0x44 + +#define KEYPAD_BITMASK_COLS 11 +#define KEYPAD_BITMASK_ROWS 8 + +struct nspire_keypad { + void __iomem *reg_base; + u32 int_mask; + + struct input_dev *input; + struct clk *clk; + + struct matrix_keymap_data *keymap; + int row_shift; + + /* Maximum delay estimated assuming 33MHz APB */ + u32 scan_interval; /* In microseconds (~2000us max) */ + u32 row_delay; /* In microseconds (~500us max) */ + + u16 state[KEYPAD_BITMASK_ROWS]; + + bool active_low; +}; + +static irqreturn_t nspire_keypad_irq(int irq, void *dev_id) +{ + struct nspire_keypad *keypad = dev_id; + struct input_dev *input = keypad->input; + unsigned short *keymap = input->keycode; + unsigned int code; + int row, col; + u32 int_sts; + u16 state[8]; + u16 bits, changed; + + int_sts = readl(keypad->reg_base + KEYPAD_INT) & keypad->int_mask; + if (!int_sts) + return IRQ_NONE; + + memcpy_fromio(state, keypad->reg_base + KEYPAD_DATA, sizeof(state)); + + for (row = 0; row < KEYPAD_BITMASK_ROWS; row++) { + bits = state[row]; + if (keypad->active_low) + bits = ~bits; + + changed = bits ^ keypad->state[row]; + if (!changed) + continue; + + keypad->state[row] = bits; + + for (col = 0; col < KEYPAD_BITMASK_COLS; col++) { + if (!(changed & (1U << col))) + continue; + + code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keymap[code], + bits & (1U << col)); + } + } + + input_sync(input); + + writel(0x3, keypad->reg_base + KEYPAD_INT); + + return IRQ_HANDLED; +} + +static int nspire_keypad_chip_init(struct nspire_keypad *keypad) +{ + unsigned long val = 0, cycles_per_us, delay_cycles, row_delay_cycles; + + cycles_per_us = (clk_get_rate(keypad->clk) / 1000000); + if (cycles_per_us == 0) + cycles_per_us = 1; + + delay_cycles = cycles_per_us * keypad->scan_interval; + WARN_ON(delay_cycles >= (1 << 16)); /* Overflow */ + delay_cycles &= 0xffff; + + row_delay_cycles = cycles_per_us * keypad->row_delay; + WARN_ON(row_delay_cycles >= (1 << 14)); /* Overflow */ + row_delay_cycles &= 0x3fff; + + val |= 3 << 0; /* Set scan mode to 3 (continuous scan) */ + val |= row_delay_cycles << 2; /* Delay between scanning each row */ + val |= delay_cycles << 16; /* Delay between scans */ + writel(val, keypad->reg_base + KEYPAD_SCAN_MODE); + + val = (KEYPAD_BITMASK_ROWS & 0xff) | (KEYPAD_BITMASK_COLS & 0xff)<<8; + writel(val, keypad->reg_base + KEYPAD_CNTL); + + /* Enable interrupts */ + keypad->int_mask = 1 << 1; + writel(keypad->int_mask, keypad->reg_base + 0xc); + + /* Disable GPIO interrupts to prevent hanging on touchpad */ + /* Possibly used to detect touchpad events */ + writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT); + /* Acknowledge existing interrupts */ + writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS); + + return 0; +} + +static int nspire_keypad_open(struct input_dev *input) +{ + struct nspire_keypad *keypad = input_get_drvdata(input); + int error; + + error = clk_prepare_enable(keypad->clk); + if (error) + return error; + + error = nspire_keypad_chip_init(keypad); + if (error) + return error; + + return 0; +} + +static void nspire_keypad_close(struct input_dev *input) +{ + struct nspire_keypad *keypad = input_get_drvdata(input); + + clk_disable_unprepare(keypad->clk); +} + +static int nspire_keypad_probe(struct platform_device *pdev) +{ + const struct device_node *of_node = pdev->dev.of_node; + struct nspire_keypad *keypad; + struct input_dev *input; + struct resource *res; + int irq; + int error; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing platform resources\n"); + return -EINVAL; + } + + keypad = devm_kzalloc(&pdev->dev, sizeof(struct nspire_keypad), + GFP_KERNEL); + if (!keypad) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + return -ENOMEM; + } + + keypad->row_shift = get_count_order(KEYPAD_BITMASK_COLS); + + error = of_property_read_u32(of_node, "scan-interval", + &keypad->scan_interval); + if (error) { + dev_err(&pdev->dev, "failed to get scan-interval\n"); + return error; + } + + error = of_property_read_u32(of_node, "row-delay", + &keypad->row_delay); + if (error) { + dev_err(&pdev->dev, "failed to get row-delay\n"); + return error; + } + + keypad->active_low = of_property_read_bool(of_node, "active-low"); + + keypad->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "unable to get clock\n"); + return PTR_ERR(keypad->clk); + } + + keypad->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(keypad->reg_base)) + return PTR_ERR(keypad->reg_base); + + keypad->input = input = devm_input_allocate_device(&pdev->dev); + if (!input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + input_set_drvdata(input, keypad); + + input->id.bustype = BUS_HOST; + input->name = "nspire-keypad"; + input->open = nspire_keypad_open; + input->close = nspire_keypad_close; + + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_REP, input->evbit); + input_set_capability(input, EV_MSC, MSC_SCAN); + + error = matrix_keypad_build_keymap(NULL, NULL, + KEYPAD_BITMASK_ROWS, + KEYPAD_BITMASK_COLS, + NULL, input); + if (error) { + dev_err(&pdev->dev, "building keymap failed\n"); + return error; + } + + error = devm_request_irq(&pdev->dev, irq, nspire_keypad_irq, 0, + "nspire_keypad", keypad); + if (error) { + dev_err(&pdev->dev, "allocate irq %d failed\n", irq); + return error; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, + "unable to register input device: %d\n", error); + return error; + } + + platform_set_drvdata(pdev, keypad); + + dev_dbg(&pdev->dev, + "TI-NSPIRE keypad at %pR (scan_interval=%uus, row_delay=%uus%s)\n", + res, keypad->row_delay, keypad->scan_interval, + keypad->active_low ? ", active_low" : ""); + + return 0; +} + +static const struct of_device_id nspire_keypad_dt_match[] = { + { .compatible = "ti,nspire-keypad" }, + { }, +}; +MODULE_DEVICE_TABLE(of, nspire_keypad_dt_match); + +static struct platform_driver nspire_keypad_driver = { + .driver = { + .name = "nspire-keypad", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(nspire_keypad_dt_match), + }, + .probe = nspire_keypad_probe, +}; + +module_platform_driver(nspire_keypad_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TI-NSPIRE Keypad Driver"); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 1b289092f4e3..f4aa53a1fd69 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -419,8 +419,6 @@ static int omap4_keypad_remove(struct platform_device *pdev) kfree(keypad_data->keymap); kfree(keypad_data); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c index 7ac5f174c6f7..7b9b44158ad1 100644 --- a/drivers/input/keyboard/opencores-kbd.c +++ b/drivers/input/keyboard/opencores-kbd.c @@ -151,8 +151,6 @@ static int opencores_kbd_remove(struct platform_device *pdev) input_unregister_device(opencores_kbd->input); kfree(opencores_kbd); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index 74339e139d43..2c9f19ac35ea 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -707,7 +707,6 @@ err_gpio_config: err_get_irq: input_free_device(kp->input); err_alloc_device: - platform_set_drvdata(pdev, NULL); kfree(kp); return rc; } @@ -722,7 +721,6 @@ static int pmic8xxx_kp_remove(struct platform_device *pdev) input_unregister_device(kp->input); kfree(kp); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 5330d8fbf6c0..134c3b404a54 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -100,7 +100,7 @@ #define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM) struct pxa27x_keypad { - struct pxa27x_keypad_platform_data *pdata; + const struct pxa27x_keypad_platform_data *pdata; struct clk *clk; struct input_dev *input_dev; @@ -118,25 +118,254 @@ struct pxa27x_keypad { unsigned int direct_key_mask; }; -static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) +#ifdef CONFIG_OF +static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad, + struct pxa27x_keypad_platform_data *pdata) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; - unsigned short keycode; + struct device *dev = input_dev->dev.parent; + u32 rows, cols; + int error; + + error = matrix_keypad_parse_of_params(dev, &rows, &cols); + if (error) + return error; + + if (rows > MAX_MATRIX_KEY_ROWS || cols > MAX_MATRIX_KEY_COLS) { + dev_err(dev, "rows or cols exceeds maximum value\n"); + return -EINVAL; + } + + pdata->matrix_key_rows = rows; + pdata->matrix_key_cols = cols; + + error = matrix_keypad_build_keymap(NULL, NULL, + pdata->matrix_key_rows, + pdata->matrix_key_cols, + keypad->keycodes, input_dev); + if (error) + return error; + + return 0; +} + +static int pxa27x_keypad_direct_key_parse_dt(struct pxa27x_keypad *keypad, + struct pxa27x_keypad_platform_data *pdata) +{ + struct input_dev *input_dev = keypad->input_dev; + struct device *dev = input_dev->dev.parent; + struct device_node *np = dev->of_node; + const __be16 *prop; + unsigned short code; + unsigned int proplen, size; int i; + int error; - for (i = 0; i < pdata->matrix_key_map_size; i++) { - unsigned int key = pdata->matrix_key_map[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned int scancode = MATRIX_SCAN_CODE(row, col, - MATRIX_ROW_SHIFT); + error = of_property_read_u32(np, "marvell,direct-key-count", + &pdata->direct_key_num); + if (error) { + /* + * If do not have marvel,direct-key-count defined, + * it means direct key is not supported. + */ + return error == -EINVAL ? 0 : error; + } - keycode = KEY_VAL(key); - keypad->keycodes[scancode] = keycode; - __set_bit(keycode, input_dev->keybit); + error = of_property_read_u32(np, "marvell,direct-key-mask", + &pdata->direct_key_mask); + if (error) { + if (error != -EINVAL) + return error; + + /* + * If marvell,direct-key-mask is not defined, driver will use + * default value. Default value is set when configure the keypad. + */ + pdata->direct_key_mask = 0; + } + + pdata->direct_key_low_active = of_property_read_bool(np, + "marvell,direct-key-low-active"); + + prop = of_get_property(np, "marvell,direct-key-map", &proplen); + if (!prop) + return -EINVAL; + + if (proplen % sizeof(u16)) + return -EINVAL; + + size = proplen / sizeof(u16); + + /* Only MAX_DIRECT_KEY_NUM is accepted.*/ + if (size > MAX_DIRECT_KEY_NUM) + return -EINVAL; + + for (i = 0; i < size; i++) { + code = be16_to_cpup(prop + i); + keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = code; + __set_bit(code, input_dev->keybit); + } + + return 0; +} + +static int pxa27x_keypad_rotary_parse_dt(struct pxa27x_keypad *keypad, + struct pxa27x_keypad_platform_data *pdata) +{ + const __be32 *prop; + int i, relkey_ret; + unsigned int code, proplen; + const char *rotaryname[2] = { + "marvell,rotary0", "marvell,rotary1"}; + const char relkeyname[] = {"marvell,rotary-rel-key"}; + struct input_dev *input_dev = keypad->input_dev; + struct device *dev = input_dev->dev.parent; + struct device_node *np = dev->of_node; + + relkey_ret = of_property_read_u32(np, relkeyname, &code); + /* if can read correct rotary key-code, we do not need this. */ + if (relkey_ret == 0) { + unsigned short relcode; + + /* rotary0 taks lower half, rotary1 taks upper half. */ + relcode = code & 0xffff; + pdata->rotary0_rel_code = (code & 0xffff); + __set_bit(relcode, input_dev->relbit); + + relcode = code >> 16; + pdata->rotary1_rel_code = relcode; + __set_bit(relcode, input_dev->relbit); + } + + for (i = 0; i < 2; i++) { + prop = of_get_property(np, rotaryname[i], &proplen); + /* + * If the prop is not set, it means keypad does not need + * initialize the rotaryX. + */ + if (!prop) + continue; + + code = be32_to_cpup(prop); + /* + * Not all up/down key code are valid. + * Now we depends on direct-rel-code. + */ + if ((!(code & 0xffff) || !(code >> 16)) && relkey_ret) { + return relkey_ret; + } else { + unsigned int n = MAX_MATRIX_KEY_NUM + (i << 1); + unsigned short keycode; + + keycode = code & 0xffff; + keypad->keycodes[n] = keycode; + __set_bit(keycode, input_dev->keybit); + + keycode = code >> 16; + keypad->keycodes[n + 1] = keycode; + __set_bit(keycode, input_dev->keybit); + + if (i == 0) + pdata->rotary0_rel_code = -1; + else + pdata->rotary1_rel_code = -1; + } + if (i == 0) + pdata->enable_rotary0 = 1; + else + pdata->enable_rotary1 = 1; + } + + keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; + keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; + + return 0; +} + +static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) +{ + struct input_dev *input_dev = keypad->input_dev; + struct device *dev = input_dev->dev.parent; + struct device_node *np = dev->of_node; + struct pxa27x_keypad_platform_data *pdata; + int error; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "failed to allocate memory for pdata\n"); + return -ENOMEM; + } + + error = pxa27x_keypad_matrix_key_parse_dt(keypad, pdata); + if (error) { + dev_err(dev, "failed to parse matrix key\n"); + return error; + } + + error = pxa27x_keypad_direct_key_parse_dt(keypad, pdata); + if (error) { + dev_err(dev, "failed to parse direct key\n"); + return error; + } + + error = pxa27x_keypad_rotary_parse_dt(keypad, pdata); + if (error) { + dev_err(dev, "failed to parse rotary key\n"); + return error; + } + + error = of_property_read_u32(np, "marvell,debounce-interval", + &pdata->debounce_interval); + if (error) { + dev_err(dev, "failed to parse debpunce-interval\n"); + return error; } + /* + * The keycodes may not only includes matrix key but also the direct + * key or rotary key. + */ + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + + keypad->pdata = pdata; + return 0; +} + +#else + +static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad) +{ + dev_info(keypad->input_dev->dev.parent, "missing platform data\n"); + + return -EINVAL; +} + +#endif + +static int pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) +{ + const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + struct input_dev *input_dev = keypad->input_dev; + const struct matrix_keymap_data *keymap_data = + pdata ? pdata->matrix_keymap_data : NULL; + unsigned short keycode; + int i; + int error; + + error = matrix_keypad_build_keymap(keymap_data, NULL, + pdata->matrix_key_rows, + pdata->matrix_key_cols, + keypad->keycodes, input_dev); + if (error) + return error; + + /* + * The keycodes may not only include matrix keys but also the direct + * or rotary keys. + */ + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + + /* For direct keys. */ for (i = 0; i < pdata->direct_key_num; i++) { keycode = pdata->direct_key_map[i]; keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode; @@ -178,11 +407,13 @@ static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) } __clear_bit(KEY_RESERVED, input_dev->keybit); + + return 0; } static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; int row, col, num_keys_pressed = 0; uint32_t new_state[MAX_MATRIX_KEY_COLS]; @@ -284,7 +515,7 @@ static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta) static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; uint32_t kprec; /* read and reset to default count value */ @@ -300,7 +531,7 @@ static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad) static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; unsigned int new_state; uint32_t kpdk, bits_changed; @@ -340,7 +571,7 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) static void clear_wakeup_event(struct pxa27x_keypad *keypad) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; if (pdata->clear_wakeup_event) (pdata->clear_wakeup_event)(); @@ -364,7 +595,7 @@ static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) static void pxa27x_keypad_config(struct pxa27x_keypad *keypad) { - struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + const struct pxa27x_keypad_platform_data *pdata = keypad->pdata; unsigned int mask = 0, direct_key_num = 0; unsigned long kpc = 0; @@ -431,7 +662,7 @@ static void pxa27x_keypad_close(struct input_dev *dev) clk_disable_unprepare(keypad->clk); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int pxa27x_keypad_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -475,25 +706,25 @@ static int pxa27x_keypad_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops pxa27x_keypad_pm_ops = { - .suspend = pxa27x_keypad_suspend, - .resume = pxa27x_keypad_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(pxa27x_keypad_pm_ops, + pxa27x_keypad_suspend, pxa27x_keypad_resume); + + static int pxa27x_keypad_probe(struct platform_device *pdev) { - struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data; + const struct pxa27x_keypad_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; struct pxa27x_keypad *keypad; struct input_dev *input_dev; struct resource *res; int irq, error; - if (pdata == NULL) { - dev_err(&pdev->dev, "no platform data defined\n"); + /* Driver need build keycode from device tree or pdata */ + if (!np && !pdata) return -EINVAL; - } irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -555,7 +786,14 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); input_set_capability(input_dev, EV_MSC, MSC_SCAN); - pxa27x_keypad_build_keycode(keypad); + if (pdata) + error = pxa27x_keypad_build_keycode(keypad); + else + error = pxa27x_keypad_build_keycode_from_dt(keypad); + if (error) { + dev_err(&pdev->dev, "failed to build keycode\n"); + goto failed_put_clk; + } if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) || (pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) { @@ -582,7 +820,7 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) return 0; failed_free_irq: - free_irq(irq, pdev); + free_irq(irq, keypad); failed_put_clk: clk_put(keypad->clk); failed_free_io: @@ -600,7 +838,7 @@ static int pxa27x_keypad_remove(struct platform_device *pdev) struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); struct resource *res; - free_irq(keypad->irq, pdev); + free_irq(keypad->irq, keypad); clk_put(keypad->clk); input_unregister_device(keypad->input_dev); @@ -609,7 +847,6 @@ static int pxa27x_keypad_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); kfree(keypad); return 0; @@ -618,15 +855,22 @@ static int pxa27x_keypad_remove(struct platform_device *pdev) /* work with hotplug and coldplug */ MODULE_ALIAS("platform:pxa27x-keypad"); +#ifdef CONFIG_OF +static const struct of_device_id pxa27x_keypad_dt_match[] = { + { .compatible = "marvell,pxa27x-keypad" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pxa27x_keypad_dt_match); +#endif + static struct platform_driver pxa27x_keypad_driver = { .probe = pxa27x_keypad_probe, .remove = pxa27x_keypad_remove, .driver = { .name = "pxa27x-keypad", + .of_match_table = of_match_ptr(pxa27x_keypad_dt_match), .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &pxa27x_keypad_pm_ops, -#endif }, }; module_platform_driver(pxa27x_keypad_driver); diff --git a/drivers/input/keyboard/pxa930_rotary.c b/drivers/input/keyboard/pxa930_rotary.c index bcad95be73aa..248cdcf95296 100644 --- a/drivers/input/keyboard/pxa930_rotary.c +++ b/drivers/input/keyboard/pxa930_rotary.c @@ -181,7 +181,6 @@ static int pxa930_rotary_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), r); input_unregister_device(r->input_dev); iounmap(r->mmio_base); - platform_set_drvdata(pdev, NULL); kfree(r); return 0; diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 22e357b51024..ac43a486c775 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -24,7 +24,6 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/sched.h> #include <linux/input/samsung-keypad.h> @@ -79,10 +78,6 @@ struct samsung_keypad { unsigned int rows; unsigned int cols; unsigned int row_state[SAMSUNG_MAX_COLS]; -#ifdef CONFIG_OF - int row_gpios[SAMSUNG_MAX_ROWS]; - int col_gpios[SAMSUNG_MAX_COLS]; -#endif unsigned short keycodes[]; }; @@ -304,45 +299,6 @@ static struct samsung_keypad_platdata *samsung_keypad_parse_dt( return pdata; } - -static void samsung_keypad_parse_dt_gpio(struct device *dev, - struct samsung_keypad *keypad) -{ - struct device_node *np = dev->of_node; - int gpio, error, row, col; - - for (row = 0; row < keypad->rows; row++) { - gpio = of_get_named_gpio(np, "row-gpios", row); - keypad->row_gpios[row] = gpio; - if (!gpio_is_valid(gpio)) { - dev_err(dev, "keypad row[%d]: invalid gpio %d\n", - row, gpio); - continue; - } - - error = devm_gpio_request(dev, gpio, "keypad-row"); - if (error) - dev_err(dev, - "keypad row[%d] gpio request failed: %d\n", - row, error); - } - - for (col = 0; col < keypad->cols; col++) { - gpio = of_get_named_gpio(np, "col-gpios", col); - keypad->col_gpios[col] = gpio; - if (!gpio_is_valid(gpio)) { - dev_err(dev, "keypad column[%d]: invalid gpio %d\n", - col, gpio); - continue; - } - - error = devm_gpio_request(dev, gpio, "keypad-col"); - if (error) - dev_err(dev, - "keypad column[%d] gpio request failed: %d\n", - col, error); - } -} #else static struct samsung_keypad_platdata *samsung_keypad_parse_dt(struct device *dev) @@ -424,15 +380,11 @@ static int samsung_keypad_probe(struct platform_device *pdev) keypad->stopped = true; init_waitqueue_head(&keypad->wait); - if (pdev->dev.of_node) { -#ifdef CONFIG_OF - samsung_keypad_parse_dt_gpio(&pdev->dev, keypad); + if (pdev->dev.of_node) keypad->type = of_device_is_compatible(pdev->dev.of_node, "samsung,s5pv210-keypad"); -#endif - } else { + else keypad->type = platform_get_device_id(pdev)->driver_data; - } input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; @@ -487,7 +439,6 @@ static int samsung_keypad_probe(struct platform_device *pdev) err_disable_runtime_pm: pm_runtime_disable(&pdev->dev); device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); err_unprepare_clk: clk_unprepare(keypad->clk); return error; @@ -499,7 +450,6 @@ static int samsung_keypad_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); input_unregister_device(keypad->input_dev); diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index fdb9eb2df380..fe0e498d2479 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -266,7 +266,6 @@ static int sh_keysc_probe(struct platform_device *pdev) err2: iounmap(priv->iomem_base); err1: - platform_set_drvdata(pdev, NULL); kfree(priv); err0: return error; @@ -285,7 +284,6 @@ static int sh_keysc_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - platform_set_drvdata(pdev, NULL); kfree(priv); return 0; diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index cb1e8f614631..7111124b5362 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -290,7 +290,6 @@ static int spear_kbd_remove(struct platform_device *pdev) clk_unprepare(kbd->clk); device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c index ee1635011292..5f7b427dd7ed 100644 --- a/drivers/input/keyboard/tnetv107x-keypad.c +++ b/drivers/input/keyboard/tnetv107x-keypad.c @@ -296,7 +296,6 @@ error_clk: error_map: release_mem_region(kp->res->start, resource_size(kp->res)); error_res: - platform_set_drvdata(pdev, NULL); kfree(kp); return error; } @@ -311,7 +310,6 @@ static int keypad_remove(struct platform_device *pdev) clk_put(kp->clk); iounmap(kp->regs); release_mem_region(kp->res->start, resource_size(kp->res)); - platform_set_drvdata(pdev, NULL); kfree(kp); return 0; diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index 04f84fd57173..d2d178c84ea7 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -422,7 +422,7 @@ static int twl4030_kp_probe(struct platform_device *pdev) err3: /* mask all events - we don't care about the result */ (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); - free_irq(kp->irq, NULL); + free_irq(kp->irq, kp); err2: input_unregister_device(input); input = NULL; @@ -438,7 +438,6 @@ static int twl4030_kp_remove(struct platform_device *pdev) free_irq(kp->irq, kp); input_unregister_device(kp->input); - platform_set_drvdata(pdev, NULL); kfree(kp); return 0; diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c index ee163bee8cce..7b039162a3f8 100644 --- a/drivers/input/keyboard/w90p910_keypad.c +++ b/drivers/input/keyboard/w90p910_keypad.c @@ -221,7 +221,7 @@ static int w90p910_keypad_probe(struct platform_device *pdev) return 0; failed_free_irq: - free_irq(irq, pdev); + free_irq(irq, keypad); failed_put_clk: clk_put(keypad->clk); failed_free_io: @@ -239,7 +239,7 @@ static int w90p910_keypad_remove(struct platform_device *pdev) struct w90p910_keypad *keypad = platform_get_drvdata(pdev); struct resource *res; - free_irq(keypad->irq, pdev); + free_irq(keypad->irq, keypad); clk_put(keypad->clk); @@ -249,7 +249,6 @@ static int w90p910_keypad_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); kfree(keypad); return 0; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index bb698e1f9e42..0b541cdf9b8e 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -637,4 +637,14 @@ config INPUT_XEN_KBDDEV_FRONTEND To compile this driver as a module, choose M here: the module will be called xen-kbdfront. +config INPUT_SIRFSOC_ONKEY + bool "CSR SiRFSoC power on/off/suspend key support" + depends on ARCH_SIRF && OF + default y + help + Say Y here if you want to support for the SiRFSoC power on/off/suspend key + in Linux, after you press the onkey, system will suspend. + + If unsure, say N. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index d7fc17f11d77..829de43a2427 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o +obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 2f090b46e716..f2fbdd88ed20 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -127,8 +127,6 @@ static int ab8500_ponkey_remove(struct platform_device *pdev) input_unregister_device(ponkey->idev); kfree(ponkey); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c index a6666e142a91..cd139cb17e32 100644 --- a/drivers/input/misc/bfin_rotary.c +++ b/drivers/input/misc/bfin_rotary.c @@ -208,7 +208,6 @@ static int bfin_rotary_remove(struct platform_device *pdev) peripheral_free_list(per_cnt); kfree(rotary); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/input/misc/gpio_tilt_polled.c b/drivers/input/misc/gpio_tilt_polled.c index da05cca8b562..714c68369134 100644 --- a/drivers/input/misc/gpio_tilt_polled.c +++ b/drivers/input/misc/gpio_tilt_polled.c @@ -184,8 +184,6 @@ static int gpio_tilt_polled_remove(struct platform_device *pdev) struct gpio_tilt_polled_dev *tdev = platform_get_drvdata(pdev); const struct gpio_tilt_platform_data *pdata = tdev->pdata; - platform_set_drvdata(pdev, NULL); - input_unregister_polled_device(tdev->poll_dev); input_free_polled_device(tdev->poll_dev); diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index 6ab3decc86e6..f34beb228d36 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -125,7 +125,7 @@ static int ixp4xx_spkr_probe(struct platform_device *dev) return 0; err_free_irq: - free_irq(IRQ_IXP4XX_TIMER2, dev); + free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); err_free_device: input_free_device(input_dev); @@ -138,13 +138,12 @@ static int ixp4xx_spkr_remove(struct platform_device *dev) unsigned int pin = (unsigned int) input_get_drvdata(input_dev); input_unregister_device(input_dev); - platform_set_drvdata(dev, NULL); /* turn the speaker off */ disable_irq(IRQ_IXP4XX_TIMER2); ixp4xx_spkr_control(pin, 0); - free_irq(IRQ_IXP4XX_TIMER2, dev); + free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); return 0; } diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c index b40ee4b47f4f..def21dc84522 100644 --- a/drivers/input/misc/m68kspkr.c +++ b/drivers/input/misc/m68kspkr.c @@ -85,7 +85,6 @@ static int m68kspkr_remove(struct platform_device *dev) struct input_dev *input_dev = platform_get_drvdata(dev); input_unregister_device(input_dev); - platform_set_drvdata(dev, NULL); /* turn off the speaker */ m68kspkr_event(NULL, EV_SND, SND_BELL, 0); diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c index f9179b2585a9..eef41cfc054d 100644 --- a/drivers/input/misc/max8925_onkey.c +++ b/drivers/input/misc/max8925_onkey.c @@ -148,8 +148,6 @@ static int max8925_onkey_remove(struct platform_device *pdev) input_unregister_device(info->idev); kfree(info); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c index 0906ca593d5f..d0277a7b1579 100644 --- a/drivers/input/misc/mc13783-pwrbutton.c +++ b/drivers/input/misc/mc13783-pwrbutton.c @@ -250,7 +250,6 @@ static int mc13783_pwrbutton_remove(struct platform_device *pdev) input_unregister_device(priv->pwr); kfree(priv); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index 199db78acc4f..7288b267613d 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -100,7 +100,6 @@ static int pcspkr_remove(struct platform_device *dev) struct input_dev *pcspkr_dev = platform_get_drvdata(dev); input_unregister_device(pcspkr_dev); - platform_set_drvdata(dev, NULL); /* turn off the speaker */ pcspkr_event(NULL, EV_SND, SND_BELL, 0); diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c index a9da65e41c5b..ec086f6f3cc3 100644 --- a/drivers/input/misc/pm8xxx-vibrator.c +++ b/drivers/input/misc/pm8xxx-vibrator.c @@ -249,8 +249,6 @@ static int pm8xxx_vib_remove(struct platform_device *pdev) input_unregister_device(vib->vib_input_dev); kfree(vib); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c index 4b811be73974..b49b738aa9c6 100644 --- a/drivers/input/misc/pmic8xxx-pwrkey.c +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -175,9 +175,8 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) return 0; free_press_irq: - free_irq(key_press_irq, NULL); + free_irq(key_press_irq, pwrkey); unreg_input_dev: - platform_set_drvdata(pdev, NULL); input_unregister_device(pwr); pwr = NULL; free_input_dev: @@ -198,7 +197,6 @@ static int pmic8xxx_pwrkey_remove(struct platform_device *pdev) free_irq(key_press_irq, pwrkey); free_irq(key_release_irq, pwrkey); input_unregister_device(pwrkey->pwr); - platform_set_drvdata(pdev, NULL); kfree(pwrkey); return 0; diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 0808868461de..a37f0c909aba 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -133,7 +133,6 @@ static int pwm_beeper_remove(struct platform_device *pdev) { struct pwm_beeper *beeper = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); input_unregister_device(beeper->input); pwm_disable(beeper->pwm); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index aff47b2c38ff..5b1aff825138 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -317,8 +317,6 @@ static int rotary_encoder_remove(struct platform_device *pdev) if (!dev_get_platdata(&pdev->dev)) kfree(pdata); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/misc/sgi_btns.c b/drivers/input/misc/sgi_btns.c index ad6415ceaf5f..95cf299ef9a3 100644 --- a/drivers/input/misc/sgi_btns.c +++ b/drivers/input/misc/sgi_btns.c @@ -128,7 +128,7 @@ static int sgi_buttons_probe(struct platform_device *pdev) __clear_bit(KEY_RESERVED, input->keybit); bdev->poll_dev = poll_dev; - dev_set_drvdata(&pdev->dev, bdev); + platform_set_drvdata(pdev, bdev); error = input_register_polled_device(poll_dev); if (error) @@ -139,19 +139,16 @@ static int sgi_buttons_probe(struct platform_device *pdev) err_free_mem: input_free_polled_device(poll_dev); kfree(bdev); - dev_set_drvdata(&pdev->dev, NULL); return error; } static int sgi_buttons_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct buttons_dev *bdev = dev_get_drvdata(dev); + struct buttons_dev *bdev = platform_get_drvdata(pdev); input_unregister_polled_device(bdev->poll_dev); input_free_polled_device(bdev->poll_dev); kfree(bdev); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c new file mode 100644 index 000000000000..0621c367049a --- /dev/null +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -0,0 +1,165 @@ +/* + * Power key driver for SiRF PrimaII + * + * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/rtc/sirfsoc_rtciobrg.h> +#include <linux/of.h> + +struct sirfsoc_pwrc_drvdata { + u32 pwrc_base; + struct input_dev *input; +}; + +#define PWRC_ON_KEY_BIT (1 << 0) + +#define PWRC_INT_STATUS 0xc +#define PWRC_INT_MASK 0x10 + +static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id) +{ + struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_id; + u32 int_status; + + int_status = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + + PWRC_INT_STATUS); + sirfsoc_rtc_iobrg_writel(int_status & ~PWRC_ON_KEY_BIT, + pwrcdrv->pwrc_base + PWRC_INT_STATUS); + + /* + * For a typical Linux system, we report KEY_SUSPEND to trigger apm-power.c + * to queue a SUSPEND APM event + */ + input_event(pwrcdrv->input, EV_PWR, KEY_SUSPEND, 1); + input_sync(pwrcdrv->input); + + /* + * Todo: report KEY_POWER event for Android platforms, Android PowerManager + * will handle the suspend and powerdown/hibernation + */ + + return IRQ_HANDLED; +} + +static const struct of_device_id sirfsoc_pwrc_of_match[] = { + { .compatible = "sirf,prima2-pwrc" }, + {}, +} +MODULE_DEVICE_TABLE(of, sirfsoc_pwrc_of_match); + +static int sirfsoc_pwrc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct sirfsoc_pwrc_drvdata *pwrcdrv; + int irq; + int error; + + pwrcdrv = devm_kzalloc(&pdev->dev, sizeof(struct sirfsoc_pwrc_drvdata), + GFP_KERNEL); + if (!pwrcdrv) { + dev_info(&pdev->dev, "Not enough memory for the device data\n"); + return -ENOMEM; + } + + /* + * we can't use of_iomap because pwrc is not mapped in memory, + * the so-called base address is only offset in rtciobrg + */ + error = of_property_read_u32(np, "reg", &pwrcdrv->pwrc_base); + if (error) { + dev_err(&pdev->dev, + "unable to find base address of pwrc node in dtb\n"); + return error; + } + + pwrcdrv->input = devm_input_allocate_device(&pdev->dev); + if (!pwrcdrv->input) + return -ENOMEM; + + pwrcdrv->input->name = "sirfsoc pwrckey"; + pwrcdrv->input->phys = "pwrc/input0"; + pwrcdrv->input->evbit[0] = BIT_MASK(EV_PWR); + + irq = platform_get_irq(pdev, 0); + error = devm_request_irq(&pdev->dev, irq, + sirfsoc_pwrc_isr, IRQF_SHARED, + "sirfsoc_pwrc_int", pwrcdrv); + if (error) { + dev_err(&pdev->dev, "unable to claim irq %d, error: %d\n", + irq, error); + return error; + } + + sirfsoc_rtc_iobrg_writel( + sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK) | + PWRC_ON_KEY_BIT, + pwrcdrv->pwrc_base + PWRC_INT_MASK); + + error = input_register_device(pwrcdrv->input); + if (error) { + dev_err(&pdev->dev, + "unable to register input device, error: %d\n", + error); + return error; + } + + platform_set_drvdata(pdev, pwrcdrv); + device_init_wakeup(&pdev->dev, 1); + + return 0; +} + +static int sirfsoc_pwrc_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pwrc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sirfsoc_pwrc_drvdata *pwrcdrv = platform_get_drvdata(pdev); + + /* + * Do not mask pwrc interrupt as we want pwrc work as a wakeup source + * if users touch X_ONKEY_B, see arch/arm/mach-prima2/pm.c + */ + sirfsoc_rtc_iobrg_writel( + sirfsoc_rtc_iobrg_readl( + pwrcdrv->pwrc_base + PWRC_INT_MASK) | PWRC_ON_KEY_BIT, + pwrcdrv->pwrc_base + PWRC_INT_MASK); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, pwrc_resume); + +static struct platform_driver sirfsoc_pwrc_driver = { + .probe = sirfsoc_pwrc_probe, + .remove = sirfsoc_pwrc_remove, + .driver = { + .name = "sirfsoc-pwrc", + .owner = THIS_MODULE, + .pm = &sirfsoc_pwrc_pm_ops, + .of_match_table = of_match_ptr(sirfsoc_pwrc_of_match), + } +}; + +module_platform_driver(sirfsoc_pwrc_driver); + +MODULE_LICENSE("GPLv2"); +MODULE_AUTHOR("Binghua Duan <Binghua.Duan@csr.com>, Xianglong Du <Xianglong.Du@csr.com>"); +MODULE_DESCRIPTION("CSR Prima2 PWRC Driver"); +MODULE_ALIAS("platform:sirfsoc-pwrc"); diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index a53586a7fbdb..65fd3150919b 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -175,7 +175,7 @@ static int sparcspkr_probe(struct device *dev) static void sparcspkr_shutdown(struct platform_device *dev) { - struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); + struct sparcspkr_state *state = platform_get_drvdata(dev); struct input_dev *input_dev = state->input_dev; /* turn off the speaker */ @@ -211,7 +211,7 @@ static int bbc_beep_probe(struct platform_device *op) if (!info->regs) goto out_free; - dev_set_drvdata(&op->dev, state); + platform_set_drvdata(op, state); err = sparcspkr_probe(&op->dev); if (err) @@ -220,7 +220,6 @@ static int bbc_beep_probe(struct platform_device *op) return 0; out_clear_drvdata: - dev_set_drvdata(&op->dev, NULL); of_iounmap(&op->resource[0], info->regs, 6); out_free: @@ -231,7 +230,7 @@ out_err: static int bbc_remove(struct platform_device *op) { - struct sparcspkr_state *state = dev_get_drvdata(&op->dev); + struct sparcspkr_state *state = platform_get_drvdata(op); struct input_dev *input_dev = state->input_dev; struct bbc_beep_info *info = &state->u.bbc; @@ -242,7 +241,6 @@ static int bbc_remove(struct platform_device *op) of_iounmap(&op->resource[0], info->regs, 6); - dev_set_drvdata(&op->dev, NULL); kfree(state); return 0; @@ -290,7 +288,7 @@ static int grover_beep_probe(struct platform_device *op) if (!info->enable_reg) goto out_unmap_freq_regs; - dev_set_drvdata(&op->dev, state); + platform_set_drvdata(op, state); err = sparcspkr_probe(&op->dev); if (err) @@ -299,7 +297,6 @@ static int grover_beep_probe(struct platform_device *op) return 0; out_clear_drvdata: - dev_set_drvdata(&op->dev, NULL); of_iounmap(&op->resource[3], info->enable_reg, 1); out_unmap_freq_regs: @@ -312,7 +309,7 @@ out_err: static int grover_remove(struct platform_device *op) { - struct sparcspkr_state *state = dev_get_drvdata(&op->dev); + struct sparcspkr_state *state = platform_get_drvdata(op); struct grover_beep_info *info = &state->u.grover; struct input_dev *input_dev = state->input_dev; @@ -324,7 +321,6 @@ static int grover_remove(struct platform_device *op) of_iounmap(&op->resource[3], info->enable_reg, 1); of_iounmap(&op->resource[2], info->freq_regs, 2); - dev_set_drvdata(&op->dev, NULL); kfree(state); return 0; diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c index b55d5af217a7..62ec52b2e347 100644 --- a/drivers/input/mouse/amimouse.c +++ b/drivers/input/mouse/amimouse.c @@ -133,7 +133,6 @@ static int __exit amimouse_remove(struct platform_device *pdev) { struct input_dev *dev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); input_unregister_device(dev); return 0; } diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 2baff1b79a55..4ef4d5e198ae 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -88,6 +88,10 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI 0x0259 #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a #define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b +/* MacbookAir6,2 (unibody, June 2013) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293 #define BCM5974_DEVICE(prod) { \ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ @@ -145,6 +149,10 @@ static const struct usb_device_id bcm5974_table[] = { BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS), + /* MacbookAir6,2 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_JIS), /* Terminating entry */ {} }; @@ -172,15 +180,18 @@ struct bt_data { /* trackpad header types */ enum tp_type { TYPE1, /* plain trackpad */ - TYPE2 /* button integrated in trackpad */ + TYPE2, /* button integrated in trackpad */ + TYPE3 /* additional header fields since June 2013 */ }; /* trackpad finger data offsets, le16-aligned */ #define FINGER_TYPE1 (13 * sizeof(__le16)) #define FINGER_TYPE2 (15 * sizeof(__le16)) +#define FINGER_TYPE3 (19 * sizeof(__le16)) /* trackpad button data offsets */ #define BUTTON_TYPE2 15 +#define BUTTON_TYPE3 23 /* list of device capability bits */ #define HAS_INTEGRATED_BUTTON 1 @@ -400,6 +411,19 @@ static const struct bcm5974_config bcm5974_config_table[] = { { SN_COORD, -150, 6730 }, { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } }, + { + USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING8_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING8_JIS, + HAS_INTEGRATED_BUTTON, + 0, sizeof(struct bt_data), + 0x83, TYPE3, FINGER_TYPE3, FINGER_TYPE3 + SIZEOF_ALL_FINGERS, + { SN_PRESSURE, 0, 300 }, + { SN_WIDTH, 0, 2048 }, + { SN_COORD, -4620, 5140 }, + { SN_COORD, -150, 6600 }, + { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } + }, {} }; @@ -557,6 +581,9 @@ static int report_tp_state(struct bcm5974 *dev, int size) input_report_key(input, BTN_LEFT, ibt); } + if (c->tp_type == TYPE3) + input_report_key(input, BTN_LEFT, dev->tp_data[BUTTON_TYPE3]); + input_sync(input); return 0; @@ -572,9 +599,14 @@ static int report_tp_state(struct bcm5974 *dev, int size) static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) { - char *data = kmalloc(8, GFP_KERNEL); int retval = 0, size; + char *data; + + /* Type 3 does not require a mode switch */ + if (dev->cfg.tp_type == TYPE3) + return 0; + data = kmalloc(8, GFP_KERNEL); if (!data) { dev_err(&dev->intf->dev, "out of memory\n"); retval = -ENOMEM; diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 532eaca4cc56..6b44413f54e3 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -138,7 +138,6 @@ static int gpio_mouse_probe(struct platform_device *pdev) out_free_polldev: input_free_polled_device(input_poll); - platform_set_drvdata(pdev, NULL); out_free_gpios: while (--i >= 0) { @@ -165,8 +164,6 @@ static int gpio_mouse_remove(struct platform_device *pdev) gpio_free(pin); } - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/mouse/navpoint.c b/drivers/input/mouse/navpoint.c index 8e1b98ea5648..0b8d33591dee 100644 --- a/drivers/input/mouse/navpoint.c +++ b/drivers/input/mouse/navpoint.c @@ -287,7 +287,7 @@ static int navpoint_probe(struct platform_device *pdev) return 0; err_free_irq: - free_irq(ssp->irq, &pdev->dev); + free_irq(ssp->irq, navpoint); err_free_mem: input_free_device(input); kfree(navpoint); diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 1bda828f4b55..94c17c28d268 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -256,4 +256,14 @@ config SERIO_APBPS2 To compile this driver as a module, choose M here: the module will be called apbps2. +config SERIO_OLPC_APSP + tristate "OLPC AP-SP input support" + depends on OF + help + Say Y here if you want support for the keyboard and touchpad included + in the OLPC XO-1.75 and XO-4 laptops. + + To compile this driver as a module, choose M here: the module will + be called olpc_apsp. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 8edb36c2cdb4..12298b1c0e71 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o obj-$(CONFIG_SERIO_APBPS2) += apbps2.o +obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c index 479ce5fe8955..a0a2657e31ff 100644 --- a/drivers/input/serio/altera_ps2.c +++ b/drivers/input/serio/altera_ps2.c @@ -163,7 +163,6 @@ static int altera_ps2_remove(struct platform_device *pdev) { struct ps2if *ps2if = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); serio_unregister_port(ps2if->io); free_irq(ps2if->irq, ps2if); iounmap(ps2if->base); diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index 190ce35af7df..3290b287ac4b 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -314,8 +314,6 @@ static int __exit psif_remove(struct platform_device *pdev) clk_put(psif->pclk); kfree(psif); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/serio/olpc_apsp.c b/drivers/input/serio/olpc_apsp.c new file mode 100644 index 000000000000..818aa466b5d2 --- /dev/null +++ b/drivers/input/serio/olpc_apsp.c @@ -0,0 +1,287 @@ +/* + * OLPC serio driver for multiplexed input from Marvell MMP security processor + * + * Copyright (C) 2011-2013 One Laptop Per Child + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 <linux/module.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/serio.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/delay.h> + +/* + * The OLPC XO-1.75 and XO-4 laptops do not have a hardware PS/2 controller. + * Instead, the OLPC firmware runs a bit-banging PS/2 implementation on an + * otherwise-unused slow processor which is included in the Marvell MMP2/MMP3 + * SoC, known as the "Security Processor" (SP) or "Wireless Trusted Module" + * (WTM). This firmware then reports its results via the WTM registers, + * which we read from the Application Processor (AP, i.e. main CPU) in this + * driver. + * + * On the hardware side we have a PS/2 mouse and an AT keyboard, the data + * is multiplexed through this system. We create a serio port for each one, + * and demultiplex the data accordingly. + */ + +/* WTM register offsets */ +#define SECURE_PROCESSOR_COMMAND 0x40 +#define COMMAND_RETURN_STATUS 0x80 +#define COMMAND_FIFO_STATUS 0xc4 +#define PJ_RST_INTERRUPT 0xc8 +#define PJ_INTERRUPT_MASK 0xcc + +/* + * The upper byte of SECURE_PROCESSOR_COMMAND and COMMAND_RETURN_STATUS is + * used to identify which port (device) is being talked to. The lower byte + * is the data being sent/received. + */ +#define PORT_MASK 0xff00 +#define DATA_MASK 0x00ff +#define PORT_SHIFT 8 +#define KEYBOARD_PORT 0 +#define TOUCHPAD_PORT 1 + +/* COMMAND_FIFO_STATUS */ +#define CMD_CNTR_MASK 0x7 /* Number of pending/unprocessed commands */ +#define MAX_PENDING_CMDS 4 /* from device specs */ + +/* PJ_RST_INTERRUPT */ +#define SP_COMMAND_COMPLETE_RESET 0x1 + +/* PJ_INTERRUPT_MASK */ +#define INT_0 (1 << 0) + +/* COMMAND_FIFO_STATUS */ +#define CMD_STS_MASK 0x100 + +struct olpc_apsp { + struct device *dev; + struct serio *kbio; + struct serio *padio; + void __iomem *base; + int open_count; + int irq; +}; + +static int olpc_apsp_write(struct serio *port, unsigned char val) +{ + struct olpc_apsp *priv = port->port_data; + unsigned int i; + u32 which = 0; + + if (port == priv->padio) + which = TOUCHPAD_PORT << PORT_SHIFT; + else + which = KEYBOARD_PORT << PORT_SHIFT; + + dev_dbg(priv->dev, "olpc_apsp_write which=%x val=%x\n", which, val); + for (i = 0; i < 50; i++) { + u32 sts = readl(priv->base + COMMAND_FIFO_STATUS); + if ((sts & CMD_CNTR_MASK) < MAX_PENDING_CMDS) { + writel(which | val, + priv->base + SECURE_PROCESSOR_COMMAND); + return 0; + } + /* SP busy. This has not been seen in practice. */ + mdelay(1); + } + + dev_dbg(priv->dev, "olpc_apsp_write timeout, status=%x\n", + readl(priv->base + COMMAND_FIFO_STATUS)); + + return -ETIMEDOUT; +} + +static irqreturn_t olpc_apsp_rx(int irq, void *dev_id) +{ + struct olpc_apsp *priv = dev_id; + unsigned int w, tmp; + struct serio *serio; + + /* + * Write 1 to PJ_RST_INTERRUPT to acknowledge and clear the interrupt + * Write 0xff00 to SECURE_PROCESSOR_COMMAND. + */ + tmp = readl(priv->base + PJ_RST_INTERRUPT); + if (!(tmp & SP_COMMAND_COMPLETE_RESET)) { + dev_warn(priv->dev, "spurious interrupt?\n"); + return IRQ_NONE; + } + + w = readl(priv->base + COMMAND_RETURN_STATUS); + dev_dbg(priv->dev, "olpc_apsp_rx %x\n", w); + + if (w >> PORT_SHIFT == KEYBOARD_PORT) + serio = priv->kbio; + else + serio = priv->padio; + + serio_interrupt(serio, w & DATA_MASK, 0); + + /* Ack and clear interrupt */ + writel(tmp | SP_COMMAND_COMPLETE_RESET, priv->base + PJ_RST_INTERRUPT); + writel(PORT_MASK, priv->base + SECURE_PROCESSOR_COMMAND); + + pm_wakeup_event(priv->dev, 1000); + return IRQ_HANDLED; +} + +static int olpc_apsp_open(struct serio *port) +{ + struct olpc_apsp *priv = port->port_data; + unsigned int tmp; + + if (priv->open_count++ == 0) { + /* Enable interrupt 0 by clearing its bit */ + tmp = readl(priv->base + PJ_INTERRUPT_MASK); + writel(tmp & ~INT_0, priv->base + PJ_INTERRUPT_MASK); + } + + return 0; +} + +static void olpc_apsp_close(struct serio *port) +{ + struct olpc_apsp *priv = port->port_data; + unsigned int tmp; + + if (--priv->open_count == 0) { + /* Disable interrupt 0 */ + tmp = readl(priv->base + PJ_INTERRUPT_MASK); + writel(tmp | INT_0, priv->base + PJ_INTERRUPT_MASK); + } +} + +static int olpc_apsp_probe(struct platform_device *pdev) +{ + struct serio *kb_serio, *pad_serio; + struct olpc_apsp *priv; + struct resource *res; + struct device_node *np; + unsigned long l; + int error; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct olpc_apsp), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + np = pdev->dev.of_node; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + dev_err(&pdev->dev, "Failed to map WTM registers\n"); + return PTR_ERR(priv->base); + } + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) + return priv->irq; + + l = readl(priv->base + COMMAND_FIFO_STATUS); + if (!(l & CMD_STS_MASK)) { + dev_err(&pdev->dev, "SP cannot accept commands.\n"); + return -EIO; + } + + /* KEYBOARD */ + kb_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!kb_serio) + return -ENOMEM; + kb_serio->id.type = SERIO_8042_XL; + kb_serio->write = olpc_apsp_write; + kb_serio->open = olpc_apsp_open; + kb_serio->close = olpc_apsp_close; + kb_serio->port_data = priv; + kb_serio->dev.parent = &pdev->dev; + strlcpy(kb_serio->name, "sp keyboard", sizeof(kb_serio->name)); + strlcpy(kb_serio->phys, "sp/serio0", sizeof(kb_serio->phys)); + priv->kbio = kb_serio; + serio_register_port(kb_serio); + + /* TOUCHPAD */ + pad_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!pad_serio) { + error = -ENOMEM; + goto err_pad; + } + pad_serio->id.type = SERIO_8042; + pad_serio->write = olpc_apsp_write; + pad_serio->open = olpc_apsp_open; + pad_serio->close = olpc_apsp_close; + pad_serio->port_data = priv; + pad_serio->dev.parent = &pdev->dev; + strlcpy(pad_serio->name, "sp touchpad", sizeof(pad_serio->name)); + strlcpy(pad_serio->phys, "sp/serio1", sizeof(pad_serio->phys)); + priv->padio = pad_serio; + serio_register_port(pad_serio); + + error = request_irq(priv->irq, olpc_apsp_rx, 0, "olpc-apsp", priv); + if (error) { + dev_err(&pdev->dev, "Failed to request IRQ\n"); + goto err_irq; + } + + priv->dev = &pdev->dev; + device_init_wakeup(priv->dev, 1); + platform_set_drvdata(pdev, priv); + + dev_dbg(&pdev->dev, "probed successfully.\n"); + return 0; + +err_irq: + serio_unregister_port(pad_serio); +err_pad: + serio_unregister_port(kb_serio); + return error; +} + +static int olpc_apsp_remove(struct platform_device *pdev) +{ + struct olpc_apsp *priv = platform_get_drvdata(pdev); + + free_irq(priv->irq, priv); + + serio_unregister_port(priv->kbio); + serio_unregister_port(priv->padio); + + return 0; +} + +static struct of_device_id olpc_apsp_dt_ids[] = { + { .compatible = "olpc,ap-sp", }, + {} +}; +MODULE_DEVICE_TABLE(of, olpc_apsp_dt_ids); + +static struct platform_driver olpc_apsp_driver = { + .probe = olpc_apsp_probe, + .remove = olpc_apsp_remove, + .driver = { + .name = "olpc-apsp", + .owner = THIS_MODULE, + .of_match_table = olpc_apsp_dt_ids, + }, +}; + +MODULE_DESCRIPTION("OLPC AP-SP serio driver"); +MODULE_LICENSE("GPL"); +module_platform_driver(olpc_apsp_driver); diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index 436a3433f8e5..7a65a1bc5226 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -181,7 +181,6 @@ static int q40kbd_remove(struct platform_device *pdev) free_irq(Q40_IRQ_KEYBOARD, q40kbd); kfree(q40kbd); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index 17be85948ffd..4b7662a17ae9 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -349,8 +349,6 @@ static int xps2_of_remove(struct platform_device *of_dev) kfree(drvdata); - platform_set_drvdata(of_dev, NULL); - return 0; } diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c index c7068942ebe8..f7de14a268bf 100644 --- a/drivers/input/touchscreen/88pm860x-ts.c +++ b/drivers/input/touchscreen/88pm860x-ts.c @@ -237,7 +237,7 @@ static int pm860x_touch_probe(struct platform_device *pdev) touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL); if (touch == NULL) return -ENOMEM; - dev_set_drvdata(&pdev->dev, touch); + platform_set_drvdata(pdev, touch); touch->idev = input_allocate_device(); if (touch->idev == NULL) { @@ -299,7 +299,6 @@ static int pm860x_touch_remove(struct platform_device *pdev) input_unregister_device(touch->idev); free_irq(touch->irq, touch); - platform_set_drvdata(pdev, NULL); kfree(touch); return 0; } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f9a5fd89bc02..3b9758b5f4d7 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -167,6 +167,36 @@ config TOUCHSCREEN_CYTTSP_SPI To compile this driver as a module, choose M here: the module will be called cyttsp_spi. +config TOUCHSCREEN_CYTTSP4_CORE + tristate "Cypress TrueTouch Gen4 Touchscreen Driver" + help + Core driver for Cypress TrueTouch(tm) Standard Product + Generation4 touchscreen controllers. + + Say Y here if you have a Cypress Gen4 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here. + +config TOUCHSCREEN_CYTTSP4_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_CYTTSP4_CORE && I2C + help + Say Y here if the touchscreen is connected via I2C bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp4_i2c. + +config TOUCHSCREEN_CYTTSP4_SPI + tristate "support SPI bus connection" + depends on TOUCHSCREEN_CYTTSP4_CORE && SPI_MASTER + help + Say Y here if the touchscreen is connected via SPI bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp4_spi. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X @@ -879,6 +909,7 @@ config TOUCHSCREEN_STMPE config TOUCHSCREEN_TPS6507X tristate "TPS6507x based touchscreens" depends on I2C + select INPUT_POLLDEV help Say Y here if you have a TPS6507x based touchscreen controller. diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 6bfbeab67c9f..f5216c1bf53e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -18,8 +18,11 @@ obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o -obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o cyttsp_i2c_common.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI) += cyttsp4_spi.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c index 2c1e46b7e45b..268a35e55d7f 100644 --- a/drivers/input/touchscreen/atmel-wm97xx.c +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -372,7 +372,6 @@ static int __init atmel_wm97xx_probe(struct platform_device *pdev) err_irq: free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); err: - platform_set_drvdata(pdev, NULL); kfree(atmel_wm97xx); return ret; } @@ -386,7 +385,6 @@ static int __exit atmel_wm97xx_remove(struct platform_device *pdev) free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); del_timer_sync(&atmel_wm97xx->pen_timer); wm97xx_unregister_mach_ops(wm); - platform_set_drvdata(pdev, NULL); kfree(atmel_wm97xx); return 0; diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c index 95f6785a94b0..bddabc595077 100644 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ b/drivers/input/touchscreen/atmel_tsadcc.c @@ -183,10 +183,13 @@ static int atmel_tsadcc_probe(struct platform_device *pdev) struct input_dev *input_dev; struct resource *res; struct at91_tsadcc_data *pdata = pdev->dev.platform_data; - int err = 0; + int err; unsigned int prsc; unsigned int reg; + if (!pdata) + return -EINVAL; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "no mmio resource defined.\n"); @@ -265,9 +268,6 @@ static int atmel_tsadcc_probe(struct platform_device *pdev) prsc = clk_get_rate(ts_dev->clk); dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc); - if (!pdata) - goto err_fail; - if (!pdata->adc_clock) pdata->adc_clock = ADC_DEFAULT_CLOCK; @@ -325,7 +325,7 @@ err_free_mem: static int atmel_tsadcc_remove(struct platform_device *pdev) { - struct atmel_tsadcc *ts_dev = dev_get_drvdata(&pdev->dev); + struct atmel_tsadcc *ts_dev = platform_get_drvdata(pdev); struct resource *res; free_irq(ts_dev->irq, ts_dev); diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c new file mode 100644 index 000000000000..edcf7993034b --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -0,0 +1,2166 @@ +/* + * cyttsp4_core.c + * Cypress TrueTouch(TM) Standard Product V4 Core driver module. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2012 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include "cyttsp4_core.h" +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/sched.h> +#include <linux/slab.h> + +/* Timeout in ms. */ +#define CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT 500 +#define CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT 5000 +#define CY_CORE_MODE_CHANGE_TIMEOUT 1000 +#define CY_CORE_RESET_AND_WAIT_TIMEOUT 500 +#define CY_CORE_WAKEUP_TIMEOUT 500 + +#define CY_CORE_STARTUP_RETRY_COUNT 3 + +static const u8 ldr_exit[] = { + 0xFF, 0x01, 0x3B, 0x00, 0x00, 0x4F, 0x6D, 0x17 +}; + +static const u8 ldr_err_app[] = { + 0x01, 0x02, 0x00, 0x00, 0x55, 0xDD, 0x17 +}; + +static inline size_t merge_bytes(u8 high, u8 low) +{ + return (high << 8) + low; +} + +#ifdef VERBOSE_DEBUG +static void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size, + const char *data_name) +{ + int i, k; + const char fmt[] = "%02X "; + int max; + + if (!size) + return; + + max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED); + + pr_buf[0] = 0; + for (i = k = 0; i < size && k < max; i++, k += 3) + scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]); + + dev_vdbg(dev, "%s: %s[0..%d]=%s%s\n", __func__, data_name, size - 1, + pr_buf, size <= max ? "" : CY_PR_TRUNCATED); +} +#else +#define cyttsp4_pr_buf(dev, pr_buf, dptr, size, data_name) do { } while (0) +#endif + +static int cyttsp4_load_status_regs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + struct device *dev = cd->dev; + int rc; + + rc = cyttsp4_adap_read(cd, CY_REG_BASE, si->si_ofs.mode_size, + si->xy_mode); + if (rc < 0) + dev_err(dev, "%s: fail read mode regs r=%d\n", + __func__, rc); + else + cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_mode, + si->si_ofs.mode_size, "xy_mode"); + + return rc; +} + +static int cyttsp4_handshake(struct cyttsp4 *cd, u8 mode) +{ + u8 cmd = mode ^ CY_HST_TOGGLE; + int rc; + + /* + * Mode change issued, handshaking now will cause endless mode change + * requests, for sync mode modechange will do same with handshake + * */ + if (mode & CY_HST_MODE_CHANGE) + return 0; + + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); + if (rc < 0) + dev_err(cd->dev, "%s: bus write fail on handshake (ret=%d)\n", + __func__, rc); + + return rc; +} + +static int cyttsp4_hw_soft_reset(struct cyttsp4 *cd) +{ + u8 cmd = CY_HST_RESET; + int rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); + if (rc < 0) { + dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n", + __func__); + return rc; + } + return 0; +} + +static int cyttsp4_hw_hard_reset(struct cyttsp4 *cd) +{ + if (cd->cpdata->xres) { + cd->cpdata->xres(cd->cpdata, cd->dev); + dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__); + return 0; + } + dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__); + return -ENOSYS; +} + +static int cyttsp4_hw_reset(struct cyttsp4 *cd) +{ + int rc = cyttsp4_hw_hard_reset(cd); + if (rc == -ENOSYS) + rc = cyttsp4_hw_soft_reset(cd); + return rc; +} + +/* + * Gets number of bits for a touch filed as parameter, + * sets maximum value for field which is used as bit mask + * and returns number of bytes required for that field + */ +static int cyttsp4_bits_2_bytes(unsigned int nbits, size_t *max) +{ + *max = 1UL << nbits; + return (nbits + 7) / 8; +} + +static int cyttsp4_si_data_offsets(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(si->si_data), + &si->si_data); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read sysinfo data offsets r=%d\n", + __func__, rc); + return rc; + } + + /* Print sysinfo data offsets */ + cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)&si->si_data, + sizeof(si->si_data), "sysinfo_data_offsets"); + + /* convert sysinfo data offset bytes into integers */ + + si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, + si->si_data.map_szl); + si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, + si->si_data.map_szl); + si->si_ofs.cydata_ofs = merge_bytes(si->si_data.cydata_ofsh, + si->si_data.cydata_ofsl); + si->si_ofs.test_ofs = merge_bytes(si->si_data.test_ofsh, + si->si_data.test_ofsl); + si->si_ofs.pcfg_ofs = merge_bytes(si->si_data.pcfg_ofsh, + si->si_data.pcfg_ofsl); + si->si_ofs.opcfg_ofs = merge_bytes(si->si_data.opcfg_ofsh, + si->si_data.opcfg_ofsl); + si->si_ofs.ddata_ofs = merge_bytes(si->si_data.ddata_ofsh, + si->si_data.ddata_ofsl); + si->si_ofs.mdata_ofs = merge_bytes(si->si_data.mdata_ofsh, + si->si_data.mdata_ofsl); + return rc; +} + +static int cyttsp4_si_get_cydata(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int read_offset; + int mfgid_sz, calc_mfgid_sz; + void *p; + int rc; + + si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs; + dev_dbg(cd->dev, "%s: cydata size: %Zd\n", __func__, + si->si_ofs.cydata_size); + + p = krealloc(si->si_ptrs.cydata, si->si_ofs.cydata_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc cydata memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.cydata = p; + + read_offset = si->si_ofs.cydata_ofs; + + /* Read the CYDA registers up to MFGID field */ + rc = cyttsp4_adap_read(cd, read_offset, + offsetof(struct cyttsp4_cydata, mfgid_sz) + + sizeof(si->si_ptrs.cydata->mfgid_sz), + si->si_ptrs.cydata); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read cydata r=%d\n", + __func__, rc); + return rc; + } + + /* Check MFGID size */ + mfgid_sz = si->si_ptrs.cydata->mfgid_sz; + calc_mfgid_sz = si->si_ofs.cydata_size - sizeof(struct cyttsp4_cydata); + if (mfgid_sz != calc_mfgid_sz) { + dev_err(cd->dev, "%s: mismatch in MFGID size, reported:%d calculated:%d\n", + __func__, mfgid_sz, calc_mfgid_sz); + return -EINVAL; + } + + read_offset += offsetof(struct cyttsp4_cydata, mfgid_sz) + + sizeof(si->si_ptrs.cydata->mfgid_sz); + + /* Read the CYDA registers for MFGID field */ + rc = cyttsp4_adap_read(cd, read_offset, si->si_ptrs.cydata->mfgid_sz, + si->si_ptrs.cydata->mfg_id); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read cydata r=%d\n", + __func__, rc); + return rc; + } + + read_offset += si->si_ptrs.cydata->mfgid_sz; + + /* Read the rest of the CYDA registers */ + rc = cyttsp4_adap_read(cd, read_offset, + sizeof(struct cyttsp4_cydata) + - offsetof(struct cyttsp4_cydata, cyito_idh), + &si->si_ptrs.cydata->cyito_idh); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read cydata r=%d\n", + __func__, rc); + return rc; + } + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.cydata, + si->si_ofs.cydata_size, "sysinfo_cydata"); + return rc; +} + +static int cyttsp4_si_get_test_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.test_size = si->si_ofs.pcfg_ofs - si->si_ofs.test_ofs; + + p = krealloc(si->si_ptrs.test, si->si_ofs.test_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc test memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.test = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.test_ofs, si->si_ofs.test_size, + si->si_ptrs.test); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read test data r=%d\n", + __func__, rc); + return rc; + } + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.test, si->si_ofs.test_size, + "sysinfo_test_data"); + if (si->si_ptrs.test->post_codel & + CY_POST_CODEL_WDG_RST) + dev_info(cd->dev, "%s: %s codel=%02X\n", + __func__, "Reset was a WATCHDOG RESET", + si->si_ptrs.test->post_codel); + + if (!(si->si_ptrs.test->post_codel & + CY_POST_CODEL_CFG_DATA_CRC_FAIL)) + dev_info(cd->dev, "%s: %s codel=%02X\n", __func__, + "Config Data CRC FAIL", + si->si_ptrs.test->post_codel); + + if (!(si->si_ptrs.test->post_codel & + CY_POST_CODEL_PANEL_TEST_FAIL)) + dev_info(cd->dev, "%s: %s codel=%02X\n", + __func__, "PANEL TEST FAIL", + si->si_ptrs.test->post_codel); + + dev_info(cd->dev, "%s: SCANNING is %s codel=%02X\n", + __func__, si->si_ptrs.test->post_codel & 0x08 ? + "ENABLED" : "DISABLED", + si->si_ptrs.test->post_codel); + return rc; +} + +static int cyttsp4_si_get_pcfg_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.pcfg_size = si->si_ofs.opcfg_ofs - si->si_ofs.pcfg_ofs; + + p = krealloc(si->si_ptrs.pcfg, si->si_ofs.pcfg_size, GFP_KERNEL); + if (p == NULL) { + rc = -ENOMEM; + dev_err(cd->dev, "%s: fail alloc pcfg memory r=%d\n", + __func__, rc); + return rc; + } + si->si_ptrs.pcfg = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size, + si->si_ptrs.pcfg); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read pcfg data r=%d\n", + __func__, rc); + return rc; + } + + si->si_ofs.max_x = merge_bytes((si->si_ptrs.pcfg->res_xh + & CY_PCFG_RESOLUTION_X_MASK), si->si_ptrs.pcfg->res_xl); + si->si_ofs.x_origin = !!(si->si_ptrs.pcfg->res_xh + & CY_PCFG_ORIGIN_X_MASK); + si->si_ofs.max_y = merge_bytes((si->si_ptrs.pcfg->res_yh + & CY_PCFG_RESOLUTION_Y_MASK), si->si_ptrs.pcfg->res_yl); + si->si_ofs.y_origin = !!(si->si_ptrs.pcfg->res_yh + & CY_PCFG_ORIGIN_Y_MASK); + si->si_ofs.max_p = merge_bytes(si->si_ptrs.pcfg->max_zh, + si->si_ptrs.pcfg->max_zl); + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.pcfg, + si->si_ofs.pcfg_size, "sysinfo_pcfg_data"); + return rc; +} + +static int cyttsp4_si_get_opcfg_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + struct cyttsp4_tch_abs_params *tch; + struct cyttsp4_tch_rec_params *tch_old, *tch_new; + enum cyttsp4_tch_abs abs; + int i; + void *p; + int rc; + + si->si_ofs.opcfg_size = si->si_ofs.ddata_ofs - si->si_ofs.opcfg_ofs; + + p = krealloc(si->si_ptrs.opcfg, si->si_ofs.opcfg_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc opcfg memory\n", __func__); + rc = -ENOMEM; + goto cyttsp4_si_get_opcfg_data_exit; + } + si->si_ptrs.opcfg = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size, + si->si_ptrs.opcfg); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read opcfg data r=%d\n", + __func__, rc); + goto cyttsp4_si_get_opcfg_data_exit; + } + si->si_ofs.cmd_ofs = si->si_ptrs.opcfg->cmd_ofs; + si->si_ofs.rep_ofs = si->si_ptrs.opcfg->rep_ofs; + si->si_ofs.rep_sz = (si->si_ptrs.opcfg->rep_szh * 256) + + si->si_ptrs.opcfg->rep_szl; + si->si_ofs.num_btns = si->si_ptrs.opcfg->num_btns; + si->si_ofs.num_btn_regs = (si->si_ofs.num_btns + + CY_NUM_BTN_PER_REG - 1) / CY_NUM_BTN_PER_REG; + si->si_ofs.tt_stat_ofs = si->si_ptrs.opcfg->tt_stat_ofs; + si->si_ofs.obj_cfg0 = si->si_ptrs.opcfg->obj_cfg0; + si->si_ofs.max_tchs = si->si_ptrs.opcfg->max_tchs & + CY_BYTE_OFS_MASK; + si->si_ofs.tch_rec_size = si->si_ptrs.opcfg->tch_rec_size & + CY_BYTE_OFS_MASK; + + /* Get the old touch fields */ + for (abs = CY_TCH_X; abs < CY_NUM_TCH_FIELDS; abs++) { + tch = &si->si_ofs.tch_abs[abs]; + tch_old = &si->si_ptrs.opcfg->tch_rec_old[abs]; + + tch->ofs = tch_old->loc & CY_BYTE_OFS_MASK; + tch->size = cyttsp4_bits_2_bytes(tch_old->size, + &tch->max); + tch->bofs = (tch_old->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; + } + + /* button fields */ + si->si_ofs.btn_rec_size = si->si_ptrs.opcfg->btn_rec_size; + si->si_ofs.btn_diff_ofs = si->si_ptrs.opcfg->btn_diff_ofs; + si->si_ofs.btn_diff_size = si->si_ptrs.opcfg->btn_diff_size; + + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { + /* Get the extended touch fields */ + for (i = 0; i < CY_NUM_EXT_TCH_FIELDS; abs++, i++) { + tch = &si->si_ofs.tch_abs[abs]; + tch_new = &si->si_ptrs.opcfg->tch_rec_new[i]; + + tch->ofs = tch_new->loc & CY_BYTE_OFS_MASK; + tch->size = cyttsp4_bits_2_bytes(tch_new->size, + &tch->max); + tch->bofs = (tch_new->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; + } + } + + for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) { + dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__, + cyttsp4_tch_abs_string[abs]); + dev_dbg(cd->dev, "%s: ofs =%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].ofs); + dev_dbg(cd->dev, "%s: siz =%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].size); + dev_dbg(cd->dev, "%s: max =%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].max); + dev_dbg(cd->dev, "%s: bofs=%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].bofs); + } + + si->si_ofs.mode_size = si->si_ofs.tt_stat_ofs + 1; + si->si_ofs.data_size = si->si_ofs.max_tchs * + si->si_ptrs.opcfg->tch_rec_size; + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.opcfg, + si->si_ofs.opcfg_size, "sysinfo_opcfg_data"); + +cyttsp4_si_get_opcfg_data_exit: + return rc; +} + +static int cyttsp4_si_get_ddata(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.ddata_size = si->si_ofs.mdata_ofs - si->si_ofs.ddata_ofs; + + p = krealloc(si->si_ptrs.ddata, si->si_ofs.ddata_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc ddata memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.ddata = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.ddata_ofs, si->si_ofs.ddata_size, + si->si_ptrs.ddata); + if (rc < 0) + dev_err(cd->dev, "%s: fail read ddata data r=%d\n", + __func__, rc); + else + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.ddata, + si->si_ofs.ddata_size, "sysinfo_ddata"); + return rc; +} + +static int cyttsp4_si_get_mdata(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.mdata_size = si->si_ofs.map_sz - si->si_ofs.mdata_ofs; + + p = krealloc(si->si_ptrs.mdata, si->si_ofs.mdata_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc mdata memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.mdata = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.mdata_ofs, si->si_ofs.mdata_size, + si->si_ptrs.mdata); + if (rc < 0) + dev_err(cd->dev, "%s: fail read mdata data r=%d\n", + __func__, rc); + else + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.mdata, + si->si_ofs.mdata_size, "sysinfo_mdata"); + return rc; +} + +static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int btn; + int num_defined_keys; + u16 *key_table; + void *p; + int rc = 0; + + if (si->si_ofs.num_btns) { + si->si_ofs.btn_keys_size = si->si_ofs.num_btns * + sizeof(struct cyttsp4_btn); + + p = krealloc(si->btn, si->si_ofs.btn_keys_size, + GFP_KERNEL|__GFP_ZERO); + if (p == NULL) { + dev_err(cd->dev, "%s: %s\n", __func__, + "fail alloc btn_keys memory"); + return -ENOMEM; + } + si->btn = p; + + if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL) + num_defined_keys = 0; + else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL) + num_defined_keys = 0; + else + num_defined_keys = cd->cpdata->sett + [CY_IC_GRPNUM_BTN_KEYS]->size; + + for (btn = 0; btn < si->si_ofs.num_btns && + btn < num_defined_keys; btn++) { + key_table = (u16 *)cd->cpdata->sett + [CY_IC_GRPNUM_BTN_KEYS]->data; + si->btn[btn].key_code = key_table[btn]; + si->btn[btn].state = CY_BTN_RELEASED; + si->btn[btn].enabled = true; + } + for (; btn < si->si_ofs.num_btns; btn++) { + si->btn[btn].key_code = KEY_RESERVED; + si->btn[btn].state = CY_BTN_RELEASED; + si->btn[btn].enabled = true; + } + + return rc; + } + + si->si_ofs.btn_keys_size = 0; + kfree(si->btn); + si->btn = NULL; + return rc; +} + +static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + + p = krealloc(si->xy_mode, si->si_ofs.mode_size, GFP_KERNEL|__GFP_ZERO); + if (p == NULL) + return -ENOMEM; + si->xy_mode = p; + + p = krealloc(si->xy_data, si->si_ofs.data_size, GFP_KERNEL|__GFP_ZERO); + if (p == NULL) + return -ENOMEM; + si->xy_data = p; + + p = krealloc(si->btn_rec_data, + si->si_ofs.btn_rec_size * si->si_ofs.num_btns, + GFP_KERNEL|__GFP_ZERO); + if (p == NULL) + return -ENOMEM; + si->btn_rec_data = p; + + return 0; +} + +static void cyttsp4_si_put_log_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + dev_dbg(cd->dev, "%s: cydata_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.cydata_ofs, si->si_ofs.cydata_size); + dev_dbg(cd->dev, "%s: test_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.test_ofs, si->si_ofs.test_size); + dev_dbg(cd->dev, "%s: pcfg_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size); + dev_dbg(cd->dev, "%s: opcfg_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size); + dev_dbg(cd->dev, "%s: ddata_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.ddata_ofs, si->si_ofs.ddata_size); + dev_dbg(cd->dev, "%s: mdata_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.mdata_ofs, si->si_ofs.mdata_size); + + dev_dbg(cd->dev, "%s: cmd_ofs =%4Zd\n", __func__, + si->si_ofs.cmd_ofs); + dev_dbg(cd->dev, "%s: rep_ofs =%4Zd\n", __func__, + si->si_ofs.rep_ofs); + dev_dbg(cd->dev, "%s: rep_sz =%4Zd\n", __func__, + si->si_ofs.rep_sz); + dev_dbg(cd->dev, "%s: num_btns =%4Zd\n", __func__, + si->si_ofs.num_btns); + dev_dbg(cd->dev, "%s: num_btn_regs =%4Zd\n", __func__, + si->si_ofs.num_btn_regs); + dev_dbg(cd->dev, "%s: tt_stat_ofs =%4Zd\n", __func__, + si->si_ofs.tt_stat_ofs); + dev_dbg(cd->dev, "%s: tch_rec_size =%4Zd\n", __func__, + si->si_ofs.tch_rec_size); + dev_dbg(cd->dev, "%s: max_tchs =%4Zd\n", __func__, + si->si_ofs.max_tchs); + dev_dbg(cd->dev, "%s: mode_size =%4Zd\n", __func__, + si->si_ofs.mode_size); + dev_dbg(cd->dev, "%s: data_size =%4Zd\n", __func__, + si->si_ofs.data_size); + dev_dbg(cd->dev, "%s: map_sz =%4Zd\n", __func__, + si->si_ofs.map_sz); + + dev_dbg(cd->dev, "%s: btn_rec_size =%2Zd\n", __func__, + si->si_ofs.btn_rec_size); + dev_dbg(cd->dev, "%s: btn_diff_ofs =%2Zd\n", __func__, + si->si_ofs.btn_diff_ofs); + dev_dbg(cd->dev, "%s: btn_diff_size =%2Zd\n", __func__, + si->si_ofs.btn_diff_size); + + dev_dbg(cd->dev, "%s: max_x = 0x%04ZX (%Zd)\n", __func__, + si->si_ofs.max_x, si->si_ofs.max_x); + dev_dbg(cd->dev, "%s: x_origin = %Zd (%s)\n", __func__, + si->si_ofs.x_origin, + si->si_ofs.x_origin == CY_NORMAL_ORIGIN ? + "left corner" : "right corner"); + dev_dbg(cd->dev, "%s: max_y = 0x%04ZX (%Zd)\n", __func__, + si->si_ofs.max_y, si->si_ofs.max_y); + dev_dbg(cd->dev, "%s: y_origin = %Zd (%s)\n", __func__, + si->si_ofs.y_origin, + si->si_ofs.y_origin == CY_NORMAL_ORIGIN ? + "upper corner" : "lower corner"); + dev_dbg(cd->dev, "%s: max_p = 0x%04ZX (%Zd)\n", __func__, + si->si_ofs.max_p, si->si_ofs.max_p); + + dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__, + si->xy_mode, si->xy_data); +} + +static int cyttsp4_get_sysinfo_regs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int rc; + + rc = cyttsp4_si_data_offsets(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_cydata(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_test_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_pcfg_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_opcfg_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_ddata(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_mdata(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_btn_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_op_data_ptrs(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: failed to get_op_data\n", + __func__); + return rc; + } + + cyttsp4_si_put_log_data(cd); + + /* provide flow control handshake */ + rc = cyttsp4_handshake(cd, si->si_data.hst_mode); + if (rc < 0) + dev_err(cd->dev, "%s: handshake fail on sysinfo reg\n", + __func__); + + si->ready = true; + return rc; +} + +static void cyttsp4_queue_startup_(struct cyttsp4 *cd) +{ + if (cd->startup_state == STARTUP_NONE) { + cd->startup_state = STARTUP_QUEUED; + schedule_work(&cd->startup_work); + dev_dbg(cd->dev, "%s: cyttsp4_startup queued\n", __func__); + } else { + dev_dbg(cd->dev, "%s: startup_state = %d\n", __func__, + cd->startup_state); + } +} + +static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md, + int max_slots) +{ + int t; + + if (md->num_prv_tch == 0) + return; + + for (t = 0; t < max_slots; t++) { + input_mt_slot(md->input, t); + input_mt_report_slot_state(md->input, + MT_TOOL_FINGER, false); + } +} + +static void cyttsp4_lift_all(struct cyttsp4_mt_data *md) +{ + if (!md->si) + return; + + if (md->num_prv_tch != 0) { + cyttsp4_report_slot_liftoff(md, + md->si->si_ofs.tch_abs[CY_TCH_T].max); + input_sync(md->input); + md->num_prv_tch = 0; + } +} + +static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md, + int *axis, int size, int max, u8 *xy_data, int bofs) +{ + int nbyte; + int next; + + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { + dev_vdbg(&md->input->dev, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" + " xy_data[%d]=%02X(%d) bofs=%d\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next], bofs); + *axis = (*axis * 256) + (xy_data[next] >> bofs); + next++; + } + + *axis &= max - 1; + + dev_vdbg(&md->input->dev, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" + " xy_data[%d]=%02X(%d)\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next]); +} + +static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, + struct cyttsp4_touch *touch, u8 *xy_data) +{ + struct device *dev = &md->input->dev; + struct cyttsp4_sysinfo *si = md->si; + enum cyttsp4_tch_abs abs; + int tmp; + bool flipped; + + for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { + cyttsp4_get_touch_axis(md, &touch->abs[abs], + si->si_ofs.tch_abs[abs].size, + si->si_ofs.tch_abs[abs].max, + xy_data + si->si_ofs.tch_abs[abs].ofs, + si->si_ofs.tch_abs[abs].bofs); + dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__, + cyttsp4_tch_abs_string[abs], + touch->abs[abs], touch->abs[abs]); + } + + if (md->pdata->flags & CY_FLAG_FLIP) { + tmp = touch->abs[CY_TCH_X]; + touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y]; + touch->abs[CY_TCH_Y] = tmp; + flipped = true; + } else + flipped = false; + + if (md->pdata->flags & CY_FLAG_INV_X) { + if (flipped) + touch->abs[CY_TCH_X] = md->si->si_ofs.max_y - + touch->abs[CY_TCH_X]; + else + touch->abs[CY_TCH_X] = md->si->si_ofs.max_x - + touch->abs[CY_TCH_X]; + } + if (md->pdata->flags & CY_FLAG_INV_Y) { + if (flipped) + touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x - + touch->abs[CY_TCH_Y]; + else + touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y - + touch->abs[CY_TCH_Y]; + } + + dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n", + __func__, flipped ? "true" : "false", + md->pdata->flags & CY_FLAG_INV_X ? "true" : "false", + md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false", + touch->abs[CY_TCH_X], touch->abs[CY_TCH_X], + touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]); +} + +static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids) +{ + int t; + + for (t = 0; t < max_slots; t++) { + if (ids[t]) + continue; + input_mt_slot(input, t); + input_mt_report_slot_state(input, MT_TOOL_FINGER, false); + } + + input_sync(input); +} + +static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) +{ + struct device *dev = &md->input->dev; + struct cyttsp4_sysinfo *si = md->si; + struct cyttsp4_touch tch; + int sig; + int i, j, t = 0; + int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)]; + + memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int)); + for (i = 0; i < num_cur_tch; i++) { + cyttsp4_get_touch(md, &tch, si->xy_data + + (i * si->si_ofs.tch_rec_size)); + if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) || + (tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) { + dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n", + __func__, i, tch.abs[CY_TCH_T], + md->pdata->frmwrk->abs[(CY_ABS_ID_OST * + CY_NUM_ABS_SET) + CY_MAX_OST]); + continue; + } + + /* use 0 based track id's */ + sig = md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) { + t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]; + if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) { + dev_dbg(dev, "%s: t=%d e=%d lift-off\n", + __func__, t, tch.abs[CY_TCH_E]); + goto cyttsp4_get_mt_touches_pr_tch; + } + input_mt_slot(md->input, t); + input_mt_report_slot_state(md->input, MT_TOOL_FINGER, + true); + ids[t] = true; + } + + /* all devices: position and pressure fields */ + for (j = 0; j <= CY_ABS_W_OST; j++) { + sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) * + CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) + input_report_abs(md->input, sig, + tch.abs[CY_TCH_X + j]); + } + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { + /* + * TMA400 size and orientation fields: + * if pressure is non-zero and major touch + * signal is zero, then set major and minor touch + * signals to minimum non-zero value + */ + if (tch.abs[CY_TCH_P] > 0 && tch.abs[CY_TCH_MAJ] == 0) + tch.abs[CY_TCH_MAJ] = tch.abs[CY_TCH_MIN] = 1; + + /* Get the extended touch fields */ + for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) { + sig = md->pdata->frmwrk->abs + [((CY_ABS_MAJ_OST + j) * + CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) + input_report_abs(md->input, sig, + tch.abs[CY_TCH_MAJ + j]); + } + } + +cyttsp4_get_mt_touches_pr_tch: + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) + dev_dbg(dev, + "%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d\n", + __func__, t, + tch.abs[CY_TCH_X], + tch.abs[CY_TCH_Y], + tch.abs[CY_TCH_P], + tch.abs[CY_TCH_MAJ], + tch.abs[CY_TCH_MIN], + tch.abs[CY_TCH_OR], + tch.abs[CY_TCH_E]); + else + dev_dbg(dev, + "%s: t=%d x=%d y=%d z=%d e=%d\n", __func__, + t, + tch.abs[CY_TCH_X], + tch.abs[CY_TCH_Y], + tch.abs[CY_TCH_P], + tch.abs[CY_TCH_E]); + } + + cyttsp4_final_sync(md->input, si->si_ofs.tch_abs[CY_TCH_T].max, ids); + + md->num_prv_tch = num_cur_tch; + + return; +} + +/* read xy_data for all current touches */ +static int cyttsp4_xy_worker(struct cyttsp4 *cd) +{ + struct cyttsp4_mt_data *md = &cd->md; + struct device *dev = &md->input->dev; + struct cyttsp4_sysinfo *si = md->si; + u8 num_cur_tch; + u8 hst_mode; + u8 rep_len; + u8 rep_stat; + u8 tt_stat; + int rc = 0; + + /* + * Get event data from cyttsp4 device. + * The event data includes all data + * for all active touches. + * Event data also includes button data + */ + /* + * Use 2 reads: + * 1st read to get mode + button bytes + touch count (core) + * 2nd read (optional) to get touch 1 - touch n data + */ + hst_mode = si->xy_mode[CY_REG_BASE]; + rep_len = si->xy_mode[si->si_ofs.rep_ofs]; + rep_stat = si->xy_mode[si->si_ofs.rep_ofs + 1]; + tt_stat = si->xy_mode[si->si_ofs.tt_stat_ofs]; + dev_vdbg(dev, "%s: %s%02X %s%d %s%02X %s%02X\n", __func__, + "hst_mode=", hst_mode, "rep_len=", rep_len, + "rep_stat=", rep_stat, "tt_stat=", tt_stat); + + num_cur_tch = GET_NUM_TOUCHES(tt_stat); + dev_vdbg(dev, "%s: num_cur_tch=%d\n", __func__, num_cur_tch); + + if (rep_len == 0 && num_cur_tch > 0) { + dev_err(dev, "%s: report length error rep_len=%d num_tch=%d\n", + __func__, rep_len, num_cur_tch); + goto cyttsp4_xy_worker_exit; + } + + /* read touches */ + if (num_cur_tch > 0) { + rc = cyttsp4_adap_read(cd, si->si_ofs.tt_stat_ofs + 1, + num_cur_tch * si->si_ofs.tch_rec_size, + si->xy_data); + if (rc < 0) { + dev_err(dev, "%s: read fail on touch regs r=%d\n", + __func__, rc); + goto cyttsp4_xy_worker_exit; + } + } + + /* print xy data */ + cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_data, num_cur_tch * + si->si_ofs.tch_rec_size, "xy_data"); + + /* check any error conditions */ + if (IS_BAD_PKT(rep_stat)) { + dev_dbg(dev, "%s: Invalid buffer detected\n", __func__); + rc = 0; + goto cyttsp4_xy_worker_exit; + } + + if (IS_LARGE_AREA(tt_stat)) + dev_dbg(dev, "%s: Large area detected\n", __func__); + + if (num_cur_tch > si->si_ofs.max_tchs) { + dev_err(dev, "%s: too many tch; set to max tch (n=%d c=%Zd)\n", + __func__, num_cur_tch, si->si_ofs.max_tchs); + num_cur_tch = si->si_ofs.max_tchs; + } + + /* extract xy_data for all currently reported touches */ + dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__, + num_cur_tch); + if (num_cur_tch) + cyttsp4_get_mt_touches(md, num_cur_tch); + else + cyttsp4_lift_all(md); + + rc = 0; + +cyttsp4_xy_worker_exit: + return rc; +} + +static int cyttsp4_mt_attention(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + struct cyttsp4_mt_data *md = &cd->md; + int rc = 0; + + if (!md->si) + return 0; + + mutex_lock(&md->report_lock); + if (!md->is_suspended) { + /* core handles handshake */ + rc = cyttsp4_xy_worker(cd); + } else { + dev_vdbg(dev, "%s: Ignoring report while suspended\n", + __func__); + } + mutex_unlock(&md->report_lock); + if (rc < 0) + dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +static irqreturn_t cyttsp4_irq(int irq, void *handle) +{ + struct cyttsp4 *cd = handle; + struct device *dev = cd->dev; + enum cyttsp4_mode cur_mode; + u8 cmd_ofs = cd->sysinfo.si_ofs.cmd_ofs; + u8 mode[3]; + int rc; + + /* + * Check whether this IRQ should be ignored (external) + * This should be the very first thing to check since + * ignore_irq may be set for a very short period of time + */ + if (atomic_read(&cd->ignore_irq)) { + dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); + return IRQ_HANDLED; + } + + dev_dbg(dev, "%s int:0x%x\n", __func__, cd->int_status); + + mutex_lock(&cd->system_lock); + + /* Just to debug */ + if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) + dev_vdbg(dev, "%s: Received IRQ while in sleep\n", __func__); + + rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), mode); + if (rc) { + dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); + goto cyttsp4_irq_exit; + } + dev_vdbg(dev, "%s mode[0-2]:0x%X 0x%X 0x%X\n", __func__, + mode[0], mode[1], mode[2]); + + if (IS_BOOTLOADER(mode[0], mode[1])) { + cur_mode = CY_MODE_BOOTLOADER; + dev_vdbg(dev, "%s: bl running\n", __func__); + if (cd->mode == CY_MODE_BOOTLOADER) { + /* Signal bootloader heartbeat heard */ + wake_up(&cd->wait_q); + goto cyttsp4_irq_exit; + } + + /* switch to bootloader */ + dev_dbg(dev, "%s: restart switch to bl m=%d -> m=%d\n", + __func__, cd->mode, cur_mode); + + /* catch operation->bl glitch */ + if (cd->mode != CY_MODE_UNKNOWN) { + /* Incase startup_state do not let startup_() */ + cd->mode = CY_MODE_UNKNOWN; + cyttsp4_queue_startup_(cd); + goto cyttsp4_irq_exit; + } + + /* + * do not wake thread on this switch since + * it is possible to get an early heartbeat + * prior to performing the reset + */ + cd->mode = cur_mode; + + goto cyttsp4_irq_exit; + } + + switch (mode[0] & CY_HST_MODE) { + case CY_HST_OPERATE: + cur_mode = CY_MODE_OPERATIONAL; + dev_vdbg(dev, "%s: operational\n", __func__); + break; + case CY_HST_CAT: + cur_mode = CY_MODE_CAT; + dev_vdbg(dev, "%s: CaT\n", __func__); + break; + case CY_HST_SYSINFO: + cur_mode = CY_MODE_SYSINFO; + dev_vdbg(dev, "%s: sysinfo\n", __func__); + break; + default: + cur_mode = CY_MODE_UNKNOWN; + dev_err(dev, "%s: unknown HST mode 0x%02X\n", __func__, + mode[0]); + break; + } + + /* Check whether this IRQ should be ignored (internal) */ + if (cd->int_status & CY_INT_IGNORE) { + dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); + goto cyttsp4_irq_exit; + } + + /* Check for wake up interrupt */ + if (cd->int_status & CY_INT_AWAKE) { + cd->int_status &= ~CY_INT_AWAKE; + wake_up(&cd->wait_q); + dev_vdbg(dev, "%s: Received wake up interrupt\n", __func__); + goto cyttsp4_irq_handshake; + } + + /* Expecting mode change interrupt */ + if ((cd->int_status & CY_INT_MODE_CHANGE) + && (mode[0] & CY_HST_MODE_CHANGE) == 0) { + cd->int_status &= ~CY_INT_MODE_CHANGE; + dev_dbg(dev, "%s: finish mode switch m=%d -> m=%d\n", + __func__, cd->mode, cur_mode); + cd->mode = cur_mode; + wake_up(&cd->wait_q); + goto cyttsp4_irq_handshake; + } + + /* compare current core mode to current device mode */ + dev_vdbg(dev, "%s: cd->mode=%d cur_mode=%d\n", + __func__, cd->mode, cur_mode); + if ((mode[0] & CY_HST_MODE_CHANGE) == 0 && cd->mode != cur_mode) { + /* Unexpected mode change occurred */ + dev_err(dev, "%s %d->%d 0x%x\n", __func__, cd->mode, + cur_mode, cd->int_status); + dev_dbg(dev, "%s: Unexpected mode change, startup\n", + __func__); + cyttsp4_queue_startup_(cd); + goto cyttsp4_irq_exit; + } + + /* Expecting command complete interrupt */ + dev_vdbg(dev, "%s: command byte:0x%x\n", __func__, mode[cmd_ofs]); + if ((cd->int_status & CY_INT_EXEC_CMD) + && mode[cmd_ofs] & CY_CMD_COMPLETE) { + cd->int_status &= ~CY_INT_EXEC_CMD; + dev_vdbg(dev, "%s: Received command complete interrupt\n", + __func__); + wake_up(&cd->wait_q); + /* + * It is possible to receive a single interrupt for + * command complete and touch/button status report. + * Continue processing for a possible status report. + */ + } + + /* This should be status report, read status regs */ + if (cd->mode == CY_MODE_OPERATIONAL) { + dev_vdbg(dev, "%s: Read status registers\n", __func__); + rc = cyttsp4_load_status_regs(cd); + if (rc < 0) + dev_err(dev, "%s: fail read mode regs r=%d\n", + __func__, rc); + } + + cyttsp4_mt_attention(cd); + +cyttsp4_irq_handshake: + /* handshake the event */ + dev_vdbg(dev, "%s: Handshake mode=0x%02X r=%d\n", + __func__, mode[0], rc); + rc = cyttsp4_handshake(cd, mode[0]); + if (rc < 0) + dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n", + __func__, mode[0], rc); + + /* + * a non-zero udelay period is required for using + * IRQF_TRIGGER_LOW in order to delay until the + * device completes isr deassert + */ + udelay(cd->cpdata->level_irq_udelay); + +cyttsp4_irq_exit: + mutex_unlock(&cd->system_lock); + return IRQ_HANDLED; +} + +static void cyttsp4_start_wd_timer(struct cyttsp4 *cd) +{ + if (!CY_WATCHDOG_TIMEOUT) + return; + + mod_timer(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(CY_WATCHDOG_TIMEOUT)); +} + +static void cyttsp4_stop_wd_timer(struct cyttsp4 *cd) +{ + if (!CY_WATCHDOG_TIMEOUT) + return; + + /* + * Ensure we wait until the watchdog timer + * running on a different CPU finishes + */ + del_timer_sync(&cd->watchdog_timer); + cancel_work_sync(&cd->watchdog_work); + del_timer_sync(&cd->watchdog_timer); +} + +static void cyttsp4_watchdog_timer(unsigned long handle) +{ + struct cyttsp4 *cd = (struct cyttsp4 *)handle; + + dev_vdbg(cd->dev, "%s: Watchdog timer triggered\n", __func__); + + if (!cd) + return; + + if (!work_pending(&cd->watchdog_work)) + schedule_work(&cd->watchdog_work); + + return; +} + +static int cyttsp4_request_exclusive(struct cyttsp4 *cd, void *ownptr, + int timeout_ms) +{ + int t = msecs_to_jiffies(timeout_ms); + bool with_timeout = (timeout_ms != 0); + + mutex_lock(&cd->system_lock); + if (!cd->exclusive_dev && cd->exclusive_waits == 0) { + cd->exclusive_dev = ownptr; + goto exit; + } + + cd->exclusive_waits++; +wait: + mutex_unlock(&cd->system_lock); + if (with_timeout) { + t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting exclusive access\n", + __func__); + mutex_lock(&cd->system_lock); + cd->exclusive_waits--; + mutex_unlock(&cd->system_lock); + return -ETIME; + } + } else { + wait_event(cd->wait_q, !cd->exclusive_dev); + } + mutex_lock(&cd->system_lock); + if (cd->exclusive_dev) + goto wait; + cd->exclusive_dev = ownptr; + cd->exclusive_waits--; +exit: + mutex_unlock(&cd->system_lock); + + return 0; +} + +/* + * returns error if was not owned + */ +static int cyttsp4_release_exclusive(struct cyttsp4 *cd, void *ownptr) +{ + mutex_lock(&cd->system_lock); + if (cd->exclusive_dev != ownptr) { + mutex_unlock(&cd->system_lock); + return -EINVAL; + } + + dev_vdbg(cd->dev, "%s: exclusive_dev %p freed\n", + __func__, cd->exclusive_dev); + cd->exclusive_dev = NULL; + wake_up(&cd->wait_q); + mutex_unlock(&cd->system_lock); + return 0; +} + +static int cyttsp4_wait_bl_heartbeat(struct cyttsp4 *cd) +{ + long t; + int rc = 0; + + /* wait heartbeat */ + dev_vdbg(cd->dev, "%s: wait heartbeat...\n", __func__); + t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_BOOTLOADER, + msecs_to_jiffies(CY_CORE_RESET_AND_WAIT_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting bl heartbeat cd->mode=%d\n", + __func__, cd->mode); + rc = -ETIME; + } + + return rc; +} + +static int cyttsp4_wait_sysinfo_mode(struct cyttsp4 *cd) +{ + long t; + + dev_vdbg(cd->dev, "%s: wait sysinfo...\n", __func__); + + t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_SYSINFO, + msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting exit bl cd->mode=%d\n", + __func__, cd->mode); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_MODE_CHANGE; + mutex_unlock(&cd->system_lock); + return -ETIME; + } + + return 0; +} + +static int cyttsp4_reset_and_wait(struct cyttsp4 *cd) +{ + int rc; + + /* reset hardware */ + mutex_lock(&cd->system_lock); + dev_dbg(cd->dev, "%s: reset hw...\n", __func__); + rc = cyttsp4_hw_reset(cd); + cd->mode = CY_MODE_UNKNOWN; + mutex_unlock(&cd->system_lock); + if (rc < 0) { + dev_err(cd->dev, "%s:Fail hw reset r=%d\n", __func__, rc); + return rc; + } + + return cyttsp4_wait_bl_heartbeat(cd); +} + +/* + * returns err if refused or timeout; block until mode change complete + * bit is set (mode change interrupt) + */ +static int cyttsp4_set_mode(struct cyttsp4 *cd, int new_mode) +{ + u8 new_dev_mode; + u8 mode; + long t; + int rc; + + switch (new_mode) { + case CY_MODE_OPERATIONAL: + new_dev_mode = CY_HST_OPERATE; + break; + case CY_MODE_SYSINFO: + new_dev_mode = CY_HST_SYSINFO; + break; + case CY_MODE_CAT: + new_dev_mode = CY_HST_CAT; + break; + default: + dev_err(cd->dev, "%s: invalid mode: %02X(%d)\n", + __func__, new_mode, new_mode); + return -EINVAL; + } + + /* change mode */ + dev_dbg(cd->dev, "%s: %s=%p new_dev_mode=%02X new_mode=%d\n", + __func__, "have exclusive", cd->exclusive_dev, + new_dev_mode, new_mode); + + mutex_lock(&cd->system_lock); + rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); + if (rc < 0) { + mutex_unlock(&cd->system_lock); + dev_err(cd->dev, "%s: Fail read mode r=%d\n", + __func__, rc); + goto exit; + } + + /* Clear device mode bits and set to new mode */ + mode &= ~CY_HST_MODE; + mode |= new_dev_mode | CY_HST_MODE_CHANGE; + + cd->int_status |= CY_INT_MODE_CHANGE; + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode), &mode); + mutex_unlock(&cd->system_lock); + if (rc < 0) { + dev_err(cd->dev, "%s: Fail write mode change r=%d\n", + __func__, rc); + goto exit; + } + + /* wait for mode change done interrupt */ + t = wait_event_timeout(cd->wait_q, + (cd->int_status & CY_INT_MODE_CHANGE) == 0, + msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); + dev_dbg(cd->dev, "%s: back from wait t=%ld cd->mode=%d\n", + __func__, t, cd->mode); + + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: %s\n", __func__, + "tmo waiting mode change"); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_MODE_CHANGE; + mutex_unlock(&cd->system_lock); + rc = -EINVAL; + } + +exit: + return rc; +} + +static void cyttsp4_watchdog_work(struct work_struct *work) +{ + struct cyttsp4 *cd = + container_of(work, struct cyttsp4, watchdog_work); + u8 *mode; + int retval; + + mutex_lock(&cd->system_lock); + retval = cyttsp4_load_status_regs(cd); + if (retval < 0) { + dev_err(cd->dev, + "%s: failed to access device in watchdog timer r=%d\n", + __func__, retval); + cyttsp4_queue_startup_(cd); + goto cyttsp4_timer_watchdog_exit_error; + } + mode = &cd->sysinfo.xy_mode[CY_REG_BASE]; + if (IS_BOOTLOADER(mode[0], mode[1])) { + dev_err(cd->dev, + "%s: device found in bootloader mode when operational mode\n", + __func__); + cyttsp4_queue_startup_(cd); + goto cyttsp4_timer_watchdog_exit_error; + } + + cyttsp4_start_wd_timer(cd); +cyttsp4_timer_watchdog_exit_error: + mutex_unlock(&cd->system_lock); + return; +} + +static int cyttsp4_core_sleep_(struct cyttsp4 *cd) +{ + enum cyttsp4_sleep_state ss = SS_SLEEP_ON; + enum cyttsp4_int_state int_status = CY_INT_IGNORE; + int rc = 0; + u8 mode[2]; + + /* Already in sleep mode? */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) { + mutex_unlock(&cd->system_lock); + return 0; + } + cd->sleep_state = SS_SLEEPING; + mutex_unlock(&cd->system_lock); + + cyttsp4_stop_wd_timer(cd); + + /* Wait until currently running IRQ handler exits and disable IRQ */ + disable_irq(cd->irq); + + dev_vdbg(cd->dev, "%s: write DEEP SLEEP...\n", __func__); + mutex_lock(&cd->system_lock); + rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); + if (rc) { + mutex_unlock(&cd->system_lock); + dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); + goto error; + } + + if (IS_BOOTLOADER(mode[0], mode[1])) { + mutex_unlock(&cd->system_lock); + dev_err(cd->dev, "%s: Device in BOOTLADER mode.\n", __func__); + rc = -EINVAL; + goto error; + } + + mode[0] |= CY_HST_SLEEP; + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode[0]), &mode[0]); + mutex_unlock(&cd->system_lock); + if (rc) { + dev_err(cd->dev, "%s: Fail write adapter r=%d\n", __func__, rc); + goto error; + } + dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__); + + if (cd->cpdata->power) { + dev_dbg(cd->dev, "%s: Power down HW\n", __func__); + rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, &cd->ignore_irq); + } else { + dev_dbg(cd->dev, "%s: No power function\n", __func__); + rc = 0; + } + if (rc < 0) { + dev_err(cd->dev, "%s: HW Power down fails r=%d\n", + __func__, rc); + goto error; + } + + /* Give time to FW to sleep */ + msleep(50); + + goto exit; + +error: + ss = SS_SLEEP_OFF; + int_status = CY_INT_NONE; + cyttsp4_start_wd_timer(cd); + +exit: + mutex_lock(&cd->system_lock); + cd->sleep_state = ss; + cd->int_status |= int_status; + mutex_unlock(&cd->system_lock); + enable_irq(cd->irq); + return rc; +} + +static int cyttsp4_core_sleep(struct cyttsp4 *cd) +{ + int rc; + + rc = cyttsp4_request_exclusive(cd, cd->dev, + CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return 0; + } + + rc = cyttsp4_core_sleep_(cd); + + if (cyttsp4_release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + + return rc; +} + +static int cyttsp4_core_wake_(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + int rc; + u8 mode; + int t; + + /* Already woken? */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_OFF) { + mutex_unlock(&cd->system_lock); + return 0; + } + cd->int_status &= ~CY_INT_IGNORE; + cd->int_status |= CY_INT_AWAKE; + cd->sleep_state = SS_WAKING; + + if (cd->cpdata->power) { + dev_dbg(dev, "%s: Power up HW\n", __func__); + rc = cd->cpdata->power(cd->cpdata, 1, dev, &cd->ignore_irq); + } else { + dev_dbg(dev, "%s: No power function\n", __func__); + rc = -ENOSYS; + } + if (rc < 0) { + dev_err(dev, "%s: HW Power up fails r=%d\n", + __func__, rc); + + /* Initiate a read transaction to wake up */ + cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); + } else + dev_vdbg(cd->dev, "%s: HW power up succeeds\n", + __func__); + mutex_unlock(&cd->system_lock); + + t = wait_event_timeout(cd->wait_q, + (cd->int_status & CY_INT_AWAKE) == 0, + msecs_to_jiffies(CY_CORE_WAKEUP_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(dev, "%s: TMO waiting for wakeup\n", __func__); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_AWAKE; + /* Try starting up */ + cyttsp4_queue_startup_(cd); + mutex_unlock(&cd->system_lock); + } + + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_SLEEP_OFF; + mutex_unlock(&cd->system_lock); + + cyttsp4_start_wd_timer(cd); + + return 0; +} + +static int cyttsp4_core_wake(struct cyttsp4 *cd) +{ + int rc; + + rc = cyttsp4_request_exclusive(cd, cd->dev, + CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return 0; + } + + rc = cyttsp4_core_wake_(cd); + + if (cyttsp4_release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + + return rc; +} + +static int cyttsp4_startup_(struct cyttsp4 *cd) +{ + int retry = CY_CORE_STARTUP_RETRY_COUNT; + int rc; + + cyttsp4_stop_wd_timer(cd); + +reset: + if (retry != CY_CORE_STARTUP_RETRY_COUNT) + dev_dbg(cd->dev, "%s: Retry %d\n", __func__, + CY_CORE_STARTUP_RETRY_COUNT - retry); + + /* reset hardware and wait for heartbeat */ + rc = cyttsp4_reset_and_wait(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + /* exit bl into sysinfo mode */ + dev_vdbg(cd->dev, "%s: write exit ldr...\n", __func__); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_IGNORE; + cd->int_status |= CY_INT_MODE_CHANGE; + + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(ldr_exit), + (u8 *)ldr_exit); + mutex_unlock(&cd->system_lock); + if (rc < 0) { + dev_err(cd->dev, "%s: Fail write r=%d\n", __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + rc = cyttsp4_wait_sysinfo_mode(cd); + if (rc < 0) { + u8 buf[sizeof(ldr_err_app)]; + int rc1; + + /* Check for invalid/corrupted touch application */ + rc1 = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(ldr_err_app), + buf); + if (rc1) { + dev_err(cd->dev, "%s: Fail read r=%d\n", __func__, rc1); + } else if (!memcmp(buf, ldr_err_app, sizeof(ldr_err_app))) { + dev_err(cd->dev, "%s: Error launching touch application\n", + __func__); + mutex_lock(&cd->system_lock); + cd->invalid_touch_app = true; + mutex_unlock(&cd->system_lock); + goto exit_no_wd; + } + + if (retry--) + goto reset; + goto exit; + } + + mutex_lock(&cd->system_lock); + cd->invalid_touch_app = false; + mutex_unlock(&cd->system_lock); + + /* read sysinfo data */ + dev_vdbg(cd->dev, "%s: get sysinfo regs..\n", __func__); + rc = cyttsp4_get_sysinfo_regs(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: failed to get sysinfo regs rc=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + rc = cyttsp4_set_mode(cd, CY_MODE_OPERATIONAL); + if (rc < 0) { + dev_err(cd->dev, "%s: failed to set mode to operational rc=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + cyttsp4_lift_all(&cd->md); + + /* restore to sleep if was suspended */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) { + cd->sleep_state = SS_SLEEP_OFF; + mutex_unlock(&cd->system_lock); + cyttsp4_core_sleep_(cd); + goto exit_no_wd; + } + mutex_unlock(&cd->system_lock); + +exit: + cyttsp4_start_wd_timer(cd); +exit_no_wd: + return rc; +} + +static int cyttsp4_startup(struct cyttsp4 *cd) +{ + int rc; + + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_RUNNING; + mutex_unlock(&cd->system_lock); + + rc = cyttsp4_request_exclusive(cd, cd->dev, + CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + goto exit; + } + + rc = cyttsp4_startup_(cd); + + if (cyttsp4_release_exclusive(cd, cd->dev) < 0) + /* Don't return fail code, mode is already changed. */ + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + +exit: + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_NONE; + mutex_unlock(&cd->system_lock); + + /* Wake the waiters for end of startup */ + wake_up(&cd->wait_q); + + return rc; +} + +static void cyttsp4_startup_work_function(struct work_struct *work) +{ + struct cyttsp4 *cd = container_of(work, struct cyttsp4, startup_work); + int rc; + + rc = cyttsp4_startup(cd); + if (rc < 0) + dev_err(cd->dev, "%s: Fail queued startup r=%d\n", + __func__, rc); +} + +static void cyttsp4_free_si_ptrs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + + if (!si) + return; + + kfree(si->si_ptrs.cydata); + kfree(si->si_ptrs.test); + kfree(si->si_ptrs.pcfg); + kfree(si->si_ptrs.opcfg); + kfree(si->si_ptrs.ddata); + kfree(si->si_ptrs.mdata); + kfree(si->btn); + kfree(si->xy_mode); + kfree(si->xy_data); + kfree(si->btn_rec_data); +} + +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME) +static int cyttsp4_core_suspend(struct device *dev) +{ + struct cyttsp4 *cd = dev_get_drvdata(dev); + struct cyttsp4_mt_data *md = &cd->md; + int rc; + + md->is_suspended = true; + + rc = cyttsp4_core_sleep(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on sleep\n", __func__); + return -EAGAIN; + } + return 0; +} + +static int cyttsp4_core_resume(struct device *dev) +{ + struct cyttsp4 *cd = dev_get_drvdata(dev); + struct cyttsp4_mt_data *md = &cd->md; + int rc; + + md->is_suspended = false; + + rc = cyttsp4_core_wake(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on wake\n", __func__); + return -EAGAIN; + } + + return 0; +} +#endif + +const struct dev_pm_ops cyttsp4_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume) + SET_RUNTIME_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume, NULL) +}; +EXPORT_SYMBOL_GPL(cyttsp4_pm_ops); + +static int cyttsp4_mt_open(struct input_dev *input) +{ + pm_runtime_get(input->dev.parent); + return 0; +} + +static void cyttsp4_mt_close(struct input_dev *input) +{ + struct cyttsp4_mt_data *md = input_get_drvdata(input); + mutex_lock(&md->report_lock); + if (!md->is_suspended) + pm_runtime_put(input->dev.parent); + mutex_unlock(&md->report_lock); +} + + +static int cyttsp4_setup_input_device(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + struct cyttsp4_mt_data *md = &cd->md; + int signal = CY_IGNORE_VALUE; + int max_x, max_y, max_p, min, max; + int max_x_tmp, max_y_tmp; + int i; + int rc; + + dev_vdbg(dev, "%s: Initialize event signals\n", __func__); + __set_bit(EV_ABS, md->input->evbit); + __set_bit(EV_REL, md->input->evbit); + __set_bit(EV_KEY, md->input->evbit); + + max_x_tmp = md->si->si_ofs.max_x; + max_y_tmp = md->si->si_ofs.max_y; + + /* get maximum values from the sysinfo data */ + if (md->pdata->flags & CY_FLAG_FLIP) { + max_x = max_y_tmp - 1; + max_y = max_x_tmp - 1; + } else { + max_x = max_x_tmp - 1; + max_y = max_y_tmp - 1; + } + max_p = md->si->si_ofs.max_p; + + /* set event signal capabilities */ + for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) { + signal = md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST]; + if (signal != CY_IGNORE_VALUE) { + __set_bit(signal, md->input->absbit); + min = md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_MIN_OST]; + max = md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_MAX_OST]; + if (i == CY_ABS_ID_OST) { + /* shift track ids down to start at 0 */ + max = max - min; + min = min - min; + } else if (i == CY_ABS_X_OST) + max = max_x; + else if (i == CY_ABS_Y_OST) + max = max_y; + else if (i == CY_ABS_P_OST) + max = max_p; + input_set_abs_params(md->input, signal, min, max, + md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_FUZZ_OST], + md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_FLAT_OST]); + dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n", + __func__, signal, min, max); + if ((i == CY_ABS_ID_OST) && + (md->si->si_ofs.tch_rec_size < + CY_TMA4XX_TCH_REC_SIZE)) + break; + } + } + + input_mt_init_slots(md->input, md->si->si_ofs.tch_abs[CY_TCH_T].max, + INPUT_MT_DIRECT); + rc = input_register_device(md->input); + if (rc < 0) + dev_err(dev, "%s: Error, failed register input device r=%d\n", + __func__, rc); + return rc; +} + +static int cyttsp4_mt_probe(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + struct cyttsp4_mt_data *md = &cd->md; + struct cyttsp4_mt_platform_data *pdata = cd->pdata->mt_pdata; + int rc = 0; + + mutex_init(&md->report_lock); + md->pdata = pdata; + /* Create the input device and register it. */ + dev_vdbg(dev, "%s: Create the input device and register it\n", + __func__); + md->input = input_allocate_device(); + if (md->input == NULL) { + dev_err(dev, "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENOSYS; + goto error_alloc_failed; + } + + md->input->name = pdata->inp_dev_name; + scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev)); + md->input->phys = md->phys; + md->input->id.bustype = cd->bus_ops->bustype; + md->input->dev.parent = dev; + md->input->open = cyttsp4_mt_open; + md->input->close = cyttsp4_mt_close; + input_set_drvdata(md->input, md); + + /* get sysinfo */ + md->si = &cd->sysinfo; + if (!md->si) { + dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, md->si); + goto error_get_sysinfo; + } + + rc = cyttsp4_setup_input_device(cd); + if (rc) + goto error_init_input; + + return 0; + +error_init_input: + input_free_device(md->input); +error_get_sysinfo: + input_set_drvdata(md->input, NULL); +error_alloc_failed: + dev_err(dev, "%s failed.\n", __func__); + return rc; +} + +struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + struct device *dev, u16 irq, size_t xfer_buf_size) +{ + struct cyttsp4 *cd; + struct cyttsp4_platform_data *pdata = dev_get_platdata(dev); + unsigned long irq_flags; + int rc = 0; + + if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { + dev_err(dev, "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + + cd = kzalloc(sizeof(*cd), GFP_KERNEL); + if (!cd) { + dev_err(dev, "%s: Error, kzalloc\n", __func__); + rc = -ENOMEM; + goto error_alloc_data; + } + + cd->xfer_buf = kzalloc(xfer_buf_size, GFP_KERNEL); + if (!cd->xfer_buf) { + dev_err(dev, "%s: Error, kzalloc\n", __func__); + rc = -ENOMEM; + goto error_free_cd; + } + + /* Initialize device info */ + cd->dev = dev; + cd->pdata = pdata; + cd->cpdata = pdata->core_pdata; + cd->bus_ops = ops; + + /* Initialize mutexes and spinlocks */ + mutex_init(&cd->system_lock); + mutex_init(&cd->adap_lock); + + /* Initialize wait queue */ + init_waitqueue_head(&cd->wait_q); + + /* Initialize works */ + INIT_WORK(&cd->startup_work, cyttsp4_startup_work_function); + INIT_WORK(&cd->watchdog_work, cyttsp4_watchdog_work); + + /* Initialize IRQ */ + cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); + if (cd->irq < 0) { + rc = -EINVAL; + goto error_free_xfer; + } + + dev_set_drvdata(dev, cd); + + /* Call platform init function */ + if (cd->cpdata->init) { + dev_dbg(cd->dev, "%s: Init HW\n", __func__); + rc = cd->cpdata->init(cd->cpdata, 1, cd->dev); + } else { + dev_dbg(cd->dev, "%s: No HW INIT function\n", __func__); + rc = 0; + } + if (rc < 0) + dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc); + + dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq); + if (cd->cpdata->level_irq_udelay > 0) + /* use level triggered interrupts */ + irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + else + /* use edge triggered interrupts */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + + rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags, + dev_name(dev), cd); + if (rc < 0) { + dev_err(dev, "%s: Error, could not request irq\n", __func__); + goto error_request_irq; + } + + /* Setup watchdog timer */ + setup_timer(&cd->watchdog_timer, cyttsp4_watchdog_timer, + (unsigned long)cd); + + /* + * call startup directly to ensure that the device + * is tested before leaving the probe + */ + rc = cyttsp4_startup(cd); + + /* Do not fail probe if startup fails but the device is detected */ + if (rc < 0 && cd->mode == CY_MODE_UNKNOWN) { + dev_err(cd->dev, "%s: Fail initial startup r=%d\n", + __func__, rc); + goto error_startup; + } + + rc = cyttsp4_mt_probe(cd); + if (rc < 0) { + dev_err(dev, "%s: Error, fail mt probe\n", __func__); + goto error_startup; + } + + pm_runtime_enable(dev); + + return cd; + +error_startup: + cancel_work_sync(&cd->startup_work); + cyttsp4_stop_wd_timer(cd); + pm_runtime_disable(dev); + cyttsp4_free_si_ptrs(cd); + free_irq(cd->irq, cd); +error_request_irq: + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, 0, dev); + dev_set_drvdata(dev, NULL); +error_free_xfer: + kfree(cd->xfer_buf); +error_free_cd: + kfree(cd); +error_alloc_data: +error_no_pdata: + dev_err(dev, "%s failed.\n", __func__); + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(cyttsp4_probe); + +static void cyttsp4_mt_release(struct cyttsp4_mt_data *md) +{ + input_unregister_device(md->input); + input_set_drvdata(md->input, NULL); +} + +int cyttsp4_remove(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + + cyttsp4_mt_release(&cd->md); + + /* + * Suspend the device before freeing the startup_work and stopping + * the watchdog since sleep function restarts watchdog on failure + */ + pm_runtime_suspend(dev); + pm_runtime_disable(dev); + + cancel_work_sync(&cd->startup_work); + + cyttsp4_stop_wd_timer(cd); + + free_irq(cd->irq, cd); + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, 0, dev); + dev_set_drvdata(dev, NULL); + cyttsp4_free_si_ptrs(cd); + kfree(cd); + return 0; +} +EXPORT_SYMBOL_GPL(cyttsp4_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen core driver"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h new file mode 100644 index 000000000000..86a254354136 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_core.h @@ -0,0 +1,472 @@ +/* + * cyttsp4_core.h + * Cypress TrueTouch(TM) Standard Product V4 Core driver module. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2012 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#ifndef _LINUX_CYTTSP4_CORE_H +#define _LINUX_CYTTSP4_CORE_H + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/stringify.h> +#include <linux/types.h> +#include <linux/platform_data/cyttsp4.h> + +#define CY_REG_BASE 0x00 + +#define CY_POST_CODEL_WDG_RST 0x01 +#define CY_POST_CODEL_CFG_DATA_CRC_FAIL 0x02 +#define CY_POST_CODEL_PANEL_TEST_FAIL 0x04 + +#define CY_NUM_BTN_PER_REG 4 + +/* touch record system information offset masks and shifts */ +#define CY_BYTE_OFS_MASK 0x1F +#define CY_BOFS_MASK 0xE0 +#define CY_BOFS_SHIFT 5 + +#define CY_TMA1036_TCH_REC_SIZE 6 +#define CY_TMA4XX_TCH_REC_SIZE 9 +#define CY_TMA1036_MAX_TCH 0x0E +#define CY_TMA4XX_MAX_TCH 0x1E + +#define CY_NORMAL_ORIGIN 0 /* upper, left corner */ +#define CY_INVERT_ORIGIN 1 /* lower, right corner */ + +/* helpers */ +#define GET_NUM_TOUCHES(x) ((x) & 0x1F) +#define IS_LARGE_AREA(x) ((x) & 0x20) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define IS_BOOTLOADER(hst_mode, reset_detect) \ + ((hst_mode) & 0x01 || (reset_detect) != 0) +#define IS_TMO(t) ((t) == 0) + + +enum cyttsp_cmd_bits { + CY_CMD_COMPLETE = (1 << 6), +}; + +/* Timeout in ms. */ +#define CY_WATCHDOG_TIMEOUT 1000 + +#define CY_MAX_PRINT_SIZE 512 +#ifdef VERBOSE_DEBUG +#define CY_MAX_PRBUF_SIZE PIPE_BUF +#define CY_PR_TRUNCATED " truncated..." +#endif + +enum cyttsp4_ic_grpnum { + CY_IC_GRPNUM_RESERVED, + CY_IC_GRPNUM_CMD_REGS, + CY_IC_GRPNUM_TCH_REP, + CY_IC_GRPNUM_DATA_REC, + CY_IC_GRPNUM_TEST_REC, + CY_IC_GRPNUM_PCFG_REC, + CY_IC_GRPNUM_TCH_PARM_VAL, + CY_IC_GRPNUM_TCH_PARM_SIZE, + CY_IC_GRPNUM_RESERVED1, + CY_IC_GRPNUM_RESERVED2, + CY_IC_GRPNUM_OPCFG_REC, + CY_IC_GRPNUM_DDATA_REC, + CY_IC_GRPNUM_MDATA_REC, + CY_IC_GRPNUM_TEST_REGS, + CY_IC_GRPNUM_BTN_KEYS, + CY_IC_GRPNUM_TTHE_REGS, + CY_IC_GRPNUM_NUM +}; + +enum cyttsp4_int_state { + CY_INT_NONE, + CY_INT_IGNORE = (1 << 0), + CY_INT_MODE_CHANGE = (1 << 1), + CY_INT_EXEC_CMD = (1 << 2), + CY_INT_AWAKE = (1 << 3), +}; + +enum cyttsp4_mode { + CY_MODE_UNKNOWN, + CY_MODE_BOOTLOADER = (1 << 1), + CY_MODE_OPERATIONAL = (1 << 2), + CY_MODE_SYSINFO = (1 << 3), + CY_MODE_CAT = (1 << 4), + CY_MODE_STARTUP = (1 << 5), + CY_MODE_LOADER = (1 << 6), + CY_MODE_CHANGE_MODE = (1 << 7), + CY_MODE_CHANGED = (1 << 8), + CY_MODE_CMD_COMPLETE = (1 << 9), +}; + +enum cyttsp4_sleep_state { + SS_SLEEP_OFF, + SS_SLEEP_ON, + SS_SLEEPING, + SS_WAKING, +}; + +enum cyttsp4_startup_state { + STARTUP_NONE, + STARTUP_QUEUED, + STARTUP_RUNNING, +}; + +#define CY_NUM_REVCTRL 8 +struct cyttsp4_cydata { + u8 ttpidh; + u8 ttpidl; + u8 fw_ver_major; + u8 fw_ver_minor; + u8 revctrl[CY_NUM_REVCTRL]; + u8 blver_major; + u8 blver_minor; + u8 jtag_si_id3; + u8 jtag_si_id2; + u8 jtag_si_id1; + u8 jtag_si_id0; + u8 mfgid_sz; + u8 cyito_idh; + u8 cyito_idl; + u8 cyito_verh; + u8 cyito_verl; + u8 ttsp_ver_major; + u8 ttsp_ver_minor; + u8 device_info; + u8 mfg_id[]; +} __packed; + +struct cyttsp4_test { + u8 post_codeh; + u8 post_codel; +} __packed; + +struct cyttsp4_pcfg { + u8 electrodes_x; + u8 electrodes_y; + u8 len_xh; + u8 len_xl; + u8 len_yh; + u8 len_yl; + u8 res_xh; + u8 res_xl; + u8 res_yh; + u8 res_yl; + u8 max_zh; + u8 max_zl; + u8 panel_info0; +} __packed; + +struct cyttsp4_tch_rec_params { + u8 loc; + u8 size; +} __packed; + +#define CY_NUM_TCH_FIELDS 7 +#define CY_NUM_EXT_TCH_FIELDS 3 +struct cyttsp4_opcfg { + u8 cmd_ofs; + u8 rep_ofs; + u8 rep_szh; + u8 rep_szl; + u8 num_btns; + u8 tt_stat_ofs; + u8 obj_cfg0; + u8 max_tchs; + u8 tch_rec_size; + struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS]; + u8 btn_rec_size; /* btn record size (in bytes) */ + u8 btn_diff_ofs; /* btn data loc, diff counts */ + u8 btn_diff_size; /* btn size of diff counts (in bits) */ + struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS]; +} __packed; + +struct cyttsp4_sysinfo_ptr { + struct cyttsp4_cydata *cydata; + struct cyttsp4_test *test; + struct cyttsp4_pcfg *pcfg; + struct cyttsp4_opcfg *opcfg; + struct cyttsp4_ddata *ddata; + struct cyttsp4_mdata *mdata; +} __packed; + +struct cyttsp4_sysinfo_data { + u8 hst_mode; + u8 reserved; + u8 map_szh; + u8 map_szl; + u8 cydata_ofsh; + u8 cydata_ofsl; + u8 test_ofsh; + u8 test_ofsl; + u8 pcfg_ofsh; + u8 pcfg_ofsl; + u8 opcfg_ofsh; + u8 opcfg_ofsl; + u8 ddata_ofsh; + u8 ddata_ofsl; + u8 mdata_ofsh; + u8 mdata_ofsl; +} __packed; + +enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */ + CY_TCH_X, /* X */ + CY_TCH_Y, /* Y */ + CY_TCH_P, /* P (Z) */ + CY_TCH_T, /* TOUCH ID */ + CY_TCH_E, /* EVENT ID */ + CY_TCH_O, /* OBJECT ID */ + CY_TCH_W, /* SIZE */ + CY_TCH_MAJ, /* TOUCH_MAJOR */ + CY_TCH_MIN, /* TOUCH_MINOR */ + CY_TCH_OR, /* ORIENTATION */ + CY_TCH_NUM_ABS +}; + +static const char * const cyttsp4_tch_abs_string[] = { + [CY_TCH_X] = "X", + [CY_TCH_Y] = "Y", + [CY_TCH_P] = "P", + [CY_TCH_T] = "T", + [CY_TCH_E] = "E", + [CY_TCH_O] = "O", + [CY_TCH_W] = "W", + [CY_TCH_MAJ] = "MAJ", + [CY_TCH_MIN] = "MIN", + [CY_TCH_OR] = "OR", + [CY_TCH_NUM_ABS] = "INVALID" +}; + +struct cyttsp4_touch { + int abs[CY_TCH_NUM_ABS]; +}; + +struct cyttsp4_tch_abs_params { + size_t ofs; /* abs byte offset */ + size_t size; /* size in bits */ + size_t max; /* max value */ + size_t bofs; /* bit offset */ +}; + +struct cyttsp4_sysinfo_ofs { + size_t chip_type; + size_t cmd_ofs; + size_t rep_ofs; + size_t rep_sz; + size_t num_btns; + size_t num_btn_regs; /* ceil(num_btns/4) */ + size_t tt_stat_ofs; + size_t tch_rec_size; + size_t obj_cfg0; + size_t max_tchs; + size_t mode_size; + size_t data_size; + size_t map_sz; + size_t max_x; + size_t x_origin; /* left or right corner */ + size_t max_y; + size_t y_origin; /* upper or lower corner */ + size_t max_p; + size_t cydata_ofs; + size_t test_ofs; + size_t pcfg_ofs; + size_t opcfg_ofs; + size_t ddata_ofs; + size_t mdata_ofs; + size_t cydata_size; + size_t test_size; + size_t pcfg_size; + size_t opcfg_size; + size_t ddata_size; + size_t mdata_size; + size_t btn_keys_size; + struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS]; + size_t btn_rec_size; /* btn record size (in bytes) */ + size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */ + size_t btn_diff_size;/* btn size of diff counts (in bits) */ +}; + +enum cyttsp4_btn_state { + CY_BTN_RELEASED, + CY_BTN_PRESSED, + CY_BTN_NUM_STATE +}; + +struct cyttsp4_btn { + bool enabled; + int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */ + int key_code; +}; + +struct cyttsp4_sysinfo { + bool ready; + struct cyttsp4_sysinfo_data si_data; + struct cyttsp4_sysinfo_ptr si_ptrs; + struct cyttsp4_sysinfo_ofs si_ofs; + struct cyttsp4_btn *btn; /* button states */ + u8 *btn_rec_data; /* button diff count data */ + u8 *xy_mode; /* operational mode and status regs */ + u8 *xy_data; /* operational touch regs */ +}; + +struct cyttsp4_mt_data { + struct cyttsp4_mt_platform_data *pdata; + struct cyttsp4_sysinfo *si; + struct input_dev *input; + struct mutex report_lock; + bool is_suspended; + char phys[NAME_MAX]; + int num_prv_tch; +}; + +struct cyttsp4 { + struct device *dev; + struct mutex system_lock; + struct mutex adap_lock; + enum cyttsp4_mode mode; + enum cyttsp4_sleep_state sleep_state; + enum cyttsp4_startup_state startup_state; + int int_status; + wait_queue_head_t wait_q; + int irq; + struct work_struct startup_work; + struct work_struct watchdog_work; + struct timer_list watchdog_timer; + struct cyttsp4_sysinfo sysinfo; + void *exclusive_dev; + int exclusive_waits; + atomic_t ignore_irq; + bool invalid_touch_app; + struct cyttsp4_mt_data md; + struct cyttsp4_platform_data *pdata; + struct cyttsp4_core_platform_data *cpdata; + const struct cyttsp4_bus_ops *bus_ops; + u8 *xfer_buf; +#ifdef VERBOSE_DEBUG + u8 pr_buf[CY_MAX_PRBUF_SIZE]; +#endif +}; + +struct cyttsp4_bus_ops { + u16 bustype; + int (*write)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + const void *values); + int (*read)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + void *values); +}; + +enum cyttsp4_hst_mode_bits { + CY_HST_TOGGLE = (1 << 7), + CY_HST_MODE_CHANGE = (1 << 3), + CY_HST_MODE = (7 << 4), + CY_HST_OPERATE = (0 << 4), + CY_HST_SYSINFO = (1 << 4), + CY_HST_CAT = (2 << 4), + CY_HST_LOWPOW = (1 << 2), + CY_HST_SLEEP = (1 << 1), + CY_HST_RESET = (1 << 0), +}; + +/* abs settings */ +#define CY_IGNORE_VALUE 0xFFFF + +/* abs signal capabilities offsets in the frameworks array */ +enum cyttsp4_sig_caps { + CY_SIGNAL_OST, + CY_MIN_OST, + CY_MAX_OST, + CY_FUZZ_OST, + CY_FLAT_OST, + CY_NUM_ABS_SET /* number of signal capability fields */ +}; + +/* abs axis signal offsets in the framworks array */ +enum cyttsp4_sig_ost { + CY_ABS_X_OST, + CY_ABS_Y_OST, + CY_ABS_P_OST, + CY_ABS_W_OST, + CY_ABS_ID_OST, + CY_ABS_MAJ_OST, + CY_ABS_MIN_OST, + CY_ABS_OR_OST, + CY_NUM_ABS_OST /* number of abs signals */ +}; + +enum cyttsp4_flags { + CY_FLAG_NONE = 0x00, + CY_FLAG_HOVER = 0x04, + CY_FLAG_FLIP = 0x08, + CY_FLAG_INV_X = 0x10, + CY_FLAG_INV_Y = 0x20, + CY_FLAG_VKEYS = 0x40, +}; + +enum cyttsp4_object_id { + CY_OBJ_STANDARD_FINGER, + CY_OBJ_LARGE_OBJECT, + CY_OBJ_STYLUS, + CY_OBJ_HOVER, +}; + +enum cyttsp4_event_id { + CY_EV_NO_EVENT, + CY_EV_TOUCHDOWN, + CY_EV_MOVE, /* significant displacement (> act dist) */ + CY_EV_LIFTOFF, /* record reports last position */ +}; + +/* x-axis resolution of panel in pixels */ +#define CY_PCFG_RESOLUTION_X_MASK 0x7F + +/* y-axis resolution of panel in pixels */ +#define CY_PCFG_RESOLUTION_Y_MASK 0x7F + +/* x-axis, 0:origin is on left side of panel, 1: right */ +#define CY_PCFG_ORIGIN_X_MASK 0x80 + +/* y-axis, 0:origin is on top side of panel, 1: bottom */ +#define CY_PCFG_ORIGIN_Y_MASK 0x80 + +static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u8 addr, int size, + void *buf) +{ + return ts->bus_ops->read(ts->dev, ts->xfer_buf, addr, size, buf); +} + +static inline int cyttsp4_adap_write(struct cyttsp4 *ts, u8 addr, int size, + const void *buf) +{ + return ts->bus_ops->write(ts->dev, ts->xfer_buf, addr, size, buf); +} + +extern struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + struct device *dev, u16 irq, size_t xfer_buf_size); +extern int cyttsp4_remove(struct cyttsp4 *ts); +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, const void *values); +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, void *values); +extern const struct dev_pm_ops cyttsp4_pm_ops; + +#endif /* _LINUX_CYTTSP4_CORE_H */ diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c new file mode 100644 index 000000000000..8e2012c79058 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_i2c.c @@ -0,0 +1,90 @@ +/* + * cyttsp_i2c.c + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * Copyright (C) 2013 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include "cyttsp4_core.h" + +#include <linux/i2c.h> +#include <linux/input.h> + +#define CYTTSP4_I2C_DATA_SIZE (3 * 256) + +static const struct cyttsp4_bus_ops cyttsp4_i2c_bus_ops = { + .bustype = BUS_I2C, + .write = cyttsp_i2c_write_block_data, + .read = cyttsp_i2c_read_block_data, +}; + +static int cyttsp4_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cyttsp4 *ts; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C functionality not Supported\n"); + return -EIO; + } + + ts = cyttsp4_probe(&cyttsp4_i2c_bus_ops, &client->dev, client->irq, + CYTTSP4_I2C_DATA_SIZE); + + if (IS_ERR(ts)) + return PTR_ERR(ts); + + return 0; +} + +static int cyttsp4_i2c_remove(struct i2c_client *client) +{ + struct cyttsp4 *ts = i2c_get_clientdata(client); + + cyttsp4_remove(ts); + + return 0; +} + +static const struct i2c_device_id cyttsp4_i2c_id[] = { + { CYTTSP4_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id); + +static struct i2c_driver cyttsp4_i2c_driver = { + .driver = { + .name = CYTTSP4_I2C_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp4_pm_ops, + }, + .probe = cyttsp4_i2c_probe, + .remove = cyttsp4_i2c_remove, + .id_table = cyttsp4_i2c_id, +}; + +module_i2c_driver(cyttsp4_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("i2c:cyttsp4"); diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c new file mode 100644 index 000000000000..f8f891bead34 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_spi.c @@ -0,0 +1,205 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * Copyright (C) 2013 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include "cyttsp4_core.h" + +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/spi/spi.h> + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_BITS_PER_WORD 8 +#define CY_SPI_A8_BIT 0x02 +#define CY_SPI_WR_HEADER_BYTES 2 +#define CY_SPI_RD_HEADER_BYTES 1 +#define CY_SPI_CMD_BYTES 2 +#define CY_SPI_SYNC_BYTE 0 +#define CY_SPI_SYNC_ACK 0x62 /* from TRM *A protocol */ +#define CY_SPI_DATA_SIZE (2 * 256) + +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) + +static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf, + u8 op, u8 reg, u8 *buf, int length) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_message msg; + struct spi_transfer xfer[2]; + u8 *wr_buf = &xfer_buf[0]; + u8 rd_buf[CY_SPI_CMD_BYTES]; + int retval; + int i; + + if (length > CY_SPI_DATA_SIZE) { + dev_err(dev, "%s: length %d is too big.\n", + __func__, length); + return -EINVAL; + } + + memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); + memset(rd_buf, 0, CY_SPI_CMD_BYTES); + + if (reg > 255) + wr_buf[0] = op + CY_SPI_A8_BIT; + else + wr_buf[0] = op; + if (op == CY_SPI_WR_OP) + wr_buf[1] = reg % 256; + if (op == CY_SPI_WR_OP && length > 0) + memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + /* + We set both TX and RX buffers because Cypress TTSP + requires full duplex operation. + */ + xfer[0].tx_buf = wr_buf; + xfer[0].rx_buf = rd_buf; + switch (op) { + case CY_SPI_WR_OP: + xfer[0].len = length + CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + break; + + case CY_SPI_RD_OP: + xfer[0].len = CY_SPI_RD_HEADER_BYTES; + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + break; + + default: + dev_err(dev, "%s: bad operation code=%d\n", __func__, op); + return -EINVAL; + } + + retval = spi_sync(spi, &msg); + if (retval < 0) { + dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n", + __func__, retval, xfer[1].len, op); + + /* + * do not return here since was a bad ACK sequence + * let the following ACK check handle any errors and + * allow silent retries + */ + } + + if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK) { + dev_dbg(dev, "%s: operation %d failed\n", __func__, op); + + for (i = 0; i < CY_SPI_CMD_BYTES; i++) + dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n", + __func__, i, rd_buf[i]); + for (i = 0; i < length; i++) + dev_dbg(dev, "%s: test buf[%d]:0x%02x\n", + __func__, i, buf[i]); + + return -EIO; + } + + return 0; +} + +static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf, + u8 addr, u8 length, void *data) +{ + int rc; + + rc = cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, NULL, 0); + if (rc) + return rc; + else + return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data, + length); +} + +static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf, + u8 addr, u8 length, const void *data) +{ + return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data, + length); +} + +static const struct cyttsp4_bus_ops cyttsp_spi_bus_ops = { + .bustype = BUS_SPI, + .write = cyttsp_spi_write_block_data, + .read = cyttsp_spi_read_block_data, +}; + +static int cyttsp4_spi_probe(struct spi_device *spi) +{ + struct cyttsp4 *ts; + int error; + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + error = spi_setup(spi); + if (error < 0) { + dev_err(&spi->dev, "%s: SPI setup error %d\n", + __func__, error); + return error; + } + + ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, + CY_SPI_DATA_BUF_SIZE); + + if (IS_ERR(ts)) + return PTR_ERR(ts); + + return 0; +} + +static int cyttsp4_spi_remove(struct spi_device *spi) +{ + struct cyttsp4 *ts = spi_get_drvdata(spi); + cyttsp4_remove(ts); + + return 0; +} + +static struct spi_driver cyttsp4_spi_driver = { + .driver = { + .name = CYTTSP4_SPI_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp4_pm_ops, + }, + .probe = cyttsp4_spi_probe, + .remove = cyttsp4_spi_remove, +}; + +module_spi_driver(cyttsp4_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("spi:cyttsp4"); diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index ae89d2609ab0..d53e0b72a407 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -84,7 +84,8 @@ static int ttsp_read_block_data(struct cyttsp *ts, u8 command, int tries; for (tries = 0; tries < CY_NUM_RETRY; tries++) { - error = ts->bus_ops->read(ts, command, length, buf); + error = ts->bus_ops->read(ts->dev, ts->xfer_buf, command, + length, buf); if (!error) return 0; @@ -101,7 +102,8 @@ static int ttsp_write_block_data(struct cyttsp *ts, u8 command, int tries; for (tries = 0; tries < CY_NUM_RETRY; tries++) { - error = ts->bus_ops->write(ts, command, length, buf); + error = ts->bus_ops->write(ts->dev, ts->xfer_buf, command, + length, buf); if (!error) return 0; diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h index f1ebde369f86..0cf564a79fb5 100644 --- a/drivers/input/touchscreen/cyttsp_core.h +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -112,9 +112,10 @@ struct cyttsp; struct cyttsp_bus_ops { u16 bustype; - int (*write)(struct cyttsp *ts, - u8 addr, u8 length, const void *values); - int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values); + int (*write)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + const void *values); + int (*read)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + void *values); }; enum cyttsp_state { @@ -144,6 +145,10 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size); void cyttsp_remove(struct cyttsp *ts); +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, const void *values); +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, void *values); extern const struct dev_pm_ops cyttsp_pm_ops; #endif /* __CYTTSP_CORE_H__ */ diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c index 4dbdf44b8fc5..63104a86a9bd 100644 --- a/drivers/input/touchscreen/cyttsp_i2c.c +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -1,5 +1,5 @@ /* - * Source for: + * cyttsp_i2c.c * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. * For use with Cypress Txx3xx parts. * Supported parts include: @@ -19,11 +19,7 @@ * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> * */ @@ -34,47 +30,6 @@ #define CY_I2C_DATA_SIZE 128 -static int cyttsp_i2c_read_block_data(struct cyttsp *ts, - u8 addr, u8 length, void *values) -{ - struct i2c_client *client = to_i2c_client(ts->dev); - struct i2c_msg msgs[] = { - { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = &addr, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = length, - .buf = values, - }, - }; - int retval; - - retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (retval < 0) - return retval; - - return retval != ARRAY_SIZE(msgs) ? -EIO : 0; -} - -static int cyttsp_i2c_write_block_data(struct cyttsp *ts, - u8 addr, u8 length, const void *values) -{ - struct i2c_client *client = to_i2c_client(ts->dev); - int retval; - - ts->xfer_buf[0] = addr; - memcpy(&ts->xfer_buf[1], values, length); - - retval = i2c_master_send(client, ts->xfer_buf, length + 1); - - return retval < 0 ? retval : 0; -} - static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = { .bustype = BUS_I2C, .write = cyttsp_i2c_write_block_data, @@ -98,7 +53,6 @@ static int cyttsp_i2c_probe(struct i2c_client *client, return PTR_ERR(ts); i2c_set_clientdata(client, ts); - return 0; } diff --git a/drivers/input/touchscreen/cyttsp_i2c_common.c b/drivers/input/touchscreen/cyttsp_i2c_common.c new file mode 100644 index 000000000000..07c553fbcef2 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_i2c_common.c @@ -0,0 +1,79 @@ +/* + * cyttsp_i2c_common.c + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx3xx and Txx4xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * TMA4XX + * TMA1036 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include <linux/device.h> +#include <linux/export.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/types.h> + +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, + u8 addr, u8 length, void *values) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = values, + }, + }; + int retval; + + retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (retval < 0) + return retval; + + return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +} +EXPORT_SYMBOL_GPL(cyttsp_i2c_read_block_data); + +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, + u8 addr, u8 length, const void *values) +{ + struct i2c_client *client = to_i2c_client(dev); + int retval; + + xfer_buf[0] = addr; + memcpy(&xfer_buf[1], values, length); + + retval = i2c_master_send(client, xfer_buf, length + 1); + + return retval < 0 ? retval : 0; +} +EXPORT_SYMBOL_GPL(cyttsp_i2c_write_block_data); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c index 861b7f77605b..1df625337b84 100644 --- a/drivers/input/touchscreen/cyttsp_spi.c +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -8,6 +8,7 @@ * * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * Copyright (C) 2013 Cypress Semiconductor * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,11 +20,7 @@ * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> * */ @@ -43,19 +40,19 @@ #define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) #define CY_SPI_BITS_PER_WORD 8 -static int cyttsp_spi_xfer(struct cyttsp *ts, +static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf, u8 op, u8 reg, u8 *buf, int length) { - struct spi_device *spi = to_spi_device(ts->dev); + struct spi_device *spi = to_spi_device(dev); struct spi_message msg; struct spi_transfer xfer[2]; - u8 *wr_buf = &ts->xfer_buf[0]; - u8 *rd_buf = &ts->xfer_buf[CY_SPI_DATA_BUF_SIZE]; + u8 *wr_buf = &xfer_buf[0]; + u8 *rd_buf = &xfer_buf[CY_SPI_DATA_BUF_SIZE]; int retval; int i; if (length > CY_SPI_DATA_SIZE) { - dev_err(ts->dev, "%s: length %d is too big.\n", + dev_err(dev, "%s: length %d is too big.\n", __func__, length); return -EINVAL; } @@ -95,13 +92,13 @@ static int cyttsp_spi_xfer(struct cyttsp *ts, break; default: - dev_err(ts->dev, "%s: bad operation code=%d\n", __func__, op); + dev_err(dev, "%s: bad operation code=%d\n", __func__, op); return -EINVAL; } retval = spi_sync(spi, &msg); if (retval < 0) { - dev_dbg(ts->dev, "%s: spi_sync() error %d, len=%d, op=%d\n", + dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n", __func__, retval, xfer[1].len, op); /* @@ -113,14 +110,13 @@ static int cyttsp_spi_xfer(struct cyttsp *ts, if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 || rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) { - - dev_dbg(ts->dev, "%s: operation %d failed\n", __func__, op); + dev_dbg(dev, "%s: operation %d failed\n", __func__, op); for (i = 0; i < CY_SPI_CMD_BYTES; i++) - dev_dbg(ts->dev, "%s: test rd_buf[%d]:0x%02x\n", + dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n", __func__, i, rd_buf[i]); for (i = 0; i < length; i++) - dev_dbg(ts->dev, "%s: test buf[%d]:0x%02x\n", + dev_dbg(dev, "%s: test buf[%d]:0x%02x\n", __func__, i, buf[i]); return -EIO; @@ -129,16 +125,18 @@ static int cyttsp_spi_xfer(struct cyttsp *ts, return 0; } -static int cyttsp_spi_read_block_data(struct cyttsp *ts, +static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, void *data) { - return cyttsp_spi_xfer(ts, CY_SPI_RD_OP, addr, data, length); + return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data, + length); } -static int cyttsp_spi_write_block_data(struct cyttsp *ts, +static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, const void *data) { - return cyttsp_spi_xfer(ts, CY_SPI_WR_OP, addr, (void *)data, length); + return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data, + length); } static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = { diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c index 8f561e22bdd4..ab64d58c3ac0 100644 --- a/drivers/input/touchscreen/da9052_tsi.c +++ b/drivers/input/touchscreen/da9052_tsi.c @@ -329,8 +329,6 @@ static int da9052_ts_remove(struct platform_device *pdev) input_unregister_device(tsi->dev); kfree(tsi); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index 39f3df8670c3..ef5fcb0945e9 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -166,24 +166,22 @@ static int egalax_firmware_version(struct i2c_client *client) } static int egalax_ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct egalax_ts *ts; struct input_dev *input_dev; - int ret; int error; - ts = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL); + ts = devm_kzalloc(&client->dev, sizeof(struct egalax_ts), GFP_KERNEL); if (!ts) { dev_err(&client->dev, "Failed to allocate memory\n"); return -ENOMEM; } - input_dev = input_allocate_device(); + input_dev = devm_input_allocate_device(&client->dev); if (!input_dev) { dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_ts; + return -ENOMEM; } ts->client = client; @@ -193,19 +191,17 @@ static int egalax_ts_probe(struct i2c_client *client, error = egalax_wake_up_device(client); if (error) { dev_err(&client->dev, "Failed to wake up the controller\n"); - goto err_free_dev; + return error; } - ret = egalax_firmware_version(client); - if (ret < 0) { + error = egalax_firmware_version(client); + if (error < 0) { dev_err(&client->dev, "Failed to read firmware version\n"); - error = -EIO; - goto err_free_dev; + return error; } input_dev->name = "EETI eGalax Touch Screen"; input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); @@ -221,41 +217,21 @@ static int egalax_ts_probe(struct i2c_client *client, input_set_drvdata(input_dev, ts); - error = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "egalax_ts", ts); + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, + egalax_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "egalax_ts", ts); if (error < 0) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_dev; + return error; } error = input_register_device(ts->input_dev); if (error) - goto err_free_irq; + return error; i2c_set_clientdata(client, ts); return 0; - -err_free_irq: - free_irq(client->irq, ts); -err_free_dev: - input_free_device(input_dev); -err_free_ts: - kfree(ts); - - return error; -} - -static int egalax_ts_remove(struct i2c_client *client) -{ - struct egalax_ts *ts = i2c_get_clientdata(client); - - free_irq(client->irq, ts); - - input_unregister_device(ts->input_dev); - kfree(ts); - - return 0; } static const struct i2c_device_id egalax_ts_id[] = { @@ -301,7 +277,6 @@ static struct i2c_driver egalax_ts_driver = { }, .id_table = egalax_ts_id, .probe = egalax_ts_probe, - .remove = egalax_ts_remove, }; module_i2c_driver(egalax_ts_driver); diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c index 465db5dba8b4..e30d837dae2f 100644 --- a/drivers/input/touchscreen/intel-mid-touch.c +++ b/drivers/input/touchscreen/intel-mid-touch.c @@ -651,8 +651,6 @@ static int mrstouch_remove(struct platform_device *pdev) input_unregister_device(tsdev->input); kfree(tsdev); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index 282d7c7ad2fc..e463a79ffecc 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -145,7 +145,6 @@ static int jornada720_ts_probe(struct platform_device *pdev) fail2: free_irq(IRQ_GPIO9, pdev); fail1: - platform_set_drvdata(pdev, NULL); input_free_device(input_dev); kfree(jornada_ts); return error; @@ -156,7 +155,6 @@ static int jornada720_ts_remove(struct platform_device *pdev) struct jornada_ts *jornada_ts = platform_get_drvdata(pdev); free_irq(IRQ_GPIO9, pdev); - platform_set_drvdata(pdev, NULL); input_unregister_device(jornada_ts->dev); kfree(jornada_ts); diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index 89308fe38752..d6f099c47f84 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -233,8 +233,6 @@ static int mc13783_ts_remove(struct platform_device *pdev) { struct mc13783_ts_priv *priv = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - destroy_workqueue(priv->workq); input_unregister_device(priv->idev); kfree(priv); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 51e7b87827a4..50fb1293874e 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -336,7 +336,6 @@ static int titsc_remove(struct platform_device *pdev) input_unregister_device(ts_dev->input); - platform_set_drvdata(pdev, NULL); kfree(ts_dev); return 0; } diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c index acfb87607b87..c47827a26e3c 100644 --- a/drivers/input/touchscreen/tnetv107x-ts.c +++ b/drivers/input/touchscreen/tnetv107x-ts.c @@ -351,7 +351,6 @@ error_clk: error_map: release_mem_region(ts->res->start, resource_size(ts->res)); error_res: - platform_set_drvdata(pdev, NULL); kfree(ts); return error; @@ -366,7 +365,6 @@ static int tsc_remove(struct platform_device *pdev) clk_put(ts->clk); iounmap(ts->regs); release_mem_region(ts->res->start, resource_size(ts->res)); - platform_set_drvdata(pdev, NULL); kfree(ts); return 0; diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index 820a066c3b8a..94cde2cb1491 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -17,6 +17,7 @@ #include <linux/workqueue.h> #include <linux/slab.h> #include <linux/input.h> +#include <linux/input-polldev.h> #include <linux/platform_device.h> #include <linux/mfd/tps6507x.h> #include <linux/input/tps6507x-ts.h> @@ -38,20 +39,13 @@ struct ts_event { }; struct tps6507x_ts { - struct input_dev *input_dev; struct device *dev; + struct input_polled_dev *poll_dev; + struct tps6507x_dev *mfd; char phys[32]; - struct delayed_work work; - unsigned polling; /* polling is active */ struct ts_event tc; - struct tps6507x_dev *mfd; - u16 model; - unsigned pendown; - int irq; - void (*clear_penirq)(void); - unsigned long poll_period; /* ms */ u16 min_pressure; - int vref; /* non-zero to leave vref on */ + bool pendown; }; static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data) @@ -161,18 +155,15 @@ static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc) return ret; } -static void tps6507x_ts_handler(struct work_struct *work) +static void tps6507x_ts_poll(struct input_polled_dev *poll_dev) { - struct tps6507x_ts *tsc = container_of(work, - struct tps6507x_ts, work.work); - struct input_dev *input_dev = tsc->input_dev; - int pendown; - int schd; - int poll = 0; + struct tps6507x_ts *tsc = poll_dev->private; + struct input_dev *input_dev = poll_dev->input; + bool pendown; s32 ret; - ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE, - &tsc->tc.pressure); + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE, + &tsc->tc.pressure); if (ret) goto done; @@ -183,7 +174,7 @@ static void tps6507x_ts_handler(struct work_struct *work) input_report_key(input_dev, BTN_TOUCH, 0); input_report_abs(input_dev, ABS_PRESSURE, 0); input_sync(input_dev); - tsc->pendown = 0; + tsc->pendown = false; } if (pendown) { @@ -208,76 +199,69 @@ static void tps6507x_ts_handler(struct work_struct *work) input_report_abs(input_dev, ABS_Y, tsc->tc.y); input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure); input_sync(input_dev); - tsc->pendown = 1; - poll = 1; + tsc->pendown = true; } done: - /* always poll if not using interrupts */ - poll = 1; - - if (poll) { - schd = schedule_delayed_work(&tsc->work, - msecs_to_jiffies(tsc->poll_period)); - if (schd) - tsc->polling = 1; - else { - tsc->polling = 0; - dev_err(tsc->dev, "re-schedule failed"); - } - } else - tsc->polling = 0; - - ret = tps6507x_adc_standby(tsc); + tps6507x_adc_standby(tsc); } static int tps6507x_ts_probe(struct platform_device *pdev) { - int error; - struct tps6507x_ts *tsc; struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); - struct touchscreen_init_data *init_data; + const struct tps6507x_board *tps_board; + const struct touchscreen_init_data *init_data; + struct tps6507x_ts *tsc; + struct input_polled_dev *poll_dev; struct input_dev *input_dev; - struct tps6507x_board *tps_board; - int schd; + int error; - /** + /* * tps_board points to pmic related constants * coming from the board-evm file. */ - - tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data; - + tps_board = dev_get_platdata(tps6507x_dev->dev); if (!tps_board) { dev_err(tps6507x_dev->dev, "Could not find tps6507x platform data\n"); - return -EIO; + return -ENODEV; } - /** + /* * init_data points to array of regulator_init structures * coming from the board-evm file. */ - init_data = tps_board->tps6507x_ts_init_data; tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL); if (!tsc) { dev_err(tps6507x_dev->dev, "failed to allocate driver data\n"); - error = -ENOMEM; - goto err0; + return -ENOMEM; } - tps6507x_dev->ts = tsc; tsc->mfd = tps6507x_dev; tsc->dev = tps6507x_dev->dev; - input_dev = input_allocate_device(); - if (!input_dev) { - dev_err(tsc->dev, "Failed to allocate input device.\n"); + tsc->min_pressure = init_data ? + init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE; + + snprintf(tsc->phys, sizeof(tsc->phys), + "%s/input0", dev_name(tsc->dev)); + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + dev_err(tsc->dev, "Failed to allocate polled input device.\n"); error = -ENOMEM; - goto err1; + goto err_free_mem; } + tsc->poll_dev = poll_dev; + + poll_dev->private = tsc; + poll_dev->poll = tps6507x_ts_poll; + poll_dev->poll_interval = init_data ? + init_data->poll_period : TSC_DEFAULT_POLL_PERIOD; + + input_dev = poll_dev->input; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); @@ -286,76 +270,42 @@ static int tps6507x_ts_probe(struct platform_device *pdev) input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0); input_dev->name = "TPS6507x Touchscreen"; - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = tsc->dev; - - snprintf(tsc->phys, sizeof(tsc->phys), - "%s/input0", dev_name(tsc->dev)); input_dev->phys = tsc->phys; - - dev_dbg(tsc->dev, "device: %s\n", input_dev->phys); - - input_set_drvdata(input_dev, tsc); - - tsc->input_dev = input_dev; - - INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler); - + input_dev->dev.parent = tsc->dev; + input_dev->id.bustype = BUS_I2C; if (init_data) { - tsc->poll_period = init_data->poll_period; - tsc->vref = init_data->vref; - tsc->min_pressure = init_data->min_pressure; input_dev->id.vendor = init_data->vendor; input_dev->id.product = init_data->product; input_dev->id.version = init_data->version; - } else { - tsc->poll_period = TSC_DEFAULT_POLL_PERIOD; - tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE; } error = tps6507x_adc_standby(tsc); if (error) - goto err2; + goto err_free_polled_dev; - error = input_register_device(input_dev); + error = input_register_polled_device(poll_dev); if (error) - goto err2; + goto err_free_polled_dev; - schd = schedule_delayed_work(&tsc->work, - msecs_to_jiffies(tsc->poll_period)); - - if (schd) - tsc->polling = 1; - else { - tsc->polling = 0; - dev_err(tsc->dev, "schedule failed"); - goto err2; - } - platform_set_drvdata(pdev, tps6507x_dev); + platform_set_drvdata(pdev, tsc); return 0; -err2: - cancel_delayed_work_sync(&tsc->work); - input_free_device(input_dev); -err1: +err_free_polled_dev: + input_free_polled_device(poll_dev); +err_free_mem: kfree(tsc); - tps6507x_dev->ts = NULL; -err0: return error; } static int tps6507x_ts_remove(struct platform_device *pdev) { - struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); - struct tps6507x_ts *tsc = tps6507x_dev->ts; - struct input_dev *input_dev = tsc->input_dev; - - cancel_delayed_work_sync(&tsc->work); + struct tps6507x_ts *tsc = platform_get_drvdata(pdev); + struct input_polled_dev *poll_dev = tsc->poll_dev; - input_unregister_device(input_dev); + input_unregister_polled_device(poll_dev); + input_free_polled_device(poll_dev); - tps6507x_dev->ts = NULL; kfree(tsc); return 0; diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c index d2ef8f05c66e..003d0c3b5d08 100644 --- a/drivers/input/touchscreen/w90p910_ts.c +++ b/drivers/input/touchscreen/w90p910_ts.c @@ -318,8 +318,6 @@ static int w90x900ts_remove(struct platform_device *pdev) input_unregister_device(w90p910_ts->input); kfree(w90p910_ts); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c index bf0d07620bac..7ccaa1b12b05 100644 --- a/drivers/input/touchscreen/wacom_i2c.c +++ b/drivers/input/touchscreen/wacom_i2c.c @@ -1,7 +1,7 @@ /* * Wacom Penabled Driver for I2C * - * Copyright (c) 2011 Tatsunosuke Tobita, Wacom. + * Copyright (c) 2011 - 2013 Tatsunosuke Tobita, Wacom. * <tobita.tatsunosuke@wacom.co.jp> * * This program is free software; you can redistribute it @@ -27,7 +27,6 @@ #define WACOM_CMD_THROW0 0x05 #define WACOM_CMD_THROW1 0x00 #define WACOM_QUERY_SIZE 19 -#define WACOM_RETRY_CNT 100 struct wacom_features { int x_max; @@ -40,6 +39,8 @@ struct wacom_i2c { struct i2c_client *client; struct input_dev *input; u8 data[WACOM_QUERY_SIZE]; + bool prox; + int tool; }; static int wacom_query_device(struct i2c_client *client, @@ -112,9 +113,14 @@ static irqreturn_t wacom_i2c_irq(int irq, void *dev_id) y = le16_to_cpup((__le16 *)&data[6]); pressure = le16_to_cpup((__le16 *)&data[8]); + if (!wac_i2c->prox) + wac_i2c->tool = (data[3] & 0x0c) ? + BTN_TOOL_RUBBER : BTN_TOOL_PEN; + + wac_i2c->prox = data[3] & 0x20; + input_report_key(input, BTN_TOUCH, tsw || ers); - input_report_key(input, BTN_TOOL_PEN, tsw); - input_report_key(input, BTN_TOOL_RUBBER, ers); + input_report_key(input, wac_i2c->tool, wac_i2c->prox); input_report_key(input, BTN_STYLUS, f1); input_report_key(input, BTN_STYLUS2, f2); input_report_abs(input, ABS_X, x); diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 5f68f2cfdfd0..d5cc3acecfd3 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -44,6 +44,7 @@ #include <linux/uaccess.h> #include <linux/moduleparam.h> #include <linux/jiffies.h> +#include <linux/syscalls.h> #include <asm/ptrace.h> #include <asm/irq_regs.h> @@ -586,6 +587,7 @@ struct sysrq_state { /* reset sequence handling */ bool reset_canceled; + bool reset_requested; unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)]; int reset_seq_len; int reset_seq_cnt; @@ -624,18 +626,26 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state) state->reset_seq_version = sysrq_reset_seq_version; } -static void sysrq_do_reset(unsigned long dummy) +static void sysrq_do_reset(unsigned long _state) { - __handle_sysrq(sysrq_xlate[KEY_B], false); + struct sysrq_state *state = (struct sysrq_state *) _state; + + state->reset_requested = true; + + sys_sync(); + kernel_restart(NULL); } static void sysrq_handle_reset_request(struct sysrq_state *state) { + if (state->reset_requested) + __handle_sysrq(sysrq_xlate[KEY_B], false); + if (sysrq_reset_downtime_ms) mod_timer(&state->keyreset_timer, jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms)); else - sysrq_do_reset(0); + sysrq_do_reset((unsigned long)state); } static void sysrq_detect_reset_sequence(struct sysrq_state *state, @@ -837,7 +847,8 @@ static int sysrq_connect(struct input_handler *handler, sysrq->handle.handler = handler; sysrq->handle.name = "sysrq"; sysrq->handle.private = sysrq; - setup_timer(&sysrq->keyreset_timer, sysrq_do_reset, 0); + setup_timer(&sysrq->keyreset_timer, + sysrq_do_reset, (unsigned long)sysrq); error = input_register_handle(&sysrq->handle); if (error) { |