2019-06-13 17:08:13

by Thomas Bogendoerfer

[permalink] [raw]
Subject: [PATCH v3 0/7] Use MFD framework for SGI IOC3 drivers

SGI IOC3 ASIC includes support for ethernet, PS2 keyboard/mouse,
NIC (number in a can), GPIO and a byte bus. By attaching a
SuperIO chip to it, it also supports serial lines and a parallel
port. The chip is used on a variety of SGI systems with different
configurations. This patchset moves code out of the network driver,
which doesn't belong there, into its new place a MFD driver and
specific platform drivers for the different subfunctions.

Changes in v3:
- use 1-wire subsystem for handling proms
- pci-xtalk driver uses prom information to create PCI subsystem
ids for use in MFD driver
- changed MFD driver to only use static declared mfd_cells
- added IP30 system board setup to MFD driver
- mac address is now read from ioc3-eth driver with nvmem framework

Changes in v2:
- fixed issue in ioc3kbd.c reported by Dmitry Torokhov
- merged IP27 RTC removal and 8250 serial driver addition into
main MFD patch to keep patches bisectable

Thomas Bogendoerfer (7):
nvmem: core: add nvmem_device_find
MIPS: PCI: refactor ioc3 special handling
MIPS: PCI: use information from 1-wire PROM for IOC3 detection
MIPS: SGI-IP27: remove ioc3 ethernet init
mfd: ioc3: Add driver for SGI IOC3 chip
MIPS: SGI-IP27: fix readb/writeb addressing
Input: add IOC3 serio driver

arch/mips/include/asm/mach-ip27/mangle-port.h | 4 +-
arch/mips/include/asm/pci/bridge.h | 1 +
arch/mips/include/asm/sn/ioc3.h | 356 ++---
arch/mips/pci/pci-xtalk-bridge.c | 296 ++--
arch/mips/sgi-ip27/ip27-console.c | 5 +-
arch/mips/sgi-ip27/ip27-init.c | 13 -
arch/mips/sgi-ip27/ip27-timer.c | 20 -
arch/mips/sgi-ip27/ip27-xtalk.c | 38 +-
drivers/input/serio/Kconfig | 10 +
drivers/input/serio/Makefile | 1 +
drivers/input/serio/ioc3kbd.c | 158 ++
drivers/mfd/Kconfig | 13 +
drivers/mfd/Makefile | 1 +
drivers/mfd/ioc3.c | 683 +++++++++
drivers/net/ethernet/sgi/Kconfig | 4 +-
drivers/net/ethernet/sgi/ioc3-eth.c | 1932 ++++++++++---------------
drivers/nvmem/core.c | 62 +-
drivers/rtc/rtc-m48t35.c | 11 +
drivers/tty/serial/8250/8250_ioc3.c | 98 ++
drivers/tty/serial/8250/Kconfig | 11 +
drivers/tty/serial/8250/Makefile | 1 +
include/linux/nvmem-consumer.h | 9 +
22 files changed, 2152 insertions(+), 1575 deletions(-)
create mode 100644 drivers/input/serio/ioc3kbd.c
create mode 100644 drivers/mfd/ioc3.c
create mode 100644 drivers/tty/serial/8250/8250_ioc3.c

--
2.13.7


2019-06-13 17:08:18

by Thomas Bogendoerfer

[permalink] [raw]
Subject: [PATCH v3 1/7] nvmem: core: add nvmem_device_find

nvmem_device_find provides a way to search for nvmem devices with
the help of a match function simlair to bus_find_device.

Signed-off-by: Thomas Bogendoerfer <[email protected]>
---
drivers/nvmem/core.c | 62 ++++++++++++++++++++++--------------------
include/linux/nvmem-consumer.h | 9 ++++++
2 files changed, 41 insertions(+), 30 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index c7892c3da91f..29a1c937b290 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -76,36 +76,18 @@ static struct bus_type nvmem_bus_type = {
.name = "nvmem",
};

+#if IS_ENABLED(CONFIG_OF)
static int of_nvmem_match(struct device *dev, void *nvmem_np)
{
return dev->of_node == nvmem_np;
}
+#endif

-static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np)
+static int nvmem_match_name(struct device *dev, void *data)
{
- struct device *d;
-
- if (!nvmem_np)
- return NULL;
-
- d = bus_find_device(&nvmem_bus_type, NULL, nvmem_np, of_nvmem_match);
-
- if (!d)
- return NULL;
+ const char *name = data;

- return to_nvmem_device(d);
-}
-
-static struct nvmem_device *nvmem_find(const char *name)
-{
- struct device *d;
-
- d = bus_find_device_by_name(&nvmem_bus_type, NULL, name);
-
- if (!d)
- return NULL;
-
- return to_nvmem_device(d);
+ return sysfs_streq(name, dev_name(dev));
}

static void nvmem_cell_drop(struct nvmem_cell *cell)
@@ -537,13 +519,16 @@ int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
}
EXPORT_SYMBOL(devm_nvmem_unregister);

-static struct nvmem_device *__nvmem_device_get(struct device_node *np,
- const char *nvmem_name)
+static struct nvmem_device *__nvmem_device_get(void *data,
+ int (*match)(struct device *dev, void *data))
{
struct nvmem_device *nvmem = NULL;
+ struct device *dev;

mutex_lock(&nvmem_mutex);
- nvmem = np ? of_nvmem_find(np) : nvmem_find(nvmem_name);
+ dev = bus_find_device(&nvmem_bus_type, NULL, data, match);
+ if (dev)
+ nvmem = to_nvmem_device(dev);
mutex_unlock(&nvmem_mutex);
if (!nvmem)
return ERR_PTR(-EPROBE_DEFER);
@@ -592,7 +577,7 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id)
if (!nvmem_np)
return ERR_PTR(-ENOENT);

- return __nvmem_device_get(nvmem_np, NULL);
+ return __nvmem_device_get(nvmem_np, of_nvmem_match);
}
EXPORT_SYMBOL_GPL(of_nvmem_device_get);
#endif
@@ -618,10 +603,26 @@ struct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name)

}

- return __nvmem_device_get(NULL, dev_name);
+ return __nvmem_device_get((void *)dev_name, nvmem_match_name);
}
EXPORT_SYMBOL_GPL(nvmem_device_get);

+/**
+ * nvmem_device_find() - Find nvmem device with matching function
+ *
+ * @data: Data to pass to match function
+ * @match: Callback function to check device
+ *
+ * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
+ * on success.
+ */
+struct nvmem_device *nvmem_device_find(void *data,
+ int (*match)(struct device *dev, void *data))
+{
+ return __nvmem_device_get(data, match);
+}
+EXPORT_SYMBOL_GPL(nvmem_device_find);
+
static int devm_nvmem_device_match(struct device *dev, void *res, void *data)
{
struct nvmem_device **nvmem = res;
@@ -715,7 +716,8 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
if ((strcmp(lookup->dev_id, dev_id) == 0) &&
(strcmp(lookup->con_id, con_id) == 0)) {
/* This is the right entry. */
- nvmem = __nvmem_device_get(NULL, lookup->nvmem_name);
+ nvmem = __nvmem_device_get((void *)lookup->nvmem_name,
+ nvmem_match_name);
if (IS_ERR(nvmem)) {
/* Provider may not be registered yet. */
cell = ERR_CAST(nvmem);
@@ -785,7 +787,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
if (!nvmem_np)
return ERR_PTR(-EINVAL);

- nvmem = __nvmem_device_get(nvmem_np, NULL);
+ nvmem = __nvmem_device_get(nvmem_np, of_nvmem_match);
of_node_put(nvmem_np);
if (IS_ERR(nvmem))
return ERR_CAST(nvmem);
diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h
index 8f8be5b00060..a8249b2dcaf4 100644
--- a/include/linux/nvmem-consumer.h
+++ b/include/linux/nvmem-consumer.h
@@ -89,6 +89,9 @@ void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries,
int nvmem_register_notifier(struct notifier_block *nb);
int nvmem_unregister_notifier(struct notifier_block *nb);

+struct nvmem_device *nvmem_device_find(void *data,
+ int (*match)(struct device *dev, void *data));
+
#else

static inline struct nvmem_cell *nvmem_cell_get(struct device *dev,
@@ -204,6 +207,12 @@ static inline int nvmem_unregister_notifier(struct notifier_block *nb)
return -EOPNOTSUPP;
}

+static inline struct nvmem_device *nvmem_device_find(void *data,
+ int (*match)(struct device *dev, void *data))
+{
+ return NULL;
+}
+
#endif /* CONFIG_NVMEM */

#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
--
2.13.7

2019-06-13 17:08:35

by Thomas Bogendoerfer

[permalink] [raw]
Subject: [PATCH v3 4/7] MIPS: SGI-IP27: remove ioc3 ethernet init

Removed not needed disabling of ethernet interrupts in IP27 platform code.

Signed-off-by: Thomas Bogendoerfer <[email protected]>
---
arch/mips/sgi-ip27/ip27-init.c | 13 -------------
1 file changed, 13 deletions(-)

diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
index 066b33f50bcc..59d5375c9021 100644
--- a/arch/mips/sgi-ip27/ip27-init.c
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -130,17 +130,6 @@ cnodeid_t get_compact_nodeid(void)
return NASID_TO_COMPACT_NODEID(get_nasid());
}

-static inline void ioc3_eth_init(void)
-{
- struct ioc3 *ioc3;
- nasid_t nid;
-
- nid = get_nasid();
- ioc3 = (struct ioc3 *) KL_CONFIG_CH_CONS_INFO(nid)->memory_base;
-
- ioc3->eier = 0;
-}
-
extern void ip27_reboot_setup(void);

void __init plat_mem_setup(void)
@@ -182,8 +171,6 @@ void __init plat_mem_setup(void)
panic("Kernel compiled for N mode.");
#endif

- ioc3_eth_init();
-
ioport_resource.start = 0;
ioport_resource.end = ~0UL;
set_io_port_base(IO_BASE);
--
2.13.7

2019-06-13 17:08:55

by Thomas Bogendoerfer

[permalink] [raw]
Subject: [PATCH v3 7/7] Input: add IOC3 serio driver

This patch adds a platform driver for supporting keyboard and mouse
interface of SGI IOC3 chips.

Signed-off-by: Thomas Bogendoerfer <[email protected]>
---
drivers/input/serio/Kconfig | 10 +++
drivers/input/serio/Makefile | 1 +
drivers/input/serio/ioc3kbd.c | 158 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 169 insertions(+)
create mode 100644 drivers/input/serio/ioc3kbd.c

diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index f3e18f8ef9ca..373a1646019e 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -165,6 +165,16 @@ config SERIO_MACEPS2
To compile this driver as a module, choose M here: the
module will be called maceps2.

+config SERIO_SGI_IOC3
+ tristate "SGI IOC3 PS/2 controller"
+ depends on SGI_MFD_IOC3
+ help
+ Say Y here if you have an SGI Onyx2, SGI Octane or IOC3 PCI card
+ and you want to attach and use a keyboard, mouse, or both.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ioc3kbd.
+
config SERIO_LIBPS2
tristate "PS/2 driver library"
depends on SERIO_I8042 || SERIO_I8042=n
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 67950a5ccb3f..6d97bad7b844 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o
obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o
obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
+obj-$(CONFIG_SERIO_SGI_IOC3) += ioc3kbd.o
obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
obj-$(CONFIG_SERIO_RAW) += serio_raw.o
obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o
diff --git a/drivers/input/serio/ioc3kbd.c b/drivers/input/serio/ioc3kbd.c
new file mode 100644
index 000000000000..26fcf57465d6
--- /dev/null
+++ b/drivers/input/serio/ioc3kbd.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SGI IOC3 PS/2 controller driver for linux
+ *
+ * Copyright (C) 2019 Thomas Bogendoerfer <[email protected]>
+ *
+ * Based on code Copyright (C) 2005 Stanislaw Skowronek <[email protected]>
+ * Copyright (C) 2009 Johannes Dickgreber <[email protected]>
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/serio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/sn/ioc3.h>
+
+struct ioc3kbd_data {
+ struct ioc3_serioregs __iomem *regs;
+ struct serio *kbd, *aux;
+};
+
+static int ioc3kbd_write(struct serio *dev, u8 val)
+{
+ struct ioc3kbd_data *d = dev->port_data;
+ unsigned long timeout = 0;
+ u32 mask;
+
+ mask = (dev == d->aux) ? KM_CSR_M_WRT_PEND : KM_CSR_K_WRT_PEND;
+ while ((readl(&d->regs->km_csr) & mask) && (timeout < 1000)) {
+ udelay(100);
+ timeout++;
+ }
+
+ if (timeout >= 1000)
+ return -1;
+
+ writel(val, dev == d->aux ? &d->regs->m_wd : &d->regs->k_wd);
+
+ return 0;
+}
+
+static irqreturn_t ioc3kbd_intr(int itq, void *dev_id)
+{
+ struct ioc3kbd_data *d = dev_id;
+ u32 data_k, data_m;
+
+ data_k = readl(&d->regs->k_rd);
+ data_m = readl(&d->regs->m_rd);
+
+ if (data_k & KM_RD_VALID_0)
+ serio_interrupt(d->kbd,
+ (data_k >> KM_RD_DATA_0_SHIFT) & 0xff, 0);
+ if (data_k & KM_RD_VALID_1)
+ serio_interrupt(d->kbd,
+ (data_k >> KM_RD_DATA_1_SHIFT) & 0xff, 0);
+ if (data_k & KM_RD_VALID_2)
+ serio_interrupt(d->kbd,
+ (data_k >> KM_RD_DATA_2_SHIFT) & 0xff, 0);
+ if (data_m & KM_RD_VALID_0)
+ serio_interrupt(d->aux,
+ (data_m >> KM_RD_DATA_0_SHIFT) & 0xff, 0);
+ if (data_m & KM_RD_VALID_1)
+ serio_interrupt(d->aux,
+ (data_m >> KM_RD_DATA_1_SHIFT) & 0xff, 0);
+ if (data_m & KM_RD_VALID_2)
+ serio_interrupt(d->aux,
+ (data_m >> KM_RD_DATA_2_SHIFT) & 0xff, 0);
+
+ return 0;
+}
+
+static int ioc3kbd_probe(struct platform_device *pdev)
+{
+ struct ioc3_serioregs __iomem *regs;
+ struct device *dev = &pdev->dev;
+ struct ioc3kbd_data *d;
+ struct serio *sk, *sa;
+ struct resource *mem;
+ int irq, ret;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENXIO;
+
+ d = devm_kzalloc(&pdev->dev, sizeof(struct ioc3kbd_data), GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ ret = devm_request_irq(&pdev->dev, irq, ioc3kbd_intr, IRQF_SHARED,
+ "ioc3-kbd", d);
+ if (ret) {
+ dev_err(&pdev->dev, "could not request IRQ %d\n", irq);
+ return ret;
+ }
+
+ sk = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!sk)
+ return -ENOMEM;
+
+ sa = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!sa) {
+ kfree(sk);
+ return -ENOMEM;
+ }
+
+ sk->id.type = SERIO_8042;
+ sk->write = ioc3kbd_write;
+ snprintf(sk->name, sizeof(sk->name), "IOC3 keyboard %d", pdev->id);
+ snprintf(sk->phys, sizeof(sk->phys), "ioc3/serio%dkbd", pdev->id);
+ sk->port_data = d;
+ sk->dev.parent = &pdev->dev;
+
+ sa->id.type = SERIO_8042;
+ sa->write = ioc3kbd_write;
+ snprintf(sa->name, sizeof(sa->name), "IOC3 auxiliary %d", pdev->id);
+ snprintf(sa->phys, sizeof(sa->phys), "ioc3/serio%daux", pdev->id);
+ sa->port_data = d;
+ sa->dev.parent = dev;
+
+ d->regs = regs;
+ d->kbd = sk;
+ d->aux = sa;
+
+ platform_set_drvdata(pdev, d);
+ serio_register_port(d->kbd);
+ serio_register_port(d->aux);
+ return 0;
+}
+
+static int ioc3kbd_remove(struct platform_device *pdev)
+{
+ struct ioc3kbd_data *d = platform_get_drvdata(pdev);
+
+ serio_unregister_port(d->kbd);
+ serio_unregister_port(d->aux);
+ return 0;
+}
+
+static struct platform_driver ioc3kbd_driver = {
+ .probe = ioc3kbd_probe,
+ .remove = ioc3kbd_remove,
+ .driver = {
+ .name = "ioc3-kbd",
+ },
+};
+module_platform_driver(ioc3kbd_driver);
+
+MODULE_AUTHOR("Thomas Bogendoerfer <[email protected]>");
+MODULE_DESCRIPTION("SGI IOC3 serio driver");
+MODULE_LICENSE("GPL");
--
2.13.7

2019-07-01 08:31:14

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH v3 7/7] Input: add IOC3 serio driver

Hi Thomas,

On Thu, Jun 13, 2019 at 07:06:33PM +0200, Thomas Bogendoerfer wrote:
> This patch adds a platform driver for supporting keyboard and mouse
> interface of SGI IOC3 chips.
>
> Signed-off-by: Thomas Bogendoerfer <[email protected]>
> ---
> drivers/input/serio/Kconfig | 10 +++
> drivers/input/serio/Makefile | 1 +
> drivers/input/serio/ioc3kbd.c | 158 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 169 insertions(+)
> create mode 100644 drivers/input/serio/ioc3kbd.c
>
> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> index f3e18f8ef9ca..373a1646019e 100644
> --- a/drivers/input/serio/Kconfig
> +++ b/drivers/input/serio/Kconfig
> @@ -165,6 +165,16 @@ config SERIO_MACEPS2
> To compile this driver as a module, choose M here: the
> module will be called maceps2.
>
> +config SERIO_SGI_IOC3
> + tristate "SGI IOC3 PS/2 controller"
> + depends on SGI_MFD_IOC3
> + help
> + Say Y here if you have an SGI Onyx2, SGI Octane or IOC3 PCI card
> + and you want to attach and use a keyboard, mouse, or both.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called ioc3kbd.
> +
> config SERIO_LIBPS2
> tristate "PS/2 driver library"
> depends on SERIO_I8042 || SERIO_I8042=n
> diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
> index 67950a5ccb3f..6d97bad7b844 100644
> --- a/drivers/input/serio/Makefile
> +++ b/drivers/input/serio/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o
> obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
> obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o
> obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
> +obj-$(CONFIG_SERIO_SGI_IOC3) += ioc3kbd.o
> obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
> obj-$(CONFIG_SERIO_RAW) += serio_raw.o
> obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o
> diff --git a/drivers/input/serio/ioc3kbd.c b/drivers/input/serio/ioc3kbd.c
> new file mode 100644
> index 000000000000..26fcf57465d6
> --- /dev/null
> +++ b/drivers/input/serio/ioc3kbd.c
> @@ -0,0 +1,158 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * SGI IOC3 PS/2 controller driver for linux
> + *
> + * Copyright (C) 2019 Thomas Bogendoerfer <[email protected]>
> + *
> + * Based on code Copyright (C) 2005 Stanislaw Skowronek <[email protected]>
> + * Copyright (C) 2009 Johannes Dickgreber <[email protected]>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/serio.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include <asm/sn/ioc3.h>
> +
> +struct ioc3kbd_data {
> + struct ioc3_serioregs __iomem *regs;
> + struct serio *kbd, *aux;
> +};
> +
> +static int ioc3kbd_write(struct serio *dev, u8 val)
> +{
> + struct ioc3kbd_data *d = dev->port_data;
> + unsigned long timeout = 0;
> + u32 mask;
> +
> + mask = (dev == d->aux) ? KM_CSR_M_WRT_PEND : KM_CSR_K_WRT_PEND;
> + while ((readl(&d->regs->km_csr) & mask) && (timeout < 1000)) {
> + udelay(100);
> + timeout++;
> + }
> +
> + if (timeout >= 1000)
> + return -1;

-ETIMEDOUT?

> +
> + writel(val, dev == d->aux ? &d->regs->m_wd : &d->regs->k_wd);

Nit: there are 2 spaces after ?, only one is needed.

> +
> + return 0;
> +}
> +
> +static irqreturn_t ioc3kbd_intr(int itq, void *dev_id)
> +{
> + struct ioc3kbd_data *d = dev_id;
> + u32 data_k, data_m;
> +
> + data_k = readl(&d->regs->k_rd);
> + data_m = readl(&d->regs->m_rd);
> +
> + if (data_k & KM_RD_VALID_0)
> + serio_interrupt(d->kbd,
> + (data_k >> KM_RD_DATA_0_SHIFT) & 0xff, 0);

This is weird formatting, you need one more tab here.

> + if (data_k & KM_RD_VALID_1)
> + serio_interrupt(d->kbd,
> + (data_k >> KM_RD_DATA_1_SHIFT) & 0xff, 0);
> + if (data_k & KM_RD_VALID_2)
> + serio_interrupt(d->kbd,
> + (data_k >> KM_RD_DATA_2_SHIFT) & 0xff, 0);
> + if (data_m & KM_RD_VALID_0)
> + serio_interrupt(d->aux,
> + (data_m >> KM_RD_DATA_0_SHIFT) & 0xff, 0);
> + if (data_m & KM_RD_VALID_1)
> + serio_interrupt(d->aux,
> + (data_m >> KM_RD_DATA_1_SHIFT) & 0xff, 0);
> + if (data_m & KM_RD_VALID_2)
> + serio_interrupt(d->aux,
> + (data_m >> KM_RD_DATA_2_SHIFT) & 0xff, 0);
> +
> + return 0;
> +}
> +
> +static int ioc3kbd_probe(struct platform_device *pdev)
> +{
> + struct ioc3_serioregs __iomem *regs;
> + struct device *dev = &pdev->dev;
> + struct ioc3kbd_data *d;
> + struct serio *sk, *sa;
> + struct resource *mem;
> + int irq, ret;
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + regs = devm_ioremap_resource(&pdev->dev, mem);

We have a brand new helper: devm_platform_ioremap_resource()

> + if (IS_ERR(regs))
> + return PTR_ERR(regs);
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0)
> + return -ENXIO;
> +
> + d = devm_kzalloc(&pdev->dev, sizeof(struct ioc3kbd_data), GFP_KERNEL);

I think we nor prefer deriving type from the pointer:

d = evm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);

> + if (!d)
> + return -ENOMEM;
> +
> + ret = devm_request_irq(&pdev->dev, irq, ioc3kbd_intr, IRQF_SHARED,
> + "ioc3-kbd", d);
> + if (ret) {
> + dev_err(&pdev->dev, "could not request IRQ %d\n", irq);
> + return ret;
> + }

You need to make sure that interrupt will not fire while serio ports are
not yet allocated/registered. Is there a way to inhibit interrupt
generation on controller side?

> +
> + sk = kzalloc(sizeof(struct serio), GFP_KERNEL);

sk = kzalloc(sizeof(*sk), GFP_KERNEL);

> + if (!sk)
> + return -ENOMEM;
> +
> + sa = kzalloc(sizeof(struct serio), GFP_KERNEL);

sa = kzalloc(sizeof(*sa), GFP_KERNEL);

> + if (!sa) {
> + kfree(sk);
> + return -ENOMEM;
> + }
> +
> + sk->id.type = SERIO_8042;
> + sk->write = ioc3kbd_write;
> + snprintf(sk->name, sizeof(sk->name), "IOC3 keyboard %d", pdev->id);
> + snprintf(sk->phys, sizeof(sk->phys), "ioc3/serio%dkbd", pdev->id);
> + sk->port_data = d;
> + sk->dev.parent = &pdev->dev;
> +
> + sa->id.type = SERIO_8042;
> + sa->write = ioc3kbd_write;
> + snprintf(sa->name, sizeof(sa->name), "IOC3 auxiliary %d", pdev->id);
> + snprintf(sa->phys, sizeof(sa->phys), "ioc3/serio%daux", pdev->id);
> + sa->port_data = d;
> + sa->dev.parent = dev;
> +
> + d->regs = regs;
> + d->kbd = sk;
> + d->aux = sa;
> +
> + platform_set_drvdata(pdev, d);
> + serio_register_port(d->kbd);
> + serio_register_port(d->aux);
> + return 0;
> +}
> +
> +static int ioc3kbd_remove(struct platform_device *pdev)
> +{
> + struct ioc3kbd_data *d = platform_get_drvdata(pdev);
> +
> + serio_unregister_port(d->kbd);
> + serio_unregister_port(d->aux);

If you unregister ports while interrupt is registered/enabled you may
get a crash.

> + return 0;
> +}
> +
> +static struct platform_driver ioc3kbd_driver = {
> + .probe = ioc3kbd_probe,
> + .remove = ioc3kbd_remove,
> + .driver = {
> + .name = "ioc3-kbd",
> + },
> +};
> +module_platform_driver(ioc3kbd_driver);
> +
> +MODULE_AUTHOR("Thomas Bogendoerfer <[email protected]>");
> +MODULE_DESCRIPTION("SGI IOC3 serio driver");
> +MODULE_LICENSE("GPL");
> --
> 2.13.7
>

--
Dmitry