aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2012-02-28 08:05:17 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2012-03-16 15:05:45 -0400
commit1b1247dd75aa5cf5fae54a3bec7280046e9c7957 (patch)
treeb2a63af28a30582f7e429f4f6627ad179a831df4 /drivers/mfd
parentbc628fd19d2d1d053b88fa225bb599be026c048b (diff)
mfd: Add support for RICOH PMIC RC5T583
Ricoh power management IC RC5T583 contains is multi functional device having multiple sub devices inside this. This device has multiple dcdc/ldo regulators, gpios, interrupt controllers, on-key, RTCs, ADCs. This device have 4 DCDCs, 8 LDOs, 8 GPIOs, 6 ADCs, 3 RTCs etc. Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig14
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/rc5t583-irq.c408
-rw-r--r--drivers/mfd/rc5t583.c386
4 files changed, 809 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 82da44877cdc..0f593966a310 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -846,6 +846,20 @@ config MFD_INTEL_MSIC
846 Passage) chip. This chip embeds audio, battery, GPIO, etc. 846 Passage) chip. This chip embeds audio, battery, GPIO, etc.
847 devices used in Intel Medfield platforms. 847 devices used in Intel Medfield platforms.
848 848
849config MFD_RC5T583
850 bool "Ricoh RC5T583 Power Management system device"
851 depends on I2C && GENERIC_HARDIRQS
852 select MFD_CORE
853 select REGMAP_I2C
854 help
855 Select this option to get support for the RICOH583 Power
856 Management system device.
857 This driver provides common support for accessing the device
858 through i2c interface. The device supports multiple sub-devices
859 like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey.
860 Additional drivers must be enabled in order to use the
861 different functionality of the device.
862
849endmenu 863endmenu
850endif 864endif
851 865
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 27430d3e839c..ea0bb809739e 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -112,4 +112,5 @@ obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
112obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o 112obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
113obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o 113obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
114obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o 114obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
115obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
115obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o 116obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o
diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c
new file mode 100644
index 000000000000..fa6f80fad5f1
--- /dev/null
+++ b/drivers/mfd/rc5t583-irq.c
@@ -0,0 +1,408 @@
1/*
2 * Interrupt driver for RICOH583 power management chip.
3 *
4 * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
5 * Author: Laxman dewangan <ldewangan@nvidia.com>
6 *
7 * based on code
8 * Copyright (C) 2011 RICOH COMPANY,LTD
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms and conditions of the GNU General Public License,
12 * version 2, as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23#include <linux/interrupt.h>
24#include <linux/irq.h>
25#include <linux/init.h>
26#include <linux/i2c.h>
27#include <linux/mfd/rc5t583.h>
28
29enum int_type {
30 SYS_INT = 0x1,
31 DCDC_INT = 0x2,
32 RTC_INT = 0x4,
33 ADC_INT = 0x8,
34 GPIO_INT = 0x10,
35};
36
37static int gpedge_add[] = {
38 RC5T583_GPIO_GPEDGE2,
39 RC5T583_GPIO_GPEDGE2
40};
41
42static int irq_en_add[] = {
43 RC5T583_INT_EN_SYS1,
44 RC5T583_INT_EN_SYS2,
45 RC5T583_INT_EN_DCDC,
46 RC5T583_INT_EN_RTC,
47 RC5T583_INT_EN_ADC1,
48 RC5T583_INT_EN_ADC2,
49 RC5T583_INT_EN_ADC3,
50 RC5T583_GPIO_EN_INT
51};
52
53static int irq_mon_add[] = {
54 RC5T583_INT_MON_SYS1,
55 RC5T583_INT_MON_SYS2,
56 RC5T583_INT_MON_DCDC,
57 RC5T583_INT_MON_RTC,
58 RC5T583_INT_IR_ADCL,
59 RC5T583_INT_IR_ADCH,
60 RC5T583_INT_IR_ADCEND,
61 RC5T583_INT_IR_GPIOF,
62 RC5T583_INT_IR_GPIOR
63};
64
65static int irq_clr_add[] = {
66 RC5T583_INT_IR_SYS1,
67 RC5T583_INT_IR_SYS2,
68 RC5T583_INT_IR_DCDC,
69 RC5T583_INT_IR_RTC,
70 RC5T583_INT_IR_ADCL,
71 RC5T583_INT_IR_ADCH,
72 RC5T583_INT_IR_ADCEND,
73 RC5T583_INT_IR_GPIOF,
74 RC5T583_INT_IR_GPIOR
75};
76
77static int main_int_type[] = {
78 SYS_INT,
79 SYS_INT,
80 DCDC_INT,
81 RTC_INT,
82 ADC_INT,
83 ADC_INT,
84 ADC_INT,
85 GPIO_INT,
86 GPIO_INT,
87};
88
89struct rc5t583_irq_data {
90 u8 int_type;
91 u8 master_bit;
92 u8 int_en_bit;
93 u8 mask_reg_index;
94 int grp_index;
95};
96
97#define RC5T583_IRQ(_int_type, _master_bit, _grp_index, \
98 _int_bit, _mask_ind) \
99 { \
100 .int_type = _int_type, \
101 .master_bit = _master_bit, \
102 .grp_index = _grp_index, \
103 .int_en_bit = _int_bit, \
104 .mask_reg_index = _mask_ind, \
105 }
106
107static const struct rc5t583_irq_data rc5t583_irqs[RC5T583_MAX_IRQS] = {
108 [RC5T583_IRQ_ONKEY] = RC5T583_IRQ(SYS_INT, 0, 0, 0, 0),
109 [RC5T583_IRQ_ACOK] = RC5T583_IRQ(SYS_INT, 0, 1, 1, 0),
110 [RC5T583_IRQ_LIDOPEN] = RC5T583_IRQ(SYS_INT, 0, 2, 2, 0),
111 [RC5T583_IRQ_PREOT] = RC5T583_IRQ(SYS_INT, 0, 3, 3, 0),
112 [RC5T583_IRQ_CLKSTP] = RC5T583_IRQ(SYS_INT, 0, 4, 4, 0),
113 [RC5T583_IRQ_ONKEY_OFF] = RC5T583_IRQ(SYS_INT, 0, 5, 5, 0),
114 [RC5T583_IRQ_WD] = RC5T583_IRQ(SYS_INT, 0, 7, 7, 0),
115 [RC5T583_IRQ_EN_PWRREQ1] = RC5T583_IRQ(SYS_INT, 0, 8, 0, 1),
116 [RC5T583_IRQ_EN_PWRREQ2] = RC5T583_IRQ(SYS_INT, 0, 9, 1, 1),
117 [RC5T583_IRQ_PRE_VINDET] = RC5T583_IRQ(SYS_INT, 0, 10, 2, 1),
118
119 [RC5T583_IRQ_DC0LIM] = RC5T583_IRQ(DCDC_INT, 1, 0, 0, 2),
120 [RC5T583_IRQ_DC1LIM] = RC5T583_IRQ(DCDC_INT, 1, 1, 1, 2),
121 [RC5T583_IRQ_DC2LIM] = RC5T583_IRQ(DCDC_INT, 1, 2, 2, 2),
122 [RC5T583_IRQ_DC3LIM] = RC5T583_IRQ(DCDC_INT, 1, 3, 3, 2),
123
124 [RC5T583_IRQ_CTC] = RC5T583_IRQ(RTC_INT, 2, 0, 0, 3),
125 [RC5T583_IRQ_YALE] = RC5T583_IRQ(RTC_INT, 2, 5, 5, 3),
126 [RC5T583_IRQ_DALE] = RC5T583_IRQ(RTC_INT, 2, 6, 6, 3),
127 [RC5T583_IRQ_WALE] = RC5T583_IRQ(RTC_INT, 2, 7, 7, 3),
128
129 [RC5T583_IRQ_AIN1L] = RC5T583_IRQ(ADC_INT, 3, 0, 0, 4),
130 [RC5T583_IRQ_AIN2L] = RC5T583_IRQ(ADC_INT, 3, 1, 1, 4),
131 [RC5T583_IRQ_AIN3L] = RC5T583_IRQ(ADC_INT, 3, 2, 2, 4),
132 [RC5T583_IRQ_VBATL] = RC5T583_IRQ(ADC_INT, 3, 3, 3, 4),
133 [RC5T583_IRQ_VIN3L] = RC5T583_IRQ(ADC_INT, 3, 4, 4, 4),
134 [RC5T583_IRQ_VIN8L] = RC5T583_IRQ(ADC_INT, 3, 5, 5, 4),
135 [RC5T583_IRQ_AIN1H] = RC5T583_IRQ(ADC_INT, 3, 6, 0, 5),
136 [RC5T583_IRQ_AIN2H] = RC5T583_IRQ(ADC_INT, 3, 7, 1, 5),
137 [RC5T583_IRQ_AIN3H] = RC5T583_IRQ(ADC_INT, 3, 8, 2, 5),
138 [RC5T583_IRQ_VBATH] = RC5T583_IRQ(ADC_INT, 3, 9, 3, 5),
139 [RC5T583_IRQ_VIN3H] = RC5T583_IRQ(ADC_INT, 3, 10, 4, 5),
140 [RC5T583_IRQ_VIN8H] = RC5T583_IRQ(ADC_INT, 3, 11, 5, 5),
141 [RC5T583_IRQ_ADCEND] = RC5T583_IRQ(ADC_INT, 3, 12, 0, 6),
142
143 [RC5T583_IRQ_GPIO0] = RC5T583_IRQ(GPIO_INT, 4, 0, 0, 7),
144 [RC5T583_IRQ_GPIO1] = RC5T583_IRQ(GPIO_INT, 4, 1, 1, 7),
145 [RC5T583_IRQ_GPIO2] = RC5T583_IRQ(GPIO_INT, 4, 2, 2, 7),
146 [RC5T583_IRQ_GPIO3] = RC5T583_IRQ(GPIO_INT, 4, 3, 3, 7),
147 [RC5T583_IRQ_GPIO4] = RC5T583_IRQ(GPIO_INT, 4, 4, 4, 7),
148 [RC5T583_IRQ_GPIO5] = RC5T583_IRQ(GPIO_INT, 4, 5, 5, 7),
149 [RC5T583_IRQ_GPIO6] = RC5T583_IRQ(GPIO_INT, 4, 6, 6, 7),
150 [RC5T583_IRQ_GPIO7] = RC5T583_IRQ(GPIO_INT, 4, 7, 7, 7),
151};
152
153static void rc5t583_irq_lock(struct irq_data *irq_data)
154{
155 struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
156 mutex_lock(&rc5t583->irq_lock);
157}
158
159static void rc5t583_irq_unmask(struct irq_data *irq_data)
160{
161 struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
162 unsigned int __irq = irq_data->irq - rc5t583->irq_base;
163 const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq];
164
165 rc5t583->group_irq_en[data->grp_index] |= 1 << data->grp_index;
166 rc5t583->intc_inten_reg |= 1 << data->master_bit;
167 rc5t583->irq_en_reg[data->mask_reg_index] |= 1 << data->int_en_bit;
168}
169
170static void rc5t583_irq_mask(struct irq_data *irq_data)
171{
172 struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
173 unsigned int __irq = irq_data->irq - rc5t583->irq_base;
174 const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq];
175
176 rc5t583->group_irq_en[data->grp_index] &= ~(1 << data->grp_index);
177 if (!rc5t583->group_irq_en[data->grp_index])
178 rc5t583->intc_inten_reg &= ~(1 << data->master_bit);
179
180 rc5t583->irq_en_reg[data->mask_reg_index] &= ~(1 << data->int_en_bit);
181}
182
183static int rc5t583_irq_set_type(struct irq_data *irq_data, unsigned int type)
184{
185 struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
186 unsigned int __irq = irq_data->irq - rc5t583->irq_base;
187 const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq];
188 int val = 0;
189 int gpedge_index;
190 int gpedge_bit_pos;
191
192 /* Supporting only trigger level inetrrupt */
193 if ((data->int_type & GPIO_INT) && (type & IRQ_TYPE_EDGE_BOTH)) {
194 gpedge_index = data->int_en_bit / 4;
195 gpedge_bit_pos = data->int_en_bit % 4;
196
197 if (type & IRQ_TYPE_EDGE_FALLING)
198 val |= 0x2;
199
200 if (type & IRQ_TYPE_EDGE_RISING)
201 val |= 0x1;
202
203 rc5t583->gpedge_reg[gpedge_index] &= ~(3 << gpedge_bit_pos);
204 rc5t583->gpedge_reg[gpedge_index] |= (val << gpedge_bit_pos);
205 rc5t583_irq_unmask(irq_data);
206 return 0;
207 }
208 return -EINVAL;
209}
210
211static void rc5t583_irq_sync_unlock(struct irq_data *irq_data)
212{
213 struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
214 int i;
215 int ret;
216
217 for (i = 0; i < ARRAY_SIZE(rc5t583->gpedge_reg); i++) {
218 ret = rc5t583_write(rc5t583->dev, gpedge_add[i],
219 rc5t583->gpedge_reg[i]);
220 if (ret < 0)
221 dev_warn(rc5t583->dev,
222 "Error in writing reg 0x%02x error: %d\n",
223 gpedge_add[i], ret);
224 }
225
226 for (i = 0; i < ARRAY_SIZE(rc5t583->irq_en_reg); i++) {
227 ret = rc5t583_write(rc5t583->dev, irq_en_add[i],
228 rc5t583->irq_en_reg[i]);
229 if (ret < 0)
230 dev_warn(rc5t583->dev,
231 "Error in writing reg 0x%02x error: %d\n",
232 irq_en_add[i], ret);
233 }
234
235 ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN,
236 rc5t583->intc_inten_reg);
237 if (ret < 0)
238 dev_warn(rc5t583->dev,
239 "Error in writing reg 0x%02x error: %d\n",
240 RC5T583_INTC_INTEN, ret);
241
242 mutex_unlock(&rc5t583->irq_lock);
243}
244#ifdef CONFIG_PM_SLEEP
245static int rc5t583_irq_set_wake(struct irq_data *irq_data, unsigned int on)
246{
247 struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
248 return irq_set_irq_wake(rc5t583->chip_irq, on);
249}
250#else
251#define rc5t583_irq_set_wake NULL
252#endif
253
254static irqreturn_t rc5t583_irq(int irq, void *data)
255{
256 struct rc5t583 *rc5t583 = data;
257 uint8_t int_sts[RC5T583_MAX_INTERRUPT_MASK_REGS];
258 uint8_t master_int;
259 int i;
260 int ret;
261 unsigned int rtc_int_sts = 0;
262
263 /* Clear the status */
264 for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++)
265 int_sts[i] = 0;
266
267 ret = rc5t583_read(rc5t583->dev, RC5T583_INTC_INTMON, &master_int);
268 if (ret < 0) {
269 dev_err(rc5t583->dev,
270 "Error in reading reg 0x%02x error: %d\n",
271 RC5T583_INTC_INTMON, ret);
272 return IRQ_HANDLED;
273 }
274
275 for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; ++i) {
276 if (!(master_int & main_int_type[i]))
277 continue;
278
279 ret = rc5t583_read(rc5t583->dev, irq_mon_add[i], &int_sts[i]);
280 if (ret < 0) {
281 dev_warn(rc5t583->dev,
282 "Error in reading reg 0x%02x error: %d\n",
283 irq_mon_add[i], ret);
284 int_sts[i] = 0;
285 continue;
286 }
287
288 if (main_int_type[i] & RTC_INT) {
289 rtc_int_sts = 0;
290 if (int_sts[i] & 0x1)
291 rtc_int_sts |= BIT(6);
292 if (int_sts[i] & 0x2)
293 rtc_int_sts |= BIT(7);
294 if (int_sts[i] & 0x4)
295 rtc_int_sts |= BIT(0);
296 if (int_sts[i] & 0x8)
297 rtc_int_sts |= BIT(5);
298 }
299
300 ret = rc5t583_write(rc5t583->dev, irq_clr_add[i],
301 ~int_sts[i]);
302 if (ret < 0)
303 dev_warn(rc5t583->dev,
304 "Error in reading reg 0x%02x error: %d\n",
305 irq_clr_add[i], ret);
306
307 if (main_int_type[i] & RTC_INT)
308 int_sts[i] = rtc_int_sts;
309 }
310
311 /* Merge gpio interrupts for rising and falling case*/
312 int_sts[7] |= int_sts[8];
313
314 /* Call interrupt handler if enabled */
315 for (i = 0; i < RC5T583_MAX_IRQS; ++i) {
316 const struct rc5t583_irq_data *data = &rc5t583_irqs[i];
317 if ((int_sts[data->mask_reg_index] & (1 << data->int_en_bit)) &&
318 (rc5t583->group_irq_en[data->master_bit] &
319 (1 << data->grp_index)))
320 handle_nested_irq(rc5t583->irq_base + i);
321 }
322
323 return IRQ_HANDLED;
324}
325
326static struct irq_chip rc5t583_irq_chip = {
327 .name = "rc5t583-irq",
328 .irq_mask = rc5t583_irq_mask,
329 .irq_unmask = rc5t583_irq_unmask,
330 .irq_bus_lock = rc5t583_irq_lock,
331 .irq_bus_sync_unlock = rc5t583_irq_sync_unlock,
332 .irq_set_type = rc5t583_irq_set_type,
333 .irq_set_wake = rc5t583_irq_set_wake,
334};
335
336int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base)
337{
338 int i, ret;
339
340 if (!irq_base) {
341 dev_warn(rc5t583->dev, "No interrupt support on IRQ base\n");
342 return -EINVAL;
343 }
344
345 mutex_init(&rc5t583->irq_lock);
346
347 /* Initailize all int register to 0 */
348 for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) {
349 ret = rc5t583_write(rc5t583->dev, irq_en_add[i],
350 rc5t583->irq_en_reg[i]);
351 if (ret < 0)
352 dev_warn(rc5t583->dev,
353 "Error in writing reg 0x%02x error: %d\n",
354 irq_en_add[i], ret);
355 }
356
357 for (i = 0; i < RC5T583_MAX_GPEDGE_REG; i++) {
358 ret = rc5t583_write(rc5t583->dev, gpedge_add[i],
359 rc5t583->gpedge_reg[i]);
360 if (ret < 0)
361 dev_warn(rc5t583->dev,
362 "Error in writing reg 0x%02x error: %d\n",
363 gpedge_add[i], ret);
364 }
365
366 ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN, 0x0);
367 if (ret < 0)
368 dev_warn(rc5t583->dev,
369 "Error in writing reg 0x%02x error: %d\n",
370 RC5T583_INTC_INTEN, ret);
371
372 /* Clear all interrupts in case they woke up active. */
373 for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) {
374 ret = rc5t583_write(rc5t583->dev, irq_clr_add[i], 0);
375 if (ret < 0)
376 dev_warn(rc5t583->dev,
377 "Error in writing reg 0x%02x error: %d\n",
378 irq_clr_add[i], ret);
379 }
380
381 rc5t583->irq_base = irq_base;
382 rc5t583->chip_irq = irq;
383
384 for (i = 0; i < RC5T583_MAX_IRQS; i++) {
385 int __irq = i + rc5t583->irq_base;
386 irq_set_chip_data(__irq, rc5t583);
387 irq_set_chip_and_handler(__irq, &rc5t583_irq_chip,
388 handle_simple_irq);
389 irq_set_nested_thread(__irq, 1);
390#ifdef CONFIG_ARM
391 set_irq_flags(__irq, IRQF_VALID);
392#endif
393 }
394
395 ret = request_threaded_irq(irq, NULL, rc5t583_irq, IRQF_ONESHOT,
396 "rc5t583", rc5t583);
397 if (ret < 0)
398 dev_err(rc5t583->dev,
399 "Error in registering interrupt error: %d\n", ret);
400 return ret;
401}
402
403int rc5t583_irq_exit(struct rc5t583 *rc5t583)
404{
405 if (rc5t583->chip_irq)
406 free_irq(rc5t583->chip_irq, rc5t583);
407 return 0;
408}
diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c
new file mode 100644
index 000000000000..99ef944c621d
--- /dev/null
+++ b/drivers/mfd/rc5t583.c
@@ -0,0 +1,386 @@
1/*
2 * Core driver access RC5T583 power management chip.
3 *
4 * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
5 * Author: Laxman dewangan <ldewangan@nvidia.com>
6 *
7 * Based on code
8 * Copyright (C) 2011 RICOH COMPANY,LTD
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms and conditions of the GNU General Public License,
12 * version 2, as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23#include <linux/interrupt.h>
24#include <linux/irq.h>
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/err.h>
29#include <linux/slab.h>
30#include <linux/i2c.h>
31#include <linux/mfd/core.h>
32#include <linux/mfd/rc5t583.h>
33#include <linux/regmap.h>
34
35#define RICOH_ONOFFSEL_REG 0x10
36#define RICOH_SWCTL_REG 0x5E
37
38struct deepsleep_control_data {
39 u8 reg_add;
40 u8 ds_pos_bit;
41};
42
43#define DEEPSLEEP_INIT(_id, _reg, _pos) \
44 { \
45 .reg_add = RC5T583_##_reg, \
46 .ds_pos_bit = _pos, \
47 }
48
49static struct deepsleep_control_data deepsleep_data[] = {
50 DEEPSLEEP_INIT(DC0, SLPSEQ1, 0),
51 DEEPSLEEP_INIT(DC1, SLPSEQ1, 4),
52 DEEPSLEEP_INIT(DC2, SLPSEQ2, 0),
53 DEEPSLEEP_INIT(DC3, SLPSEQ2, 4),
54 DEEPSLEEP_INIT(LDO0, SLPSEQ3, 0),
55 DEEPSLEEP_INIT(LDO1, SLPSEQ3, 4),
56 DEEPSLEEP_INIT(LDO2, SLPSEQ4, 0),
57 DEEPSLEEP_INIT(LDO3, SLPSEQ4, 4),
58 DEEPSLEEP_INIT(LDO4, SLPSEQ5, 0),
59 DEEPSLEEP_INIT(LDO5, SLPSEQ5, 4),
60 DEEPSLEEP_INIT(LDO6, SLPSEQ6, 0),
61 DEEPSLEEP_INIT(LDO7, SLPSEQ6, 4),
62 DEEPSLEEP_INIT(LDO8, SLPSEQ7, 0),
63 DEEPSLEEP_INIT(LDO9, SLPSEQ7, 4),
64 DEEPSLEEP_INIT(PSO0, SLPSEQ8, 0),
65 DEEPSLEEP_INIT(PSO1, SLPSEQ8, 4),
66 DEEPSLEEP_INIT(PSO2, SLPSEQ9, 0),
67 DEEPSLEEP_INIT(PSO3, SLPSEQ9, 4),
68 DEEPSLEEP_INIT(PSO4, SLPSEQ10, 0),
69 DEEPSLEEP_INIT(PSO5, SLPSEQ10, 4),
70 DEEPSLEEP_INIT(PSO6, SLPSEQ11, 0),
71 DEEPSLEEP_INIT(PSO7, SLPSEQ11, 4),
72};
73
74#define EXT_PWR_REQ \
75 (RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL)
76
77static struct mfd_cell rc5t583_subdevs[] = {
78 {.name = "rc5t583-regulator",},
79 {.name = "rc5t583-rtc", },
80 {.name = "rc5t583-key", }
81};
82
83int rc5t583_write(struct device *dev, uint8_t reg, uint8_t val)
84{
85 struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
86 return regmap_write(rc5t583->regmap, reg, val);
87}
88
89int rc5t583_read(struct device *dev, uint8_t reg, uint8_t *val)
90{
91 struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
92 unsigned int ival;
93 int ret;
94 ret = regmap_read(rc5t583->regmap, reg, &ival);
95 if (!ret)
96 *val = (uint8_t)ival;
97 return ret;
98}
99
100int rc5t583_set_bits(struct device *dev, unsigned int reg,
101 unsigned int bit_mask)
102{
103 struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
104 return regmap_update_bits(rc5t583->regmap, reg, bit_mask, bit_mask);
105}
106
107int rc5t583_clear_bits(struct device *dev, unsigned int reg,
108 unsigned int bit_mask)
109{
110 struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
111 return regmap_update_bits(rc5t583->regmap, reg, bit_mask, 0);
112}
113
114int rc5t583_update(struct device *dev, unsigned int reg,
115 unsigned int val, unsigned int mask)
116{
117 struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
118 return regmap_update_bits(rc5t583->regmap, reg, mask, val);
119}
120
121static int __rc5t583_set_ext_pwrreq1_control(struct device *dev,
122 int id, int ext_pwr, int slots)
123{
124 int ret;
125 uint8_t sleepseq_val;
126 unsigned int en_bit;
127 unsigned int slot_bit;
128
129 if (id == RC5T583_DS_DC0) {
130 dev_err(dev, "PWRREQ1 is invalid control for rail %d\n", id);
131 return -EINVAL;
132 }
133
134 en_bit = deepsleep_data[id].ds_pos_bit;
135 slot_bit = en_bit + 1;
136 ret = rc5t583_read(dev, deepsleep_data[id].reg_add, &sleepseq_val);
137 if (ret < 0) {
138 dev_err(dev, "Error in reading reg 0x%x\n",
139 deepsleep_data[id].reg_add);
140 return ret;
141 }
142
143 sleepseq_val &= ~(0xF << en_bit);
144 sleepseq_val |= BIT(en_bit);
145 sleepseq_val |= ((slots & 0x7) << slot_bit);
146 ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(1));
147 if (ret < 0) {
148 dev_err(dev, "Error in updating the 0x%02x register\n",
149 RICOH_ONOFFSEL_REG);
150 return ret;
151 }
152
153 ret = rc5t583_write(dev, deepsleep_data[id].reg_add, sleepseq_val);
154 if (ret < 0) {
155 dev_err(dev, "Error in writing reg 0x%x\n",
156 deepsleep_data[id].reg_add);
157 return ret;
158 }
159
160 if (id == RC5T583_DS_LDO4) {
161 ret = rc5t583_write(dev, RICOH_SWCTL_REG, 0x1);
162 if (ret < 0)
163 dev_err(dev, "Error in writing reg 0x%x\n",
164 RICOH_SWCTL_REG);
165 }
166 return ret;
167}
168
169static int __rc5t583_set_ext_pwrreq2_control(struct device *dev,
170 int id, int ext_pwr)
171{
172 int ret;
173
174 if (id != RC5T583_DS_DC0) {
175 dev_err(dev, "PWRREQ2 is invalid control for rail %d\n", id);
176 return -EINVAL;
177 }
178
179 ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(2));
180 if (ret < 0)
181 dev_err(dev, "Error in updating the ONOFFSEL 0x10 register\n");
182 return ret;
183}
184
185int rc5t583_ext_power_req_config(struct device *dev, int ds_id,
186 int ext_pwr_req, int deepsleep_slot_nr)
187{
188 if ((ext_pwr_req & EXT_PWR_REQ) == EXT_PWR_REQ)
189 return -EINVAL;
190
191 if (ext_pwr_req & RC5T583_EXT_PWRREQ1_CONTROL)
192 return __rc5t583_set_ext_pwrreq1_control(dev, ds_id,
193 ext_pwr_req, deepsleep_slot_nr);
194
195 if (ext_pwr_req & RC5T583_EXT_PWRREQ2_CONTROL)
196 return __rc5t583_set_ext_pwrreq2_control(dev,
197 ds_id, ext_pwr_req);
198 return 0;
199}
200
201static int rc5t583_clear_ext_power_req(struct rc5t583 *rc5t583,
202 struct rc5t583_platform_data *pdata)
203{
204 int ret;
205 int i;
206 uint8_t on_off_val = 0;
207
208 /* Clear ONOFFSEL register */
209 if (pdata->enable_shutdown)
210 on_off_val = 0x1;
211
212 ret = rc5t583_write(rc5t583->dev, RICOH_ONOFFSEL_REG, on_off_val);
213 if (ret < 0)
214 dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n",
215 RICOH_ONOFFSEL_REG, ret);
216
217 ret = rc5t583_write(rc5t583->dev, RICOH_SWCTL_REG, 0x0);
218 if (ret < 0)
219 dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n",
220 RICOH_SWCTL_REG, ret);
221
222 /* Clear sleep sequence register */
223 for (i = RC5T583_SLPSEQ1; i <= RC5T583_SLPSEQ11; ++i) {
224 ret = rc5t583_write(rc5t583->dev, i, 0x0);
225 if (ret < 0)
226 dev_warn(rc5t583->dev,
227 "Error in writing reg 0x%02x error: %d\n",
228 i, ret);
229 }
230 return 0;
231}
232
233static bool volatile_reg(struct device *dev, unsigned int reg)
234{
235 /* Enable caching in interrupt registers */
236 switch (reg) {
237 case RC5T583_INT_EN_SYS1:
238 case RC5T583_INT_EN_SYS2:
239 case RC5T583_INT_EN_DCDC:
240 case RC5T583_INT_EN_RTC:
241 case RC5T583_INT_EN_ADC1:
242 case RC5T583_INT_EN_ADC2:
243 case RC5T583_INT_EN_ADC3:
244 case RC5T583_GPIO_GPEDGE1:
245 case RC5T583_GPIO_GPEDGE2:
246 case RC5T583_GPIO_EN_INT:
247 return false;
248
249 case RC5T583_GPIO_MON_IOIN:
250 /* This is gpio input register */
251 return true;
252
253 default:
254 /* Enable caching in gpio registers */
255 if ((reg >= RC5T583_GPIO_IOSEL) &&
256 (reg <= RC5T583_GPIO_GPOFUNC))
257 return false;
258
259 /* Enable caching in sleep seq registers */
260 if ((reg >= RC5T583_SLPSEQ1) && (reg <= RC5T583_SLPSEQ11))
261 return false;
262
263 /* Enable caching of regulator registers */
264 if ((reg >= RC5T583_REG_DC0CTL) && (reg <= RC5T583_REG_SR3CTL))
265 return false;
266 if ((reg >= RC5T583_REG_LDOEN1) &&
267 (reg <= RC5T583_REG_LDO9DAC_DS))
268 return false;
269
270 break;
271 }
272
273 return true;
274}
275
276static const struct regmap_config rc5t583_regmap_config = {
277 .reg_bits = 8,
278 .val_bits = 8,
279 .volatile_reg = volatile_reg,
280 .max_register = RC5T583_MAX_REGS,
281 .num_reg_defaults_raw = RC5T583_MAX_REGS,
282 .cache_type = REGCACHE_RBTREE,
283};
284
285static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
286 const struct i2c_device_id *id)
287{
288 struct rc5t583 *rc5t583;
289 struct rc5t583_platform_data *pdata = i2c->dev.platform_data;
290 int ret;
291 bool irq_init_success = false;
292
293 if (!pdata) {
294 dev_err(&i2c->dev, "Err: Platform data not found\n");
295 return -EINVAL;
296 }
297
298 rc5t583 = devm_kzalloc(&i2c->dev, sizeof(struct rc5t583), GFP_KERNEL);
299 if (!rc5t583) {
300 dev_err(&i2c->dev, "Memory allocation failed\n");
301 return -ENOMEM;
302 }
303
304 rc5t583->dev = &i2c->dev;
305 i2c_set_clientdata(i2c, rc5t583);
306
307 rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config);
308 if (IS_ERR(rc5t583->regmap)) {
309 ret = PTR_ERR(rc5t583->regmap);
310 dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
311 return ret;
312 }
313
314 ret = rc5t583_clear_ext_power_req(rc5t583, pdata);
315 if (ret < 0)
316 goto err_irq_init;
317
318 if (i2c->irq) {
319 ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base);
320 /* Still continue with waring if irq init fails */
321 if (ret)
322 dev_warn(&i2c->dev, "IRQ init failed: %d\n", ret);
323 else
324 irq_init_success = true;
325 }
326
327 ret = mfd_add_devices(rc5t583->dev, -1, rc5t583_subdevs,
328 ARRAY_SIZE(rc5t583_subdevs), NULL, 0);
329 if (ret) {
330 dev_err(&i2c->dev, "add mfd devices failed: %d\n", ret);
331 goto err_add_devs;
332 }
333
334 return 0;
335
336err_add_devs:
337 if (irq_init_success)
338 rc5t583_irq_exit(rc5t583);
339err_irq_init:
340 regmap_exit(rc5t583->regmap);
341 return ret;
342}
343
344static int __devexit rc5t583_i2c_remove(struct i2c_client *i2c)
345{
346 struct rc5t583 *rc5t583 = i2c_get_clientdata(i2c);
347
348 mfd_remove_devices(rc5t583->dev);
349 rc5t583_irq_exit(rc5t583);
350 regmap_exit(rc5t583->regmap);
351 return 0;
352}
353
354static const struct i2c_device_id rc5t583_i2c_id[] = {
355 {.name = "rc5t583", .driver_data = 0},
356 {}
357};
358
359MODULE_DEVICE_TABLE(i2c, rc5t583_i2c_id);
360
361static struct i2c_driver rc5t583_i2c_driver = {
362 .driver = {
363 .name = "rc5t583",
364 .owner = THIS_MODULE,
365 },
366 .probe = rc5t583_i2c_probe,
367 .remove = __devexit_p(rc5t583_i2c_remove),
368 .id_table = rc5t583_i2c_id,
369};
370
371static int __init rc5t583_i2c_init(void)
372{
373 return i2c_add_driver(&rc5t583_i2c_driver);
374}
375subsys_initcall(rc5t583_i2c_init);
376
377static void __exit rc5t583_i2c_exit(void)
378{
379 i2c_del_driver(&rc5t583_i2c_driver);
380}
381
382module_exit(rc5t583_i2c_exit);
383
384MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
385MODULE_DESCRIPTION("RICOH RC5T583 power management system device driver");
386MODULE_LICENSE("GPL v2");