Commit | Line | Data |
---|---|---|
5fafed3e | 1 | /* |
722dc546 | 2 | * Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver |
5fafed3e FB |
3 | * |
4 | * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ | |
5 | * Author: Felipe Balbi <balbi@ti.com> | |
722dc546 | 6 | * Author: Marcin Niestroj <m.niestroj@grinn-global.com> |
5fafed3e FB |
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> | |
722dc546 | 22 | #include <linux/mfd/tps65217.h> |
5fafed3e FB |
23 | #include <linux/mfd/tps65218.h> |
24 | #include <linux/module.h> | |
25 | #include <linux/of.h> | |
26 | #include <linux/platform_device.h> | |
722dc546 | 27 | #include <linux/regmap.h> |
5fafed3e FB |
28 | #include <linux/slab.h> |
29 | ||
722dc546 MN |
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 { | |
5fafed3e | 49 | struct device *dev; |
722dc546 | 50 | struct regmap *regmap; |
5fafed3e | 51 | struct input_dev *idev; |
722dc546 MN |
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 | { }, | |
5fafed3e | 60 | }; |
722dc546 | 61 | MODULE_DEVICE_TABLE(of, of_tps6521x_pb_match); |
5fafed3e | 62 | |
722dc546 | 63 | static irqreturn_t tps6521x_pb_irq(int irq, void *_pwr) |
5fafed3e | 64 | { |
722dc546 MN |
65 | struct tps6521x_pwrbutton *pwr = _pwr; |
66 | const struct tps6521x_data *tps_data = pwr->data; | |
5fafed3e FB |
67 | unsigned int reg; |
68 | int error; | |
69 | ||
722dc546 | 70 | error = regmap_read(pwr->regmap, tps_data->reg_status, ®); |
5fafed3e FB |
71 | if (error) { |
72 | dev_err(pwr->dev, "can't read register: %d\n", error); | |
73 | goto out; | |
74 | } | |
75 | ||
722dc546 | 76 | if (reg & tps_data->pb_mask) { |
5fafed3e FB |
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 | ||
722dc546 | 89 | static int tps6521x_pb_probe(struct platform_device *pdev) |
5fafed3e | 90 | { |
5fafed3e | 91 | struct device *dev = &pdev->dev; |
722dc546 | 92 | struct tps6521x_pwrbutton *pwr; |
5fafed3e | 93 | struct input_dev *idev; |
722dc546 | 94 | const struct of_device_id *match; |
5fafed3e FB |
95 | int error; |
96 | int irq; | |
97 | ||
722dc546 MN |
98 | match = of_match_node(of_tps6521x_pb_match, pdev->dev.of_node); |
99 | if (!match) | |
100 | return -ENXIO; | |
101 | ||
5fafed3e FB |
102 | pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); |
103 | if (!pwr) | |
104 | return -ENOMEM; | |
105 | ||
722dc546 MN |
106 | pwr->data = match->data; |
107 | ||
5fafed3e FB |
108 | idev = devm_input_allocate_device(dev); |
109 | if (!idev) | |
110 | return -ENOMEM; | |
111 | ||
722dc546 MN |
112 | idev->name = pwr->data->name; |
113 | snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0", | |
114 | pwr->data->name); | |
115 | idev->phys = pwr->phys; | |
5fafed3e FB |
116 | idev->dev.parent = dev; |
117 | idev->id.bustype = BUS_I2C; | |
118 | ||
119 | input_set_capability(idev, EV_KEY, KEY_POWER); | |
120 | ||
722dc546 | 121 | pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL); |
5fafed3e FB |
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); | |
722dc546 MN |
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, | |
5fafed3e FB |
134 | IRQF_TRIGGER_RISING | |
135 | IRQF_TRIGGER_FALLING | | |
136 | IRQF_ONESHOT, | |
722dc546 | 137 | pwr->data->name, pwr); |
5fafed3e FB |
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 | ||
722dc546 MN |
153 | static struct platform_driver tps6521x_pb_driver = { |
154 | .probe = tps6521x_pb_probe, | |
5fafed3e | 155 | .driver = { |
722dc546 MN |
156 | .name = "tps6521x_pwrbutton", |
157 | .of_match_table = of_tps6521x_pb_match, | |
5fafed3e FB |
158 | }, |
159 | }; | |
722dc546 | 160 | module_platform_driver(tps6521x_pb_driver); |
5fafed3e | 161 | |
722dc546 | 162 | MODULE_DESCRIPTION("TPS6521X Power Button"); |
5fafed3e FB |
163 | MODULE_LICENSE("GPL v2"); |
164 | MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); |