19eb9cc93SPascal PAILLET-LME // SPDX-License-Identifier: GPL-2.0 29eb9cc93SPascal PAILLET-LME // Copyright (C) STMicroelectronics 2018 39eb9cc93SPascal PAILLET-LME // Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics. 49eb9cc93SPascal PAILLET-LME 59eb9cc93SPascal PAILLET-LME #include <linux/input.h> 69eb9cc93SPascal PAILLET-LME #include <linux/interrupt.h> 79eb9cc93SPascal PAILLET-LME #include <linux/mfd/stpmic1.h> 89eb9cc93SPascal PAILLET-LME #include <linux/module.h> 99eb9cc93SPascal PAILLET-LME #include <linux/of.h> 109eb9cc93SPascal PAILLET-LME #include <linux/platform_device.h> 119eb9cc93SPascal PAILLET-LME #include <linux/property.h> 129eb9cc93SPascal PAILLET-LME #include <linux/regmap.h> 139eb9cc93SPascal PAILLET-LME 149eb9cc93SPascal PAILLET-LME /** 159eb9cc93SPascal PAILLET-LME * struct stpmic1_onkey - OnKey data 169eb9cc93SPascal PAILLET-LME * @input_dev: pointer to input device 179eb9cc93SPascal PAILLET-LME * @irq_falling: irq that we are hooked on to 189eb9cc93SPascal PAILLET-LME * @irq_rising: irq that we are hooked on to 199eb9cc93SPascal PAILLET-LME */ 209eb9cc93SPascal PAILLET-LME struct stpmic1_onkey { 219eb9cc93SPascal PAILLET-LME struct input_dev *input_dev; 229eb9cc93SPascal PAILLET-LME int irq_falling; 239eb9cc93SPascal PAILLET-LME int irq_rising; 249eb9cc93SPascal PAILLET-LME }; 259eb9cc93SPascal PAILLET-LME 269eb9cc93SPascal PAILLET-LME static irqreturn_t onkey_falling_irq(int irq, void *ponkey) 279eb9cc93SPascal PAILLET-LME { 289eb9cc93SPascal PAILLET-LME struct stpmic1_onkey *onkey = ponkey; 299eb9cc93SPascal PAILLET-LME struct input_dev *input_dev = onkey->input_dev; 309eb9cc93SPascal PAILLET-LME 319eb9cc93SPascal PAILLET-LME input_report_key(input_dev, KEY_POWER, 1); 329eb9cc93SPascal PAILLET-LME pm_wakeup_event(input_dev->dev.parent, 0); 339eb9cc93SPascal PAILLET-LME input_sync(input_dev); 349eb9cc93SPascal PAILLET-LME 359eb9cc93SPascal PAILLET-LME return IRQ_HANDLED; 369eb9cc93SPascal PAILLET-LME } 379eb9cc93SPascal PAILLET-LME 389eb9cc93SPascal PAILLET-LME static irqreturn_t onkey_rising_irq(int irq, void *ponkey) 399eb9cc93SPascal PAILLET-LME { 409eb9cc93SPascal PAILLET-LME struct stpmic1_onkey *onkey = ponkey; 419eb9cc93SPascal PAILLET-LME struct input_dev *input_dev = onkey->input_dev; 429eb9cc93SPascal PAILLET-LME 439eb9cc93SPascal PAILLET-LME input_report_key(input_dev, KEY_POWER, 0); 449eb9cc93SPascal PAILLET-LME pm_wakeup_event(input_dev->dev.parent, 0); 459eb9cc93SPascal PAILLET-LME input_sync(input_dev); 469eb9cc93SPascal PAILLET-LME 479eb9cc93SPascal PAILLET-LME return IRQ_HANDLED; 489eb9cc93SPascal PAILLET-LME } 499eb9cc93SPascal PAILLET-LME 509eb9cc93SPascal PAILLET-LME static int stpmic1_onkey_probe(struct platform_device *pdev) 519eb9cc93SPascal PAILLET-LME { 529eb9cc93SPascal PAILLET-LME struct stpmic1 *pmic = dev_get_drvdata(pdev->dev.parent); 539eb9cc93SPascal PAILLET-LME struct device *dev = &pdev->dev; 549eb9cc93SPascal PAILLET-LME struct input_dev *input_dev; 559eb9cc93SPascal PAILLET-LME struct stpmic1_onkey *onkey; 569eb9cc93SPascal PAILLET-LME unsigned int val, reg = 0; 579eb9cc93SPascal PAILLET-LME int error; 589eb9cc93SPascal PAILLET-LME 599eb9cc93SPascal PAILLET-LME onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL); 609eb9cc93SPascal PAILLET-LME if (!onkey) 619eb9cc93SPascal PAILLET-LME return -ENOMEM; 629eb9cc93SPascal PAILLET-LME 639eb9cc93SPascal PAILLET-LME onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling"); 640bec8b7eSStephen Boyd if (onkey->irq_falling < 0) 659eb9cc93SPascal PAILLET-LME return onkey->irq_falling; 669eb9cc93SPascal PAILLET-LME 679eb9cc93SPascal PAILLET-LME onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising"); 680bec8b7eSStephen Boyd if (onkey->irq_rising < 0) 699eb9cc93SPascal PAILLET-LME return onkey->irq_rising; 709eb9cc93SPascal PAILLET-LME 719eb9cc93SPascal PAILLET-LME if (!device_property_read_u32(dev, "power-off-time-sec", &val)) { 729eb9cc93SPascal PAILLET-LME if (val > 0 && val <= 16) { 739eb9cc93SPascal PAILLET-LME dev_dbg(dev, "power-off-time=%d seconds\n", val); 749eb9cc93SPascal PAILLET-LME reg |= PONKEY_PWR_OFF; 759eb9cc93SPascal PAILLET-LME reg |= ((16 - val) & PONKEY_TURNOFF_TIMER_MASK); 769eb9cc93SPascal PAILLET-LME } else { 779eb9cc93SPascal PAILLET-LME dev_err(dev, "power-off-time-sec out of range\n"); 789eb9cc93SPascal PAILLET-LME return -EINVAL; 799eb9cc93SPascal PAILLET-LME } 809eb9cc93SPascal PAILLET-LME } 819eb9cc93SPascal PAILLET-LME 829eb9cc93SPascal PAILLET-LME if (device_property_present(dev, "st,onkey-clear-cc-flag")) 839eb9cc93SPascal PAILLET-LME reg |= PONKEY_CC_FLAG_CLEAR; 849eb9cc93SPascal PAILLET-LME 859eb9cc93SPascal PAILLET-LME error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR, 869eb9cc93SPascal PAILLET-LME PONKEY_TURNOFF_MASK, reg); 879eb9cc93SPascal PAILLET-LME if (error) { 889eb9cc93SPascal PAILLET-LME dev_err(dev, "PKEY_TURNOFF_CR write failed: %d\n", error); 899eb9cc93SPascal PAILLET-LME return error; 909eb9cc93SPascal PAILLET-LME } 919eb9cc93SPascal PAILLET-LME 929eb9cc93SPascal PAILLET-LME if (device_property_present(dev, "st,onkey-pu-inactive")) { 939eb9cc93SPascal PAILLET-LME error = regmap_update_bits(pmic->regmap, PADS_PULL_CR, 949eb9cc93SPascal PAILLET-LME PONKEY_PU_INACTIVE, 959eb9cc93SPascal PAILLET-LME PONKEY_PU_INACTIVE); 969eb9cc93SPascal PAILLET-LME if (error) { 979eb9cc93SPascal PAILLET-LME dev_err(dev, "ONKEY Pads configuration failed: %d\n", 989eb9cc93SPascal PAILLET-LME error); 999eb9cc93SPascal PAILLET-LME return error; 1009eb9cc93SPascal PAILLET-LME } 1019eb9cc93SPascal PAILLET-LME } 1029eb9cc93SPascal PAILLET-LME 1039eb9cc93SPascal PAILLET-LME input_dev = devm_input_allocate_device(dev); 1049eb9cc93SPascal PAILLET-LME if (!input_dev) { 1059eb9cc93SPascal PAILLET-LME dev_err(dev, "Can't allocate Pwr Onkey Input Device\n"); 1069eb9cc93SPascal PAILLET-LME return -ENOMEM; 1079eb9cc93SPascal PAILLET-LME } 1089eb9cc93SPascal PAILLET-LME 1099eb9cc93SPascal PAILLET-LME input_dev->name = "pmic_onkey"; 1109eb9cc93SPascal PAILLET-LME input_dev->phys = "pmic_onkey/input0"; 1119eb9cc93SPascal PAILLET-LME 1129eb9cc93SPascal PAILLET-LME input_set_capability(input_dev, EV_KEY, KEY_POWER); 1139eb9cc93SPascal PAILLET-LME 1149eb9cc93SPascal PAILLET-LME onkey->input_dev = input_dev; 1159eb9cc93SPascal PAILLET-LME 1169eb9cc93SPascal PAILLET-LME /* interrupt is nested in a thread */ 1179eb9cc93SPascal PAILLET-LME error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL, 1189eb9cc93SPascal PAILLET-LME onkey_falling_irq, IRQF_ONESHOT, 1199eb9cc93SPascal PAILLET-LME dev_name(dev), onkey); 1209eb9cc93SPascal PAILLET-LME if (error) { 1219eb9cc93SPascal PAILLET-LME dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error); 1229eb9cc93SPascal PAILLET-LME return error; 1239eb9cc93SPascal PAILLET-LME } 1249eb9cc93SPascal PAILLET-LME 1259eb9cc93SPascal PAILLET-LME error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL, 1269eb9cc93SPascal PAILLET-LME onkey_rising_irq, IRQF_ONESHOT, 1279eb9cc93SPascal PAILLET-LME dev_name(dev), onkey); 1289eb9cc93SPascal PAILLET-LME if (error) { 1299eb9cc93SPascal PAILLET-LME dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error); 1309eb9cc93SPascal PAILLET-LME return error; 1319eb9cc93SPascal PAILLET-LME } 1329eb9cc93SPascal PAILLET-LME 1339eb9cc93SPascal PAILLET-LME error = input_register_device(input_dev); 1349eb9cc93SPascal PAILLET-LME if (error) { 1359eb9cc93SPascal PAILLET-LME dev_err(dev, "Can't register power button: %d\n", error); 1369eb9cc93SPascal PAILLET-LME return error; 1379eb9cc93SPascal PAILLET-LME } 1389eb9cc93SPascal PAILLET-LME 1399eb9cc93SPascal PAILLET-LME platform_set_drvdata(pdev, onkey); 1409eb9cc93SPascal PAILLET-LME device_init_wakeup(dev, true); 1419eb9cc93SPascal PAILLET-LME 1429eb9cc93SPascal PAILLET-LME return 0; 1439eb9cc93SPascal PAILLET-LME } 1449eb9cc93SPascal PAILLET-LME 1459eb9cc93SPascal PAILLET-LME static int __maybe_unused stpmic1_onkey_suspend(struct device *dev) 1469eb9cc93SPascal PAILLET-LME { 1479eb9cc93SPascal PAILLET-LME struct platform_device *pdev = to_platform_device(dev); 1489eb9cc93SPascal PAILLET-LME struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); 1499eb9cc93SPascal PAILLET-LME 1509eb9cc93SPascal PAILLET-LME if (device_may_wakeup(dev)) { 1519eb9cc93SPascal PAILLET-LME enable_irq_wake(onkey->irq_falling); 1529eb9cc93SPascal PAILLET-LME enable_irq_wake(onkey->irq_rising); 1539eb9cc93SPascal PAILLET-LME } 1549eb9cc93SPascal PAILLET-LME return 0; 1559eb9cc93SPascal PAILLET-LME } 1569eb9cc93SPascal PAILLET-LME 1579eb9cc93SPascal PAILLET-LME static int __maybe_unused stpmic1_onkey_resume(struct device *dev) 1589eb9cc93SPascal PAILLET-LME { 1599eb9cc93SPascal PAILLET-LME struct platform_device *pdev = to_platform_device(dev); 1609eb9cc93SPascal PAILLET-LME struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); 1619eb9cc93SPascal PAILLET-LME 1629eb9cc93SPascal PAILLET-LME if (device_may_wakeup(dev)) { 1639eb9cc93SPascal PAILLET-LME disable_irq_wake(onkey->irq_falling); 1649eb9cc93SPascal PAILLET-LME disable_irq_wake(onkey->irq_rising); 1659eb9cc93SPascal PAILLET-LME } 1669eb9cc93SPascal PAILLET-LME return 0; 1679eb9cc93SPascal PAILLET-LME } 1689eb9cc93SPascal PAILLET-LME 1699eb9cc93SPascal PAILLET-LME static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm, 1709eb9cc93SPascal PAILLET-LME stpmic1_onkey_suspend, 1719eb9cc93SPascal PAILLET-LME stpmic1_onkey_resume); 1729eb9cc93SPascal PAILLET-LME 1739eb9cc93SPascal PAILLET-LME static const struct of_device_id of_stpmic1_onkey_match[] = { 1749eb9cc93SPascal PAILLET-LME { .compatible = "st,stpmic1-onkey" }, 1759eb9cc93SPascal PAILLET-LME { }, 1769eb9cc93SPascal PAILLET-LME }; 1779eb9cc93SPascal PAILLET-LME 1789eb9cc93SPascal PAILLET-LME MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match); 1799eb9cc93SPascal PAILLET-LME 1809eb9cc93SPascal PAILLET-LME static struct platform_driver stpmic1_onkey_driver = { 1819eb9cc93SPascal PAILLET-LME .probe = stpmic1_onkey_probe, 1829eb9cc93SPascal PAILLET-LME .driver = { 1839eb9cc93SPascal PAILLET-LME .name = "stpmic1_onkey", 1849eb9cc93SPascal PAILLET-LME .of_match_table = of_match_ptr(of_stpmic1_onkey_match), 1859eb9cc93SPascal PAILLET-LME .pm = &stpmic1_onkey_pm, 1869eb9cc93SPascal PAILLET-LME }, 1879eb9cc93SPascal PAILLET-LME }; 1889eb9cc93SPascal PAILLET-LME module_platform_driver(stpmic1_onkey_driver); 1899eb9cc93SPascal PAILLET-LME 1909eb9cc93SPascal PAILLET-LME MODULE_DESCRIPTION("Onkey driver for STPMIC1"); 1919eb9cc93SPascal PAILLET-LME MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>"); 1929eb9cc93SPascal PAILLET-LME MODULE_LICENSE("GPL v2"); 193