mfd: mt6397-core: Add GPIO sub-module support
[deliverable/linux.git] / drivers / mfd / mt6397-core.c
1 /*
2 * Copyright (c) 2014 MediaTek Inc.
3 * Author: Flora Fu, MediaTek
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15 #include <linux/interrupt.h>
16 #include <linux/module.h>
17 #include <linux/of_device.h>
18 #include <linux/of_irq.h>
19 #include <linux/regmap.h>
20 #include <linux/mfd/core.h>
21 #include <linux/mfd/mt6397/core.h>
22 #include <linux/mfd/mt6397/registers.h>
23
24 static const struct mfd_cell mt6397_devs[] = {
25 {
26 .name = "mt6397-rtc",
27 .of_compatible = "mediatek,mt6397-rtc",
28 }, {
29 .name = "mt6397-regulator",
30 .of_compatible = "mediatek,mt6397-regulator",
31 }, {
32 .name = "mt6397-codec",
33 .of_compatible = "mediatek,mt6397-codec",
34 }, {
35 .name = "mt6397-clk",
36 .of_compatible = "mediatek,mt6397-clk",
37 }, {
38 .name = "mt6397-pinctrl",
39 .of_compatible = "mediatek,mt6397-pinctrl",
40 },
41 };
42
43 static void mt6397_irq_lock(struct irq_data *data)
44 {
45 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
46
47 mutex_lock(&mt6397->irqlock);
48 }
49
50 static void mt6397_irq_sync_unlock(struct irq_data *data)
51 {
52 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
53
54 regmap_write(mt6397->regmap, MT6397_INT_CON0, mt6397->irq_masks_cur[0]);
55 regmap_write(mt6397->regmap, MT6397_INT_CON1, mt6397->irq_masks_cur[1]);
56
57 mutex_unlock(&mt6397->irqlock);
58 }
59
60 static void mt6397_irq_disable(struct irq_data *data)
61 {
62 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
63 int shift = data->hwirq & 0xf;
64 int reg = data->hwirq >> 4;
65
66 mt6397->irq_masks_cur[reg] &= ~BIT(shift);
67 }
68
69 static void mt6397_irq_enable(struct irq_data *data)
70 {
71 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
72 int shift = data->hwirq & 0xf;
73 int reg = data->hwirq >> 4;
74
75 mt6397->irq_masks_cur[reg] |= BIT(shift);
76 }
77
78 static struct irq_chip mt6397_irq_chip = {
79 .name = "mt6397-irq",
80 .irq_bus_lock = mt6397_irq_lock,
81 .irq_bus_sync_unlock = mt6397_irq_sync_unlock,
82 .irq_enable = mt6397_irq_enable,
83 .irq_disable = mt6397_irq_disable,
84 };
85
86 static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg,
87 int irqbase)
88 {
89 unsigned int status;
90 int i, irq, ret;
91
92 ret = regmap_read(mt6397->regmap, reg, &status);
93 if (ret) {
94 dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret);
95 return;
96 }
97
98 for (i = 0; i < 16; i++) {
99 if (status & BIT(i)) {
100 irq = irq_find_mapping(mt6397->irq_domain, irqbase + i);
101 if (irq)
102 handle_nested_irq(irq);
103 }
104 }
105
106 regmap_write(mt6397->regmap, reg, status);
107 }
108
109 static irqreturn_t mt6397_irq_thread(int irq, void *data)
110 {
111 struct mt6397_chip *mt6397 = data;
112
113 mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS0, 0);
114 mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS1, 16);
115
116 return IRQ_HANDLED;
117 }
118
119 static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq,
120 irq_hw_number_t hw)
121 {
122 struct mt6397_chip *mt6397 = d->host_data;
123
124 irq_set_chip_data(irq, mt6397);
125 irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq);
126 irq_set_nested_thread(irq, 1);
127 #ifdef CONFIG_ARM
128 set_irq_flags(irq, IRQF_VALID);
129 #else
130 irq_set_noprobe(irq);
131 #endif
132
133 return 0;
134 }
135
136 static const struct irq_domain_ops mt6397_irq_domain_ops = {
137 .map = mt6397_irq_domain_map,
138 };
139
140 static int mt6397_irq_init(struct mt6397_chip *mt6397)
141 {
142 int ret;
143
144 mutex_init(&mt6397->irqlock);
145
146 /* Mask all interrupt sources */
147 regmap_write(mt6397->regmap, MT6397_INT_CON0, 0x0);
148 regmap_write(mt6397->regmap, MT6397_INT_CON1, 0x0);
149
150 mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node,
151 MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397);
152 if (!mt6397->irq_domain) {
153 dev_err(mt6397->dev, "could not create irq domain\n");
154 return -ENOMEM;
155 }
156
157 ret = devm_request_threaded_irq(mt6397->dev, mt6397->irq, NULL,
158 mt6397_irq_thread, IRQF_ONESHOT, "mt6397-pmic", mt6397);
159 if (ret) {
160 dev_err(mt6397->dev, "failed to register irq=%d; err: %d\n",
161 mt6397->irq, ret);
162 return ret;
163 }
164
165 return 0;
166 }
167
168 static int mt6397_probe(struct platform_device *pdev)
169 {
170 int ret;
171 struct mt6397_chip *mt6397;
172
173 mt6397 = devm_kzalloc(&pdev->dev, sizeof(*mt6397), GFP_KERNEL);
174 if (!mt6397)
175 return -ENOMEM;
176
177 mt6397->dev = &pdev->dev;
178 /*
179 * mt6397 MFD is child device of soc pmic wrapper.
180 * Regmap is set from its parent.
181 */
182 mt6397->regmap = dev_get_regmap(pdev->dev.parent, NULL);
183 if (!mt6397->regmap)
184 return -ENODEV;
185
186 platform_set_drvdata(pdev, mt6397);
187
188 mt6397->irq = platform_get_irq(pdev, 0);
189 if (mt6397->irq > 0) {
190 ret = mt6397_irq_init(mt6397);
191 if (ret)
192 return ret;
193 }
194
195 ret = mfd_add_devices(&pdev->dev, -1, mt6397_devs,
196 ARRAY_SIZE(mt6397_devs), NULL, 0, NULL);
197 if (ret)
198 dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
199
200 return ret;
201 }
202
203 static int mt6397_remove(struct platform_device *pdev)
204 {
205 mfd_remove_devices(&pdev->dev);
206
207 return 0;
208 }
209
210 static const struct of_device_id mt6397_of_match[] = {
211 { .compatible = "mediatek,mt6397" },
212 { }
213 };
214 MODULE_DEVICE_TABLE(of, mt6397_of_match);
215
216 static struct platform_driver mt6397_driver = {
217 .probe = mt6397_probe,
218 .remove = mt6397_remove,
219 .driver = {
220 .name = "mt6397",
221 .of_match_table = of_match_ptr(mt6397_of_match),
222 },
223 };
224
225 module_platform_driver(mt6397_driver);
226
227 MODULE_AUTHOR("Flora Fu, MediaTek");
228 MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC");
229 MODULE_LICENSE("GPL");
230 MODULE_ALIAS("platform:mt6397");
This page took 0.09997 seconds and 5 git commands to generate.