/* * Driver for the PF1550 ON_KEY * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct onkey_drv_data { struct device *dev; struct pf1550_dev *pf1550; int irq; int keycode; int wakeup; struct input_dev *input; }; static struct pf1550_irq_info pf1550_onkey_irqs[] = { { PF1550_ONKEY_IRQ_PUSHI, "release" }, { PF1550_ONKEY_IRQ_1SI, "1S" }, { PF1550_ONKEY_IRQ_2SI, "2S" }, { PF1550_ONKEY_IRQ_3SI, "3S" }, { PF1550_ONKEY_IRQ_4SI, "4S" }, { PF1550_ONKEY_IRQ_8SI, "8S" }, }; static irqreturn_t pf1550_onkey_irq_handler(int irq, void *data) { struct onkey_drv_data *onkey = data; int i, state, irq_type = -1; onkey->irq = irq; for (i = 0; i < ARRAY_SIZE(pf1550_onkey_irqs); i++) if (onkey->irq == pf1550_onkey_irqs[i].virq) irq_type = pf1550_onkey_irqs[i].irq; switch (irq_type) { case PF1550_ONKEY_IRQ_PUSHI: state = 0; break; case PF1550_ONKEY_IRQ_1SI: case PF1550_ONKEY_IRQ_2SI: case PF1550_ONKEY_IRQ_3SI: case PF1550_ONKEY_IRQ_4SI: case PF1550_ONKEY_IRQ_8SI: state = 1; break; default: dev_err(onkey->dev, "onkey interrupt: irq %d occurred\n", irq_type); return IRQ_HANDLED; } input_event(onkey->input, EV_KEY, onkey->keycode, state); input_sync(onkey->input); return IRQ_HANDLED; } static int pf1550_onkey_probe(struct platform_device *pdev) { struct onkey_drv_data *onkey; struct input_dev *input = NULL; struct device_node *np = pdev->dev.of_node; struct pf1550_dev *pf1550 = dev_get_drvdata(pdev->dev.parent); int i, error; if (!np) return -ENODEV; onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL); if (!onkey) return -ENOMEM; if (of_property_read_u32(np, "linux,keycode", &onkey->keycode)) { onkey->keycode = KEY_POWER; dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n"); } onkey->wakeup = of_property_read_bool(np, "wakeup"); input = devm_input_allocate_device(&pdev->dev); if (!input) { dev_err(&pdev->dev, "failed to allocate the input device\n"); return -ENOMEM; } input->name = pdev->name; input->phys = "pf1550-onkey/input0"; input->id.bustype = BUS_HOST; input_set_capability(input, EV_KEY, onkey->keycode); for (i = 0; i < ARRAY_SIZE(pf1550_onkey_irqs); i++) { struct pf1550_irq_info *onkey_irq = &pf1550_onkey_irqs[i]; unsigned int virq = 0; virq = regmap_irq_get_virq(pf1550->irq_data_onkey, onkey_irq->irq); if (!virq) return -EINVAL; onkey_irq->virq = virq; error = devm_request_threaded_irq(&pdev->dev, virq, NULL, pf1550_onkey_irq_handler, IRQF_NO_SUSPEND, onkey_irq->name, onkey); if (error) { dev_err(&pdev->dev, "failed: irq request (IRQ: %d, error :%d)\n", onkey_irq->irq, error); return error; } } error = input_register_device(input); if (error < 0) { dev_err(&pdev->dev, "failed to register input device\n"); input_free_device(input); return error; } onkey->input = input; onkey->pf1550 = pf1550; platform_set_drvdata(pdev, onkey); device_init_wakeup(&pdev->dev, onkey->wakeup); return 0; } static int pf1550_onkey_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct onkey_drv_data *onkey = platform_get_drvdata(pdev); if (!device_may_wakeup(&pdev->dev)) regmap_write(onkey->pf1550->regmap, PF1550_PMIC_REG_ONKEY_INT_MASK0, ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI | ONKEY_IRQ_2SI | ONKEY_IRQ_3SI | ONKEY_IRQ_4SI | ONKEY_IRQ_8SI); else enable_irq_wake(onkey->pf1550->irq); return 0; } static int pf1550_onkey_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct onkey_drv_data *onkey = platform_get_drvdata(pdev); if (!device_may_wakeup(&pdev->dev)) regmap_write(onkey->pf1550->regmap, PF1550_PMIC_REG_ONKEY_INT_MASK0, ~(ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI | ONKEY_IRQ_2SI | ONKEY_IRQ_3SI | ONKEY_IRQ_4SI | ONKEY_IRQ_8SI)); else disable_irq_wake(onkey->pf1550->irq); return 0; } static const struct of_device_id pf1550_onkey_ids[] = { { .compatible = "fsl,pf1550-onkey" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pf1550_onkey_ids); static SIMPLE_DEV_PM_OPS(pf1550_onkey_pm_ops, pf1550_onkey_suspend, pf1550_onkey_resume); static struct platform_driver pf1550_onkey_driver = { .driver = { .name = "pf1550-onkey", .pm = &pf1550_onkey_pm_ops, .of_match_table = pf1550_onkey_ids, }, .probe = pf1550_onkey_probe, }; module_platform_driver(pf1550_onkey_driver); MODULE_AUTHOR("Freescale Semiconductor"); MODULE_DESCRIPTION(" PF1550 onkey Driver"); MODULE_LICENSE("GPL");