* [PATCH v2] input,serio: support for GRLIB APBPS2 PS/2 Keyboard/Mouse
@ 2013-02-21 13:31 Daniel Hellstrom
2013-02-21 18:14 ` Dmitry Torokhov
0 siblings, 1 reply; 3+ messages in thread
From: Daniel Hellstrom @ 2013-02-21 13:31 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, software
APBPS2 is a PS/2 core part of GRLIB found in SPARC32/LEON
products.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
.../bindings/input/ps2keyb-mouse-apbps2.txt | 20 ++
drivers/input/serio/Kconfig | 10 +
drivers/input/serio/Makefile | 1 +
drivers/input/serio/apbps2.c | 228 ++++++++++++++++++++
4 files changed, 259 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
create mode 100644 drivers/input/serio/apbps2.c
diff --git a/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
new file mode 100644
index 0000000..1553d28
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
@@ -0,0 +1,20 @@
+Aeroflex Gaisler APBPS2 PS/2 Core, supporting Keyboard or Mouse.
+
+The APBPS2 PS/2 core is available in the GRLIB VHDL IP core library.
+
+Note: In the ordinary environment for the APBPS2 core, a LEON SPARC system,
+these properties are built from information in the AMBA plug&play and from
+bootloader settings.
+
+Required properties:
+
+- name : Should be "GAISLER_APBPS2" or "01_060"
+- reg : Address and length of the register set for the device
+- interrupts : Interrupt numbers for this device
+
+Optional properties:
+- keyboard : if present it indicates that a keyboard is connected, if not
+ present the driver will assume that a mouse is connected instead
+
+For further information look in the documentation for the GLIB IP core library:
+http://www.gaisler.com/products/grlib/grip.pdf
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 560c243..4a6bb3d 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -244,4 +244,14 @@ config SERIO_ARC_PS2
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.
+
endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 4b0c8f8..8edb36c 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -26,3 +26,4 @@ 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
diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c
new file mode 100644
index 0000000..9af129d
--- /dev/null
+++ b/drivers/input/serio/apbps2.c
@@ -0,0 +1,228 @@
+/*
+ * linux/drivers/input/serio/apbps2.c
+ *
+ * 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/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 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_PE) ? SERIO_FRAME : 0;
+
+ /* clear error bits? */
+ if (rxflags)
+ iowrite32be(status, &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;
+ }
+ platform_set_drvdata(ofdev, priv);
+
+ /* Find Device Address */
+ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+ priv->regs = devm_request_and_ioremap(&ofdev->dev, res);
+ if (!priv->regs) {
+ dev_err(&ofdev->dev, "io-regs mapping failed\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ /* 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;
+ }
+
+ 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));
+ strlcpy(priv->io->phys, "apbps2", sizeof(priv->io->phys));
+
+ dev_info(&ofdev->dev, "irq = %d, base = 0x%p\n", irq, priv->regs);
+
+ /* Set reload register to system freq in kHz/10 */
+ iowrite32be(freq_hz / 10000, &priv->regs->reload);
+
+ serio_register_port(priv->io);
+
+ 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 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");
--
1.7.0.4
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v2] input,serio: support for GRLIB APBPS2 PS/2 Keyboard/Mouse
2013-02-21 13:31 [PATCH v2] input,serio: support for GRLIB APBPS2 PS/2 Keyboard/Mouse Daniel Hellstrom
@ 2013-02-21 18:14 ` Dmitry Torokhov
2013-02-25 10:11 ` Daniel Hellstrom
0 siblings, 1 reply; 3+ messages in thread
From: Dmitry Torokhov @ 2013-02-21 18:14 UTC (permalink / raw)
To: Daniel Hellstrom; +Cc: linux-input, software
Hi Danilel,
On Thu, Feb 21, 2013 at 02:31:42PM +0100, Daniel Hellstrom wrote:
> APBPS2 is a PS/2 core part of GRLIB found in SPARC32/LEON
> products.
Thank you for making the changes, I have a couple more comments.
>
> Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
> ---
> .../bindings/input/ps2keyb-mouse-apbps2.txt | 20 ++
> drivers/input/serio/Kconfig | 10 +
> drivers/input/serio/Makefile | 1 +
> drivers/input/serio/apbps2.c | 228 ++++++++++++++++++++
> 4 files changed, 259 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
> create mode 100644 drivers/input/serio/apbps2.c
>
> diff --git a/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
> new file mode 100644
> index 0000000..1553d28
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
> @@ -0,0 +1,20 @@
> +Aeroflex Gaisler APBPS2 PS/2 Core, supporting Keyboard or Mouse.
> +
> +The APBPS2 PS/2 core is available in the GRLIB VHDL IP core library.
> +
> +Note: In the ordinary environment for the APBPS2 core, a LEON SPARC system,
> +these properties are built from information in the AMBA plug&play and from
> +bootloader settings.
> +
> +Required properties:
> +
> +- name : Should be "GAISLER_APBPS2" or "01_060"
> +- reg : Address and length of the register set for the device
> +- interrupts : Interrupt numbers for this device
> +
> +Optional properties:
> +- keyboard : if present it indicates that a keyboard is connected, if not
> + present the driver will assume that a mouse is connected instead
> +
> +For further information look in the documentation for the GLIB IP core library:
> +http://www.gaisler.com/products/grlib/grip.pdf
> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> index 560c243..4a6bb3d 100644
> --- a/drivers/input/serio/Kconfig
> +++ b/drivers/input/serio/Kconfig
> @@ -244,4 +244,14 @@ config SERIO_ARC_PS2
> 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.
> +
> endif
> diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
> index 4b0c8f8..8edb36c 100644
> --- a/drivers/input/serio/Makefile
> +++ b/drivers/input/serio/Makefile
> @@ -26,3 +26,4 @@ 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
> diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c
> new file mode 100644
> index 0000000..9af129d
> --- /dev/null
> +++ b/drivers/input/serio/apbps2.c
> @@ -0,0 +1,228 @@
> +/*
> + * linux/drivers/input/serio/apbps2.c
Please drop the file name - this way if we ever need to rename/move file
it will not get in the way.
> + *
> + * 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/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 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_PE) ? SERIO_FRAME : 0;
> +
> + /* clear error bits? */
> + if (rxflags)
> + iowrite32be(status, &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;
> + }
> + platform_set_drvdata(ofdev, priv);
> +
> + /* Find Device Address */
> + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
> + priv->regs = devm_request_and_ioremap(&ofdev->dev, res);
> + if (!priv->regs) {
> + dev_err(&ofdev->dev, "io-regs mapping failed\n");
> + return -EADDRNOTAVAIL;
> + }
Could you please make sure you shut off IRQs in chip here as well,
like yo udo in apbps2_close(), before requesting IRQ?
> +
> + /* 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;
> + }
> +
> + 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));
> + strlcpy(priv->io->phys, "apbps2", sizeof(priv->io->phys));
Phys is supposed to be unique within the system, you may want to use a
counter or some other identifying data for particular port. Or is there
just one PS/2 port in the system?
> +
> + dev_info(&ofdev->dev, "irq = %d, base = 0x%p\n", irq, priv->regs);
> +
> + /* Set reload register to system freq in kHz/10 */
> + iowrite32be(freq_hz / 10000, &priv->regs->reload);
> +
> + serio_register_port(priv->io);
> +
> + 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 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");
> --
> 1.7.0.4
>
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v2] input,serio: support for GRLIB APBPS2 PS/2 Keyboard/Mouse
2013-02-21 18:14 ` Dmitry Torokhov
@ 2013-02-25 10:11 ` Daniel Hellstrom
0 siblings, 0 replies; 3+ messages in thread
From: Daniel Hellstrom @ 2013-02-25 10:11 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, software
Hi Dmitry,
On 02/21/2013 07:14 PM, Dmitry Torokhov wrote:
> Hi Danilel,
>
> On Thu, Feb 21, 2013 at 02:31:42PM +0100, Daniel Hellstrom wrote:
>> APBPS2 is a PS/2 core part of GRLIB found in SPARC32/LEON
>> products.
> Thank you for making the changes, I have a couple more comments.
Thank you for your comments and your time, I appreciate it.
>> Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
>> ---
>> .../bindings/input/ps2keyb-mouse-apbps2.txt | 20 ++
>> drivers/input/serio/Kconfig | 10 +
>> drivers/input/serio/Makefile | 1 +
>> drivers/input/serio/apbps2.c | 228 ++++++++++++++++++++
>> 4 files changed, 259 insertions(+), 0 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
>> create mode 100644 drivers/input/serio/apbps2.c
>>
>> diff --git a/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
>> new file mode 100644
>> index 0000000..1553d28
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt
>> @@ -0,0 +1,20 @@
>> +Aeroflex Gaisler APBPS2 PS/2 Core, supporting Keyboard or Mouse.
>> +
>> +The APBPS2 PS/2 core is available in the GRLIB VHDL IP core library.
>> +
>> +Note: In the ordinary environment for the APBPS2 core, a LEON SPARC system,
>> +these properties are built from information in the AMBA plug&play and from
>> +bootloader settings.
>> +
>> +Required properties:
>> +
>> +- name : Should be "GAISLER_APBPS2" or "01_060"
>> +- reg : Address and length of the register set for the device
>> +- interrupts : Interrupt numbers for this device
>> +
>> +Optional properties:
>> +- keyboard : if present it indicates that a keyboard is connected, if not
>> + present the driver will assume that a mouse is connected instead
>> +
>> +For further information look in the documentation for the GLIB IP core library:
>> +http://www.gaisler.com/products/grlib/grip.pdf
>> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
>> index 560c243..4a6bb3d 100644
>> --- a/drivers/input/serio/Kconfig
>> +++ b/drivers/input/serio/Kconfig
>> @@ -244,4 +244,14 @@ config SERIO_ARC_PS2
>> 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.
>> +
>> endif
>> diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
>> index 4b0c8f8..8edb36c 100644
>> --- a/drivers/input/serio/Makefile
>> +++ b/drivers/input/serio/Makefile
>> @@ -26,3 +26,4 @@ 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
>> diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c
>> new file mode 100644
>> index 0000000..9af129d
>> --- /dev/null
>> +++ b/drivers/input/serio/apbps2.c
>> @@ -0,0 +1,228 @@
>> +/*
>> + * linux/drivers/input/serio/apbps2.c
> Please drop the file name - this way if we ever need to rename/move file
> it will not get in the way.
ok.
>
>> + *
>> + * 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/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 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_PE) ? SERIO_FRAME : 0;
>> +
>> + /* clear error bits? */
>> + if (rxflags)
>> + iowrite32be(status, &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;
>> + }
>> + platform_set_drvdata(ofdev, priv);
>> +
>> + /* Find Device Address */
>> + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
>> + priv->regs = devm_request_and_ioremap(&ofdev->dev, res);
>> + if (!priv->regs) {
>> + dev_err(&ofdev->dev, "io-regs mapping failed\n");
>> + return -EADDRNOTAVAIL;
>> + }
> Could you please make sure you shut off IRQs in chip here as well,
> like yo udo in apbps2_close(), before requesting IRQ?
I will do that, it might not be wise to rely on the boot loader here.
>
>> +
>> + /* 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;
>> + }
>> +
>> + 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));
>> + strlcpy(priv->io->phys, "apbps2", sizeof(priv->io->phys));
> Phys is supposed to be unique within the system, you may want to use a
> counter or some other identifying data for particular port. Or is there
> just one PS/2 port in the system?
Ok, I will use a static counter.
I will resend the patch with the updates.
Thanks,
Daniel
>> +
>> + dev_info(&ofdev->dev, "irq = %d, base = 0x%p\n", irq, priv->regs);
>> +
>> + /* Set reload register to system freq in kHz/10 */
>> + iowrite32be(freq_hz / 10000, &priv->regs->reload);
>> +
>> + serio_register_port(priv->io);
>> +
>> + 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 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");
>> --
>> 1.7.0.4
>>
> Thanks.
>
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2013-02-25 10:12 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-02-21 13:31 [PATCH v2] input,serio: support for GRLIB APBPS2 PS/2 Keyboard/Mouse Daniel Hellstrom
2013-02-21 18:14 ` Dmitry Torokhov
2013-02-25 10:11 ` Daniel Hellstrom
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).