diff options
Diffstat (limited to 'drivers/input/serio')
35 files changed, 4960 insertions, 1192 deletions
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index adef447f23e..bc2d47431bd 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -2,9 +2,9 @@ # Input core configuration # config SERIO - tristate "Serial I/O support" if EMBEDDED || !X86 + tristate "Serial I/O support" default y - ---help--- + help Say Yes here if you have any input device that uses serial I/O to communicate with the system. This includes the * standard AT keyboard and PS/2 mouse * @@ -16,13 +16,20 @@ config SERIO To compile this driver as a module, choose M here: the module will be called serio. +config ARCH_MIGHT_HAVE_PC_SERIO + bool + help + Select this config option from the architecture Kconfig if + the architecture might use a PC serio device (i8042) to + communicate with keyboard, mouse, etc. + if SERIO config SERIO_I8042 - tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86 + tristate "i8042 PC Keyboard controller" default y - depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K - ---help--- + depends on ARCH_MIGHT_HAVE_PC_SERIO + help i8042 is the chip over which the standard AT keyboard and PS/2 mouse are connected to the computer. If you use these devices, you'll need to say Y here. @@ -35,7 +42,8 @@ config SERIO_I8042 config SERIO_SERPORT tristate "Serial port line discipline" default y - ---help--- + depends on TTY + help Say Y here if you plan to use an input device (mouse, joystick, tablet, 6dof) that communicates over the RS232 serial (COM) port. @@ -49,7 +57,7 @@ config SERIO_SERPORT config SERIO_CT82C710 tristate "ct82c710 Aux port controller" depends on X86 - ---help--- + help Say Y here if you have a Texas Instruments TravelMate notebook equipped with the ct82c710 chip and want to use a mouse connected to the "QuickPort". @@ -66,7 +74,7 @@ config SERIO_Q40KBD config SERIO_PARKBD tristate "Parallel port keyboard adapter" depends on PARPORT - ---help--- + help Say Y here if you built a simple parallel port adapter to attach an additional AT keyboard, XT keyboard or PS/2 mouse. @@ -79,7 +87,7 @@ config SERIO_PARKBD config SERIO_RPCKBD tristate "Acorn RiscPC keyboard controller" - depends on ARCH_ACORN || ARCH_CLPS7500 + depends on ARCH_ACORN default y help Say Y here if you have the Acorn RiscPC and want to use an AT @@ -88,6 +96,16 @@ config SERIO_RPCKBD To compile this driver as a module, choose M here: the module will be called rpckbd. +config SERIO_AT32PSIF + tristate "AVR32 PSIF PS/2 keyboard and mouse controller" + depends on AVR32 + help + Say Y here if you want to use the PSIF peripheral on AVR32 devices + and connect a PS/2 keyboard and/or mouse to it. + + To compile this driver as a module, choose M here: the module will + be called at32psif. + config SERIO_AMBAKMI tristate "AMBA KMI keyboard controller" depends on ARM_AMBA @@ -114,7 +132,7 @@ config HP_SDC tristate "HP System Device Controller i8042 Support" depends on (GSC || HP300) && SERIO default y - ---help--- + help This option enables support for the "System Device Controller", an i8042 carrying microcode to manage a few miscellaneous devices on some Hewlett Packard systems. @@ -157,7 +175,8 @@ config SERIO_MACEPS2 module will be called maceps2. config SERIO_LIBPS2 - tristate "PS/2 driver library" if EMBEDDED + tristate "PS/2 driver library" + depends on SERIO_I8042 || SERIO_I8042=n help Say Y here if you are using a driver for device connected to a PS/2 port, such as PS/2 mouse or standard AT keyboard. @@ -180,4 +199,86 @@ config SERIO_RAW To compile this driver as a module, choose M here: the module will be called serio_raw. +config SERIO_XILINX_XPS_PS2 + tristate "Xilinx XPS PS/2 Controller Support" + depends on PPC || MICROBLAZE + help + This driver supports XPS PS/2 IP from the Xilinx EDK on + PowerPC platform. + + To compile this driver as a module, choose M here: the + module will be called xilinx_ps2. + +config SERIO_ALTERA_PS2 + tristate "Altera UP PS/2 controller" + depends on HAS_IOMEM + help + Say Y here if you have Altera University Program PS/2 ports. + + To compile this driver as a module, choose M here: the + module will be called altera_ps2. + +config SERIO_AMS_DELTA + tristate "Amstrad Delta (E3) mailboard support" + depends on MACH_AMS_DELTA + default y + ---help--- + Say Y here if you have an E3 and want to use its mailboard, + or any standard AT keyboard connected to the mailboard port. + + When used for the E3 mailboard, a non-standard key table + must be loaded from userspace, possibly using udev extras + provided keymap helper utility. + + To compile this driver as a module, choose M here; + the module will be called ams_delta_serio. + +config SERIO_PS2MULT + tristate "TQC PS/2 multiplexer" + help + Say Y here if you have the PS/2 line multiplexer like the one + present on TQC boards. + + To compile this driver as a module, choose M here: the + module will be called ps2mult. + +config SERIO_ARC_PS2 + tristate "ARC PS/2 support" + help + Say Y here if you have an ARC FPGA platform with a PS/2 + controller in it. + + To compile this driver as a module, choose M here; the module + will be called arc_ps2. + +config SERIO_APBPS2 + tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller" + depends on OF + help + Say Y here if you want support for GRLIB APBPS2 peripherals used + to connect to PS/2 keyboard and/or mouse. + + 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 OLPC || COMPILE_TEST + 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. + +config HYPERV_KEYBOARD + tristate "Microsoft Synthetic Keyboard driver" + depends on HYPERV + default HYPERV + help + Select this option to enable the Hyper-V Keyboard driver. + + To compile this driver as a module, choose M here: the module will + be called hyperv_keyboard. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 4155197867a..815d874fe72 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -12,11 +12,20 @@ obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o +obj-$(CONFIG_SERIO_AT32PSIF) += at32psif.o obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o obj-$(CONFIG_HP_SDC) += hp_sdc.o obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o +obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o obj-$(CONFIG_SERIO_LIBPS2) += libps2.o obj-$(CONFIG_SERIO_RAW) += serio_raw.o +obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o +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 +obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c new file mode 100644 index 00000000000..cce69d6b958 --- /dev/null +++ b/drivers/input/serio/altera_ps2.c @@ -0,0 +1,201 @@ +/* + * Altera University Program PS2 controller driver + * + * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw> + * + * Based on sa1111ps2.c, which is: + * Copyright (C) 2002 Russell King + * + * 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/module.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/of.h> + +#define DRV_NAME "altera_ps2" + +struct ps2if { + struct serio *io; + struct resource *iomem_res; + void __iomem *base; + unsigned irq; +}; + +/* + * Read all bytes waiting in the PS2 port. There should be + * at the most one, but we loop for safety. + */ +static irqreturn_t altera_ps2_rxint(int irq, void *dev_id) +{ + struct ps2if *ps2if = dev_id; + unsigned int status; + int handled = IRQ_NONE; + + while ((status = readl(ps2if->base)) & 0xffff0000) { + serio_interrupt(ps2if->io, status & 0xff, 0); + handled = IRQ_HANDLED; + } + + return handled; +} + +/* + * Write a byte to the PS2 port. + */ +static int altera_ps2_write(struct serio *io, unsigned char val) +{ + struct ps2if *ps2if = io->port_data; + + writel(val, ps2if->base); + return 0; +} + +static int altera_ps2_open(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + + /* clear fifo */ + while (readl(ps2if->base) & 0xffff0000) + /* empty */; + + writel(1, ps2if->base + 4); /* enable rx irq */ + return 0; +} + +static void altera_ps2_close(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + + writel(0, ps2if->base); /* disable rx irq */ +} + +/* + * Add one device to this driver. + */ +static int altera_ps2_probe(struct platform_device *pdev) +{ + struct ps2if *ps2if; + struct serio *serio; + int error, irq; + + ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { + error = -ENOMEM; + goto err_free_mem; + } + + serio->id.type = SERIO_8042; + serio->write = altera_ps2_write; + serio->open = altera_ps2_open; + serio->close = altera_ps2_close; + strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name)); + strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &pdev->dev; + ps2if->io = serio; + + ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (ps2if->iomem_res == NULL) { + error = -ENOENT; + goto err_free_mem; + } + + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + error = -ENXIO; + goto err_free_mem; + } + ps2if->irq = irq; + + if (!request_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res), pdev->name)) { + error = -EBUSY; + goto err_free_mem; + } + + ps2if->base = ioremap(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + if (!ps2if->base) { + error = -ENOMEM; + goto err_free_res; + } + + error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if); + if (error) { + dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n", + ps2if->irq, error); + goto err_unmap; + } + + dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq); + + serio_register_port(ps2if->io); + platform_set_drvdata(pdev, ps2if); + + return 0; + + err_unmap: + iounmap(ps2if->base); + err_free_res: + release_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + err_free_mem: + kfree(ps2if); + kfree(serio); + return error; +} + +/* + * Remove one device from this driver. + */ +static int altera_ps2_remove(struct platform_device *pdev) +{ + struct ps2if *ps2if = platform_get_drvdata(pdev); + + serio_unregister_port(ps2if->io); + free_irq(ps2if->irq, ps2if); + iounmap(ps2if->base); + release_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + kfree(ps2if); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id altera_ps2_match[] = { + { .compatible = "ALTR,ps2-1.0", }, + { .compatible = "altr,ps2-1.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, altera_ps2_match); +#endif /* CONFIG_OF */ + +/* + * Our device driver structure + */ +static struct platform_driver altera_ps2_driver = { + .probe = altera_ps2_probe, + .remove = altera_ps2_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(altera_ps2_match), + }, +}; +module_platform_driver(altera_ps2_driver); + +MODULE_DESCRIPTION("Altera University Program PS2 controller driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c index 5a7b49c3553..8b748d99b93 100644 --- a/drivers/input/serio/ambakmi.c +++ b/drivers/input/serio/ambakmi.c @@ -10,7 +10,6 @@ * (at your option) any later version. */ #include <linux/module.h> -#include <linux/init.h> #include <linux/serio.h> #include <linux/errno.h> #include <linux/interrupt.h> @@ -57,7 +56,7 @@ static int amba_kmi_write(struct serio *io, unsigned char val) struct amba_kmi_port *kmi = io->port_data; unsigned int timeleft = 10000; /* timeout in 100ms */ - while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && timeleft--) + while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && --timeleft) udelay(10); if (timeleft) @@ -72,7 +71,7 @@ static int amba_kmi_open(struct serio *io) unsigned int divisor; int ret; - ret = clk_enable(kmi->clk); + ret = clk_prepare_enable(kmi->clk); if (ret) goto out; @@ -80,7 +79,8 @@ static int amba_kmi_open(struct serio *io) writeb(divisor, KMICLKDIV); writeb(KMICR_EN, KMICR); - ret = request_irq(kmi->irq, amba_kmi_int, 0, "kmi-pl050", kmi); + ret = request_irq(kmi->irq, amba_kmi_int, IRQF_SHARED, "kmi-pl050", + kmi); if (ret) { printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq); writeb(0, KMICR); @@ -92,7 +92,7 @@ static int amba_kmi_open(struct serio *io) return 0; clk_disable: - clk_disable(kmi->clk); + clk_disable_unprepare(kmi->clk); out: return ret; } @@ -104,10 +104,11 @@ static void amba_kmi_close(struct serio *io) writeb(0, KMICR); free_irq(kmi->irq, kmi); - clk_disable(kmi->clk); + clk_disable_unprepare(kmi->clk); } -static int amba_kmi_probe(struct amba_device *dev, void *id) +static int amba_kmi_probe(struct amba_device *dev, + const struct amba_id *id) { struct amba_kmi_port *kmi; struct serio *io; @@ -117,27 +118,25 @@ static int amba_kmi_probe(struct amba_device *dev, void *id) if (ret) return ret; - kmi = kmalloc(sizeof(struct amba_kmi_port), GFP_KERNEL); - io = kmalloc(sizeof(struct serio), GFP_KERNEL); + kmi = kzalloc(sizeof(struct amba_kmi_port), GFP_KERNEL); + io = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!kmi || !io) { ret = -ENOMEM; goto out; } - memset(kmi, 0, sizeof(struct amba_kmi_port)); - memset(io, 0, sizeof(struct serio)); io->id.type = SERIO_8042; io->write = amba_kmi_write; io->open = amba_kmi_open; io->close = amba_kmi_close; - strlcpy(io->name, dev->dev.bus_id, sizeof(io->name)); - strlcpy(io->phys, dev->dev.bus_id, sizeof(io->phys)); + strlcpy(io->name, dev_name(&dev->dev), sizeof(io->name)); + strlcpy(io->phys, dev_name(&dev->dev), sizeof(io->phys)); io->port_data = kmi; io->dev.parent = &dev->dev; - kmi->io = io; - kmi->base = ioremap(dev->res.start, KMI_SIZE); + kmi->io = io; + kmi->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!kmi->base) { ret = -ENOMEM; goto out; @@ -168,8 +167,6 @@ static int amba_kmi_remove(struct amba_device *dev) { struct amba_kmi_port *kmi = amba_get_drvdata(dev); - amba_set_drvdata(dev, NULL); - serio_unregister_port(kmi->io); clk_put(kmi->clk); iounmap(kmi->base); @@ -196,9 +193,12 @@ static struct amba_id amba_kmi_idtable[] = { { 0, 0 } }; +MODULE_DEVICE_TABLE(amba, amba_kmi_idtable); + static struct amba_driver ambakmi_driver = { .drv = { .name = "kmi-pl050", + .owner = THIS_MODULE, }, .id_table = amba_kmi_idtable, .probe = amba_kmi_probe, @@ -206,18 +206,7 @@ static struct amba_driver ambakmi_driver = { .resume = amba_kmi_resume, }; -static int __init amba_kmi_init(void) -{ - return amba_driver_register(&ambakmi_driver); -} - -static void __exit amba_kmi_exit(void) -{ - amba_driver_unregister(&ambakmi_driver); -} - -module_init(amba_kmi_init); -module_exit(amba_kmi_exit); +module_amba_driver(ambakmi_driver); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("AMBA KMI controller driver"); diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c new file mode 100644 index 00000000000..45887e31242 --- /dev/null +++ b/drivers/input/serio/ams_delta_serio.c @@ -0,0 +1,191 @@ +/* + * Amstrad E3 (Delta) keyboard port driver + * + * Copyright (c) 2006 Matt Callow + * Copyright (c) 2010 Janusz Krzysztofik + * + * 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. + * + * Thanks to Cliff Lawson for his help + * + * The Amstrad Delta keyboard (aka mailboard) uses normal PC-AT style serial + * transmission. The keyboard port is formed of two GPIO lines, for clock + * and data. Due to strict timing requirements of the interface, + * the serial data stream is read and processed by a FIQ handler. + * The resulting words are fetched by this driver from a circular buffer. + * + * Standard AT keyboard driver (atkbd) is used for handling the keyboard data. + * However, when used with the E3 mailboard that producecs non-standard + * scancodes, a custom key table must be prepared and loaded from userspace. + */ +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/serio.h> +#include <linux/slab.h> +#include <linux/module.h> + +#include <asm/mach-types.h> +#include <mach/board-ams-delta.h> + +#include <mach/ams-delta-fiq.h> + +MODULE_AUTHOR("Matt Callow"); +MODULE_DESCRIPTION("AMS Delta (E3) keyboard port driver"); +MODULE_LICENSE("GPL"); + +static struct serio *ams_delta_serio; + +static int check_data(int data) +{ + int i, parity = 0; + + /* check valid stop bit */ + if (!(data & 0x400)) { + dev_warn(&ams_delta_serio->dev, + "invalid stop bit, data=0x%X\n", + data); + return SERIO_FRAME; + } + /* calculate the parity */ + for (i = 1; i < 10; i++) { + if (data & (1 << i)) + parity++; + } + /* it should be odd */ + if (!(parity & 0x01)) { + dev_warn(&ams_delta_serio->dev, + "paritiy check failed, data=0x%X parity=0x%X\n", + data, parity); + return SERIO_PARITY; + } + return 0; +} + +static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id) +{ + int *circ_buff = &fiq_buffer[FIQ_CIRC_BUFF]; + int data, dfl; + u8 scancode; + + fiq_buffer[FIQ_IRQ_PEND] = 0; + + /* + * Read data from the circular buffer, check it + * and then pass it on the serio + */ + while (fiq_buffer[FIQ_KEYS_CNT] > 0) { + + data = circ_buff[fiq_buffer[FIQ_HEAD_OFFSET]++]; + fiq_buffer[FIQ_KEYS_CNT]--; + if (fiq_buffer[FIQ_HEAD_OFFSET] == fiq_buffer[FIQ_BUF_LEN]) + fiq_buffer[FIQ_HEAD_OFFSET] = 0; + + dfl = check_data(data); + scancode = (u8) (data >> 1) & 0xFF; + serio_interrupt(ams_delta_serio, scancode, dfl); + } + return IRQ_HANDLED; +} + +static int ams_delta_serio_open(struct serio *serio) +{ + /* enable keyboard */ + gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 1); + + return 0; +} + +static void ams_delta_serio_close(struct serio *serio) +{ + /* disable keyboard */ + gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 0); +} + +static const struct gpio ams_delta_gpios[] __initconst_or_module = { + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATA, + .flags = GPIOF_DIR_IN, + .label = "serio-data", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK, + .flags = GPIOF_DIR_IN, + .label = "serio-clock", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_PWR, + .flags = GPIOF_OUT_INIT_LOW, + .label = "serio-power", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATAOUT, + .flags = GPIOF_OUT_INIT_LOW, + .label = "serio-dataout", + }, +}; + +static int __init ams_delta_serio_init(void) +{ + int err; + + if (!machine_is_ams_delta()) + return -ENODEV; + + ams_delta_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ams_delta_serio) + return -ENOMEM; + + ams_delta_serio->id.type = SERIO_8042; + ams_delta_serio->open = ams_delta_serio_open; + ams_delta_serio->close = ams_delta_serio_close; + strlcpy(ams_delta_serio->name, "AMS DELTA keyboard adapter", + sizeof(ams_delta_serio->name)); + strlcpy(ams_delta_serio->phys, "GPIO/serio0", + sizeof(ams_delta_serio->phys)); + + err = gpio_request_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); + if (err) { + pr_err("ams_delta_serio: Couldn't request gpio pins\n"); + goto serio; + } + + err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), + ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING, + "ams-delta-serio", 0); + if (err < 0) { + pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n", + gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK)); + goto gpio; + } + /* + * Since GPIO register handling for keyboard clock pin is performed + * at FIQ level, switch back from edge to simple interrupt handler + * to avoid bad interaction. + */ + irq_set_handler(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), + handle_simple_irq); + + serio_register_port(ams_delta_serio); + dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name); + + return 0; +gpio: + gpio_free_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); +serio: + kfree(ams_delta_serio); + return err; +} +module_init(ams_delta_serio_init); + +static void __exit ams_delta_serio_exit(void) +{ + serio_unregister_port(ams_delta_serio); + free_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0); + gpio_free_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); +} +module_exit(ams_delta_serio_exit); diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c new file mode 100644 index 00000000000..98be824544a --- /dev/null +++ b/drivers/input/serio/apbps2.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2013 Aeroflex Gaisler + * + * This driver supports the APBPS2 PS/2 core available in the GRLIB + * VHDL IP core library. + * + * Full documentation of the APBPS2 core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * See "Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt" for + * information on open firmware properties. + * + * 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. + * + * Contributors: Daniel Hellstrom <daniel@gaisler.com> + */ +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <linux/module.h> +#include <linux/serio.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/of_irq.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/io.h> + +struct apbps2_regs { + u32 __iomem data; /* 0x00 */ + u32 __iomem status; /* 0x04 */ + u32 __iomem ctrl; /* 0x08 */ + u32 __iomem reload; /* 0x0c */ +}; + +#define APBPS2_STATUS_DR (1<<0) +#define APBPS2_STATUS_PE (1<<1) +#define APBPS2_STATUS_FE (1<<2) +#define APBPS2_STATUS_KI (1<<3) +#define APBPS2_STATUS_RF (1<<4) +#define APBPS2_STATUS_TF (1<<5) +#define APBPS2_STATUS_TCNT (0x1f<<22) +#define APBPS2_STATUS_RCNT (0x1f<<27) + +#define APBPS2_CTRL_RE (1<<0) +#define APBPS2_CTRL_TE (1<<1) +#define APBPS2_CTRL_RI (1<<2) +#define APBPS2_CTRL_TI (1<<3) + +struct apbps2_priv { + struct serio *io; + struct apbps2_regs *regs; +}; + +static int apbps2_idx; + +static irqreturn_t apbps2_isr(int irq, void *dev_id) +{ + struct apbps2_priv *priv = dev_id; + unsigned long status, data, rxflags; + irqreturn_t ret = IRQ_NONE; + + while ((status = ioread32be(&priv->regs->status)) & APBPS2_STATUS_DR) { + data = ioread32be(&priv->regs->data); + rxflags = (status & APBPS2_STATUS_PE) ? SERIO_PARITY : 0; + rxflags |= (status & APBPS2_STATUS_FE) ? SERIO_FRAME : 0; + + /* clear error bits? */ + if (rxflags) + iowrite32be(0, &priv->regs->status); + + serio_interrupt(priv->io, data, rxflags); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static int apbps2_write(struct serio *io, unsigned char val) +{ + struct apbps2_priv *priv = io->port_data; + unsigned int tleft = 10000; /* timeout in 100ms */ + + /* delay until PS/2 controller has room for more chars */ + while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) && tleft--) + udelay(10); + + if ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) == 0) { + iowrite32be(val, &priv->regs->data); + + iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI | APBPS2_CTRL_TE, + &priv->regs->ctrl); + return 0; + } + + return -ETIMEDOUT; +} + +static int apbps2_open(struct serio *io) +{ + struct apbps2_priv *priv = io->port_data; + int limit; + unsigned long tmp; + + /* clear error flags */ + iowrite32be(0, &priv->regs->status); + + /* Clear old data if available (unlikely) */ + limit = 1024; + while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_DR) && --limit) + tmp = ioread32be(&priv->regs->data); + + /* Enable reciever and it's interrupt */ + iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI, &priv->regs->ctrl); + + return 0; +} + +static void apbps2_close(struct serio *io) +{ + struct apbps2_priv *priv = io->port_data; + + /* stop interrupts at PS/2 HW level */ + iowrite32be(0, &priv->regs->ctrl); +} + +/* Initialize one APBPS2 PS/2 core */ +static int apbps2_of_probe(struct platform_device *ofdev) +{ + struct apbps2_priv *priv; + int irq, err; + u32 freq_hz; + struct resource *res; + + priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&ofdev->dev, "memory allocation failed\n"); + return -ENOMEM; + } + + /* Find Device Address */ + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + /* Reset hardware, disable interrupt */ + iowrite32be(0, &priv->regs->ctrl); + + /* IRQ */ + irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + err = devm_request_irq(&ofdev->dev, irq, apbps2_isr, + IRQF_SHARED, "apbps2", priv); + if (err) { + dev_err(&ofdev->dev, "request IRQ%d failed\n", irq); + return err; + } + + /* Get core frequency */ + if (of_property_read_u32(ofdev->dev.of_node, "freq", &freq_hz)) { + dev_err(&ofdev->dev, "unable to get core frequency\n"); + return -EINVAL; + } + + /* Set reload register to core freq in kHz/10 */ + iowrite32be(freq_hz / 10000, &priv->regs->reload); + + priv->io = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!priv->io) + return -ENOMEM; + + priv->io->id.type = SERIO_8042; + priv->io->open = apbps2_open; + priv->io->close = apbps2_close; + priv->io->write = apbps2_write; + priv->io->port_data = priv; + strlcpy(priv->io->name, "APBPS2 PS/2", sizeof(priv->io->name)); + snprintf(priv->io->phys, sizeof(priv->io->phys), + "apbps2_%d", apbps2_idx++); + + dev_info(&ofdev->dev, "irq = %d, base = 0x%p\n", irq, priv->regs); + + serio_register_port(priv->io); + + platform_set_drvdata(ofdev, priv); + + return 0; +} + +static int apbps2_of_remove(struct platform_device *of_dev) +{ + struct apbps2_priv *priv = platform_get_drvdata(of_dev); + + serio_unregister_port(priv->io); + + return 0; +} + +static const struct of_device_id apbps2_of_match[] = { + { .name = "GAISLER_APBPS2", }, + { .name = "01_060", }, + {} +}; + +MODULE_DEVICE_TABLE(of, apbps2_of_match); + +static struct platform_driver apbps2_of_driver = { + .driver = { + .name = "grlib-apbps2", + .owner = THIS_MODULE, + .of_match_table = apbps2_of_match, + }, + .probe = apbps2_of_probe, + .remove = apbps2_of_remove, +}; + +module_platform_driver(apbps2_of_driver); + +MODULE_AUTHOR("Aeroflex Gaisler AB."); +MODULE_DESCRIPTION("GRLIB APBPS2 PS/2 serial I/O"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/serio/arc_ps2.c b/drivers/input/serio/arc_ps2.c new file mode 100644 index 00000000000..8024a6d7fcc --- /dev/null +++ b/drivers/input/serio/arc_ps2.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Driver is originally developed by Pavel Sokolov <psokolov@synopsys.com> + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#define ARC_PS2_PORTS 2 + +#define ARC_ARC_PS2_ID 0x0001f609 + +#define STAT_TIMEOUT 128 + +#define PS2_STAT_RX_FRM_ERR (1) +#define PS2_STAT_RX_BUF_OVER (1 << 1) +#define PS2_STAT_RX_INT_EN (1 << 2) +#define PS2_STAT_RX_VAL (1 << 3) +#define PS2_STAT_TX_ISNOT_FUL (1 << 4) +#define PS2_STAT_TX_INT_EN (1 << 5) + +struct arc_ps2_port { + void __iomem *data_addr; + void __iomem *status_addr; + struct serio *io; +}; + +struct arc_ps2_data { + struct arc_ps2_port port[ARC_PS2_PORTS]; + void __iomem *addr; + unsigned int frame_error; + unsigned int buf_overflow; + unsigned int total_int; +}; + +static void arc_ps2_check_rx(struct arc_ps2_data *arc_ps2, + struct arc_ps2_port *port) +{ + unsigned int timeout = 1000; + unsigned int flag, status; + unsigned char data; + + do { + status = ioread32(port->status_addr); + if (!(status & PS2_STAT_RX_VAL)) + return; + + data = ioread32(port->data_addr) & 0xff; + + flag = 0; + arc_ps2->total_int++; + if (status & PS2_STAT_RX_FRM_ERR) { + arc_ps2->frame_error++; + flag |= SERIO_PARITY; + } else if (status & PS2_STAT_RX_BUF_OVER) { + arc_ps2->buf_overflow++; + flag |= SERIO_FRAME; + } + + serio_interrupt(port->io, data, flag); + } while (--timeout); + + dev_err(&port->io->dev, "PS/2 hardware stuck\n"); +} + +static irqreturn_t arc_ps2_interrupt(int irq, void *dev) +{ + struct arc_ps2_data *arc_ps2 = dev; + int i; + + for (i = 0; i < ARC_PS2_PORTS; i++) + arc_ps2_check_rx(arc_ps2, &arc_ps2->port[i]); + + return IRQ_HANDLED; +} + +static int arc_ps2_write(struct serio *io, unsigned char val) +{ + unsigned status; + struct arc_ps2_port *port = io->port_data; + int timeout = STAT_TIMEOUT; + + do { + status = ioread32(port->status_addr); + cpu_relax(); + + if (status & PS2_STAT_TX_ISNOT_FUL) { + iowrite32(val & 0xff, port->data_addr); + return 0; + } + + } while (--timeout); + + dev_err(&io->dev, "write timeout\n"); + return -ETIMEDOUT; +} + +static int arc_ps2_open(struct serio *io) +{ + struct arc_ps2_port *port = io->port_data; + + iowrite32(PS2_STAT_RX_INT_EN, port->status_addr); + + return 0; +} + +static void arc_ps2_close(struct serio *io) +{ + struct arc_ps2_port *port = io->port_data; + + iowrite32(ioread32(port->status_addr) & ~PS2_STAT_RX_INT_EN, + port->status_addr); +} + +static void __iomem *arc_ps2_calc_addr(struct arc_ps2_data *arc_ps2, + int index, bool status) +{ + void __iomem *addr; + + addr = arc_ps2->addr + 4 + 4 * index; + if (status) + addr += ARC_PS2_PORTS * 4; + + return addr; +} + +static void arc_ps2_inhibit_ports(struct arc_ps2_data *arc_ps2) +{ + void __iomem *addr; + u32 val; + int i; + + for (i = 0; i < ARC_PS2_PORTS; i++) { + addr = arc_ps2_calc_addr(arc_ps2, i, true); + val = ioread32(addr); + val &= ~(PS2_STAT_RX_INT_EN | PS2_STAT_TX_INT_EN); + iowrite32(val, addr); + } +} + +static int arc_ps2_create_port(struct platform_device *pdev, + struct arc_ps2_data *arc_ps2, + int index) +{ + struct arc_ps2_port *port = &arc_ps2->port[index]; + struct serio *io; + + io = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!io) + return -ENOMEM; + + io->id.type = SERIO_8042; + io->write = arc_ps2_write; + io->open = arc_ps2_open; + io->close = arc_ps2_close; + snprintf(io->name, sizeof(io->name), "ARC PS/2 port%d", index); + snprintf(io->phys, sizeof(io->phys), "arc/serio%d", index); + io->port_data = port; + + port->io = io; + + port->data_addr = arc_ps2_calc_addr(arc_ps2, index, false); + port->status_addr = arc_ps2_calc_addr(arc_ps2, index, true); + + dev_dbg(&pdev->dev, "port%d is allocated (data = 0x%p, status = 0x%p)\n", + index, port->data_addr, port->status_addr); + + serio_register_port(port->io); + return 0; +} + +static int arc_ps2_probe(struct platform_device *pdev) +{ + struct arc_ps2_data *arc_ps2; + struct resource *res; + int irq; + int error, id, i; + + irq = platform_get_irq_byname(pdev, "arc_ps2_irq"); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ defined\n"); + return -EINVAL; + } + + arc_ps2 = devm_kzalloc(&pdev->dev, sizeof(struct arc_ps2_data), + GFP_KERNEL); + if (!arc_ps2) { + dev_err(&pdev->dev, "out of memory\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + arc_ps2->addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(arc_ps2->addr)) + return PTR_ERR(arc_ps2->addr); + + dev_info(&pdev->dev, "irq = %d, address = 0x%p, ports = %i\n", + irq, arc_ps2->addr, ARC_PS2_PORTS); + + id = ioread32(arc_ps2->addr); + if (id != ARC_ARC_PS2_ID) { + dev_err(&pdev->dev, "device id does not match\n"); + return -ENXIO; + } + + arc_ps2_inhibit_ports(arc_ps2); + + error = devm_request_irq(&pdev->dev, irq, arc_ps2_interrupt, + 0, "arc_ps2", arc_ps2); + if (error) { + dev_err(&pdev->dev, "Could not allocate IRQ\n"); + return error; + } + + for (i = 0; i < ARC_PS2_PORTS; i++) { + error = arc_ps2_create_port(pdev, arc_ps2, i); + if (error) { + while (--i >= 0) + serio_unregister_port(arc_ps2->port[i].io); + return error; + } + } + + platform_set_drvdata(pdev, arc_ps2); + + return 0; +} + +static int arc_ps2_remove(struct platform_device *pdev) +{ + struct arc_ps2_data *arc_ps2 = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < ARC_PS2_PORTS; i++) + serio_unregister_port(arc_ps2->port[i].io); + + dev_dbg(&pdev->dev, "interrupt count = %i\n", arc_ps2->total_int); + dev_dbg(&pdev->dev, "frame error count = %i\n", arc_ps2->frame_error); + dev_dbg(&pdev->dev, "buffer overflow count = %i\n", + arc_ps2->buf_overflow); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id arc_ps2_match[] = { + { .compatible = "snps,arc_ps2" }, + { }, +}; +MODULE_DEVICE_TABLE(of, arc_ps2_match); +#endif + +static struct platform_driver arc_ps2_driver = { + .driver = { + .name = "arc_ps2", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(arc_ps2_match), + }, + .probe = arc_ps2_probe, + .remove = arc_ps2_remove, +}; + +module_platform_driver(arc_ps2_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pavel Sokolov <psokolov@synopsys.com>"); +MODULE_DESCRIPTION("ARC PS/2 Driver"); diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c new file mode 100644 index 00000000000..3290b287ac4 --- /dev/null +++ b/drivers/input/serio/at32psif.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * Driver for the AT32AP700X PS/2 controller (PSIF). + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/serio.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* PSIF register offsets */ +#define PSIF_CR 0x00 +#define PSIF_RHR 0x04 +#define PSIF_THR 0x08 +#define PSIF_SR 0x10 +#define PSIF_IER 0x14 +#define PSIF_IDR 0x18 +#define PSIF_IMR 0x1c +#define PSIF_PSR 0x24 + +/* Bitfields in control register. */ +#define PSIF_CR_RXDIS_OFFSET 1 +#define PSIF_CR_RXDIS_SIZE 1 +#define PSIF_CR_RXEN_OFFSET 0 +#define PSIF_CR_RXEN_SIZE 1 +#define PSIF_CR_SWRST_OFFSET 15 +#define PSIF_CR_SWRST_SIZE 1 +#define PSIF_CR_TXDIS_OFFSET 9 +#define PSIF_CR_TXDIS_SIZE 1 +#define PSIF_CR_TXEN_OFFSET 8 +#define PSIF_CR_TXEN_SIZE 1 + +/* Bitfields in interrupt disable, enable, mask and status register. */ +#define PSIF_NACK_OFFSET 8 +#define PSIF_NACK_SIZE 1 +#define PSIF_OVRUN_OFFSET 5 +#define PSIF_OVRUN_SIZE 1 +#define PSIF_PARITY_OFFSET 9 +#define PSIF_PARITY_SIZE 1 +#define PSIF_RXRDY_OFFSET 4 +#define PSIF_RXRDY_SIZE 1 +#define PSIF_TXEMPTY_OFFSET 1 +#define PSIF_TXEMPTY_SIZE 1 +#define PSIF_TXRDY_OFFSET 0 +#define PSIF_TXRDY_SIZE 1 + +/* Bitfields in prescale register. */ +#define PSIF_PSR_PRSCV_OFFSET 0 +#define PSIF_PSR_PRSCV_SIZE 12 + +/* Bitfields in receive hold register. */ +#define PSIF_RHR_RXDATA_OFFSET 0 +#define PSIF_RHR_RXDATA_SIZE 8 + +/* Bitfields in transmit hold register. */ +#define PSIF_THR_TXDATA_OFFSET 0 +#define PSIF_THR_TXDATA_SIZE 8 + +/* Bit manipulation macros */ +#define PSIF_BIT(name) \ + (1 << PSIF_##name##_OFFSET) + +#define PSIF_BF(name, value) \ + (((value) & ((1 << PSIF_##name##_SIZE) - 1)) \ + << PSIF_##name##_OFFSET) + +#define PSIF_BFEXT(name, value) \ + (((value) >> PSIF_##name##_OFFSET) \ + & ((1 << PSIF_##name##_SIZE) - 1)) + +#define PSIF_BFINS(name, value, old) \ + (((old) & ~(((1 << PSIF_##name##_SIZE) - 1) \ + << PSIF_##name##_OFFSET)) \ + | PSIF_BF(name, value)) + +/* Register access macros */ +#define psif_readl(port, reg) \ + __raw_readl((port)->regs + PSIF_##reg) + +#define psif_writel(port, reg, value) \ + __raw_writel((value), (port)->regs + PSIF_##reg) + +struct psif { + struct platform_device *pdev; + struct clk *pclk; + struct serio *io; + void __iomem *regs; + unsigned int irq; + /* Prevent concurrent writes to PSIF THR. */ + spinlock_t lock; + bool open; +}; + +static irqreturn_t psif_interrupt(int irq, void *_ptr) +{ + struct psif *psif = _ptr; + int retval = IRQ_NONE; + unsigned int io_flags = 0; + unsigned long status; + + status = psif_readl(psif, SR); + + if (status & PSIF_BIT(RXRDY)) { + unsigned char val = (unsigned char) psif_readl(psif, RHR); + + if (status & PSIF_BIT(PARITY)) + io_flags |= SERIO_PARITY; + if (status & PSIF_BIT(OVRUN)) + dev_err(&psif->pdev->dev, "overrun read error\n"); + + serio_interrupt(psif->io, val, io_flags); + + retval = IRQ_HANDLED; + } + + return retval; +} + +static int psif_write(struct serio *io, unsigned char val) +{ + struct psif *psif = io->port_data; + unsigned long flags; + int timeout = 10; + int retval = 0; + + spin_lock_irqsave(&psif->lock, flags); + + while (!(psif_readl(psif, SR) & PSIF_BIT(TXEMPTY)) && timeout--) + udelay(50); + + if (timeout >= 0) { + psif_writel(psif, THR, val); + } else { + dev_dbg(&psif->pdev->dev, "timeout writing to THR\n"); + retval = -EBUSY; + } + + spin_unlock_irqrestore(&psif->lock, flags); + + return retval; +} + +static int psif_open(struct serio *io) +{ + struct psif *psif = io->port_data; + int retval; + + retval = clk_enable(psif->pclk); + if (retval) + goto out; + + psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN)); + psif_writel(psif, IER, PSIF_BIT(RXRDY)); + + psif->open = true; +out: + return retval; +} + +static void psif_close(struct serio *io) +{ + struct psif *psif = io->port_data; + + psif->open = false; + + psif_writel(psif, IDR, ~0UL); + psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); + + clk_disable(psif->pclk); +} + +static void psif_set_prescaler(struct psif *psif) +{ + unsigned long prscv; + unsigned long rate = clk_get_rate(psif->pclk); + + /* PRSCV = Pulse length (100 us) * PSIF module frequency. */ + prscv = 100 * (rate / 1000000UL); + + if (prscv > ((1<<PSIF_PSR_PRSCV_SIZE) - 1)) { + prscv = (1<<PSIF_PSR_PRSCV_SIZE) - 1; + dev_dbg(&psif->pdev->dev, "pclk too fast, " + "prescaler set to max\n"); + } + + clk_enable(psif->pclk); + psif_writel(psif, PSR, prscv); + clk_disable(psif->pclk); +} + +static int __init psif_probe(struct platform_device *pdev) +{ + struct resource *regs; + struct psif *psif; + struct serio *io; + struct clk *pclk; + int irq; + int ret; + + psif = kzalloc(sizeof(struct psif), GFP_KERNEL); + if (!psif) { + dev_dbg(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto out; + } + psif->pdev = pdev; + + io = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!io) { + dev_dbg(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto out_free_psif; + } + psif->io = io; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_dbg(&pdev->dev, "no mmio resources defined\n"); + ret = -ENOMEM; + goto out_free_io; + } + + psif->regs = ioremap(regs->start, resource_size(regs)); + if (!psif->regs) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "could not map I/O memory\n"); + goto out_free_io; + } + + pclk = clk_get(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) { + dev_dbg(&pdev->dev, "could not get peripheral clock\n"); + ret = PTR_ERR(pclk); + goto out_iounmap; + } + psif->pclk = pclk; + + /* Reset the PSIF to enter at a known state. */ + ret = clk_enable(pclk); + if (ret) { + dev_dbg(&pdev->dev, "could not enable pclk\n"); + goto out_put_clk; + } + psif_writel(psif, CR, PSIF_BIT(CR_SWRST)); + clk_disable(pclk); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_dbg(&pdev->dev, "could not get irq\n"); + ret = -ENXIO; + goto out_put_clk; + } + ret = request_irq(irq, psif_interrupt, IRQF_SHARED, "at32psif", psif); + if (ret) { + dev_dbg(&pdev->dev, "could not request irq %d\n", irq); + goto out_put_clk; + } + psif->irq = irq; + + io->id.type = SERIO_8042; + io->write = psif_write; + io->open = psif_open; + io->close = psif_close; + snprintf(io->name, sizeof(io->name), "AVR32 PS/2 port%d", pdev->id); + snprintf(io->phys, sizeof(io->phys), "at32psif/serio%d", pdev->id); + io->port_data = psif; + io->dev.parent = &pdev->dev; + + psif_set_prescaler(psif); + + spin_lock_init(&psif->lock); + serio_register_port(psif->io); + platform_set_drvdata(pdev, psif); + + dev_info(&pdev->dev, "Atmel AVR32 PSIF PS/2 driver on 0x%08x irq %d\n", + (int)psif->regs, psif->irq); + + return 0; + +out_put_clk: + clk_put(psif->pclk); +out_iounmap: + iounmap(psif->regs); +out_free_io: + kfree(io); +out_free_psif: + kfree(psif); +out: + return ret; +} + +static int __exit psif_remove(struct platform_device *pdev) +{ + struct psif *psif = platform_get_drvdata(pdev); + + psif_writel(psif, IDR, ~0UL); + psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); + + serio_unregister_port(psif->io); + iounmap(psif->regs); + free_irq(psif->irq, psif); + clk_put(psif->pclk); + kfree(psif); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int psif_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct psif *psif = platform_get_drvdata(pdev); + + if (psif->open) { + psif_writel(psif, CR, PSIF_BIT(CR_RXDIS) | PSIF_BIT(CR_TXDIS)); + clk_disable(psif->pclk); + } + + return 0; +} + +static int psif_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct psif *psif = platform_get_drvdata(pdev); + + if (psif->open) { + clk_enable(psif->pclk); + psif_set_prescaler(psif); + psif_writel(psif, CR, PSIF_BIT(CR_RXEN) | PSIF_BIT(CR_TXEN)); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(psif_pm_ops, psif_suspend, psif_resume); + +static struct platform_driver psif_driver = { + .remove = __exit_p(psif_remove), + .driver = { + .name = "atmel_psif", + .owner = THIS_MODULE, + .pm = &psif_pm_ops, + }, +}; + +module_platform_driver_probe(psif_driver, psif_probe); + +MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); +MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c index 0d35018c23a..cfe549d4eaa 100644 --- a/drivers/input/serio/ct82c710.c +++ b/drivers/input/serio/ct82c710.c @@ -1,6 +1,4 @@ /* - * $Id: ct82c710.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ @@ -37,6 +35,7 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <asm/io.h> @@ -112,9 +111,11 @@ static void ct82c710_close(struct serio *serio) static int ct82c710_open(struct serio *serio) { unsigned char status; + int err; - if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL)) - return -1; + err = request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL); + if (err) + return err; status = inb_p(CT82C710_STATUS); @@ -132,7 +133,7 @@ static int ct82c710_open(struct serio *serio) status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON); outb_p(status, CT82C710_STATUS); free_irq(CT82C710_IRQ, NULL); - return -1; + return -EBUSY; } return 0; @@ -174,7 +175,7 @@ static int __init ct82c710_detect(void) return 0; } -static int __devinit ct82c710_probe(struct platform_device *dev) +static int ct82c710_probe(struct platform_device *dev) { ct82c710_port = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!ct82c710_port) @@ -192,10 +193,13 @@ static int __devinit ct82c710_probe(struct platform_device *dev) serio_register_port(ct82c710_port); + printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n", + (unsigned long long)CT82C710_DATA, CT82C710_IRQ); + return 0; } -static int __devexit ct82c710_remove(struct platform_device *dev) +static int ct82c710_remove(struct platform_device *dev) { serio_unregister_port(ct82c710_port); @@ -208,7 +212,7 @@ static struct platform_driver ct82c710_driver = { .owner = THIS_MODULE, }, .probe = ct82c710_probe, - .remove = __devexit_p(ct82c710_remove), + .remove = ct82c710_remove, }; @@ -238,11 +242,6 @@ static int __init ct82c710_init(void) if (error) goto err_free_device; - serio_register_port(ct82c710_port); - - printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n", - (unsigned long long)CT82C710_DATA, CT82C710_IRQ); - return 0; err_free_device: diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 74f14e09778..8d9ba0c3827 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -6,7 +6,7 @@ * Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org> * * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c - * Copyright (c) 1999 Alex deVries <alex@onefishtwo.ca> + * Copyright (c) 1999 Alex deVries <alex@onefishtwo.ca> * Copyright (c) 1999-2000 Philipp Rumpf <prumpf@tux.org> * Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr> * Copyright (c) 2000-2001 Thomas Marteau <marteaut@esiee.fr> @@ -24,6 +24,7 @@ #include <linux/init.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/serio.h> #include <linux/input.h> #include <linux/interrupt.h> @@ -141,7 +142,7 @@ static void gscps2_flush(struct gscps2port *ps2port) /* * gscps2_writeb_output() - write a byte to the port * - * returns 1 on sucess, 0 on error + * returns 1 on success, 0 on error */ static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data) @@ -326,7 +327,7 @@ static void gscps2_close(struct serio *port) * @return: success/error report */ -static int __init gscps2_probe(struct parisc_device *dev) +static int gscps2_probe(struct parisc_device *dev) { struct gscps2port *ps2port; struct serio *serio; @@ -340,8 +341,8 @@ static int __init gscps2_probe(struct parisc_device *dev) if (dev->id.sversion == 0x96) hpa += GSC_DINO_OFFSET; - ps2port = kmalloc(sizeof(struct gscps2port), GFP_KERNEL); - serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + ps2port = kzalloc(sizeof(struct gscps2port), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!ps2port || !serio) { ret = -ENOMEM; goto fail_nomem; @@ -349,8 +350,6 @@ static int __init gscps2_probe(struct parisc_device *dev) dev_set_drvdata(&dev->dev, ps2port); - memset(ps2port, 0, sizeof(struct gscps2port)); - memset(serio, 0, sizeof(struct serio)); ps2port->port = serio; ps2port->padev = dev; ps2port->addr = ioremap_nocache(hpa, GSC_STATUS + 4); @@ -359,9 +358,9 @@ static int __init gscps2_probe(struct parisc_device *dev) gscps2_reset(ps2port); ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f; - snprintf(serio->name, sizeof(serio->name), "GSC PS/2 %s", + snprintf(serio->name, sizeof(serio->name), "gsc-ps2-%s", (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse"); - strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys)); + strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys)); serio->id.type = SERIO_8042; serio->write = gscps2_write; serio->open = gscps2_open; @@ -415,7 +414,7 @@ fail_nomem: * @return: success/error report */ -static int __devexit gscps2_remove(struct parisc_device *dev) +static int gscps2_remove(struct parisc_device *dev) { struct gscps2port *ps2port = dev_get_drvdata(&dev->dev); diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c index 93a1a6ba216..65605e4ef3c 100644 --- a/drivers/input/serio/hil_mlc.c +++ b/drivers/input/serio/hil_mlc.c @@ -58,6 +58,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/slab.h> #include <linux/timer.h> #include <linux/list.h> @@ -76,7 +77,7 @@ static struct timer_list hil_mlcs_kicker; static int hil_mlcs_probe; static void hil_mlcs_process(unsigned long unused); -DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0); +static DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0); /* #define HIL_MLC_DEBUG */ @@ -459,7 +460,7 @@ static int hilse_operate(hil_mlc *mlc, int repoll) #define OUT_LAST(pack) \ { HILSE_OUT_LAST, { .packet = pack }, 0, 0, 0, 0 }, -const struct hilse_node hil_mlc_se[HILSEN_END] = { +static const struct hilse_node hil_mlc_se[HILSEN_END] = { /* 0 HILSEN_START */ FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0) @@ -685,13 +686,12 @@ static int hilse_donode(hil_mlc *mlc) write_lock_irqsave(&mlc->lock, flags); pack = node->object.packet; out: - if (mlc->istarted) - goto out2; - /* Prepare to receive input */ - if ((node + 1)->act & HILSE_IN) - hilse_setup_input(mlc, node + 1); + if (!mlc->istarted) { + /* Prepare to receive input */ + if ((node + 1)->act & HILSE_IN) + hilse_setup_input(mlc, node + 1); + } - out2: write_unlock_irqrestore(&mlc->lock, flags); if (down_trylock(&mlc->osem)) { @@ -784,7 +784,7 @@ static void hil_mlcs_process(unsigned long unused) /************************* Keepalive timer task *********************/ -void hil_mlcs_timer(unsigned long data) +static void hil_mlcs_timer(unsigned long data) { hil_mlcs_probe = 1; tasklet_schedule(&hil_mlcs_tasklet); @@ -914,15 +914,15 @@ int hil_mlc_register(hil_mlc *mlc) mlc->ostarted = 0; rwlock_init(&mlc->lock); - init_MUTEX(&mlc->osem); + sema_init(&mlc->osem, 1); - init_MUTEX(&mlc->isem); + sema_init(&mlc->isem, 1); mlc->icount = -1; mlc->imatch = 0; mlc->opercnt = 0; - init_MUTEX_LOCKED(&(mlc->csem)); + sema_init(&(mlc->csem), 0); hil_mlc_clear_di_scratch(mlc); hil_mlc_clear_di_map(mlc, 0); @@ -931,9 +931,15 @@ int hil_mlc_register(hil_mlc *mlc) hil_mlc_copy_di_scratch(mlc, i); mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL); mlc->serio[i] = mlc_serio; + if (!mlc->serio[i]) { + for (; i >= 0; i--) + kfree(mlc->serio[i]); + return -ENOMEM; + } snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i); snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i); mlc_serio->id = hil_mlc_serio_id; + mlc_serio->id.id = i; /* HIL port no. */ mlc_serio->write = hil_mlc_serio_write; mlc_serio->open = hil_mlc_serio_open; mlc_serio->close = hil_mlc_serio_close; @@ -992,10 +998,8 @@ int hil_mlc_unregister(hil_mlc *mlc) static int __init hil_mlc_init(void) { - init_timer(&hil_mlcs_kicker); - hil_mlcs_kicker.expires = jiffies + HZ; - hil_mlcs_kicker.function = &hil_mlcs_timer; - add_timer(&hil_mlcs_kicker); + setup_timer(&hil_mlcs_kicker, &hil_mlcs_timer, 0); + mod_timer(&hil_mlcs_kicker, jiffies + HZ); tasklet_enable(&hil_mlcs_tasklet); @@ -1004,9 +1008,7 @@ static int __init hil_mlc_init(void) static void __exit hil_mlc_exit(void) { - del_timer(&hil_mlcs_kicker); - - tasklet_disable(&hil_mlcs_tasklet); + del_timer_sync(&hil_mlcs_kicker); tasklet_kill(&hil_mlcs_tasklet); } diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index 6af199805ff..852858e5d8d 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -67,10 +67,10 @@ #include <linux/module.h> #include <linux/ioport.h> #include <linux/time.h> +#include <linux/semaphore.h> #include <linux/slab.h> #include <linux/hil.h> #include <asm/io.h> -#include <asm/system.h> /* Machine-specific abstraction */ @@ -104,6 +104,10 @@ EXPORT_SYMBOL(__hp_sdc_enqueue_transaction); EXPORT_SYMBOL(hp_sdc_enqueue_transaction); EXPORT_SYMBOL(hp_sdc_dequeue_transaction); +static bool hp_sdc_disabled; +module_param_named(no_hpsdc, hp_sdc_disabled, bool, 0); +MODULE_PARM_DESC(no_hpsdc, "Do not enable HP SDC driver."); + static hp_i8042_sdc hp_sdc; /* All driver state is kept in here. */ /*************** primitives for use in any context *********************/ @@ -318,7 +322,7 @@ static void hp_sdc_tasklet(unsigned long foo) * it back to the application. and be less verbose. */ printk(KERN_WARNING PREFIX "read timeout (%ius)!\n", - tv.tv_usec - hp_sdc.rtv.tv_usec); + (int)(tv.tv_usec - hp_sdc.rtv.tv_usec)); curr->idx += hp_sdc.rqty; hp_sdc.rqty = 0; tmp = curr->seq[curr->actidx]; @@ -790,7 +794,7 @@ int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) /************************* Keepalive timer task *********************/ -void hp_sdc_kicker (unsigned long data) +static void hp_sdc_kicker(unsigned long data) { tasklet_schedule(&hp_sdc.task); /* Re-insert the periodic task. */ @@ -814,6 +818,7 @@ static const struct parisc_device_id hp_sdc_tbl[] = { MODULE_DEVICE_TABLE(parisc, hp_sdc_tbl); static int __init hp_sdc_init_hppa(struct parisc_device *d); +static struct delayed_work moduleloader_work; static struct parisc_driver hp_sdc_driver = { .name = "hp_sdc", @@ -873,7 +878,7 @@ static int __init hp_sdc_init(void) #endif errstr = "IRQ not available for"; - if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED|IRQF_SAMPLE_RANDOM, + if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED, "HP SDC", &hp_sdc)) goto err1; @@ -899,7 +904,7 @@ static int __init hp_sdc_init(void) ts_sync[1] = 0x0f; ts_sync[2] = ts_sync[3] = ts_sync[4] = ts_sync[5] = 0; t_sync.act.semaphore = &s_sync; - init_MUTEX_LOCKED(&s_sync); + sema_init(&s_sync, 0); hp_sdc_enqueue_transaction(&t_sync); down(&s_sync); /* Wait for t_sync to complete */ @@ -925,8 +930,15 @@ static int __init hp_sdc_init(void) #if defined(__hppa__) +static void request_module_delayed(struct work_struct *work) +{ + request_module("hp_sdc_mlc"); +} + static int __init hp_sdc_init_hppa(struct parisc_device *d) { + int ret; + if (!d) return 1; if (hp_sdc.dev != NULL) @@ -939,17 +951,26 @@ static int __init hp_sdc_init_hppa(struct parisc_device *d) hp_sdc.data_io = d->hpa.start + 0x800; hp_sdc.status_io = d->hpa.start + 0x801; - return hp_sdc_init(); + INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed); + + ret = hp_sdc_init(); + /* after successful initialization give SDC some time to settle + * and then load the hp_sdc_mlc upper layer driver */ + if (!ret) + schedule_delayed_work(&moduleloader_work, + msecs_to_jiffies(2000)); + + return ret; } #endif /* __hppa__ */ -#if !defined(__mc68000__) /* Link error on m68k! */ -static void __exit hp_sdc_exit(void) -#else static void hp_sdc_exit(void) -#endif { + /* do nothing if we don't have a SDC */ + if (!hp_sdc.dev) + return; + write_lock_irq(&hp_sdc.lock); /* Turn off all maskable "sub-function" irq's. */ @@ -963,11 +984,12 @@ static void hp_sdc_exit(void) free_irq(hp_sdc.irq, &hp_sdc); write_unlock_irq(&hp_sdc.lock); - del_timer(&hp_sdc.kicker); + del_timer_sync(&hp_sdc.kicker); tasklet_kill(&hp_sdc.task); #if defined(__hppa__) + cancel_delayed_work_sync(&moduleloader_work); if (unregister_parisc_driver(&hp_sdc_driver)) printk(KERN_WARNING PREFIX "Error unregistering HP SDC"); #endif @@ -983,6 +1005,11 @@ static int __init hp_sdc_register(void) unsigned char i; #endif + if (hp_sdc_disabled) { + printk(KERN_WARNING PREFIX "HP SDC driver disabled by no_hpsdc=1.\n"); + return -ENODEV; + } + hp_sdc.dev = NULL; hp_sdc.dev_err = 0; #if defined(__hppa__) @@ -1011,7 +1038,7 @@ static int __init hp_sdc_register(void) return hp_sdc.dev_err; } - init_MUTEX_LOCKED(&tq_init_sem); + sema_init(&tq_init_sem, 0); tq_init.actidx = 0; tq_init.idx = 1; diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c index c45ea74d53e..d50f0678bf4 100644 --- a/drivers/input/serio/hp_sdc_mlc.c +++ b/drivers/input/serio/hp_sdc_mlc.c @@ -40,7 +40,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/string.h> -#include <asm/semaphore.h> +#include <linux/semaphore.h> #define PREFIX "HP SDC MLC: " @@ -50,7 +50,7 @@ MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); MODULE_DESCRIPTION("Glue for onboard HIL MLC in HP-PARISC machines"); MODULE_LICENSE("Dual BSD/GPL"); -struct hp_sdc_mlc_priv_s { +static struct hp_sdc_mlc_priv_s { int emtestmode; hp_sdc_transaction trans; u8 tseq[16]; @@ -125,7 +125,7 @@ static void hp_sdc_mlc_isr (int irq, void *dev_id, break; default: - printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data); + printk(KERN_WARNING PREFIX "Unknown HIL Error status (%x)!\n", data); break; } @@ -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); @@ -305,6 +305,12 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) static int __init hp_sdc_mlc_init(void) { hil_mlc *mlc = &hp_sdc_mlc; + int err; + +#ifdef __mc68000__ + if (!MACH_IS_HP300) + return -ENODEV; +#endif printk(KERN_INFO PREFIX "Registering the System Domain Controller's HIL MLC.\n"); @@ -318,22 +324,21 @@ static int __init hp_sdc_mlc_init(void) mlc->out = &hp_sdc_mlc_out; mlc->priv = &hp_sdc_mlc_priv; - if (hil_mlc_register(mlc)) { + err = hil_mlc_register(mlc); + if (err) { printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n"); - goto err0; + return err; } if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) { printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n"); - goto err1; + if (hil_mlc_unregister(mlc)) + printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" + "This is bad. Could cause an oops.\n"); + return -EBUSY; } + return 0; - err1: - if (hil_mlc_unregister(mlc)) - printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" - "This is bad. Could cause an oops.\n"); - err0: - return -EBUSY; } static void __exit hp_sdc_mlc_exit(void) diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c new file mode 100644 index 00000000000..61326199462 --- /dev/null +++ b/drivers/input/serio/hyperv-keyboard.c @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2013, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/completion.h> +#include <linux/hyperv.h> +#include <linux/serio.h> +#include <linux/slab.h> + +/* + * Current version 1.0 + * + */ +#define SYNTH_KBD_VERSION_MAJOR 1 +#define SYNTH_KBD_VERSION_MINOR 0 +#define SYNTH_KBD_VERSION (SYNTH_KBD_VERSION_MINOR | \ + (SYNTH_KBD_VERSION_MAJOR << 16)) + + +/* + * Message types in the synthetic input protocol + */ +enum synth_kbd_msg_type { + SYNTH_KBD_PROTOCOL_REQUEST = 1, + SYNTH_KBD_PROTOCOL_RESPONSE = 2, + SYNTH_KBD_EVENT = 3, + SYNTH_KBD_LED_INDICATORS = 4, +}; + +/* + * Basic message structures. + */ +struct synth_kbd_msg_hdr { + __le32 type; +}; + +struct synth_kbd_msg { + struct synth_kbd_msg_hdr header; + char data[]; /* Enclosed message */ +}; + +union synth_kbd_version { + __le32 version; +}; + +/* + * Protocol messages + */ +struct synth_kbd_protocol_request { + struct synth_kbd_msg_hdr header; + union synth_kbd_version version_requested; +}; + +#define PROTOCOL_ACCEPTED BIT(0) +struct synth_kbd_protocol_response { + struct synth_kbd_msg_hdr header; + __le32 proto_status; +}; + +#define IS_UNICODE BIT(0) +#define IS_BREAK BIT(1) +#define IS_E0 BIT(2) +#define IS_E1 BIT(3) +struct synth_kbd_keystroke { + struct synth_kbd_msg_hdr header; + __le16 make_code; + __le16 reserved0; + __le32 info; /* Additional information */ +}; + + +#define HK_MAXIMUM_MESSAGE_SIZE 256 + +#define KBD_VSC_SEND_RING_BUFFER_SIZE (10 * PAGE_SIZE) +#define KBD_VSC_RECV_RING_BUFFER_SIZE (10 * PAGE_SIZE) + +#define XTKBD_EMUL0 0xe0 +#define XTKBD_EMUL1 0xe1 +#define XTKBD_RELEASE 0x80 + + +/* + * Represents a keyboard device + */ +struct hv_kbd_dev { + struct hv_device *hv_dev; + struct serio *hv_serio; + struct synth_kbd_protocol_request protocol_req; + struct synth_kbd_protocol_response protocol_resp; + /* Synchronize the request/response if needed */ + struct completion wait_event; + spinlock_t lock; /* protects 'started' field */ + bool started; +}; + +static void hv_kbd_on_receive(struct hv_device *hv_dev, + struct synth_kbd_msg *msg, u32 msg_length) +{ + struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + struct synth_kbd_keystroke *ks_msg; + unsigned long flags; + u32 msg_type = __le32_to_cpu(msg->header.type); + u32 info; + u16 scan_code; + + switch (msg_type) { + case SYNTH_KBD_PROTOCOL_RESPONSE: + /* + * Validate the information provided by the host. + * If the host is giving us a bogus packet, + * drop the packet (hoping the problem + * goes away). + */ + if (msg_length < sizeof(struct synth_kbd_protocol_response)) { + dev_err(&hv_dev->device, + "Illegal protocol response packet (len: %d)\n", + msg_length); + break; + } + + memcpy(&kbd_dev->protocol_resp, msg, + sizeof(struct synth_kbd_protocol_response)); + complete(&kbd_dev->wait_event); + break; + + case SYNTH_KBD_EVENT: + /* + * Validate the information provided by the host. + * If the host is giving us a bogus packet, + * drop the packet (hoping the problem + * goes away). + */ + if (msg_length < sizeof(struct synth_kbd_keystroke)) { + dev_err(&hv_dev->device, + "Illegal keyboard event packet (len: %d)\n", + msg_length); + break; + } + + ks_msg = (struct synth_kbd_keystroke *)msg; + info = __le32_to_cpu(ks_msg->info); + + /* + * Inject the information through the serio interrupt. + */ + spin_lock_irqsave(&kbd_dev->lock, flags); + if (kbd_dev->started) { + if (info & IS_E0) + serio_interrupt(kbd_dev->hv_serio, + XTKBD_EMUL0, 0); + if (info & IS_E1) + serio_interrupt(kbd_dev->hv_serio, + XTKBD_EMUL1, 0); + scan_code = __le16_to_cpu(ks_msg->make_code); + if (info & IS_BREAK) + scan_code |= XTKBD_RELEASE; + + serio_interrupt(kbd_dev->hv_serio, scan_code, 0); + } + spin_unlock_irqrestore(&kbd_dev->lock, flags); + break; + + default: + dev_err(&hv_dev->device, + "unhandled message type %d\n", msg_type); + } +} + +static void hv_kbd_handle_received_packet(struct hv_device *hv_dev, + struct vmpacket_descriptor *desc, + u32 bytes_recvd, + u64 req_id) +{ + struct synth_kbd_msg *msg; + u32 msg_sz; + + switch (desc->type) { + case VM_PKT_COMP: + break; + + case VM_PKT_DATA_INBAND: + /* + * We have a packet that has "inband" data. The API used + * for retrieving the packet guarantees that the complete + * packet is read. So, minimally, we should be able to + * parse the payload header safely (assuming that the host + * can be trusted. Trusting the host seems to be a + * reasonable assumption because in a virtualized + * environment there is not whole lot you can do if you + * don't trust the host. + * + * Nonetheless, let us validate if the host can be trusted + * (in a trivial way). The interesting aspect of this + * validation is how do you recover if we discover that the + * host is not to be trusted? Simply dropping the packet, I + * don't think is an appropriate recovery. In the interest + * of failing fast, it may be better to crash the guest. + * For now, I will just drop the packet! + */ + + msg_sz = bytes_recvd - (desc->offset8 << 3); + if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) { + /* + * Drop the packet and hope + * the problem magically goes away. + */ + dev_err(&hv_dev->device, + "Illegal packet (type: %d, tid: %llx, size: %d)\n", + desc->type, req_id, msg_sz); + break; + } + + msg = (void *)desc + (desc->offset8 << 3); + hv_kbd_on_receive(hv_dev, msg, msg_sz); + break; + + default: + dev_err(&hv_dev->device, + "unhandled packet type %d, tid %llx len %d\n", + desc->type, req_id, bytes_recvd); + break; + } +} + +static void hv_kbd_on_channel_callback(void *context) +{ + struct hv_device *hv_dev = context; + void *buffer; + int bufferlen = 0x100; /* Start with sensible size */ + u32 bytes_recvd; + u64 req_id; + int error; + + buffer = kmalloc(bufferlen, GFP_ATOMIC); + if (!buffer) + return; + + while (1) { + error = vmbus_recvpacket_raw(hv_dev->channel, buffer, bufferlen, + &bytes_recvd, &req_id); + switch (error) { + case 0: + if (bytes_recvd == 0) { + kfree(buffer); + return; + } + + hv_kbd_handle_received_packet(hv_dev, buffer, + bytes_recvd, req_id); + break; + + case -ENOBUFS: + kfree(buffer); + /* Handle large packet */ + bufferlen = bytes_recvd; + buffer = kmalloc(bytes_recvd, GFP_ATOMIC); + if (!buffer) + return; + break; + } + } +} + +static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev) +{ + struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + struct synth_kbd_protocol_request *request; + struct synth_kbd_protocol_response *response; + u32 proto_status; + int error; + + request = &kbd_dev->protocol_req; + memset(request, 0, sizeof(struct synth_kbd_protocol_request)); + request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST); + request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION); + + error = vmbus_sendpacket(hv_dev->channel, request, + sizeof(struct synth_kbd_protocol_request), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (error) + return error; + + if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ)) + return -ETIMEDOUT; + + response = &kbd_dev->protocol_resp; + proto_status = __le32_to_cpu(response->proto_status); + if (!(proto_status & PROTOCOL_ACCEPTED)) { + dev_err(&hv_dev->device, + "synth_kbd protocol request failed (version %d)\n", + SYNTH_KBD_VERSION); + return -ENODEV; + } + + return 0; +} + +static int hv_kbd_start(struct serio *serio) +{ + struct hv_kbd_dev *kbd_dev = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&kbd_dev->lock, flags); + kbd_dev->started = true; + spin_unlock_irqrestore(&kbd_dev->lock, flags); + + return 0; +} + +static void hv_kbd_stop(struct serio *serio) +{ + struct hv_kbd_dev *kbd_dev = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&kbd_dev->lock, flags); + kbd_dev->started = false; + spin_unlock_irqrestore(&kbd_dev->lock, flags); +} + +static int hv_kbd_probe(struct hv_device *hv_dev, + const struct hv_vmbus_device_id *dev_id) +{ + struct hv_kbd_dev *kbd_dev; + struct serio *hv_serio; + int error; + + kbd_dev = kzalloc(sizeof(struct hv_kbd_dev), GFP_KERNEL); + hv_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!kbd_dev || !hv_serio) { + error = -ENOMEM; + goto err_free_mem; + } + + kbd_dev->hv_dev = hv_dev; + kbd_dev->hv_serio = hv_serio; + spin_lock_init(&kbd_dev->lock); + init_completion(&kbd_dev->wait_event); + hv_set_drvdata(hv_dev, kbd_dev); + + hv_serio->dev.parent = &hv_dev->device; + hv_serio->id.type = SERIO_8042_XL; + hv_serio->port_data = kbd_dev; + strlcpy(hv_serio->name, dev_name(&hv_dev->device), + sizeof(hv_serio->name)); + strlcpy(hv_serio->phys, dev_name(&hv_dev->device), + sizeof(hv_serio->phys)); + + hv_serio->start = hv_kbd_start; + hv_serio->stop = hv_kbd_stop; + + error = vmbus_open(hv_dev->channel, + KBD_VSC_SEND_RING_BUFFER_SIZE, + KBD_VSC_RECV_RING_BUFFER_SIZE, + NULL, 0, + hv_kbd_on_channel_callback, + hv_dev); + if (error) + goto err_free_mem; + + error = hv_kbd_connect_to_vsp(hv_dev); + if (error) + goto err_close_vmbus; + + serio_register_port(kbd_dev->hv_serio); + return 0; + +err_close_vmbus: + vmbus_close(hv_dev->channel); +err_free_mem: + kfree(hv_serio); + kfree(kbd_dev); + return error; +} + +static int hv_kbd_remove(struct hv_device *hv_dev) +{ + struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + + serio_unregister_port(kbd_dev->hv_serio); + vmbus_close(hv_dev->channel); + kfree(kbd_dev); + + hv_set_drvdata(hv_dev, NULL); + + return 0; +} + +/* + * Keyboard GUID + * {f912ad6d-2b17-48ea-bd65-f927a61c7684} + */ +#define HV_KBD_GUID \ + .guid = { \ + 0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, \ + 0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 \ + } + +static const struct hv_vmbus_device_id id_table[] = { + /* Keyboard guid */ + { HV_KBD_GUID, }, + { }, +}; + +MODULE_DEVICE_TABLE(vmbus, id_table); + +static struct hv_driver hv_kbd_drv = { + .name = KBUILD_MODNAME, + .id_table = id_table, + .probe = hv_kbd_probe, + .remove = hv_kbd_remove, +}; + +static int __init hv_kbd_init(void) +{ + return vmbus_driver_register(&hv_kbd_drv); +} + +static void __exit hv_kbd_exit(void) +{ + vmbus_driver_unregister(&hv_kbd_drv); +} + +MODULE_LICENSE("GPL"); +module_init(hv_kbd_init); +module_exit(hv_kbd_exit); diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h index 3b4e13b9ce1..a5eed2ade53 100644 --- a/drivers/input/serio/i8042-io.h +++ b/drivers/input/serio/i8042-io.h @@ -25,8 +25,13 @@ #elif defined(__arm__) /* defined in include/asm-arm/arch-xxx/irqs.h */ #include <asm/irq.h> -#elif defined(CONFIG_SUPERH64) +#elif defined(CONFIG_SH_CAYMAN) #include <asm/irq.h> +#elif defined(CONFIG_PPC) +extern int of_i8042_kbd_irq; +extern int of_i8042_aux_irq; +# define I8042_KBD_IRQ of_i8042_kbd_irq +# define I8042_AUX_IRQ of_i8042_aux_irq #else # define I8042_KBD_IRQ 1 # define I8042_AUX_IRQ 12 @@ -67,11 +72,11 @@ static inline int i8042_platform_init(void) * On some platforms touching the i8042 data register region can do really * bad things. Because of this the region is always reserved on such boxes. */ -#if defined(CONFIG_PPC_MERGE) +#if defined(CONFIG_PPC) if (check_legacy_ioport(I8042_DATA_REG)) return -ENODEV; #endif -#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__) +#if !defined(__sh__) && !defined(__alpha__) if (!request_region(I8042_DATA_REG, 16, "i8042")) return -EBUSY; #endif diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h index 2906e1b60c0..f708c75d16f 100644 --- a/drivers/input/serio/i8042-ppcio.h +++ b/drivers/input/serio/i8042-ppcio.h @@ -52,81 +52,6 @@ static inline void i8042_platform_exit(void) { } -#elif defined(CONFIG_SPRUCE) - -#define I8042_KBD_IRQ 22 -#define I8042_AUX_IRQ 21 - -#define I8042_KBD_PHYS_DESC "spruceps2/serio0" -#define I8042_AUX_PHYS_DESC "spruceps2/serio1" -#define I8042_MUX_PHYS_DESC "spruceps2/serio%d" - -#define I8042_COMMAND_REG 0xff810000 -#define I8042_DATA_REG 0xff810001 - -static inline int i8042_read_data(void) -{ - unsigned long kbd_data; - - __raw_writel(0x00000088, 0xff500008); - eieio(); - - __raw_writel(0x03000000, 0xff50000c); - eieio(); - - asm volatile("lis 7,0xff88 \n\ - lswi 6,7,0x8 \n\ - mr %0,6" - : "=r" (kbd_data) :: "6", "7"); - - __raw_writel(0x00000000, 0xff50000c); - eieio(); - - return (unsigned char)(kbd_data >> 24); -} - -static inline int i8042_read_status(void) -{ - unsigned long kbd_status; - - __raw_writel(0x00000088, 0xff500008); - eieio(); - - __raw_writel(0x03000000, 0xff50000c); - eieio(); - - asm volatile("lis 7,0xff88 \n\ - ori 7,7,0x8 \n\ - lswi 6,7,0x8 \n\ - mr %0,6" - : "=r" (kbd_status) :: "6", "7"); - - __raw_writel(0x00000000, 0xff50000c); - eieio(); - - return (unsigned char)(kbd_status >> 24); -} - -static inline void i8042_write_data(int val) -{ - *((unsigned char *)0xff810000) = (char)val; -} - -static inline void i8042_write_command(int val) -{ - *((unsigned char *)0xff810001) = (char)val; -} - -static inline int i8042_platform_init(void) -{ - i8042_reset = 1; - return 0; -} - -static inline void i8042_platform_exit(void) -{ -} - #else #include "i8042-io.h" diff --git a/drivers/input/serio/i8042-snirm.h b/drivers/input/serio/i8042-snirm.h new file mode 100644 index 00000000000..409a9341143 --- /dev/null +++ b/drivers/input/serio/i8042-snirm.h @@ -0,0 +1,75 @@ +#ifndef _I8042_SNIRM_H +#define _I8042_SNIRM_H + +#include <asm/sni.h> + +/* + * 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. + */ + +/* + * Names. + */ + +#define I8042_KBD_PHYS_DESC "onboard/serio0" +#define I8042_AUX_PHYS_DESC "onboard/serio1" +#define I8042_MUX_PHYS_DESC "onboard/serio%d" + +/* + * IRQs. + */ +static int i8042_kbd_irq; +static int i8042_aux_irq; +#define I8042_KBD_IRQ i8042_kbd_irq +#define I8042_AUX_IRQ i8042_aux_irq + +static void __iomem *kbd_iobase; + +#define I8042_COMMAND_REG (kbd_iobase + 0x64UL) +#define I8042_DATA_REG (kbd_iobase + 0x60UL) + +static inline int i8042_read_data(void) +{ + return readb(kbd_iobase + 0x60UL); +} + +static inline int i8042_read_status(void) +{ + return readb(kbd_iobase + 0x64UL); +} + +static inline void i8042_write_data(int val) +{ + writeb(val, kbd_iobase + 0x60UL); +} + +static inline void i8042_write_command(int val) +{ + writeb(val, kbd_iobase + 0x64UL); +} +static inline int i8042_platform_init(void) +{ + /* RM200 is strange ... */ + if (sni_brd_type == SNI_BRD_RM200) { + kbd_iobase = ioremap(0x16000000, 4); + i8042_kbd_irq = 33; + i8042_aux_irq = 44; + } else { + kbd_iobase = ioremap(0x14000000, 4); + i8042_kbd_irq = 1; + i8042_aux_irq = 12; + } + if (!kbd_iobase) + return -ENOMEM; + + return 0; +} + +static inline void i8042_platform_exit(void) +{ + +} + +#endif /* _I8042_SNIRM_H */ diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index d9ca55891cd..d6aa4c67dbb 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -1,10 +1,11 @@ #ifndef _I8042_SPARCIO_H #define _I8042_SPARCIO_H +#include <linux/of_device.h> + #include <asm/io.h> #include <asm/oplib.h> #include <asm/prom.h> -#include <asm/of_device.h> static int i8042_kbd_irq = -1; static int i8042_aux_irq = -1; @@ -41,33 +42,35 @@ static inline void i8042_write_command(int val) writeb(val, kbd_iobase + 0x64UL); } +#ifdef CONFIG_PCI + #define OBP_PS2KBD_NAME1 "kb_ps2" #define OBP_PS2KBD_NAME2 "keyboard" #define OBP_PS2MS_NAME1 "kdmouse" #define OBP_PS2MS_NAME2 "mouse" -static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_device_id *match) +static int sparc_i8042_probe(struct platform_device *op) { - struct device_node *dp = op->node; + struct device_node *dp = op->dev.of_node; dp = dp->child; while (dp) { if (!strcmp(dp->name, OBP_PS2KBD_NAME1) || !strcmp(dp->name, OBP_PS2KBD_NAME2)) { - struct of_device *kbd = of_find_device_by_node(dp); - unsigned int irq = kbd->irqs[0]; + struct platform_device *kbd = of_find_device_by_node(dp); + unsigned int irq = kbd->archdata.irqs[0]; if (irq == 0xffffffff) - irq = op->irqs[0]; + irq = op->archdata.irqs[0]; i8042_kbd_irq = irq; kbd_iobase = of_ioremap(&kbd->resource[0], 0, 8, "kbd"); kbd_res = &kbd->resource[0]; } else if (!strcmp(dp->name, OBP_PS2MS_NAME1) || !strcmp(dp->name, OBP_PS2MS_NAME2)) { - struct of_device *ms = of_find_device_by_node(dp); - unsigned int irq = ms->irqs[0]; + struct platform_device *ms = of_find_device_by_node(dp); + unsigned int irq = ms->archdata.irqs[0]; if (irq == 0xffffffff) - irq = op->irqs[0]; + irq = op->archdata.irqs[0]; i8042_aux_irq = irq; } @@ -77,14 +80,14 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev return 0; } -static int __devexit sparc_i8042_remove(struct of_device *op) +static int sparc_i8042_remove(struct platform_device *op) { of_iounmap(kbd_res, kbd_iobase, 8); return 0; } -static struct of_device_id sparc_i8042_match[] = { +static const struct of_device_id sparc_i8042_match[] = { { .name = "8042", }, @@ -92,18 +95,18 @@ static struct of_device_id sparc_i8042_match[] = { }; MODULE_DEVICE_TABLE(of, sparc_i8042_match); -static struct of_platform_driver sparc_i8042_driver = { - .name = "i8042", - .match_table = sparc_i8042_match, +static struct platform_driver sparc_i8042_driver = { + .driver = { + .name = "i8042", + .owner = THIS_MODULE, + .of_match_table = sparc_i8042_match, + }, .probe = sparc_i8042_probe, - .remove = __devexit_p(sparc_i8042_remove), + .remove = sparc_i8042_remove, }; static int __init i8042_platform_init(void) { -#ifndef CONFIG_PCI - return -ENODEV; -#else struct device_node *root = of_find_node_by_path("/"); if (!strcmp(root->name, "SUNW,JavaStation-1")) { @@ -113,8 +116,7 @@ static int __init i8042_platform_init(void) if (!kbd_iobase) return -ENODEV; } else { - int err = of_register_driver(&sparc_i8042_driver, - &of_bus_type); + int err = platform_driver_register(&sparc_i8042_driver); if (err) return err; @@ -131,17 +133,25 @@ static int __init i8042_platform_init(void) i8042_reset = 1; return 0; -#endif /* CONFIG_PCI */ } static inline void i8042_platform_exit(void) { -#ifdef CONFIG_PCI struct device_node *root = of_find_node_by_path("/"); if (strcmp(root->name, "SUNW,JavaStation-1")) - of_unregister_driver(&sparc_i8042_driver); -#endif + platform_driver_unregister(&sparc_i8042_driver); +} + +#else /* !CONFIG_PCI */ +static int __init i8042_platform_init(void) +{ + return -ENODEV; +} + +static inline void i8042_platform_exit(void) +{ } +#endif /* !CONFIG_PCI */ #endif /* _I8042_SPARCIO_H */ diff --git a/drivers/input/serio/i8042-unicore32io.h b/drivers/input/serio/i8042-unicore32io.h new file mode 100644 index 00000000000..73f5cc124a3 --- /dev/null +++ b/drivers/input/serio/i8042-unicore32io.h @@ -0,0 +1,73 @@ +/* + * Code specific to PKUnity SoC and UniCore ISA + * + * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + * Copyright (C) 2001-2011 Guan Xuetao + * + * 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. + */ +#ifndef _I8042_UNICORE32_H +#define _I8042_UNICORE32_H + +#include <mach/hardware.h> + +/* + * Names. + */ +#define I8042_KBD_PHYS_DESC "isa0060/serio0" +#define I8042_AUX_PHYS_DESC "isa0060/serio1" +#define I8042_MUX_PHYS_DESC "isa0060/serio%d" + +/* + * IRQs. + */ +#define I8042_KBD_IRQ IRQ_PS2_KBD +#define I8042_AUX_IRQ IRQ_PS2_AUX + +/* + * Register numbers. + */ +#define I8042_COMMAND_REG PS2_COMMAND +#define I8042_STATUS_REG PS2_STATUS +#define I8042_DATA_REG PS2_DATA + +#define I8042_REGION_START (resource_size_t)(PS2_DATA) +#define I8042_REGION_SIZE (resource_size_t)(16) + +static inline int i8042_read_data(void) +{ + return readb(I8042_DATA_REG); +} + +static inline int i8042_read_status(void) +{ + return readb(I8042_STATUS_REG); +} + +static inline void i8042_write_data(int val) +{ + writeb(val, I8042_DATA_REG); +} + +static inline void i8042_write_command(int val) +{ + writeb(val, I8042_COMMAND_REG); +} + +static inline int i8042_platform_init(void) +{ + if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042")) + return -EBUSY; + + i8042_reset = 1; + return 0; +} + +static inline void i8042_platform_exit(void) +{ + release_mem_region(I8042_REGION_START, I8042_REGION_SIZE); +} + +#endif /* _I8042_UNICORE32_H */ diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 6858bc58f0f..136b7b204f5 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -7,6 +7,10 @@ * the Free Software Foundation. */ +#ifdef CONFIG_X86 +#include <asm/x86_init.h> +#endif + /* * Names. */ @@ -63,13 +67,39 @@ static inline void i8042_write_command(int val) outb(val, I8042_COMMAND_REG); } -#if defined(__i386__) +#ifdef CONFIG_X86 #include <linux/dmi.h> -static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { + { + /* + * Arima-Rioworks HDAMB - + * AUX LOOP command does not raise AUX IRQ + */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), + DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), + DMI_MATCH(DMI_BOARD_VERSION, "Rev E"), + }, + }, + { + /* ASUS G1S */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_BOARD_NAME, "G1S"), + DMI_MATCH(DMI_BOARD_VERSION, "1.0"), + }, + }, + { + /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"), + DMI_MATCH(DMI_BOARD_VERSION, "REV 2.X"), + }, + }, { - .ident = "Compaq Proliant 8500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), @@ -77,7 +107,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Compaq Proliant DL760", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), @@ -85,13 +114,89 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "OQO Model 01", + /* OQO Model 01 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "OQO"), DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), DMI_MATCH(DMI_PRODUCT_VERSION, "00"), }, }, + { + /* ULI EV4873 - AUX LOOP does not work properly */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ULI"), + DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + }, + }, + { + /* Microsoft Virtual Machine */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), + DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"), + }, + }, + { + /* Medion MAM 2070 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + }, + }, + { + /* Blue FB5601 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "blue"), + DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), + DMI_MATCH(DMI_PRODUCT_VERSION, "M606"), + }, + }, + { + /* Gigabyte M912 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "M912"), + DMI_MATCH(DMI_PRODUCT_VERSION, "01"), + }, + }, + { + /* Gigabyte M1022M netbook */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "M1022E"), + DMI_MATCH(DMI_BOARD_VERSION, "1.02"), + }, + }, + { + /* Gigabyte Spring Peak - defines wrong chassis type */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"), + }, + }, + { + /* Gigabyte T1005 - defines wrong chassis type ("Other") */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "T1005"), + }, + }, + { + /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), + }, + }, { } }; @@ -102,69 +207,83 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { * ... apparently some Toshibas don't like MUX mode either and * die horrible death on reboot. */ -static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { { - .ident = "Fujitsu Lifebook P7010/P7010D", + /* Fujitsu Lifebook P7010/P7010D */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), }, }, { - .ident = "Fujitsu Lifebook P7010", + /* Fujitsu Lifebook P7010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), }, }, { - .ident = "Fujitsu Lifebook P5020D", + /* Fujitsu Lifebook P5020D */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), }, }, { - .ident = "Fujitsu Lifebook S2000", + /* Fujitsu Lifebook S2000 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), }, }, { - .ident = "Fujitsu Lifebook S6230", + /* Fujitsu Lifebook S6230 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), }, }, { - .ident = "Fujitsu T70H", + /* Fujitsu T70H */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), }, }, { - .ident = "Fujitsu-Siemens Lifebook T3010", + /* Fujitsu-Siemens Lifebook T3010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), }, }, { - .ident = "Fujitsu-Siemens Lifebook E4010", + /* Fujitsu-Siemens Lifebook E4010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), }, }, { + /* Fujitsu-Siemens Amilo Pro 2010 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), + }, + }, + { + /* Fujitsu-Siemens Amilo Pro 2030 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), + }, + }, + { /* * No data is coming from the touchscreen unless KBC * is in legacy mode. */ - .ident = "Panasonic CF-29", + /* Panasonic CF-29 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), @@ -172,71 +291,414 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, { /* - * Errors on MUX ports are reported without raising AUXDATA + * HP Pavilion DV4017EA - + * errors on MUX ports are reported without raising AUXDATA * causing "spurious NAK" messages. */ - .ident = "HP Pavilion DV4017EA", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), }, }, { - .ident = "Toshiba P10", + /* + * HP Pavilion ZT1000 - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), + DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"), + }, + }, + { + /* + * HP Pavilion DV4270ca - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), + }, + }, + { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), }, }, { - .ident = "Toshiba Equium A110", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), }, }, { - .ident = "Alienware Sentia", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"), + }, + }, + { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), }, }, { - .ident = "Sharp Actius MM20", + /* Sharp Actius MM20 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), }, }, { - .ident = "Sony Vaio FS-115b", + /* Sony Vaio FS-115b */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), }, }, { - .ident = "Amoi M636/A737", + /* + * Sony Vaio FZ-240E - + * reset and GET ID commands issued via KBD port are + * sometimes being delivered to AUX3. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), + }, + }, + { + /* + * Most (all?) VAIOs do not have external PS/2 ports nor + * they implement active multiplexing properly, and + * MUX discovery usually messes up keyboard/touchpad. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + }, + }, + { + /* Amoi M636/A737 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), }, }, + { + /* Lenovo 3000 n100 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), + }, + }, + { + /* Acer Aspire 5710 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"), + }, + }, + { + /* Gericom Bellagio */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), + DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), + }, + }, + { + /* IBM 2656 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), + DMI_MATCH(DMI_PRODUCT_NAME, "2656"), + }, + }, + { + /* Dell XPS M1530 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), + }, + }, + { + /* Compal HEL80I */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), + }, + }, + { + /* Dell Vostro 1510 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), + }, + }, + { + /* Acer Aspire 5536 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), + }, + }, + { + /* Dell Vostro V13 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + }, + }, + { + /* Newer HP Pavilion dv4 models */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + }, + }, { } }; +static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { + { + /* MSI Wind U-100 */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "U-100"), + DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + }, + }, + { + /* LG Electronics X110 */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X110"), + DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), + }, + }, + { + /* Acer Aspire One 150 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), + }, + }, + { + /* Advent 4211 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), + }, + }, + { + /* Medion Akoya Mini E1210 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), + DMI_MATCH(DMI_PRODUCT_NAME, "E1210"), + }, + }, + { + /* Medion Akoya E1222 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), + DMI_MATCH(DMI_PRODUCT_NAME, "E122X"), + }, + }, + { + /* Mivvy M310 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), + DMI_MATCH(DMI_PRODUCT_NAME, "N10"), + }, + }, + { + /* Dell Vostro 1320 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), + }, + }, + { + /* Dell Vostro 1520 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), + }, + }, + { + /* Dell Vostro 1720 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), + }, + }, + { + /* Lenovo Ideapad U455 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20046"), + }, + }, + { } +}; +#ifdef CONFIG_PNP +static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = { + { + /* Intel MBO Desktop D845PESV */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + }, + }, + { + /* MSI Wind U-100 */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "U-100"), + DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + }, + }, + { } +}; +static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = { + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ + }, + }, + { } +}; #endif +static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { + { + /* Dell Vostro V13 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + }, + }, + { + /* Newer HP Pavilion dv4 models */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + }, + }, + { } +}; + +/* + * Some Wistron based laptops need us to explicitly enable the 'Dritek + * keyboard extension' to make their extra keys start generating scancodes. + * Originally, this was just confined to older laptops, but a few Acer laptops + * have turned up in 2007 that also need this again. + */ +static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { + { + /* Acer Aspire 5100 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), + }, + }, + { + /* Acer Aspire 5610 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), + }, + }, + { + /* Acer Aspire 5630 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), + }, + }, + { + /* Acer Aspire 5650 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), + }, + }, + { + /* Acer Aspire 5680 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), + }, + }, + { + /* Acer Aspire 5720 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), + }, + }, + { + /* Acer Aspire 9110 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), + }, + }, + { + /* Acer TravelMate 660 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), + }, + }, + { + /* Acer TravelMate 2490 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), + }, + }, + { + /* Acer TravelMate 4280 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), + }, + }, + { } +}; + +#endif /* CONFIG_X86 */ #ifdef CONFIG_PNP #include <linux/pnp.h> -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; @@ -247,6 +709,17 @@ static int i8042_pnp_aux_irq; static char i8042_pnp_kbd_name[32]; static char i8042_pnp_aux_name[32]; +static void i8042_pnp_id_to_string(struct pnp_id *id, char *dst, int dst_size) +{ + strlcpy(dst, "PNP:", dst_size); + + while (id) { + strlcat(dst, " ", dst_size); + strlcat(dst, id->id, dst_size); + id = id->next; + } +} + static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *did) { if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1) @@ -258,11 +731,16 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id * if (pnp_irq_valid(dev,0)) i8042_pnp_kbd_irq = pnp_irq(dev, 0); - strncpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name)); + strlcpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name)); if (strlen(pnp_dev_name(dev))) { - strncat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name)); - strncat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name)); + strlcat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name)); + strlcat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name)); } + i8042_pnp_id_to_string(dev->id, i8042_kbd_firmware_id, + sizeof(i8042_kbd_firmware_id)); + + /* Keyboard ports are always supposed to be wakeup-enabled */ + device_set_wakeup_enable(&dev->dev, true); i8042_pnp_kbd_devices++; return 0; @@ -279,21 +757,37 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id * if (pnp_irq_valid(dev, 0)) i8042_pnp_aux_irq = pnp_irq(dev, 0); - strncpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name)); + strlcpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name)); if (strlen(pnp_dev_name(dev))) { - strncat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name)); - strncat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name)); + strlcat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name)); + strlcat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name)); } + i8042_pnp_id_to_string(dev->id, i8042_aux_firmware_id, + sizeof(i8042_aux_firmware_id)); i8042_pnp_aux_devices++; return 0; } static struct pnp_device_id pnp_kbd_devids[] = { + { .id = "PNP0300", .driver_data = 0 }, + { .id = "PNP0301", .driver_data = 0 }, + { .id = "PNP0302", .driver_data = 0 }, { .id = "PNP0303", .driver_data = 0 }, + { .id = "PNP0304", .driver_data = 0 }, + { .id = "PNP0305", .driver_data = 0 }, + { .id = "PNP0306", .driver_data = 0 }, + { .id = "PNP0309", .driver_data = 0 }, + { .id = "PNP030a", .driver_data = 0 }, { .id = "PNP030b", .driver_data = 0 }, + { .id = "PNP0320", .driver_data = 0 }, + { .id = "PNP0343", .driver_data = 0 }, + { .id = "PNP0344", .driver_data = 0 }, + { .id = "PNP0345", .driver_data = 0 }, + { .id = "CPQA0D7", .driver_data = 0 }, { .id = "", }, }; +MODULE_DEVICE_TABLE(pnp, pnp_kbd_devids); static struct pnp_driver i8042_pnp_kbd_driver = { .name = "i8042 kbd", @@ -302,6 +796,7 @@ static struct pnp_driver i8042_pnp_kbd_driver = { }; static struct pnp_device_id pnp_aux_devids[] = { + { .id = "AUI0200", .driver_data = 0 }, { .id = "FJC6000", .driver_data = 0 }, { .id = "FJC6001", .driver_data = 0 }, { .id = "PNP0f03", .driver_data = 0 }, @@ -314,6 +809,7 @@ static struct pnp_device_id pnp_aux_devids[] = { { .id = "SYN0801", .driver_data = 0 }, { .id = "", }, }; +MODULE_DEVICE_TABLE(pnp, pnp_aux_devids); static struct pnp_driver i8042_pnp_aux_driver = { .name = "i8042 aux", @@ -324,12 +820,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); } } @@ -337,27 +833,33 @@ static void i8042_pnp_exit(void) static int __init i8042_pnp_init(void) { char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 }; + bool pnp_data_busted = false; int err; +#ifdef CONFIG_X86 + if (dmi_check_system(i8042_dmi_nopnp_table)) + i8042_nopnp = true; +#endif + if (i8042_nopnp) { - printk(KERN_INFO "i8042: PNP detection disabled\n"); + pr_info("PNP detection disabled\n"); return 0; } 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(); #if defined(__ia64__) return -ENODEV; #else - printk(KERN_INFO "PNP: No PS/2 controller found. Probing ports directly.\n"); + pr_info("PNP: No PS/2 controller found. Probing ports directly.\n"); return 0; #endif } @@ -369,7 +871,7 @@ static int __init i8042_pnp_init(void) snprintf(aux_irq_str, sizeof(aux_irq_str), "%d", i8042_pnp_aux_irq); - printk(KERN_INFO "PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n", + pr_info("PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n", i8042_pnp_kbd_name, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "", i8042_pnp_aux_name, i8042_pnp_data_reg, i8042_pnp_command_reg, @@ -378,33 +880,46 @@ 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) && - i8042_pnp_data_reg != i8042_data_reg) || !i8042_pnp_data_reg) { - printk(KERN_WARNING "PNP: PS/2 controller has invalid data port %#x; using default %#x\n", + i8042_pnp_data_reg != i8042_data_reg) || + !i8042_pnp_data_reg) { + pr_warn("PNP: PS/2 controller has invalid data port %#x; using default %#x\n", i8042_pnp_data_reg, i8042_data_reg); i8042_pnp_data_reg = i8042_data_reg; + pnp_data_busted = true; } if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) && - i8042_pnp_command_reg != i8042_command_reg) || !i8042_pnp_command_reg) { - printk(KERN_WARNING "PNP: PS/2 controller has invalid command port %#x; using default %#x\n", + i8042_pnp_command_reg != i8042_command_reg) || + !i8042_pnp_command_reg) { + pr_warn("PNP: PS/2 controller has invalid command port %#x; using default %#x\n", i8042_pnp_command_reg, i8042_command_reg); i8042_pnp_command_reg = i8042_command_reg; + pnp_data_busted = true; } if (!i8042_nokbd && !i8042_pnp_kbd_irq) { - printk(KERN_WARNING "PNP: PS/2 controller doesn't have KBD irq; using default %d\n", i8042_kbd_irq); + pr_warn("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 = true; } if (!i8042_noaux && !i8042_pnp_aux_irq) { - printk(KERN_WARNING "PNP: PS/2 controller doesn't have AUX irq; using default %d\n", i8042_aux_irq); - i8042_pnp_aux_irq = i8042_aux_irq; + if (!pnp_data_busted && i8042_pnp_kbd_irq) { + pr_warn("PNP: PS/2 appears to have AUX port disabled, " + "if this is incorrect please boot with i8042.nopnp\n"); + i8042_noaux = true; + } else { + pr_warn("PNP: PS/2 controller doesn't have AUX irq; using default %d\n", + i8042_aux_irq); + i8042_pnp_aux_irq = i8042_aux_irq; + } } i8042_data_reg = i8042_pnp_data_reg; @@ -412,6 +927,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; } @@ -424,6 +944,13 @@ static int __init i8042_platform_init(void) { int retval; +#ifdef CONFIG_X86 + u8 a20_on = 0xdf; + /* Just return if pre-detection shows no i8042 controller exist */ + if (!x86_platform.i8042_detect()) + return -ENODEV; +#endif + /* * On ix86 platforms touching the i8042 data register region can do really * bad things. Because of this the region is always reserved on ix86 boxes. @@ -440,16 +967,33 @@ static int __init i8042_platform_init(void) return retval; #if defined(__ia64__) - i8042_reset = 1; + i8042_reset = true; #endif -#if defined(__i386__) +#ifdef CONFIG_X86 + if (dmi_check_system(i8042_dmi_reset_table)) + 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; -#endif + i8042_nomux = true; + + if (dmi_check_system(i8042_dmi_notimeout_table)) + i8042_notimeout = true; + + if (dmi_check_system(i8042_dmi_dritek_table)) + i8042_dritek = true; + + /* + * A20 was already enabled during early kernel init. But some buggy + * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to + * resume from S3. So we do it here and hope that nothing breaks. + */ + i8042_command(&a20_on, 0x10d1); + i8042_command(NULL, 0x00ff); /* Null command for SMM firmware */ +#endif /* CONFIG_X86 */ return retval; } diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 3888dc307e0..3807c3e971c 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -10,9 +10,11 @@ * the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/types.h> #include <linux/delay.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/init.h> @@ -20,6 +22,8 @@ #include <linux/err.h> #include <linux/rcupdate.h> #include <linux/platform_device.h> +#include <linux/i8042.h> +#include <linux/slab.h> #include <asm/io.h> @@ -27,63 +31,87 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); 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."); +MODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing controller 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"); -static unsigned int i8042_blink_frequency = 500; -module_param_named(panicblink, i8042_blink_frequency, uint, 0600); -MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics"); +static bool i8042_notimeout; +module_param_named(notimeout, i8042_notimeout, bool, 0); +MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042"); + +#ifdef CONFIG_X86 +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 +static bool i8042_bypass_aux_irq_test; +static char i8042_kbd_firmware_id[128]; +static char i8042_aux_firmware_id[128]; + #include "i8042.h" +/* + * i8042_lock protects serialization between i8042_command and + * the interrupt handler. + */ static DEFINE_SPINLOCK(i8042_lock); +/* + * Writers to AUX and KBD ports as well as users issuing i8042_command + * directly should acquire i8042_mutex (by means of calling + * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that + * they do not disturb each other (unfortunately in many i8042 + * implementations write to one of the ports will immediately abort + * command that is being processed by another port). + */ +static DEFINE_MUTEX(i8042_mutex); + struct i8042_port { struct serio *serio; int irq; - unsigned char exists; + bool exists; signed char mux; }; @@ -96,13 +124,69 @@ 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; static irqreturn_t i8042_interrupt(int irq, void *dev_id); +static bool (*i8042_platform_filter)(unsigned char data, unsigned char str, + struct serio *serio); + +void i8042_lock_chip(void) +{ + mutex_lock(&i8042_mutex); +} +EXPORT_SYMBOL(i8042_lock_chip); + +void i8042_unlock_chip(void) +{ + mutex_unlock(&i8042_mutex); +} +EXPORT_SYMBOL(i8042_unlock_chip); + +int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str, + struct serio *serio)) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&i8042_lock, flags); + + if (i8042_platform_filter) { + ret = -EBUSY; + goto out; + } + + i8042_platform_filter = filter; + +out: + spin_unlock_irqrestore(&i8042_lock, flags); + return ret; +} +EXPORT_SYMBOL(i8042_install_filter); + +int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str, + struct serio *port)) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&i8042_lock, flags); + + if (i8042_platform_filter != filter) { + ret = -EINVAL; + goto out; + } + + i8042_platform_filter = NULL; + +out: + spin_unlock_irqrestore(&i8042_lock, flags); + return ret; +} +EXPORT_SYMBOL(i8042_remove_filter); /* * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to @@ -141,21 +225,26 @@ static int i8042_flush(void) { unsigned long flags; unsigned char data, str; - int i = 0; + int count = 0; + int retval = 0; spin_lock_irqsave(&i8042_lock, flags); - while (((str = i8042_read_status()) & I8042_STR_OBF) && (i < I8042_BUFFER_SIZE)) { - udelay(50); - data = i8042_read_data(); - i++; - dbg("%02x <- i8042 (flush, %s)", data, - str & I8042_STR_AUXDATA ? "aux" : "kbd"); + while ((str = i8042_read_status()) & I8042_STR_OBF) { + if (count++ < I8042_BUFFER_SIZE) { + udelay(50); + data = i8042_read_data(); + dbg("%02x <- i8042 (flush, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); + } else { + retval = -EIO; + break; + } } spin_unlock_irqrestore(&i8042_lock, flags); - return i; + return retval; } /* @@ -177,38 +266,38 @@ static int __i8042_command(unsigned char *param, int command) if (error) return error; - dbg("%02x -> i8042 (command)", command & 0xff); + dbg("%02x -> i8042 (command)\n", command & 0xff); i8042_write_command(command & 0xff); for (i = 0; i < ((command >> 12) & 0xf); i++) { error = i8042_wait_write(); if (error) return error; - dbg("%02x -> i8042 (parameter)", param[i]); + dbg("%02x -> i8042 (parameter)\n", param[i]); i8042_write_data(param[i]); } for (i = 0; i < ((command >> 8) & 0xf); i++) { error = i8042_wait_read(); if (error) { - dbg(" -- i8042 (timeout)"); + dbg(" -- i8042 (timeout)\n"); return error; } if (command == I8042_CMD_AUX_LOOP && !(i8042_read_status() & I8042_STR_AUXDATA)) { - dbg(" -- i8042 (auxerr)"); + dbg(" -- i8042 (auxerr)\n"); return -1; } param[i] = i8042_read_data(); - dbg("%02x <- i8042 (return)", param[i]); + dbg("%02x <- i8042 (return)\n", param[i]); } return 0; } -static int i8042_command(unsigned char *param, int command) +int i8042_command(unsigned char *param, int command) { unsigned long flags; int retval; @@ -219,6 +308,7 @@ static int i8042_command(unsigned char *param, int command) return retval; } +EXPORT_SYMBOL(i8042_command); /* * i8042_kbd_write() sends a byte out through the keyboard interface. @@ -232,7 +322,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c) spin_lock_irqsave(&i8042_lock, flags); if (!(retval = i8042_wait_write())) { - dbg("%02x -> i8042 (kbd-data)", c); + dbg("%02x -> i8042 (kbd-data)\n", c); i8042_write_data(c); } @@ -254,6 +344,46 @@ 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)) + pr_warn("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)) + pr_err("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 @@ -263,7 +393,7 @@ static int i8042_start(struct serio *serio) { struct i8042_port *port = serio->port_data; - port->exists = 1; + port->exists = true; mb(); return 0; } @@ -277,12 +407,44 @@ static void i8042_stop(struct serio *serio) { struct i8042_port *port = serio->port_data; - port->exists = 0; - synchronize_sched(); + port->exists = false; + + /* + * We synchronize with both AUX and KBD IRQs because there is + * a (very unlikely) chance that AUX IRQ is raised for KBD port + * and vice versa. + */ + synchronize_irq(I8042_AUX_IRQ); + synchronize_irq(I8042_KBD_IRQ); port->serio = NULL; } /* + * i8042_filter() filters out unwanted bytes from the input data stream. + * It is called from i8042_interrupt and thus is running with interrupts + * off and i8042_lock held. + */ +static bool i8042_filter(unsigned char data, unsigned char str, + struct serio *serio) +{ + if (unlikely(i8042_suppress_kbd_ack)) { + if ((~str & I8042_STR_AUXDATA) && + (data == 0xfa || data == 0xfe)) { + i8042_suppress_kbd_ack--; + dbg("Extra keyboard ACK - filtered out\n"); + return true; + } + } + + if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) { + dbg("Filtered out by platform filter\n"); + return true; + } + + return false; +} + +/* * i8042_interrupt() is the most important function in this driver - * it handles the interrupts from the i8042, and sends incoming bytes * to the upper layers. @@ -291,22 +453,26 @@ static void i8042_stop(struct serio *serio) static irqreturn_t i8042_interrupt(int irq, void *dev_id) { struct i8042_port *port; + struct serio *serio; unsigned long flags; unsigned char str, data; unsigned int dfl; unsigned int port_no; + bool filtered; int ret = 1; spin_lock_irqsave(&i8042_lock, flags); + str = i8042_read_status(); if (unlikely(~str & I8042_STR_OBF)) { spin_unlock_irqrestore(&i8042_lock, flags); - if (irq) dbg("Interrupt %d, without any data", irq); + if (irq) + dbg("Interrupt %d, without any data\n", irq); ret = 0; goto out; } + data = i8042_read_data(); - spin_unlock_irqrestore(&i8042_lock, flags); if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { static unsigned long last_transmit; @@ -314,7 +480,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) dfl = 0; if (str & I8042_STR_MUXERR) { - dbg("MUX error, status is %02x, data is %02x", str, data); + dbg("MUX error, status is %02x, data is %02x\n", + str, data); /* * When MUXERR condition is signalled the data register can only contain * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately @@ -348,35 +515,33 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) } else { dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | - ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); + ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0); port_no = (str & I8042_STR_AUXDATA) ? I8042_AUX_PORT_NO : I8042_KBD_PORT_NO; } port = &i8042_ports[port_no]; + serio = port->exists ? port->serio : NULL; - dbg("%02x <- i8042 (interrupt, %d, %d%s%s)", + dbg("%02x <- i8042 (interrupt, %d, %d%s%s)\n", data, port_no, irq, dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_TIMEOUT ? ", timeout" : ""); - if (unlikely(i8042_suppress_kbd_ack)) - if (port_no == I8042_KBD_PORT_NO && - (data == 0xfa || data == 0xfe)) { - i8042_suppress_kbd_ack--; - goto out; - } + filtered = i8042_filter(data, str, serio); - if (likely(port->exists)) - serio_interrupt(port->serio, data, dfl); + spin_unlock_irqrestore(&i8042_lock, flags); + + if (likely(port->exists && !filtered)) + serio_interrupt(serio, data, dfl); out: return IRQ_RETVAL(ret); } /* - * 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) @@ -385,7 +550,9 @@ static int i8042_enable_kbd_port(void) i8042_ctr |= I8042_CTR_KBDINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n"); + i8042_ctr &= ~I8042_CTR_KBDINT; + i8042_ctr |= I8042_CTR_KBDDIS; + pr_err("Failed to enable KBD port\n"); return -EIO; } @@ -402,7 +569,9 @@ static int i8042_enable_aux_port(void) i8042_ctr |= I8042_CTR_AUXINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n"); + i8042_ctr &= ~I8042_CTR_AUXINT; + i8042_ctr |= I8042_CTR_AUXDIS; + pr_err("Failed to enable AUX port\n"); return -EIO; } @@ -428,14 +597,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. */ @@ -447,14 +617,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) @@ -469,21 +646,14 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version) * LCS/Telegraphics. */ -static int __devinit i8042_check_mux(void) +static int __init i8042_check_mux(void) { unsigned char mux_version; - if (i8042_set_mux_mode(1, &mux_version)) + if (i8042_set_mux_mode(true, &mux_version)) return -1; -/* - * Workaround for interference with USB Legacy emulation - * that causes a v10.12 MUX to be found. - */ - if (mux_version == 0xAC) - return -1; - - printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", + pr_info("Detected active multiplexing controller, rev %d.%d\n", (mux_version >> 4) & 0xf, mux_version & 0xf); /* @@ -493,11 +663,11 @@ static int __devinit i8042_check_mux(void) i8042_ctr &= ~I8042_CTR_AUXINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n"); + pr_err("Failed to disable AUX port, can't use MUX\n"); return -EIO; } - i8042_mux_present = 1; + i8042_mux_present = true; return 0; } @@ -505,25 +675,29 @@ 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 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; + int ret = 0; spin_lock_irqsave(&i8042_lock, flags); str = i8042_read_status(); if (str & I8042_STR_OBF) { data = i8042_read_data(); + dbg("%02x <- i8042 (aux_test_irq, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); if (i8042_irq_being_tested && data == 0xa5 && (str & I8042_STR_AUXDATA)) complete(&i8042_aux_irq_delivered); + ret = 1; } spin_unlock_irqrestore(&i8042_lock, flags); - return IRQ_HANDLED; + return IRQ_RETVAL(ret); } /* @@ -531,7 +705,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 __init i8042_toggle_aux(bool on) { unsigned char param; int i; @@ -559,11 +733,11 @@ static int __devinit i8042_toggle_aux(int on) * the presence of an AUX interface. */ -static int __devinit i8042_check_aux(void) +static int __init 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; @@ -600,19 +774,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)) { - 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(false)) { + pr_warn("Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); + pr_warn("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; /* @@ -620,7 +794,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. @@ -633,7 +807,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; @@ -641,7 +815,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); @@ -657,6 +831,7 @@ static int __devinit i8042_check_aux(void) * AUX IRQ was never delivered so we need to flush the controller to * get rid of the byte we put there; otherwise keyboard may not work. */ + dbg(" -- i8042 (aux irq test timeout)\n"); i8042_flush(); retval = -1; } @@ -681,8 +856,8 @@ static int __devinit i8042_check_aux(void) static int i8042_controller_check(void) { - if (i8042_flush() == I8042_BUFFER_SIZE) { - printk(KERN_ERR "i8042.c: No controller found.\n"); + if (i8042_flush()) { + pr_err("No controller found\n"); return -ENODEV; } @@ -692,22 +867,40 @@ static int i8042_controller_check(void) static int i8042_controller_selftest(void) { unsigned char param; + int i = 0; - if (!i8042_reset) - return 0; + /* + * We try this 5 times; on some really fragile systems this does not + * take the first time... + */ + do { - if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { - printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n"); - return -ENODEV; - } + if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { + pr_err("i8042 controller selftest timeout\n"); + return -ENODEV; + } - if (param != I8042_RET_CTL_TEST) { - printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n", - param, I8042_RET_CTL_TEST); - return -EIO; - } + if (param == I8042_RET_CTL_TEST) + return 0; + + dbg("i8042 controller selftest: %#x != %#x\n", + param, I8042_RET_CTL_TEST); + msleep(50); + } while (i++ < 5); +#ifdef CONFIG_X86 + /* + * On x86, we don't fail entire i8042 initialization if controller + * reset fails in hopes that keyboard port will still be functional + * and user will still get a working keyboard. This is especially + * important on netbooks. On other arches we trust hardware more. + */ + pr_info("giving up on controller selftest, continuing anyway...\n"); return 0; +#else + pr_err("i8042 controller selftest failed\n"); + return -EIO; +#endif } /* @@ -719,17 +912,30 @@ static int i8042_controller_selftest(void) static int i8042_controller_init(void) { unsigned long flags; + int n = 0; + unsigned char ctr[2]; /* - * Save the CTR for restoral on unload / reboot. + * Save the CTR for restore on unload / reboot. */ - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { - printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n"); - return -EIO; - } + do { + if (n >= 10) { + pr_err("Unable to get stable CTR read\n"); + return -EIO; + } + + if (n != 0) + udelay(50); - i8042_initial_ctr = i8042_ctr; + if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) { + pr_err("Can't read CTR while initializing i8042\n"); + return -EIO; + } + + } while (n < 2 || ctr[0] != ctr[1]); + + i8042_initial_ctr = i8042_ctr = ctr[0]; /* * Disable the keyboard interface and interrupt. @@ -747,7 +953,7 @@ static int i8042_controller_init(void) if (i8042_unlock) i8042_ctr |= I8042_CTR_IGNKEYLOCK; else - printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n"); + pr_warn("Warning: Keylock active\n"); } spin_unlock_irqrestore(&i8042_lock, flags); @@ -757,7 +963,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. @@ -774,10 +980,16 @@ static int i8042_controller_init(void) */ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n"); + pr_err("Can't write CTR while initializing i8042\n"); return -EIO; } +/* + * Flush whatever accumulated while we were disabling keyboard port. + */ + + i8042_flush(); + return 0; } @@ -786,7 +998,7 @@ static int i8042_controller_init(void) * Reset the controller and reset CRT to the original value set by BIOS. */ -static void i8042_controller_reset(void) +static void i8042_controller_reset(bool force_reset) { i8042_flush(); @@ -797,32 +1009,36 @@ 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_ctr, I8042_CMD_CTL_WCTR)) + pr_warn("Can't write CTR while resetting\n"); + /* * Disable MUX mode if present. */ if (i8042_mux_present) - i8042_set_mux_mode(0, NULL); + i8042_set_mux_mode(false, NULL); /* * Reset the controller if requested. */ - i8042_controller_selftest(); + if (i8042_reset || force_reset) + i8042_controller_selftest(); /* * Restore the original control register setting. */ if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR)) - printk(KERN_WARNING "i8042.c: Can't restore CTR.\n"); + pr_warn("Can't restore CTR\n"); } /* - * i8042_panic_blink() will flash the keyboard LEDs and is called when - * kernel panics. Flashing LEDs is useful for users running X who may - * not see the console and will help distingushing panics from "real" + * i8042_panic_blink() will turn the keyboard LEDs on or off and is called + * when kernel panics. Flashing LEDs is useful for users running X who may + * not see the console and will help distinguishing panics from "real" * lockups. * * Note that DELAY has a limit of 10ms so we will not get stuck here @@ -831,82 +1047,61 @@ static void i8042_controller_reset(void) #define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0) -static long i8042_panic_blink(long count) +static long i8042_panic_blink(int state) { long delay = 0; - static long last_blink; - static char led; - - /* - * We expect frequency to be about 1/2s. KDB uses about 1s. - * Make sure they are different. - */ - if (!i8042_blink_frequency) - return 0; - if (count - last_blink < i8042_blink_frequency) - return 0; + char led; - led ^= 0x01 | 0x04; + led = (state) ? 0x01 | 0x04 : 0; while (i8042_read_status() & I8042_STR_IBF) DELAY; - dbg("%02x -> i8042 (panic blink)", 0xed); + dbg("%02x -> i8042 (panic blink)\n", 0xed); i8042_suppress_kbd_ack = 2; i8042_write_data(0xed); /* set leds */ DELAY; while (i8042_read_status() & I8042_STR_IBF) DELAY; DELAY; - dbg("%02x -> i8042 (panic blink)", led); + dbg("%02x -> i8042 (panic blink)\n", led); i8042_write_data(led); DELAY; - last_blink = count; return delay; } #undef DELAY -#ifdef CONFIG_PM -/* - * 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). - */ - -static int i8042_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_X86 +static void i8042_dritek_enable(void) { - if (dev->dev.power.power_state.event != state.event) { - if (state.event == PM_EVENT_SUSPEND) - i8042_controller_reset(); - - dev->dev.power.power_state = state; - } + unsigned char param = 0x90; + int error; - return 0; + error = i8042_command(¶m, 0x1059); + if (error) + pr_warn("Failed to enable DRITEK extension: %d\n", error); } +#endif +#ifdef CONFIG_PM /* - * 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_controller_resume(bool force_reset) { int error; -/* - * Do not bother with restoring state if we haven't suspened yet - */ - if (dev->dev.power.power_state.event == PM_EVENT_ON) - return 0; - error = i8042_controller_check(); if (error) return error; - error = i8042_controller_selftest(); - if (error) - return error; + if (i8042_reset || force_reset) { + error = i8042_controller_selftest(); + if (error) + return error; + } /* * Restore original CTR value and disable all ports @@ -918,15 +1113,23 @@ static int i8042_resume(struct platform_device *dev) i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS; i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT); if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042: Can't write CTR to resume\n"); - return -EIO; + pr_warn("Can't write CTR to resume, retrying...\n"); + msleep(50); + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + pr_err("CTR write retry failed\n"); + return -EIO; + } } + +#ifdef CONFIG_X86 + if (i8042_dritek) + i8042_dritek_enable(); +#endif + if (i8042_mux_present) { - if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports()) - printk(KERN_WARNING - "i8042: failed to resume active multiplexor, " - "mouse won't work.\n"); + if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports()) + pr_warn("failed to resume active multiplexor, mouse won't work\n"); } else if (i8042_ports[I8042_AUX_PORT_NO].serio) i8042_enable_aux_port(); @@ -935,10 +1138,58 @@ static int i8042_resume(struct platform_device *dev) i8042_interrupt(0, NULL); - dev->dev.power.power_state = PMSG_ON; + return 0; +} + +/* + * Here we try to restore the original BIOS settings to avoid + * upsetting it. + */ + +static int i8042_pm_suspend(struct device *dev) +{ + i8042_controller_reset(true); + + return 0; +} + +static int i8042_pm_resume(struct device *dev) +{ + /* + * On resume from S2R we always try to reset the controller + * to bring it in a sane state. (In case of S2D we expect + * BIOS to reset the controller for us.) + */ + return i8042_controller_resume(true); +} + +static int i8042_pm_thaw(struct device *dev) +{ + i8042_interrupt(0, NULL); + + return 0; +} + +static int i8042_pm_reset(struct device *dev) +{ + i8042_controller_reset(false); return 0; } + +static int i8042_pm_restore(struct device *dev) +{ + return i8042_controller_resume(false); +} + +static const struct dev_pm_ops i8042_pm_ops = { + .suspend = i8042_pm_suspend, + .resume = i8042_pm_resume, + .thaw = i8042_pm_thaw, + .poweroff = i8042_pm_reset, + .restore = i8042_pm_restore, +}; + #endif /* CONFIG_PM */ /* @@ -948,10 +1199,10 @@ static int i8042_resume(struct platform_device *dev) static void i8042_shutdown(struct platform_device *dev) { - i8042_controller_reset(); + i8042_controller_reset(false); } -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]; @@ -964,10 +1215,13 @@ 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)); strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); + strlcpy(serio->firmware_id, i8042_kbd_firmware_id, + sizeof(serio->firmware_id)); port->serio = serio; port->irq = I8042_KBD_IRQ; @@ -975,7 +1229,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; @@ -994,6 +1248,9 @@ 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)); + strlcpy(serio->firmware_id, i8042_aux_firmware_id, + sizeof(serio->firmware_id)); + 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); @@ -1006,13 +1263,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; @@ -1022,7 +1279,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; @@ -1038,7 +1295,7 @@ static void __devinit i8042_register_ports(void) } } -static void __devinit i8042_unregister_ports(void) +static void i8042_unregister_ports(void) { int i; @@ -1050,6 +1307,21 @@ static void __devinit i8042_unregister_ports(void) } } +/* + * Checks whether port belongs to i8042 controller. + */ +bool i8042_check_port_owner(const struct serio *port) +{ + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) + if (i8042_ports[i].serio == port) + return true; + + return false; +} +EXPORT_SYMBOL(i8042_check_port_owner); + static void i8042_free_irqs(void) { if (i8042_aux_irq_registered) @@ -1057,10 +1329,10 @@ 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) +static int __init i8042_setup_aux(void) { int (*aux_enable)(void); int error; @@ -1091,7 +1363,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: @@ -1101,7 +1373,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; @@ -1118,7 +1390,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: @@ -1128,18 +1400,27 @@ 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; - error = i8042_controller_selftest(); - if (error) - return error; + i8042_platform_device = dev; + + if (i8042_reset) { + error = i8042_controller_selftest(); + if (error) + return error; + } error = i8042_controller_init(); if (error) return error; +#ifdef CONFIG_X86 + if (i8042_dritek) + i8042_dritek_enable(); +#endif + if (!i8042_noaux) { error = i8042_setup_aux(); if (error && error != -ENODEV && error != -EBUSY) @@ -1151,7 +1432,6 @@ static int __devinit i8042_probe(struct platform_device *dev) if (error) goto out_fail; } - /* * Ok, everything is ready, let's register all serio ports */ @@ -1162,16 +1442,18 @@ static int __devinit i8042_probe(struct platform_device *dev) out_fail: i8042_free_aux_ports(); /* in case KBD failed but AUX not */ i8042_free_irqs(); - i8042_controller_reset(); + i8042_controller_reset(false); + i8042_platform_device = NULL; return error; } -static int __devexit i8042_remove(struct platform_device *dev) +static int i8042_remove(struct platform_device *dev) { i8042_unregister_ports(); i8042_free_irqs(); - i8042_controller_reset(); + i8042_controller_reset(false); + i8042_platform_device = NULL; return 0; } @@ -1180,18 +1462,17 @@ static struct platform_driver i8042_driver = { .driver = { .name = "i8042", .owner = THIS_MODULE, - }, - .probe = i8042_probe, - .remove = __devexit_p(i8042_remove), - .shutdown = i8042_shutdown, #ifdef CONFIG_PM - .suspend = i8042_suspend, - .resume = i8042_resume, + .pm = &i8042_pm_ops, #endif + }, + .remove = i8042_remove, + .shutdown = i8042_shutdown, }; static int __init i8042_init(void) { + struct platform_device *pdev; int err; dbg_init(); @@ -1204,31 +1485,18 @@ static int __init i8042_init(void) if (err) goto err_platform_exit; - err = platform_driver_register(&i8042_driver); - if (err) + pdev = platform_create_bundle(&i8042_driver, i8042_probe, NULL, 0, NULL, 0); + if (IS_ERR(pdev)) { + err = PTR_ERR(pdev); goto err_platform_exit; - - i8042_platform_device = platform_device_alloc("i8042", -1); - if (!i8042_platform_device) { - err = -ENOMEM; - goto err_unregister_driver; } - err = platform_device_add(i8042_platform_device); - if (err) - goto err_free_device; - panic_blink = i8042_panic_blink; return 0; - err_free_device: - platform_device_put(i8042_platform_device); - err_unregister_driver: - platform_driver_unregister(&i8042_driver); err_platform_exit: i8042_platform_exit(); - return err; } diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h index b3eb7a72d96..fc080beffed 100644 --- a/drivers/input/serio/i8042.h +++ b/drivers/input/serio/i8042.h @@ -16,14 +16,18 @@ #if defined(CONFIG_MACH_JAZZ) #include "i8042-jazzio.h" -#elif defined(CONFIG_SGI_IP22) +#elif defined(CONFIG_SGI_HAS_I8042) #include "i8042-ip22io.h" +#elif defined(CONFIG_SNI_RM) +#include "i8042-snirm.h" #elif defined(CONFIG_PPC) #include "i8042-ppcio.h" #elif defined(CONFIG_SPARC) #include "i8042-sparcio.h" #elif defined(CONFIG_X86) || defined(CONFIG_IA64) #include "i8042-x86ia64io.h" +#elif defined(CONFIG_UNICORE32) +#include "i8042-unicore32io.h" #else #include "i8042-io.h" #endif @@ -37,52 +41,6 @@ #define I8042_CTL_TIMEOUT 10000 /* - * Status register bits. - */ - -#define I8042_STR_PARITY 0x80 -#define I8042_STR_TIMEOUT 0x40 -#define I8042_STR_AUXDATA 0x20 -#define I8042_STR_KEYLOCK 0x10 -#define I8042_STR_CMDDAT 0x08 -#define I8042_STR_MUXERR 0x04 -#define I8042_STR_IBF 0x02 -#define I8042_STR_OBF 0x01 - -/* - * Control register bits. - */ - -#define I8042_CTR_KBDINT 0x01 -#define I8042_CTR_AUXINT 0x02 -#define I8042_CTR_IGNKEYLOCK 0x08 -#define I8042_CTR_KBDDIS 0x10 -#define I8042_CTR_AUXDIS 0x20 -#define I8042_CTR_XLATE 0x40 - -/* - * Commands. - */ - -#define I8042_CMD_CTL_RCTR 0x0120 -#define I8042_CMD_CTL_WCTR 0x1060 -#define I8042_CMD_CTL_TEST 0x01aa - -#define I8042_CMD_KBD_DISABLE 0x00ad -#define I8042_CMD_KBD_ENABLE 0x00ae -#define I8042_CMD_KBD_TEST 0x01ab -#define I8042_CMD_KBD_LOOP 0x11d2 - -#define I8042_CMD_AUX_DISABLE 0x00a7 -#define I8042_CMD_AUX_ENABLE 0x00a8 -#define I8042_CMD_AUX_TEST 0x01a9 -#define I8042_CMD_AUX_SEND 0x10d4 -#define I8042_CMD_AUX_LOOP 0x11d3 - -#define I8042_CMD_MUX_PFX 0x0090 -#define I8042_CMD_MUX_SEND 0x1090 - -/* * Return codes. */ @@ -109,15 +67,19 @@ #ifdef DEBUG static unsigned long i8042_start_time; #define dbg_init() do { i8042_start_time = jiffies; } while (0) -#define dbg(format, arg...) \ - do { \ +#define dbg(format, arg...) \ + do { \ if (i8042_debug) \ - printk(KERN_DEBUG __FILE__ ": " format " [%d]\n" , \ - ## arg, (int) (jiffies - i8042_start_time)); \ + printk(KERN_DEBUG KBUILD_MODNAME ": [%d] " format, \ + (int) (jiffies - i8042_start_time), ##arg); \ } while (0) #else #define dbg_init() do { } while (0) -#define dbg(format, arg...) do {} while (0) +#define dbg(format, arg...) \ + do { \ + if (0) \ + printk(KERN_DEBUG pr_fmt(format), ##arg); \ + } while (0) #endif #endif /* _I8042_H */ diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 10d9d74ae43..75516996db2 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -13,12 +13,11 @@ #include <linux/delay.h> #include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/slab.h> +#include <linux/sched.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/serio.h> -#include <linux/init.h> +#include <linux/i8042.h> #include <linux/libps2.h> #define DRIVER_DESC "PS/2 driver library" @@ -27,15 +26,6 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); MODULE_DESCRIPTION("PS/2 driver library"); MODULE_LICENSE("GPL"); -/* Work structure to schedule execution of a command */ -struct ps2work { - struct work_struct work; - struct ps2dev *ps2dev; - int command; - unsigned char param[0]; -}; - - /* * ps2_sendbyte() sends a byte to the device and waits for acknowledge. * It doesn't handle retransmission, though it could - because if there @@ -64,6 +54,24 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) } EXPORT_SYMBOL(ps2_sendbyte); +void ps2_begin_command(struct ps2dev *ps2dev) +{ + mutex_lock(&ps2dev->cmd_mutex); + + if (i8042_check_port_owner(ps2dev->serio)) + i8042_lock_chip(); +} +EXPORT_SYMBOL(ps2_begin_command); + +void ps2_end_command(struct ps2dev *ps2dev) +{ + if (i8042_check_port_owner(ps2dev->serio)) + i8042_unlock_chip(); + + mutex_unlock(&ps2dev->cmd_mutex); +} +EXPORT_SYMBOL(ps2_end_command); + /* * ps2_drain() waits for device to transmit requested number of bytes * and discards them. @@ -76,7 +84,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) maxbytes = sizeof(ps2dev->cmdbuf); } - mutex_lock(&ps2dev->cmd_mutex); + ps2_begin_command(ps2dev); serio_pause_rx(ps2dev->serio); ps2dev->flags = PS2_FLAG_CMD; @@ -86,7 +94,8 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD), msecs_to_jiffies(timeout)); - mutex_unlock(&ps2dev->cmd_mutex); + + ps2_end_command(ps2dev); } EXPORT_SYMBOL(ps2_drain); @@ -171,7 +180,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; @@ -189,8 +198,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; @@ -202,7 +209,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) /* * Some devices (Synaptics) peform the reset before * ACKing the reset command, and so it can take a long - * time before the ACK arrrives. + * time before the ACK arrives. */ if (ps2_sendbyte(ps2dev, command & 0xff, command == PS2_CMD_RESET_BAT ? 1000 : 200)) @@ -220,7 +227,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) timeout = wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD1), timeout); - if (ps2dev->cmdcnt && timeout > 0) { + if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) { timeout = ps2_adjust_timeout(ps2dev, command, timeout); wait_event_timeout(ps2dev->wait, @@ -241,53 +248,21 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) ps2dev->flags = 0; serio_continue_rx(ps2dev->serio); - mutex_unlock(&ps2dev->cmd_mutex); return rc; } -EXPORT_SYMBOL(ps2_command); - -/* - * ps2_execute_scheduled_command() sends a command, previously scheduled by - * ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.) - */ - -static void ps2_execute_scheduled_command(struct work_struct *work) -{ - struct ps2work *ps2work = container_of(work, struct ps2work, work); - - ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command); - kfree(ps2work); -} - -/* - * ps2_schedule_command() allows to schedule delayed execution of a PS/2 - * command and can be used to issue a command from an interrupt or softirq - * context. - */ +EXPORT_SYMBOL(__ps2_command); -int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command) +int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) { - struct ps2work *ps2work; - int send = (command >> 12) & 0xf; - int receive = (command >> 8) & 0xf; - - if (!(ps2work = kmalloc(sizeof(struct ps2work) + max(send, receive), GFP_ATOMIC))) - return -1; + int rc; - memset(ps2work, 0, sizeof(struct ps2work)); - ps2work->ps2dev = ps2dev; - ps2work->command = command; - memcpy(ps2work->param, param, send); - INIT_WORK(&ps2work->work, ps2_execute_scheduled_command); + ps2_begin_command(ps2dev); + rc = __ps2_command(ps2dev, param, command); + ps2_end_command(ps2dev); - if (!schedule_work(&ps2work->work)) { - kfree(ps2work); - return -1; - } - - return 0; + return rc; } -EXPORT_SYMBOL(ps2_schedule_command); +EXPORT_SYMBOL(ps2_command); /* * ps2_init() initializes ps2dev structure @@ -315,9 +290,17 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) break; case PS2_RET_NAK: - ps2dev->nak = 1; + ps2dev->flags |= PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_NAK; break; + case PS2_RET_ERR: + if (ps2dev->flags & PS2_FLAG_NAK) { + ps2dev->flags &= ~PS2_FLAG_NAK; + ps2dev->nak = PS2_RET_ERR; + break; + } + /* * Workaround for mice which don't ACK the Get ID command. * These are valid mouse IDs that we recognize. @@ -335,8 +318,11 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) } - if (!ps2dev->nak && ps2dev->cmdcnt) - ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; + if (!ps2dev->nak) { + ps2dev->flags &= ~PS2_FLAG_NAK; + if (ps2dev->cmdcnt) + ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; + } ps2dev->flags &= ~PS2_FLAG_ACK; wake_up(&ps2dev->wait); @@ -382,6 +368,7 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev) if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD)) wake_up(&ps2dev->wait); - ps2dev->flags = 0; + /* reset all flags except last nack */ + ps2dev->flags &= PS2_FLAG_NAK; } EXPORT_SYMBOL(ps2_cmd_aborted); diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c index 558200e96d0..bc85e1cc66d 100644 --- a/drivers/input/serio/maceps2.c +++ b/drivers/input/serio/maceps2.c @@ -21,7 +21,6 @@ #include <asm/io.h> #include <asm/irq.h> -#include <asm/system.h> #include <asm/ip32/mace.h> #include <asm/ip32/ip32_ints.h> @@ -117,7 +116,7 @@ static void maceps2_close(struct serio *dev) } -static struct serio * __devinit maceps2_allocate_port(int idx) +static struct serio *maceps2_allocate_port(int idx) { struct serio *serio; @@ -136,7 +135,7 @@ static struct serio * __devinit maceps2_allocate_port(int idx) return serio; } -static int __devinit maceps2_probe(struct platform_device *dev) +static int maceps2_probe(struct platform_device *dev) { maceps2_port[0] = maceps2_allocate_port(0); maceps2_port[1] = maceps2_allocate_port(1); @@ -152,7 +151,7 @@ static int __devinit maceps2_probe(struct platform_device *dev) return 0; } -static int __devexit maceps2_remove(struct platform_device *dev) +static int maceps2_remove(struct platform_device *dev) { serio_unregister_port(maceps2_port[0]); serio_unregister_port(maceps2_port[1]); @@ -166,7 +165,7 @@ static struct platform_driver maceps2_driver = { .owner = THIS_MODULE, }, .probe = maceps2_probe, - .remove = __devexit_p(maceps2_remove), + .remove = maceps2_remove, }; static int __init maceps2_init(void) diff --git a/drivers/input/serio/olpc_apsp.c b/drivers/input/serio/olpc_apsp.c new file mode 100644 index 00000000000..d906f3ebc8c --- /dev/null +++ b/drivers/input/serio/olpc_apsp.c @@ -0,0 +1,283 @@ +/* + * 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/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); + 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 const 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/parkbd.c b/drivers/input/serio/parkbd.c index 688610e86a3..26b45936f9f 100644 --- a/drivers/input/serio/parkbd.c +++ b/drivers/input/serio/parkbd.c @@ -46,6 +46,7 @@ #include <linux/module.h> #include <linux/parport.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/serio.h> @@ -102,7 +103,7 @@ static int parkbd_write(struct serio *port, unsigned char c) return 0; } -static void parkbd_interrupt(int irq, void *dev_id) +static void parkbd_interrupt(void *dev_id) { if (parkbd_writing) { diff --git a/drivers/input/serio/pcips2.c b/drivers/input/serio/pcips2.c index ea5e3c6ddb6..e862c6ea9d9 100644 --- a/drivers/input/serio/pcips2.c +++ b/drivers/input/serio/pcips2.c @@ -15,7 +15,7 @@ #include <linux/ioport.h> #include <linux/input.h> #include <linux/pci.h> -#include <linux/init.h> +#include <linux/slab.h> #include <linux/serio.h> #include <linux/delay.h> #include <asm/io.h> @@ -126,7 +126,7 @@ static void pcips2_close(struct serio *io) free_irq(ps2if->dev->irq, ps2if); } -static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id) +static int pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct pcips2_data *ps2if; struct serio *serio; @@ -140,22 +140,20 @@ static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_i if (ret) goto disable; - ps2if = kmalloc(sizeof(struct pcips2_data), GFP_KERNEL); - serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + ps2if = kzalloc(sizeof(struct pcips2_data), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!ps2if || !serio) { ret = -ENOMEM; goto release; } - memset(ps2if, 0, sizeof(struct pcips2_data)); - memset(serio, 0, sizeof(struct serio)); serio->id.type = SERIO_8042; serio->write = pcips2_write; serio->open = pcips2_open; serio->close = pcips2_close; strlcpy(serio->name, pci_name(dev), sizeof(serio->name)); - strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys)); + strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys)); serio->port_data = ps2if; serio->dev.parent = &dev->dev; ps2if->io = serio; @@ -177,18 +175,17 @@ static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_i return ret; } -static void __devexit pcips2_remove(struct pci_dev *dev) +static void pcips2_remove(struct pci_dev *dev) { struct pcips2_data *ps2if = pci_get_drvdata(dev); serio_unregister_port(ps2if->io); - pci_set_drvdata(dev, NULL); kfree(ps2if); pci_release_regions(dev); pci_disable_device(dev); } -static struct pci_device_id pcips2_ids[] = { +static const struct pci_device_id pcips2_ids[] = { { .vendor = 0x14f2, /* MOBILITY */ .device = 0x0123, /* Keyboard */ @@ -207,28 +204,17 @@ static struct pci_device_id pcips2_ids[] = { }, { 0, } }; +MODULE_DEVICE_TABLE(pci, pcips2_ids); static struct pci_driver pcips2_driver = { .name = "pcips2", .id_table = pcips2_ids, .probe = pcips2_probe, - .remove = __devexit_p(pcips2_remove), + .remove = pcips2_remove, }; -static int __init pcips2_init(void) -{ - return pci_register_driver(&pcips2_driver); -} - -static void __exit pcips2_exit(void) -{ - pci_unregister_driver(&pcips2_driver); -} - -module_init(pcips2_init); -module_exit(pcips2_exit); +module_pci_driver(pcips2_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver"); -MODULE_DEVICE_TABLE(pci, pcips2_ids); diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c new file mode 100644 index 00000000000..a76fb64f03d --- /dev/null +++ b/drivers/input/serio/ps2mult.c @@ -0,0 +1,307 @@ +/* + * TQC PS/2 Multiplexer driver + * + * Copyright (C) 2010 Dmitry Eremin-Solenikov + * + * 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/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/serio.h> + +MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>"); +MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver"); +MODULE_LICENSE("GPL"); + +#define PS2MULT_KB_SELECTOR 0xA0 +#define PS2MULT_MS_SELECTOR 0xA1 +#define PS2MULT_ESCAPE 0x7D +#define PS2MULT_BSYNC 0x7E +#define PS2MULT_SESSION_START 0x55 +#define PS2MULT_SESSION_END 0x56 + +struct ps2mult_port { + struct serio *serio; + unsigned char sel; + bool registered; +}; + +#define PS2MULT_NUM_PORTS 2 +#define PS2MULT_KBD_PORT 0 +#define PS2MULT_MOUSE_PORT 1 + +struct ps2mult { + struct serio *mx_serio; + struct ps2mult_port ports[PS2MULT_NUM_PORTS]; + + spinlock_t lock; + struct ps2mult_port *in_port; + struct ps2mult_port *out_port; + bool escape; +}; + +/* First MUST come PS2MULT_NUM_PORTS selectors */ +static const unsigned char ps2mult_controls[] = { + PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR, + PS2MULT_ESCAPE, PS2MULT_BSYNC, + PS2MULT_SESSION_START, PS2MULT_SESSION_END, +}; + +static const struct serio_device_id ps2mult_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_PS2MULT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids); + +static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port) +{ + struct serio *mx_serio = psm->mx_serio; + + serio_write(mx_serio, port->sel); + psm->out_port = port; + dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel); +} + +static int ps2mult_serio_write(struct serio *serio, unsigned char data) +{ + struct serio *mx_port = serio->parent; + struct ps2mult *psm = serio_get_drvdata(mx_port); + struct ps2mult_port *port = serio->port_data; + bool need_escape; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + + if (psm->out_port != port) + ps2mult_select_port(psm, port); + + need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls)); + + dev_dbg(&serio->dev, + "write: %s%02x\n", need_escape ? "ESC " : "", data); + + if (need_escape) + serio_write(mx_port, PS2MULT_ESCAPE); + + serio_write(mx_port, data); + + spin_unlock_irqrestore(&psm->lock, flags); + + return 0; +} + +static int ps2mult_serio_start(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio->parent); + struct ps2mult_port *port = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + port->registered = true; + spin_unlock_irqrestore(&psm->lock, flags); + + return 0; +} + +static void ps2mult_serio_stop(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio->parent); + struct ps2mult_port *port = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + port->registered = false; + spin_unlock_irqrestore(&psm->lock, flags); +} + +static int ps2mult_create_port(struct ps2mult *psm, int i) +{ + struct serio *mx_serio = psm->mx_serio; + struct serio *serio; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), + "%s/port%d", mx_serio->phys, i); + serio->id.type = SERIO_8042; + serio->write = ps2mult_serio_write; + serio->start = ps2mult_serio_start; + serio->stop = ps2mult_serio_stop; + serio->parent = psm->mx_serio; + serio->port_data = &psm->ports[i]; + + psm->ports[i].serio = serio; + + return 0; +} + +static void ps2mult_reset(struct ps2mult *psm) +{ + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + + serio_write(psm->mx_serio, PS2MULT_SESSION_END); + serio_write(psm->mx_serio, PS2MULT_SESSION_START); + + ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]); + + spin_unlock_irqrestore(&psm->lock, flags); +} + +static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) +{ + struct ps2mult *psm; + int i; + int error; + + if (!serio->write) + return -EINVAL; + + psm = kzalloc(sizeof(*psm), GFP_KERNEL); + if (!psm) + return -ENOMEM; + + spin_lock_init(&psm->lock); + psm->mx_serio = serio; + + for (i = 0; i < PS2MULT_NUM_PORTS; i++) { + psm->ports[i].sel = ps2mult_controls[i]; + error = ps2mult_create_port(psm, i); + if (error) + goto err_out; + } + + psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT]; + + serio_set_drvdata(serio, psm); + error = serio_open(serio, drv); + if (error) + goto err_out; + + ps2mult_reset(psm); + + for (i = 0; i < PS2MULT_NUM_PORTS; i++) { + struct serio *s = psm->ports[i].serio; + + dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys); + serio_register_port(s); + } + + return 0; + +err_out: + while (--i >= 0) + kfree(psm->ports[i].serio); + kfree(psm); + return error; +} + +static void ps2mult_disconnect(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + + /* Note that serio core already take care of children ports */ + serio_write(serio, PS2MULT_SESSION_END); + serio_close(serio); + kfree(psm); + + serio_set_drvdata(serio, NULL); +} + +static int ps2mult_reconnect(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + + ps2mult_reset(psm); + + return 0; +} + +static irqreturn_t ps2mult_interrupt(struct serio *serio, + unsigned char data, unsigned int dfl) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + struct ps2mult_port *in_port; + unsigned long flags; + + dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl); + + spin_lock_irqsave(&psm->lock, flags); + + if (psm->escape) { + psm->escape = false; + in_port = psm->in_port; + if (in_port->registered) + serio_interrupt(in_port->serio, data, dfl); + goto out; + } + + switch (data) { + case PS2MULT_ESCAPE: + dev_dbg(&serio->dev, "ESCAPE\n"); + psm->escape = true; + break; + + case PS2MULT_BSYNC: + dev_dbg(&serio->dev, "BSYNC\n"); + psm->in_port = psm->out_port; + break; + + case PS2MULT_SESSION_START: + dev_dbg(&serio->dev, "SS\n"); + break; + + case PS2MULT_SESSION_END: + dev_dbg(&serio->dev, "SE\n"); + break; + + case PS2MULT_KB_SELECTOR: + dev_dbg(&serio->dev, "KB\n"); + psm->in_port = &psm->ports[PS2MULT_KBD_PORT]; + break; + + case PS2MULT_MS_SELECTOR: + dev_dbg(&serio->dev, "MS\n"); + psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT]; + break; + + default: + in_port = psm->in_port; + if (in_port->registered) + serio_interrupt(in_port->serio, data, dfl); + break; + } + + out: + spin_unlock_irqrestore(&psm->lock, flags); + return IRQ_HANDLED; +} + +static struct serio_driver ps2mult_drv = { + .driver = { + .name = "ps2mult", + }, + .description = "TQC PS/2 Multiplexer driver", + .id_table = ps2mult_serio_ids, + .interrupt = ps2mult_interrupt, + .connect = ps2mult_connect, + .disconnect = ps2mult_disconnect, + .reconnect = ps2mult_reconnect, +}; + +module_serio_driver(ps2mult_drv); diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index cb89aff2e16..594256c3855 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -1,6 +1,4 @@ /* - * $Id: q40kbd.c,v 1.12 2002/02/02 22:26:44 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: @@ -32,12 +30,12 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/serio.h> #include <linux/interrupt.h> #include <linux/err.h> #include <linux/bitops.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -45,26 +43,31 @@ #include <asm/irq.h> #include <asm/q40ints.h> +#define DRV_NAME "q40kbd" + MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -DEFINE_SPINLOCK(q40kbd_lock); -static struct serio *q40kbd_port; -static struct platform_device *q40kbd_device; +struct q40kbd { + struct serio *port; + spinlock_t lock; +}; static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) { + struct q40kbd *q40kbd = dev_id; unsigned long flags; - spin_lock_irqsave(&q40kbd_lock, flags); + spin_lock_irqsave(&q40kbd->lock, flags); if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) - serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0); + serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0); master_outb(-1, KEYBOARD_UNLOCK_REG); - spin_unlock_irqrestore(&q40kbd_lock, flags); + spin_unlock_irqrestore(&q40kbd->lock, flags); return IRQ_HANDLED; } @@ -73,17 +76,23 @@ static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) * q40kbd_flush() flushes all data that may be in the keyboard buffers */ -static void q40kbd_flush(void) +static void q40kbd_flush(struct q40kbd *q40kbd) { int maxread = 100; unsigned long flags; - spin_lock_irqsave(&q40kbd_lock, flags); + spin_lock_irqsave(&q40kbd->lock, flags); while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) master_inb(KEYCODE_REG); - spin_unlock_irqrestore(&q40kbd_lock, flags); + spin_unlock_irqrestore(&q40kbd->lock, flags); +} + +static void q40kbd_stop(void) +{ + master_outb(0, KEY_IRQ_ENABLE_REG); + master_outb(-1, KEYBOARD_UNLOCK_REG); } /* @@ -93,12 +102,9 @@ static void q40kbd_flush(void) static int q40kbd_open(struct serio *port) { - q40kbd_flush(); + struct q40kbd *q40kbd = port->port_data; - if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) { - printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD); - return -EBUSY; - } + q40kbd_flush(q40kbd); /* off we go */ master_outb(-1, KEYBOARD_UNLOCK_REG); @@ -109,35 +115,70 @@ static int q40kbd_open(struct serio *port) static void q40kbd_close(struct serio *port) { - master_outb(0, KEY_IRQ_ENABLE_REG); - master_outb(-1, KEYBOARD_UNLOCK_REG); - free_irq(Q40_IRQ_KEYBOARD, NULL); + struct q40kbd *q40kbd = port->port_data; - q40kbd_flush(); + q40kbd_stop(); + q40kbd_flush(q40kbd); } -static int __devinit q40kbd_probe(struct platform_device *dev) +static int q40kbd_probe(struct platform_device *pdev) { - q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL); - if (!q40kbd_port) - return -ENOMEM; - - q40kbd_port->id.type = SERIO_8042; - q40kbd_port->open = q40kbd_open; - q40kbd_port->close = q40kbd_close; - q40kbd_port->dev.parent = &dev->dev; - strlcpy(q40kbd_port->name, "Q40 Kbd Port", sizeof(q40kbd_port->name)); - strlcpy(q40kbd_port->phys, "Q40", sizeof(q40kbd_port->phys)); - - serio_register_port(q40kbd_port); + struct q40kbd *q40kbd; + struct serio *port; + int error; + + q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL); + port = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!q40kbd || !port) { + error = -ENOMEM; + goto err_free_mem; + } + + q40kbd->port = port; + spin_lock_init(&q40kbd->lock); + + port->id.type = SERIO_8042; + port->open = q40kbd_open; + port->close = q40kbd_close; + port->port_data = q40kbd; + port->dev.parent = &pdev->dev; + strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name)); + strlcpy(port->phys, "Q40", sizeof(port->phys)); + + q40kbd_stop(); + + error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, + DRV_NAME, q40kbd); + if (error) { + dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD); + goto err_free_mem; + } + + serio_register_port(q40kbd->port); + + platform_set_drvdata(pdev, q40kbd); printk(KERN_INFO "serio: Q40 kbd registered\n"); return 0; + +err_free_mem: + kfree(port); + kfree(q40kbd); + return error; } -static int __devexit q40kbd_remove(struct platform_device *dev) +static int q40kbd_remove(struct platform_device *pdev) { - serio_unregister_port(q40kbd_port); + struct q40kbd *q40kbd = platform_get_drvdata(pdev); + + /* + * q40kbd_close() will be called as part of unregistering + * and will ensure that IRQ is turned off, so it is safe + * to unregister port first and free IRQ later. + */ + serio_unregister_port(q40kbd->port); + free_irq(Q40_IRQ_KEYBOARD, q40kbd); + kfree(q40kbd); return 0; } @@ -147,43 +188,7 @@ static struct platform_driver q40kbd_driver = { .name = "q40kbd", .owner = THIS_MODULE, }, - .probe = q40kbd_probe, - .remove = __devexit_p(q40kbd_remove), + .remove = q40kbd_remove, }; -static int __init q40kbd_init(void) -{ - int error; - - if (!MACH_IS_Q40) - return -EIO; - - error = platform_driver_register(&q40kbd_driver); - if (error) - return error; - - q40kbd_device = platform_device_alloc("q40kbd", -1); - if (!q40kbd_device) - goto err_unregister_driver; - - error = platform_device_add(q40kbd_device); - if (error) - goto err_free_device; - - return 0; - - err_free_device: - platform_device_put(q40kbd_device); - err_unregister_driver: - platform_driver_unregister(&q40kbd_driver); - return error; -} - -static void __exit q40kbd_exit(void) -{ - platform_device_unregister(q40kbd_device); - platform_driver_unregister(&q40kbd_driver); -} - -module_init(q40kbd_init); -module_exit(q40kbd_exit); +module_platform_driver_probe(q40kbd_driver, q40kbd_probe); diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index 49f84315cb3..e462e7791bb 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -1,6 +1,4 @@ /* - * $Id: rpckbd.c,v 1.7 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2002 Russell King */ @@ -31,20 +29,24 @@ #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/slab.h> -#include <asm/irq.h> -#include <asm/hardware.h> -#include <asm/io.h> +#include <mach/hardware.h> #include <asm/hardware/iomd.h> -#include <asm/system.h> MODULE_AUTHOR("Vojtech Pavlik, Russell King"); MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kart"); + +struct rpckbd_data { + int tx_irq; + int rx_irq; +}; static int rpckbd_write(struct serio *port, unsigned char val) { @@ -78,19 +80,21 @@ static irqreturn_t rpckbd_tx(int irq, void *dev_id) static int rpckbd_open(struct serio *port) { + struct rpckbd_data *rpckbd = port->port_data; + /* Reset the keyboard state machine. */ iomd_writeb(0, IOMD_KCTRL); iomd_writeb(8, IOMD_KCTRL); iomd_readb(IOMD_KARTRX); - if (request_irq(IRQ_KEYBOARDRX, rpckbd_rx, 0, "rpckbd", port) != 0) { + if (request_irq(rpckbd->rx_irq, rpckbd_rx, 0, "rpckbd", port) != 0) { printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n"); return -EBUSY; } - if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", port) != 0) { + if (request_irq(rpckbd->tx_irq, rpckbd_tx, 0, "rpckbd", port) != 0) { printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n"); - free_irq(IRQ_KEYBOARDRX, NULL); + free_irq(rpckbd->rx_irq, port); return -EBUSY; } @@ -99,27 +103,47 @@ static int rpckbd_open(struct serio *port) static void rpckbd_close(struct serio *port) { - free_irq(IRQ_KEYBOARDRX, port); - free_irq(IRQ_KEYBOARDTX, port); + struct rpckbd_data *rpckbd = port->port_data; + + free_irq(rpckbd->rx_irq, port); + free_irq(rpckbd->tx_irq, port); } /* * Allocate and initialize serio structure for subsequent registration * with serio core. */ -static int __devinit rpckbd_probe(struct platform_device *dev) +static int rpckbd_probe(struct platform_device *dev) { + struct rpckbd_data *rpckbd; struct serio *serio; + int tx_irq, rx_irq; + + rx_irq = platform_get_irq(dev, 0); + if (rx_irq <= 0) + return rx_irq < 0 ? rx_irq : -ENXIO; + + tx_irq = platform_get_irq(dev, 1); + if (tx_irq <= 0) + return tx_irq < 0 ? tx_irq : -ENXIO; serio = kzalloc(sizeof(struct serio), GFP_KERNEL); - if (!serio) + rpckbd = kzalloc(sizeof(*rpckbd), GFP_KERNEL); + if (!serio || !rpckbd) { + kfree(rpckbd); + kfree(serio); return -ENOMEM; + } + + rpckbd->rx_irq = rx_irq; + rpckbd->tx_irq = tx_irq; serio->id.type = SERIO_8042; serio->write = rpckbd_write; serio->open = rpckbd_open; serio->close = rpckbd_close; serio->dev.parent = &dev->dev; + serio->port_data = rpckbd; strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name)); strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys)); @@ -128,30 +152,23 @@ static int __devinit rpckbd_probe(struct platform_device *dev) return 0; } -static int __devexit rpckbd_remove(struct platform_device *dev) +static int rpckbd_remove(struct platform_device *dev) { struct serio *serio = platform_get_drvdata(dev); + struct rpckbd_data *rpckbd = serio->port_data; + serio_unregister_port(serio); + kfree(rpckbd); + return 0; } static struct platform_driver rpckbd_driver = { .probe = rpckbd_probe, - .remove = __devexit_p(rpckbd_remove), + .remove = rpckbd_remove, .driver = { .name = "kart", + .owner = THIS_MODULE, }, }; - -static int __init rpckbd_init(void) -{ - return platform_driver_register(&rpckbd_driver); -} - -static void __exit rpckbd_exit(void) -{ - platform_driver_unregister(&rpckbd_driver); -} - -module_init(rpckbd_init); -module_exit(rpckbd_exit); +module_platform_driver(rpckbd_driver); diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c index 559508795af..b3e688911fd 100644 --- a/drivers/input/serio/sa1111ps2.c +++ b/drivers/input/serio/sa1111ps2.c @@ -20,10 +20,29 @@ #include <linux/spinlock.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/hardware/sa1111.h> +#define PS2CR 0x0000 +#define PS2STAT 0x0004 +#define PS2DATA 0x0008 +#define PS2CLKDIV 0x000c +#define PS2PRECNT 0x0010 + +#define PS2CR_ENA 0x08 +#define PS2CR_FKD 0x02 +#define PS2CR_FKC 0x01 + +#define PS2STAT_STP 0x0100 +#define PS2STAT_TXE 0x0080 +#define PS2STAT_TXB 0x0040 +#define PS2STAT_RXF 0x0020 +#define PS2STAT_RXB 0x0010 +#define PS2STAT_ENA 0x0008 +#define PS2STAT_RXP 0x0004 +#define PS2STAT_KBD 0x0002 +#define PS2STAT_KBC 0x0001 + struct ps2if { struct serio *io; struct sa1111_dev *dev; @@ -45,22 +64,22 @@ static irqreturn_t ps2_rxint(int irq, void *dev_id) struct ps2if *ps2if = dev_id; unsigned int scancode, flag, status; - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); while (status & PS2STAT_RXF) { if (status & PS2STAT_STP) - sa1111_writel(PS2STAT_STP, ps2if->base + SA1111_PS2STAT); + sa1111_writel(PS2STAT_STP, ps2if->base + PS2STAT); flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) | (status & PS2STAT_RXP ? 0 : SERIO_PARITY); - scancode = sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff; + scancode = sa1111_readl(ps2if->base + PS2DATA) & 0xff; if (hweight8(scancode) & 1) flag ^= SERIO_PARITY; serio_interrupt(ps2if->io, scancode, flag); - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); } return IRQ_HANDLED; @@ -75,12 +94,12 @@ static irqreturn_t ps2_txint(int irq, void *dev_id) unsigned int status; spin_lock(&ps2if->lock); - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); if (ps2if->head == ps2if->tail) { - disable_irq(irq); + disable_irq_nosync(irq); /* done */ } else if (status & PS2STAT_TXE) { - sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + SA1111_PS2DATA); + sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA); ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1); } spin_unlock(&ps2if->lock); @@ -103,8 +122,8 @@ static int ps2_write(struct serio *io, unsigned char val) /* * If the TX register is empty, we can go straight out. */ - if (sa1111_readl(ps2if->base + SA1111_PS2STAT) & PS2STAT_TXE) { - sa1111_writel(val, ps2if->base + SA1111_PS2DATA); + if (sa1111_readl(ps2if->base + PS2STAT) & PS2STAT_TXE) { + sa1111_writel(val, ps2if->base + PS2DATA); } else { if (ps2if->head == ps2if->tail) enable_irq(ps2if->dev->irq[1]); @@ -124,13 +143,16 @@ static int ps2_open(struct serio *io) struct ps2if *ps2if = io->port_data; int ret; - sa1111_enable_device(ps2if->dev); + ret = sa1111_enable_device(ps2if->dev); + if (ret) + return ret; ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0, SA1111_DRIVER_NAME(ps2if->dev), ps2if); if (ret) { printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", ps2if->dev->irq[0], ret); + sa1111_disable_device(ps2if->dev); return ret; } @@ -140,6 +162,7 @@ static int ps2_open(struct serio *io) printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", ps2if->dev->irq[1], ret); free_irq(ps2if->dev->irq[0], ps2if); + sa1111_disable_device(ps2if->dev); return ret; } @@ -147,7 +170,7 @@ static int ps2_open(struct serio *io) enable_irq_wake(ps2if->dev->irq[0]); - sa1111_writel(PS2CR_ENA, ps2if->base + SA1111_PS2CR); + sa1111_writel(PS2CR_ENA, ps2if->base + PS2CR); return 0; } @@ -155,7 +178,7 @@ static void ps2_close(struct serio *io) { struct ps2if *ps2if = io->port_data; - sa1111_writel(0, ps2if->base + SA1111_PS2CR); + sa1111_writel(0, ps2if->base + PS2CR); disable_irq_wake(ps2if->dev->irq[0]); @@ -170,26 +193,26 @@ static void ps2_close(struct serio *io) /* * Clear the input buffer. */ -static void __init ps2_clear_input(struct ps2if *ps2if) +static void ps2_clear_input(struct ps2if *ps2if) { int maxread = 100; while (maxread--) { - if ((sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff) == 0xff) + if ((sa1111_readl(ps2if->base + PS2DATA) & 0xff) == 0xff) break; } } -static inline unsigned int -ps2_test_one(struct ps2if *ps2if, unsigned int mask) +static unsigned int ps2_test_one(struct ps2if *ps2if, + unsigned int mask) { unsigned int val; - sa1111_writel(PS2CR_ENA | mask, ps2if->base + SA1111_PS2CR); + sa1111_writel(PS2CR_ENA | mask, ps2if->base + PS2CR); udelay(2); - val = sa1111_readl(ps2if->base + SA1111_PS2STAT); + val = sa1111_readl(ps2if->base + PS2STAT); return val & (PS2STAT_KBC | PS2STAT_KBD); } @@ -197,7 +220,7 @@ ps2_test_one(struct ps2if *ps2if, unsigned int mask) * Test the keyboard interface. We basically check to make sure that * we can drive each line to the keyboard independently of each other. */ -static int __init ps2_test(struct ps2if *ps2if) +static int ps2_test(struct ps2if *ps2if) { unsigned int stat; int ret = 0; @@ -220,7 +243,7 @@ static int __init ps2_test(struct ps2if *ps2if) ret = -ENODEV; } - sa1111_writel(0, ps2if->base + SA1111_PS2CR); + sa1111_writel(0, ps2if->base + PS2CR); return ret; } @@ -234,22 +257,20 @@ static int ps2_probe(struct sa1111_dev *dev) struct serio *serio; int ret; - ps2if = kmalloc(sizeof(struct ps2if), GFP_KERNEL); - serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!ps2if || !serio) { ret = -ENOMEM; goto free; } - memset(ps2if, 0, sizeof(struct ps2if)); - memset(serio, 0, sizeof(struct serio)); serio->id.type = SERIO_8042; serio->write = ps2_write; serio->open = ps2_open; serio->close = ps2_close; - strlcpy(serio->name, dev->dev.bus_id, sizeof(serio->name)); - strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys)); + strlcpy(serio->name, dev_name(&dev->dev), sizeof(serio->name)); + strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys)); serio->port_data = ps2if; serio->dev.parent = &dev->dev; ps2if->io = serio; @@ -276,8 +297,8 @@ static int ps2_probe(struct sa1111_dev *dev) sa1111_enable_device(ps2if->dev); /* Incoming clock is 8MHz */ - sa1111_writel(0, ps2if->base + SA1111_PS2CLKDIV); - sa1111_writel(127, ps2if->base + SA1111_PS2PRECNT); + sa1111_writel(0, ps2if->base + PS2CLKDIV); + sa1111_writel(127, ps2if->base + PS2PRECNT); /* * Flush any pending input. @@ -302,8 +323,7 @@ static int ps2_probe(struct sa1111_dev *dev) out: sa1111_disable_device(ps2if->dev); - release_mem_region(dev->res.start, - dev->res.end - dev->res.start + 1); + release_mem_region(dev->res.start, resource_size(&dev->res)); free: sa1111_set_drvdata(dev, NULL); kfree(ps2if); @@ -319,8 +339,7 @@ static int ps2_remove(struct sa1111_dev *dev) struct ps2if *ps2if = sa1111_get_drvdata(dev); serio_unregister_port(ps2if->io); - release_mem_region(dev->res.start, - dev->res.end - dev->res.start + 1); + release_mem_region(dev->res.start, resource_size(&dev->res)); sa1111_set_drvdata(dev, NULL); kfree(ps2if); @@ -334,6 +353,7 @@ static int ps2_remove(struct sa1111_dev *dev) static struct sa1111_driver ps2_driver = { .drv = { .name = "sa1111-ps2", + .owner = THIS_MODULE, }, .devid = SA1111_DEVID_PS2, .probe = ps2_probe, diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 5895202b972..b29134de983 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -26,35 +26,24 @@ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/stddef.h> #include <linux/module.h> #include <linux/serio.h> #include <linux/errno.h> -#include <linux/wait.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/kthread.h> +#include <linux/workqueue.h> #include <linux/mutex.h> -#include <linux/freezer.h> MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Serio abstraction core"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(serio_interrupt); -EXPORT_SYMBOL(__serio_register_port); -EXPORT_SYMBOL(serio_unregister_port); -EXPORT_SYMBOL(serio_unregister_child_port); -EXPORT_SYMBOL(__serio_register_driver); -EXPORT_SYMBOL(serio_unregister_driver); -EXPORT_SYMBOL(serio_open); -EXPORT_SYMBOL(serio_close); -EXPORT_SYMBOL(serio_rescan); -EXPORT_SYMBOL(serio_reconnect); - /* * serio_mutex protects entire serio subsystem and is taken every time - * serio port or driver registrered or unregistered. + * serio port or driver registered or unregistered. */ static DEFINE_MUTEX(serio_mutex); @@ -63,8 +52,9 @@ static LIST_HEAD(serio_list); static struct bus_type serio_bus; static void serio_add_port(struct serio *serio); -static void serio_reconnect_port(struct serio *serio); +static int serio_reconnect_port(struct serio *serio); static void serio_disconnect_port(struct serio *serio); +static void serio_reconnect_subtree(struct serio *serio); static void serio_attach_driver(struct serio_driver *drv); static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) @@ -129,11 +119,10 @@ static int serio_bind_driver(struct serio *serio, struct serio_driver *drv) error = device_bind_driver(&serio->dev); if (error) { - printk(KERN_WARNING - "serio: device_bind_driver() failed " - "for %s (%s) and %s, error: %d\n", - serio->phys, serio->name, - drv->description, error); + dev_warn(&serio->dev, + "device_bind_driver() failed for %s (%s) and %s, error: %d\n", + serio->phys, serio->name, + drv->description, error); serio_disconnect_driver(serio); serio->dev.driver = NULL; return error; @@ -148,9 +137,9 @@ static void serio_find_driver(struct serio *serio) error = device_attach(&serio->dev); if (error < 0) - printk(KERN_WARNING - "serio: device_attach() failed for %s (%s), error: %d\n", - serio->phys, serio->name, error); + dev_warn(&serio->dev, + "device_attach() failed for %s (%s), error: %d\n", + serio->phys, serio->name, error); } @@ -161,6 +150,7 @@ static void serio_find_driver(struct serio *serio) enum serio_event_type { SERIO_RESCAN_PORT, SERIO_RECONNECT_PORT, + SERIO_RECONNECT_SUBTREE, SERIO_REGISTER_PORT, SERIO_ATTACH_DRIVER, }; @@ -174,61 +164,22 @@ struct serio_event { static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */ static LIST_HEAD(serio_event_list); -static DECLARE_WAIT_QUEUE_HEAD(serio_wait); -static struct task_struct *serio_task; -static int serio_queue_event(void *object, struct module *owner, - enum serio_event_type event_type) +static struct serio_event *serio_get_event(void) { + struct serio_event *event = NULL; unsigned long flags; - struct serio_event *event; - int retval = 0; spin_lock_irqsave(&serio_event_lock, flags); - /* - * Scan event list for the other events for the same serio port, - * starting with the most recent one. If event is the same we - * do not need add new one. If event is of different type we - * need to add this event and should not look further because - * we need to preseve sequence of distinct events. - */ - list_for_each_entry_reverse(event, &serio_event_list, node) { - if (event->object == object) { - if (event->type == event_type) - goto out; - break; - } + if (!list_empty(&serio_event_list)) { + event = list_first_entry(&serio_event_list, + struct serio_event, node); + list_del_init(&event->node); } - event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC); - if (!event) { - printk(KERN_ERR - "serio: Not enough memory to queue event %d\n", - event_type); - retval = -ENOMEM; - goto out; - } - - if (!try_module_get(owner)) { - printk(KERN_WARNING - "serio: Can't get module reference, dropping event %d\n", - event_type); - kfree(event); - retval = -EINVAL; - goto out; - } - - event->type = event_type; - event->object = object; - event->owner = owner; - - list_add_tail(&event->node, &serio_event_list); - wake_up(&serio_wait); - -out: spin_unlock_irqrestore(&serio_event_lock, flags); - return retval; + return event; } static void serio_free_event(struct serio_event *event) @@ -237,26 +188,25 @@ static void serio_free_event(struct serio_event *event) kfree(event); } -static void serio_remove_duplicate_events(struct serio_event *event) +static void serio_remove_duplicate_events(void *object, + enum serio_event_type type) { - struct list_head *node, *next; - struct serio_event *e; + struct serio_event *e, *next; unsigned long flags; spin_lock_irqsave(&serio_event_lock, flags); - list_for_each_safe(node, next, &serio_event_list) { - e = list_entry(node, struct serio_event, node); - if (event->object == e->object) { + list_for_each_entry_safe(e, next, &serio_event_list, node) { + if (object == e->object) { /* * If this event is of different type we should not * look further - we only suppress duplicate events * that were sent back-to-back. */ - if (event->type != e->type) + if (type != e->type) break; - list_del_init(node); + list_del_init(&e->node); serio_free_event(e); } } @@ -264,87 +214,112 @@ static void serio_remove_duplicate_events(struct serio_event *event) spin_unlock_irqrestore(&serio_event_lock, flags); } - -static struct serio_event *serio_get_event(void) +static void serio_handle_event(struct work_struct *work) { struct serio_event *event; - struct list_head *node; - unsigned long flags; - spin_lock_irqsave(&serio_event_lock, flags); + mutex_lock(&serio_mutex); - if (list_empty(&serio_event_list)) { - spin_unlock_irqrestore(&serio_event_lock, flags); - return NULL; - } + while ((event = serio_get_event())) { - node = serio_event_list.next; - event = list_entry(node, struct serio_event, node); - list_del_init(node); + switch (event->type) { - spin_unlock_irqrestore(&serio_event_lock, flags); + case SERIO_REGISTER_PORT: + serio_add_port(event->object); + break; - return event; + case SERIO_RECONNECT_PORT: + serio_reconnect_port(event->object); + break; + + case SERIO_RESCAN_PORT: + serio_disconnect_port(event->object); + serio_find_driver(event->object); + break; + + case SERIO_RECONNECT_SUBTREE: + serio_reconnect_subtree(event->object); + break; + + case SERIO_ATTACH_DRIVER: + serio_attach_driver(event->object); + break; + } + + serio_remove_duplicate_events(event->object, event->type); + serio_free_event(event); + } + + mutex_unlock(&serio_mutex); } -static void serio_handle_event(void) +static DECLARE_WORK(serio_event_work, serio_handle_event); + +static int serio_queue_event(void *object, struct module *owner, + enum serio_event_type event_type) { + unsigned long flags; struct serio_event *event; + int retval = 0; - mutex_lock(&serio_mutex); + spin_lock_irqsave(&serio_event_lock, flags); /* - * Note that we handle only one event here to give swsusp - * a chance to freeze kseriod thread. Serio events should - * be pretty rare so we are not concerned about taking - * performance hit. + * Scan event list for the other events for the same serio port, + * starting with the most recent one. If event is the same we + * do not need add new one. If event is of different type we + * need to add this event and should not look further because + * we need to preseve sequence of distinct events. */ - if ((event = serio_get_event())) { - - switch (event->type) { - case SERIO_REGISTER_PORT: - serio_add_port(event->object); - break; - - case SERIO_RECONNECT_PORT: - serio_reconnect_port(event->object); - break; + list_for_each_entry_reverse(event, &serio_event_list, node) { + if (event->object == object) { + if (event->type == event_type) + goto out; + break; + } + } - case SERIO_RESCAN_PORT: - serio_disconnect_port(event->object); - serio_find_driver(event->object); - break; + event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC); + if (!event) { + pr_err("Not enough memory to queue event %d\n", event_type); + retval = -ENOMEM; + goto out; + } - case SERIO_ATTACH_DRIVER: - serio_attach_driver(event->object); - break; + if (!try_module_get(owner)) { + pr_warning("Can't get module reference, dropping event %d\n", + event_type); + kfree(event); + retval = -EINVAL; + goto out; + } - default: - break; - } + event->type = event_type; + event->object = object; + event->owner = owner; - serio_remove_duplicate_events(event); - serio_free_event(event); - } + list_add_tail(&event->node, &serio_event_list); + queue_work(system_long_wq, &serio_event_work); - mutex_unlock(&serio_mutex); +out: + spin_unlock_irqrestore(&serio_event_lock, flags); + return retval; } /* - * Remove all events that have been submitted for a given serio port. + * Remove all events that have been submitted for a given + * object, be it serio port or driver. */ -static void serio_remove_pending_events(struct serio *serio) +static void serio_remove_pending_events(void *object) { - struct list_head *node, *next; - struct serio_event *event; + struct serio_event *event, *next; unsigned long flags; spin_lock_irqsave(&serio_event_lock, flags); - list_for_each_safe(node, next, &serio_event_list) { - event = list_entry(node, struct serio_event, node); - if (event->object == serio) { - list_del_init(node); + list_for_each_entry_safe(event, next, &serio_event_list, node) { + if (event->object == object) { + list_del_init(&event->node); serio_free_event(event); } } @@ -353,12 +328,10 @@ static void serio_remove_pending_events(struct serio *serio) } /* - * Destroy child serio port (if any) that has not been fully registered yet. + * Locate child serio port (if any) that has not been fully registered yet. * - * Note that we rely on the fact that port can have only one child and therefore - * only one child registration request can be pending. Additionally, children - * are registered by driver's connect() handler so there can't be a grandchild - * pending registration together with a child. + * Children are registered by driver's connect() handler so there can't be a + * grandchild pending registration together with a child. */ static struct serio *serio_get_pending_child(struct serio *parent) { @@ -382,20 +355,6 @@ static struct serio *serio_get_pending_child(struct serio *parent) return child; } -static int serio_thread(void *nothing) -{ - do { - serio_handle_event(); - wait_event_interruptible(serio_wait, - kthread_should_stop() || !list_empty(&serio_event_list)); - try_to_freeze(); - } while (!kthread_should_stop()); - - printk(KERN_DEBUG "serio: kseriod exiting\n"); - return 0; -} - - /* * Serio port operations */ @@ -406,7 +365,7 @@ static ssize_t serio_show_description(struct device *dev, struct device_attribut return sprintf(buf, "%s\n", serio->name); } -static ssize_t serio_show_modalias(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { struct serio *serio = to_serio_port(dev); @@ -414,49 +373,31 @@ static ssize_t serio_show_modalias(struct device *dev, struct device_attribute * serio->id.type, serio->id.proto, serio->id.id, serio->id.extra); } -static ssize_t serio_show_id_type(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct serio *serio = to_serio_port(dev); return sprintf(buf, "%02x\n", serio->id.type); } -static ssize_t serio_show_id_proto(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t proto_show(struct device *dev, struct device_attribute *attr, char *buf) { struct serio *serio = to_serio_port(dev); return sprintf(buf, "%02x\n", serio->id.proto); } -static ssize_t serio_show_id_id(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct serio *serio = to_serio_port(dev); return sprintf(buf, "%02x\n", serio->id.id); } -static ssize_t serio_show_id_extra(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t extra_show(struct device *dev, struct device_attribute *attr, char *buf) { struct serio *serio = to_serio_port(dev); return sprintf(buf, "%02x\n", serio->id.extra); } -static DEVICE_ATTR(type, S_IRUGO, serio_show_id_type, NULL); -static DEVICE_ATTR(proto, S_IRUGO, serio_show_id_proto, NULL); -static DEVICE_ATTR(id, S_IRUGO, serio_show_id_id, NULL); -static DEVICE_ATTR(extra, S_IRUGO, serio_show_id_extra, NULL); - -static struct attribute *serio_device_id_attrs[] = { - &dev_attr_type.attr, - &dev_attr_proto.attr, - &dev_attr_id.attr, - &dev_attr_extra.attr, - NULL -}; - -static struct attribute_group serio_id_attr_group = { - .name = "id", - .attrs = serio_device_id_attrs, -}; - -static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct serio *serio = to_serio_port(dev); struct device_driver *drv; @@ -469,14 +410,15 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * if (!strncmp(buf, "none", count)) { serio_disconnect_port(serio); } else if (!strncmp(buf, "reconnect", count)) { - serio_reconnect_port(serio); + serio_reconnect_subtree(serio); } else if (!strncmp(buf, "rescan", count)) { serio_disconnect_port(serio); serio_find_driver(serio); + serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { serio_disconnect_port(serio); error = serio_bind_driver(serio, to_serio_driver(drv)); - put_driver(drv); + serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); } else { error = -EINVAL; } @@ -499,9 +441,9 @@ static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute * retval = count; if (!strncmp(buf, "manual", count)) { - serio->manual_bind = 1; + serio->manual_bind = true; } else if (!strncmp(buf, "auto", count)) { - serio->manual_bind = 0; + serio->manual_bind = false; } else { retval = -EINVAL; } @@ -509,14 +451,55 @@ static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute * return retval; } -static struct device_attribute serio_device_attrs[] = { - __ATTR(description, S_IRUGO, serio_show_description, NULL), - __ATTR(modalias, S_IRUGO, serio_show_modalias, NULL), - __ATTR(drvctl, S_IWUSR, NULL, serio_rebind_driver), - __ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode), - __ATTR_NULL +static ssize_t firmware_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct serio *serio = to_serio_port(dev); + + return sprintf(buf, "%s\n", serio->firmware_id); +} + +static DEVICE_ATTR_RO(type); +static DEVICE_ATTR_RO(proto); +static DEVICE_ATTR_RO(id); +static DEVICE_ATTR_RO(extra); + +static struct attribute *serio_device_id_attrs[] = { + &dev_attr_type.attr, + &dev_attr_proto.attr, + &dev_attr_id.attr, + &dev_attr_extra.attr, + NULL }; +static struct attribute_group serio_id_attr_group = { + .name = "id", + .attrs = serio_device_id_attrs, +}; + +static DEVICE_ATTR_RO(modalias); +static DEVICE_ATTR_WO(drvctl); +static DEVICE_ATTR(description, S_IRUGO, serio_show_description, NULL); +static DEVICE_ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode); +static DEVICE_ATTR_RO(firmware_id); + +static struct attribute *serio_device_attrs[] = { + &dev_attr_modalias.attr, + &dev_attr_description.attr, + &dev_attr_drvctl.attr, + &dev_attr_bind_mode.attr, + &dev_attr_firmware_id.attr, + NULL +}; + +static struct attribute_group serio_device_attr_group = { + .attrs = serio_device_attrs, +}; + +static const struct attribute_group *serio_device_attr_groups[] = { + &serio_id_attr_group, + &serio_device_attr_group, + NULL +}; static void serio_release_port(struct device *dev) { @@ -536,13 +519,16 @@ static void serio_init_port(struct serio *serio) __module_get(THIS_MODULE); INIT_LIST_HEAD(&serio->node); + INIT_LIST_HEAD(&serio->child_node); + INIT_LIST_HEAD(&serio->children); spin_lock_init(&serio->lock); mutex_init(&serio->drv_mutex); device_initialize(&serio->dev); - snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id), - "serio%ld", (long)atomic_inc_return(&serio_no) - 1); + dev_set_name(&serio->dev, "serio%ld", + (long)atomic_inc_return(&serio_no) - 1); serio->dev.bus = &serio_bus; serio->dev.release = serio_release_port; + serio->dev.groups = serio_device_attr_groups; if (serio->parent) { serio->dev.parent = &serio->parent->dev; serio->depth = serio->parent->depth + 1; @@ -557,42 +543,36 @@ static void serio_init_port(struct serio *serio) */ static void serio_add_port(struct serio *serio) { + struct serio *parent = serio->parent; int error; - if (serio->parent) { - serio_pause_rx(serio->parent); - serio->parent->child = serio; - serio_continue_rx(serio->parent); + if (parent) { + serio_pause_rx(parent); + list_add_tail(&serio->child_node, &parent->children); + serio_continue_rx(parent); } list_add_tail(&serio->node, &serio_list); + if (serio->start) serio->start(serio); + error = device_add(&serio->dev); if (error) - printk(KERN_ERR - "serio: device_add() failed for %s (%s), error: %d\n", + dev_err(&serio->dev, + "device_add() failed for %s (%s), error: %d\n", serio->phys, serio->name, error); - else { - serio->registered = 1; - error = sysfs_create_group(&serio->dev.kobj, &serio_id_attr_group); - if (error) - printk(KERN_ERR - "serio: sysfs_create_group() failed for %s (%s), error: %d\n", - serio->phys, serio->name, error); - } } /* - * serio_destroy_port() completes deregistration process and removes + * serio_destroy_port() completes unregistration process and removes * port from the system */ static void serio_destroy_port(struct serio *serio) { struct serio *child; - child = serio_get_pending_child(serio); - if (child) { + while ((child = serio_get_pending_child(serio)) != NULL) { serio_remove_pending_events(child); put_device(&child->dev); } @@ -602,16 +582,13 @@ static void serio_destroy_port(struct serio *serio) if (serio->parent) { serio_pause_rx(serio->parent); - serio->parent->child = NULL; + list_del_init(&serio->child_node); serio_continue_rx(serio->parent); serio->parent = NULL; } - if (serio->registered) { - sysfs_remove_group(&serio->dev.kobj, &serio_id_attr_group); + if (device_is_registered(&serio->dev)) device_del(&serio->dev); - serio->registered = 0; - } list_del_init(&serio->node); serio_remove_pending_events(serio); @@ -619,48 +596,100 @@ static void serio_destroy_port(struct serio *serio) } /* - * Reconnect serio port and all its children (re-initialize attached devices) + * Reconnect serio port (re-initialize attached device). + * If reconnect fails (old device is no longer attached or + * there was no device to begin with) we do full rescan in + * hope of finding a driver for the port. + */ +static int serio_reconnect_port(struct serio *serio) +{ + int error = serio_reconnect_driver(serio); + + if (error) { + serio_disconnect_port(serio); + serio_find_driver(serio); + } + + return error; +} + +/* + * Reconnect serio port and all its children (re-initialize attached + * devices). */ -static void serio_reconnect_port(struct serio *serio) +static void serio_reconnect_subtree(struct serio *root) { + struct serio *s = root; + int error; + do { - if (serio_reconnect_driver(serio)) { - serio_disconnect_port(serio); - serio_find_driver(serio); - /* Ok, old children are now gone, we are done */ - break; + error = serio_reconnect_port(s); + if (!error) { + /* + * Reconnect was successful, move on to do the + * first child. + */ + if (!list_empty(&s->children)) { + s = list_first_entry(&s->children, + struct serio, child_node); + continue; + } + } + + /* + * Either it was a leaf node or reconnect failed and it + * became a leaf node. Continue reconnecting starting with + * the next sibling of the parent node. + */ + while (s != root) { + struct serio *parent = s->parent; + + if (!list_is_last(&s->child_node, &parent->children)) { + s = list_entry(s->child_node.next, + struct serio, child_node); + break; + } + + s = parent; } - serio = serio->child; - } while (serio); + } while (s != root); } /* * serio_disconnect_port() unbinds a port from its driver. As a side effect - * all child ports are unbound and destroyed. + * all children ports are unbound and destroyed. */ static void serio_disconnect_port(struct serio *serio) { - struct serio *s, *parent; + struct serio *s = serio; + + /* + * Children ports should be disconnected and destroyed + * first; we travel the tree in depth-first order. + */ + while (!list_empty(&serio->children)) { + + /* Locate a leaf */ + while (!list_empty(&s->children)) + s = list_first_entry(&s->children, + struct serio, child_node); - if (serio->child) { /* - * Children ports should be disconnected and destroyed - * first, staring with the leaf one, since we don't want - * to do recursion + * Prune this leaf node unless it is the one we + * started with. */ - for (s = serio; s->child; s = s->child) - /* empty */; - - do { - parent = s->parent; + if (s != serio) { + struct serio *parent = s->parent; device_release_driver(&s->dev); serio_destroy_port(s); - } while ((s = parent) != serio); + + s = parent; + } } /* - * Ok, no children left, now disconnect this port + * OK, no children left, now disconnect this port. */ device_release_driver(&serio->dev); } @@ -669,11 +698,13 @@ void serio_rescan(struct serio *serio) { serio_queue_event(serio, NULL, SERIO_RESCAN_PORT); } +EXPORT_SYMBOL(serio_rescan); void serio_reconnect(struct serio *serio) { - serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT); + serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE); } +EXPORT_SYMBOL(serio_reconnect); /* * Submits register request to kseriod for subsequent execution. @@ -684,6 +715,7 @@ void __serio_register_port(struct serio *serio, struct module *owner) serio_init_port(serio); serio_queue_event(serio, owner, SERIO_REGISTER_PORT); } +EXPORT_SYMBOL(__serio_register_port); /* * Synchronously unregisters serio port. @@ -695,61 +727,66 @@ void serio_unregister_port(struct serio *serio) serio_destroy_port(serio); mutex_unlock(&serio_mutex); } +EXPORT_SYMBOL(serio_unregister_port); /* - * Safely unregisters child port if one is present. + * Safely unregisters children ports if they are present. */ void serio_unregister_child_port(struct serio *serio) { + struct serio *s, *next; + mutex_lock(&serio_mutex); - if (serio->child) { - serio_disconnect_port(serio->child); - serio_destroy_port(serio->child); + list_for_each_entry_safe(s, next, &serio->children, child_node) { + serio_disconnect_port(s); + serio_destroy_port(s); } mutex_unlock(&serio_mutex); } +EXPORT_SYMBOL(serio_unregister_child_port); /* * Serio driver operations */ -static ssize_t serio_driver_show_description(struct device_driver *drv, char *buf) +static ssize_t description_show(struct device_driver *drv, char *buf) { struct serio_driver *driver = to_serio_driver(drv); return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)"); } +static DRIVER_ATTR_RO(description); -static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf) +static ssize_t bind_mode_show(struct device_driver *drv, char *buf) { struct serio_driver *serio_drv = to_serio_driver(drv); return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto"); } -static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count) +static ssize_t bind_mode_store(struct device_driver *drv, const char *buf, size_t count) { struct serio_driver *serio_drv = to_serio_driver(drv); int retval; retval = count; if (!strncmp(buf, "manual", count)) { - serio_drv->manual_bind = 1; + serio_drv->manual_bind = true; } else if (!strncmp(buf, "auto", count)) { - serio_drv->manual_bind = 0; + serio_drv->manual_bind = false; } else { retval = -EINVAL; } return retval; } +static DRIVER_ATTR_RW(bind_mode); - -static struct driver_attribute serio_driver_attrs[] = { - __ATTR(description, S_IRUGO, serio_driver_show_description, NULL), - __ATTR(bind_mode, S_IWUSR | S_IRUGO, - serio_driver_show_bind_mode, serio_driver_set_bind_mode), - __ATTR_NULL +static struct attribute *serio_driver_attrs[] = { + &driver_attr_description.attr, + &driver_attr_bind_mode.attr, + NULL, }; +ATTRIBUTE_GROUPS(serio_driver); static int serio_driver_probe(struct device *dev) { @@ -769,8 +806,10 @@ static int serio_driver_remove(struct device *dev) static void serio_cleanup(struct serio *serio) { + mutex_lock(&serio->drv_mutex); if (serio->drv && serio->drv->cleanup) serio->drv->cleanup(serio); + mutex_unlock(&serio->drv_mutex); } static void serio_shutdown(struct device *dev) @@ -786,14 +825,13 @@ static void serio_attach_driver(struct serio_driver *drv) error = driver_attach(&drv->driver); if (error) - printk(KERN_WARNING - "serio: driver_attach() failed for %s with error %d\n", - drv->driver.name, error); + pr_warning("driver_attach() failed for %s with error %d\n", + drv->driver.name, error); } int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name) { - int manual_bind = drv->manual_bind; + bool manual_bind = drv->manual_bind; int error; drv->driver.bus = &serio_bus; @@ -804,12 +842,11 @@ int __serio_register_driver(struct serio_driver *drv, struct module *owner, cons * Temporarily disable automatic binding because probing * takes long time and we are better off doing it in kseriod */ - drv->manual_bind = 1; + drv->manual_bind = true; error = driver_register(&drv->driver); if (error) { - printk(KERN_ERR - "serio: driver_register() failed for %s, error: %d\n", + pr_err("driver_register() failed for %s, error: %d\n", drv->driver.name, error); return error; } @@ -819,7 +856,7 @@ int __serio_register_driver(struct serio_driver *drv, struct module *owner, cons * driver to free ports */ if (!manual_bind) { - drv->manual_bind = 0; + drv->manual_bind = false; error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER); if (error) { driver_unregister(&drv->driver); @@ -829,13 +866,16 @@ int __serio_register_driver(struct serio_driver *drv, struct module *owner, cons return 0; } +EXPORT_SYMBOL(__serio_register_driver); void serio_unregister_driver(struct serio_driver *drv) { struct serio *serio; mutex_lock(&serio_mutex); - drv->manual_bind = 1; /* so serio_find_driver ignores it */ + + drv->manual_bind = true; /* so serio_find_driver ignores it */ + serio_remove_pending_events(drv); start_over: list_for_each_entry(serio, &serio_list, node) { @@ -850,6 +890,7 @@ start_over: driver_unregister(&drv->driver); mutex_unlock(&serio_mutex); } +EXPORT_SYMBOL(serio_unregister_driver); static void serio_set_drv(struct serio *serio, struct serio_driver *drv) { @@ -869,22 +910,16 @@ static int serio_bus_match(struct device *dev, struct device_driver *drv) return serio_match_port(serio_drv->id_table, serio); } -#ifdef CONFIG_HOTPLUG - #define SERIO_ADD_UEVENT_VAR(fmt, val...) \ do { \ - int err = add_uevent_var(envp, num_envp, &i, \ - buffer, buffer_size, &len, \ - fmt, val); \ + int err = add_uevent_var(env, fmt, val); \ if (err) \ return err; \ } while (0) -static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) +static int serio_uevent(struct device *dev, struct kobj_uevent_env *env) { struct serio *serio; - int i = 0; - int len = 0; if (!dev) return -ENODEV; @@ -895,32 +930,24 @@ static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buf SERIO_ADD_UEVENT_VAR("SERIO_PROTO=%02x", serio->id.proto); SERIO_ADD_UEVENT_VAR("SERIO_ID=%02x", serio->id.id); SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra); + SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X", serio->id.type, serio->id.proto, serio->id.id, serio->id.extra); - envp[i] = NULL; + + if (serio->firmware_id[0]) + SERIO_ADD_UEVENT_VAR("SERIO_FIRMWARE_ID=%s", + serio->firmware_id); return 0; } #undef SERIO_ADD_UEVENT_VAR -#else - -static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) -{ - return -ENODEV; -} - -#endif /* CONFIG_HOTPLUG */ - #ifdef CONFIG_PM -static int serio_suspend(struct device *dev, pm_message_t state) +static int serio_suspend(struct device *dev) { - if (dev->power.power_state.event != state.event) { - if (state.event == PM_EVENT_SUSPEND) - serio_cleanup(to_serio_port(dev)); + struct serio *serio = to_serio_port(dev); - dev->power.power_state = state; - } + serio_cleanup(serio); return 0; } @@ -929,19 +956,21 @@ static int serio_resume(struct device *dev) { struct serio *serio = to_serio_port(dev); - if (dev->power.power_state.event != PM_EVENT_ON && - serio_reconnect_driver(serio)) { - /* - * Driver re-probing can take a while, so better let kseriod - * deal with it. - */ - serio_rescan(serio); - } - - dev->power.power_state = PMSG_ON; + /* + * Driver reconnect can take a while, so better let kseriod + * deal with it. + */ + 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 */ @@ -955,6 +984,7 @@ int serio_open(struct serio *serio, struct serio_driver *drv) } return 0; } +EXPORT_SYMBOL(serio_open); /* called from serio_driver->connect/disconnect methods under serio_mutex */ void serio_close(struct serio *serio) @@ -964,6 +994,7 @@ void serio_close(struct serio *serio) serio_set_drv(serio, NULL); } +EXPORT_SYMBOL(serio_close); irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int dfl) @@ -975,7 +1006,7 @@ irqreturn_t serio_interrupt(struct serio *serio, if (likely(serio->drv)) { ret = serio->drv->interrupt(serio, data, dfl); - } else if (!dfl && serio->registered) { + } else if (!dfl && device_is_registered(&serio->dev)) { serio_rescan(serio); ret = IRQ_HANDLED; } @@ -984,19 +1015,18 @@ irqreturn_t serio_interrupt(struct serio *serio, return ret; } +EXPORT_SYMBOL(serio_interrupt); static struct bus_type serio_bus = { .name = "serio", - .dev_attrs = serio_device_attrs, - .drv_attrs = serio_driver_attrs, + .drv_groups = serio_driver_groups, .match = serio_bus_match, .uevent = serio_uevent, .probe = serio_driver_probe, .remove = serio_driver_remove, .shutdown = serio_shutdown, #ifdef CONFIG_PM - .suspend = serio_suspend, - .resume = serio_resume, + .pm = &serio_pm_ops, #endif }; @@ -1006,15 +1036,7 @@ static int __init serio_init(void) error = bus_register(&serio_bus); if (error) { - printk(KERN_ERR "serio: failed to register serio bus, error: %d\n", error); - return error; - } - - serio_task = kthread_run(serio_thread, NULL, "kseriod"); - if (IS_ERR(serio_task)) { - bus_unregister(&serio_bus); - error = PTR_ERR(serio_task); - printk(KERN_ERR "serio: Failed to start kseriod, error: %d\n", error); + pr_err("Failed to register serio bus, error: %d\n", error); return error; } @@ -1024,7 +1046,12 @@ static int __init serio_init(void) static void __exit serio_exit(void) { bus_unregister(&serio_bus); - kthread_stop(serio_task); + + /* + * There should not be any outstanding events but work may + * still be scheduled so simply cancel it. + */ + cancel_work_sync(&serio_event_work); } subsys_initcall(serio_init); diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index 887357666c6..c9a02fe5757 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -9,11 +9,12 @@ * the Free Software Foundation. */ +#include <linux/kref.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/poll.h> #include <linux/module.h> #include <linux/serio.h> -#include <linux/init.h> #include <linux/major.h> #include <linux/device.h> #include <linux/miscdevice.h> @@ -32,15 +33,16 @@ struct serio_raw { unsigned int tail, head; char name[16]; - unsigned int refcnt; + struct kref kref; struct serio *serio; struct miscdevice dev; wait_queue_head_t wait; - struct list_head list; + struct list_head client_list; struct list_head node; + bool dead; }; -struct serio_raw_list { +struct serio_raw_client { struct fasync_struct *fasync; struct serio_raw *serio_raw; struct list_head node; @@ -48,7 +50,6 @@ struct serio_raw_list { static DEFINE_MUTEX(serio_raw_mutex); static LIST_HEAD(serio_raw_list); -static unsigned int serio_raw_no; /********************************************************************* * Interface with userspace (file operations) * @@ -56,11 +57,9 @@ static unsigned int serio_raw_no; static int serio_raw_fasync(int fd, struct file *file, int on) { - struct serio_raw_list *list = file->private_data; - int retval; + struct serio_raw_client *client = file->private_data; - retval = fasync_helper(fd, file, on, &list->fasync); - return retval < 0 ? retval : 0; + return fasync_helper(fd, file, on, &client->fasync); } static struct serio_raw *serio_raw_locate(int minor) @@ -78,72 +77,74 @@ static struct serio_raw *serio_raw_locate(int minor) static int serio_raw_open(struct inode *inode, struct file *file) { struct serio_raw *serio_raw; - struct serio_raw_list *list; - int retval = 0; + struct serio_raw_client *client; + int retval; retval = mutex_lock_interruptible(&serio_raw_mutex); if (retval) return retval; - if (!(serio_raw = serio_raw_locate(iminor(inode)))) { + serio_raw = serio_raw_locate(iminor(inode)); + if (!serio_raw) { retval = -ENODEV; goto out; } - if (!serio_raw->serio) { + if (serio_raw->dead) { retval = -ENODEV; goto out; } - if (!(list = kzalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) { + client = kzalloc(sizeof(struct serio_raw_client), GFP_KERNEL); + if (!client) { retval = -ENOMEM; goto out; } - list->serio_raw = serio_raw; - file->private_data = list; + client->serio_raw = serio_raw; + file->private_data = client; - serio_raw->refcnt++; - list_add_tail(&list->node, &serio_raw->list); + kref_get(&serio_raw->kref); + + serio_pause_rx(serio_raw->serio); + list_add_tail(&client->node, &serio_raw->client_list); + serio_continue_rx(serio_raw->serio); out: mutex_unlock(&serio_raw_mutex); return retval; } -static int serio_raw_cleanup(struct serio_raw *serio_raw) +static void serio_raw_free(struct kref *kref) { - if (--serio_raw->refcnt == 0) { - misc_deregister(&serio_raw->dev); - list_del_init(&serio_raw->node); - kfree(serio_raw); + struct serio_raw *serio_raw = + container_of(kref, struct serio_raw, kref); - return 1; - } - - return 0; + put_device(&serio_raw->serio->dev); + kfree(serio_raw); } static int serio_raw_release(struct inode *inode, struct file *file) { - struct serio_raw_list *list = file->private_data; - struct serio_raw *serio_raw = list->serio_raw; + struct serio_raw_client *client = file->private_data; + struct serio_raw *serio_raw = client->serio_raw; - mutex_lock(&serio_raw_mutex); + serio_pause_rx(serio_raw->serio); + list_del(&client->node); + serio_continue_rx(serio_raw->serio); - serio_raw_fasync(-1, file, 0); - serio_raw_cleanup(serio_raw); + kfree(client); + + kref_put(&serio_raw->kref, serio_raw_free); - mutex_unlock(&serio_raw_mutex); return 0; } -static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c) +static bool serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c) { - unsigned long flags; - int empty; + bool empty; - spin_lock_irqsave(&serio_raw->serio->lock, flags); + serio_pause_rx(serio_raw->serio); empty = serio_raw->head == serio_raw->tail; if (!empty) { @@ -151,53 +152,65 @@ static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c) serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN; } - spin_unlock_irqrestore(&serio_raw->serio->lock, flags); + serio_continue_rx(serio_raw->serio); return !empty; } -static ssize_t serio_raw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +static ssize_t serio_raw_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) { - struct serio_raw_list *list = file->private_data; - struct serio_raw *serio_raw = list->serio_raw; - char c; - ssize_t retval = 0; - - if (!serio_raw->serio) - return -ENODEV; - - if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK)) - return -EAGAIN; - - retval = wait_event_interruptible(list->serio_raw->wait, - serio_raw->head != serio_raw->tail || !serio_raw->serio); - if (retval) - return retval; + struct serio_raw_client *client = file->private_data; + struct serio_raw *serio_raw = client->serio_raw; + char uninitialized_var(c); + ssize_t read = 0; + int error; + + for (;;) { + if (serio_raw->dead) + return -ENODEV; + + if (serio_raw->head == serio_raw->tail && + (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + if (count == 0) + break; + + while (read < count && serio_raw_fetch_byte(serio_raw, &c)) { + if (put_user(c, buffer++)) + return -EFAULT; + read++; + } - if (!serio_raw->serio) - return -ENODEV; + if (read) + break; - while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) { - if (put_user(c, buffer++)) - return -EFAULT; - retval++; + if (!(file->f_flags & O_NONBLOCK)) { + error = wait_event_interruptible(serio_raw->wait, + serio_raw->head != serio_raw->tail || + serio_raw->dead); + if (error) + return error; + } } - return retval; + return read; } -static ssize_t serio_raw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +static ssize_t serio_raw_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) { - struct serio_raw_list *list = file->private_data; - ssize_t written = 0; - int retval; + struct serio_raw_client *client = file->private_data; + struct serio_raw *serio_raw = client->serio_raw; + int retval = 0; unsigned char c; retval = mutex_lock_interruptible(&serio_raw_mutex); if (retval) return retval; - if (!list->serio_raw->serio) { + if (serio_raw->dead) { retval = -ENODEV; goto out; } @@ -210,59 +223,67 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer, siz retval = -EFAULT; goto out; } - if (serio_write(list->serio_raw->serio, c)) { - retval = -EIO; + + if (serio_write(serio_raw->serio, c)) { + /* Either signal error or partial write */ + if (retval == 0) + retval = -EIO; goto out; } - written++; - }; + + retval++; + } out: mutex_unlock(&serio_raw_mutex); - return written; + return retval; } static unsigned int serio_raw_poll(struct file *file, poll_table *wait) { - struct serio_raw_list *list = file->private_data; + struct serio_raw_client *client = file->private_data; + struct serio_raw *serio_raw = client->serio_raw; + unsigned int mask; - poll_wait(file, &list->serio_raw->wait, wait); + poll_wait(file, &serio_raw->wait, wait); - if (list->serio_raw->head != list->serio_raw->tail) - return POLLIN | POLLRDNORM; + mask = serio_raw->dead ? POLLHUP | POLLERR : POLLOUT | POLLWRNORM; + if (serio_raw->head != serio_raw->tail) + mask |= POLLIN | POLLRDNORM; - return 0; + return mask; } static const struct file_operations serio_raw_fops = { - .owner = THIS_MODULE, - .open = serio_raw_open, - .release = serio_raw_release, - .read = serio_raw_read, - .write = serio_raw_write, - .poll = serio_raw_poll, - .fasync = serio_raw_fasync, + .owner = THIS_MODULE, + .open = serio_raw_open, + .release = serio_raw_release, + .read = serio_raw_read, + .write = serio_raw_write, + .poll = serio_raw_poll, + .fasync = serio_raw_fasync, + .llseek = noop_llseek, }; /********************************************************************* - * Interface with serio port * + * Interface with serio port * *********************************************************************/ static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data, unsigned int dfl) { struct serio_raw *serio_raw = serio_get_drvdata(serio); - struct serio_raw_list *list; + struct serio_raw_client *client; unsigned int head = serio_raw->head; - /* we are holding serio->lock here so we are prootected */ + /* we are holding serio->lock here so we are protected */ serio_raw->queue[head] = data; head = (head + 1) % SERIO_RAW_QUEUE_LEN; if (likely(head != serio_raw->tail)) { serio_raw->head = head; - list_for_each_entry(list, &serio_raw->list, node) - kill_fasync(&list->fasync, SIGIO, POLL_IN); + list_for_each_entry(client, &serio_raw->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_IN); wake_up_interruptible(&serio_raw->wait); } @@ -271,29 +292,37 @@ static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data, static int serio_raw_connect(struct serio *serio, struct serio_driver *drv) { + static atomic_t serio_raw_no = ATOMIC_INIT(0); struct serio_raw *serio_raw; int err; - if (!(serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL))) { - printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n"); + serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL); + if (!serio_raw) { + dev_dbg(&serio->dev, "can't allocate memory for a device\n"); return -ENOMEM; } - mutex_lock(&serio_raw_mutex); + snprintf(serio_raw->name, sizeof(serio_raw->name), + "serio_raw%ld", (long)atomic_inc_return(&serio_raw_no) - 1); + kref_init(&serio_raw->kref); + INIT_LIST_HEAD(&serio_raw->client_list); + init_waitqueue_head(&serio_raw->wait); - snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++); - serio_raw->refcnt = 1; serio_raw->serio = serio; - INIT_LIST_HEAD(&serio_raw->list); - init_waitqueue_head(&serio_raw->wait); + get_device(&serio->dev); serio_set_drvdata(serio, serio_raw); err = serio_open(serio, drv); if (err) - goto out_free; + goto err_free; + + err = mutex_lock_killable(&serio_raw_mutex); + if (err) + goto err_close; list_add_tail(&serio_raw->node, &serio_raw_list); + mutex_unlock(&serio_raw_mutex); serio_raw->dev.minor = PSMOUSE_MINOR; serio_raw->dev.name = serio_raw->name; @@ -307,23 +336,23 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv) } if (err) { - printk(KERN_INFO "serio_raw: failed to register raw access device for %s\n", + dev_err(&serio->dev, + "failed to register raw access device for %s\n", serio->phys); - goto out_close; + goto err_unlink; } - printk(KERN_INFO "serio_raw: raw access enabled on %s (%s, minor %d)\n", - serio->phys, serio_raw->name, serio_raw->dev.minor); - goto out; + dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n", + serio->phys, serio_raw->name, serio_raw->dev.minor); + return 0; -out_close: - serio_close(serio); +err_unlink: list_del_init(&serio_raw->node); -out_free: +err_close: + serio_close(serio); +err_free: serio_set_drvdata(serio, NULL); - kfree(serio_raw); -out: - mutex_unlock(&serio_raw_mutex); + kref_put(&serio_raw->kref, serio_raw_free); return err; } @@ -333,7 +362,8 @@ static int serio_raw_reconnect(struct serio *serio) struct serio_driver *drv = serio->drv; if (!drv || !serio_raw) { - printk(KERN_DEBUG "serio_raw: reconnect request, but serio is disconnected, ignoring...\n"); + dev_dbg(&serio->dev, + "reconnect request, but serio is disconnected, ignoring...\n"); return -1; } @@ -344,22 +374,40 @@ static int serio_raw_reconnect(struct serio *serio) return 0; } +/* + * Wake up users waiting for IO so they can disconnect from + * dead device. + */ +static void serio_raw_hangup(struct serio_raw *serio_raw) +{ + struct serio_raw_client *client; + + serio_pause_rx(serio_raw->serio); + list_for_each_entry(client, &serio_raw->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + serio_continue_rx(serio_raw->serio); + + wake_up_interruptible(&serio_raw->wait); +} + + static void serio_raw_disconnect(struct serio *serio) { - struct serio_raw *serio_raw; + struct serio_raw *serio_raw = serio_get_drvdata(serio); + + misc_deregister(&serio_raw->dev); mutex_lock(&serio_raw_mutex); + serio_raw->dead = true; + list_del_init(&serio_raw->node); + mutex_unlock(&serio_raw_mutex); - serio_raw = serio_get_drvdata(serio); + serio_raw_hangup(serio_raw); serio_close(serio); - serio_set_drvdata(serio, NULL); + kref_put(&serio_raw->kref, serio_raw_free); - serio_raw->serio = NULL; - if (!serio_raw_cleanup(serio_raw)) - wake_up_interruptible(&serio_raw->wait); - - mutex_unlock(&serio_raw_mutex); + serio_set_drvdata(serio, NULL); } static struct serio_device_id serio_raw_serio_ids[] = { @@ -369,6 +417,12 @@ static struct serio_device_id serio_raw_serio_ids[] = { .id = SERIO_ANY, .extra = SERIO_ANY, }, + { + .type = SERIO_8042_XL, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, { 0 } }; @@ -384,18 +438,7 @@ static struct serio_driver serio_raw_drv = { .connect = serio_raw_connect, .reconnect = serio_raw_reconnect, .disconnect = serio_raw_disconnect, - .manual_bind = 1, + .manual_bind = true, }; -static int __init serio_raw_init(void) -{ - return serio_register_driver(&serio_raw_drv); -} - -static void __exit serio_raw_exit(void) -{ - serio_unregister_driver(&serio_raw_drv); -} - -module_init(serio_raw_init); -module_exit(serio_raw_exit); +module_serio_driver(serio_raw_drv); diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index e1a3a79ab3f..0cb7ef59071 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -15,6 +15,7 @@ #include <asm/uaccess.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> @@ -46,7 +47,7 @@ struct serport { static int serport_serio_write(struct serio *serio, unsigned char data) { struct serport *serport = serio->port_data; - return -(serport->tty->driver->write(serport->tty, &data, 1) != 1); + return -(serport->tty->ops->write(serport->tty, &data, 1) != 1); } static int serport_serio_open(struct serio *serio) @@ -115,14 +116,15 @@ static void serport_ldisc_close(struct tty_struct *tty) /* * serport_ldisc_receive() is called by the low level tty driver when characters - * are ready for us. We forward the characters, one by one to the 'interrupt' - * routine. + * are ready for us. We forward the characters and flags, one by one to the + * 'interrupt' routine. */ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct serport *serport = (struct serport*) tty->disc_data; unsigned long flags; + unsigned int ch_flags = 0; int i; spin_lock_irqsave(&serport->lock, flags); @@ -130,8 +132,25 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c if (!test_bit(SERPORT_ACTIVE, &serport->flags)) goto out; - for (i = 0; i < count; i++) - serio_interrupt(serport->serio, cp[i], 0); + for (i = 0; i < count; i++) { + if (fp) { + switch (fp[i]) { + case TTY_FRAME: + ch_flags = SERIO_FRAME; + break; + + case TTY_PARITY: + ch_flags = SERIO_PARITY; + break; + + default: + ch_flags = 0; + break; + } + } + + serio_interrupt(serport->serio, cp[i], ch_flags); + } out: spin_unlock_irqrestore(&serport->lock, flags); @@ -164,6 +183,7 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u serio->open = serport_serio_open; serio->close = serport_serio_close; serio->port_data = serport; + serio->dev.parent = tty->dev; serio_register_port(serport->serio); printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name)); @@ -216,7 +236,7 @@ static void serport_ldisc_write_wakeup(struct tty_struct * tty) * The line discipline structure. */ -static struct tty_ldisc serport_ldisc = { +static struct tty_ldisc_ops serport_ldisc = { .owner = THIS_MODULE, .name = "input", .open = serport_ldisc_open, diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c new file mode 100644 index 00000000000..e6cf52ebad8 --- /dev/null +++ b/drivers/input/serio/xilinx_ps2.c @@ -0,0 +1,377 @@ +/* + * Xilinx XPS PS/2 device driver + * + * (c) 2005 MontaVista Software, Inc. + * (c) 2008 Xilinx, Inc. + * + * 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. + * + * 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 <linux/module.h> +#include <linux/serio.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> + +#define DRIVER_NAME "xilinx_ps2" + +/* Register offsets for the xps2 device */ +#define XPS2_SRST_OFFSET 0x00000000 /* Software Reset register */ +#define XPS2_STATUS_OFFSET 0x00000004 /* Status register */ +#define XPS2_RX_DATA_OFFSET 0x00000008 /* Receive Data register */ +#define XPS2_TX_DATA_OFFSET 0x0000000C /* Transmit Data register */ +#define XPS2_GIER_OFFSET 0x0000002C /* Global Interrupt Enable reg */ +#define XPS2_IPISR_OFFSET 0x00000030 /* Interrupt Status register */ +#define XPS2_IPIER_OFFSET 0x00000038 /* Interrupt Enable register */ + +/* Reset Register Bit Definitions */ +#define XPS2_SRST_RESET 0x0000000A /* Software Reset */ + +/* Status Register Bit Positions */ +#define XPS2_STATUS_RX_FULL 0x00000001 /* Receive Full */ +#define XPS2_STATUS_TX_FULL 0x00000002 /* Transmit Full */ + +/* Bit definitions for ISR/IER registers. Both the registers have the same bit + * definitions and are only defined once. */ +#define XPS2_IPIXR_WDT_TOUT 0x00000001 /* Watchdog Timeout Interrupt */ +#define XPS2_IPIXR_TX_NOACK 0x00000002 /* Transmit No ACK Interrupt */ +#define XPS2_IPIXR_TX_ACK 0x00000004 /* Transmit ACK (Data) Interrupt */ +#define XPS2_IPIXR_RX_OVF 0x00000008 /* Receive Overflow Interrupt */ +#define XPS2_IPIXR_RX_ERR 0x00000010 /* Receive Error Interrupt */ +#define XPS2_IPIXR_RX_FULL 0x00000020 /* Receive Data Interrupt */ + +/* Mask for all the Transmit Interrupts */ +#define XPS2_IPIXR_TX_ALL (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_TX_ACK) + +/* Mask for all the Receive Interrupts */ +#define XPS2_IPIXR_RX_ALL (XPS2_IPIXR_RX_OVF | XPS2_IPIXR_RX_ERR | \ + XPS2_IPIXR_RX_FULL) + +/* Mask for all the Interrupts */ +#define XPS2_IPIXR_ALL (XPS2_IPIXR_TX_ALL | XPS2_IPIXR_RX_ALL | \ + XPS2_IPIXR_WDT_TOUT) + +/* Global Interrupt Enable mask */ +#define XPS2_GIER_GIE_MASK 0x80000000 + +struct xps2data { + int irq; + spinlock_t lock; + void __iomem *base_address; /* virt. address of control registers */ + unsigned int flags; + struct serio *serio; /* serio */ + struct device *dev; +}; + +/************************************/ +/* XPS PS/2 data transmission calls */ +/************************************/ + +/** + * xps2_recv() - attempts to receive a byte from the PS/2 port. + * @drvdata: pointer to ps2 device private data structure + * @byte: address where the read data will be copied + * + * If there is any data available in the PS/2 receiver, this functions reads + * the data, otherwise it returns error. + */ +static int xps2_recv(struct xps2data *drvdata, u8 *byte) +{ + u32 sr; + int status = -1; + + /* If there is data available in the PS/2 receiver, read it */ + sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET); + if (sr & XPS2_STATUS_RX_FULL) { + *byte = in_be32(drvdata->base_address + XPS2_RX_DATA_OFFSET); + status = 0; + } + + return status; +} + +/*********************/ +/* Interrupt handler */ +/*********************/ +static irqreturn_t xps2_interrupt(int irq, void *dev_id) +{ + struct xps2data *drvdata = dev_id; + u32 intr_sr; + u8 c; + int status; + + /* Get the PS/2 interrupts and clear them */ + intr_sr = in_be32(drvdata->base_address + XPS2_IPISR_OFFSET); + out_be32(drvdata->base_address + XPS2_IPISR_OFFSET, intr_sr); + + /* Check which interrupt is active */ + if (intr_sr & XPS2_IPIXR_RX_OVF) + dev_warn(drvdata->dev, "receive overrun error\n"); + + if (intr_sr & XPS2_IPIXR_RX_ERR) + drvdata->flags |= SERIO_PARITY; + + if (intr_sr & (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_WDT_TOUT)) + drvdata->flags |= SERIO_TIMEOUT; + + if (intr_sr & XPS2_IPIXR_RX_FULL) { + status = xps2_recv(drvdata, &c); + + /* Error, if a byte is not received */ + if (status) { + dev_err(drvdata->dev, + "wrong rcvd byte count (%d)\n", status); + } else { + serio_interrupt(drvdata->serio, c, drvdata->flags); + drvdata->flags = 0; + } + } + + return IRQ_HANDLED; +} + +/*******************/ +/* serio callbacks */ +/*******************/ + +/** + * sxps2_write() - sends a byte out through the PS/2 port. + * @pserio: pointer to the serio structure of the PS/2 port + * @c: data that needs to be written to the PS/2 port + * + * This function checks if the PS/2 transmitter is empty and sends a byte. + * Otherwise it returns error. Transmission fails only when nothing is connected + * to the PS/2 port. Thats why, we do not try to resend the data in case of a + * failure. + */ +static int sxps2_write(struct serio *pserio, unsigned char c) +{ + struct xps2data *drvdata = pserio->port_data; + unsigned long flags; + u32 sr; + int status = -1; + + spin_lock_irqsave(&drvdata->lock, flags); + + /* If the PS/2 transmitter is empty send a byte of data */ + sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET); + if (!(sr & XPS2_STATUS_TX_FULL)) { + out_be32(drvdata->base_address + XPS2_TX_DATA_OFFSET, c); + status = 0; + } + + spin_unlock_irqrestore(&drvdata->lock, flags); + + return status; +} + +/** + * sxps2_open() - called when a port is opened by the higher layer. + * @pserio: pointer to the serio structure of the PS/2 device + * + * This function requests irq and enables interrupts for the PS/2 device. + */ +static int sxps2_open(struct serio *pserio) +{ + struct xps2data *drvdata = pserio->port_data; + int error; + u8 c; + + error = request_irq(drvdata->irq, &xps2_interrupt, 0, + DRIVER_NAME, drvdata); + if (error) { + dev_err(drvdata->dev, + "Couldn't allocate interrupt %d\n", drvdata->irq); + return error; + } + + /* start reception by enabling the interrupts */ + out_be32(drvdata->base_address + XPS2_GIER_OFFSET, XPS2_GIER_GIE_MASK); + out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, XPS2_IPIXR_RX_ALL); + (void)xps2_recv(drvdata, &c); + + return 0; /* success */ +} + +/** + * sxps2_close() - frees the interrupt. + * @pserio: pointer to the serio structure of the PS/2 device + * + * This function frees the irq and disables interrupts for the PS/2 device. + */ +static void sxps2_close(struct serio *pserio) +{ + struct xps2data *drvdata = pserio->port_data; + + /* Disable the PS2 interrupts */ + out_be32(drvdata->base_address + XPS2_GIER_OFFSET, 0x00); + out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0x00); + free_irq(drvdata->irq, drvdata); +} + +/** + * xps2_of_probe - probe method for the PS/2 device. + * @of_dev: pointer to OF device structure + * @match: pointer to the structure used for matching a device + * + * This function probes the PS/2 device in the device tree. + * It initializes the driver data structure and the hardware. + * It returns 0, if the driver is bound to the PS/2 device, or a negative + * value if there is an error. + */ +static int xps2_of_probe(struct platform_device *ofdev) +{ + struct resource r_mem; /* IO mem resources */ + struct xps2data *drvdata; + struct serio *serio; + struct device *dev = &ofdev->dev; + resource_size_t remap_size, phys_addr; + unsigned int irq; + int error; + + dev_info(dev, "Device Tree Probing \'%s\'\n", + ofdev->dev.of_node->name); + + /* Get iospace for the device */ + error = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem); + if (error) { + dev_err(dev, "invalid address\n"); + return error; + } + + /* Get IRQ for the device */ + irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + if (!irq) { + dev_err(dev, "no IRQ found\n"); + return -ENODEV; + } + + drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!drvdata || !serio) { + error = -ENOMEM; + goto failed1; + } + + spin_lock_init(&drvdata->lock); + drvdata->irq = irq; + drvdata->serio = serio; + drvdata->dev = dev; + + phys_addr = r_mem.start; + remap_size = resource_size(&r_mem); + if (!request_mem_region(phys_addr, remap_size, DRIVER_NAME)) { + dev_err(dev, "Couldn't lock memory region at 0x%08llX\n", + (unsigned long long)phys_addr); + error = -EBUSY; + goto failed1; + } + + /* Fill in configuration data and add them to the list */ + drvdata->base_address = ioremap(phys_addr, remap_size); + if (drvdata->base_address == NULL) { + dev_err(dev, "Couldn't ioremap memory at 0x%08llX\n", + (unsigned long long)phys_addr); + error = -EFAULT; + goto failed2; + } + + /* Disable all the interrupts, just in case */ + out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0); + + /* Reset the PS2 device and abort any current transaction, to make sure + * we have the PS2 in a good state */ + out_be32(drvdata->base_address + XPS2_SRST_OFFSET, XPS2_SRST_RESET); + + dev_info(dev, "Xilinx PS2 at 0x%08llX mapped to 0x%p, irq=%d\n", + (unsigned long long)phys_addr, drvdata->base_address, + drvdata->irq); + + serio->id.type = SERIO_8042; + serio->write = sxps2_write; + serio->open = sxps2_open; + serio->close = sxps2_close; + serio->port_data = drvdata; + serio->dev.parent = dev; + snprintf(serio->name, sizeof(serio->name), + "Xilinx XPS PS/2 at %08llX", (unsigned long long)phys_addr); + snprintf(serio->phys, sizeof(serio->phys), + "xilinxps2/serio at %08llX", (unsigned long long)phys_addr); + + serio_register_port(serio); + + platform_set_drvdata(ofdev, drvdata); + return 0; /* success */ + +failed2: + release_mem_region(phys_addr, remap_size); +failed1: + kfree(serio); + kfree(drvdata); + + return error; +} + +/** + * xps2_of_remove - unbinds the driver from the PS/2 device. + * @of_dev: pointer to OF device structure + * + * This function is called if a device is physically removed from the system or + * if the driver module is being unloaded. It frees any resources allocated to + * the device. + */ +static int xps2_of_remove(struct platform_device *of_dev) +{ + struct xps2data *drvdata = platform_get_drvdata(of_dev); + struct resource r_mem; /* IO mem resources */ + + serio_unregister_port(drvdata->serio); + iounmap(drvdata->base_address); + + /* Get iospace of the device */ + if (of_address_to_resource(of_dev->dev.of_node, 0, &r_mem)) + dev_err(drvdata->dev, "invalid address\n"); + else + release_mem_region(r_mem.start, resource_size(&r_mem)); + + kfree(drvdata); + + return 0; +} + +/* Match table for of_platform binding */ +static const struct of_device_id xps2_of_match[] = { + { .compatible = "xlnx,xps-ps2-1.00.a", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, xps2_of_match); + +static struct platform_driver xps2_of_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = xps2_of_match, + }, + .probe = xps2_of_probe, + .remove = xps2_of_remove, +}; +module_platform_driver(xps2_of_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx XPS PS/2 driver"); +MODULE_LICENSE("GPL"); + |
