aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFlora Fu <flora.fu@mediatek.com>2015-02-22 07:15:29 -0500
committerLee Jones <lee.jones@linaro.org>2015-03-04 03:18:27 -0500
commit6df8dd5c185b212d3d7364402df58bff0e67ace4 (patch)
tree111743466d36b07117d87131645aaba303392e05 /drivers
parent82a00c49ed97cf5b9aa96bd6cda2021720578ecc (diff)
mfd: Add support for the MediaTek MT6397 PMIC
This adds support for the MediaTek MT6397 PMIC. This is a multifunction device with the following sub modules: - Regulator - RTC - Audio codec - GPIO - Clock It is interfaced to the host controller using SPI interface by a proprietary hardware called PMIC wrapper or pwrap. MT6397 MFD is a child device of the pwrap. Signed-off-by: Flora Fu, MediaTek Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/Kconfig10
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/mt6397-core.c227
3 files changed, 238 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b5fb03c109bc..c602f7440e00 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -503,6 +503,16 @@ config MFD_MAX8998
503 additional drivers must be enabled in order to use the functionality 503 additional drivers must be enabled in order to use the functionality
504 of the device. 504 of the device.
505 505
506config MFD_MT6397
507 tristate "MediaTek MT6397 PMIC Support"
508 select MFD_CORE
509 select IRQ_DOMAIN
510 help
511 Say yes here to add support for MediaTek MT6397 PMIC. This is
512 a Power Management IC. This driver provides common support for
513 accessing the device; additional drivers must be enabled in order
514 to use the functionality of the device.
515
506config MFD_MENF21BMC 516config MFD_MENF21BMC
507 tristate "MEN 14F021P00 Board Management Controller Support" 517 tristate "MEN 14F021P00 Board Management Controller Support"
508 depends on I2C 518 depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index c8bb34929903..26303f712afb 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -181,3 +181,4 @@ obj-$(CONFIG_MFD_RT5033) += rt5033.o
181 181
182intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o 182intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
183obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o 183obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
184obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
new file mode 100644
index 000000000000..09bc7804952a
--- /dev/null
+++ b/drivers/mfd/mt6397-core.c
@@ -0,0 +1,227 @@
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
24static 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};
39
40static void mt6397_irq_lock(struct irq_data *data)
41{
42 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
43
44 mutex_lock(&mt6397->irqlock);
45}
46
47static void mt6397_irq_sync_unlock(struct irq_data *data)
48{
49 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
50
51 regmap_write(mt6397->regmap, MT6397_INT_CON0, mt6397->irq_masks_cur[0]);
52 regmap_write(mt6397->regmap, MT6397_INT_CON1, mt6397->irq_masks_cur[1]);
53
54 mutex_unlock(&mt6397->irqlock);
55}
56
57static void mt6397_irq_disable(struct irq_data *data)
58{
59 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
60 int shift = data->hwirq & 0xf;
61 int reg = data->hwirq >> 4;
62
63 mt6397->irq_masks_cur[reg] &= ~BIT(shift);
64}
65
66static void mt6397_irq_enable(struct irq_data *data)
67{
68 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq);
69 int shift = data->hwirq & 0xf;
70 int reg = data->hwirq >> 4;
71
72 mt6397->irq_masks_cur[reg] |= BIT(shift);
73}
74
75static struct irq_chip mt6397_irq_chip = {
76 .name = "mt6397-irq",
77 .irq_bus_lock = mt6397_irq_lock,
78 .irq_bus_sync_unlock = mt6397_irq_sync_unlock,
79 .irq_enable = mt6397_irq_enable,
80 .irq_disable = mt6397_irq_disable,
81};
82
83static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg,
84 int irqbase)
85{
86 unsigned int status;
87 int i, irq, ret;
88
89 ret = regmap_read(mt6397->regmap, reg, &status);
90 if (ret) {
91 dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret);
92 return;
93 }
94
95 for (i = 0; i < 16; i++) {
96 if (status & BIT(i)) {
97 irq = irq_find_mapping(mt6397->irq_domain, irqbase + i);
98 if (irq)
99 handle_nested_irq(irq);
100 }
101 }
102
103 regmap_write(mt6397->regmap, reg, status);
104}
105
106static irqreturn_t mt6397_irq_thread(int irq, void *data)
107{
108 struct mt6397_chip *mt6397 = data;
109
110 mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS0, 0);
111 mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS1, 16);
112
113 return IRQ_HANDLED;
114}
115
116static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq,
117 irq_hw_number_t hw)
118{
119 struct mt6397_chip *mt6397 = d->host_data;
120
121 irq_set_chip_data(irq, mt6397);
122 irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq);
123 irq_set_nested_thread(irq, 1);
124#ifdef CONFIG_ARM
125 set_irq_flags(irq, IRQF_VALID);
126#else
127 irq_set_noprobe(irq);
128#endif
129
130 return 0;
131}
132
133static struct irq_domain_ops mt6397_irq_domain_ops = {
134 .map = mt6397_irq_domain_map,
135};
136
137static int mt6397_irq_init(struct mt6397_chip *mt6397)
138{
139 int ret;
140
141 mutex_init(&mt6397->irqlock);
142
143 /* Mask all interrupt sources */
144 regmap_write(mt6397->regmap, MT6397_INT_CON0, 0x0);
145 regmap_write(mt6397->regmap, MT6397_INT_CON1, 0x0);
146
147 mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node,
148 MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397);
149 if (!mt6397->irq_domain) {
150 dev_err(mt6397->dev, "could not create irq domain\n");
151 return -ENOMEM;
152 }
153
154 ret = devm_request_threaded_irq(mt6397->dev, mt6397->irq, NULL,
155 mt6397_irq_thread, IRQF_ONESHOT, "mt6397-pmic", mt6397);
156 if (ret) {
157 dev_err(mt6397->dev, "failed to register irq=%d; err: %d\n",
158 mt6397->irq, ret);
159 return ret;
160 }
161
162 return 0;
163}
164
165static int mt6397_probe(struct platform_device *pdev)
166{
167 int ret;
168 struct mt6397_chip *mt6397;
169
170 mt6397 = devm_kzalloc(&pdev->dev, sizeof(*mt6397), GFP_KERNEL);
171 if (!mt6397)
172 return -ENOMEM;
173
174 mt6397->dev = &pdev->dev;
175 /*
176 * mt6397 MFD is child device of soc pmic wrapper.
177 * Regmap is set from its parent.
178 */
179 mt6397->regmap = dev_get_regmap(pdev->dev.parent, NULL);
180 if (!mt6397->regmap)
181 return -ENODEV;
182
183 platform_set_drvdata(pdev, mt6397);
184
185 mt6397->irq = platform_get_irq(pdev, 0);
186 if (mt6397->irq > 0) {
187 ret = mt6397_irq_init(mt6397);
188 if (ret)
189 return ret;
190 }
191
192 ret = mfd_add_devices(&pdev->dev, -1, mt6397_devs,
193 ARRAY_SIZE(mt6397_devs), NULL, 0, NULL);
194 if (ret)
195 dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
196
197 return ret;
198}
199
200static int mt6397_remove(struct platform_device *pdev)
201{
202 mfd_remove_devices(&pdev->dev);
203
204 return 0;
205}
206
207static const struct of_device_id mt6397_of_match[] = {
208 { .compatible = "mediatek,mt6397" },
209 { }
210};
211MODULE_DEVICE_TABLE(of, mt6397_of_match);
212
213static struct platform_driver mt6397_driver = {
214 .probe = mt6397_probe,
215 .remove = mt6397_remove,
216 .driver = {
217 .name = "mt6397",
218 .of_match_table = of_match_ptr(mt6397_of_match),
219 },
220};
221
222module_platform_driver(mt6397_driver);
223
224MODULE_AUTHOR("Flora Fu, MediaTek");
225MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC");
226MODULE_LICENSE("GPL");
227MODULE_ALIAS("platform:mt6397");