aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorChris Zhong <zyw@rock-chips.com>2014-09-03 09:51:44 -0400
committerLee Jones <lee.jones@linaro.org>2014-09-26 03:15:56 -0400
commitf69a7cf74d5536faa180437581be2a9c0aad1bb1 (patch)
tree568f6982d0c4e488f5608031457cd7ec6cdfc2a4 /drivers/mfd
parenta53b9a97ac33fa74d136c2e3fd0d76ba7872a1d0 (diff)
mfd: RK808: Add new mfd driver for RK808
The RK808 chip is a power management IC for multimedia and handheld devices. It contains the following components: - Regulators - RTC - Clkout The RK808 core driver is registered as a platform driver and provides communication through I2C with the host device for the different components. Signed-off-by: Chris Zhong <zyw@rock-chips.com> Signed-off-by: Zhang Qing <zhangqing@rock-chips.com> Tested-by: Heiko <heiko@sntech.de> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig13
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/rk808.c245
3 files changed, 259 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 82f70dcab136..049796a28215 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -597,6 +597,19 @@ config MFD_RC5T583
597 Additional drivers must be enabled in order to use the 597 Additional drivers must be enabled in order to use the
598 different functionality of the device. 598 different functionality of the device.
599 599
600config MFD_RK808
601 tristate "Rockchip RK808 Power Management chip"
602 depends on I2C && OF
603 select MFD_CORE
604 select REGMAP_I2C
605 select REGMAP_IRQ
606 help
607 If you say yes here you get support for the RK808
608 Power Management chips.
609 This driver provides common support for accessing the device
610 through I2C interface. The device supports multiple sub-devices
611 including interrupts, RTC, LDO & DCDC regulators, and onkey.
612
600config MFD_RN5T618 613config MFD_RN5T618
601 tristate "Ricoh RN5T5618 PMIC" 614 tristate "Ricoh RN5T5618 PMIC"
602 depends on I2C 615 depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index aa5a73a5ba47..bd111213bd58 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -160,6 +160,7 @@ obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
160obj-$(CONFIG_MFD_PALMAS) += palmas.o 160obj-$(CONFIG_MFD_PALMAS) += palmas.o
161obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o 161obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
162obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o 162obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
163obj-$(CONFIG_MFD_RK808) += rk808.o
163obj-$(CONFIG_MFD_RN5T618) += rn5t618.o 164obj-$(CONFIG_MFD_RN5T618) += rn5t618.o
164obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o 165obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
165obj-$(CONFIG_MFD_SYSCON) += syscon.o 166obj-$(CONFIG_MFD_SYSCON) += syscon.o
diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
new file mode 100644
index 000000000000..0324422b3b10
--- /dev/null
+++ b/drivers/mfd/rk808.c
@@ -0,0 +1,245 @@
1/*
2 * MFD core driver for Rockchip RK808
3 *
4 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
5 *
6 * Author: Chris Zhong <zyw@rock-chips.com>
7 * Author: Zhang Qing <zhangqing@rock-chips.com>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms and conditions of the GNU General Public License,
11 * version 2, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 */
18
19#include <linux/i2c.h>
20#include <linux/interrupt.h>
21#include <linux/mfd/rk808.h>
22#include <linux/mfd/core.h>
23#include <linux/module.h>
24#include <linux/regmap.h>
25
26struct rk808_reg_data {
27 int addr;
28 int mask;
29 int value;
30};
31
32static const struct regmap_config rk808_regmap_config = {
33 .reg_bits = 8,
34 .val_bits = 8,
35 .max_register = RK808_IO_POL_REG,
36};
37
38static struct resource rtc_resources[] = {
39 {
40 .start = RK808_IRQ_RTC_ALARM,
41 .end = RK808_IRQ_RTC_ALARM,
42 .flags = IORESOURCE_IRQ,
43 }
44};
45
46static const struct mfd_cell rk808s[] = {
47 { .name = "rk808-clkout", },
48 { .name = "rk808-regulator", },
49 {
50 .name = "rk808-rtc",
51 .num_resources = ARRAY_SIZE(rtc_resources),
52 .resources = &rtc_resources[0],
53 },
54};
55
56static const struct rk808_reg_data pre_init_reg[] = {
57 { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA },
58 { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA },
59 { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
60 { RK808_BUCK1_CONFIG_REG, BUCK1_RATE_MASK, BUCK_ILMIN_200MA },
61 { RK808_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_200MA },
62 { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT |
63 VB_LO_SEL_3500MV },
64};
65
66static const struct regmap_irq rk808_irqs[] = {
67 /* INT_STS */
68 [RK808_IRQ_VOUT_LO] = {
69 .mask = RK808_IRQ_VOUT_LO_MSK,
70 .reg_offset = 0,
71 },
72 [RK808_IRQ_VB_LO] = {
73 .mask = RK808_IRQ_VB_LO_MSK,
74 .reg_offset = 0,
75 },
76 [RK808_IRQ_PWRON] = {
77 .mask = RK808_IRQ_PWRON_MSK,
78 .reg_offset = 0,
79 },
80 [RK808_IRQ_PWRON_LP] = {
81 .mask = RK808_IRQ_PWRON_LP_MSK,
82 .reg_offset = 0,
83 },
84 [RK808_IRQ_HOTDIE] = {
85 .mask = RK808_IRQ_HOTDIE_MSK,
86 .reg_offset = 0,
87 },
88 [RK808_IRQ_RTC_ALARM] = {
89 .mask = RK808_IRQ_RTC_ALARM_MSK,
90 .reg_offset = 0,
91 },
92 [RK808_IRQ_RTC_PERIOD] = {
93 .mask = RK808_IRQ_RTC_PERIOD_MSK,
94 .reg_offset = 0,
95 },
96
97 /* INT_STS2 */
98 [RK808_IRQ_PLUG_IN_INT] = {
99 .mask = RK808_IRQ_PLUG_IN_INT_MSK,
100 .reg_offset = 1,
101 },
102 [RK808_IRQ_PLUG_OUT_INT] = {
103 .mask = RK808_IRQ_PLUG_OUT_INT_MSK,
104 .reg_offset = 1,
105 },
106};
107
108static struct regmap_irq_chip rk808_irq_chip = {
109 .name = "rk808",
110 .irqs = rk808_irqs,
111 .num_irqs = ARRAY_SIZE(rk808_irqs),
112 .num_regs = 2,
113 .irq_reg_stride = 2,
114 .status_base = RK808_INT_STS_REG1,
115 .mask_base = RK808_INT_STS_MSK_REG1,
116 .ack_base = RK808_INT_STS_REG1,
117 .init_ack_masked = true,
118};
119
120static struct i2c_client *rk808_i2c_client;
121static void rk808_device_shutdown(void)
122{
123 int ret;
124 struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
125
126 if (!rk808) {
127 dev_warn(&rk808_i2c_client->dev,
128 "have no rk808, so do nothing here\n");
129 return;
130 }
131
132 ret = regmap_update_bits(rk808->regmap,
133 RK808_DEVCTRL_REG,
134 DEV_OFF_RST, DEV_OFF_RST);
135 if (ret)
136 dev_err(&rk808_i2c_client->dev, "power off error!\n");
137}
138
139static int rk808_probe(struct i2c_client *client,
140 const struct i2c_device_id *id)
141{
142 struct device_node *np = client->dev.of_node;
143 struct rk808 *rk808;
144 int pm_off = 0;
145 int ret;
146 int i;
147
148 if (!client->irq) {
149 dev_err(&client->dev, "No interrupt support, no core IRQ\n");
150 return -EINVAL;
151 }
152
153 rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL);
154 if (!rk808)
155 return -ENOMEM;
156
157 rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config);
158 if (IS_ERR(rk808->regmap)) {
159 dev_err(&client->dev, "regmap initialization failed\n");
160 return PTR_ERR(rk808->regmap);
161 }
162
163 for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) {
164 ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr,
165 pre_init_reg[i].mask,
166 pre_init_reg[i].value);
167 if (ret) {
168 dev_err(&client->dev,
169 "0x%x write err\n", pre_init_reg[i].addr);
170 return ret;
171 }
172 }
173
174 ret = regmap_add_irq_chip(rk808->regmap, client->irq,
175 IRQF_ONESHOT, -1,
176 &rk808_irq_chip, &rk808->irq_data);
177 if (ret) {
178 dev_err(&client->dev, "Failed to add irq_chip %d\n", ret);
179 return ret;
180 }
181
182 rk808->i2c = client;
183 i2c_set_clientdata(client, rk808);
184
185 ret = mfd_add_devices(&client->dev, -1,
186 rk808s, ARRAY_SIZE(rk808s),
187 NULL, 0, regmap_irq_get_domain(rk808->irq_data));
188 if (ret) {
189 dev_err(&client->dev, "failed to add MFD devices %d\n", ret);
190 goto err_irq;
191 }
192
193 pm_off = of_property_read_bool(np,
194 "rockchip,system-power-controller");
195 if (pm_off && !pm_power_off) {
196 rk808_i2c_client = client;
197 pm_power_off = rk808_device_shutdown;
198 }
199
200 return 0;
201
202err_irq:
203 regmap_del_irq_chip(client->irq, rk808->irq_data);
204 return ret;
205}
206
207static int rk808_remove(struct i2c_client *client)
208{
209 struct rk808 *rk808 = i2c_get_clientdata(client);
210
211 regmap_del_irq_chip(client->irq, rk808->irq_data);
212 mfd_remove_devices(&client->dev);
213 pm_power_off = NULL;
214
215 return 0;
216}
217
218static struct of_device_id rk808_of_match[] = {
219 { .compatible = "rockchip,rk808" },
220 { },
221};
222MODULE_DEVICE_TABLE(of, rk808_of_match);
223
224static const struct i2c_device_id rk808_ids[] = {
225 { "rk808" },
226 { },
227};
228MODULE_DEVICE_TABLE(i2c, rk808_ids);
229
230static struct i2c_driver rk808_i2c_driver = {
231 .driver = {
232 .name = "rk808",
233 .of_match_table = rk808_of_match,
234 },
235 .probe = rk808_probe,
236 .remove = rk808_remove,
237 .id_table = rk808_ids,
238};
239
240module_i2c_driver(rk808_i2c_driver);
241
242MODULE_LICENSE("GPL");
243MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
244MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
245MODULE_DESCRIPTION("RK808 PMIC driver");