aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/max77686-irq.c
diff options
context:
space:
mode:
authorJonghwa Lee <jonghwa3.lee@samsung.com>2012-06-25 04:34:36 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2012-07-08 18:16:07 -0400
commitdae8a969d512ee15e08fbec7837b9dab1777896d (patch)
tree9158b5af28aa5a5be5c9d52c1dc3ffaff782914e /drivers/mfd/max77686-irq.c
parentbd0a521e88aa7a06ae7aabaed7ae196ed4ad867a (diff)
mfd: Add Maxim 77686 driver
This patch is device driver for MAX77686 chip. MAX77686 is PMIC and includes regulator and rtc on it. This driver is core of MAX77686 chip, so provides common support for accessing on-chip devices. It uses irq_domain to manage irq and regmap to read/write data to its register with i2c bus. Signed-off-by: Chiwoong Byun <woong.byun@samsung.com> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Signed-off-by: Myungjoo Ham <myungjoo.ham@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd/max77686-irq.c')
-rw-r--r--drivers/mfd/max77686-irq.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c
new file mode 100644
index 000000000000..fc101220f990
--- /dev/null
+++ b/drivers/mfd/max77686-irq.c
@@ -0,0 +1,309 @@
1/*
2 * max77686-irq.c - Interrupt controller support for MAX77686
3 *
4 * Copyright (C) 2012 Samsung Electronics Co.Ltd
5 * Chiwoong Byun <woong.byun@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * This driver is based on max8997-irq.c
22 */
23
24#include <linux/err.h>
25#include <linux/irq.h>
26#include <linux/interrupt.h>
27#include <linux/gpio.h>
28#include <linux/mfd/max77686.h>
29#include <linux/mfd/max77686-private.h>
30#include <linux/irqdomain.h>
31#include <linux/regmap.h>
32
33enum {
34 MAX77686_DEBUG_IRQ_INFO = 1 << 0,
35 MAX77686_DEBUG_IRQ_MASK = 1 << 1,
36 MAX77686_DEBUG_IRQ_INT = 1 << 2,
37};
38
39static int debug_mask = 0;
40module_param(debug_mask, int, 0);
41MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO 0x2=IRQ_MASK 0x4=IRQ_INI)");
42
43static const u8 max77686_mask_reg[] = {
44 [PMIC_INT1] = MAX77686_REG_INT1MSK,
45 [PMIC_INT2] = MAX77686_REG_INT2MSK,
46 [RTC_INT] = MAX77686_RTC_INTM,
47};
48
49static struct regmap *max77686_get_regmap(struct max77686_dev *max77686,
50 enum max77686_irq_source src)
51{
52 switch (src) {
53 case PMIC_INT1 ... PMIC_INT2:
54 return max77686->regmap;
55 case RTC_INT:
56 return max77686->rtc_regmap;
57 default:
58 return ERR_PTR(-EINVAL);
59 }
60}
61
62struct max77686_irq_data {
63 int mask;
64 enum max77686_irq_source group;
65};
66
67#define DECLARE_IRQ(idx, _group, _mask) \
68 [(idx)] = { .group = (_group), .mask = (_mask) }
69static const struct max77686_irq_data max77686_irqs[] = {
70 DECLARE_IRQ(MAX77686_PMICIRQ_PWRONF, PMIC_INT1, 1 << 0),
71 DECLARE_IRQ(MAX77686_PMICIRQ_PWRONR, PMIC_INT1, 1 << 1),
72 DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBF, PMIC_INT1, 1 << 2),
73 DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBR, PMIC_INT1, 1 << 3),
74 DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBF, PMIC_INT1, 1 << 4),
75 DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBR, PMIC_INT1, 1 << 5),
76 DECLARE_IRQ(MAX77686_PMICIRQ_ONKEY1S, PMIC_INT1, 1 << 6),
77 DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB, PMIC_INT1, 1 << 7),
78 DECLARE_IRQ(MAX77686_PMICIRQ_140C, PMIC_INT2, 1 << 0),
79 DECLARE_IRQ(MAX77686_PMICIRQ_120C, PMIC_INT2, 1 << 1),
80 DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S, RTC_INT, 1 << 0),
81 DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1, RTC_INT, 1 << 1),
82 DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2, RTC_INT, 1 << 2),
83 DECLARE_IRQ(MAX77686_RTCIRQ_SMPL, RTC_INT, 1 << 3),
84 DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S, RTC_INT, 1 << 4),
85 DECLARE_IRQ(MAX77686_RTCIRQ_WTSR, RTC_INT, 1 << 5),
86};
87
88static void max77686_irq_lock(struct irq_data *data)
89{
90 struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
91
92 if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
93 pr_info("%s\n", __func__);
94
95 mutex_lock(&max77686->irqlock);
96}
97
98static void max77686_irq_sync_unlock(struct irq_data *data)
99{
100 struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
101 int i;
102
103 for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
104 u8 mask_reg = max77686_mask_reg[i];
105 struct regmap *map = max77686_get_regmap(max77686, i);
106
107 if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
108 pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
109 __func__, i, mask_reg, max77686->irq_masks_cur[i]);
110
111 if (mask_reg == MAX77686_REG_INVALID ||
112 IS_ERR_OR_NULL(map))
113 continue;
114
115 max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i];
116
117 regmap_write(map, max77686_mask_reg[i],
118 max77686->irq_masks_cur[i]);
119 }
120
121 mutex_unlock(&max77686->irqlock);
122}
123
124static const inline struct max77686_irq_data *to_max77686_irq(int irq)
125{
126 struct irq_data *data = irq_get_irq_data(irq);
127 return &max77686_irqs[data->hwirq];
128}
129
130static void max77686_irq_mask(struct irq_data *data)
131{
132 struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
133 const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);
134
135 max77686->irq_masks_cur[irq_data->group] |= irq_data->mask;
136
137 if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
138 pr_info("%s: group=%d, cur=0x%x\n",
139 __func__, irq_data->group,
140 max77686->irq_masks_cur[irq_data->group]);
141}
142
143static void max77686_irq_unmask(struct irq_data *data)
144{
145 struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
146 const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);
147
148 max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
149
150 if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
151 pr_info("%s: group=%d, cur=0x%x\n",
152 __func__, irq_data->group,
153 max77686->irq_masks_cur[irq_data->group]);
154}
155
156static struct irq_chip max77686_irq_chip = {
157 .name = "max77686",
158 .irq_bus_lock = max77686_irq_lock,
159 .irq_bus_sync_unlock = max77686_irq_sync_unlock,
160 .irq_mask = max77686_irq_mask,
161 .irq_unmask = max77686_irq_unmask,
162};
163
164static irqreturn_t max77686_irq_thread(int irq, void *data)
165{
166 struct max77686_dev *max77686 = data;
167 unsigned int irq_reg[MAX77686_IRQ_GROUP_NR] = {};
168 unsigned int irq_src;
169 int ret;
170 int i, cur_irq;
171
172 ret = regmap_read(max77686->regmap, MAX77686_REG_INTSRC, &irq_src);
173 if (ret < 0) {
174 dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
175 ret);
176 return IRQ_NONE;
177 }
178
179 if (debug_mask & MAX77686_DEBUG_IRQ_INT)
180 pr_info("%s: irq_src=0x%x\n", __func__, irq_src);
181
182 if (irq_src == MAX77686_IRQSRC_PMIC) {
183 ret = regmap_bulk_read(max77686->rtc_regmap,
184 MAX77686_REG_INT1, irq_reg, 2);
185 if (ret < 0) {
186 dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
187 ret);
188 return IRQ_NONE;
189 }
190
191 if (debug_mask & MAX77686_DEBUG_IRQ_INT)
192 pr_info("%s: int1=0x%x, int2=0x%x\n", __func__,
193 irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
194 }
195
196 if (irq_src & MAX77686_IRQSRC_RTC) {
197 ret = regmap_read(max77686->rtc_regmap,
198 MAX77686_RTC_INT, &irq_reg[RTC_INT]);
199 if (ret < 0) {
200 dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
201 ret);
202 return IRQ_NONE;
203 }
204
205 if (debug_mask & MAX77686_DEBUG_IRQ_INT)
206 pr_info("%s: rtc int=0x%x\n", __func__,
207 irq_reg[RTC_INT]);
208
209 }
210
211 for (i = 0; i < MAX77686_IRQ_NR; i++) {
212 if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) {
213 cur_irq = irq_find_mapping(max77686->irq_domain, i);
214 if (cur_irq)
215 handle_nested_irq(cur_irq);
216 }
217 }
218
219 return IRQ_HANDLED;
220}
221
222static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq,
223 irq_hw_number_t hw)
224{
225 struct max77686_dev *max77686 = d->host_data;
226
227 irq_set_chip_data(irq, max77686);
228 irq_set_chip_and_handler(irq, &max77686_irq_chip, handle_edge_irq);
229 irq_set_nested_thread(irq, 1);
230#ifdef CONFIG_ARM
231 set_irq_flags(irq, IRQF_VALID);
232#else
233 irq_set_noprobe(irq);
234#endif
235 return 0;
236}
237
238static struct irq_domain_ops max77686_irq_domain_ops = {
239 .map = max77686_irq_domain_map,
240};
241
242int max77686_irq_init(struct max77686_dev *max77686)
243{
244 struct irq_domain *domain;
245 int i;
246 int ret;
247 int val;
248 struct regmap *map;
249
250 mutex_init(&max77686->irqlock);
251
252 max77686->irq = gpio_to_irq(max77686->irq_gpio);
253
254 if (debug_mask & MAX77686_DEBUG_IRQ_INT) {
255 ret = gpio_request(max77686->irq_gpio, "pmic_irq");
256 if (ret < 0) {
257 dev_err(max77686->dev,
258 "Failed to request gpio %d with ret: %d\n",
259 max77686->irq_gpio, ret);
260 return IRQ_NONE;
261 }
262
263 gpio_direction_input(max77686->irq_gpio);
264 val = gpio_get_value(max77686->irq_gpio);
265 gpio_free(max77686->irq_gpio);
266 pr_info("%s: gpio_irq=%x\n", __func__, val);
267 }
268
269 /* Mask individual interrupt sources */
270 for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
271 max77686->irq_masks_cur[i] = 0xff;
272 max77686->irq_masks_cache[i] = 0xff;
273 map = max77686_get_regmap(max77686, i);
274
275 if (IS_ERR_OR_NULL(map))
276 continue;
277 if (max77686_mask_reg[i] == MAX77686_REG_INVALID)
278 continue;
279
280 regmap_write(map, max77686_mask_reg[i], 0xff);
281 }
282 domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR,
283 &max77686_irq_domain_ops, max77686);
284 if (!domain) {
285 dev_err(max77686->dev, "could not create irq domain\n");
286 return -ENODEV;
287 }
288 max77686->irq_domain = domain;
289
290 ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread,
291 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
292 "max77686-irq", max77686);
293
294 if (ret)
295 dev_err(max77686->dev, "Failed to request IRQ %d: %d\n",
296 max77686->irq, ret);
297
298
299 if (debug_mask & MAX77686_DEBUG_IRQ_INFO)
300 pr_info("%s-\n", __func__);
301
302 return 0;
303}
304
305void max77686_irq_exit(struct max77686_dev *max77686)
306{
307 if (max77686->irq)
308 free_irq(max77686->irq, max77686);
309}