// SPDX-License-Identifier: GPL-2.0 // Copyright (C) STMicroelectronics 2018 // Author: Pascal Paillet for STMicroelectronics. #include #include #include #include #include #include #include #include /** * struct stpmic1_onkey - OnKey data * @input_dev: pointer to input device * @irq_falling: irq that we are hooked on to * @irq_rising: irq that we are hooked on to */ struct stpmic1_onkey { struct input_dev *input_dev; int irq_falling; int irq_rising; }; static irqreturn_t onkey_falling_irq(int irq, void *ponkey) { struct stpmic1_onkey *onkey = ponkey; struct input_dev *input_dev = onkey->input_dev; input_report_key(input_dev, KEY_POWER, 1); pm_wakeup_event(input_dev->dev.parent, 0); input_sync(input_dev); return IRQ_HANDLED; } static irqreturn_t onkey_rising_irq(int irq, void *ponkey) { struct stpmic1_onkey *onkey = ponkey; struct input_dev *input_dev = onkey->input_dev; input_report_key(input_dev, KEY_POWER, 0); pm_wakeup_event(input_dev->dev.parent, 0); input_sync(input_dev); return IRQ_HANDLED; } static int stpmic1_onkey_probe(struct platform_device *pdev) { struct stpmic1 *pmic = dev_get_drvdata(pdev->dev.parent); struct device *dev = &pdev->dev; struct input_dev *input_dev; struct stpmic1_onkey *onkey; unsigned int val, reg = 0; int error; onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL); if (!onkey) return -ENOMEM; onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling"); if (onkey->irq_falling < 0) return onkey->irq_falling; onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising"); if (onkey->irq_rising < 0) return onkey->irq_rising; if (!device_property_read_u32(dev, "power-off-time-sec", &val)) { if (val > 0 && val <= 16) { dev_dbg(dev, "power-off-time=%d seconds\n", val); reg |= PONKEY_PWR_OFF; reg |= ((16 - val) & PONKEY_TURNOFF_TIMER_MASK); } else { dev_err(dev, "power-off-time-sec out of range\n"); return -EINVAL; } } if (device_property_present(dev, "st,onkey-clear-cc-flag")) reg |= PONKEY_CC_FLAG_CLEAR; error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR, PONKEY_TURNOFF_MASK, reg); if (error) { dev_err(dev, "PKEY_TURNOFF_CR write failed: %d\n", error); return error; } if (device_property_present(dev, "st,onkey-pu-inactive")) { error = regmap_update_bits(pmic->regmap, PADS_PULL_CR, PONKEY_PU_INACTIVE, PONKEY_PU_INACTIVE); if (error) { dev_err(dev, "ONKEY Pads configuration failed: %d\n", error); return error; } } input_dev = devm_input_allocate_device(dev); if (!input_dev) { dev_err(dev, "Can't allocate Pwr Onkey Input Device\n"); return -ENOMEM; } input_dev->name = "pmic_onkey"; input_dev->phys = "pmic_onkey/input0"; input_set_capability(input_dev, EV_KEY, KEY_POWER); onkey->input_dev = input_dev; /* interrupt is nested in a thread */ error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL, onkey_falling_irq, IRQF_ONESHOT, dev_name(dev), onkey); if (error) { dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error); return error; } error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL, onkey_rising_irq, IRQF_ONESHOT, dev_name(dev), onkey); if (error) { dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error); return error; } error = input_register_device(input_dev); if (error) { dev_err(dev, "Can't register power button: %d\n", error); return error; } platform_set_drvdata(pdev, onkey); device_init_wakeup(dev, true); return 0; } static int __maybe_unused stpmic1_onkey_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); if (device_may_wakeup(dev)) { enable_irq_wake(onkey->irq_falling); enable_irq_wake(onkey->irq_rising); } return 0; } static int __maybe_unused stpmic1_onkey_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); if (device_may_wakeup(dev)) { disable_irq_wake(onkey->irq_falling); disable_irq_wake(onkey->irq_rising); } return 0; } static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm, stpmic1_onkey_suspend, stpmic1_onkey_resume); static const struct of_device_id of_stpmic1_onkey_match[] = { { .compatible = "st,stpmic1-onkey" }, { }, }; MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match); static struct platform_driver stpmic1_onkey_driver = { .probe = stpmic1_onkey_probe, .driver = { .name = "stpmic1_onkey", .of_match_table = of_match_ptr(of_stpmic1_onkey_match), .pm = &stpmic1_onkey_pm, }, }; module_platform_driver(stpmic1_onkey_driver); MODULE_DESCRIPTION("Onkey driver for STPMIC1"); MODULE_AUTHOR("Pascal Paillet "); MODULE_LICENSE("GPL v2");