[PATCH v2 1/2] input: keyboard: Add keys driver for the LPC32xx SoC

Russell King - ARM Linux linux at arm.linux.org.uk
Wed Apr 25 06:50:24 EDT 2012


On Wed, Apr 25, 2012 at 10:59:45AM +0200, Roland Stigge wrote:
> +/*
> + * Key scanner platform configuration structure
> + */
> +struct lpc32XX_kscan_cfg {

Why the upper case names?

> +	u32	matrix_sz;	/* Size of matrix in XxY, ie. 3 = 3x3 */
> +	int	*keymap;	/* Pointer to key map for the scan matrix */
> +	u32	deb_clks;	/* Debounce clocks (based on 32KHz clock) */
> +	u32	scan_delay;	/* Scan delay (based on 32KHz clock) */
> +};
> +
> +struct lpc32xx_kscan_drv {
> +	struct input_dev *input;
> +	struct lpc32XX_kscan_cfg *kscancfg;
> +	struct clk *clk;
> +	void __iomem *kscan_base;
> +	int irq;
> +	u8 lastkeystates[8];
> +};
> +
> +static void lpc32xx_mod_states(struct lpc32xx_kscan_drv *kscandat, int off)
> +{
> +	u8 st, key;
> +	int j, scancode, keycode;
> +
> +	key = (u8)readl(LPC32XX_KS_DATA(kscandat->kscan_base, off));

You don't need a narrowing cast for assignment - it happens automatically.

> +	if (key != kscandat->lastkeystates[off]) {
> +		for (j = 0; j < kscandat->kscancfg->matrix_sz; j++) {
> +			st = key & (1 << j);
> +			if (st != (kscandat->lastkeystates[off] & (1 << j))) {
> +				/* Key state changed, signal an event */
> +				scancode = (int)
> +				    (j * kscandat->kscancfg->matrix_sz) + off;

Why this cast?  j is an int, matrix_sz is a u32 (so unsigned int), and off
is an int too.

What about:
	changed = key ^ kscandat->lastkeystates[off];
	if (changed) {
		for (j = 0; j < kscandat->kscancfg->matrix_sz; j++) {
			if (changed & (1 << j)) {
				scancode = j * kscandat->kscancfg->matrix_sz + off;

I'd also suggest that you treat collections of bits as unsigned integers
rather than signed.  You can use a plain 'unsigned' to declare them
rather than 'unsigned int' if you're worred about too much typing ;)
It makes sense for key, changed, lastkeystates, and scancode to all
be unsigned.

> +				keycode = kscandat->kscancfg->keymap[scancode];
> +				input_report_key(kscandat->input, keycode,
> +						 (st != 0));
> +			}
> +		}
> +
> +		kscandat->lastkeystates[off] = key;
> +	}
> +}
> +
> +static irqreturn_t lpc32xx_kscan_irq(int irq, void *dev_id)
> +{
> +	int i;
> +	struct lpc32xx_kscan_drv *kscandat = (struct lpc32xx_kscan_drv *)dev_id;

No need to cast from void *.  The compiler won't complain.

> +
> +	for (i = 0; i < kscandat->kscancfg->matrix_sz; i++)
> +		lpc32xx_mod_states(kscandat, i);
> +
> +	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
> +
> +	input_sync(kscandat->input);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +#ifdef CONFIG_OF
> +static struct lpc32XX_kscan_cfg *lpc32XX_parse_dt(struct device *dev)
> +{
> +	struct lpc32XX_kscan_cfg *pdata;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *key_np;
> +	int key_count;
> +	int i;
> +
> +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
> +	if (!pdata) {
> +		dev_err(dev, "could not allocate memory for platform data\n");
> +		return NULL;
> +	}
> +
> +	of_property_read_u32(np, "nxp,matrix-size", &pdata->matrix_sz);
> +	of_property_read_u32(np, "nxp,debounce-delay-ms", &pdata->deb_clks);
> +	of_property_read_u32(np, "nxp,scan-delay-ms", &pdata->scan_delay);
> +
> +	if (!pdata->matrix_sz || !pdata->deb_clks || !pdata->scan_delay) {
> +		dev_err(dev,
> +			"matrix size, debounce or scan delay not specified\n");
> +		return NULL;
> +	}
> +
> +	key_count = pdata->matrix_sz * pdata->matrix_sz;
> +	pdata->keymap = devm_kzalloc(dev, sizeof(int) * key_count, GFP_KERNEL);
> +	if (!pdata->keymap) {
> +		dev_err(dev, "could not allocate memory for keymap\n");
> +		return NULL;
> +	}
> +
> +	i = 0;
> +	for_each_child_of_node(np, key_np) {
> +		u32 key_code;
> +		of_property_read_u32(key_np, "linux,code", &key_code);
> +		pdata->keymap[i++] = key_code;
> +	}
> +
> +	return pdata;
> +}
> +#else
> +static struct lpc32XX_kscan_cfg *lpc32XX_parse_dt(struct device *dev)
> +{
> +	return NULL;
> +}
> +#endif
> +
> +static int __devinit lpc32xx_kscan_probe(struct platform_device *pdev)
> +{
> +	struct lpc32xx_kscan_drv *kscandat;
> +	struct resource *res;
> +	int retval, i, keynum;
> +
> +	kscandat = kzalloc(sizeof(struct lpc32xx_kscan_drv), GFP_KERNEL);
> +	if (unlikely(!kscandat)) {

No need to use unlikely() here, this isn't a fast path, so it's better
to let the compiler make the decisions.

> +		dev_err(&pdev->dev, "failed to allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res == NULL) {

	if (!res) { will do here.

> +		dev_err(&pdev->dev, "failed to get platform I/O memory\n");
> +		retval = -EBUSY;
> +		goto out1;
> +	}
> +
> +	kscandat->kscan_base = devm_request_and_ioremap(&pdev->dev, res);
> +	if (kscandat->kscan_base == NULL) {
> +		dev_err(&pdev->dev, "failed to request and remap I/O memory\n");
> +		retval = -EBUSY;
> +		goto out1;
> +	}
> +
> +	/* Get the key scanner clock */
> +	kscandat->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(kscandat->clk)) {
> +		dev_err(&pdev->dev, "failed to get clock\n");
> +		retval = -ENODEV;
> +		goto out1;
> +	}
> +	clk_enable(kscandat->clk);

clk_prepare_enable() ?  What about doing this in the open/release
callbacks from the input device, so when this input device isn't
actually being used, it's not consuming power?

Don't forget that these functions can also fail.

> +
> +	kscandat->irq = platform_get_irq(pdev, 0);
> +	if ((kscandat->irq < 0) || (kscandat->irq >= NR_IRQS)) {
> +		dev_err(&pdev->dev, "failed to get platform irq\n");
> +		retval = -EINVAL;
> +		goto out2;
> +	}
> +	retval = request_irq(kscandat->irq, lpc32xx_kscan_irq,
> +			     0, pdev->name, kscandat);
> +	if (retval) {
> +		dev_err(&pdev->dev, "failed to request irq\n");
> +		goto out2;
> +	}
> +
> +	kscandat->input = input_allocate_device();
> +	if (kscandat->input == NULL) {
> +		dev_err(&pdev->dev, "failed to allocate device\n");
> +		retval = -ENOMEM;
> +		goto out3;
> +	}
> +
> +	if (pdev->dev.of_node)
> +		kscandat->kscancfg = lpc32XX_parse_dt(&pdev->dev);
> +	else
> +		kscandat->kscancfg =
> +			(struct lpc32XX_kscan_cfg *)pdev->dev.platform_data;

Cast not required.

> +	if (!kscandat->kscancfg) {
> +		dev_err(&pdev->dev, "failed to get platform data\n");
> +		retval = -EINVAL;
> +		goto out4;
> +	}
> +
> +	platform_set_drvdata(pdev, kscandat);
> +
> +	/* Setup key input */
> +	kscandat->input->evbit[0]	= BIT_MASK(EV_KEY);
> +	kscandat->input->name		= pdev->name;
> +	kscandat->input->phys		= "matrix-keys/input0";
> +	kscandat->input->dev.parent	=  &pdev->dev;
> +	kscandat->input->id.vendor	= 0x0001;
> +	kscandat->input->id.product	= 0x0001;
> +	kscandat->input->id.version	= 0x0100;
> +	keynum = kscandat->kscancfg->matrix_sz * kscandat->kscancfg->matrix_sz;
> +	for (i = 0; i < keynum; i++)
> +		__set_bit(kscandat->kscancfg->keymap[i],
> +			kscandat->input->keybit);
> +
> +	input_set_capability(kscandat->input, EV_MSC, MSC_SCAN);
> +
> +	retval = input_register_device(kscandat->input);
> +	if (retval) {
> +		dev_err(&pdev->dev, "failed to register input device\n");
> +		goto out4;
> +	}
> +
> +	/* Configure the key scanner */
> +	writel(kscandat->kscancfg->deb_clks,
> +		LPC32XX_KS_DEB(kscandat->kscan_base));
> +	writel(kscandat->kscancfg->scan_delay,
> +		LPC32XX_KS_SCAN_CTL(kscandat->kscan_base));
> +	writel(LPC32XX_KSCAN_FTST_USE32K_CLK,
> +		LPC32XX_KS_FAST_TST(kscandat->kscan_base));
> +	writel(kscandat->kscancfg->matrix_sz,
> +		LPC32XX_KS_MATRIX_DIM(kscandat->kscan_base));
> +	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
> +
> +	return 0;
> +
> +out4:
> +	input_free_device(kscandat->input);
> +out3:
> +	free_irq(kscandat->irq, pdev);
> +out2:

No balancing of the clock enable.

> +	clk_put(kscandat->clk);
> +out1:
> +	kfree(kscandat);
> +
> +	return retval;
> +}
> +
> +static int __devexit lpc32xx_kscan_remove(struct platform_device *pdev)
> +{
> +	struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
> +
> +	free_irq(kscandat->irq, pdev);
> +	input_unregister_device(kscandat->input);

No balancing of clk_enable() ?

> +	clk_put(kscandat->clk);
> +	kfree(kscandat);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int lpc32xx_kscan_suspend(struct platform_device *pdev,
> +	pm_message_t state)
> +{
> +	struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
> +
> +	/* Clear IRQ and disable clock */
> +	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
> +	clk_disable(kscandat->clk);
> +
> +	return 0;
> +}
> +
> +static int lpc32xx_kscan_resume(struct platform_device *pdev)
> +{
> +	struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
> +
> +	/* Enable clock and clear IRQ */
> +	clk_enable(kscandat->clk);
> +	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
> +
> +	return 0;
> +}
> +#else
> +#define lpc32xx_kscan_suspend	NULL
> +#define lpc32xx_kscan_resume	NULL
> +#endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id lpc32xx_kscan_match[] = {
> +	{ .compatible = "nxp,lpc3220-key" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, lpc32xx_kscan_match);
> +#endif
> +
> +static struct platform_driver lpc32xx_kscan_driver = {
> +	.probe		= lpc32xx_kscan_probe,
> +	.remove		= __devexit_p(lpc32xx_kscan_remove),
> +	.suspend	= lpc32xx_kscan_suspend,
> +	.resume		= lpc32xx_kscan_resume,
> +	.driver		= {
> +		.name	= "lpc32xx_keys",
> +		.of_match_table = of_match_ptr(lpc32xx_kscan_match),
> +	}
> +};
> +
> +module_platform_driver(lpc32xx_kscan_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Kevin Wells <kevin.wells at nxp.com>");
> +MODULE_AUTHOR("Roland Stigge <stigge at antcom.de>");
> +MODULE_DESCRIPTION("Key scanner driver for LPC32XX devices");
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



More information about the linux-arm-kernel mailing list