aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/serio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/serio')
-rw-r--r--drivers/input/serio/Kconfig57
-rw-r--r--drivers/input/serio/Makefile4
-rw-r--r--drivers/input/serio/altera_ps2.c26
-rw-r--r--drivers/input/serio/ambakmi.c33
-rw-r--r--drivers/input/serio/ams_delta_serio.c59
-rw-r--r--drivers/input/serio/apbps2.c228
-rw-r--r--drivers/input/serio/arc_ps2.c280
-rw-r--r--drivers/input/serio/at32psif.c39
-rw-r--r--drivers/input/serio/ct82c710.c6
-rw-r--r--drivers/input/serio/gscps2.c6
-rw-r--r--drivers/input/serio/hil_mlc.c13
-rw-r--r--drivers/input/serio/hp_sdc.c9
-rw-r--r--drivers/input/serio/hyperv-keyboard.c439
-rw-r--r--drivers/input/serio/i8042-io.h2
-rw-r--r--drivers/input/serio/i8042-sparcio.h6
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h74
-rw-r--r--drivers/input/serio/i8042.c60
-rw-r--r--drivers/input/serio/i8042.h24
-rw-r--r--drivers/input/serio/libps2.c3
-rw-r--r--drivers/input/serio/maceps2.c9
-rw-r--r--drivers/input/serio/olpc_apsp.c283
-rw-r--r--drivers/input/serio/pcips2.c23
-rw-r--r--drivers/input/serio/ps2mult.c13
-rw-r--r--drivers/input/serio/q40kbd.c150
-rw-r--r--drivers/input/serio/rpckbd.c66
-rw-r--r--drivers/input/serio/sa1111ps2.c78
-rw-r--r--drivers/input/serio/serio.c127
-rw-r--r--drivers/input/serio/serio_raw.c282
-rw-r--r--drivers/input/serio/serport.c28
-rw-r--r--drivers/input/serio/xilinx_ps2.c68
30 files changed, 1936 insertions, 559 deletions
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 55f2c2293ec..bc2d47431bd 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -2,7 +2,7 @@
# Input core configuration
#
config SERIO
- tristate "Serial I/O support" if EXPERT || !X86
+ tristate "Serial I/O support"
default y
help
Say Yes here if you have any input device that uses serial I/O to
@@ -16,13 +16,19 @@ 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 EXPERT || !X86
+ tristate "i8042 PC Keyboard controller"
default y
- depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
- (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
+ 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,
@@ -36,6 +42,7 @@ config SERIO_I8042
config SERIO_SERPORT
tristate "Serial port line discipline"
default y
+ 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.
@@ -168,7 +175,7 @@ config SERIO_MACEPS2
module will be called maceps2.
config SERIO_LIBPS2
- tristate "PS/2 driver library" if EXPERT
+ 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
@@ -204,6 +211,7 @@ config SERIO_XILINX_XPS_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.
@@ -234,4 +242,43 @@ config SERIO_PS2MULT
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 dbbe37616c9..815d874fe72 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -25,3 +25,7 @@ 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
index d363dc4571a..cce69d6b958 100644
--- a/drivers/input/serio/altera_ps2.c
+++ b/drivers/input/serio/altera_ps2.c
@@ -12,7 +12,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/interrupt.h>
@@ -81,7 +80,7 @@ static void altera_ps2_close(struct serio *io)
/*
* Add one device to this driver.
*/
-static int __devinit altera_ps2_probe(struct platform_device *pdev)
+static int altera_ps2_probe(struct platform_device *pdev)
{
struct ps2if *ps2if;
struct serio *serio;
@@ -159,11 +158,10 @@ static int __devinit altera_ps2_probe(struct platform_device *pdev)
/*
* Remove one device from this driver.
*/
-static int __devexit altera_ps2_remove(struct platform_device *pdev)
+static int altera_ps2_remove(struct platform_device *pdev)
{
struct ps2if *ps2if = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
serio_unregister_port(ps2if->io);
free_irq(ps2if->irq, ps2if);
iounmap(ps2if->base);
@@ -177,11 +175,10 @@ static int __devexit altera_ps2_remove(struct platform_device *pdev)
#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);
-#else /* CONFIG_OF */
-#define altera_ps2_match NULL
#endif /* CONFIG_OF */
/*
@@ -189,25 +186,14 @@ MODULE_DEVICE_TABLE(of, altera_ps2_match);
*/
static struct platform_driver altera_ps2_driver = {
.probe = altera_ps2_probe,
- .remove = __devexit_p(altera_ps2_remove),
+ .remove = altera_ps2_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
- .of_match_table = altera_ps2_match,
+ .of_match_table = of_match_ptr(altera_ps2_match),
},
};
-
-static int __init altera_ps2_init(void)
-{
- return platform_driver_register(&altera_ps2_driver);
-}
-module_init(altera_ps2_init);
-
-static void __exit altera_ps2_exit(void)
-{
- platform_driver_unregister(&altera_ps2_driver);
-}
-module_exit(altera_ps2_exit);
+module_platform_driver(altera_ps2_driver);
MODULE_DESCRIPTION("Altera University Program PS2 controller driver");
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
index 12abc50508e..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>
@@ -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,10 @@ 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 __devinit amba_kmi_probe(struct amba_device *dev,
+static int amba_kmi_probe(struct amba_device *dev,
const struct amba_id *id)
{
struct amba_kmi_port *kmi;
@@ -163,12 +163,10 @@ static int __devinit amba_kmi_probe(struct amba_device *dev,
return ret;
}
-static int __devexit amba_kmi_remove(struct amba_device *dev)
+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);
@@ -195,6 +193,8 @@ 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",
@@ -202,22 +202,11 @@ static struct amba_driver ambakmi_driver = {
},
.id_table = amba_kmi_idtable,
.probe = amba_kmi_probe,
- .remove = __devexit_p(amba_kmi_remove),
+ .remove = amba_kmi_remove,
.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
index 4b2a42f9f0b..45887e31242 100644
--- a/drivers/input/serio/ams_delta_serio.c
+++ b/drivers/input/serio/ams_delta_serio.c
@@ -24,9 +24,10 @@
#include <linux/irq.h>
#include <linux/serio.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <asm/mach-types.h>
-#include <plat/board-ams-delta.h>
+#include <mach/board-ams-delta.h>
#include <mach/ams-delta-fiq.h>
@@ -91,8 +92,7 @@ static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id)
static int ams_delta_serio_open(struct serio *serio)
{
/* enable keyboard */
- ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR,
- AMD_DELTA_LATCH2_KEYBRD_PWR);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 1);
return 0;
}
@@ -100,9 +100,32 @@ static int ams_delta_serio_open(struct serio *serio)
static void ams_delta_serio_close(struct serio *serio)
{
/* disable keyboard */
- ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0);
+ 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;
@@ -122,19 +145,12 @@ static int __init ams_delta_serio_init(void)
strlcpy(ams_delta_serio->phys, "GPIO/serio0",
sizeof(ams_delta_serio->phys));
- err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_DATA, "serio-data");
+ err = gpio_request_array(ams_delta_gpios,
+ ARRAY_SIZE(ams_delta_gpios));
if (err) {
- pr_err("ams_delta_serio: Couldn't request gpio pin for data\n");
+ pr_err("ams_delta_serio: Couldn't request gpio pins\n");
goto serio;
}
- gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
-
- err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_CLK, "serio-clock");
- if (err) {
- pr_err("ams_delta_serio: couldn't request gpio pin for clock\n");
- goto gpio_data;
- }
- gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING,
@@ -142,7 +158,7 @@ static int __init ams_delta_serio_init(void)
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_clk;
+ goto gpio;
}
/*
* Since GPIO register handling for keyboard clock pin is performed
@@ -156,10 +172,9 @@ static int __init ams_delta_serio_init(void)
dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name);
return 0;
-gpio_clk:
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
-gpio_data:
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
+gpio:
+ gpio_free_array(ams_delta_gpios,
+ ARRAY_SIZE(ams_delta_gpios));
serio:
kfree(ams_delta_serio);
return err;
@@ -169,8 +184,8 @@ module_init(ams_delta_serio_init);
static void __exit ams_delta_serio_exit(void)
{
serio_unregister_port(ams_delta_serio);
- free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
+ 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
index 6ee8f0ddad5..3290b287ac4 100644
--- a/drivers/input/serio/at32psif.c
+++ b/drivers/input/serio/at32psif.c
@@ -98,9 +98,9 @@ struct psif {
struct serio *io;
void __iomem *regs;
unsigned int irq;
- unsigned int open;
/* Prevent concurrent writes to PSIF THR. */
spinlock_t lock;
+ bool open;
};
static irqreturn_t psif_interrupt(int irq, void *_ptr)
@@ -164,7 +164,7 @@ static int psif_open(struct serio *io)
psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN));
psif_writel(psif, IER, PSIF_BIT(RXRDY));
- psif->open = 1;
+ psif->open = true;
out:
return retval;
}
@@ -173,7 +173,7 @@ static void psif_close(struct serio *io)
{
struct psif *psif = io->port_data;
- psif->open = 0;
+ psif->open = false;
psif_writel(psif, IDR, ~0UL);
psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS));
@@ -314,14 +314,13 @@ static int __exit psif_remove(struct platform_device *pdev)
clk_put(psif->pclk);
kfree(psif);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
-#ifdef CONFIG_PM
-static int psif_suspend(struct platform_device *pdev, pm_message_t state)
+#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) {
@@ -332,8 +331,9 @@ static int psif_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int psif_resume(struct platform_device *pdev)
+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) {
@@ -344,34 +344,21 @@ static int psif_resume(struct platform_device *pdev)
return 0;
}
-#else
-#define psif_suspend NULL
-#define psif_resume NULL
#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,
},
- .suspend = psif_suspend,
- .resume = psif_resume,
};
-static int __init psif_init(void)
-{
- return platform_driver_probe(&psif_driver, psif_probe);
-}
-
-static void __exit psif_exit(void)
-{
- platform_driver_unregister(&psif_driver);
-}
-
-module_init(psif_init);
-module_exit(psif_exit);
+module_platform_driver_probe(psif_driver, psif_probe);
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
+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 85281656724..cfe549d4eaa 100644
--- a/drivers/input/serio/ct82c710.c
+++ b/drivers/input/serio/ct82c710.c
@@ -175,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)
@@ -199,7 +199,7 @@ static int __devinit ct82c710_probe(struct platform_device *dev)
return 0;
}
-static int __devexit ct82c710_remove(struct platform_device *dev)
+static int ct82c710_remove(struct platform_device *dev)
{
serio_unregister_port(ct82c710_port);
@@ -212,7 +212,7 @@ static struct platform_driver ct82c710_driver = {
.owner = THIS_MODULE,
},
.probe = ct82c710_probe,
- .remove = __devexit_p(ct82c710_remove),
+ .remove = ct82c710_remove,
};
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
index 4225f5d6b15..8d9ba0c3827 100644
--- a/drivers/input/serio/gscps2.c
+++ b/drivers/input/serio/gscps2.c
@@ -327,7 +327,7 @@ static void gscps2_close(struct serio *port)
* @return: success/error report
*/
-static int __devinit gscps2_probe(struct parisc_device *dev)
+static int gscps2_probe(struct parisc_device *dev)
{
struct gscps2port *ps2port;
struct serio *serio;
@@ -414,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);
@@ -444,7 +444,7 @@ static struct parisc_driver parisc_ps2_driver = {
.name = "gsc_ps2",
.id_table = gscps2_device_tbl,
.probe = gscps2_probe,
- .remove = __devexit_p(gscps2_remove),
+ .remove = gscps2_remove,
};
static int __init gscps2_init(void)
diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c
index bfd3865d886..65605e4ef3c 100644
--- a/drivers/input/serio/hil_mlc.c
+++ b/drivers/input/serio/hil_mlc.c
@@ -686,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)) {
@@ -1010,8 +1009,6 @@ static int __init hil_mlc_init(void)
static void __exit hil_mlc_exit(void)
{
del_timer_sync(&hil_mlcs_kicker);
-
- tasklet_disable(&hil_mlcs_tasklet);
tasklet_kill(&hil_mlcs_tasklet);
}
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 42206205e4f..852858e5d8d 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -71,7 +71,6 @@
#include <linux/slab.h>
#include <linux/hil.h>
#include <asm/io.h>
-#include <asm/system.h>
/* Machine-specific abstraction */
@@ -105,7 +104,7 @@ EXPORT_SYMBOL(__hp_sdc_enqueue_transaction);
EXPORT_SYMBOL(hp_sdc_enqueue_transaction);
EXPORT_SYMBOL(hp_sdc_dequeue_transaction);
-static unsigned int hp_sdc_disabled;
+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.");
@@ -795,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. */
@@ -879,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;
@@ -985,7 +984,7 @@ 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);
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 5d48bb66aa7..a5eed2ade53 100644
--- a/drivers/input/serio/i8042-io.h
+++ b/drivers/input/serio/i8042-io.h
@@ -76,7 +76,7 @@ static inline int i8042_platform_init(void)
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-sparcio.h b/drivers/input/serio/i8042-sparcio.h
index 395a9af3adc..d6aa4c67dbb 100644
--- a/drivers/input/serio/i8042-sparcio.h
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -49,7 +49,7 @@ static inline void i8042_write_command(int val)
#define OBP_PS2MS_NAME1 "kdmouse"
#define OBP_PS2MS_NAME2 "mouse"
-static int __devinit sparc_i8042_probe(struct platform_device *op)
+static int sparc_i8042_probe(struct platform_device *op)
{
struct device_node *dp = op->dev.of_node;
@@ -80,7 +80,7 @@ static int __devinit sparc_i8042_probe(struct platform_device *op)
return 0;
}
-static int __devexit sparc_i8042_remove(struct platform_device *op)
+static int sparc_i8042_remove(struct platform_device *op)
{
of_iounmap(kbd_res, kbd_iobase, 8);
@@ -102,7 +102,7 @@ static struct platform_driver sparc_i8042_driver = {
.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)
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index bb9f5d31f0d..136b7b204f5 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -177,6 +177,20 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
},
},
{
+ /* 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"),
@@ -321,6 +335,12 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
},
{
.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"),
},
@@ -382,6 +402,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
},
},
{
+ /* 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"),
@@ -431,6 +458,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
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"),
+ },
+ },
{ }
};
@@ -505,6 +539,13 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"),
},
},
+ {
+ /* Lenovo Ideapad U455 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "20046"),
+ },
+ },
{ }
};
@@ -560,6 +601,13 @@ static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = {
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"),
+ },
+ },
{ }
};
@@ -661,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)
@@ -677,6 +736,8 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *
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);
@@ -701,6 +762,8 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *
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;
@@ -724,6 +787,7 @@ static struct pnp_device_id pnp_kbd_devids[] = {
{ .id = "CPQA0D7", .driver_data = 0 },
{ .id = "", },
};
+MODULE_DEVICE_TABLE(pnp, pnp_kbd_devids);
static struct pnp_driver i8042_pnp_kbd_driver = {
.name = "i8042 kbd",
@@ -745,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",
@@ -880,6 +945,7 @@ 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;
@@ -919,6 +985,14 @@ static int __init i8042_platform_init(void)
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 d37a48e099d..3807c3e971c 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -87,6 +87,8 @@ 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"
@@ -223,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)\n",
- 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;
}
/*
@@ -849,7 +856,7 @@ static int __init i8042_check_aux(void)
static int i8042_controller_check(void)
{
- if (i8042_flush() == I8042_BUFFER_SIZE) {
+ if (i8042_flush()) {
pr_err("No controller found\n");
return -ENODEV;
}
@@ -991,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();
@@ -1016,7 +1023,7 @@ static void i8042_controller_reset(void)
* Reset the controller if requested.
*/
- if (i8042_reset)
+ if (i8042_reset || force_reset)
i8042_controller_selftest();
/*
@@ -1031,7 +1038,7 @@ static void i8042_controller_reset(void)
/*
* 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 distingushing panics from "real"
+ * 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
@@ -1139,9 +1146,9 @@ static int i8042_controller_resume(bool force_reset)
* upsetting it.
*/
-static int i8042_pm_reset(struct device *dev)
+static int i8042_pm_suspend(struct device *dev)
{
- i8042_controller_reset();
+ i8042_controller_reset(true);
return 0;
}
@@ -1163,13 +1170,20 @@ static int i8042_pm_thaw(struct device *dev)
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_reset,
+ .suspend = i8042_pm_suspend,
.resume = i8042_pm_resume,
.thaw = i8042_pm_thaw,
.poweroff = i8042_pm_reset,
@@ -1185,7 +1199,7 @@ static const struct dev_pm_ops i8042_pm_ops = {
static void i8042_shutdown(struct platform_device *dev)
{
- i8042_controller_reset();
+ i8042_controller_reset(false);
}
static int __init i8042_create_kbd_port(void)
@@ -1206,6 +1220,8 @@ static int __init i8042_create_kbd_port(void)
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;
@@ -1232,6 +1248,8 @@ static int __init 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);
@@ -1277,7 +1295,7 @@ static void __init i8042_register_ports(void)
}
}
-static void __devexit i8042_unregister_ports(void)
+static void i8042_unregister_ports(void)
{
int i;
@@ -1424,17 +1442,17 @@ static int __init 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;
@@ -1448,7 +1466,7 @@ static struct platform_driver i8042_driver = {
.pm = &i8042_pm_ops,
#endif
},
- .remove = __devexit_p(i8042_remove),
+ .remove = i8042_remove,
.shutdown = i8042_shutdown,
};
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
index 3452708fbe3..fc080beffed 100644
--- a/drivers/input/serio/i8042.h
+++ b/drivers/input/serio/i8042.h
@@ -41,30 +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
-
-/*
* Return codes.
*/
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index 980af94ba9c..75516996db2 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -18,7 +18,6 @@
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/i8042.h>
-#include <linux/init.h>
#include <linux/libps2.h>
#define DRIVER_DESC "PS/2 driver library"
@@ -210,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))
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/pcips2.c b/drivers/input/serio/pcips2.c
index 43494742541..e862c6ea9d9 100644
--- a/drivers/input/serio/pcips2.c
+++ b/drivers/input/serio/pcips2.c
@@ -16,7 +16,6 @@
#include <linux/input.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <asm/io.h>
@@ -127,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;
@@ -176,12 +175,11 @@ 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);
@@ -206,28 +204,17 @@ static const 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
index 15aa81c9f1f..a76fb64f03d 100644
--- a/drivers/input/serio/ps2mult.c
+++ b/drivers/input/serio/ps2mult.c
@@ -304,15 +304,4 @@ static struct serio_driver ps2mult_drv = {
.reconnect = ps2mult_reconnect,
};
-static int __init ps2mult_init(void)
-{
- return serio_register_driver(&ps2mult_drv);
-}
-
-static void __exit ps2mult_exit(void)
-{
- serio_unregister_driver(&ps2mult_drv);
-}
-
-module_init(ps2mult_init);
-module_exit(ps2mult_exit);
+module_serio_driver(ps2mult_drv);
diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c
index 5eb84b3b67f..594256c3855 100644
--- a/drivers/input/serio/q40kbd.c
+++ b/drivers/input/serio/q40kbd.c
@@ -30,7 +30,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/serio.h>
#include <linux/interrupt.h>
#include <linux/err.h>
@@ -44,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);
-static 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;
}
@@ -72,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);
}
/*
@@ -92,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);
@@ -108,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;
}
@@ -146,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 -ENODEV;
-
- 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 7ec3c97dc1b..e462e7791bb 100644
--- a/drivers/input/serio/rpckbd.c
+++ b/drivers/input/serio/rpckbd.c
@@ -29,23 +29,25 @@
#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 <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)
{
while (!(iomd_readb(IOMD_KCTRL) & (1 << 7)))
@@ -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, port);
+ 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,31 +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 d55874e5d1c..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_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 __devinit 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 unsigned int __devinit ps2_test_one(struct ps2if *ps2if,
+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 @@ static unsigned int __devinit ps2_test_one(struct ps2if *ps2if,
* 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 __devinit ps2_test(struct ps2if *ps2if)
+static int ps2_test(struct ps2if *ps2if)
{
unsigned int stat;
int ret = 0;
@@ -220,7 +243,7 @@ static int __devinit ps2_test(struct ps2if *ps2if)
ret = -ENODEV;
}
- sa1111_writel(0, ps2if->base + SA1111_PS2CR);
+ sa1111_writel(0, ps2if->base + PS2CR);
return ret;
}
@@ -228,7 +251,7 @@ static int __devinit ps2_test(struct ps2if *ps2if)
/*
* Add one device to this driver.
*/
-static int __devinit ps2_probe(struct sa1111_dev *dev)
+static int ps2_probe(struct sa1111_dev *dev)
{
struct ps2if *ps2if;
struct serio *serio;
@@ -274,8 +297,8 @@ static int __devinit 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.
@@ -300,8 +323,7 @@ static int __devinit 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);
@@ -312,13 +334,12 @@ static int __devinit ps2_probe(struct sa1111_dev *dev)
/*
* Remove one device from this driver.
*/
-static int __devexit ps2_remove(struct sa1111_dev *dev)
+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);
@@ -332,10 +353,11 @@ static int __devexit 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,
- .remove = __devexit_p(ps2_remove),
+ .remove = ps2_remove,
};
static int __init ps2_init(void)
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index ba70058e2be..b29134de983 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -365,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);
@@ -373,54 +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 const struct attribute_group *serio_device_attr_groups[] = {
- &serio_id_attr_group,
- NULL
-};
-
-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;
@@ -441,7 +418,6 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
} 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;
@@ -475,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)
{
@@ -733,19 +750,20 @@ 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;
@@ -761,14 +779,14 @@ static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char
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)
{
@@ -892,8 +910,6 @@ 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(env, fmt, val); \
@@ -914,22 +930,18 @@ static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)
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);
+ 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, struct kobj_uevent_env *env)
-{
- return -ENODEV;
-}
-
-#endif /* CONFIG_HOTPLUG */
-
#ifdef CONFIG_PM
static int serio_suspend(struct device *dev)
{
@@ -1007,8 +1019,7 @@ 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,
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index b7ba4597f7f..c9a02fe5757 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -9,12 +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>
@@ -33,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;
@@ -49,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) *
@@ -57,9 +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;
+ struct serio_raw_client *client = file->private_data;
- return fasync_helper(fd, file, on, &list->fasync);
+ return fasync_helper(fd, file, on, &client->fasync);
}
static struct serio_raw *serio_raw_locate(int minor)
@@ -77,8 +77,8 @@ 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)
@@ -90,60 +90,61 @@ static int serio_raw_open(struct inode *inode, struct file *file)
goto out;
}
- if (!serio_raw->serio) {
+ if (serio_raw->dead) {
retval = -ENODEV;
goto out;
}
- list = kzalloc(sizeof(struct serio_raw_list), GFP_KERNEL);
- if (!list) {
+ 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);
-
- return 1;
- }
+ struct serio_raw *serio_raw =
+ container_of(kref, struct serio_raw, kref);
- 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_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;
+ struct serio_raw_client *client = file->private_data;
+ struct serio_raw *serio_raw = client->serio_raw;
char uninitialized_var(c);
- ssize_t retval = 0;
+ ssize_t read = 0;
+ int error;
- if (!serio_raw->serio)
- return -ENODEV;
+ for (;;) {
+ if (serio_raw->dead)
+ return -ENODEV;
- if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
- return -EAGAIN;
+ 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;
+ if (count == 0)
+ break;
- if (!serio_raw->serio)
- return -ENODEV;
+ while (read < count && serio_raw_fetch_byte(serio_raw, &c)) {
+ if (put_user(c, buffer++))
+ return -EFAULT;
+ read++;
+ }
- while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
- if (put_user(c, buffer++))
- return -EFAULT;
- retval++;
+ if (read)
+ break;
+
+ 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,60 +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,
- .llseek = noop_llseek,
+ .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);
}
@@ -272,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;
@@ -308,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;
}
@@ -334,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;
}
@@ -345,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);
-
- serio_raw->serio = NULL;
- if (!serio_raw_cleanup(serio_raw))
- wake_up_interruptible(&serio_raw->wait);
+ kref_put(&serio_raw->kref, serio_raw_free);
- mutex_unlock(&serio_raw_mutex);
+ serio_set_drvdata(serio, NULL);
}
static struct serio_device_id serio_raw_serio_ids[] = {
@@ -391,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 8755f5f3ad3..0cb7ef59071 100644
--- a/drivers/input/serio/serport.c
+++ b/drivers/input/serio/serport.c
@@ -124,7 +124,7 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c
{
struct serport *serport = (struct serport*) tty->disc_data;
unsigned long flags;
- unsigned int ch_flags;
+ unsigned int ch_flags = 0;
int i;
spin_lock_irqsave(&serport->lock, flags);
@@ -133,18 +133,20 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c
goto out;
for (i = 0; i < count; i++) {
- switch (fp[i]) {
- case TTY_FRAME:
- ch_flags = SERIO_FRAME;
- break;
-
- case TTY_PARITY:
- ch_flags = SERIO_PARITY;
- break;
-
- default:
- ch_flags = 0;
- break;
+ 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);
diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c
index 80baa53da5b..e6cf52ebad8 100644
--- a/drivers/input/serio/xilinx_ps2.c
+++ b/drivers/input/serio/xilinx_ps2.c
@@ -20,11 +20,11 @@
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <linux/init.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"
@@ -73,7 +73,8 @@ struct xps2data {
spinlock_t lock;
void __iomem *base_address; /* virt. address of control registers */
unsigned int flags;
- struct serio serio; /* serio */
+ struct serio *serio; /* serio */
+ struct device *dev;
};
/************************************/
@@ -119,7 +120,7 @@ static irqreturn_t xps2_interrupt(int irq, void *dev_id)
/* Check which interrupt is active */
if (intr_sr & XPS2_IPIXR_RX_OVF)
- dev_warn(drvdata->serio.dev.parent, "receive overrun error\n");
+ dev_warn(drvdata->dev, "receive overrun error\n");
if (intr_sr & XPS2_IPIXR_RX_ERR)
drvdata->flags |= SERIO_PARITY;
@@ -132,10 +133,10 @@ static irqreturn_t xps2_interrupt(int irq, void *dev_id)
/* Error, if a byte is not received */
if (status) {
- dev_err(drvdata->serio.dev.parent,
+ dev_err(drvdata->dev,
"wrong rcvd byte count (%d)\n", status);
} else {
- serio_interrupt(&drvdata->serio, c, drvdata->flags);
+ serio_interrupt(drvdata->serio, c, drvdata->flags);
drvdata->flags = 0;
}
}
@@ -193,7 +194,7 @@ static int sxps2_open(struct serio *pserio)
error = request_irq(drvdata->irq, &xps2_interrupt, 0,
DRIVER_NAME, drvdata);
if (error) {
- dev_err(drvdata->serio.dev.parent,
+ dev_err(drvdata->dev,
"Couldn't allocate interrupt %d\n", drvdata->irq);
return error;
}
@@ -232,14 +233,14 @@ static void sxps2_close(struct serio *pserio)
* It returns 0, if the driver is bound to the PS/2 device, or a negative
* value if there is an error.
*/
-static int __devinit xps2_of_probe(struct platform_device *ofdev)
+static int xps2_of_probe(struct platform_device *ofdev)
{
- struct resource r_irq; /* Interrupt resources */
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",
@@ -253,21 +254,23 @@ static int __devinit xps2_of_probe(struct platform_device *ofdev)
}
/* Get IRQ for the device */
- if (of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq) == NO_IRQ) {
+ 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);
- if (!drvdata) {
- dev_err(dev, "Couldn't allocate device private record\n");
- return -ENOMEM;
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!drvdata || !serio) {
+ error = -ENOMEM;
+ goto failed1;
}
- dev_set_drvdata(dev, drvdata);
-
spin_lock_init(&drvdata->lock);
- drvdata->irq = r_irq.start;
+ drvdata->irq = irq;
+ drvdata->serio = serio;
+ drvdata->dev = dev;
phys_addr = r_mem.start;
remap_size = resource_size(&r_mem);
@@ -298,7 +301,6 @@ static int __devinit xps2_of_probe(struct platform_device *ofdev)
(unsigned long long)phys_addr, drvdata->base_address,
drvdata->irq);
- serio = &drvdata->serio;
serio->id.type = SERIO_8042;
serio->write = sxps2_write;
serio->open = sxps2_open;
@@ -312,13 +314,14 @@ static int __devinit xps2_of_probe(struct platform_device *ofdev)
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);
- dev_set_drvdata(dev, NULL);
return error;
}
@@ -331,30 +334,27 @@ failed1:
* if the driver module is being unloaded. It frees any resources allocated to
* the device.
*/
-static int __devexit xps2_of_remove(struct platform_device *of_dev)
+static int xps2_of_remove(struct platform_device *of_dev)
{
- struct device *dev = &of_dev->dev;
- struct xps2data *drvdata = dev_get_drvdata(dev);
+ struct xps2data *drvdata = platform_get_drvdata(of_dev);
struct resource r_mem; /* IO mem resources */
- serio_unregister_port(&drvdata->serio);
+ 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(dev, "invalid address\n");
+ dev_err(drvdata->dev, "invalid address\n");
else
release_mem_region(r_mem.start, resource_size(&r_mem));
kfree(drvdata);
- dev_set_drvdata(dev, NULL);
-
return 0;
}
/* Match table for of_platform binding */
-static const struct of_device_id xps2_of_match[] __devinitconst = {
+static const struct of_device_id xps2_of_match[] = {
{ .compatible = "xlnx,xps-ps2-1.00.a", },
{ /* end of list */ },
};
@@ -367,21 +367,9 @@ static struct platform_driver xps2_of_driver = {
.of_match_table = xps2_of_match,
},
.probe = xps2_of_probe,
- .remove = __devexit_p(xps2_of_remove),
+ .remove = xps2_of_remove,
};
-
-static int __init xps2_init(void)
-{
- return platform_driver_register(&xps2_of_driver);
-}
-
-static void __exit xps2_cleanup(void)
-{
- platform_driver_unregister(&xps2_of_driver);
-}
-
-module_init(xps2_init);
-module_exit(xps2_cleanup);
+module_platform_driver(xps2_of_driver);
MODULE_AUTHOR("Xilinx, Inc.");
MODULE_DESCRIPTION("Xilinx XPS PS/2 driver");