1 /* 2 * Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver 3 * 4 * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ 5 * Author: Felipe Balbi <balbi@ti.com> 6 * Author: Marcin Niestroj <m.niestroj@grinn-global.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any 13 * kind, whether express or implied; without even the implied warranty 14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 18 #include <linux/init.h> 19 #include <linux/input.h> 20 #include <linux/interrupt.h> 21 #include <linux/kernel.h> 22 #include <linux/mfd/tps65217.h> 23 #include <linux/mfd/tps65218.h> 24 #include <linux/module.h> 25 #include <linux/of.h> 26 #include <linux/platform_device.h> 27 #include <linux/regmap.h> 28 #include <linux/slab.h> 29 30 struct tps6521x_data { 31 unsigned int reg_status; 32 unsigned int pb_mask; 33 const char *name; 34 }; 35 36 static const struct tps6521x_data tps65217_data = { 37 .reg_status = TPS65217_REG_STATUS, 38 .pb_mask = TPS65217_STATUS_PB, 39 .name = "tps65217_pwrbutton", 40 }; 41 42 static const struct tps6521x_data tps65218_data = { 43 .reg_status = TPS65218_REG_STATUS, 44 .pb_mask = TPS65218_STATUS_PB_STATE, 45 .name = "tps65218_pwrbutton", 46 }; 47 48 struct tps6521x_pwrbutton { 49 struct device *dev; 50 struct regmap *regmap; 51 struct input_dev *idev; 52 const struct tps6521x_data *data; 53 char phys[32]; 54 }; 55 56 static const struct of_device_id of_tps6521x_pb_match[] = { 57 { .compatible = "ti,tps65217-pwrbutton", .data = &tps65217_data }, 58 { .compatible = "ti,tps65218-pwrbutton", .data = &tps65218_data }, 59 { }, 60 }; 61 MODULE_DEVICE_TABLE(of, of_tps6521x_pb_match); 62 63 static irqreturn_t tps6521x_pb_irq(int irq, void *_pwr) 64 { 65 struct tps6521x_pwrbutton *pwr = _pwr; 66 const struct tps6521x_data *tps_data = pwr->data; 67 unsigned int reg; 68 int error; 69 70 error = regmap_read(pwr->regmap, tps_data->reg_status, ®); 71 if (error) { 72 dev_err(pwr->dev, "can't read register: %d\n", error); 73 goto out; 74 } 75 76 if (reg & tps_data->pb_mask) { 77 input_report_key(pwr->idev, KEY_POWER, 1); 78 pm_wakeup_event(pwr->dev, 0); 79 } else { 80 input_report_key(pwr->idev, KEY_POWER, 0); 81 } 82 83 input_sync(pwr->idev); 84 85 out: 86 return IRQ_HANDLED; 87 } 88 89 static int tps6521x_pb_probe(struct platform_device *pdev) 90 { 91 struct device *dev = &pdev->dev; 92 struct tps6521x_pwrbutton *pwr; 93 struct input_dev *idev; 94 const struct of_device_id *match; 95 int error; 96 int irq; 97 98 match = of_match_node(of_tps6521x_pb_match, pdev->dev.of_node); 99 if (!match) 100 return -ENXIO; 101 102 pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); 103 if (!pwr) 104 return -ENOMEM; 105 106 pwr->data = match->data; 107 108 idev = devm_input_allocate_device(dev); 109 if (!idev) 110 return -ENOMEM; 111 112 idev->name = pwr->data->name; 113 snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0", 114 pwr->data->name); 115 idev->phys = pwr->phys; 116 idev->dev.parent = dev; 117 idev->id.bustype = BUS_I2C; 118 119 input_set_capability(idev, EV_KEY, KEY_POWER); 120 121 pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL); 122 pwr->dev = dev; 123 pwr->idev = idev; 124 platform_set_drvdata(pdev, pwr); 125 device_init_wakeup(dev, true); 126 127 irq = platform_get_irq(pdev, 0); 128 if (irq < 0) { 129 dev_err(dev, "No IRQ resource!\n"); 130 return -EINVAL; 131 } 132 133 error = devm_request_threaded_irq(dev, irq, NULL, tps6521x_pb_irq, 134 IRQF_TRIGGER_RISING | 135 IRQF_TRIGGER_FALLING | 136 IRQF_ONESHOT, 137 pwr->data->name, pwr); 138 if (error) { 139 dev_err(dev, "failed to request IRQ #%d: %d\n", 140 irq, error); 141 return error; 142 } 143 144 error= input_register_device(idev); 145 if (error) { 146 dev_err(dev, "Can't register power button: %d\n", error); 147 return error; 148 } 149 150 return 0; 151 } 152 153 static struct platform_driver tps6521x_pb_driver = { 154 .probe = tps6521x_pb_probe, 155 .driver = { 156 .name = "tps6521x_pwrbutton", 157 .of_match_table = of_tps6521x_pb_match, 158 }, 159 }; 160 module_platform_driver(tps6521x_pb_driver); 161 162 MODULE_DESCRIPTION("TPS6521X Power Button"); 163 MODULE_LICENSE("GPL v2"); 164 MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); 165