linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Daniel Hellstrom <daniel@gaisler.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: linux-input@vger.kernel.org, software@gaisler.com
Subject: Re: [PATCH 1/1] input,serio: support for GRLIB APBPS2 PS/2 Keyboard/Mouse
Date: Thu, 21 Feb 2013 10:49:41 +0100	[thread overview]
Message-ID: <5125EDB5.3070909@gaisler.com> (raw)
In-Reply-To: <20130220184318.GD15152@core.coreip.homeip.net>

On 02/20/2013 07:43 PM, Dmitry Torokhov wrote:
> Hi Daniel,
>
> On Wed, Feb 20, 2013 at 03:41:33PM +0100, Daniel Hellstrom wrote:
>> 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                       |  266 ++++++++++++++++++++
>>   4 files changed, 297 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 4a4e182..d0304c3 100644
>> --- a/drivers/input/serio/Kconfig
>> +++ b/drivers/input/serio/Kconfig
>> @@ -236,6 +236,7 @@ config SERIO_PS2MULT
>>   
>>   config SERIO_ARC_PS2
>>   	tristate "ARC PS/2 support"
>> +	depends on OF
> This looks like an unrelated change.
You are correct, it was meant to end up in SERIO_APBPS2 of course, sorry for that.. will fix for next patch
>>   	help
>>   	  Say Y here if you have an ARC FPGA platform with a PS/2
>>   	  controller in it.
>> @@ -243,4 +244,13 @@ 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"
>> +        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..78b28e1
>> --- /dev/null
>> +++ b/drivers/input/serio/apbps2.c
>> @@ -0,0 +1,266 @@
>> +/*
>> + *  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/init.h>
>> +#include <linux/serio.h>
>> +#include <linux/errno.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/device.h>
>> +#include <linux/delay.h>
>> +#include <linux/slab.h>
>> +#include <linux/err.h>
>> +#include <linux/string.h>
>> +#include <linux/kernel.h>
>> +#include <linux/io.h>
>> +
>> +struct apbps2_regs {
>> +	u32 data;	/* 0x00 */
>> +	u32 status;	/* 0x04 */
>> +	u32 ctrl;	/* 0x08 */
>> +	u32 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)
>> +
>> +/* Register read/write functions */
>> +#define APBPS2_READ(reg) apbps2_read_reg(reg)
>> +#define APBPS2_WRITE(reg, data) apbps2_write_reg(reg, data)
> Why?
>
>> +
>> +struct apbps2_priv {
>> +	struct serio		io;
>> +	struct apbps2_regs	*regs;
>> +	int			irq;
>> +};
>> +
>> +static inline unsigned long apbps2_read_reg(void __iomem *reg)
>> +{
>> +	return ioread32be(reg);
>> +}
>> +
>> +static inline void apbps2_write_reg(void __iomem *reg, unsigned long data)
>> +{
>> +	iowrite32be(data, reg);
> Do we really need these wrappers?

Ok, I will rewrite using io{read,write}32be() directly without wrappers and macros, I though this was the preferred solution.

>
>> +}
>> +
>> +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 = APBPS2_READ(&priv->regs->status)) & APBPS2_STATUS_DR) {
>> +		data = APBPS2_READ(&priv->regs->data);
>> +		rxflags = (status & APBPS2_STATUS_PE) ? SERIO_PARITY : 0;
>> +		rxflags |= (status & APBPS2_STATUS_PE) ? SERIO_FRAME : 0;
>> +
>> +		/* clear error bits? */
>> +		if (rxflags)
>> +			APBPS2_WRITE(&priv->regs->status, 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 ((APBPS2_READ(&priv->regs->status) & APBPS2_STATUS_TF) && tleft--)
>> +		udelay(10);
>> +
>> +	if ((APBPS2_READ(&priv->regs->status) & APBPS2_STATUS_TF) == 0) {
>> +		APBPS2_WRITE(&priv->regs->data, val);
>> +
>> +		APBPS2_WRITE(&priv->regs->ctrl, APBPS2_CTRL_RE |
>> +						APBPS2_CTRL_RI |
>> +						APBPS2_CTRL_TE);
>> +		return 0;
>> +	}
>> +
>> +	return SERIO_TIMEOUT;
> No, it should return negative error code. -EIO for example.
Ok, I will return -ETIMEDOUT instead.
>
>> +}
>> +
>> +static int apbps2_open(struct serio *io)
>> +{
>> +	struct apbps2_priv *priv = io->port_data;
>> +	int err, limit;
>> +	unsigned long tmp;
>> +
>> +	err = devm_request_irq(&io->dev, priv->irq, apbps2_isr, IRQF_SHARED,
>> +								"apbps2", priv);
>> +	if (err) {
>> +		dev_err(&io->dev, "request IRQ%d failed\n", priv->irq);
>> +		return err;
>> +	}
>> +
>> +	/* clear error flags */
>> +	APBPS2_WRITE(&priv->regs->status, 0);
>> +
>> +	/* Clear old data if available (unlikely) */
>> +	limit = 1024;
>> +	while ((APBPS2_READ(&priv->regs->status) & APBPS2_STATUS_DR) && --limit)
>> +		tmp = APBPS2_READ(&priv->regs->data);
>> +
>> +	/* Enable reciever and it's interrupt */
>> +	APBPS2_WRITE(&priv->regs->ctrl, APBPS2_CTRL_RE | APBPS2_CTRL_RI);
>> +
>> +	return 0;
>> +}
>> +
>> +static void apbps2_close(struct serio *io)
>> +{
>> +	struct apbps2_priv *priv = io->port_data;
>> +
>> +	/* stop interrupts at PS/2 HW level */
>> +	APBPS2_WRITE(&priv->regs->ctrl, 0);
>> +
>> +	/* unregister PS/2 interrupt handler */
>> +	devm_free_irq(&io->dev, priv->irq, priv);
> What is the benefit (except for wasting memory) of using
> devm_request_irq()/devm_free_irq() in this fashion?
None.

>
> By the way, I would prefer if request IRQ was done in probe and freeing
> in remove. I know that many existing serio drivers do it in open/close,
> but this is not correct. We shoudl make sure all resources are available
> beforehand.
This has been done to avoid spending time in the APBPS2 ISR when the PS/2 interface is not used, and the interrupt is shared with another hardware.

Ok, I will move it to from open/close to probe/remove, at that point I beleive it actually does makes sens to use devm_request_irq() to help with freeing so I will keep using devm_request_irq() unless 
someone objects.


>
>> +}
>> +
>> +/* Initialize one APBPS2 PS/2 core */
>> +static int apbps2_of_probe(struct platform_device *ofdev)
>> +{
>> +	struct apbps2_priv *priv;
>> +	int len;
>> +	u32 *addr, *freq_hz;
>> +	char *typestr;
>> +	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 */
>> +	priv->irq = ofdev->archdata.irqs[0];
>> +
>> +	/* Get core frequency */
>> +	freq_hz = (u32 *)of_get_property(ofdev->dev.of_node, "freq", &len);
> There is of_property_read_u32 for this.
Ok.
>
>> +	if (!freq_hz) {
>> +		dev_err(&ofdev->dev, "unable to get core frequency\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv->io.open	= apbps2_open;
>> +	priv->io.close	= apbps2_close;
>> +	priv->io.port_data = priv;
>> +
>> +	/* Get keyboard property. If no such property we know it is a mouse */
>> +	addr = (u32 *)of_get_property(ofdev->dev.of_node, "keyboard", &len);
> And for this.
Ok.
>
>> +	if (!addr) {
>> +		priv->io.id.type = SERIO_PS_PSTHRU;
> This is really for pass-through PS/2 ports. Why are even doing this?
> Can't you switch the devices over? atkbd and psmouse drivers will query
> the attached devices and figure out to which they should bind. Both
> ports should declare themselves as SERIO_8042.

This is a good question which I will have to investigate, in our old driver from 2.6.21.1 this was probably the case. For newer designs however, I can see no reason why not to use SERIO_8042 here as well.

>> +		priv->io.write = apbps2_write;
>> +		strlcpy(priv->io.name, "APBPS2 PS/2 Mouse",
>> +						sizeof(priv->io.name));
>> +		strlcpy(priv->io.phys, "APBPS2 PS/2 Mouse",
>> +						sizeof(priv->io.phys));
>> +		typestr = "Mouse";
>> +	} else {
>> +		priv->io.id.type = SERIO_8042;
>> +		priv->io.write = apbps2_write;
>> +		strlcpy(priv->io.name, "APBPS2 PS/2 Keyboard",
>> +						sizeof(priv->io.name));
>> +		strlcpy(priv->io.phys, "APBPS2 PS/2 Keyboard",
>> +						sizeof(priv->io.phys));
>> +		typestr = "Keyboard";
>> +	}
>> +
>> +	dev_info(&ofdev->dev, "%s, irq = %d, base = 0x%p\n",
>> +			typestr, priv->irq, priv->regs);
> dev_dbg() if you need this.
I would prefer to use dev_info() so that it is printed on startup on our embedded systems. I see that there are several other serio drivers that uses dev_info() in a similar fashion.

>
>> +
>> +	/* Set reload register to system freq in kHz/10 */
>> +	APBPS2_WRITE(&priv->regs->reload, *freq_hz / 10000);
>> +
>> +	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);
>> +
>> +	of_iounmap(&of_dev->resource[0], priv->regs,
>> +					resource_size(&of_dev->resource[0]));
> I am pretty sure it messes up allocation with
> devm_request_and_ioremap().

You are correct. I missed to remove that during changing to devm_*.
>
>> +	kfree(priv);
> And this certainly messes up devm_kzalloc(). And where do you unregister
> the ports you created in probe? I am willing to bet module unload was
> never tested.

You are correct again. I will fix this and call serio_unregister_port().

I tested before converting to the use of devm_*, must have screwed it up double up.


>> +
>> +	return 0;
>> +}
>> +
>> +static struct of_device_id apbps2_of_match[] = {
>> +	{
>> +	 .name = "GAISLER_APBPS2",
>> +	 },
> This is weird formatting. Also matching is uually done on compatible
> strings.

This is the format of the SPARC32/LEON AMBA Plug&Play bus, there is not much I can do about that. A LEON is unique in that it has Plug&Play on the lowest level I/O registers. Several driver in the 
kernel uses this nomenclature.

>> +	{
>> +	 .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");
> Thanks.

Thank you very much for your comments, I will repost the patch as soon as I fixed it and retested it.

Thanks,
Daniel

  reply	other threads:[~2013-02-21  9:51 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-02-20 14:41 [PATCH 1/1] input,serio: support for GRLIB APBPS2 PS/2 Keyboard/Mouse Daniel Hellstrom
2013-02-20 18:43 ` Dmitry Torokhov
2013-02-21  9:49   ` Daniel Hellstrom [this message]
2013-02-21 18:03     ` Dmitry Torokhov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5125EDB5.3070909@gaisler.com \
    --to=daniel@gaisler.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=software@gaisler.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).