From 6fa612b56c575a5235568593eab4240c90608630 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 11 May 2009 15:49:12 +0200 Subject: microblaze: Kconfig: Enable drivers for Microblaze Signed-off-by: Michal Simek --- drivers/input/serio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index da3c3a5d268..c4b3fbd1a80 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -192,7 +192,7 @@ config SERIO_RAW config SERIO_XILINX_XPS_PS2 tristate "Xilinx XPS PS/2 Controller Support" - depends on PPC + depends on PPC || MICROBLAZE help This driver supports XPS PS/2 IP from the Xilinx EDK on PowerPC platform. -- cgit v1.2.3-70-g09d2 From dc890c2dcd63a90de68ee5f0253eefbb89d725f0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 7 Jun 2009 23:27:31 +0100 Subject: [ARM] 5544/1: Trust PrimeCell resource sizes I found the PrimeCell/AMBA Bus drivers distrusting the resource passed in as part of the struct amba_device abstraction. This patch removes all hard coded resource sizes found in the PrimeCell drivers and move the responsibility of this definition back to the platform/board device definition, which already exist and appear to be correct for all in-tree users of these drivers. We do this using the resource_size() inline function which was also replicated in the only driver using the resource size, so that has been changed too. The KMI_SIZE was left in kmi.h in case someone likes it. Test-compiled against Versatile and Integrator defconfigs, seems to work but I don't posess these boards and cannot test them. Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/input/serio/ambakmi.c | 2 +- drivers/mmc/host/mmci.c | 2 +- drivers/rtc/rtc-pl030.c | 2 +- drivers/rtc/rtc-pl031.c | 3 +-- drivers/serial/amba-pl010.c | 2 +- drivers/serial/amba-pl011.c | 2 +- drivers/video/amba-clcd.c | 2 +- sound/arm/aaci.c | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c index a28c06d686e..89b394183a7 100644 --- a/drivers/input/serio/ambakmi.c +++ b/drivers/input/serio/ambakmi.c @@ -135,7 +135,7 @@ static int amba_kmi_probe(struct amba_device *dev, struct amba_id *id) io->dev.parent = &dev->dev; kmi->io = io; - kmi->base = ioremap(dev->res.start, KMI_SIZE); + kmi->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!kmi->base) { ret = -ENOMEM; goto out; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 7d4febdab28..e1aa8471ab1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -546,7 +546,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) host->mclk = clk_get_rate(host->clk); DBG(host, "eventual mclk rate: %u Hz\n", host->mclk); } - host->base = ioremap(dev->res.start, SZ_4K); + host->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!host->base) { ret = -ENOMEM; goto clk_disable; diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c index aaf1f75fa29..457231bb102 100644 --- a/drivers/rtc/rtc-pl030.c +++ b/drivers/rtc/rtc-pl030.c @@ -117,7 +117,7 @@ static int pl030_probe(struct amba_device *dev, struct amba_id *id) goto err_rtc; } - rtc->base = ioremap(dev->res.start, SZ_4K); + rtc->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!rtc->base) { ret = -ENOMEM; goto err_map; diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 451fc13784d..f41873f98f6 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -142,8 +142,7 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id) goto out; } - ldata->base = ioremap(adev->res.start, - adev->res.end - adev->res.start + 1); + ldata->base = ioremap(adev->res.start, resource_size(&adev->res)); if (!ldata->base) { ret = -ENOMEM; goto out_no_remap; diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index cdc049d4350..58a4879c7e4 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -686,7 +686,7 @@ static int pl010_probe(struct amba_device *dev, struct amba_id *id) goto out; } - base = ioremap(dev->res.start, PAGE_SIZE); + base = ioremap(dev->res.start, resource_size(&dev->res)); if (!base) { ret = -ENOMEM; goto free; diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index 8c5bda27736..bf82e28770a 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -767,7 +767,7 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id) goto out; } - base = ioremap(dev->res.start, PAGE_SIZE); + base = ioremap(dev->res.start, resource_size(&dev->res)); if (!base) { ret = -ENOMEM; goto free; diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index d1f80bac54f..fb8163d181a 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -351,7 +351,7 @@ static int clcdfb_register(struct clcd_fb *fb) } fb->fb.fix.mmio_start = fb->dev->res.start; - fb->fb.fix.mmio_len = 4096; + fb->fb.fix.mmio_len = resource_size(&fb->dev->res); fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len); if (!fb->regs) { diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 5c48e36038f..dc78272fc39 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -1089,7 +1089,7 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id) goto out; } - aaci->base = ioremap(dev->res.start, SZ_4K); + aaci->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!aaci->base) { ret = -ENOMEM; goto out; -- cgit v1.2.3-70-g09d2 From 72398e4b1a4cf55d3698a4f265b638093a470b04 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 7 Jul 2009 22:04:55 -0700 Subject: Input: use resource_size when allocating resources Use the function resource_size, which reduces the chance of introducing off-by-one errors in calculating the resource size. The semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @@ struct resource *res; @@ - (res->end - res->start) + 1 + resource_size(res) // Signed-off-by: Julia Lawall Signed-off-by: Dmitry Torokhov --- drivers/input/misc/cobalt_btns.c | 2 +- drivers/input/serio/at32psif.c | 2 +- drivers/input/touchscreen/atmel_tsadcc.c | 8 ++++---- drivers/input/touchscreen/w90p910_ts.c | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c index 2adf9cb265d..d01da9bd5da 100644 --- a/drivers/input/misc/cobalt_btns.c +++ b/drivers/input/misc/cobalt_btns.c @@ -116,7 +116,7 @@ static int __devinit cobalt_buttons_probe(struct platform_device *pdev) } bdev->poll_dev = poll_dev; - bdev->reg = ioremap(res->start, res->end - res->start + 1); + bdev->reg = ioremap(res->start, resource_size(res)); dev_set_drvdata(&pdev->dev, bdev); error = input_register_polled_device(poll_dev); diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index 41fda8c67b1..a6fb7a3dcc4 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -231,7 +231,7 @@ static int __init psif_probe(struct platform_device *pdev) goto out_free_io; } - psif->regs = ioremap(regs->start, regs->end - regs->start + 1); + psif->regs = ioremap(regs->start, resource_size(regs)); if (!psif->regs) { ret = -ENOMEM; dev_dbg(&pdev->dev, "could not map I/O memory\n"); diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c index 055969e8be1..9c7fce4d74d 100644 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ b/drivers/input/touchscreen/atmel_tsadcc.c @@ -204,14 +204,14 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev) goto err_free_dev; } - if (!request_mem_region(res->start, res->end - res->start + 1, + if (!request_mem_region(res->start, resource_size(res), "atmel tsadcc regs")) { dev_err(&pdev->dev, "resources is unavailable.\n"); err = -EBUSY; goto err_free_dev; } - tsc_base = ioremap(res->start, res->end - res->start + 1); + tsc_base = ioremap(res->start, resource_size(res)); if (!tsc_base) { dev_err(&pdev->dev, "failed to map registers.\n"); err = -ENOMEM; @@ -286,7 +286,7 @@ err_free_irq: err_unmap_regs: iounmap(tsc_base); err_release_mem: - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); err_free_dev: input_free_device(ts_dev->input); err_free_mem: @@ -305,7 +305,7 @@ static int __devexit atmel_tsadcc_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap(tsc_base); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); clk_disable(ts_dev->clk); clk_put(ts_dev->clk); diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c index 6071f588257..b3e782fdd2b 100644 --- a/drivers/input/touchscreen/w90p910_ts.c +++ b/drivers/input/touchscreen/w90p910_ts.c @@ -241,13 +241,13 @@ static int __devinit w90x900ts_probe(struct platform_device *pdev) goto fail1; } - if (!request_mem_region(res->start, res->end - res->start + 1, + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { err = -EBUSY; goto fail1; } - w90p910_ts->ts_reg = ioremap(res->start, res->end - res->start + 1); + w90p910_ts->ts_reg = ioremap(res->start, resource_size(res)); if (!w90p910_ts->ts_reg) { err = -ENOMEM; goto fail2; @@ -296,7 +296,7 @@ static int __devinit w90x900ts_probe(struct platform_device *pdev) fail4: free_irq(w90p910_ts->irq_num, w90p910_ts); fail3: iounmap(w90p910_ts->ts_reg); -fail2: release_mem_region(res->start, res->end - res->start + 1); +fail2: release_mem_region(res->start, resource_size(res)); fail1: input_free_device(input_dev); kfree(w90p910_ts); return err; @@ -312,7 +312,7 @@ static int __devexit w90x900ts_remove(struct platform_device *pdev) iounmap(w90p910_ts->ts_reg); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); input_unregister_device(w90p910_ts->input); kfree(w90p910_ts); -- cgit v1.2.3-70-g09d2 From ebd7768daeb39b0691e25175e25b980f13e913e2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 22 Jul 2009 21:51:32 -0700 Subject: Input: i8042 - switch to using dev_pm_ops Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 582245c497e..9f5c0506242 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -923,41 +923,27 @@ static void i8042_dritek_enable(void) #ifdef CONFIG_PM -static bool i8042_suspended; - /* - * Here we try to restore the original BIOS settings. We only want to - * do that once, when we really suspend, not when we taking memory - * snapshot for swsusp (in this case we'll perform required cleanup - * as part of shutdown process). + * Here we try to restore the original BIOS settings to avoid + * upsetting it. */ -static int i8042_suspend(struct platform_device *dev, pm_message_t state) +static int i8042_pm_reset(struct device *dev) { - if (!i8042_suspended && state.event == PM_EVENT_SUSPEND) - i8042_controller_reset(); - - i8042_suspended = state.event == PM_EVENT_SUSPEND || - state.event == PM_EVENT_FREEZE; + i8042_controller_reset(); return 0; } - /* - * Here we try to reset everything back to a state in which suspended + * Here we try to reset everything back to a state we had + * before suspending. */ -static int i8042_resume(struct platform_device *dev) +static int i8042_pm_restore(struct device *dev) { int error; -/* - * Do not bother with restoring state if we haven't suspened yet - */ - if (!i8042_suspended) - return 0; - error = i8042_controller_check(); if (error) return error; @@ -1001,11 +987,18 @@ static int i8042_resume(struct platform_device *dev) if (i8042_ports[I8042_KBD_PORT_NO].serio) i8042_enable_kbd_port(); - i8042_suspended = false; i8042_interrupt(0, NULL); return 0; } + +static const struct dev_pm_ops i8042_pm_ops = { + .suspend = i8042_pm_reset, + .resume = i8042_pm_restore, + .poweroff = i8042_pm_reset, + .restore = i8042_pm_restore, +}; + #endif /* CONFIG_PM */ /* @@ -1251,14 +1244,13 @@ static struct platform_driver i8042_driver = { .driver = { .name = "i8042", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &i8042_pm_ops, +#endif }, .probe = i8042_probe, .remove = __devexit_p(i8042_remove), .shutdown = i8042_shutdown, -#ifdef CONFIG_PM - .suspend = i8042_suspend, - .resume = i8042_resume, -#endif }; static int __init i8042_init(void) -- cgit v1.2.3-70-g09d2 From 633aae23ff31bef692a70772652e753a0ae59b81 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 22 Jul 2009 21:51:36 -0700 Subject: Input: serio - switch to using dev_pm_ops Signed-off-by: Dmitry Torokhov --- drivers/input/serio/serio.c | 23 +++++++++++------------ include/linux/serio.h | 2 -- 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index d66f4944f2a..0236f0d5fd9 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -931,15 +931,11 @@ static int serio_uevent(struct device *dev, struct kobj_uevent_env *env) #endif /* CONFIG_HOTPLUG */ #ifdef CONFIG_PM -static int serio_suspend(struct device *dev, pm_message_t state) +static int serio_suspend(struct device *dev) { struct serio *serio = to_serio_port(dev); - if (!serio->suspended && state.event == PM_EVENT_SUSPEND) - serio_cleanup(serio); - - serio->suspended = state.event == PM_EVENT_SUSPEND || - state.event == PM_EVENT_FREEZE; + serio_cleanup(serio); return 0; } @@ -952,13 +948,17 @@ static int serio_resume(struct device *dev) * Driver reconnect can take a while, so better let kseriod * deal with it. */ - if (serio->suspended) { - serio->suspended = false; - serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT); - } + serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT); return 0; } + +static const struct dev_pm_ops serio_pm_ops = { + .suspend = serio_suspend, + .resume = serio_resume, + .poweroff = serio_suspend, + .restore = serio_resume, +}; #endif /* CONFIG_PM */ /* called from serio_driver->connect/disconnect methods under serio_mutex */ @@ -1015,8 +1015,7 @@ static struct bus_type serio_bus = { .remove = serio_driver_remove, .shutdown = serio_shutdown, #ifdef CONFIG_PM - .suspend = serio_suspend, - .resume = serio_resume, + .pm = &serio_pm_ops, #endif }; diff --git a/include/linux/serio.h b/include/linux/serio.h index 126d24c9eaa..a640bc2afe7 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -31,8 +31,6 @@ struct serio { bool manual_bind; bool registered; /* port has been fully registered with driver core */ - bool suspended; /* port is suspended */ - struct serio_device_id id; -- cgit v1.2.3-70-g09d2 From c6fe6b0783a8fd923d11dd0388cbd561ff15bdf1 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sun, 2 Aug 2009 15:13:29 +0200 Subject: parisc: hp_sdc_mlc.c - check return value of down_trylock() Signed-off-by: Helge Deller --- drivers/input/serio/hp_sdc_mlc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c index b587e2d576a..820e51673b2 100644 --- a/drivers/input/serio/hp_sdc_mlc.c +++ b/drivers/input/serio/hp_sdc_mlc.c @@ -296,7 +296,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) priv->tseq[3] = 0; if (mlc->opacket & HIL_CTRL_APE) { priv->tseq[3] |= HP_SDC_LPC_APE_IPF; - down_trylock(&mlc->csem); + BUG_ON(down_trylock(&mlc->csem)); } enqueue: hp_sdc_enqueue_transaction(&priv->trans); -- cgit v1.2.3-70-g09d2 From fc69f4a6af49ee69475dc4217924d9edf77760e0 Mon Sep 17 00:00:00 2001 From: Tai-hwa Liang Date: Sun, 10 May 2009 18:15:39 -0700 Subject: Input: add new driver for Sentelic Finger Sensing Pad This is the driver for Sentelic Finger Sensing Pad which can be found on MSI WIND Netbook. Signed-off-by: Tai-hwa Liang Signed-off-by: Dmitry Torokhov --- Documentation/input/sentelic.txt | 475 ++++++++++++++++++++ drivers/input/mouse/Kconfig | 8 + drivers/input/mouse/Makefile | 1 + drivers/input/mouse/psmouse-base.c | 26 +- drivers/input/mouse/psmouse.h | 1 + drivers/input/mouse/sentelic.c | 867 +++++++++++++++++++++++++++++++++++++ drivers/input/mouse/sentelic.h | 98 +++++ drivers/input/serio/libps2.c | 15 +- include/linux/libps2.h | 1 + 9 files changed, 1488 insertions(+), 4 deletions(-) create mode 100644 Documentation/input/sentelic.txt create mode 100644 drivers/input/mouse/sentelic.c create mode 100644 drivers/input/mouse/sentelic.h (limited to 'drivers/input/serio') diff --git a/Documentation/input/sentelic.txt b/Documentation/input/sentelic.txt new file mode 100644 index 00000000000..f7160a2fb6a --- /dev/null +++ b/Documentation/input/sentelic.txt @@ -0,0 +1,475 @@ +Copyright (C) 2002-2008 Sentelic Corporation. +Last update: Oct-31-2008 + +============================================================================== +* Finger Sensing Pad Intellimouse Mode(scrolling wheel, 4th and 5th buttons) +============================================================================== +A) MSID 4: Scrolling wheel mode plus Forward page(4th button) and Backward + page (5th button) +@1. Set sample rate to 200; +@2. Set sample rate to 200; +@3. Set sample rate to 80; +@4. Issuing the "Get device ID" command (0xF2) and waits for the response; +@5. FSP will respond 0x04. + +Packet 1 + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |Y|X|y|x|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 | | |B|F|W|W|W|W| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7 => Y overflow + Bit6 => X overflow + Bit5 => Y sign bit + Bit4 => X sign bit + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: X Movement(9-bit 2's complement integers) +Byte 3: Y Movement(9-bit 2's complement integers) +Byte 4: Bit3~Bit0 => the scrolling wheel's movement since the last data report. + valid values, -8 ~ +7 + Bit4 => 1 = 4th mouse button is pressed, Forward one page. + 0 = 4th mouse button is not pressed. + Bit5 => 1 = 5th mouse button is pressed, Backward one page. + 0 = 5th mouse button is not pressed. + +B) MSID 6: Horizontal and Vertical scrolling. +@ Set bit 1 in register 0x40 to 1 + +# FSP replaces scrolling wheel's movement as 4 bits to show horizontal and + vertical scrolling. + +Packet 1 + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |Y|X|y|x|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 | | |B|F|l|r|u|d| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7 => Y overflow + Bit6 => X overflow + Bit5 => Y sign bit + Bit4 => X sign bit + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: X Movement(9-bit 2's complement integers) +Byte 3: Y Movement(9-bit 2's complement integers) +Byte 4: Bit0 => the Vertical scrolling movement downward. + Bit1 => the Vertical scrolling movement upward. + Bit2 => the Vertical scrolling movement rightward. + Bit3 => the Vertical scrolling movement leftward. + Bit4 => 1 = 4th mouse button is pressed, Forward one page. + 0 = 4th mouse button is not pressed. + Bit5 => 1 = 5th mouse button is pressed, Backward one page. + 0 = 5th mouse button is not pressed. + +C) MSID 7: +# FSP uses 2 packets(8 Bytes) data to represent Absolute Position + so we have PACKET NUMBER to identify packets. + If PACKET NUMBER is 0, the packet is Packet 1. + If PACKET NUMBER is 1, the packet is Packet 2. + Please count this number in program. + +# MSID6 special packet will be enable at the same time when enable MSID 7. + +============================================================================== +* Absolute position for STL3886-G0. +============================================================================== +@ Set bit 2 or 3 in register 0x40 to 1 +@ Set bit 6 in register 0x40 to 1 + +Packet 1 (ABSOLUTE POSITION) + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |0|1|V|1|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |r|l|d|u|X|X|Y|Y| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordination packet + => 10, Notify packet + Bit5 => valid bit + Bit4 => 1 + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: X coordinate (xpos[9:2]) +Byte 3: Y coordinate (ypos[9:2]) +Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0]) + Bit3~Bit2 => X coordinate (ypos[1:0]) + Bit4 => scroll up + Bit5 => scroll down + Bit6 => scroll left + Bit7 => scroll right + +Notify Packet for G0 + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |1|0|0|1|1|M|R|L| 2 |C|C|C|C|C|C|C|C| 3 |M|M|M|M|M|M|M|M| 4 |0|0|0|0|0|0|0|0| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordination packet + => 10, Notify packet + Bit5 => 0 + Bit4 => 1 + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: Message Type => 0x5A (Enable/Disable status packet) + Mode Type => 0xA5 (Normal/Icon mode status) +Byte 3: Message Type => 0x00 (Disabled) + => 0x01 (Enabled) + Mode Type => 0x00 (Normal) + => 0x01 (Icon) +Byte 4: Bit7~Bit0 => Don't Care + +============================================================================== +* Absolute position for STL3888-A0. +============================================================================== +Packet 1 (ABSOLUTE POSITION) + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |0|1|V|A|1|L|0|1| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |x|x|y|y|X|X|Y|Y| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordination packet + => 10, Notify packet + Bit5 => Valid bit, 0 means that the coordinate is invalid or finger up. + When both fingers are up, the last two reports have zero valid + bit. + Bit4 => arc + Bit3 => 1 + Bit2 => Left Button, 1 is pressed, 0 is released. + Bit1 => 0 + Bit0 => 1 +Byte 2: X coordinate (xpos[9:2]) +Byte 3: Y coordinate (ypos[9:2]) +Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0]) + Bit3~Bit2 => X coordinate (ypos[1:0]) + Bit5~Bit4 => y1_g + Bit7~Bit6 => x1_g + +Packet 2 (ABSOLUTE POSITION) + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |0|1|V|A|1|R|1|0| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |x|x|y|y|X|X|Y|Y| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordinates packet + => 10, Notify packet + Bit5 => Valid bit, 0 means that the coordinate is invalid or finger up. + When both fingers are up, the last two reports have zero valid + bit. + Bit4 => arc + Bit3 => 1 + Bit2 => Right Button, 1 is pressed, 0 is released. + Bit1 => 1 + Bit0 => 0 +Byte 2: X coordinate (xpos[9:2]) +Byte 3: Y coordinate (ypos[9:2]) +Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0]) + Bit3~Bit2 => X coordinate (ypos[1:0]) + Bit5~Bit4 => y2_g + Bit7~Bit6 => x2_g + +Notify Packet for STL3888-A0 + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |1|0|1|P|1|M|R|L| 2 |C|C|C|C|C|C|C|C| 3 |0|0|F|F|0|0|0|i| 4 |r|l|d|u|0|0|0|0| + |---------------| |---------------| |---------------| |---------------| + +Byte 1: Bit7~Bit6 => 00, Normal data packet + => 01, Absolute coordination packet + => 10, Notify packet + Bit5 => 1 + Bit4 => when in absolute coordinates mode (valid when EN_PKT_GO is 1): + 0: left button is generated by the on-pad command + 1: left button is generated by the external button + Bit3 => 1 + Bit2 => Middle Button, 1 is pressed, 0 is not pressed. + Bit1 => Right Button, 1 is pressed, 0 is not pressed. + Bit0 => Left Button, 1 is pressed, 0 is not pressed. +Byte 2: Message Type => 0xB7 (Multi Finger, Multi Coordinate mode) +Byte 3: Bit7~Bit6 => Don't care + Bit5~Bit4 => Number of fingers + Bit3~Bit1 => Reserved + Bit0 => 1: enter gesture mode; 0: leaving gesture mode +Byte 4: Bit7 => scroll right button + Bit6 => scroll left button + Bit5 => scroll down button + Bit4 => scroll up button + * Note that if gesture and additional button (Bit4~Bit7) + happen at the same time, the button information will not + be sent. + Bit3~Bit0 => Reserved + +Sample sequence of Multi-finger, Multi-coordinate mode: + + notify packet (valid bit == 1), abs pkt 1, abs pkt 2, abs pkt 1, + abs pkt 2, ..., notify packet(valid bit == 0) + +============================================================================== +* FSP Enable/Disable packet +============================================================================== + Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------| + 1 |Y|X|0|0|1|M|R|L| 2 |0|1|0|1|1|0|1|E| 3 | | | | | | | | | 4 | | | | | | | | | + |---------------| |---------------| |---------------| |---------------| + +FSP will send out enable/disable packet when FSP receive PS/2 enable/disable +command. Host will receive the packet which Middle, Right, Left button will +be set. The packet only use byte 0 and byte 1 as a pattern of original packet. +Ignore the other bytes of the packet. + +Byte 1: Bit7 => 0, Y overflow + Bit6 => 0, X overflow + Bit5 => 0, Y sign bit + Bit4 => 0, X sign bit + Bit3 => 1 + Bit2 => 1, Middle Button + Bit1 => 1, Right Button + Bit0 => 1, Left Button +Byte 2: Bit7~1 => (0101101b) + Bit0 => 1 = Enable + 0 = Disable +Byte 3: Don't care +Byte 4: Don't care (MOUSE ID 3, 4) +Byte 5~8: Don't care (Absolute packet) + +============================================================================== +* PS/2 Command Set +============================================================================== + +FSP supports basic PS/2 commanding set and modes, refer to following URL for +details about PS/2 commands: + +http://www.computer-engineering.org/index.php?title=PS/2_Mouse_Interface + +============================================================================== +* Programming Sequence for Determining Packet Parsing Flow +============================================================================== +1. Identify FSP by reading device ID(0x00) and version(0x01) register + +2. Determine number of buttons by reading status2 (0x0b) register + + buttons = reg[0x0b] & 0x30 + + if buttons == 0x30 or buttons == 0x20: + # two/four buttons + Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse' + section A for packet parsing detail(ignore byte 4, bit ~ 7) + elif buttons == 0x10: + # 6 buttons + Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse' + section B for packet parsing detail + elif buttons == 0x00: + # 6 buttons + Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse' + section A for packet parsing detail + +============================================================================== +* Programming Sequence for Register Reading/Writing +============================================================================== + +Register inversion requirement: + + Following values needed to be inverted(the '~' operator in C) before being +sent to FSP: + + 0xe9, 0xee, 0xf2 and 0xff. + +Register swapping requirement: + + Following values needed to have their higher 4 bits and lower 4 bits being +swapped before being sent to FSP: + + 10, 20, 40, 60, 80, 100 and 200. + +Register reading sequence: + + 1. send 0xf3 PS/2 command to FSP; + + 2. send 0x66 PS/2 command to FSP; + + 3. send 0x88 PS/2 command to FSP; + + 4. send 0xf3 PS/2 command to FSP; + + 5. if the register address being to read is not required to be + inverted(refer to the 'Register inversion requirement' section), + goto step 6 + + 5a. send 0x68 PS/2 command to FSP; + + 5b. send the inverted register address to FSP and goto step 8; + + 6. if the register address being to read is not required to be + swapped(refer to the 'Register swapping requirement' section), + goto step 7 + + 6a. send 0xcc PS/2 command to FSP; + + 6b. send the swapped register address to FSP and goto step 8; + + 7. send 0x66 PS/2 command to FSP; + + 7a. send the original register address to FSP and goto step 8; + + 8. send 0xe9(status request) PS/2 command to FSP; + + 9. the response read from FSP should be the requested register value. + +Register writing sequence: + + 1. send 0xf3 PS/2 command to FSP; + + 2. if the register address being to write is not required to be + inverted(refer to the 'Register inversion requirement' section), + goto step 3 + + 2a. send 0x74 PS/2 command to FSP; + + 2b. send the inverted register address to FSP and goto step 5; + + 3. if the register address being to write is not required to be + swapped(refer to the 'Register swapping requirement' section), + goto step 4 + + 3a. send 0x77 PS/2 command to FSP; + + 3b. send the swapped register address to FSP and goto step 5; + + 4. send 0x55 PS/2 command to FSP; + + 4a. send the register address to FSP and goto step 5; + + 5. send 0xf3 PS/2 command to FSP; + + 6. if the register value being to write is not required to be + inverted(refer to the 'Register inversion requirement' section), + goto step 7 + + 6a. send 0x47 PS/2 command to FSP; + + 6b. send the inverted register value to FSP and goto step 9; + + 7. if the register value being to write is not required to be + swapped(refer to the 'Register swapping requirement' section), + goto step 8 + + 7a. send 0x44 PS/2 command to FSP; + + 7b. send the swapped register value to FSP and goto step 9; + + 8. send 0x33 PS/2 command to FSP; + + 8a. send the register value to FSP; + + 9. the register writing sequence is completed. + +============================================================================== +* Register Listing +============================================================================== + +offset width default r/w name +0x00 bit7~bit0 0x01 RO device ID + +0x01 bit7~bit0 0xc0 RW version ID + +0x02 bit7~bit0 0x01 RO vendor ID + +0x03 bit7~bit0 0x01 RO product ID + +0x04 bit3~bit0 0x01 RW revision ID + +0x0b RO test mode status 1 + bit3 1 RO 0: rotate 180 degree, 1: no rotation + + bit5~bit4 RO number of buttons + 11 => 2, lbtn/rbtn + 10 => 4, lbtn/rbtn/scru/scrd + 01 => 6, lbtn/rbtn/scru/scrd/scrl/scrr + 00 => 6, lbtn/rbtn/scru/scrd/fbtn/bbtn + +0x0f RW register file page control + bit0 0 RW 1 to enable page 1 register files + +0x10 RW system control 1 + bit0 1 RW Reserved, must be 1 + bit1 0 RW Reserved, must be 0 + bit4 1 RW Reserved, must be 0 + bit5 0 RW register clock gating enable + 0: read only, 1: read/write enable + (Note that following registers does not require clock gating being + enabled prior to write: 05 06 07 08 09 0c 0f 10 11 12 16 17 18 23 2e + 40 41 42 43.) + +0x31 RW on-pad command detection + bit7 0 RW on-pad command left button down tag + enable + 0: disable, 1: enable + +0x34 RW on-pad command control 5 + bit4~bit0 0x05 RW XLO in 0s/4/1, so 03h = 0010.1b = 2.5 + (Note that position unit is in 0.5 scanline) + + bit7 0 RW on-pad tap zone enable + 0: disable, 1: enable + +0x35 RW on-pad command control 6 + bit4~bit0 0x1d RW XHI in 0s/4/1, so 19h = 1100.1b = 12.5 + (Note that position unit is in 0.5 scanline) + +0x36 RW on-pad command control 7 + bit4~bit0 0x04 RW YLO in 0s/4/1, so 03h = 0010.1b = 2.5 + (Note that position unit is in 0.5 scanline) + +0x37 RW on-pad command control 8 + bit4~bit0 0x13 RW YHI in 0s/4/1, so 11h = 1000.1b = 8.5 + (Note that position unit is in 0.5 scanline) + +0x40 RW system control 5 + bit1 0 RW FSP Intellimouse mode enable + 0: disable, 1: enable + + bit2 0 RW movement + abs. coordinate mode enable + 0: disable, 1: enable + (Note that this function has the functionality of bit 1 even when + bit 1 is not set. However, the format is different from that of bit 1. + In addition, when bit 1 and bit 2 are set at the same time, bit 2 will + override bit 1.) + + bit3 0 RW abs. coordinate only mode enable + 0: disable, 1: enable + (Note that this function has the functionality of bit 1 even when + bit 1 is not set. However, the format is different from that of bit 1. + In addition, when bit 1, bit 2 and bit 3 are set at the same time, + bit 3 will override bit 1 and 2.) + + bit5 0 RW auto switch enable + 0: disable, 1: enable + + bit6 0 RW G0 abs. + notify packet format enable + 0: disable, 1: enable + (Note that the absolute/relative coordinate output still depends on + bit 2 and 3. That is, if any of those bit is 1, host will receive + absolute coordinates; otherwise, host only receives packets with + relative coordinate.) + +0x43 RW on-pad control + bit0 0 RW on-pad control enable + 0: disable, 1: enable + (Note that if this bit is cleared, bit 3/5 will be ineffective) + + bit3 0 RW on-pad fix vertical scrolling enable + 0: disable, 1: enable + + bit5 0 RW on-pad fix horizontal scrolling enable + 0: disable, 1: enable diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 90bef5d498f..3feeb3af8ab 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -107,6 +107,14 @@ config MOUSE_PS2_ELANTECH entries. For further information, see . +config MOUSE_PS2_SENTELIC + bool "Sentelic Finger Sensing Pad PS/2 protocol extension" + depends on MOUSE_PS2 + help + Say Y here if you have a laptop (such as MSI WIND Netbook) + with Sentelic Finger Sensing Pad touchpad. + + If unsure, say N. config MOUSE_PS2_TOUCHKIT bool "eGalax TouchKit PS/2 protocol extension" diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index ea58c9a372b..570c84a4a65 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -27,5 +27,6 @@ psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o +psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index b407b355dce..df318887ca0 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -30,6 +30,7 @@ #include "trackpoint.h" #include "touchkit_ps2.h" #include "elantech.h" +#include "sentelic.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -666,6 +667,20 @@ static int psmouse_extensions(struct psmouse *psmouse, max_proto = PSMOUSE_IMEX; } +/* + * Try Finger Sensing Pad + */ + if (max_proto > PSMOUSE_IMEX) { + if (fsp_detect(psmouse, set_properties) == 0) { + if (!set_properties || fsp_init(psmouse) == 0) + return PSMOUSE_FSP; +/* + * Init failed, try basic relative protocols + */ + max_proto = PSMOUSE_IMEX; + } + } + if (max_proto > PSMOUSE_IMEX) { if (genius_detect(psmouse, set_properties) == 0) return PSMOUSE_GENPS; @@ -813,7 +828,16 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = elantech_detect, .init = elantech_init, }, - #endif +#endif +#ifdef CONFIG_MOUSE_PS2_SENTELIC + { + .type = PSMOUSE_FSP, + .name = "FSPPS/2", + .alias = "fsp", + .detect = fsp_detect, + .init = fsp_init, + }, +#endif { .type = PSMOUSE_CORTRON, .name = "CortronPS/2", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index e3562daeb2e..cca1744c2a0 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -91,6 +91,7 @@ enum psmouse_type { PSMOUSE_CORTRON, PSMOUSE_HGPK, PSMOUSE_ELANTECH, + PSMOUSE_FSP, PSMOUSE_AUTO /* This one should always be last */ }; diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c new file mode 100644 index 00000000000..97b1e72855a --- /dev/null +++ b/drivers/input/mouse/sentelic.c @@ -0,0 +1,867 @@ +/*- + * Finger Sensing Pad PS/2 mouse driver. + * + * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. + * Copyright (C) 2005-2009 Tai-hwa Liang, Sentelic Corporation. + * + * 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "psmouse.h" +#include "sentelic.h" + +/* + * Timeout for FSP PS/2 command only (in milliseconds). + */ +#define FSP_CMD_TIMEOUT 200 +#define FSP_CMD_TIMEOUT2 30 + +/** Driver version. */ +static const char fsp_drv_ver[] = "1.0.0-K"; + +/* + * Make sure that the value being sent to FSP will not conflict with + * possible sample rate values. + */ +static unsigned char fsp_test_swap_cmd(unsigned char reg_val) +{ + switch (reg_val) { + case 10: case 20: case 40: case 60: case 80: case 100: case 200: + /* + * The requested value being sent to FSP matched to possible + * sample rates, swap the given value such that the hardware + * wouldn't get confused. + */ + return (reg_val >> 4) | (reg_val << 4); + default: + return reg_val; /* swap isn't necessary */ + } +} + +/* + * Make sure that the value being sent to FSP will not conflict with certain + * commands. + */ +static unsigned char fsp_test_invert_cmd(unsigned char reg_val) +{ + switch (reg_val) { + case 0xe9: case 0xee: case 0xf2: case 0xff: + /* + * The requested value being sent to FSP matched to certain + * commands, inverse the given value such that the hardware + * wouldn't get confused. + */ + return ~reg_val; + default: + return reg_val; /* inversion isn't necessary */ + } +} + +static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + unsigned char addr; + int rc = -1; + + /* + * We need to shut off the device and switch it into command + * mode so we don't confuse our protocol handler. We don't need + * to do that for writes because sysfs set helper does this for + * us. + */ + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + mutex_lock(&ps2dev->cmd_mutex); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + /* should return 0xfe(request for resending) */ + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + /* should return 0xfc(failed) */ + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) { + ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2); + } else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2); + /* expect 0xfe */ + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + /* expect 0xfe */ + } + /* should return 0xfc(failed) */ + ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT); + + if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0) + goto out; + + *reg_val = param[2]; + rc = 0; + + out: + mutex_unlock(&ps2dev->cmd_mutex); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", + reg_addr, *reg_val, rc); + return rc; +} + +static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char v; + int rc = -1; + + mutex_lock(&ps2dev->cmd_mutex); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) { + /* inversion is required */ + ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2); + } else { + if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2); + } + } + /* write the register address in correct order */ + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + return -1; + + if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { + /* inversion is required */ + ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); + } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); + } + + /* write the register value in correct order */ + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + rc = 0; + + out: + mutex_unlock(&ps2dev->cmd_mutex); + dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", + reg_addr, reg_val, rc); + return rc; +} + +/* Enable register clock gating for writing certain registers */ +static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable) +{ + int v, nv; + + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1) + return -1; + + if (enable) + nv = v | FSP_BIT_EN_REG_CLK; + else + nv = v & ~FSP_BIT_EN_REG_CLK; + + /* only write if necessary */ + if (nv != v) + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1) + return -1; + + return 0; +} + +static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + int rc = -1; + + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + mutex_lock(&ps2dev->cmd_mutex); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + /* get the returned result */ + if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + goto out; + + *reg_val = param[2]; + rc = 0; + + out: + mutex_unlock(&ps2dev->cmd_mutex); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", + *reg_val, rc); + return rc; +} + +static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char v; + int rc = -1; + + mutex_lock(&ps2dev->cmd_mutex); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + return -1; + + if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { + ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); + } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); + } + + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + rc = 0; + + out: + mutex_unlock(&ps2dev->cmd_mutex); + dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", + reg_val, rc); + return rc; +} + +static int fsp_get_version(struct psmouse *psmouse, int *version) +{ + if (fsp_reg_read(psmouse, FSP_REG_VERSION, version)) + return -EIO; + + return 0; +} + +static int fsp_get_revision(struct psmouse *psmouse, int *rev) +{ + if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev)) + return -EIO; + + return 0; +} + +static int fsp_get_buttons(struct psmouse *psmouse, int *btn) +{ + static const int buttons[] = { + 0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */ + 0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */ + 0x04, /* Left/Middle/Right & Scroll Up/Down */ + 0x02, /* Left/Middle/Right */ + }; + int val; + + if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS1, &val) == -1) + return -EIO; + + *btn = buttons[(val & 0x30) >> 4]; + return 0; +} + +/* Enable on-pad command tag output */ +static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) +{ + int v, nv; + int res = 0; + + if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) { + dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n"); + return -EIO; + } + + if (enable) + nv = v | FSP_BIT_EN_OPC_TAG; + else + nv = v & ~FSP_BIT_EN_OPC_TAG; + + /* only write if necessary */ + if (nv != v) { + fsp_reg_write_enable(psmouse, true); + res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv); + fsp_reg_write_enable(psmouse, false); + } + + if (res != 0) { + dev_err(&psmouse->ps2dev.serio->dev, + "Unable to enable OPC tag.\n"); + res = -EIO; + } + + return res; +} + +static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable) +{ + struct fsp_data *pad = psmouse->private; + int val; + + if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) + return -EIO; + + pad->vscroll = enable; + + if (enable) + val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE); + else + val &= ~FSP_BIT_FIX_VSCR; + + if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) + return -EIO; + + return 0; +} + +static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable) +{ + struct fsp_data *pad = psmouse->private; + int val, v2; + + if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) + return -EIO; + + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2)) + return -EIO; + + pad->hscroll = enable; + + if (enable) { + val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE); + v2 |= FSP_BIT_EN_MSID6; + } else { + val &= ~FSP_BIT_FIX_HSCR; + v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8); + } + + if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) + return -EIO; + + /* reconfigure horizontal scrolling packet output */ + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2)) + return -EIO; + + return 0; +} + +/* + * Write device specific initial parameters. + * + * ex: 0xab 0xcd - write oxcd into register 0xab + */ +static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned long reg, val; + char *rest; + ssize_t retval; + + reg = simple_strtoul(buf, &rest, 16); + if (rest == buf || *rest != ' ' || reg > 0xff) + return -EINVAL; + + if (strict_strtoul(rest + 1, 16, &val) || val > 0xff) + return -EINVAL; + + if (fsp_reg_write_enable(psmouse, true)) + return -EIO; + + retval = fsp_reg_write(psmouse, reg, val) < 0 ? -EIO : count; + + fsp_reg_write_enable(psmouse, false); + + return count; +} + +PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg); + +static ssize_t fsp_attr_show_getreg(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%02x%02x\n", pad->last_reg, pad->last_val); +} + +/* + * Read a register from device. + * + * ex: 0xab -- read content from register 0xab + */ +static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct fsp_data *pad = psmouse->private; + unsigned long reg; + int val; + + if (strict_strtoul(buf, 16, ®) || reg > 0xff) + return -EINVAL; + + if (fsp_reg_read(psmouse, reg, &val)) + return -EIO; + + pad->last_reg = reg; + pad->last_val = val; + + return count; +} + +PSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_getreg, fsp_attr_set_getreg); + +static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse, + void *data, char *buf) +{ + int val = 0; + + if (fsp_page_reg_read(psmouse, &val)) + return -EIO; + + return sprintf(buf, "%02x\n", val); +} + +static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned long val; + + if (strict_strtoul(buf, 16, &val) || val > 0xff) + return -EINVAL; + + if (fsp_page_reg_write(psmouse, val)) + return -EIO; + + return count; +} + +PSMOUSE_DEFINE_ATTR(page, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_pagereg, fsp_attr_set_pagereg); + +static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%d\n", pad->vscroll); +} + +static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned long val; + + if (strict_strtoul(buf, 10, &val) || val > 1) + return -EINVAL; + + fsp_onpad_vscr(psmouse, val); + + return count; +} + +PSMOUSE_DEFINE_ATTR(vscroll, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_vscroll, fsp_attr_set_vscroll); + +static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%d\n", pad->hscroll); +} + +static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned long val; + + if (strict_strtoul(buf, 10, &val) || val > 1) + return -EINVAL; + + fsp_onpad_hscr(psmouse, val); + + return count; +} + +PSMOUSE_DEFINE_ATTR(hscroll, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_hscroll, fsp_attr_set_hscroll); + +static ssize_t fsp_attr_show_flags(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%c\n", + pad->flags & FSPDRV_FLAG_EN_OPC ? 'C' : 'c'); +} + +static ssize_t fsp_attr_set_flags(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct fsp_data *pad = psmouse->private; + size_t i; + + for (i = 0; i < count; i++) { + switch (buf[i]) { + case 'C': + pad->flags |= FSPDRV_FLAG_EN_OPC; + break; + case 'c': + pad->flags &= ~FSPDRV_FLAG_EN_OPC; + break; + default: + return -EINVAL; + } + } + return count; +} + +PSMOUSE_DEFINE_ATTR(flags, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_flags, fsp_attr_set_flags); + +static ssize_t fsp_attr_show_ver(struct psmouse *psmouse, + void *data, char *buf) +{ + return sprintf(buf, "Sentelic FSP kernel module %s\n", fsp_drv_ver); +} + +PSMOUSE_DEFINE_RO_ATTR(ver, S_IRUGO, NULL, fsp_attr_show_ver); + +static struct attribute *fsp_attributes[] = { + &psmouse_attr_setreg.dattr.attr, + &psmouse_attr_getreg.dattr.attr, + &psmouse_attr_page.dattr.attr, + &psmouse_attr_vscroll.dattr.attr, + &psmouse_attr_hscroll.dattr.attr, + &psmouse_attr_flags.dattr.attr, + &psmouse_attr_ver.dattr.attr, + NULL +}; + +static struct attribute_group fsp_attribute_group = { + .attrs = fsp_attributes, +}; + +#ifdef FSP_DEBUG +static void fsp_packet_debug(unsigned char packet[]) +{ + static unsigned int ps2_packet_cnt; + static unsigned int ps2_last_second; + unsigned int jiffies_msec; + + ps2_packet_cnt++; + jiffies_msec = jiffies_to_msecs(jiffies); + printk(KERN_DEBUG "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n", + jiffies_msec, packet[0], packet[1], packet[2], packet[3]); + + if (jiffies_msec - ps2_last_second > 1000) { + printk(KERN_DEBUG "PS/2 packets/sec = %d\n", ps2_packet_cnt); + ps2_packet_cnt = 0; + ps2_last_second = jiffies_msec; + } +} +#else +static void fsp_packet_debug(unsigned char packet[]) +{ +} +#endif + +static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct fsp_data *ad = psmouse->private; + unsigned char *packet = psmouse->packet; + unsigned char button_status = 0, lscroll = 0, rscroll = 0; + int rel_x, rel_y; + + if (psmouse->pktcnt < 4) + return PSMOUSE_GOOD_DATA; + + /* + * Full packet accumulated, process it + */ + + switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) { + case FSP_PKT_TYPE_ABS: + dev_warn(&psmouse->ps2dev.serio->dev, + "Unexpected absolute mode packet, ignored.\n"); + break; + + case FSP_PKT_TYPE_NORMAL_OPC: + /* on-pad click, filter it if necessary */ + if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC) + packet[0] &= ~BIT(0); + /* fall through */ + + case FSP_PKT_TYPE_NORMAL: + /* normal packet */ + /* special packet data translation from on-pad packets */ + if (packet[3] != 0) { + if (packet[3] & BIT(0)) + button_status |= 0x01; /* wheel down */ + if (packet[3] & BIT(1)) + button_status |= 0x0f; /* wheel up */ + if (packet[3] & BIT(2)) + button_status |= BIT(5);/* horizontal left */ + if (packet[3] & BIT(3)) + button_status |= BIT(4);/* horizontal right */ + /* push back to packet queue */ + if (button_status != 0) + packet[3] = button_status; + rscroll = (packet[3] >> 4) & 1; + lscroll = (packet[3] >> 5) & 1; + } + /* + * Processing wheel up/down and extra button events + */ + input_report_rel(dev, REL_WHEEL, + (int)(packet[3] & 8) - (int)(packet[3] & 7)); + input_report_rel(dev, REL_HWHEEL, lscroll - rscroll); + input_report_key(dev, BTN_BACK, lscroll); + input_report_key(dev, BTN_FORWARD, rscroll); + + /* + * Standard PS/2 Mouse + */ + input_report_key(dev, BTN_LEFT, packet[0] & 1); + input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); + input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); + + rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0; + rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0; + + input_report_rel(dev, REL_X, rel_x); + input_report_rel(dev, REL_Y, rel_y); + break; + } + + input_sync(dev); + + fsp_packet_debug(packet); + + return PSMOUSE_FULL_PACKET; +} + +static int fsp_activate_protocol(struct psmouse *psmouse) +{ + struct fsp_data *pad = psmouse->private; + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[2]; + int val; + + /* + * Standard procedure to enter FSP Intellimouse mode + * (scrolling wheel, 4th and 5th buttons) + */ + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 80; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + + ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); + if (param[0] != 0x04) { + dev_err(&psmouse->ps2dev.serio->dev, + "Unable to enable 4 bytes packet format.\n"); + return -EIO; + } + + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) { + dev_err(&psmouse->ps2dev.serio->dev, + "Unable to read SYSCTL5 register.\n"); + return -EIO; + } + + val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); + /* Ensure we are not in absolute mode */ + val &= ~FSP_BIT_EN_PKT_G0; + if (pad->buttons == 0x06) { + /* Left/Middle/Right & Scroll Up/Down/Right/Left */ + val |= FSP_BIT_EN_MSID6; + } + + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) { + dev_err(&psmouse->ps2dev.serio->dev, + "Unable to set up required mode bits.\n"); + return -EIO; + } + + /* + * Enable OPC tags such that driver can tell the difference between + * on-pad and real button click + */ + if (fsp_opc_tag_enable(psmouse, true)) + dev_warn(&psmouse->ps2dev.serio->dev, + "Failed to enable OPC tag mode.\n"); + + /* Enable on-pad vertical and horizontal scrolling */ + fsp_onpad_vscr(psmouse, true); + fsp_onpad_hscr(psmouse, true); + + return 0; +} + +int fsp_detect(struct psmouse *psmouse, int set_properties) +{ + int id; + + if (fsp_reg_read(psmouse, FSP_REG_DEVICE_ID, &id)) + return -EIO; + + if (id != 0x01) + return -ENODEV; + + if (set_properties) { + psmouse->vendor = "Sentelic"; + psmouse->name = "FingerSensingPad"; + } + + return 0; +} + +static void fsp_reset(struct psmouse *psmouse) +{ + fsp_opc_tag_enable(psmouse, false); + fsp_onpad_vscr(psmouse, false); + fsp_onpad_hscr(psmouse, false); +} + +static void fsp_disconnect(struct psmouse *psmouse) +{ + sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, + &fsp_attribute_group); + + fsp_reset(psmouse); + kfree(psmouse->private); +} + +static int fsp_reconnect(struct psmouse *psmouse) +{ + int version; + + if (fsp_detect(psmouse, 0)) + return -ENODEV; + + if (fsp_get_version(psmouse, &version)) + return -ENODEV; + + if (fsp_activate_protocol(psmouse)) + return -EIO; + + return 0; +} + +int fsp_init(struct psmouse *psmouse) +{ + struct fsp_data *priv; + int ver, rev, buttons; + int error; + + if (fsp_get_version(psmouse, &ver) || + fsp_get_revision(psmouse, &rev) || + fsp_get_buttons(psmouse, &buttons)) { + return -ENODEV; + } + + printk(KERN_INFO + "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n", + ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7); + + psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ver = ver; + priv->rev = rev; + priv->buttons = buttons; + + /* enable on-pad click by default */ + priv->flags |= FSPDRV_FLAG_EN_OPC; + + /* Set up various supported input event bits */ + __set_bit(BTN_BACK, psmouse->dev->keybit); + __set_bit(BTN_FORWARD, psmouse->dev->keybit); + __set_bit(REL_WHEEL, psmouse->dev->relbit); + __set_bit(REL_HWHEEL, psmouse->dev->relbit); + + psmouse->protocol_handler = fsp_process_byte; + psmouse->disconnect = fsp_disconnect; + psmouse->reconnect = fsp_reconnect; + psmouse->cleanup = fsp_reset; + psmouse->pktsize = 4; + + /* set default packet output based on number of buttons we found */ + error = fsp_activate_protocol(psmouse); + if (error) + goto err_out; + + error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, + &fsp_attribute_group); + if (error) { + dev_err(&psmouse->ps2dev.serio->dev, + "Failed to create sysfs attributes (%d)", error); + goto err_out; + } + + return 0; + + err_out: + kfree(psmouse->private); + psmouse->private = NULL; + return error; +} diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h new file mode 100644 index 00000000000..083559c7282 --- /dev/null +++ b/drivers/input/mouse/sentelic.h @@ -0,0 +1,98 @@ +/*- + * Finger Sensing Pad PS/2 mouse driver. + * + * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. + * Copyright (C) 2005-2009 Tai-hwa Liang, Sentelic Corporation. + * + * 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SENTELIC_H +#define __SENTELIC_H + +/* Finger-sensing Pad information registers */ +#define FSP_REG_DEVICE_ID 0x00 +#define FSP_REG_VERSION 0x01 +#define FSP_REG_REVISION 0x04 +#define FSP_REG_TMOD_STATUS1 0x0B +#define FSP_BIT_NO_ROTATION BIT(3) +#define FSP_REG_PAGE_CTRL 0x0F + +/* Finger-sensing Pad control registers */ +#define FSP_REG_SYSCTL1 0x10 +#define FSP_BIT_EN_REG_CLK BIT(5) +#define FSP_REG_OPC_QDOWN 0x31 +#define FSP_BIT_EN_OPC_TAG BIT(7) +#define FSP_REG_OPTZ_XLO 0x34 +#define FSP_REG_OPTZ_XHI 0x35 +#define FSP_REG_OPTZ_YLO 0x36 +#define FSP_REG_OPTZ_YHI 0x37 +#define FSP_REG_SYSCTL5 0x40 +#define FSP_BIT_90_DEGREE BIT(0) +#define FSP_BIT_EN_MSID6 BIT(1) +#define FSP_BIT_EN_MSID7 BIT(2) +#define FSP_BIT_EN_MSID8 BIT(3) +#define FSP_BIT_EN_AUTO_MSID8 BIT(5) +#define FSP_BIT_EN_PKT_G0 BIT(6) + +#define FSP_REG_ONPAD_CTL 0x43 +#define FSP_BIT_ONPAD_ENABLE BIT(0) +#define FSP_BIT_ONPAD_FBBB BIT(1) +#define FSP_BIT_FIX_VSCR BIT(3) +#define FSP_BIT_FIX_HSCR BIT(5) +#define FSP_BIT_DRAG_LOCK BIT(6) + +/* Finger-sensing Pad packet formating related definitions */ + +/* absolute packet type */ +#define FSP_PKT_TYPE_NORMAL (0x00) +#define FSP_PKT_TYPE_ABS (0x01) +#define FSP_PKT_TYPE_NOTIFY (0x02) +#define FSP_PKT_TYPE_NORMAL_OPC (0x03) +#define FSP_PKT_TYPE_SHIFT (6) + +#ifdef __KERNEL__ + +struct fsp_data { + unsigned char ver; /* hardware version */ + unsigned char rev; /* hardware revison */ + unsigned char buttons; /* Number of buttons */ + unsigned int flags; +#define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */ + + bool vscroll; /* Vertical scroll zone enabled */ + bool hscroll; /* Horizontal scroll zone enabled */ + + unsigned char last_reg; /* Last register we requested read from */ + unsigned char last_val; +}; + +#ifdef CONFIG_MOUSE_PS2_SENTELIC +extern int fsp_detect(struct psmouse *psmouse, int set_properties); +extern int fsp_init(struct psmouse *psmouse); +#else +inline int fsp_detect(struct psmouse *psmouse, int set_properties) +{ + return -ENOSYS; +} +inline int fsp_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif + +#endif /* __KERNEL__ */ + +#endif /* !__SENTELIC_H */ diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index be5bbbb8ae4..3a95b508bf2 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -161,7 +161,7 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) * ps2_command() can only be called from a process context */ -int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) +int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) { int timeout; int send = (command >> 12) & 0xf; @@ -179,8 +179,6 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) return -1; } - mutex_lock(&ps2dev->cmd_mutex); - serio_pause_rx(ps2dev->serio); ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; ps2dev->cmdcnt = receive; @@ -231,7 +229,18 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) ps2dev->flags = 0; serio_continue_rx(ps2dev->serio); + return rc; +} +EXPORT_SYMBOL(__ps2_command); + +int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) +{ + int rc; + + mutex_lock(&ps2dev->cmd_mutex); + rc = __ps2_command(ps2dev, param, command); mutex_unlock(&ps2dev->cmd_mutex); + return rc; } EXPORT_SYMBOL(ps2_command); diff --git a/include/linux/libps2.h b/include/linux/libps2.h index b94534b7e26..fcf5fbe6a50 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -44,6 +44,7 @@ struct ps2dev { void ps2_init(struct ps2dev *ps2dev, struct serio *serio); int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); +int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data); int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data); -- cgit v1.2.3-70-g09d2 From 1c7827ae70e7c8456e08f7bb9ef2238d27814cbe Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 3 Sep 2009 21:45:34 -0700 Subject: Input: i8042 - bypass AUX IRQ delivery test on laptops It seems that many laptops do not fully implement AUX LOOP command in their keyboard controllers, causing issues with touchpad detection. We know however that almost every laptop/portable uses a PS/2 pointing device and, even if user disables it in favor of an external mouse, the system will not use IRQ 12 for anything else. Therefore we may bypass AUX IRQ delivery test when running on a laptop and assume that it is routed properly. Just to be safe we require the box to have good PNP data in order to bypass the test. [Jin Dongming : fix crash caused by missing terminator in the DMI table] Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 33 +++++++++++++++++++++++++++++++++ drivers/input/serio/i8042.c | 4 +++- 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index ae04d8a494e..66829a860ee 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -449,6 +449,34 @@ static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = { }, { } }; + +static struct dmi_system_id __initdata i8042_dmi_laptop_table[] = { + { + .ident = "Portable", + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ + }, + }, + { + .ident = "Laptop", + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + }, + }, + { + .ident = "Notebook", + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + }, + { + .ident = "Sub-Notebook", + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ + }, + }, + { } +}; #endif /* @@ -727,6 +755,11 @@ static int __init i8042_pnp_init(void) i8042_kbd_irq = i8042_pnp_kbd_irq; i8042_aux_irq = i8042_pnp_aux_irq; +#ifdef CONFIG_X86 + i8042_bypass_aux_irq_test = !pnp_data_busted && + dmi_check_system(i8042_dmi_laptop_table); +#endif + return 0; } diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 9f5c0506242..b53a015bf8a 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -83,6 +83,8 @@ module_param_named(debug, i8042_debug, bool, 0600); MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off"); #endif +static bool i8042_bypass_aux_irq_test; + #include "i8042.h" static DEFINE_SPINLOCK(i8042_lock); @@ -641,7 +643,7 @@ static int __devinit i8042_check_aux(void) * used it for a PCI card or somethig else. */ - if (i8042_noloop || aux_loop_broken) { + if (i8042_noloop || i8042_bypass_aux_irq_test || aux_loop_broken) { /* * Without LOOP command we can't test AUX IRQ delivery. Assume the port * is working and hope we are right. -- cgit v1.2.3-70-g09d2 From 5ddbc77c3eb54336fcd44b7b66b44784d65677e2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 9 Sep 2009 19:08:15 -0700 Subject: Input: i8042 - try disabling and re-enabling AUX port at close Ever since we switched from having a polling timer to registering IRQ handlers for both keyboard and AUX ports at the driver registration time, on certain boxes probing for a mouse results in keyboard stopping working. The only real difference between old and new way is that before we disabled ports after unsuccessful probe whereas now we leave them as is. Try to emulate the old behavior by disabling and immediately re-enabling AUX and KBD ports when corresponding serio port is being closed. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 50 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index b53a015bf8a..8aaf8fcacf6 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -264,6 +264,49 @@ static int i8042_aux_write(struct serio *serio, unsigned char c) I8042_CMD_MUX_SEND + port->mux); } + +/* + * i8042_aux_close attempts to clear AUX or KBD port state by disabling + * and then re-enabling it. + */ + +static void i8042_port_close(struct serio *serio) +{ + int irq_bit; + int disable_bit; + const char *port_name; + + if (serio == i8042_ports[I8042_AUX_PORT_NO].serio) { + irq_bit = I8042_CTR_AUXINT; + disable_bit = I8042_CTR_AUXDIS; + port_name = "AUX"; + } else { + irq_bit = I8042_CTR_KBDINT; + disable_bit = I8042_CTR_KBDDIS; + port_name = "KBD"; + } + + i8042_ctr &= ~irq_bit; + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + printk(KERN_WARNING + "i8042.c: Can't write CTR while closing %s port.\n", + port_name); + + udelay(50); + + i8042_ctr &= ~disable_bit; + i8042_ctr |= irq_bit; + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + printk(KERN_ERR "i8042.c: Can't reactivate %s port.\n", + port_name); + + /* + * See if there is any data appeared while we were messing with + * port state. + */ + i8042_interrupt(0, NULL); +} + /* * i8042_start() is called by serio core when port is about to finish * registering. It will mark port as existing so i8042_interrupt can @@ -393,7 +436,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) } /* - * i8042_enable_kbd_port enables keybaord port on chip + * i8042_enable_kbd_port enables keyboard port on chip */ static int i8042_enable_kbd_port(void) @@ -841,6 +884,9 @@ static void i8042_controller_reset(void) i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS; i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT); + if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR)) + printk(KERN_WARNING "i8042.c: Can't write CTR while resetting.\n"); + /* * Disable MUX mode if present. */ @@ -1026,6 +1072,7 @@ static int __devinit i8042_create_kbd_port(void) serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write; serio->start = i8042_start; serio->stop = i8042_stop; + serio->close = i8042_port_close; serio->port_data = port; serio->dev.parent = &i8042_platform_device->dev; strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name)); @@ -1056,6 +1103,7 @@ static int __devinit i8042_create_aux_port(int idx) if (idx < 0) { strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name)); strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); + serio->close = i8042_port_close; } else { snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1); -- cgit v1.2.3-70-g09d2 From 386b384900a200d5fcabdd4a9c27eb21db606cd4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 9 Sep 2009 19:08:16 -0700 Subject: Input: i8042 - use boolean type where it makes sense Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 38 ++++++------- drivers/input/serio/i8042.c | 103 +++++++++++++++++----------------- 2 files changed, 71 insertions(+), 70 deletions(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 66829a860ee..248a6acf669 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -550,9 +550,9 @@ static struct dmi_system_id __initdata i8042_dmi_dritek_table[] = { #ifdef CONFIG_PNP #include -static int i8042_pnp_kbd_registered; +static bool i8042_pnp_kbd_registered; static unsigned int i8042_pnp_kbd_devices; -static int i8042_pnp_aux_registered; +static bool i8042_pnp_aux_registered; static unsigned int i8042_pnp_aux_devices; static int i8042_pnp_command_reg; @@ -640,12 +640,12 @@ static struct pnp_driver i8042_pnp_aux_driver = { static void i8042_pnp_exit(void) { if (i8042_pnp_kbd_registered) { - i8042_pnp_kbd_registered = 0; + i8042_pnp_kbd_registered = false; pnp_unregister_driver(&i8042_pnp_kbd_driver); } if (i8042_pnp_aux_registered) { - i8042_pnp_aux_registered = 0; + i8042_pnp_aux_registered = false; pnp_unregister_driver(&i8042_pnp_aux_driver); } } @@ -653,12 +653,12 @@ static void i8042_pnp_exit(void) static int __init i8042_pnp_init(void) { char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 }; - int pnp_data_busted = 0; + int pnp_data_busted = false; int err; #ifdef CONFIG_X86 if (dmi_check_system(i8042_dmi_nopnp_table)) - i8042_nopnp = 1; + i8042_nopnp = true; #endif if (i8042_nopnp) { @@ -668,11 +668,11 @@ static int __init i8042_pnp_init(void) err = pnp_register_driver(&i8042_pnp_kbd_driver); if (!err) - i8042_pnp_kbd_registered = 1; + i8042_pnp_kbd_registered = true; err = pnp_register_driver(&i8042_pnp_aux_driver); if (!err) - i8042_pnp_aux_registered = 1; + i8042_pnp_aux_registered = true; if (!i8042_pnp_kbd_devices && !i8042_pnp_aux_devices) { i8042_pnp_exit(); @@ -700,9 +700,9 @@ static int __init i8042_pnp_init(void) #if defined(__ia64__) if (!i8042_pnp_kbd_devices) - i8042_nokbd = 1; + i8042_nokbd = true; if (!i8042_pnp_aux_devices) - i8042_noaux = 1; + i8042_noaux = true; #endif if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) && @@ -713,7 +713,7 @@ static int __init i8042_pnp_init(void) "using default %#x\n", i8042_pnp_data_reg, i8042_data_reg); i8042_pnp_data_reg = i8042_data_reg; - pnp_data_busted = 1; + pnp_data_busted = true; } if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) && @@ -724,7 +724,7 @@ static int __init i8042_pnp_init(void) "using default %#x\n", i8042_pnp_command_reg, i8042_command_reg); i8042_pnp_command_reg = i8042_command_reg; - pnp_data_busted = 1; + pnp_data_busted = true; } if (!i8042_nokbd && !i8042_pnp_kbd_irq) { @@ -732,7 +732,7 @@ static int __init i8042_pnp_init(void) "PNP: PS/2 controller doesn't have KBD irq; " "using default %d\n", i8042_kbd_irq); i8042_pnp_kbd_irq = i8042_kbd_irq; - pnp_data_busted = 1; + pnp_data_busted = true; } if (!i8042_noaux && !i8042_pnp_aux_irq) { @@ -741,7 +741,7 @@ static int __init i8042_pnp_init(void) "PNP: PS/2 appears to have AUX port disabled, " "if this is incorrect please boot with " "i8042.nopnp\n"); - i8042_noaux = 1; + i8042_noaux = true; } else { printk(KERN_WARNING "PNP: PS/2 controller doesn't have AUX irq; " @@ -788,21 +788,21 @@ static int __init i8042_platform_init(void) return retval; #if defined(__ia64__) - i8042_reset = 1; + i8042_reset = true; #endif #ifdef CONFIG_X86 if (dmi_check_system(i8042_dmi_reset_table)) - i8042_reset = 1; + i8042_reset = true; if (dmi_check_system(i8042_dmi_noloop_table)) - i8042_noloop = 1; + i8042_noloop = true; if (dmi_check_system(i8042_dmi_nomux_table)) - i8042_nomux = 1; + i8042_nomux = true; if (dmi_check_system(i8042_dmi_dritek_table)) - i8042_dritek = 1; + i8042_dritek = true; #endif /* CONFIG_X86 */ return retval; diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 8aaf8fcacf6..61ed7a966c6 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -28,35 +28,35 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver"); MODULE_LICENSE("GPL"); -static unsigned int i8042_nokbd; +static bool i8042_nokbd; module_param_named(nokbd, i8042_nokbd, bool, 0); MODULE_PARM_DESC(nokbd, "Do not probe or use KBD port."); -static unsigned int i8042_noaux; +static bool i8042_noaux; module_param_named(noaux, i8042_noaux, bool, 0); MODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port."); -static unsigned int i8042_nomux; +static bool i8042_nomux; module_param_named(nomux, i8042_nomux, bool, 0); MODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing conrtoller is present."); -static unsigned int i8042_unlock; +static bool i8042_unlock; module_param_named(unlock, i8042_unlock, bool, 0); MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); -static unsigned int i8042_reset; +static bool i8042_reset; module_param_named(reset, i8042_reset, bool, 0); MODULE_PARM_DESC(reset, "Reset controller during init and cleanup."); -static unsigned int i8042_direct; +static bool i8042_direct; module_param_named(direct, i8042_direct, bool, 0); MODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode."); -static unsigned int i8042_dumbkbd; +static bool i8042_dumbkbd; module_param_named(dumbkbd, i8042_dumbkbd, bool, 0); MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard"); -static unsigned int i8042_noloop; +static bool i8042_noloop; module_param_named(noloop, i8042_noloop, bool, 0); MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port"); @@ -65,20 +65,20 @@ module_param_named(panicblink, i8042_blink_frequency, uint, 0600); MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics"); #ifdef CONFIG_X86 -static unsigned int i8042_dritek; +static bool i8042_dritek; module_param_named(dritek, i8042_dritek, bool, 0); MODULE_PARM_DESC(dritek, "Force enable the Dritek keyboard extension"); #endif #ifdef CONFIG_PNP -static int i8042_nopnp; +static bool i8042_nopnp; module_param_named(nopnp, i8042_nopnp, bool, 0); MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings"); #endif #define DEBUG #ifdef DEBUG -static int i8042_debug; +static bool i8042_debug; module_param_named(debug, i8042_debug, bool, 0600); MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off"); #endif @@ -92,7 +92,7 @@ static DEFINE_SPINLOCK(i8042_lock); struct i8042_port { struct serio *serio; int irq; - unsigned char exists; + bool exists; signed char mux; }; @@ -105,9 +105,9 @@ static struct i8042_port i8042_ports[I8042_NUM_PORTS]; static unsigned char i8042_initial_ctr; static unsigned char i8042_ctr; -static unsigned char i8042_mux_present; -static unsigned char i8042_kbd_irq_registered; -static unsigned char i8042_aux_irq_registered; +static bool i8042_mux_present; +static bool i8042_kbd_irq_registered; +static bool i8042_aux_irq_registered; static unsigned char i8042_suppress_kbd_ack; static struct platform_device *i8042_platform_device; @@ -316,7 +316,7 @@ static int i8042_start(struct serio *serio) { struct i8042_port *port = serio->port_data; - port->exists = 1; + port->exists = true; mb(); return 0; } @@ -330,7 +330,7 @@ static void i8042_stop(struct serio *serio) { struct i8042_port *port = serio->port_data; - port->exists = 0; + port->exists = false; /* * We synchronize with both AUX and KBD IRQs because there is @@ -492,14 +492,15 @@ static int i8042_enable_mux_ports(void) } /* - * i8042_set_mux_mode checks whether the controller has an active - * multiplexor and puts the chip into Multiplexed (1) or Legacy (0) mode. + * i8042_set_mux_mode checks whether the controller has an + * active multiplexor and puts the chip into Multiplexed (true) + * or Legacy (false) mode. */ -static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version) +static int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version) { - unsigned char param; + unsigned char param, val; /* * Get rid of bytes in the queue. */ @@ -511,14 +512,21 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version) * mouse interface, the last should be version. */ - param = 0xf0; - if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xf0) + param = val = 0xf0; + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) + return -1; + param = val = multiplex ? 0x56 : 0xf6; + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) return -1; - param = mode ? 0x56 : 0xf6; - if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != (mode ? 0x56 : 0xf6)) + param = val = multiplex ? 0xa4 : 0xa5; + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == val) return -1; - param = mode ? 0xa4 : 0xa5; - if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == (mode ? 0xa4 : 0xa5)) + +/* + * Workaround for interference with USB Legacy emulation + * that causes a v10.12 MUX to be found. + */ + if (param == 0xac) return -1; if (mux_version) @@ -537,14 +545,7 @@ static int __devinit i8042_check_mux(void) { unsigned char mux_version; - if (i8042_set_mux_mode(1, &mux_version)) - return -1; - -/* - * Workaround for interference with USB Legacy emulation - * that causes a v10.12 MUX to be found. - */ - if (mux_version == 0xAC) + if (i8042_set_mux_mode(true, &mux_version)) return -1; printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", @@ -561,7 +562,7 @@ static int __devinit i8042_check_mux(void) return -EIO; } - i8042_mux_present = 1; + i8042_mux_present = true; return 0; } @@ -570,7 +571,7 @@ static int __devinit i8042_check_mux(void) * The following is used to test AUX IRQ delivery. */ static struct completion i8042_aux_irq_delivered __devinitdata; -static int i8042_irq_being_tested __devinitdata; +static bool i8042_irq_being_tested __devinitdata; static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id) { @@ -597,7 +598,7 @@ static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id) * verifies success by readinng CTR. Used when testing for presence of AUX * port. */ -static int __devinit i8042_toggle_aux(int on) +static int __devinit i8042_toggle_aux(bool on) { unsigned char param; int i; @@ -628,8 +629,8 @@ static int __devinit i8042_toggle_aux(int on) static int __devinit i8042_check_aux(void) { int retval = -1; - int irq_registered = 0; - int aux_loop_broken = 0; + bool irq_registered = false; + bool aux_loop_broken = false; unsigned long flags; unsigned char param; @@ -666,19 +667,19 @@ static int __devinit i8042_check_aux(void) * mark it as broken */ if (!retval) - aux_loop_broken = 1; + aux_loop_broken = true; } /* * Bit assignment test - filters out PS/2 i8042's in AT mode */ - if (i8042_toggle_aux(0)) { + if (i8042_toggle_aux(false)) { printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n"); } - if (i8042_toggle_aux(1)) + if (i8042_toggle_aux(true)) return -1; /* @@ -699,7 +700,7 @@ static int __devinit i8042_check_aux(void) "i8042", i8042_platform_device)) goto out; - irq_registered = 1; + irq_registered = true; if (i8042_enable_aux_port()) goto out; @@ -707,7 +708,7 @@ static int __devinit i8042_check_aux(void) spin_lock_irqsave(&i8042_lock, flags); init_completion(&i8042_aux_irq_delivered); - i8042_irq_being_tested = 1; + i8042_irq_being_tested = true; param = 0xa5; retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); @@ -844,7 +845,7 @@ static int i8042_controller_init(void) */ if (~i8042_ctr & I8042_CTR_XLATE) - i8042_direct = 1; + i8042_direct = true; /* * Set nontranslated mode for the kbd interface if requested by an option. @@ -892,7 +893,7 @@ static void i8042_controller_reset(void) */ if (i8042_mux_present) - i8042_set_mux_mode(0, NULL); + i8042_set_mux_mode(false, NULL); /* * Reset the controller if requested. @@ -1025,7 +1026,7 @@ static int i8042_pm_restore(struct device *dev) #endif if (i8042_mux_present) { - if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports()) + if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports()) printk(KERN_WARNING "i8042: failed to resume active multiplexor, " "mouse won't work.\n"); @@ -1167,7 +1168,7 @@ static void i8042_free_irqs(void) if (i8042_kbd_irq_registered) free_irq(I8042_KBD_IRQ, i8042_platform_device); - i8042_aux_irq_registered = i8042_kbd_irq_registered = 0; + i8042_aux_irq_registered = i8042_kbd_irq_registered = false; } static int __devinit i8042_setup_aux(void) @@ -1201,7 +1202,7 @@ static int __devinit i8042_setup_aux(void) if (aux_enable()) goto err_free_irq; - i8042_aux_irq_registered = 1; + i8042_aux_irq_registered = true; return 0; err_free_irq: @@ -1228,7 +1229,7 @@ static int __devinit i8042_setup_kbd(void) if (error) goto err_free_irq; - i8042_kbd_irq_registered = 1; + i8042_kbd_irq_registered = true; return 0; err_free_irq: -- cgit v1.2.3-70-g09d2 From f81134163fc785622f58af27363079ba1de7c7aa Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 9 Sep 2009 19:08:17 -0700 Subject: Input: i8042 - use platform_driver_probe i8042 is not hot-pluggable and we create the device when we register the driver, so let's save some memory by using platform_device_probe and using __init instead of __devinit. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) (limited to 'drivers/input/serio') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 61ed7a966c6..eb3ff94af58 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -541,7 +541,7 @@ static int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version) * LCS/Telegraphics. */ -static int __devinit i8042_check_mux(void) +static int __init i8042_check_mux(void) { unsigned char mux_version; @@ -570,10 +570,10 @@ static int __devinit i8042_check_mux(void) /* * The following is used to test AUX IRQ delivery. */ -static struct completion i8042_aux_irq_delivered __devinitdata; -static bool i8042_irq_being_tested __devinitdata; +static struct completion i8042_aux_irq_delivered __initdata; +static bool i8042_irq_being_tested __initdata; -static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id) +static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id) { unsigned long flags; unsigned char str, data; @@ -598,7 +598,7 @@ static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id) * verifies success by readinng CTR. Used when testing for presence of AUX * port. */ -static int __devinit i8042_toggle_aux(bool on) +static int __init i8042_toggle_aux(bool on) { unsigned char param; int i; @@ -626,7 +626,7 @@ static int __devinit i8042_toggle_aux(bool on) * the presence of an AUX interface. */ -static int __devinit i8042_check_aux(void) +static int __init i8042_check_aux(void) { int retval = -1; bool irq_registered = false; @@ -1060,7 +1060,7 @@ static void i8042_shutdown(struct platform_device *dev) i8042_controller_reset(); } -static int __devinit i8042_create_kbd_port(void) +static int __init i8042_create_kbd_port(void) { struct serio *serio; struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO]; @@ -1085,7 +1085,7 @@ static int __devinit i8042_create_kbd_port(void) return 0; } -static int __devinit i8042_create_aux_port(int idx) +static int __init i8042_create_aux_port(int idx) { struct serio *serio; int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx; @@ -1117,13 +1117,13 @@ static int __devinit i8042_create_aux_port(int idx) return 0; } -static void __devinit i8042_free_kbd_port(void) +static void __init i8042_free_kbd_port(void) { kfree(i8042_ports[I8042_KBD_PORT_NO].serio); i8042_ports[I8042_KBD_PORT_NO].serio = NULL; } -static void __devinit i8042_free_aux_ports(void) +static void __init i8042_free_aux_ports(void) { int i; @@ -1133,7 +1133,7 @@ static void __devinit i8042_free_aux_ports(void) } } -static void __devinit i8042_register_ports(void) +static void __init i8042_register_ports(void) { int i; @@ -1171,7 +1171,7 @@ static void i8042_free_irqs(void) i8042_aux_irq_registered = i8042_kbd_irq_registered = false; } -static int __devinit i8042_setup_aux(void) +static int __init i8042_setup_aux(void) { int (*aux_enable)(void); int error; @@ -1212,7 +1212,7 @@ static int __devinit i8042_setup_aux(void) return error; } -static int __devinit i8042_setup_kbd(void) +static int __init i8042_setup_kbd(void) { int error; @@ -1239,7 +1239,7 @@ static int __devinit i8042_setup_kbd(void) return error; } -static int __devinit i8042_probe(struct platform_device *dev) +static int __init i8042_probe(struct platform_device *dev) { int error; @@ -1299,7 +1299,6 @@ static struct platform_driver i8042_driver = { .pm = &i8042_pm_ops, #endif }, - .probe = i8042_probe, .remove = __devexit_p(i8042_remove), .shutdown = i8042_shutdown, }; @@ -1318,28 +1317,28 @@ static int __init i8042_init(void) if (err) goto err_platform_exit; - err = platform_driver_register(&i8042_driver); - if (err) - goto err_platform_exit; - i8042_platform_device = platform_device_alloc("i8042", -1); if (!i8042_platform_device) { err = -ENOMEM; - goto err_unregister_driver; + goto err_platform_exit; } err = platform_device_add(i8042_platform_device); if (err) goto err_free_device; + err = platform_driver_probe(&i8042_driver, i8042_probe); + if (err) + goto err_del_device; + panic_blink = i8042_panic_blink; return 0; + err_del_device: + platform_device_del(i8042_platform_device); err_free_device: platform_device_put(i8042_platform_device); - err_unregister_driver: - platform_driver_unregister(&i8042_driver); err_platform_exit: i8042_platform_exit(); @@ -1348,8 +1347,8 @@ static int __init i8042_init(void) static void __exit i8042_exit(void) { - platform_device_unregister(i8042_platform_device); platform_driver_unregister(&i8042_driver); + platform_device_unregister(i8042_platform_device); i8042_platform_exit(); panic_blink = NULL; -- cgit v1.2.3-70-g09d2