blob: 337bcccdb9146856f89779bdb22fdf9f5f8cfaab [file] [log] [blame]
Thomas Gleixner1802d0b2019-05-27 08:55:21 +02001// SPDX-License-Identifier: GPL-2.0-only
Flora Fu6df8dd52015-02-22 13:15:29 +01002/*
3 * Copyright (c) 2014 MediaTek Inc.
4 * Author: Flora Fu, MediaTek
Flora Fu6df8dd52015-02-22 13:15:29 +01005 */
6
7#include <linux/interrupt.h>
8#include <linux/module.h>
9#include <linux/of_device.h>
10#include <linux/of_irq.h>
11#include <linux/regmap.h>
12#include <linux/mfd/core.h>
13#include <linux/mfd/mt6397/core.h>
John Crispin44760cf2016-01-27 12:47:38 +010014#include <linux/mfd/mt6323/core.h>
Flora Fu6df8dd52015-02-22 13:15:29 +010015#include <linux/mfd/mt6397/registers.h>
John Crispin44760cf2016-01-27 12:47:38 +010016#include <linux/mfd/mt6323/registers.h>
Flora Fu6df8dd52015-02-22 13:15:29 +010017
Eddie Huanga5d7ea02015-05-06 15:23:40 +080018#define MT6397_RTC_BASE 0xe000
19#define MT6397_RTC_SIZE 0x3e
20
John Crispin44760cf2016-01-27 12:47:38 +010021#define MT6323_CID_CODE 0x23
John Crispin1d2c25e2016-01-27 12:47:37 +010022#define MT6391_CID_CODE 0x91
23#define MT6397_CID_CODE 0x97
24
Eddie Huanga5d7ea02015-05-06 15:23:40 +080025static const struct resource mt6397_rtc_resources[] = {
26 {
27 .start = MT6397_RTC_BASE,
28 .end = MT6397_RTC_BASE + MT6397_RTC_SIZE,
29 .flags = IORESOURCE_MEM,
30 },
31 {
32 .start = MT6397_IRQ_RTC,
33 .end = MT6397_IRQ_RTC,
34 .flags = IORESOURCE_IRQ,
35 },
36};
37
Chen Zhong55d1d152017-10-25 21:16:04 +080038static const struct resource mt6323_keys_resources[] = {
39 DEFINE_RES_IRQ(MT6323_IRQ_STATUS_PWRKEY),
40 DEFINE_RES_IRQ(MT6323_IRQ_STATUS_FCHRKEY),
41};
42
43static const struct resource mt6397_keys_resources[] = {
44 DEFINE_RES_IRQ(MT6397_IRQ_PWRKEY),
45 DEFINE_RES_IRQ(MT6397_IRQ_HOMEKEY),
46};
47
John Crispin44760cf2016-01-27 12:47:38 +010048static const struct mfd_cell mt6323_devs[] = {
49 {
50 .name = "mt6323-regulator",
51 .of_compatible = "mediatek,mt6323-regulator"
Sean Wang040fc9b2017-03-20 14:47:27 +080052 }, {
Sean Wang1cb8af82017-01-23 11:54:45 +080053 .name = "mt6323-led",
54 .of_compatible = "mediatek,mt6323-led"
Chen Zhong55d1d152017-10-25 21:16:04 +080055 }, {
56 .name = "mtk-pmic-keys",
57 .num_resources = ARRAY_SIZE(mt6323_keys_resources),
58 .resources = mt6323_keys_resources,
59 .of_compatible = "mediatek,mt6323-keys"
Sean Wang1cb8af82017-01-23 11:54:45 +080060 },
John Crispin44760cf2016-01-27 12:47:38 +010061};
62
Flora Fu6df8dd52015-02-22 13:15:29 +010063static const struct mfd_cell mt6397_devs[] = {
64 {
65 .name = "mt6397-rtc",
Eddie Huanga5d7ea02015-05-06 15:23:40 +080066 .num_resources = ARRAY_SIZE(mt6397_rtc_resources),
67 .resources = mt6397_rtc_resources,
Flora Fu6df8dd52015-02-22 13:15:29 +010068 .of_compatible = "mediatek,mt6397-rtc",
69 }, {
70 .name = "mt6397-regulator",
71 .of_compatible = "mediatek,mt6397-regulator",
72 }, {
73 .name = "mt6397-codec",
74 .of_compatible = "mediatek,mt6397-codec",
75 }, {
76 .name = "mt6397-clk",
77 .of_compatible = "mediatek,mt6397-clk",
Hongzhou Yangcf550782015-05-27 02:10:35 -070078 }, {
79 .name = "mt6397-pinctrl",
80 .of_compatible = "mediatek,mt6397-pinctrl",
Chen Zhong55d1d152017-10-25 21:16:04 +080081 }, {
82 .name = "mtk-pmic-keys",
83 .num_resources = ARRAY_SIZE(mt6397_keys_resources),
84 .resources = mt6397_keys_resources,
85 .of_compatible = "mediatek,mt6397-keys"
86 }
Flora Fu6df8dd52015-02-22 13:15:29 +010087};
88
89static void mt6397_irq_lock(struct irq_data *data)
90{
Jiang Liu1e84aa42015-07-13 20:44:56 +000091 struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
Flora Fu6df8dd52015-02-22 13:15:29 +010092
93 mutex_lock(&mt6397->irqlock);
94}
95
96static void mt6397_irq_sync_unlock(struct irq_data *data)
97{
Jiang Liu1e84aa42015-07-13 20:44:56 +000098 struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
Flora Fu6df8dd52015-02-22 13:15:29 +010099
John Crispinfeec4792016-01-27 12:47:36 +0100100 regmap_write(mt6397->regmap, mt6397->int_con[0],
101 mt6397->irq_masks_cur[0]);
102 regmap_write(mt6397->regmap, mt6397->int_con[1],
103 mt6397->irq_masks_cur[1]);
Flora Fu6df8dd52015-02-22 13:15:29 +0100104
105 mutex_unlock(&mt6397->irqlock);
106}
107
108static void mt6397_irq_disable(struct irq_data *data)
109{
Jiang Liu1e84aa42015-07-13 20:44:56 +0000110 struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
Flora Fu6df8dd52015-02-22 13:15:29 +0100111 int shift = data->hwirq & 0xf;
112 int reg = data->hwirq >> 4;
113
114 mt6397->irq_masks_cur[reg] &= ~BIT(shift);
115}
116
117static void mt6397_irq_enable(struct irq_data *data)
118{
Jiang Liu1e84aa42015-07-13 20:44:56 +0000119 struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
Flora Fu6df8dd52015-02-22 13:15:29 +0100120 int shift = data->hwirq & 0xf;
121 int reg = data->hwirq >> 4;
122
123 mt6397->irq_masks_cur[reg] |= BIT(shift);
124}
125
Henry Chenf3151ab2015-08-10 21:10:45 +0800126#ifdef CONFIG_PM_SLEEP
127static int mt6397_irq_set_wake(struct irq_data *irq_data, unsigned int on)
128{
129 struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(irq_data);
130 int shift = irq_data->hwirq & 0xf;
131 int reg = irq_data->hwirq >> 4;
132
133 if (on)
134 mt6397->wake_mask[reg] |= BIT(shift);
135 else
136 mt6397->wake_mask[reg] &= ~BIT(shift);
137
138 return 0;
139}
140#else
141#define mt6397_irq_set_wake NULL
142#endif
143
Flora Fu6df8dd52015-02-22 13:15:29 +0100144static struct irq_chip mt6397_irq_chip = {
145 .name = "mt6397-irq",
146 .irq_bus_lock = mt6397_irq_lock,
147 .irq_bus_sync_unlock = mt6397_irq_sync_unlock,
148 .irq_enable = mt6397_irq_enable,
149 .irq_disable = mt6397_irq_disable,
Henry Chenf3151ab2015-08-10 21:10:45 +0800150 .irq_set_wake = mt6397_irq_set_wake,
Flora Fu6df8dd52015-02-22 13:15:29 +0100151};
152
153static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg,
154 int irqbase)
155{
156 unsigned int status;
157 int i, irq, ret;
158
159 ret = regmap_read(mt6397->regmap, reg, &status);
160 if (ret) {
161 dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret);
162 return;
163 }
164
165 for (i = 0; i < 16; i++) {
166 if (status & BIT(i)) {
167 irq = irq_find_mapping(mt6397->irq_domain, irqbase + i);
168 if (irq)
169 handle_nested_irq(irq);
170 }
171 }
172
173 regmap_write(mt6397->regmap, reg, status);
174}
175
176static irqreturn_t mt6397_irq_thread(int irq, void *data)
177{
178 struct mt6397_chip *mt6397 = data;
179
John Crispinfeec4792016-01-27 12:47:36 +0100180 mt6397_irq_handle_reg(mt6397, mt6397->int_status[0], 0);
181 mt6397_irq_handle_reg(mt6397, mt6397->int_status[1], 16);
Flora Fu6df8dd52015-02-22 13:15:29 +0100182
183 return IRQ_HANDLED;
184}
185
186static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq,
187 irq_hw_number_t hw)
188{
189 struct mt6397_chip *mt6397 = d->host_data;
190
191 irq_set_chip_data(irq, mt6397);
192 irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq);
193 irq_set_nested_thread(irq, 1);
Flora Fu6df8dd52015-02-22 13:15:29 +0100194 irq_set_noprobe(irq);
Flora Fu6df8dd52015-02-22 13:15:29 +0100195
196 return 0;
197}
198
Krzysztof Kozlowski7ce7b262015-04-27 21:54:13 +0900199static const struct irq_domain_ops mt6397_irq_domain_ops = {
Flora Fu6df8dd52015-02-22 13:15:29 +0100200 .map = mt6397_irq_domain_map,
201};
202
203static int mt6397_irq_init(struct mt6397_chip *mt6397)
204{
205 int ret;
206
207 mutex_init(&mt6397->irqlock);
208
209 /* Mask all interrupt sources */
John Crispinfeec4792016-01-27 12:47:36 +0100210 regmap_write(mt6397->regmap, mt6397->int_con[0], 0x0);
211 regmap_write(mt6397->regmap, mt6397->int_con[1], 0x0);
Flora Fu6df8dd52015-02-22 13:15:29 +0100212
213 mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node,
214 MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397);
215 if (!mt6397->irq_domain) {
216 dev_err(mt6397->dev, "could not create irq domain\n");
217 return -ENOMEM;
218 }
219
220 ret = devm_request_threaded_irq(mt6397->dev, mt6397->irq, NULL,
221 mt6397_irq_thread, IRQF_ONESHOT, "mt6397-pmic", mt6397);
222 if (ret) {
223 dev_err(mt6397->dev, "failed to register irq=%d; err: %d\n",
224 mt6397->irq, ret);
225 return ret;
226 }
227
228 return 0;
229}
230
Henry Chenf3151ab2015-08-10 21:10:45 +0800231#ifdef CONFIG_PM_SLEEP
232static int mt6397_irq_suspend(struct device *dev)
233{
234 struct mt6397_chip *chip = dev_get_drvdata(dev);
235
John Crispinfeec4792016-01-27 12:47:36 +0100236 regmap_write(chip->regmap, chip->int_con[0], chip->wake_mask[0]);
237 regmap_write(chip->regmap, chip->int_con[1], chip->wake_mask[1]);
Henry Chenf3151ab2015-08-10 21:10:45 +0800238
239 enable_irq_wake(chip->irq);
240
241 return 0;
242}
243
244static int mt6397_irq_resume(struct device *dev)
245{
246 struct mt6397_chip *chip = dev_get_drvdata(dev);
247
John Crispinfeec4792016-01-27 12:47:36 +0100248 regmap_write(chip->regmap, chip->int_con[0], chip->irq_masks_cur[0]);
249 regmap_write(chip->regmap, chip->int_con[1], chip->irq_masks_cur[1]);
Henry Chenf3151ab2015-08-10 21:10:45 +0800250
251 disable_irq_wake(chip->irq);
252
253 return 0;
254}
255#endif
256
257static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_irq_suspend,
258 mt6397_irq_resume);
259
Flora Fu6df8dd52015-02-22 13:15:29 +0100260static int mt6397_probe(struct platform_device *pdev)
261{
262 int ret;
John Crispin1d2c25e2016-01-27 12:47:37 +0100263 unsigned int id;
264 struct mt6397_chip *pmic;
Flora Fu6df8dd52015-02-22 13:15:29 +0100265
John Crispin1d2c25e2016-01-27 12:47:37 +0100266 pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
267 if (!pmic)
Flora Fu6df8dd52015-02-22 13:15:29 +0100268 return -ENOMEM;
269
John Crispin1d2c25e2016-01-27 12:47:37 +0100270 pmic->dev = &pdev->dev;
John Crispinfeec4792016-01-27 12:47:36 +0100271
Flora Fu6df8dd52015-02-22 13:15:29 +0100272 /*
273 * mt6397 MFD is child device of soc pmic wrapper.
274 * Regmap is set from its parent.
275 */
John Crispin1d2c25e2016-01-27 12:47:37 +0100276 pmic->regmap = dev_get_regmap(pdev->dev.parent, NULL);
277 if (!pmic->regmap)
Flora Fu6df8dd52015-02-22 13:15:29 +0100278 return -ENODEV;
279
John Crispin1d2c25e2016-01-27 12:47:37 +0100280 platform_set_drvdata(pdev, pmic);
Flora Fu6df8dd52015-02-22 13:15:29 +0100281
John Crispin1d2c25e2016-01-27 12:47:37 +0100282 ret = regmap_read(pmic->regmap, MT6397_CID, &id);
283 if (ret) {
284 dev_err(pmic->dev, "Failed to read chip id: %d\n", ret);
Henry Chen1387ff52016-04-15 16:30:29 +0800285 return ret;
John Crispin1d2c25e2016-01-27 12:47:37 +0100286 }
287
Henry Chen1387ff52016-04-15 16:30:29 +0800288 pmic->irq = platform_get_irq(pdev, 0);
289 if (pmic->irq <= 0)
290 return pmic->irq;
291
John Crispin1d2c25e2016-01-27 12:47:37 +0100292 switch (id & 0xff) {
John Crispin44760cf2016-01-27 12:47:38 +0100293 case MT6323_CID_CODE:
294 pmic->int_con[0] = MT6323_INT_CON0;
295 pmic->int_con[1] = MT6323_INT_CON1;
296 pmic->int_status[0] = MT6323_INT_STATUS0;
297 pmic->int_status[1] = MT6323_INT_STATUS1;
Henry Chen1387ff52016-04-15 16:30:29 +0800298 ret = mt6397_irq_init(pmic);
299 if (ret)
300 return ret;
301
Laxman Dewangan08e380a2016-04-08 00:13:04 +0530302 ret = devm_mfd_add_devices(&pdev->dev, -1, mt6323_devs,
303 ARRAY_SIZE(mt6323_devs), NULL,
Chen Zhonge695d3a2017-10-25 21:15:59 +0800304 0, pmic->irq_domain);
John Crispin44760cf2016-01-27 12:47:38 +0100305 break;
306
John Crispin1d2c25e2016-01-27 12:47:37 +0100307 case MT6397_CID_CODE:
308 case MT6391_CID_CODE:
309 pmic->int_con[0] = MT6397_INT_CON0;
310 pmic->int_con[1] = MT6397_INT_CON1;
311 pmic->int_status[0] = MT6397_INT_STATUS0;
312 pmic->int_status[1] = MT6397_INT_STATUS1;
Henry Chen1387ff52016-04-15 16:30:29 +0800313 ret = mt6397_irq_init(pmic);
314 if (ret)
315 return ret;
316
Laxman Dewangan08e380a2016-04-08 00:13:04 +0530317 ret = devm_mfd_add_devices(&pdev->dev, -1, mt6397_devs,
318 ARRAY_SIZE(mt6397_devs), NULL,
Chen Zhonge695d3a2017-10-25 21:15:59 +0800319 0, pmic->irq_domain);
John Crispin1d2c25e2016-01-27 12:47:37 +0100320 break;
321
322 default:
323 dev_err(&pdev->dev, "unsupported chip: %d\n", id);
Nicolas Boichata1772762018-10-22 10:55:06 +0800324 return -ENODEV;
John Crispin1d2c25e2016-01-27 12:47:37 +0100325 }
326
John Crispin1d2c25e2016-01-27 12:47:37 +0100327 if (ret) {
328 irq_domain_remove(pmic->irq_domain);
Flora Fu6df8dd52015-02-22 13:15:29 +0100329 dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
John Crispin1d2c25e2016-01-27 12:47:37 +0100330 }
Flora Fu6df8dd52015-02-22 13:15:29 +0100331
332 return ret;
333}
334
Flora Fu6df8dd52015-02-22 13:15:29 +0100335static const struct of_device_id mt6397_of_match[] = {
336 { .compatible = "mediatek,mt6397" },
John Crispin44760cf2016-01-27 12:47:38 +0100337 { .compatible = "mediatek,mt6323" },
Flora Fu6df8dd52015-02-22 13:15:29 +0100338 { }
339};
340MODULE_DEVICE_TABLE(of, mt6397_of_match);
341
Javier Martinez Canillase1d9a102016-02-10 13:50:18 -0300342static const struct platform_device_id mt6397_id[] = {
343 { "mt6397", 0 },
344 { },
345};
346MODULE_DEVICE_TABLE(platform, mt6397_id);
347
Flora Fu6df8dd52015-02-22 13:15:29 +0100348static struct platform_driver mt6397_driver = {
349 .probe = mt6397_probe,
Flora Fu6df8dd52015-02-22 13:15:29 +0100350 .driver = {
351 .name = "mt6397",
352 .of_match_table = of_match_ptr(mt6397_of_match),
Henry Chenf3151ab2015-08-10 21:10:45 +0800353 .pm = &mt6397_pm_ops,
Flora Fu6df8dd52015-02-22 13:15:29 +0100354 },
Javier Martinez Canillase1d9a102016-02-10 13:50:18 -0300355 .id_table = mt6397_id,
Flora Fu6df8dd52015-02-22 13:15:29 +0100356};
357
358module_platform_driver(mt6397_driver);
359
360MODULE_AUTHOR("Flora Fu, MediaTek");
361MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC");
362MODULE_LICENSE("GPL");