aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcin Niestroj <m.niestroj@grinn-global.com>2016-09-09 04:42:02 -0400
committerLee Jones <lee.jones@linaro.org>2016-10-04 10:48:03 -0400
commit6556bdacf646fcaa0586123ba85412de1c8f0eee (patch)
tree08127ad074d74b7b3e66a2abea909171abd06067
parent7e94e51525d3a289ed0c60b57dff36630a8413e4 (diff)
mfd: tps65217: Add support for IRQs
Add support for handling IRQs: power button, AC and USB power state changes. Mask and interrupt bits are shared within one register, which prevents us to use regmap_irq implementation. New irq_domain is created in order to add interrupt handling for each tps65217's subsystem. IRQ resources have been added for charger subsystem to be able to notify about AC and USB state changes. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com> Reviewed-by: Grygorii Strashko <grygorii.strashko@ti.com> Tested-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Lee Jones <lee.jones@linaro.org>
-rw-r--r--drivers/mfd/Kconfig1
-rw-r--r--drivers/mfd/tps65217.c195
-rw-r--r--include/linux/mfd/tps65217.h12
3 files changed, 200 insertions, 8 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 9ac9f6250ba8..a23ef437572b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1232,6 +1232,7 @@ config MFD_TPS65217
1232 depends on I2C 1232 depends on I2C
1233 select MFD_CORE 1233 select MFD_CORE
1234 select REGMAP_I2C 1234 select REGMAP_I2C
1235 select IRQ_DOMAIN
1235 help 1236 help
1236 If you say yes here you get support for the TPS65217 series of 1237 If you say yes here you get support for the TPS65217 series of
1237 Power Management / White LED chips. 1238 Power Management / White LED chips.
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
index 049a6fcac651..41b5d598814d 100644
--- a/drivers/mfd/tps65217.c
+++ b/drivers/mfd/tps65217.c
@@ -15,22 +15,99 @@
15 * GNU General Public License for more details. 15 * GNU General Public License for more details.
16 */ 16 */
17 17
18#include <linux/kernel.h>
19#include <linux/device.h> 18#include <linux/device.h>
20#include <linux/module.h> 19#include <linux/err.h>
21#include <linux/platform_device.h>
22#include <linux/init.h> 20#include <linux/init.h>
21#include <linux/interrupt.h>
23#include <linux/i2c.h> 22#include <linux/i2c.h>
24#include <linux/slab.h> 23#include <linux/irq.h>
25#include <linux/regmap.h> 24#include <linux/irqdomain.h>
26#include <linux/err.h> 25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/of.h> 27#include <linux/of.h>
28#include <linux/of_device.h> 28#include <linux/of_device.h>
29#include <linux/platform_device.h>
30#include <linux/regmap.h>
31#include <linux/slab.h>
29 32
30#include <linux/mfd/core.h> 33#include <linux/mfd/core.h>
31#include <linux/mfd/tps65217.h> 34#include <linux/mfd/tps65217.h>
32 35
33static const struct mfd_cell tps65217s[] = { 36static struct resource charger_resources[] = {
37 DEFINE_RES_IRQ_NAMED(TPS65217_IRQ_AC, "AC"),
38 DEFINE_RES_IRQ_NAMED(TPS65217_IRQ_USB, "USB"),
39};
40
41struct tps65217_irq {
42 int mask;
43 int interrupt;
44};
45
46static const struct tps65217_irq tps65217_irqs[] = {
47 [TPS65217_IRQ_PB] = {
48 .mask = TPS65217_INT_PBM,
49 .interrupt = TPS65217_INT_PBI,
50 },
51 [TPS65217_IRQ_AC] = {
52 .mask = TPS65217_INT_ACM,
53 .interrupt = TPS65217_INT_ACI,
54 },
55 [TPS65217_IRQ_USB] = {
56 .mask = TPS65217_INT_USBM,
57 .interrupt = TPS65217_INT_USBI,
58 },
59};
60
61static void tps65217_irq_lock(struct irq_data *data)
62{
63 struct tps65217 *tps = irq_data_get_irq_chip_data(data);
64
65 mutex_lock(&tps->irq_lock);
66}
67
68static void tps65217_irq_sync_unlock(struct irq_data *data)
69{
70 struct tps65217 *tps = irq_data_get_irq_chip_data(data);
71 int ret;
72
73 ret = tps65217_reg_write(tps, TPS65217_REG_INT, tps->irq_mask,
74 TPS65217_PROTECT_NONE);
75 if (ret != 0)
76 dev_err(tps->dev, "Failed to sync IRQ masks\n");
77
78 mutex_unlock(&tps->irq_lock);
79}
80
81static const inline struct tps65217_irq *
82irq_to_tps65217_irq(struct tps65217 *tps, struct irq_data *data)
83{
84 return &tps65217_irqs[data->hwirq];
85}
86
87static void tps65217_irq_enable(struct irq_data *data)
88{
89 struct tps65217 *tps = irq_data_get_irq_chip_data(data);
90 const struct tps65217_irq *irq_data = irq_to_tps65217_irq(tps, data);
91
92 tps->irq_mask &= ~irq_data->mask;
93}
94
95static void tps65217_irq_disable(struct irq_data *data)
96{
97 struct tps65217 *tps = irq_data_get_irq_chip_data(data);
98 const struct tps65217_irq *irq_data = irq_to_tps65217_irq(tps, data);
99
100 tps->irq_mask |= irq_data->mask;
101}
102
103static struct irq_chip tps65217_irq_chip = {
104 .irq_bus_lock = tps65217_irq_lock,
105 .irq_bus_sync_unlock = tps65217_irq_sync_unlock,
106 .irq_enable = tps65217_irq_enable,
107 .irq_disable = tps65217_irq_disable,
108};
109
110static struct mfd_cell tps65217s[] = {
34 { 111 {
35 .name = "tps65217-pmic", 112 .name = "tps65217-pmic",
36 .of_compatible = "ti,tps65217-pmic", 113 .of_compatible = "ti,tps65217-pmic",
@@ -41,10 +118,90 @@ static const struct mfd_cell tps65217s[] = {
41 }, 118 },
42 { 119 {
43 .name = "tps65217-charger", 120 .name = "tps65217-charger",
121 .num_resources = ARRAY_SIZE(charger_resources),
122 .resources = charger_resources,
44 .of_compatible = "ti,tps65217-charger", 123 .of_compatible = "ti,tps65217-charger",
45 }, 124 },
46}; 125};
47 126
127static irqreturn_t tps65217_irq_thread(int irq, void *data)
128{
129 struct tps65217 *tps = data;
130 unsigned int status;
131 bool handled = false;
132 int i;
133 int ret;
134
135 ret = tps65217_reg_read(tps, TPS65217_REG_INT, &status);
136 if (ret < 0) {
137 dev_err(tps->dev, "Failed to read IRQ status: %d\n",
138 ret);
139 return IRQ_NONE;
140 }
141
142 for (i = 0; i < ARRAY_SIZE(tps65217_irqs); i++) {
143 if (status & tps65217_irqs[i].interrupt) {
144 handle_nested_irq(irq_find_mapping(tps->irq_domain, i));
145 handled = true;
146 }
147 }
148
149 if (handled)
150 return IRQ_HANDLED;
151
152 return IRQ_NONE;
153}
154
155static int tps65217_irq_map(struct irq_domain *h, unsigned int virq,
156 irq_hw_number_t hw)
157{
158 struct tps65217 *tps = h->host_data;
159
160 irq_set_chip_data(virq, tps);
161 irq_set_chip_and_handler(virq, &tps65217_irq_chip, handle_edge_irq);
162 irq_set_nested_thread(virq, 1);
163 irq_set_parent(virq, tps->irq);
164 irq_set_noprobe(virq);
165
166 return 0;
167}
168
169static const struct irq_domain_ops tps65217_irq_domain_ops = {
170 .map = tps65217_irq_map,
171};
172
173static int tps65217_irq_init(struct tps65217 *tps, int irq)
174{
175 int ret;
176
177 mutex_init(&tps->irq_lock);
178 tps->irq = irq;
179
180 /* Mask all interrupt sources */
181 tps->irq_mask = (TPS65217_INT_RESERVEDM | TPS65217_INT_PBM
182 | TPS65217_INT_ACM | TPS65217_INT_USBM);
183 tps65217_reg_write(tps, TPS65217_REG_INT, tps->irq_mask,
184 TPS65217_PROTECT_NONE);
185
186 tps->irq_domain = irq_domain_add_linear(tps->dev->of_node,
187 TPS65217_NUM_IRQ, &tps65217_irq_domain_ops, tps);
188 if (!tps->irq_domain) {
189 dev_err(tps->dev, "Could not create IRQ domain\n");
190 return -ENOMEM;
191 }
192
193 ret = devm_request_threaded_irq(tps->dev, irq, NULL,
194 tps65217_irq_thread, IRQF_ONESHOT,
195 "tps65217-irq", tps);
196 if (ret) {
197 dev_err(tps->dev, "Failed to request IRQ %d: %d\n",
198 irq, ret);
199 return ret;
200 }
201
202 return 0;
203}
204
48/** 205/**
49 * tps65217_reg_read: Read a single tps65217 register. 206 * tps65217_reg_read: Read a single tps65217 register.
50 * 207 *
@@ -149,11 +306,22 @@ int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg,
149} 306}
150EXPORT_SYMBOL_GPL(tps65217_clear_bits); 307EXPORT_SYMBOL_GPL(tps65217_clear_bits);
151 308
309static bool tps65217_volatile_reg(struct device *dev, unsigned int reg)
310{
311 switch (reg) {
312 case TPS65217_REG_INT:
313 return true;
314 default:
315 return false;
316 }
317}
318
152static const struct regmap_config tps65217_regmap_config = { 319static const struct regmap_config tps65217_regmap_config = {
153 .reg_bits = 8, 320 .reg_bits = 8,
154 .val_bits = 8, 321 .val_bits = 8,
155 322
156 .max_register = TPS65217_REG_MAX, 323 .max_register = TPS65217_REG_MAX,
324 .volatile_reg = tps65217_volatile_reg,
157}; 325};
158 326
159static const struct of_device_id tps65217_of_match[] = { 327static const struct of_device_id tps65217_of_match[] = {
@@ -205,8 +373,19 @@ static int tps65217_probe(struct i2c_client *client,
205 return ret; 373 return ret;
206 } 374 }
207 375
376 if (client->irq) {
377 tps65217_irq_init(tps, client->irq);
378 } else {
379 int i;
380
381 /* Don't tell children about IRQ resources which won't fire */
382 for (i = 0; i < ARRAY_SIZE(tps65217s); i++)
383 tps65217s[i].num_resources = 0;
384 }
385
208 ret = devm_mfd_add_devices(tps->dev, -1, tps65217s, 386 ret = devm_mfd_add_devices(tps->dev, -1, tps65217s,
209 ARRAY_SIZE(tps65217s), NULL, 0, NULL); 387 ARRAY_SIZE(tps65217s), NULL, 0,
388 tps->irq_domain);
210 if (ret < 0) { 389 if (ret < 0) {
211 dev_err(tps->dev, "mfd_add_devices failed: %d\n", ret); 390 dev_err(tps->dev, "mfd_add_devices failed: %d\n", ret);
212 return ret; 391 return ret;
diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h
index 1c88231496d3..4ccda8969639 100644
--- a/include/linux/mfd/tps65217.h
+++ b/include/linux/mfd/tps65217.h
@@ -73,6 +73,7 @@
73#define TPS65217_PPATH_AC_CURRENT_MASK 0x0C 73#define TPS65217_PPATH_AC_CURRENT_MASK 0x0C
74#define TPS65217_PPATH_USB_CURRENT_MASK 0x03 74#define TPS65217_PPATH_USB_CURRENT_MASK 0x03
75 75
76#define TPS65217_INT_RESERVEDM BIT(7)
76#define TPS65217_INT_PBM BIT(6) 77#define TPS65217_INT_PBM BIT(6)
77#define TPS65217_INT_ACM BIT(5) 78#define TPS65217_INT_ACM BIT(5)
78#define TPS65217_INT_USBM BIT(4) 79#define TPS65217_INT_USBM BIT(4)
@@ -233,6 +234,13 @@ struct tps65217_bl_pdata {
233 int dft_brightness; 234 int dft_brightness;
234}; 235};
235 236
237enum tps65217_irq_type {
238 TPS65217_IRQ_PB,
239 TPS65217_IRQ_AC,
240 TPS65217_IRQ_USB,
241 TPS65217_NUM_IRQ
242};
243
236/** 244/**
237 * struct tps65217_board - packages regulator init data 245 * struct tps65217_board - packages regulator init data
238 * @tps65217_regulator_data: regulator initialization values 246 * @tps65217_regulator_data: regulator initialization values
@@ -258,6 +266,10 @@ struct tps65217 {
258 struct regulator_desc desc[TPS65217_NUM_REGULATOR]; 266 struct regulator_desc desc[TPS65217_NUM_REGULATOR];
259 struct regmap *regmap; 267 struct regmap *regmap;
260 u8 *strobes; 268 u8 *strobes;
269 struct irq_domain *irq_domain;
270 struct mutex irq_lock;
271 u8 irq_mask;
272 int irq;
261}; 273};
262 274
263static inline struct tps65217 *dev_to_tps65217(struct device *dev) 275static inline struct tps65217 *dev_to_tps65217(struct device *dev)