18c2ecf20Sopenharmony_ci/** 28c2ecf20Sopenharmony_ci * Copyright (C) 2011 Philippe Rétornaz 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Based on twl4030-pwrbutton driver by: 58c2ecf20Sopenharmony_ci * Peter De Schrijver <peter.de-schrijver@nokia.com> 68c2ecf20Sopenharmony_ci * Felipe Balbi <felipe.balbi@nokia.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 98c2ecf20Sopenharmony_ci * Public License. See the file "COPYING" in the main directory of this 108c2ecf20Sopenharmony_ci * archive for more details. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 138c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 148c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 158c2ecf20Sopenharmony_ci * GNU General Public License for more details. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License 188c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software 198c2ecf20Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/errno.h> 258c2ecf20Sopenharmony_ci#include <linux/input.h> 268c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 278c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 288c2ecf20Sopenharmony_ci#include <linux/mfd/mc13783.h> 298c2ecf20Sopenharmony_ci#include <linux/sched.h> 308c2ecf20Sopenharmony_ci#include <linux/slab.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct mc13783_pwrb { 338c2ecf20Sopenharmony_ci struct input_dev *pwr; 348c2ecf20Sopenharmony_ci struct mc13xxx *mc13783; 358c2ecf20Sopenharmony_ci#define MC13783_PWRB_B1_POL_INVERT (1 << 0) 368c2ecf20Sopenharmony_ci#define MC13783_PWRB_B2_POL_INVERT (1 << 1) 378c2ecf20Sopenharmony_ci#define MC13783_PWRB_B3_POL_INVERT (1 << 2) 388c2ecf20Sopenharmony_ci int flags; 398c2ecf20Sopenharmony_ci unsigned short keymap[3]; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define MC13783_REG_INTERRUPT_SENSE_1 5 438c2ecf20Sopenharmony_ci#define MC13783_IRQSENSE1_ONOFD1S (1 << 3) 448c2ecf20Sopenharmony_ci#define MC13783_IRQSENSE1_ONOFD2S (1 << 4) 458c2ecf20Sopenharmony_ci#define MC13783_IRQSENSE1_ONOFD3S (1 << 5) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define MC13783_REG_POWER_CONTROL_2 15 488c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON1BDBNC 4 498c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON2BDBNC 6 508c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON3BDBNC 8 518c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON1BRSTEN (1 << 1) 528c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON2BRSTEN (1 << 2) 538c2ecf20Sopenharmony_ci#define MC13783_POWER_CONTROL_2_ON3BRSTEN (1 << 3) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic irqreturn_t button_irq(int irq, void *_priv) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct mc13783_pwrb *priv = _priv; 588c2ecf20Sopenharmony_ci int val; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci mc13xxx_irq_ack(priv->mc13783, irq); 618c2ecf20Sopenharmony_ci mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci switch (irq) { 648c2ecf20Sopenharmony_ci case MC13783_IRQ_ONOFD1: 658c2ecf20Sopenharmony_ci val = val & MC13783_IRQSENSE1_ONOFD1S ? 1 : 0; 668c2ecf20Sopenharmony_ci if (priv->flags & MC13783_PWRB_B1_POL_INVERT) 678c2ecf20Sopenharmony_ci val ^= 1; 688c2ecf20Sopenharmony_ci input_report_key(priv->pwr, priv->keymap[0], val); 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci case MC13783_IRQ_ONOFD2: 728c2ecf20Sopenharmony_ci val = val & MC13783_IRQSENSE1_ONOFD2S ? 1 : 0; 738c2ecf20Sopenharmony_ci if (priv->flags & MC13783_PWRB_B2_POL_INVERT) 748c2ecf20Sopenharmony_ci val ^= 1; 758c2ecf20Sopenharmony_ci input_report_key(priv->pwr, priv->keymap[1], val); 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci case MC13783_IRQ_ONOFD3: 798c2ecf20Sopenharmony_ci val = val & MC13783_IRQSENSE1_ONOFD3S ? 1 : 0; 808c2ecf20Sopenharmony_ci if (priv->flags & MC13783_PWRB_B3_POL_INVERT) 818c2ecf20Sopenharmony_ci val ^= 1; 828c2ecf20Sopenharmony_ci input_report_key(priv->pwr, priv->keymap[2], val); 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci input_sync(priv->pwr); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return IRQ_HANDLED; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int mc13783_pwrbutton_probe(struct platform_device *pdev) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci const struct mc13xxx_buttons_platform_data *pdata; 948c2ecf20Sopenharmony_ci struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent); 958c2ecf20Sopenharmony_ci struct input_dev *pwr; 968c2ecf20Sopenharmony_ci struct mc13783_pwrb *priv; 978c2ecf20Sopenharmony_ci int err = 0; 988c2ecf20Sopenharmony_ci int reg = 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 1018c2ecf20Sopenharmony_ci if (!pdata) { 1028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing platform data\n"); 1038c2ecf20Sopenharmony_ci return -ENODEV; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci pwr = input_allocate_device(); 1078c2ecf20Sopenharmony_ci if (!pwr) { 1088c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Can't allocate power button\n"); 1098c2ecf20Sopenharmony_ci return -ENOMEM; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 1138c2ecf20Sopenharmony_ci if (!priv) { 1148c2ecf20Sopenharmony_ci err = -ENOMEM; 1158c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Can't allocate power button\n"); 1168c2ecf20Sopenharmony_ci goto free_input_dev; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci reg |= (pdata->b1on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON1BDBNC; 1208c2ecf20Sopenharmony_ci reg |= (pdata->b2on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON2BDBNC; 1218c2ecf20Sopenharmony_ci reg |= (pdata->b3on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON3BDBNC; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci priv->pwr = pwr; 1248c2ecf20Sopenharmony_ci priv->mc13783 = mc13783; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci mc13xxx_lock(mc13783); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) { 1298c2ecf20Sopenharmony_ci priv->keymap[0] = pdata->b1on_key; 1308c2ecf20Sopenharmony_ci if (pdata->b1on_key != KEY_RESERVED) 1318c2ecf20Sopenharmony_ci __set_bit(pdata->b1on_key, pwr->keybit); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (pdata->b1on_flags & MC13783_BUTTON_POL_INVERT) 1348c2ecf20Sopenharmony_ci priv->flags |= MC13783_PWRB_B1_POL_INVERT; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (pdata->b1on_flags & MC13783_BUTTON_RESET_EN) 1378c2ecf20Sopenharmony_ci reg |= MC13783_POWER_CONTROL_2_ON1BRSTEN; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD1, 1408c2ecf20Sopenharmony_ci button_irq, "b1on", priv); 1418c2ecf20Sopenharmony_ci if (err) { 1428c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Can't request irq\n"); 1438c2ecf20Sopenharmony_ci goto free_priv; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) { 1488c2ecf20Sopenharmony_ci priv->keymap[1] = pdata->b2on_key; 1498c2ecf20Sopenharmony_ci if (pdata->b2on_key != KEY_RESERVED) 1508c2ecf20Sopenharmony_ci __set_bit(pdata->b2on_key, pwr->keybit); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (pdata->b2on_flags & MC13783_BUTTON_POL_INVERT) 1538c2ecf20Sopenharmony_ci priv->flags |= MC13783_PWRB_B2_POL_INVERT; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (pdata->b2on_flags & MC13783_BUTTON_RESET_EN) 1568c2ecf20Sopenharmony_ci reg |= MC13783_POWER_CONTROL_2_ON2BRSTEN; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD2, 1598c2ecf20Sopenharmony_ci button_irq, "b2on", priv); 1608c2ecf20Sopenharmony_ci if (err) { 1618c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Can't request irq\n"); 1628c2ecf20Sopenharmony_ci goto free_irq_b1; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) { 1678c2ecf20Sopenharmony_ci priv->keymap[2] = pdata->b3on_key; 1688c2ecf20Sopenharmony_ci if (pdata->b3on_key != KEY_RESERVED) 1698c2ecf20Sopenharmony_ci __set_bit(pdata->b3on_key, pwr->keybit); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (pdata->b3on_flags & MC13783_BUTTON_POL_INVERT) 1728c2ecf20Sopenharmony_ci priv->flags |= MC13783_PWRB_B3_POL_INVERT; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (pdata->b3on_flags & MC13783_BUTTON_RESET_EN) 1758c2ecf20Sopenharmony_ci reg |= MC13783_POWER_CONTROL_2_ON3BRSTEN; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD3, 1788c2ecf20Sopenharmony_ci button_irq, "b3on", priv); 1798c2ecf20Sopenharmony_ci if (err) { 1808c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Can't request irq: %d\n", err); 1818c2ecf20Sopenharmony_ci goto free_irq_b2; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci mc13xxx_reg_rmw(mc13783, MC13783_REG_POWER_CONTROL_2, 0x3FE, reg); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci mc13xxx_unlock(mc13783); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci pwr->name = "mc13783_pwrbutton"; 1908c2ecf20Sopenharmony_ci pwr->phys = "mc13783_pwrbutton/input0"; 1918c2ecf20Sopenharmony_ci pwr->dev.parent = &pdev->dev; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci pwr->keycode = priv->keymap; 1948c2ecf20Sopenharmony_ci pwr->keycodemax = ARRAY_SIZE(priv->keymap); 1958c2ecf20Sopenharmony_ci pwr->keycodesize = sizeof(priv->keymap[0]); 1968c2ecf20Sopenharmony_ci __set_bit(EV_KEY, pwr->evbit); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci err = input_register_device(pwr); 1998c2ecf20Sopenharmony_ci if (err) { 2008c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Can't register power button: %d\n", err); 2018c2ecf20Sopenharmony_ci goto free_irq; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cifree_irq: 2098c2ecf20Sopenharmony_ci mc13xxx_lock(mc13783); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) 2128c2ecf20Sopenharmony_ci mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD3, priv); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cifree_irq_b2: 2158c2ecf20Sopenharmony_ci if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) 2168c2ecf20Sopenharmony_ci mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD2, priv); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cifree_irq_b1: 2198c2ecf20Sopenharmony_ci if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) 2208c2ecf20Sopenharmony_ci mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD1, priv); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cifree_priv: 2238c2ecf20Sopenharmony_ci mc13xxx_unlock(mc13783); 2248c2ecf20Sopenharmony_ci kfree(priv); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cifree_input_dev: 2278c2ecf20Sopenharmony_ci input_free_device(pwr); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return err; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int mc13783_pwrbutton_remove(struct platform_device *pdev) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct mc13783_pwrb *priv = platform_get_drvdata(pdev); 2358c2ecf20Sopenharmony_ci const struct mc13xxx_buttons_platform_data *pdata; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci mc13xxx_lock(priv->mc13783); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) 2428c2ecf20Sopenharmony_ci mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD3, priv); 2438c2ecf20Sopenharmony_ci if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) 2448c2ecf20Sopenharmony_ci mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD2, priv); 2458c2ecf20Sopenharmony_ci if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) 2468c2ecf20Sopenharmony_ci mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD1, priv); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci mc13xxx_unlock(priv->mc13783); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci input_unregister_device(priv->pwr); 2518c2ecf20Sopenharmony_ci kfree(priv); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic struct platform_driver mc13783_pwrbutton_driver = { 2578c2ecf20Sopenharmony_ci .probe = mc13783_pwrbutton_probe, 2588c2ecf20Sopenharmony_ci .remove = mc13783_pwrbutton_remove, 2598c2ecf20Sopenharmony_ci .driver = { 2608c2ecf20Sopenharmony_ci .name = "mc13783-pwrbutton", 2618c2ecf20Sopenharmony_ci }, 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cimodule_platform_driver(mc13783_pwrbutton_driver); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mc13783-pwrbutton"); 2678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC13783 Power Button"); 2688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Philippe Retornaz"); 270