/* * Driver for Power key/Vol Down key on s2mps25 IC by PWRON rising, falling interrupts. * * drivers/input/keyboard/s2mps25_keys.c * S2MPS25 Keyboard Driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #define WAKELOCK_TIME HZ/10 static int force_key_irq_en = 0; struct pmic_button_data { struct pmic_keys_button *button; struct input_dev *input; struct work_struct work; struct delayed_work key_work; struct workqueue_struct *irq_wqueue; bool key_pressed; bool suspended; int irq_key_p; int irq_key_r; }; struct s2mps25_keys_drvdata { struct device *dev; struct s2mps25_platform_data *s2mps25_pdata; const struct pmic_keys_platform_data *pdata; struct input_dev *input; struct pmic_button_data button_data[0]; }; static int s2mps25_keys_wake_lock_timeout(struct device *dev, long timeout) { struct wakeup_source *ws = NULL; if (!dev->power.wakeup) { pr_err("%s: Not register wakeup source\n", __func__); goto err; } ws = dev->power.wakeup; __pm_wakeup_event(ws, jiffies_to_msecs(timeout)); return 0; err: return -1; } static void s2mps25_keys_power_report_event(struct pmic_button_data *bdata) { const struct pmic_keys_button *button = bdata->button; struct input_dev *input = bdata->input; struct s2mps25_keys_drvdata *ddata = input_get_drvdata(input); unsigned int type = button->type ?: EV_KEY; int state = bdata->key_pressed; if (s2mps25_keys_wake_lock_timeout(ddata->dev, WAKELOCK_TIME) < 0) { pr_err("%s: s2mps25_keys_wake_lock_timeout fail\n", __func__); return; } /* Report new key event */ input_event(input, type, button->code, !!state); /* Sync new input event */ input_sync(input); } static void s2mps25_keys_work_func(struct work_struct *work) { struct pmic_button_data *bdata = container_of(work, struct pmic_button_data, key_work.work); s2mps25_keys_power_report_event(bdata); if (bdata->button->wakeup) pm_relax(bdata->input->dev.parent); } static irqreturn_t s2mps25_keys_press_irq_handler(int irq, void *dev_id) { struct s2mps25_keys_drvdata *ddata = dev_id; int i = 0; for (i = 0; i < ddata->pdata->nbuttons; i++) { struct pmic_button_data *bdata = &ddata->button_data[i]; if (bdata->irq_key_p == irq) { bdata->key_pressed = true; if (bdata->button->wakeup) { const struct pmic_keys_button *button = bdata->button; pm_stay_awake(bdata->input->dev.parent); if (bdata->suspended && (button->type == 0 || button->type == EV_KEY)) { /* * Simulate wakeup key press in case the key has * already released by the time we got interrupt * handler to run. */ input_report_key(bdata->input, button->code, 1); } } queue_delayed_work(bdata->irq_wqueue, &bdata->key_work, 0); } } return IRQ_HANDLED; } static irqreturn_t s2mps25_keys_release_irq_handler(int irq, void *dev_id) { struct s2mps25_keys_drvdata *ddata = dev_id; int i = 0; for (i = 0; i < ddata->pdata->nbuttons; i++) { struct pmic_button_data *bdata = &ddata->button_data[i]; if (bdata->irq_key_r == irq) { bdata->key_pressed = false; queue_delayed_work(bdata->irq_wqueue, &bdata->key_work, 0); } } return IRQ_HANDLED; } static void s2mps25_keys_report_state(struct s2mps25_keys_drvdata *ddata) { int i; for (i = 0; i < ddata->pdata->nbuttons; i++) { struct pmic_button_data *bdata = &ddata->button_data[i]; bdata->key_pressed = false; s2mps25_keys_power_report_event(bdata); } } static int s2mps25_keys_open(struct input_dev *input) { struct s2mps25_keys_drvdata *ddata = input_get_drvdata(input); dev_info(ddata->dev, "%s()\n", __func__); s2mps25_keys_report_state(ddata); return 0; } static void s2mps25_keys_close(struct input_dev *input) { struct s2mps25_keys_drvdata *ddata = input_get_drvdata(input); dev_info(ddata->dev, "%s()\n", __func__); } #if IS_ENABLED(CONFIG_OF) static struct pmic_keys_platform_data * s2mps25_keys_get_devtree_pdata(struct s2mps25_dev *iodev) { #define S2MPS25_SUPPORT_KEY_NUM (2) // power, vol-down key struct device *dev = iodev->dev; struct device_node *mfd_np, *key_np, *pp; struct pmic_keys_platform_data *pdata; struct pmic_keys_button *button; int error, nbuttons, i; size_t size; if (!iodev->dev->of_node) { pr_err("%s: error\n", __func__); error = -ENODEV; goto err_out; } mfd_np = iodev->dev->of_node; if (!mfd_np) { pr_err("%s: could not find parent_node\n", __func__); error = -ENODEV; goto err_out; } key_np = of_find_node_by_name(mfd_np, "s2mps25-keys"); if (!key_np) { pr_err("%s: could not find current_node\n", __func__); error = -ENOENT; goto err_out; } nbuttons = of_get_child_count(key_np); if (nbuttons != S2MPS25_SUPPORT_KEY_NUM) { dev_warn(dev, "%s: s2mps25-keys support only two button(%d)\n", __func__, nbuttons); error = -ENODEV; goto err_out; } size = sizeof(*pdata) + nbuttons * sizeof(*button); pdata = devm_kzalloc(dev, size, GFP_KERNEL); if (!pdata) { error = -ENOMEM; goto err_out; } pdata->buttons = (struct pmic_keys_button *)(pdata + 1); pdata->nbuttons = nbuttons; i = 0; for_each_child_of_node(key_np, pp) { button = &pdata->buttons[i++]; if (of_property_read_u32(pp, "linux,code", &button->code)) { error = -EINVAL; goto err_out; } button->desc = of_get_property(pp, "label", NULL); button->wakeup = of_property_read_bool(pp, "wakeup"); if (of_property_read_u32(pp, "linux,input-type", &button->type)) button->type = EV_KEY; if (of_property_read_u32(pp, "force_key_irq_en", &force_key_irq_en)) force_key_irq_en = 0; } return pdata; err_out: return ERR_PTR(error); } static struct of_device_id s2mps25_keys_of_match[] = { { .compatible = "s2mps25-power-keys", }, { }, }; MODULE_DEVICE_TABLE(of, s2mps25_keys_of_match); #else static inline struct pmic_keys_platform_data * s2mps25_keys_get_devtree_pdata(struct s2mps25_dev *iodev) { return ERR_PTR(-ENODEV); } #endif static void s2mps25_remove_key(struct pmic_button_data *bdata) { pr_info("%s()\n", __func__); cancel_delayed_work_sync(&bdata->key_work); } static void s2mps25_keys_force_en_irq(struct s2mps25_keys_drvdata *ddata) { int i; if (!force_key_irq_en) return; pr_info("%s()\n", __func__); for (i = 0; i < ddata->pdata->nbuttons; i++) { struct pmic_button_data *bdata = &ddata->button_data[i]; enable_irq(bdata->irq_key_r); enable_irq(bdata->irq_key_p); } } static int s2mps25_keys_set_interrupt(struct s2mps25_keys_drvdata *ddata, int irq_base) { #define POWER_KEY_IDX (0) #define VOLDN_KEY_IDX (1) struct device *dev = ddata->dev; int ret = 0, i, idx; static char irq_name[4][32] = {0, }; char name[4][32] = {"PWRONR_IRQ", "PWRONP_IRQ", "VOLDNR_IRQ", "VOLDNP_IRQ"}; for (i = 0; i < ddata->pdata->nbuttons; i++) { struct pmic_button_data *bdata = &ddata->button_data[i]; idx = i * 2; if (i == POWER_KEY_IDX) { bdata->irq_key_r = irq_base + S2MPS25_PMIC_IRQ_PWRONR_INT1; bdata->irq_key_p = irq_base + S2MPS25_PMIC_IRQ_PWRONP_INT1; } else if (i == VOLDN_KEY_IDX) { bdata->irq_key_r = irq_base + S2MPS25_PMIC_IRQ_VOLDNR_INT3; bdata->irq_key_p = irq_base + S2MPS25_PMIC_IRQ_VOLDNP_INT3; } snprintf(irq_name[idx], sizeof(irq_name[idx]) - 1, "%s@%s", name[idx], dev_name(ddata->dev)); snprintf(irq_name[idx + 1], sizeof(irq_name[idx + 1]) - 1, "%s@%s", name[idx + 1], dev_name(ddata->dev)); ret = devm_request_threaded_irq(dev, bdata->irq_key_r, NULL, s2mps25_keys_release_irq_handler, 0, irq_name[idx], ddata); if (ret < 0) { dev_err(dev, "%s: fail to request %s: %d: %d\n", __func__, name[idx], bdata->irq_key_r, ret); goto err; } ret = devm_request_threaded_irq(dev, bdata->irq_key_p, NULL, s2mps25_keys_press_irq_handler, 0, irq_name[idx + 1], ddata); if (ret < 0) { dev_err(dev, "%s: fail to request %s: %d: %d\n", __func__, name[idx + 1], bdata->irq_key_p, ret); goto err; } } return 0; err: return -1; } static int s2mps25_keys_set_buttondata(struct s2mps25_keys_drvdata *ddata, struct input_dev *input, bool *wakeup) { int cnt = 0; for (cnt = 0; cnt < ddata->pdata->nbuttons; cnt++) { struct pmic_keys_button *button = &ddata->pdata->buttons[cnt]; struct pmic_button_data *bdata = &ddata->button_data[cnt]; char device_name[32] = {0, }; bdata->input = input; bdata->button = button; if (button->wakeup) *wakeup = 1; /* Dynamic allocation for workqueue name */ snprintf(device_name, sizeof(device_name) - 1, "s2mps25-keys-wq%d@%s", cnt, dev_name(ddata->dev)); bdata->irq_wqueue = create_singlethread_workqueue(device_name); if (!bdata->irq_wqueue) { pr_err("%s: fail to create workqueue\n", __func__); goto err; } INIT_DELAYED_WORK(&bdata->key_work, s2mps25_keys_work_func); input_set_capability(input, button->type ?: EV_KEY, button->code); } return cnt; err: return -1; } static struct s2mps25_keys_drvdata * s2mps25_keys_set_drvdata(struct platform_device *pdev, struct pmic_keys_platform_data *pdata, struct input_dev *input, struct s2mps25_dev *iodev) { struct s2mps25_keys_drvdata *ddata = NULL; struct device *dev = &pdev->dev; size_t size; size = sizeof(*ddata) + pdata->nbuttons * sizeof(struct pmic_button_data); ddata = devm_kzalloc(dev, size, GFP_KERNEL); if (!ddata) { dev_err(dev, "failed to allocate ddata.\n"); return ERR_PTR(-ENOMEM); } ddata->dev = dev; ddata->pdata = pdata; ddata->input = input; platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); return ddata; } static int s2mps25_keys_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct s2mps25_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct pmic_keys_platform_data *pdata = NULL; struct s2mps25_keys_drvdata *ddata = NULL; struct input_dev *input; int ret = 0, count = 0; bool wakeup = 0; pr_info("%s: start\n", __func__); pdata = s2mps25_keys_get_devtree_pdata(iodev); if (IS_ERR(pdata)) return PTR_ERR(pdata); input = devm_input_allocate_device(dev); if (!input) { dev_err(dev, "failed to allocate state\n"); ret = -ENOMEM; goto fail1; } input->name = "sec-pmic-key"; //pdata->name ? : pdev->name; input->phys = "s2mps25-keys/input0"; input->dev.parent = dev; input->open = s2mps25_keys_open; input->close = s2mps25_keys_close; input->id.bustype = BUS_I2C; input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; ddata = s2mps25_keys_set_drvdata(pdev, pdata, input, iodev); if (IS_ERR(ddata)) { pr_err("%s: power_keys_set_drvdata fail\n", __func__); return PTR_ERR(ddata); } ret = s2mps25_keys_set_buttondata(ddata, input, &wakeup); if (ret < 0) { pr_err("%s: s2mps25_keys_set_buttondata fail\n", __func__); goto fail1; } else count = ret; ret = device_init_wakeup(dev, wakeup); if (ret < 0) { pr_err("%s: device_init_wakeup fail(%d)\n", __func__, ret); goto fail1; } ret = s2mps25_keys_set_interrupt(ddata, iodev->pdata->irq_base); if (ret < 0) { pr_err("%s: s2mps25_keys_set_interrupt fail\n", __func__); goto fail1; } ret = input_register_device(input); if (ret) { dev_err(dev, "%s: unable to register input device, error: %d\n", __func__, ret); goto fail2; } s2mps25_keys_force_en_irq(ddata); pr_info("%s: end\n", __func__); return 0; fail2: while (--count >= 0) { struct pmic_button_data *bdata = &ddata->button_data[count]; if (bdata->irq_wqueue) destroy_workqueue(bdata->irq_wqueue); s2mps25_remove_key(bdata); } fail1: return ret; } static int s2mps25_keys_remove(struct platform_device *pdev) { struct s2mps25_keys_drvdata *ddata = platform_get_drvdata(pdev); struct input_dev *input = ddata->input; int i; device_init_wakeup(&pdev->dev, false); for (i = 0; i < ddata->pdata->nbuttons; i++) { struct pmic_button_data *bdata = &ddata->button_data[i]; if (bdata->irq_wqueue) destroy_workqueue(bdata->irq_wqueue); s2mps25_remove_key(bdata); } input_unregister_device(input); return 0; } #if IS_ENABLED(CONFIG_PM_SLEEP) static int s2mps25_keys_suspend(struct device *dev) { struct s2mps25_keys_drvdata *ddata = dev_get_drvdata(dev); int i; if (device_may_wakeup(dev)) { for (i = 0; i < ddata->pdata->nbuttons; i++) { struct pmic_button_data *bdata = &ddata->button_data[i]; bdata->suspended = true; } } return 0; } static int s2mps25_keys_resume(struct device *dev) { struct s2mps25_keys_drvdata *ddata = dev_get_drvdata(dev); int i; if (device_may_wakeup(dev)) { for (i = 0; i < ddata->pdata->nbuttons; i++) { struct pmic_button_data *bdata = &ddata->button_data[i]; bdata->suspended = false; } } return 0; } #endif static SIMPLE_DEV_PM_OPS(s2mps25_keys_pm_ops, s2mps25_keys_suspend, s2mps25_keys_resume); static struct platform_driver s2mps25_keys_device_driver = { .probe = s2mps25_keys_probe, .remove = s2mps25_keys_remove, .driver = { .name = "s2mps25-power-keys", .owner = THIS_MODULE, .pm = &s2mps25_keys_pm_ops, .of_match_table = of_match_ptr(s2mps25_keys_of_match), } }; module_platform_driver(s2mps25_keys_device_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Samsung Electronics"); MODULE_DESCRIPTION("Keyboard driver for s2mps25"); MODULE_ALIAS("platform:s2mps25 Power/Vol-down key");