diff options
author | Christopher Kenna <cjk@cs.unc.edu> | 2012-09-28 13:46:28 -0400 |
---|---|---|
committer | Christopher Kenna <cjk@cs.unc.edu> | 2012-09-28 14:50:15 -0400 |
commit | daa22703f14c007e93b464c45fa60019a36f546d (patch) | |
tree | a1a130b6e128dc9d57c35c026977e1b4953105e1 /drivers/mfd | |
parent | 5aa287dcf1b5879aa0150b0511833c52885f5b4c (diff) |
Apply k4412 kernel from HardKernel for ODROID-X.
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/Kconfig | 31 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 3 | ||||
-rw-r--r-- | drivers/mfd/max77686-irq.c | 251 | ||||
-rw-r--r-- | drivers/mfd/max77686.c | 388 | ||||
-rw-r--r-- | drivers/mfd/max8698.c | 161 | ||||
-rw-r--r-- | drivers/mfd/max8997.c | 32 | ||||
-rw-r--r-- | drivers/mfd/s5m-core.c | 248 | ||||
-rw-r--r-- | drivers/mfd/s5m-irq.c | 487 | ||||
-rw-r--r-- | drivers/mfd/wm8994-core.c | 16 |
9 files changed, 1615 insertions, 2 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6ca938a6bf9..81a184098e1 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -349,6 +349,37 @@ config MFD_MAX8998 | |||
349 | additional drivers must be enabled in order to use the functionality | 349 | additional drivers must be enabled in order to use the functionality |
350 | of the device. | 350 | of the device. |
351 | 351 | ||
352 | config MFD_MAX8698 | ||
353 | bool "Maxim Semiconductor MAX8698 PMIC Support" | ||
354 | depends on I2C=y | ||
355 | select MFD_CORE | ||
356 | help | ||
357 | Say yes here to support for Maxim Semiconductor MAX8698. This is | ||
358 | a Power Management IC. This driver provies common support for | ||
359 | accessing the device, additional drivers must be enabled in order | ||
360 | to use the functionality of the device. | ||
361 | |||
362 | config MFD_MAX77686 | ||
363 | bool "Maxim Semiconductor MAX77686 PMIC Support" | ||
364 | depends on I2C=y && GENERIC_HARDIRQS | ||
365 | select MFD_CORE | ||
366 | help | ||
367 | Say yes here to support for Maxim Semiconductor MAX77686. | ||
368 | This is a Power Management IC with RTC on chip. | ||
369 | This driver provides common support for accessing the device; | ||
370 | additional drivers must be enabled in order to use the functionality | ||
371 | of the device. | ||
372 | |||
373 | config MFD_S5M_CORE | ||
374 | bool "SAMSUNG S5M Series Support" | ||
375 | depends on I2C=y && GENERIC_HARDIRQS | ||
376 | select MFD_CORE | ||
377 | help | ||
378 | Support for the Samsung Electronics S5M MFD series. | ||
379 | This driver provies common support for accessing the device, | ||
380 | additional drivers must be enabled in order to use the functionality | ||
381 | of the device | ||
382 | |||
352 | config MFD_WM8400 | 383 | config MFD_WM8400 |
353 | tristate "Support Wolfson Microelectronics WM8400" | 384 | tristate "Support Wolfson Microelectronics WM8400" |
354 | select MFD_CORE | 385 | select MFD_CORE |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d7d47d2a4c7..a761c91e227 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
@@ -64,6 +64,8 @@ max8925-objs := max8925-core.o max8925-i2c.o | |||
64 | obj-$(CONFIG_MFD_MAX8925) += max8925.o | 64 | obj-$(CONFIG_MFD_MAX8925) += max8925.o |
65 | obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o | 65 | obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o |
66 | obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o | 66 | obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o |
67 | obj-$(CONFIG_MFD_MAX8698) += max8698.o | ||
68 | obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o | ||
67 | 69 | ||
68 | pcf50633-objs := pcf50633-core.o pcf50633-irq.o | 70 | pcf50633-objs := pcf50633-core.o pcf50633-irq.o |
69 | obj-$(CONFIG_MFD_PCF50633) += pcf50633.o | 71 | obj-$(CONFIG_MFD_PCF50633) += pcf50633.o |
@@ -95,3 +97,4 @@ obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o | |||
95 | obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o | 97 | obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o |
96 | obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o | 98 | obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o |
97 | obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o | 99 | obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o |
100 | obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o | ||
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c new file mode 100644 index 00000000000..8b58e3d36a6 --- /dev/null +++ b/drivers/mfd/max77686-irq.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | * max77686-irq.c - Interrupt controller support for MAX77686 | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | ||
5 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * This driver is based on max8998-irq.c | ||
22 | */ | ||
23 | |||
24 | #include <linux/err.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/mfd/max77686.h> | ||
28 | #include <linux/mfd/max77686-private.h> | ||
29 | |||
30 | static const u8 max77686_mask_reg[] = { | ||
31 | [PMIC_INT1] = MAX77686_REG_INT1MSK, | ||
32 | [PMIC_INT2] = MAX77686_REG_INT2MSK, | ||
33 | }; | ||
34 | |||
35 | static struct i2c_client *get_i2c(struct max77686_dev *max77686, | ||
36 | enum max77686_irq_source src) | ||
37 | { | ||
38 | switch (src) { | ||
39 | case PMIC_INT1 ... PMIC_INT2: | ||
40 | return max77686->i2c; | ||
41 | default: | ||
42 | return ERR_PTR(-EINVAL); | ||
43 | } | ||
44 | |||
45 | return ERR_PTR(-EINVAL); | ||
46 | } | ||
47 | |||
48 | struct max77686_irq_data { | ||
49 | int mask; | ||
50 | enum max77686_irq_source group; | ||
51 | }; | ||
52 | |||
53 | #define DECLARE_IRQ(idx, _group, _mask) \ | ||
54 | [(idx)] = { .group = (_group), .mask = (_mask) } | ||
55 | static const struct max77686_irq_data max77686_irqs[] = { | ||
56 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_PWRONF, PMIC_INT1, 1 << 0), | ||
57 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_PWRONR, PMIC_INT1, 1 << 1), | ||
58 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_JIGONF, PMIC_INT1, 1 << 2), | ||
59 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_JIGONR, PMIC_INT1, 1 << 3), | ||
60 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_ACOKBF, PMIC_INT1, 1 << 4), | ||
61 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_ACOKBR, PMIC_INT1, 1 << 5), | ||
62 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_ONKEY1S, PMIC_INT1, 1 << 6), | ||
63 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_MRSTB, PMIC_INT1, 1 << 7), | ||
64 | |||
65 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_120C, PMIC_INT2, 1 << 0), | ||
66 | DECLARE_IRQ(MAX77686_TOPSYSIRQ_140C, PMIC_INT2, 1 << 1), | ||
67 | }; | ||
68 | |||
69 | static void max77686_irq_lock(struct irq_data *data) | ||
70 | { | ||
71 | struct max77686_dev *max77686 = irq_get_chip_data(data->irq); | ||
72 | |||
73 | mutex_lock(&max77686->irqlock); | ||
74 | } | ||
75 | |||
76 | static void max77686_irq_sync_unlock(struct irq_data *data) | ||
77 | { | ||
78 | struct max77686_dev *max77686 = irq_get_chip_data(data->irq); | ||
79 | int i; | ||
80 | |||
81 | for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) { | ||
82 | u8 mask_reg = max77686_mask_reg[i]; | ||
83 | struct i2c_client *i2c = get_i2c(max77686, i); | ||
84 | |||
85 | if (mask_reg == MAX77686_REG_INVALID || | ||
86 | IS_ERR_OR_NULL(i2c)) | ||
87 | continue; | ||
88 | max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i]; | ||
89 | |||
90 | max77686_write_reg(i2c, max77686_mask_reg[i], | ||
91 | max77686->irq_masks_cur[i]); | ||
92 | } | ||
93 | |||
94 | mutex_unlock(&max77686->irqlock); | ||
95 | } | ||
96 | |||
97 | static const inline struct max77686_irq_data * | ||
98 | irq_to_max77686_irq(struct max77686_dev *max77686, int irq) | ||
99 | { | ||
100 | return &max77686_irqs[irq - max77686->irq_base]; | ||
101 | } | ||
102 | |||
103 | static void max77686_irq_mask(struct irq_data *data) | ||
104 | { | ||
105 | struct max77686_dev *max77686 = irq_get_chip_data(data->irq); | ||
106 | const struct max77686_irq_data *irq_data = irq_to_max77686_irq(max77686, | ||
107 | data->irq); | ||
108 | |||
109 | max77686->irq_masks_cur[irq_data->group] |= irq_data->mask; | ||
110 | } | ||
111 | |||
112 | static void max77686_irq_unmask(struct irq_data *data) | ||
113 | { | ||
114 | struct max77686_dev *max77686 = irq_get_chip_data(data->irq); | ||
115 | const struct max77686_irq_data *irq_data = irq_to_max77686_irq(max77686, | ||
116 | data->irq); | ||
117 | |||
118 | max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask; | ||
119 | } | ||
120 | |||
121 | static struct irq_chip max77686_irq_chip = { | ||
122 | .name = "max77686", | ||
123 | .irq_bus_lock = max77686_irq_lock, | ||
124 | .irq_bus_sync_unlock = max77686_irq_sync_unlock, | ||
125 | .irq_mask = max77686_irq_mask, | ||
126 | .irq_unmask = max77686_irq_unmask, | ||
127 | }; | ||
128 | |||
129 | #define MAX77686_IRQSRC_PMIC (1 << 1) | ||
130 | static irqreturn_t max77686_irq_thread(int irq, void *data) | ||
131 | { | ||
132 | struct max77686_dev *max77686 = data; | ||
133 | u8 irq_reg[MAX77686_IRQ_GROUP_NR] = {}; | ||
134 | u8 irq_src; | ||
135 | int ret; | ||
136 | int i; | ||
137 | |||
138 | ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INTSRC, &irq_src); | ||
139 | if (ret < 0) { | ||
140 | dev_err(max77686->dev, "Failed to read interrupt source: %d\n", | ||
141 | ret); | ||
142 | return IRQ_NONE; | ||
143 | } | ||
144 | |||
145 | if (irq_src & MAX77686_IRQSRC_PMIC) { | ||
146 | /* PMIC INT1 ~ INT4 */ | ||
147 | max77686_bulk_read(max77686->i2c, MAX77686_REG_INT1, 2, | ||
148 | &irq_reg[PMIC_INT1]); | ||
149 | } | ||
150 | |||
151 | /* Apply masking */ | ||
152 | for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) | ||
153 | irq_reg[i] &= ~max77686->irq_masks_cur[i]; | ||
154 | |||
155 | /* Report */ | ||
156 | for (i = 0; i < MAX77686_IRQ_NR; i++) { | ||
157 | if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) | ||
158 | handle_nested_irq(max77686->irq_base + i); | ||
159 | } | ||
160 | |||
161 | return IRQ_HANDLED; | ||
162 | } | ||
163 | |||
164 | int max77686_irq_resume(struct max77686_dev *max77686) | ||
165 | { | ||
166 | if (max77686->irq && max77686->irq_base) | ||
167 | max77686_irq_thread(max77686->irq_base, max77686); | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | int max77686_irq_init(struct max77686_dev *max77686) | ||
172 | { | ||
173 | int i; | ||
174 | int cur_irq; | ||
175 | int ret; | ||
176 | |||
177 | if (!max77686->irq) { | ||
178 | dev_warn(max77686->dev, "No interrupt specified.\n"); | ||
179 | max77686->irq_base = 0; | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | if (!max77686->irq_base) { | ||
184 | dev_err(max77686->dev, "No interrupt base specified.\n"); | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | mutex_init(&max77686->irqlock); | ||
189 | |||
190 | /* Mask individual interrupt sources */ | ||
191 | for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) { | ||
192 | struct i2c_client *i2c; | ||
193 | |||
194 | max77686->irq_masks_cur[i] = 0xff; | ||
195 | max77686->irq_masks_cache[i] = 0xff; | ||
196 | i2c = get_i2c(max77686, i); | ||
197 | |||
198 | if (IS_ERR_OR_NULL(i2c)) | ||
199 | continue; | ||
200 | if (max77686_mask_reg[i] == MAX77686_REG_INVALID) | ||
201 | continue; | ||
202 | |||
203 | max77686_write_reg(i2c, max77686_mask_reg[i], 0xff); | ||
204 | } | ||
205 | |||
206 | /* Register with genirq */ | ||
207 | for (i = 0; i < MAX77686_IRQ_NR; i++) { | ||
208 | cur_irq = i + max77686->irq_base; | ||
209 | irq_set_chip_data(cur_irq, max77686); | ||
210 | irq_set_chip_and_handler(cur_irq, &max77686_irq_chip, | ||
211 | handle_edge_irq); | ||
212 | irq_set_nested_thread(cur_irq, 1); | ||
213 | #ifdef CONFIG_ARM | ||
214 | set_irq_flags(cur_irq, IRQF_VALID); | ||
215 | #else | ||
216 | irq_set_noprobe(cur_irq); | ||
217 | #endif | ||
218 | } | ||
219 | |||
220 | ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread, | ||
221 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | ||
222 | "max77686-irq", max77686); | ||
223 | |||
224 | if (ret) { | ||
225 | dev_err(max77686->dev, "Failed to request IRQ %d: %d\n", | ||
226 | max77686->irq, ret); | ||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | if (!max77686->ono) | ||
231 | return 0; | ||
232 | |||
233 | ret = request_threaded_irq(max77686->ono, NULL, max77686_irq_thread, | ||
234 | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | | ||
235 | IRQF_ONESHOT, "max77686-ono", max77686); | ||
236 | |||
237 | if (ret) | ||
238 | dev_err(max77686->dev, "Failed to request ono-IRQ %d: %d\n", | ||
239 | max77686->ono, ret); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | void max77686_irq_exit(struct max77686_dev *max77686) | ||
245 | { | ||
246 | if (max77686->ono) | ||
247 | free_irq(max77686->ono, max77686); | ||
248 | |||
249 | if (max77686->irq) | ||
250 | free_irq(max77686->irq, max77686); | ||
251 | } | ||
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c new file mode 100644 index 00000000000..ba94af7e26b --- /dev/null +++ b/drivers/mfd/max77686.c | |||
@@ -0,0 +1,388 @@ | |||
1 | /* | ||
2 | * max77686.c - mfd core driver for the Maxim 8966 and 8997 | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics | ||
5 | * MyungJoo Ham <myungjoo.ham@smasung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * This driver is based on max8998.c | ||
22 | */ | ||
23 | |||
24 | #include <linux/slab.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/pm_runtime.h> | ||
28 | #include <linux/mutex.h> | ||
29 | #include <linux/mfd/core.h> | ||
30 | #include <linux/mfd/max77686.h> | ||
31 | #include <linux/mfd/max77686-private.h> | ||
32 | |||
33 | #define I2C_ADDR_PMIC (0x12 >> 1) | ||
34 | #define I2C_ADDR_RTC (0x0C >> 1) | ||
35 | |||
36 | static struct mfd_cell max77686_devs[] = { | ||
37 | { .name = "max77686-pmic", }, | ||
38 | { .name = "max77686-rtc", }, | ||
39 | }; | ||
40 | |||
41 | int max77686_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) | ||
42 | { | ||
43 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
44 | int ret; | ||
45 | |||
46 | mutex_lock(&max77686->iolock); | ||
47 | ret = i2c_smbus_read_byte_data(i2c, reg); | ||
48 | mutex_unlock(&max77686->iolock); | ||
49 | if (ret < 0) | ||
50 | return ret; | ||
51 | |||
52 | ret &= 0xff; | ||
53 | *dest = ret; | ||
54 | return 0; | ||
55 | } | ||
56 | EXPORT_SYMBOL_GPL(max77686_read_reg); | ||
57 | |||
58 | int max77686_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf) | ||
59 | { | ||
60 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
61 | int ret; | ||
62 | |||
63 | mutex_lock(&max77686->iolock); | ||
64 | ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf); | ||
65 | mutex_unlock(&max77686->iolock); | ||
66 | if (ret < 0) | ||
67 | return ret; | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | EXPORT_SYMBOL_GPL(max77686_bulk_read); | ||
72 | |||
73 | int max77686_write_reg(struct i2c_client *i2c, u8 reg, u8 value) | ||
74 | { | ||
75 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
76 | int ret; | ||
77 | |||
78 | mutex_lock(&max77686->iolock); | ||
79 | ret = i2c_smbus_write_byte_data(i2c, reg, value); | ||
80 | mutex_unlock(&max77686->iolock); | ||
81 | return ret; | ||
82 | } | ||
83 | EXPORT_SYMBOL_GPL(max77686_write_reg); | ||
84 | |||
85 | int max77686_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf) | ||
86 | { | ||
87 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
88 | int ret; | ||
89 | |||
90 | mutex_lock(&max77686->iolock); | ||
91 | ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf); | ||
92 | mutex_unlock(&max77686->iolock); | ||
93 | if (ret < 0) | ||
94 | return ret; | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | EXPORT_SYMBOL_GPL(max77686_bulk_write); | ||
99 | |||
100 | int max77686_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) | ||
101 | { | ||
102 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
103 | int ret; | ||
104 | |||
105 | mutex_lock(&max77686->iolock); | ||
106 | ret = i2c_smbus_read_byte_data(i2c, reg); | ||
107 | if (ret >= 0) { | ||
108 | u8 old_val = ret & 0xff; | ||
109 | u8 new_val = (val & mask) | (old_val & (~mask)); | ||
110 | ret = i2c_smbus_write_byte_data(i2c, reg, new_val); | ||
111 | } | ||
112 | mutex_unlock(&max77686->iolock); | ||
113 | return ret; | ||
114 | } | ||
115 | EXPORT_SYMBOL_GPL(max77686_update_reg); | ||
116 | |||
117 | static int max77686_i2c_probe(struct i2c_client *i2c, | ||
118 | const struct i2c_device_id *id) | ||
119 | { | ||
120 | struct max77686_dev *max77686; | ||
121 | struct max77686_platform_data *pdata = i2c->dev.platform_data; | ||
122 | int ret = 0; | ||
123 | |||
124 | max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL); | ||
125 | if (max77686 == NULL) | ||
126 | return -ENOMEM; | ||
127 | |||
128 | i2c_set_clientdata(i2c, max77686); | ||
129 | max77686->dev = &i2c->dev; | ||
130 | max77686->i2c = i2c; | ||
131 | max77686->type = id->driver_data; | ||
132 | max77686->irq = i2c->irq; | ||
133 | |||
134 | if (!pdata) | ||
135 | goto err; | ||
136 | |||
137 | max77686->irq_base = pdata->irq_base; | ||
138 | max77686->ono = pdata->ono; | ||
139 | |||
140 | mutex_init(&max77686->iolock); | ||
141 | |||
142 | max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); | ||
143 | i2c_set_clientdata(max77686->rtc, max77686); | ||
144 | |||
145 | pm_runtime_set_active(max77686->dev); | ||
146 | |||
147 | max77686_irq_init(max77686); | ||
148 | |||
149 | mfd_add_devices(max77686->dev, -1, max77686_devs, | ||
150 | ARRAY_SIZE(max77686_devs), | ||
151 | NULL, 0); | ||
152 | |||
153 | /* | ||
154 | * TODO: enable others (flash, muic, rtc, battery, ...) and | ||
155 | * check the return value | ||
156 | */ | ||
157 | |||
158 | if (ret < 0) | ||
159 | goto err_mfd; | ||
160 | |||
161 | /* MAX77686 has a power button input. */ | ||
162 | device_init_wakeup(max77686->dev, pdata->wakeup); | ||
163 | |||
164 | return ret; | ||
165 | |||
166 | err_mfd: | ||
167 | mfd_remove_devices(max77686->dev); | ||
168 | i2c_unregister_device(max77686->rtc); | ||
169 | err: | ||
170 | kfree(max77686); | ||
171 | return ret; | ||
172 | } | ||
173 | |||
174 | static int max77686_i2c_remove(struct i2c_client *i2c) | ||
175 | { | ||
176 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
177 | |||
178 | mfd_remove_devices(max77686->dev); | ||
179 | i2c_unregister_device(max77686->rtc); | ||
180 | kfree(max77686); | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static const struct i2c_device_id max77686_i2c_id[] = { | ||
186 | { "max77686", 0 }, | ||
187 | { } | ||
188 | }; | ||
189 | MODULE_DEVICE_TABLE(i2c, max8998_i2c_id); | ||
190 | |||
191 | u8 max77686_dumpaddr_pmic[] = { | ||
192 | MAX77686_REG_DEVICE_ID, | ||
193 | MAX77686_REG_INTSRC, | ||
194 | MAX77686_REG_INT1, | ||
195 | MAX77686_REG_INT2, | ||
196 | MAX77686_REG_INT1MSK, | ||
197 | MAX77686_REG_INT2MSK, | ||
198 | |||
199 | MAX77686_REG_STATUS1, | ||
200 | MAX77686_REG_STATUS2, | ||
201 | |||
202 | MAX77686_REG_PWRON, | ||
203 | MAX77686_REG_ONOFFDELAY, | ||
204 | MAX77686_REG_MRSTB, | ||
205 | |||
206 | MAX77686_REG_BUCK1CTRL, | ||
207 | MAX77686_REG_BUCK1OUT, | ||
208 | |||
209 | MAX77686_REG_BUCK2CTRL1, | ||
210 | MAX77686_REG_BUCK234FREQ, | ||
211 | MAX77686_REG_BUCK2DVS1, | ||
212 | MAX77686_REG_BUCK2DVS2, | ||
213 | MAX77686_REG_BUCK2DVS3, | ||
214 | MAX77686_REG_BUCK2DVS4, | ||
215 | MAX77686_REG_BUCK2DVS5, | ||
216 | MAX77686_REG_BUCK2DVS6, | ||
217 | MAX77686_REG_BUCK2DVS7, | ||
218 | MAX77686_REG_BUCK2DVS8, | ||
219 | |||
220 | MAX77686_REG_BUCK3CTRL1, | ||
221 | MAX77686_REG_BUCK3DVS1, | ||
222 | MAX77686_REG_BUCK3DVS2, | ||
223 | MAX77686_REG_BUCK3DVS3, | ||
224 | MAX77686_REG_BUCK3DVS4, | ||
225 | MAX77686_REG_BUCK3DVS5, | ||
226 | MAX77686_REG_BUCK3DVS6, | ||
227 | MAX77686_REG_BUCK3DVS7, | ||
228 | MAX77686_REG_BUCK3DVS8, | ||
229 | |||
230 | MAX77686_REG_BUCK4CTRL1, | ||
231 | MAX77686_REG_BUCK4DVS1, | ||
232 | MAX77686_REG_BUCK4DVS2, | ||
233 | MAX77686_REG_BUCK4DVS3, | ||
234 | MAX77686_REG_BUCK4DVS4, | ||
235 | MAX77686_REG_BUCK4DVS5, | ||
236 | MAX77686_REG_BUCK4DVS6, | ||
237 | MAX77686_REG_BUCK4DVS7, | ||
238 | MAX77686_REG_BUCK4DVS8, | ||
239 | |||
240 | MAX77686_REG_BUCK5CTRL, | ||
241 | MAX77686_REG_BUCK5OUT, | ||
242 | MAX77686_REG_BUCK6CTRL, | ||
243 | MAX77686_REG_BUCK6OUT, | ||
244 | MAX77686_REG_BUCK7CTRL, | ||
245 | MAX77686_REG_BUCK7OUT, | ||
246 | MAX77686_REG_BUCK8CTRL, | ||
247 | MAX77686_REG_BUCK8OUT, | ||
248 | MAX77686_REG_BUCK9CTRL, | ||
249 | MAX77686_REG_BUCK9OUT, | ||
250 | |||
251 | MAX77686_REG_LDO1CTRL1, | ||
252 | MAX77686_REG_LDO2CTRL1, | ||
253 | MAX77686_REG_LDO3CTRL1, | ||
254 | MAX77686_REG_LDO4CTRL1, | ||
255 | MAX77686_REG_LDO5CTRL1, | ||
256 | MAX77686_REG_LDO6CTRL1, | ||
257 | MAX77686_REG_LDO7CTRL1, | ||
258 | MAX77686_REG_LDO8CTRL1, | ||
259 | MAX77686_REG_LDO9CTRL1, | ||
260 | MAX77686_REG_LDO10CTRL1, | ||
261 | MAX77686_REG_LDO11CTRL1, | ||
262 | MAX77686_REG_LDO12CTRL1, | ||
263 | MAX77686_REG_LDO13CTRL1, | ||
264 | MAX77686_REG_LDO14CTRL1, | ||
265 | MAX77686_REG_LDO15CTRL1, | ||
266 | MAX77686_REG_LDO16CTRL1, | ||
267 | MAX77686_REG_LDO17CTRL1, | ||
268 | MAX77686_REG_LDO18CTRL1, | ||
269 | MAX77686_REG_LDO19CTRL1, | ||
270 | MAX77686_REG_LDO20CTRL1, | ||
271 | MAX77686_REG_LDO21CTRL1, | ||
272 | MAX77686_REG_LDO22CTRL1, | ||
273 | MAX77686_REG_LDO23CTRL1, | ||
274 | MAX77686_REG_LDO24CTRL1, | ||
275 | MAX77686_REG_LDO25CTRL1, | ||
276 | MAX77686_REG_LDO26CTRL1, | ||
277 | |||
278 | MAX77686_REG_LDO1CTRL2, | ||
279 | MAX77686_REG_LDO2CTRL2, | ||
280 | MAX77686_REG_LDO3CTRL2, | ||
281 | MAX77686_REG_LDO4CTRL2, | ||
282 | MAX77686_REG_LDO5CTRL2, | ||
283 | MAX77686_REG_LDO6CTRL2, | ||
284 | MAX77686_REG_LDO7CTRL2, | ||
285 | MAX77686_REG_LDO8CTRL2, | ||
286 | MAX77686_REG_LDO9CTRL2, | ||
287 | MAX77686_REG_LDO10CTRL2, | ||
288 | MAX77686_REG_LDO11CTRL2, | ||
289 | MAX77686_REG_LDO12CTRL2, | ||
290 | MAX77686_REG_LDO13CTRL2, | ||
291 | MAX77686_REG_LDO14CTRL2, | ||
292 | MAX77686_REG_LDO15CTRL2, | ||
293 | MAX77686_REG_LDO16CTRL2, | ||
294 | MAX77686_REG_LDO17CTRL2, | ||
295 | MAX77686_REG_LDO18CTRL2, | ||
296 | MAX77686_REG_LDO19CTRL2, | ||
297 | MAX77686_REG_LDO20CTRL2, | ||
298 | MAX77686_REG_LDO21CTRL2, | ||
299 | MAX77686_REG_LDO22CTRL2, | ||
300 | MAX77686_REG_LDO23CTRL2, | ||
301 | MAX77686_REG_LDO24CTRL2, | ||
302 | MAX77686_REG_LDO25CTRL2, | ||
303 | MAX77686_REG_LDO26CTRL2, | ||
304 | |||
305 | MAX77686_REG_BBAT_CHARGER, | ||
306 | MAX77686_REG_32KHZ, | ||
307 | }; | ||
308 | |||
309 | static int max77686_freeze(struct device *dev) | ||
310 | { | ||
311 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
312 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
313 | int i; | ||
314 | |||
315 | for (i = 0; i < ARRAY_SIZE(max77686_dumpaddr_pmic); i++) | ||
316 | max77686_read_reg(i2c, max77686_dumpaddr_pmic[i], | ||
317 | &max77686->reg_dump[i]); | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static int max77686_restore(struct device *dev) | ||
323 | { | ||
324 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
325 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
326 | int i; | ||
327 | |||
328 | for (i = 0; i < ARRAY_SIZE(max77686_dumpaddr_pmic); i++) | ||
329 | max77686_write_reg(i2c, max77686_dumpaddr_pmic[i], | ||
330 | max77686->reg_dump[i]); | ||
331 | |||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static int max77686_suspend(struct device *dev) | ||
336 | { | ||
337 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
338 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
339 | |||
340 | if (device_may_wakeup(dev)) | ||
341 | irq_set_irq_wake(max77686->irq, 1); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int max77686_resume(struct device *dev) | ||
346 | { | ||
347 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
348 | struct max77686_dev *max77686 = i2c_get_clientdata(i2c); | ||
349 | |||
350 | if (device_may_wakeup(dev)) | ||
351 | irq_set_irq_wake(max77686->irq, 0); | ||
352 | return max77686_irq_resume(max77686); | ||
353 | } | ||
354 | |||
355 | const struct dev_pm_ops max77686_pm = { | ||
356 | .suspend = max77686_suspend, | ||
357 | .resume = max77686_resume, | ||
358 | .freeze = max77686_freeze, | ||
359 | .restore = max77686_restore, | ||
360 | }; | ||
361 | |||
362 | static struct i2c_driver max77686_i2c_driver = { | ||
363 | .driver = { | ||
364 | .name = "max77686", | ||
365 | .owner = THIS_MODULE, | ||
366 | .pm = &max77686_pm, | ||
367 | }, | ||
368 | .probe = max77686_i2c_probe, | ||
369 | .remove = max77686_i2c_remove, | ||
370 | .id_table = max77686_i2c_id, | ||
371 | }; | ||
372 | |||
373 | static int __init max77686_i2c_init(void) | ||
374 | { | ||
375 | return i2c_add_driver(&max77686_i2c_driver); | ||
376 | } | ||
377 | /* init early so consumer devices can complete system boot */ | ||
378 | subsys_initcall(max77686_i2c_init); | ||
379 | |||
380 | static void __exit max77686_i2c_exit(void) | ||
381 | { | ||
382 | i2c_del_driver(&max77686_i2c_driver); | ||
383 | } | ||
384 | module_exit(max77686_i2c_exit); | ||
385 | |||
386 | MODULE_DESCRIPTION("MAXIM 8997 multi-function core driver"); | ||
387 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | ||
388 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mfd/max8698.c b/drivers/mfd/max8698.c new file mode 100644 index 00000000000..9dd60042920 --- /dev/null +++ b/drivers/mfd/max8698.c | |||
@@ -0,0 +1,161 @@ | |||
1 | /* | ||
2 | * max8698.c - mfd core driver for the Maxim 8698 | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Samsung Electronics | ||
5 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
6 | * Marek Szyprowski <m.szyprowski@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * 2010.10.26 | ||
23 | * Modified by Taekki Kim <taekki.kim@samsung.com> | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/i2c.h> | ||
31 | #include <linux/mutex.h> | ||
32 | #include <linux/mfd/core.h> | ||
33 | #include <linux/mfd/max8698.h> | ||
34 | #include <linux/mfd/max8698-private.h> | ||
35 | |||
36 | static struct mfd_cell max8698_devs[] = { | ||
37 | { | ||
38 | .name = "max8698-pmic", | ||
39 | } | ||
40 | }; | ||
41 | |||
42 | static int max8698_i2c_device_read(struct max8698_dev *max8698, u8 reg, u8 *dest) | ||
43 | { | ||
44 | struct i2c_client *client = max8698->i2c_client; | ||
45 | int ret; | ||
46 | |||
47 | mutex_lock(&max8698->iolock); | ||
48 | ret = i2c_smbus_read_byte_data(client, reg); | ||
49 | mutex_unlock(&max8698->iolock); | ||
50 | if (ret < 0) | ||
51 | return ret; | ||
52 | |||
53 | ret &= 0xff; | ||
54 | *dest = ret; | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int max8698_i2c_device_write(struct max8698_dev *max8698, u8 reg, u8 value) | ||
59 | { | ||
60 | struct i2c_client *client = max8698->i2c_client; | ||
61 | int ret; | ||
62 | |||
63 | mutex_lock(&max8698->iolock); | ||
64 | ret = i2c_smbus_write_byte_data(client, reg, value); | ||
65 | mutex_unlock(&max8698->iolock); | ||
66 | return ret; | ||
67 | } | ||
68 | |||
69 | static int max8698_i2c_device_update(struct max8698_dev *max8698, u8 reg, | ||
70 | u8 val, u8 mask) | ||
71 | { | ||
72 | struct i2c_client *client = max8698->i2c_client; | ||
73 | int ret; | ||
74 | |||
75 | mutex_lock(&max8698->iolock); | ||
76 | ret = i2c_smbus_read_byte_data(client, reg); | ||
77 | if (ret >= 0) { | ||
78 | u8 old_val = ret & 0xff; | ||
79 | u8 new_val = (val & mask) | (old_val & (~mask)); | ||
80 | ret = i2c_smbus_write_byte_data(client, reg, new_val); | ||
81 | if (ret >= 0) | ||
82 | ret = 0; | ||
83 | } | ||
84 | mutex_unlock(&max8698->iolock); | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | static int max8698_i2c_probe(struct i2c_client *i2c, | ||
89 | const struct i2c_device_id *id) | ||
90 | { | ||
91 | struct max8698_dev *max8698; | ||
92 | int ret = 0; | ||
93 | |||
94 | max8698 = kzalloc(sizeof(struct max8698_dev), GFP_KERNEL); | ||
95 | if (max8698 == NULL) | ||
96 | return -ENOMEM; | ||
97 | |||
98 | i2c_set_clientdata(i2c, max8698); | ||
99 | max8698->dev = &i2c->dev; | ||
100 | max8698->i2c_client = i2c; | ||
101 | max8698->dev_read = max8698_i2c_device_read; | ||
102 | max8698->dev_write = max8698_i2c_device_write; | ||
103 | max8698->dev_update = max8698_i2c_device_update; | ||
104 | mutex_init(&max8698->iolock); | ||
105 | |||
106 | ret = mfd_add_devices(max8698->dev, -1, | ||
107 | max8698_devs, ARRAY_SIZE(max8698_devs), | ||
108 | NULL, 0); | ||
109 | if (ret < 0) | ||
110 | goto err; | ||
111 | |||
112 | return ret; | ||
113 | |||
114 | err: | ||
115 | mfd_remove_devices(max8698->dev); | ||
116 | kfree(max8698); | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | static int max8698_i2c_remove(struct i2c_client *i2c) | ||
121 | { | ||
122 | struct max8698_dev *max8698 = i2c_get_clientdata(i2c); | ||
123 | |||
124 | mfd_remove_devices(max8698->dev); | ||
125 | kfree(max8698); | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static const struct i2c_device_id max8698_i2c_id[] = { | ||
131 | { "max8698", 0 }, | ||
132 | { } | ||
133 | }; | ||
134 | MODULE_DEVICE_TABLE(i2c, max8698_i2c_id); | ||
135 | |||
136 | static struct i2c_driver max8698_i2c_driver = { | ||
137 | .driver = { | ||
138 | .name = "max8698", | ||
139 | .owner = THIS_MODULE, | ||
140 | }, | ||
141 | .probe = max8698_i2c_probe, | ||
142 | .remove = max8698_i2c_remove, | ||
143 | .id_table = max8698_i2c_id, | ||
144 | }; | ||
145 | |||
146 | static int __init max8698_i2c_init(void) | ||
147 | { | ||
148 | return i2c_add_driver(&max8698_i2c_driver); | ||
149 | } | ||
150 | /* init early so consumer devices can complete system boot */ | ||
151 | subsys_initcall(max8698_i2c_init); | ||
152 | |||
153 | static void __exit max8698_i2c_exit(void) | ||
154 | { | ||
155 | i2c_del_driver(&max8698_i2c_driver); | ||
156 | } | ||
157 | module_exit(max8698_i2c_exit); | ||
158 | |||
159 | MODULE_DESCRIPTION("MAXIM 8698 multi-function core driver"); | ||
160 | MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); | ||
161 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index 5d1fca0277e..dc58750bb71 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | #include <linux/i2c.h> | 25 | #include <linux/i2c.h> |
26 | #include <linux/interrupt.h> | ||
26 | #include <linux/pm_runtime.h> | 27 | #include <linux/pm_runtime.h> |
27 | #include <linux/mutex.h> | 28 | #include <linux/mutex.h> |
28 | #include <linux/mfd/core.h> | 29 | #include <linux/mfd/core.h> |
@@ -135,11 +136,13 @@ static int max8997_i2c_probe(struct i2c_client *i2c, | |||
135 | max8997->dev = &i2c->dev; | 136 | max8997->dev = &i2c->dev; |
136 | max8997->i2c = i2c; | 137 | max8997->i2c = i2c; |
137 | max8997->type = id->driver_data; | 138 | max8997->type = id->driver_data; |
139 | max8997->irq = i2c->irq; | ||
138 | 140 | ||
139 | if (!pdata) | 141 | if (!pdata) |
140 | goto err; | 142 | goto err; |
141 | 143 | ||
142 | max8997->wakeup = pdata->wakeup; | 144 | max8997->irq_base = pdata->irq_base; |
145 | max8997->ono = pdata->ono; | ||
143 | 146 | ||
144 | mutex_init(&max8997->iolock); | 147 | mutex_init(&max8997->iolock); |
145 | 148 | ||
@@ -152,6 +155,8 @@ static int max8997_i2c_probe(struct i2c_client *i2c, | |||
152 | 155 | ||
153 | pm_runtime_set_active(max8997->dev); | 156 | pm_runtime_set_active(max8997->dev); |
154 | 157 | ||
158 | max8997_irq_init(max8997); | ||
159 | |||
155 | mfd_add_devices(max8997->dev, -1, max8997_devs, | 160 | mfd_add_devices(max8997->dev, -1, max8997_devs, |
156 | ARRAY_SIZE(max8997_devs), | 161 | ARRAY_SIZE(max8997_devs), |
157 | NULL, 0); | 162 | NULL, 0); |
@@ -164,6 +169,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c, | |||
164 | if (ret < 0) | 169 | if (ret < 0) |
165 | goto err_mfd; | 170 | goto err_mfd; |
166 | 171 | ||
172 | /* MAX8997 has a power button input. */ | ||
173 | device_init_wakeup(max8997->dev, pdata->wakeup); | ||
174 | |||
167 | return ret; | 175 | return ret; |
168 | 176 | ||
169 | err_mfd: | 177 | err_mfd: |
@@ -393,7 +401,29 @@ static int max8997_restore(struct device *dev) | |||
393 | return 0; | 401 | return 0; |
394 | } | 402 | } |
395 | 403 | ||
404 | static int max8997_suspend(struct device *dev) | ||
405 | { | ||
406 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
407 | struct max8997_dev *max8997 = i2c_get_clientdata(i2c); | ||
408 | |||
409 | if (device_may_wakeup(dev)) | ||
410 | irq_set_irq_wake(max8997->irq, 1); | ||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | static int max8997_resume(struct device *dev) | ||
415 | { | ||
416 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
417 | struct max8997_dev *max8997 = i2c_get_clientdata(i2c); | ||
418 | |||
419 | if (device_may_wakeup(dev)) | ||
420 | irq_set_irq_wake(max8997->irq, 0); | ||
421 | return max8997_irq_resume(max8997); | ||
422 | } | ||
423 | |||
396 | const struct dev_pm_ops max8997_pm = { | 424 | const struct dev_pm_ops max8997_pm = { |
425 | .suspend = max8997_suspend, | ||
426 | .resume = max8997_resume, | ||
397 | .freeze = max8997_freeze, | 427 | .freeze = max8997_freeze, |
398 | .restore = max8997_restore, | 428 | .restore = max8997_restore, |
399 | }; | 429 | }; |
diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c new file mode 100644 index 00000000000..43fde54faba --- /dev/null +++ b/drivers/mfd/s5m-core.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * s5m87xx.c | ||
3 | * | ||
4 | * Copyright (c) 2011 Samsung Electronics Co., Ltd | ||
5 | * http://www.samsung.com | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/moduleparam.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/pm_runtime.h> | ||
21 | #include <linux/mutex.h> | ||
22 | #include <linux/mfd/core.h> | ||
23 | #include <linux/mfd/s5m87xx/s5m-core.h> | ||
24 | #include <linux/mfd/s5m87xx/s5m-pmic.h> | ||
25 | #include <linux/mfd/s5m87xx/s5m-rtc.h> | ||
26 | |||
27 | static struct mfd_cell s5m87xx_devs[] = { | ||
28 | { | ||
29 | .name = "s5m8767-pmic", | ||
30 | }, { | ||
31 | .name = "s5m8763-pmic", | ||
32 | }, { | ||
33 | .name = "s5m-rtc", | ||
34 | }, | ||
35 | }; | ||
36 | |||
37 | int s5m_reg_read(struct i2c_client *i2c, u8 reg, u8 *dest) | ||
38 | { | ||
39 | struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); | ||
40 | int ret; | ||
41 | |||
42 | mutex_lock(&s5m87xx->iolock); | ||
43 | ret = i2c_smbus_read_byte_data(i2c, reg); | ||
44 | mutex_unlock(&s5m87xx->iolock); | ||
45 | if (ret < 0) | ||
46 | return ret; | ||
47 | |||
48 | ret &= 0xff; | ||
49 | *dest = ret; | ||
50 | return 0; | ||
51 | } | ||
52 | EXPORT_SYMBOL(s5m_reg_read); | ||
53 | |||
54 | int s5m_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf) | ||
55 | { | ||
56 | struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); | ||
57 | int ret; | ||
58 | |||
59 | mutex_lock(&s5m87xx->iolock); | ||
60 | ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf); | ||
61 | mutex_unlock(&s5m87xx->iolock); | ||
62 | if (ret < 0) | ||
63 | return ret; | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | EXPORT_SYMBOL(s5m_bulk_read); | ||
68 | |||
69 | int s5m_reg_write(struct i2c_client *i2c, u8 reg, u8 value) | ||
70 | { | ||
71 | struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); | ||
72 | int ret; | ||
73 | |||
74 | mutex_lock(&s5m87xx->iolock); | ||
75 | ret = i2c_smbus_write_byte_data(i2c, reg, value); | ||
76 | mutex_unlock(&s5m87xx->iolock); | ||
77 | return ret; | ||
78 | } | ||
79 | EXPORT_SYMBOL(s5m_reg_write); | ||
80 | |||
81 | int s5m_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf) | ||
82 | { | ||
83 | struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); | ||
84 | int ret; | ||
85 | |||
86 | mutex_lock(&s5m87xx->iolock); | ||
87 | ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf); | ||
88 | mutex_unlock(&s5m87xx->iolock); | ||
89 | if (ret < 0) | ||
90 | return ret; | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | EXPORT_SYMBOL(s5m_bulk_write); | ||
95 | |||
96 | int s5m_reg_update(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) | ||
97 | { | ||
98 | struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); | ||
99 | int ret; | ||
100 | |||
101 | mutex_lock(&s5m87xx->iolock); | ||
102 | ret = i2c_smbus_read_byte_data(i2c, reg); | ||
103 | if (ret >= 0) { | ||
104 | u8 old_val = ret & 0xff; | ||
105 | u8 new_val = (val & mask) | (old_val & (~mask)); | ||
106 | ret = i2c_smbus_write_byte_data(i2c, reg, new_val); | ||
107 | } | ||
108 | mutex_unlock(&s5m87xx->iolock); | ||
109 | return ret; | ||
110 | } | ||
111 | EXPORT_SYMBOL(s5m_reg_update); | ||
112 | |||
113 | static int s5m87xx_i2c_probe(struct i2c_client *i2c, | ||
114 | const struct i2c_device_id *id) | ||
115 | { | ||
116 | struct s5m_platform_data *pdata = i2c->dev.platform_data; | ||
117 | struct s5m87xx_dev *s5m87xx; | ||
118 | int ret = 0; | ||
119 | |||
120 | s5m87xx = kzalloc(sizeof(struct s5m87xx_dev), GFP_KERNEL); | ||
121 | if (s5m87xx == NULL) | ||
122 | return -ENOMEM; | ||
123 | |||
124 | i2c_set_clientdata(i2c, s5m87xx); | ||
125 | s5m87xx->dev = &i2c->dev; | ||
126 | s5m87xx->i2c = i2c; | ||
127 | s5m87xx->irq = i2c->irq; | ||
128 | s5m87xx->type = id->driver_data; | ||
129 | |||
130 | if (pdata) { | ||
131 | s5m87xx->device_type = pdata->device_type; | ||
132 | s5m87xx->ono = pdata->ono; | ||
133 | s5m87xx->irq_base = pdata->irq_base; | ||
134 | s5m87xx->wakeup = pdata->wakeup; | ||
135 | s5m87xx->wtsr_smpl = pdata->wtsr_smpl; | ||
136 | } | ||
137 | |||
138 | mutex_init(&s5m87xx->iolock); | ||
139 | |||
140 | s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); | ||
141 | i2c_set_clientdata(s5m87xx->rtc, s5m87xx); | ||
142 | |||
143 | if (pdata && pdata->cfg_pmic_irq) | ||
144 | pdata->cfg_pmic_irq(); | ||
145 | |||
146 | s5m_irq_init(s5m87xx); | ||
147 | |||
148 | pm_runtime_set_active(s5m87xx->dev); | ||
149 | |||
150 | ret = mfd_add_devices(s5m87xx->dev, -1, | ||
151 | s5m87xx_devs, ARRAY_SIZE(s5m87xx_devs), | ||
152 | NULL, 0); | ||
153 | |||
154 | if (ret < 0) | ||
155 | goto err; | ||
156 | |||
157 | dev_info(s5m87xx->dev ,"S5M87xx MFD probe done!!! \n"); | ||
158 | return ret; | ||
159 | |||
160 | err: | ||
161 | mfd_remove_devices(s5m87xx->dev); | ||
162 | s5m_irq_exit(s5m87xx); | ||
163 | i2c_unregister_device(s5m87xx->rtc); | ||
164 | kfree(s5m87xx); | ||
165 | return ret; | ||
166 | } | ||
167 | |||
168 | static int s5m87xx_i2c_remove(struct i2c_client *i2c) | ||
169 | { | ||
170 | struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); | ||
171 | |||
172 | mfd_remove_devices(s5m87xx->dev); | ||
173 | s5m_irq_exit(s5m87xx); | ||
174 | i2c_unregister_device(s5m87xx->rtc); | ||
175 | kfree(s5m87xx); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static const struct i2c_device_id s5m87xx_i2c_id[] = { | ||
181 | { "s5m87xx", 0 }, | ||
182 | { } | ||
183 | }; | ||
184 | MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id); | ||
185 | |||
186 | #ifdef CONFIG_PM | ||
187 | static int s5m_suspend(struct device *dev) | ||
188 | { | ||
189 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
190 | struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); | ||
191 | |||
192 | if (s5m87xx->wakeup) | ||
193 | enable_irq_wake(s5m87xx->irq); | ||
194 | |||
195 | disable_irq(s5m87xx->irq); | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static int s5m_resume(struct device *dev) | ||
201 | { | ||
202 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
203 | struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c); | ||
204 | |||
205 | if (s5m87xx->wakeup) | ||
206 | disable_irq_wake(s5m87xx->irq); | ||
207 | |||
208 | enable_irq(s5m87xx->irq); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | #else | ||
213 | #define s5m_suspend NULL | ||
214 | #define s5m_resume NULL | ||
215 | #endif /* CONFIG_PM */ | ||
216 | |||
217 | const struct dev_pm_ops s5m87xx_apm = { | ||
218 | .suspend = s5m_suspend, | ||
219 | .resume = s5m_resume, | ||
220 | }; | ||
221 | |||
222 | static struct i2c_driver s5m87xx_i2c_driver = { | ||
223 | .driver = { | ||
224 | .name = "s5m87xx", | ||
225 | .owner = THIS_MODULE, | ||
226 | .pm = &s5m87xx_apm, | ||
227 | }, | ||
228 | .probe = s5m87xx_i2c_probe, | ||
229 | .remove = s5m87xx_i2c_remove, | ||
230 | .id_table = s5m87xx_i2c_id, | ||
231 | }; | ||
232 | |||
233 | static int __init s5m87xx_i2c_init(void) | ||
234 | { | ||
235 | return i2c_add_driver(&s5m87xx_i2c_driver); | ||
236 | } | ||
237 | |||
238 | subsys_initcall(s5m87xx_i2c_init); | ||
239 | |||
240 | static void __exit s5m87xx_i2c_exit(void) | ||
241 | { | ||
242 | i2c_del_driver(&s5m87xx_i2c_driver); | ||
243 | } | ||
244 | module_exit(s5m87xx_i2c_exit); | ||
245 | |||
246 | MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); | ||
247 | MODULE_DESCRIPTION("Core support for the S5M87XX MFD"); | ||
248 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mfd/s5m-irq.c b/drivers/mfd/s5m-irq.c new file mode 100644 index 00000000000..dbf1b7a0f17 --- /dev/null +++ b/drivers/mfd/s5m-irq.c | |||
@@ -0,0 +1,487 @@ | |||
1 | /* | ||
2 | * s5m87xx-irq.c | ||
3 | * | ||
4 | * Copyright (c) 2011 Samsung Electronics Co., Ltd | ||
5 | * http://www.samsung.com | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/device.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/mfd/s5m87xx/s5m-core.h> | ||
18 | |||
19 | struct s5m_irq_data { | ||
20 | int reg; | ||
21 | int mask; | ||
22 | }; | ||
23 | |||
24 | static struct s5m_irq_data s5m8767_irqs[] = { | ||
25 | [S5M8767_IRQ_PWRR] = { | ||
26 | .reg = 1, | ||
27 | .mask = S5M8767_IRQ_PWRR_MASK, | ||
28 | }, | ||
29 | [S5M8767_IRQ_PWRF] = { | ||
30 | .reg = 1, | ||
31 | .mask = S5M8767_IRQ_PWRF_MASK, | ||
32 | }, | ||
33 | [S5M8767_IRQ_PWR1S] = { | ||
34 | .reg = 1, | ||
35 | .mask = S5M8767_IRQ_PWR1S_MASK, | ||
36 | }, | ||
37 | [S5M8767_IRQ_JIGR] = { | ||
38 | .reg = 1, | ||
39 | .mask = S5M8767_IRQ_JIGR_MASK, | ||
40 | }, | ||
41 | [S5M8767_IRQ_JIGF] = { | ||
42 | .reg = 1, | ||
43 | .mask = S5M8767_IRQ_JIGF_MASK, | ||
44 | }, | ||
45 | [S5M8767_IRQ_LOWBAT2] = { | ||
46 | .reg = 1, | ||
47 | .mask = S5M8767_IRQ_LOWBAT2_MASK, | ||
48 | }, | ||
49 | [S5M8767_IRQ_LOWBAT1] = { | ||
50 | .reg = 1, | ||
51 | .mask = S5M8767_IRQ_LOWBAT1_MASK, | ||
52 | }, | ||
53 | [S5M8767_IRQ_MRB] = { | ||
54 | .reg = 2, | ||
55 | .mask = S5M8767_IRQ_MRB_MASK, | ||
56 | }, | ||
57 | [S5M8767_IRQ_DVSOK2] = { | ||
58 | .reg = 2, | ||
59 | .mask = S5M8767_IRQ_DVSOK2_MASK, | ||
60 | }, | ||
61 | [S5M8767_IRQ_DVSOK3] = { | ||
62 | .reg = 2, | ||
63 | .mask = S5M8767_IRQ_DVSOK3_MASK, | ||
64 | }, | ||
65 | [S5M8767_IRQ_DVSOK4] = { | ||
66 | .reg = 2, | ||
67 | .mask = S5M8767_IRQ_DVSOK4_MASK, | ||
68 | }, | ||
69 | [S5M8767_IRQ_RTC60S] = { | ||
70 | .reg = 3, | ||
71 | .mask = S5M8767_IRQ_RTC60S_MASK, | ||
72 | }, | ||
73 | [S5M8767_IRQ_RTCA1] = { | ||
74 | .reg = 3, | ||
75 | .mask = S5M8767_IRQ_RTCA1_MASK, | ||
76 | }, | ||
77 | [S5M8767_IRQ_RTCA2] = { | ||
78 | .reg = 3, | ||
79 | .mask = S5M8767_IRQ_RTCA2_MASK, | ||
80 | }, | ||
81 | [S5M8767_IRQ_SMPL] = { | ||
82 | .reg = 3, | ||
83 | .mask = S5M8767_IRQ_SMPL_MASK, | ||
84 | }, | ||
85 | [S5M8767_IRQ_RTC1S] = { | ||
86 | .reg = 3, | ||
87 | .mask = S5M8767_IRQ_RTC1S_MASK, | ||
88 | }, | ||
89 | [S5M8767_IRQ_WTSR] = { | ||
90 | .reg = 3, | ||
91 | .mask = S5M8767_IRQ_WTSR_MASK, | ||
92 | }, | ||
93 | }; | ||
94 | |||
95 | static struct s5m_irq_data s5m8763_irqs[] = { | ||
96 | [S5M8763_IRQ_DCINF] = { | ||
97 | .reg = 1, | ||
98 | .mask = S5M8763_IRQ_DCINF_MASK, | ||
99 | }, | ||
100 | [S5M8763_IRQ_DCINR] = { | ||
101 | .reg = 1, | ||
102 | .mask = S5M8763_IRQ_DCINR_MASK, | ||
103 | }, | ||
104 | [S5M8763_IRQ_JIGF] = { | ||
105 | .reg = 1, | ||
106 | .mask = S5M8763_IRQ_JIGF_MASK, | ||
107 | }, | ||
108 | [S5M8763_IRQ_JIGR] = { | ||
109 | .reg = 1, | ||
110 | .mask = S5M8763_IRQ_JIGR_MASK, | ||
111 | }, | ||
112 | [S5M8763_IRQ_PWRONF] = { | ||
113 | .reg = 1, | ||
114 | .mask = S5M8763_IRQ_PWRONF_MASK, | ||
115 | }, | ||
116 | [S5M8763_IRQ_PWRONR] = { | ||
117 | .reg = 1, | ||
118 | .mask = S5M8763_IRQ_PWRONR_MASK, | ||
119 | }, | ||
120 | [S5M8763_IRQ_WTSREVNT] = { | ||
121 | .reg = 2, | ||
122 | .mask = S5M8763_IRQ_WTSREVNT_MASK, | ||
123 | }, | ||
124 | [S5M8763_IRQ_SMPLEVNT] = { | ||
125 | .reg = 2, | ||
126 | .mask = S5M8763_IRQ_SMPLEVNT_MASK, | ||
127 | }, | ||
128 | [S5M8763_IRQ_ALARM1] = { | ||
129 | .reg = 2, | ||
130 | .mask = S5M8763_IRQ_ALARM1_MASK, | ||
131 | }, | ||
132 | [S5M8763_IRQ_ALARM0] = { | ||
133 | .reg = 2, | ||
134 | .mask = S5M8763_IRQ_ALARM0_MASK, | ||
135 | }, | ||
136 | [S5M8763_IRQ_ONKEY1S] = { | ||
137 | .reg = 3, | ||
138 | .mask = S5M8763_IRQ_ONKEY1S_MASK, | ||
139 | }, | ||
140 | [S5M8763_IRQ_TOPOFFR] = { | ||
141 | .reg = 3, | ||
142 | .mask = S5M8763_IRQ_TOPOFFR_MASK, | ||
143 | }, | ||
144 | [S5M8763_IRQ_DCINOVPR] = { | ||
145 | .reg = 3, | ||
146 | .mask = S5M8763_IRQ_DCINOVPR_MASK, | ||
147 | }, | ||
148 | [S5M8763_IRQ_CHGRSTF] = { | ||
149 | .reg = 3, | ||
150 | .mask = S5M8763_IRQ_CHGRSTF_MASK, | ||
151 | }, | ||
152 | [S5M8763_IRQ_DONER] = { | ||
153 | .reg = 3, | ||
154 | .mask = S5M8763_IRQ_DONER_MASK, | ||
155 | }, | ||
156 | [S5M8763_IRQ_CHGFAULT] = { | ||
157 | .reg = 3, | ||
158 | .mask = S5M8763_IRQ_CHGFAULT_MASK, | ||
159 | }, | ||
160 | [S5M8763_IRQ_LOBAT1] = { | ||
161 | .reg = 4, | ||
162 | .mask = S5M8763_IRQ_LOBAT1_MASK, | ||
163 | }, | ||
164 | [S5M8763_IRQ_LOBAT2] = { | ||
165 | .reg = 4, | ||
166 | .mask = S5M8763_IRQ_LOBAT2_MASK, | ||
167 | }, | ||
168 | }; | ||
169 | |||
170 | static inline struct s5m_irq_data * | ||
171 | irq_to_s5m8767_irq(struct s5m87xx_dev *s5m87xx, int irq) | ||
172 | { | ||
173 | return &s5m8767_irqs[irq - s5m87xx->irq_base]; | ||
174 | } | ||
175 | |||
176 | static void s5m8767_irq_lock(struct irq_data *data) | ||
177 | { | ||
178 | struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); | ||
179 | |||
180 | mutex_lock(&s5m87xx->irqlock); | ||
181 | } | ||
182 | |||
183 | static void s5m8767_irq_sync_unlock(struct irq_data *data) | ||
184 | { | ||
185 | struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); | ||
186 | int i; | ||
187 | |||
188 | for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) { | ||
189 | if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) { | ||
190 | s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i]; | ||
191 | s5m_reg_write(s5m87xx->i2c, S5M8767_REG_INT1M + i, | ||
192 | s5m87xx->irq_masks_cur[i]); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | mutex_unlock(&s5m87xx->irqlock); | ||
197 | } | ||
198 | |||
199 | static void s5m8767_irq_unmask(struct irq_data *data) | ||
200 | { | ||
201 | struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); | ||
202 | struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx, | ||
203 | data->irq); | ||
204 | |||
205 | s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; | ||
206 | } | ||
207 | |||
208 | static void s5m8767_irq_mask(struct irq_data *data) | ||
209 | { | ||
210 | struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); | ||
211 | struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx, | ||
212 | data->irq); | ||
213 | |||
214 | s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; | ||
215 | } | ||
216 | |||
217 | static struct irq_chip s5m8767_irq_chip = { | ||
218 | .name = "s5m8767", | ||
219 | .irq_bus_lock = s5m8767_irq_lock, | ||
220 | .irq_bus_sync_unlock = s5m8767_irq_sync_unlock, | ||
221 | .irq_mask = s5m8767_irq_mask, | ||
222 | .irq_unmask = s5m8767_irq_unmask, | ||
223 | }; | ||
224 | |||
225 | static inline struct s5m_irq_data * | ||
226 | irq_to_s5m8763_irq(struct s5m87xx_dev *s5m87xx, int irq) | ||
227 | { | ||
228 | return &s5m8763_irqs[irq - s5m87xx->irq_base]; | ||
229 | } | ||
230 | |||
231 | static void s5m8763_irq_lock(struct irq_data *data) | ||
232 | { | ||
233 | struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); | ||
234 | |||
235 | mutex_lock(&s5m87xx->irqlock); | ||
236 | } | ||
237 | |||
238 | static void s5m8763_irq_sync_unlock(struct irq_data *data) | ||
239 | { | ||
240 | struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); | ||
241 | int i; | ||
242 | |||
243 | for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) { | ||
244 | if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) { | ||
245 | s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i]; | ||
246 | s5m_reg_write(s5m87xx->i2c, S5M8763_REG_IRQM1 + i, | ||
247 | s5m87xx->irq_masks_cur[i]); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | mutex_unlock(&s5m87xx->irqlock); | ||
252 | } | ||
253 | |||
254 | static void s5m8763_irq_unmask(struct irq_data *data) | ||
255 | { | ||
256 | struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); | ||
257 | struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx, | ||
258 | data->irq); | ||
259 | |||
260 | s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; | ||
261 | } | ||
262 | |||
263 | static void s5m8763_irq_mask(struct irq_data *data) | ||
264 | { | ||
265 | struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data); | ||
266 | struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx, | ||
267 | data->irq); | ||
268 | |||
269 | s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; | ||
270 | } | ||
271 | |||
272 | static struct irq_chip s5m8763_irq_chip = { | ||
273 | .name = "s5m8763", | ||
274 | .irq_bus_lock = s5m8763_irq_lock, | ||
275 | .irq_bus_sync_unlock = s5m8763_irq_sync_unlock, | ||
276 | .irq_mask = s5m8763_irq_mask, | ||
277 | .irq_unmask = s5m8763_irq_unmask, | ||
278 | }; | ||
279 | |||
280 | |||
281 | static irqreturn_t s5m8767_irq_thread(int irq, void *data) | ||
282 | { | ||
283 | struct s5m87xx_dev *s5m87xx = data; | ||
284 | u8 irq_reg[NUM_IRQ_REGS-1]; | ||
285 | int ret; | ||
286 | int i; | ||
287 | |||
288 | |||
289 | ret = s5m_bulk_read(s5m87xx->i2c, S5M8767_REG_INT1, | ||
290 | NUM_IRQ_REGS - 1, irq_reg); | ||
291 | if (ret < 0) { | ||
292 | dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n", | ||
293 | ret); | ||
294 | return IRQ_NONE; | ||
295 | } | ||
296 | |||
297 | for (i = 0; i < NUM_IRQ_REGS - 1; i++) | ||
298 | irq_reg[i] &= ~s5m87xx->irq_masks_cur[i]; | ||
299 | |||
300 | for (i = 0; i < S5M8767_IRQ_NR; i++) { | ||
301 | if (irq_reg[s5m8767_irqs[i].reg - 1] & s5m8767_irqs[i].mask) | ||
302 | handle_nested_irq(s5m87xx->irq_base + i); | ||
303 | } | ||
304 | |||
305 | return IRQ_HANDLED; | ||
306 | } | ||
307 | |||
308 | static irqreturn_t s5m8763_irq_thread(int irq, void *data) | ||
309 | { | ||
310 | struct s5m87xx_dev *s5m87xx = data; | ||
311 | u8 irq_reg[NUM_IRQ_REGS]; | ||
312 | int ret; | ||
313 | int i; | ||
314 | |||
315 | ret = s5m_bulk_read(s5m87xx->i2c, S5M8763_REG_IRQ1, | ||
316 | NUM_IRQ_REGS, irq_reg); | ||
317 | if (ret < 0) { | ||
318 | dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n", | ||
319 | ret); | ||
320 | return IRQ_NONE; | ||
321 | } | ||
322 | |||
323 | for (i = 0; i < NUM_IRQ_REGS; i++) | ||
324 | irq_reg[i] &= ~s5m87xx->irq_masks_cur[i]; | ||
325 | |||
326 | for (i = 0; i < S5M8763_IRQ_NR; i++) { | ||
327 | if (irq_reg[s5m8763_irqs[i].reg - 1] & s5m8763_irqs[i].mask) | ||
328 | handle_nested_irq(s5m87xx->irq_base + i); | ||
329 | } | ||
330 | |||
331 | return IRQ_HANDLED; | ||
332 | } | ||
333 | |||
334 | int s5m_irq_resume(struct s5m87xx_dev *s5m87xx) | ||
335 | { | ||
336 | if (s5m87xx->irq && s5m87xx->irq_base){ | ||
337 | switch (s5m87xx->device_type) { | ||
338 | case S5M8763X: | ||
339 | s5m8763_irq_thread(s5m87xx->irq_base, s5m87xx); | ||
340 | break; | ||
341 | case S5M8767X: | ||
342 | s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx); | ||
343 | break; | ||
344 | default: | ||
345 | break; | ||
346 | |||
347 | } | ||
348 | } | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | int s5m_irq_init(struct s5m87xx_dev *s5m87xx) | ||
353 | { | ||
354 | int i; | ||
355 | int cur_irq; | ||
356 | int ret = 0; | ||
357 | int type = s5m87xx->device_type; | ||
358 | |||
359 | if (!s5m87xx->irq) { | ||
360 | dev_warn(s5m87xx->dev, | ||
361 | "No interrupt specified, no interrupts\n"); | ||
362 | s5m87xx->irq_base = 0; | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | if (!s5m87xx->irq_base) { | ||
367 | dev_err(s5m87xx->dev, | ||
368 | "No interrupt base specified, no interrupts\n"); | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | mutex_init(&s5m87xx->irqlock); | ||
373 | |||
374 | switch (type) { | ||
375 | case S5M8763X: | ||
376 | for (i = 0; i < NUM_IRQ_REGS; i++) { | ||
377 | s5m87xx->irq_masks_cur[i] = 0xff; | ||
378 | s5m87xx->irq_masks_cache[i] = 0xff; | ||
379 | s5m_reg_write(s5m87xx->i2c, S5M8763_REG_IRQM1 + i, | ||
380 | 0xff); | ||
381 | } | ||
382 | |||
383 | s5m_reg_write(s5m87xx->i2c, S5M8763_REG_STATUSM1, 0xff); | ||
384 | s5m_reg_write(s5m87xx->i2c, S5M8763_REG_STATUSM2, 0xff); | ||
385 | |||
386 | for (i = 0; i < S5M8763_IRQ_NR; i++) { | ||
387 | cur_irq = i + s5m87xx->irq_base; | ||
388 | irq_set_chip_data(cur_irq, s5m87xx); | ||
389 | irq_set_chip_and_handler(cur_irq, &s5m8763_irq_chip, | ||
390 | handle_edge_irq); | ||
391 | irq_set_nested_thread(cur_irq, 1); | ||
392 | #ifdef CONFIG_ARM | ||
393 | set_irq_flags(cur_irq, IRQF_VALID); | ||
394 | #else | ||
395 | irq_set_noprobe(cur_irq); | ||
396 | #endif | ||
397 | } | ||
398 | |||
399 | ret = request_threaded_irq(s5m87xx->irq, NULL, | ||
400 | s5m8763_irq_thread, | ||
401 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | ||
402 | "s5m87xx-irq", s5m87xx); | ||
403 | if (ret) { | ||
404 | dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n", | ||
405 | s5m87xx->irq, ret); | ||
406 | return ret; | ||
407 | } | ||
408 | break; | ||
409 | case S5M8767X: | ||
410 | for (i = 0; i < NUM_IRQ_REGS - 1; i++) { | ||
411 | s5m87xx->irq_masks_cur[i] = 0xff; | ||
412 | s5m87xx->irq_masks_cache[i] = 0xff; | ||
413 | s5m_reg_write(s5m87xx->i2c, S5M8767_REG_INT1M + i, | ||
414 | 0xff); | ||
415 | } | ||
416 | for (i = 0; i < S5M8767_IRQ_NR; i++) { | ||
417 | cur_irq = i + s5m87xx->irq_base; | ||
418 | ret = irq_set_chip_data(cur_irq, s5m87xx); | ||
419 | if (ret) { | ||
420 | dev_err(s5m87xx->dev, | ||
421 | "Failed to irq_set_chip_data %d: %d\n", | ||
422 | s5m87xx->irq, ret); | ||
423 | return ret; | ||
424 | } | ||
425 | |||
426 | irq_set_chip_and_handler(cur_irq, &s5m8767_irq_chip, | ||
427 | handle_edge_irq); | ||
428 | irq_set_nested_thread(cur_irq, 1); | ||
429 | #ifdef CONFIG_ARM | ||
430 | set_irq_flags(cur_irq, IRQF_VALID); | ||
431 | #else | ||
432 | irq_set_noprobe(cur_irq); | ||
433 | #endif | ||
434 | } | ||
435 | |||
436 | ret = request_threaded_irq(s5m87xx->irq, NULL, | ||
437 | s5m8767_irq_thread, | ||
438 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | ||
439 | "s5m87xx-irq", s5m87xx); | ||
440 | if (ret) { | ||
441 | dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n", | ||
442 | s5m87xx->irq, ret); | ||
443 | return ret; | ||
444 | } | ||
445 | break; | ||
446 | default: | ||
447 | break; | ||
448 | } | ||
449 | |||
450 | if (!s5m87xx->ono) | ||
451 | return 0; | ||
452 | |||
453 | switch (type) { | ||
454 | case S5M8763X: | ||
455 | ret = request_threaded_irq(s5m87xx->ono, NULL, | ||
456 | s5m8763_irq_thread, | ||
457 | IRQF_TRIGGER_FALLING | | ||
458 | IRQF_TRIGGER_RISING | | ||
459 | IRQF_ONESHOT, "s5m87xx-ono", | ||
460 | s5m87xx); | ||
461 | break; | ||
462 | case S5M8767X: | ||
463 | ret = request_threaded_irq(s5m87xx->ono, NULL, | ||
464 | s5m8767_irq_thread, | ||
465 | IRQF_TRIGGER_FALLING | | ||
466 | IRQF_TRIGGER_RISING | | ||
467 | IRQF_ONESHOT, "s5m87xx-ono", s5m87xx); | ||
468 | break; | ||
469 | default: | ||
470 | break; | ||
471 | } | ||
472 | |||
473 | if (ret) | ||
474 | dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n", | ||
475 | s5m87xx->ono, ret); | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | void s5m_irq_exit(struct s5m87xx_dev *s5m87xx) | ||
481 | { | ||
482 | if (s5m87xx->ono) | ||
483 | free_irq(s5m87xx->ono, s5m87xx); | ||
484 | |||
485 | if (s5m87xx->irq) | ||
486 | free_irq(s5m87xx->irq, s5m87xx); | ||
487 | } | ||
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index e198d40292e..f9c04de57d4 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c | |||
@@ -348,6 +348,19 @@ static int wm8994_resume(struct device *dev) | |||
348 | 348 | ||
349 | return 0; | 349 | return 0; |
350 | } | 350 | } |
351 | |||
352 | static int wm8994_i2c_suspend(struct i2c_client *i2c, pm_message_t state) | ||
353 | { | ||
354 | return wm8994_suspend(&i2c->dev); | ||
355 | } | ||
356 | |||
357 | static int wm8994_i2c_resume(struct i2c_client *i2c) | ||
358 | { | ||
359 | return wm8994_resume(&i2c->dev); | ||
360 | } | ||
361 | #else | ||
362 | #define wm8994_i2c_suspend NULL | ||
363 | #define wm8994_i2c_resume NULL | ||
351 | #endif | 364 | #endif |
352 | 365 | ||
353 | #ifdef CONFIG_REGULATOR | 366 | #ifdef CONFIG_REGULATOR |
@@ -653,10 +666,11 @@ static struct i2c_driver wm8994_i2c_driver = { | |||
653 | .driver = { | 666 | .driver = { |
654 | .name = "wm8994", | 667 | .name = "wm8994", |
655 | .owner = THIS_MODULE, | 668 | .owner = THIS_MODULE, |
656 | .pm = &wm8994_pm_ops, | ||
657 | }, | 669 | }, |
658 | .probe = wm8994_i2c_probe, | 670 | .probe = wm8994_i2c_probe, |
659 | .remove = wm8994_i2c_remove, | 671 | .remove = wm8994_i2c_remove, |
672 | .suspend = wm8994_i2c_suspend, | ||
673 | .resume = wm8994_i2c_resume, | ||
660 | .id_table = wm8994_i2c_id, | 674 | .id_table = wm8994_i2c_id, |
661 | }; | 675 | }; |
662 | 676 | ||