[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20120425105024.GU24211@n2100.arm.linux.org.uk>
Date: Wed, 25 Apr 2012 11:50:24 +0100
From: Russell King - ARM Linux <linux@....linux.org.uk>
To: Roland Stigge <stigge@...com.de>
Cc: dmitry.torokhov@...il.com, axel.lin@...il.com, riyer@...dia.com,
michael.hennerich@...log.com, grant.likely@...retlab.ca,
linux-input@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org, kevin.wells@....com,
srinivas.bakki@....com
Subject: Re: [PATCH v2 1/2] input: keyboard: Add keys driver for the
LPC32xx SoC
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@....com>");
> +MODULE_AUTHOR("Roland Stigge <stigge@...com.de>");
> +MODULE_DESCRIPTION("Key scanner driver for LPC32XX devices");
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@...ts.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists