Commit | Line | Data |
---|---|---|
39b27ad9 KK |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // | |
3 | // Copyright (c) 2012 Samsung Electronics Co., Ltd | |
4 | // http://www.samsung.com | |
0f5f7078 SK |
5 | |
6 | #include <linux/module.h> | |
7 | #include <linux/moduleparam.h> | |
8 | #include <linux/init.h> | |
9 | #include <linux/err.h> | |
10 | #include <linux/slab.h> | |
11 | #include <linux/i2c.h> | |
1c5a099b | 12 | #include <linux/of.h> |
0f5f7078 SK |
13 | #include <linux/interrupt.h> |
14 | #include <linux/pm_runtime.h> | |
15 | #include <linux/mutex.h> | |
16 | #include <linux/mfd/core.h> | |
54227bcf SK |
17 | #include <linux/mfd/samsung/core.h> |
18 | #include <linux/mfd/samsung/irq.h> | |
ce24991e | 19 | #include <linux/mfd/samsung/s2mpa01.h> |
25f311fa | 20 | #include <linux/mfd/samsung/s2mps11.h> |
76b9840b | 21 | #include <linux/mfd/samsung/s2mps13.h> |
dc691966 | 22 | #include <linux/mfd/samsung/s2mps14.h> |
9e4808d2 | 23 | #include <linux/mfd/samsung/s2mps15.h> |
00e2573d | 24 | #include <linux/mfd/samsung/s2mpu02.h> |
25f311fa | 25 | #include <linux/mfd/samsung/s5m8767.h> |
0f5f7078 SK |
26 | #include <linux/regmap.h> |
27 | ||
5ac98553 | 28 | static const struct mfd_cell s5m8767_devs[] = { |
f01faec6 SM |
29 | { .name = "s5m8767-pmic", }, |
30 | { .name = "s5m-rtc", }, | |
0f5f7078 | 31 | { |
39fed00f | 32 | .name = "s5m8767-clk", |
53c31b34 | 33 | .of_compatible = "samsung,s5m8767-clk", |
f01faec6 | 34 | }, |
0f5f7078 SK |
35 | }; |
36 | ||
5ac98553 | 37 | static const struct mfd_cell s2mps11_devs[] = { |
f01faec6 SM |
38 | { .name = "s2mps11-regulator", }, |
39 | { .name = "s2mps14-rtc", }, | |
9b6d1343 | 40 | { |
3134bcae | 41 | .name = "s2mps11-clk", |
53c31b34 | 42 | .of_compatible = "samsung,s2mps11-clk", |
f01faec6 | 43 | }, |
9b6d1343 SK |
44 | }; |
45 | ||
3bc2ee91 | 46 | static const struct mfd_cell s2mps13_devs[] = { |
2fadbbf0 | 47 | { .name = "s2mps13-regulator", }, |
3bc2ee91 CC |
48 | { .name = "s2mps13-rtc", }, |
49 | { | |
50 | .name = "s2mps13-clk", | |
51 | .of_compatible = "samsung,s2mps13-clk", | |
52 | }, | |
53 | }; | |
54 | ||
dc691966 | 55 | static const struct mfd_cell s2mps14_devs[] = { |
f01faec6 SM |
56 | { .name = "s2mps14-regulator", }, |
57 | { .name = "s2mps14-rtc", }, | |
dc691966 | 58 | { |
dc691966 | 59 | .name = "s2mps14-clk", |
53c31b34 | 60 | .of_compatible = "samsung,s2mps14-clk", |
f01faec6 | 61 | }, |
dc691966 KK |
62 | }; |
63 | ||
9e4808d2 | 64 | static const struct mfd_cell s2mps15_devs[] = { |
f01faec6 SM |
65 | { .name = "s2mps15-regulator", }, |
66 | { .name = "s2mps15-rtc", }, | |
9e4808d2 | 67 | { |
9e4808d2 TA |
68 | .name = "s2mps13-clk", |
69 | .of_compatible = "samsung,s2mps13-clk", | |
70 | }, | |
71 | }; | |
72 | ||
ce24991e | 73 | static const struct mfd_cell s2mpa01_devs[] = { |
f01faec6 | 74 | { .name = "s2mpa01-pmic", }, |
ec85d4a1 | 75 | { .name = "s2mps14-rtc", }, |
ce24991e SK |
76 | }; |
77 | ||
54e8827d | 78 | static const struct mfd_cell s2mpu02_devs[] = { |
f01faec6 | 79 | { .name = "s2mpu02-regulator", }, |
54e8827d CC |
80 | }; |
81 | ||
df20b7c5 | 82 | static const struct of_device_id sec_dt_match[] = { |
4720eb47 KK |
83 | { |
84 | .compatible = "samsung,s5m8767-pmic", | |
26aec009 | 85 | .data = (void *)S5M8767X, |
ce24991e SK |
86 | }, { |
87 | .compatible = "samsung,s2mps11-pmic", | |
aa32acad | 88 | .data = (void *)S2MPS11X, |
3bc2ee91 CC |
89 | }, { |
90 | .compatible = "samsung,s2mps13-pmic", | |
91 | .data = (void *)S2MPS13X, | |
ce24991e SK |
92 | }, { |
93 | .compatible = "samsung,s2mps14-pmic", | |
dc691966 | 94 | .data = (void *)S2MPS14X, |
9e4808d2 TA |
95 | }, { |
96 | .compatible = "samsung,s2mps15-pmic", | |
97 | .data = (void *)S2MPS15X, | |
ce24991e SK |
98 | }, { |
99 | .compatible = "samsung,s2mpa01-pmic", | |
100 | .data = (void *)S2MPA01, | |
54e8827d CC |
101 | }, { |
102 | .compatible = "samsung,s2mpu02-pmic", | |
103 | .data = (void *)S2MPU02, | |
ce24991e SK |
104 | }, { |
105 | /* Sentinel */ | |
dc691966 | 106 | }, |
26aec009 | 107 | }; |
fe8166c9 | 108 | MODULE_DEVICE_TABLE(of, sec_dt_match); |
26aec009 | 109 | |
ce24991e SK |
110 | static bool s2mpa01_volatile(struct device *dev, unsigned int reg) |
111 | { | |
112 | switch (reg) { | |
113 | case S2MPA01_REG_INT1M: | |
114 | case S2MPA01_REG_INT2M: | |
115 | case S2MPA01_REG_INT3M: | |
116 | return false; | |
117 | default: | |
118 | return true; | |
119 | } | |
120 | } | |
121 | ||
6b845ba9 MB |
122 | static bool s2mps11_volatile(struct device *dev, unsigned int reg) |
123 | { | |
124 | switch (reg) { | |
125 | case S2MPS11_REG_INT1M: | |
126 | case S2MPS11_REG_INT2M: | |
127 | case S2MPS11_REG_INT3M: | |
128 | return false; | |
129 | default: | |
130 | return true; | |
131 | } | |
132 | } | |
133 | ||
00e2573d CC |
134 | static bool s2mpu02_volatile(struct device *dev, unsigned int reg) |
135 | { | |
136 | switch (reg) { | |
137 | case S2MPU02_REG_INT1M: | |
138 | case S2MPU02_REG_INT2M: | |
139 | case S2MPU02_REG_INT3M: | |
140 | return false; | |
141 | default: | |
142 | return true; | |
143 | } | |
144 | } | |
145 | ||
a30fffb0 | 146 | static const struct regmap_config sec_regmap_config = { |
0f5f7078 SK |
147 | .reg_bits = 8, |
148 | .val_bits = 8, | |
149 | }; | |
150 | ||
ce24991e SK |
151 | static const struct regmap_config s2mpa01_regmap_config = { |
152 | .reg_bits = 8, | |
153 | .val_bits = 8, | |
154 | ||
155 | .max_register = S2MPA01_REG_LDO_OVCB4, | |
156 | .volatile_reg = s2mpa01_volatile, | |
157 | .cache_type = REGCACHE_FLAT, | |
158 | }; | |
159 | ||
a30fffb0 | 160 | static const struct regmap_config s2mps11_regmap_config = { |
25f311fa MB |
161 | .reg_bits = 8, |
162 | .val_bits = 8, | |
163 | ||
164 | .max_register = S2MPS11_REG_L38CTRL, | |
6b845ba9 MB |
165 | .volatile_reg = s2mps11_volatile, |
166 | .cache_type = REGCACHE_FLAT, | |
25f311fa MB |
167 | }; |
168 | ||
76b9840b CC |
169 | static const struct regmap_config s2mps13_regmap_config = { |
170 | .reg_bits = 8, | |
171 | .val_bits = 8, | |
172 | ||
173 | .max_register = S2MPS13_REG_LDODSCH5, | |
174 | .volatile_reg = s2mps11_volatile, | |
175 | .cache_type = REGCACHE_FLAT, | |
176 | }; | |
177 | ||
dc691966 KK |
178 | static const struct regmap_config s2mps14_regmap_config = { |
179 | .reg_bits = 8, | |
180 | .val_bits = 8, | |
181 | ||
182 | .max_register = S2MPS14_REG_LDODSCH3, | |
183 | .volatile_reg = s2mps11_volatile, | |
184 | .cache_type = REGCACHE_FLAT, | |
185 | }; | |
186 | ||
9e4808d2 TA |
187 | static const struct regmap_config s2mps15_regmap_config = { |
188 | .reg_bits = 8, | |
189 | .val_bits = 8, | |
190 | ||
191 | .max_register = S2MPS15_REG_LDODSCH4, | |
192 | .volatile_reg = s2mps11_volatile, | |
193 | .cache_type = REGCACHE_FLAT, | |
194 | }; | |
195 | ||
00e2573d CC |
196 | static const struct regmap_config s2mpu02_regmap_config = { |
197 | .reg_bits = 8, | |
198 | .val_bits = 8, | |
199 | ||
200 | .max_register = S2MPU02_REG_DVSDATA, | |
201 | .volatile_reg = s2mpu02_volatile, | |
202 | .cache_type = REGCACHE_FLAT, | |
203 | }; | |
204 | ||
a30fffb0 | 205 | static const struct regmap_config s5m8767_regmap_config = { |
25f311fa MB |
206 | .reg_bits = 8, |
207 | .val_bits = 8, | |
208 | ||
209 | .max_register = S5M8767_REG_LDO28CTRL, | |
6b845ba9 MB |
210 | .volatile_reg = s2mps11_volatile, |
211 | .cache_type = REGCACHE_FLAT, | |
25f311fa | 212 | }; |
26aec009 | 213 | |
c2c9f1fe KK |
214 | static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic) |
215 | { | |
216 | unsigned int val; | |
217 | ||
218 | /* For each device type, the REG_ID is always the first register */ | |
219 | if (!regmap_read(sec_pmic->regmap_pmic, S2MPS11_REG_ID, &val)) | |
220 | dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", val); | |
221 | } | |
222 | ||
8a97d428 KK |
223 | static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic) |
224 | { | |
225 | int err; | |
226 | ||
227 | if (sec_pmic->device_type != S2MPS13X) | |
228 | return; | |
229 | ||
230 | if (sec_pmic->pdata->disable_wrstbi) { | |
231 | /* | |
232 | * If WRSTBI pin is pulled down this feature must be disabled | |
233 | * because each Suspend to RAM will trigger buck voltage reset | |
234 | * to default values. | |
235 | */ | |
236 | err = regmap_update_bits(sec_pmic->regmap_pmic, | |
237 | S2MPS13_REG_WRSTBI, | |
238 | S2MPS13_REG_WRSTBI_MASK, 0x0); | |
239 | if (err) | |
240 | dev_warn(sec_pmic->dev, | |
241 | "Cannot initialize WRSTBI config: %d\n", | |
242 | err); | |
243 | } | |
244 | } | |
245 | ||
26aec009 ADK |
246 | /* |
247 | * Only the common platform data elements for s5m8767 are parsed here from the | |
248 | * device tree. Other sub-modules of s5m8767 such as pmic, rtc , charger and | |
249 | * others have to parse their own platform data elements from device tree. | |
250 | * | |
251 | * The s5m8767 platform data structure is instantiated here and the drivers for | |
252 | * the sub-modules need not instantiate another instance while parsing their | |
253 | * platform data. | |
254 | */ | |
4720eb47 KK |
255 | static struct sec_platform_data * |
256 | sec_pmic_i2c_parse_dt_pdata(struct device *dev) | |
26aec009 ADK |
257 | { |
258 | struct sec_platform_data *pd; | |
259 | ||
260 | pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); | |
82a00c49 | 261 | if (!pd) |
26aec009 | 262 | return ERR_PTR(-ENOMEM); |
26aec009 | 263 | |
d785334a KK |
264 | pd->manual_poweroff = of_property_read_bool(dev->of_node, |
265 | "samsung,s2mps11-acokb-ground"); | |
8a97d428 KK |
266 | pd->disable_wrstbi = of_property_read_bool(dev->of_node, |
267 | "samsung,s2mps11-wrstbi-ground"); | |
26aec009 ADK |
268 | return pd; |
269 | } | |
26aec009 | 270 | |
a403e589 | 271 | static int sec_pmic_probe(struct i2c_client *i2c) |
0f5f7078 | 272 | { |
e349c910 | 273 | const struct regmap_config *regmap; |
5a7b909c | 274 | struct sec_platform_data *pdata; |
54e8827d | 275 | const struct mfd_cell *sec_devs; |
63063bfb | 276 | struct sec_pmic_dev *sec_pmic; |
54e8827d | 277 | int ret, num_sec_devs; |
0f5f7078 | 278 | |
63063bfb | 279 | sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev), |
621210e2 | 280 | GFP_KERNEL); |
63063bfb | 281 | if (sec_pmic == NULL) |
0f5f7078 SK |
282 | return -ENOMEM; |
283 | ||
63063bfb SK |
284 | i2c_set_clientdata(i2c, sec_pmic); |
285 | sec_pmic->dev = &i2c->dev; | |
286 | sec_pmic->i2c = i2c; | |
287 | sec_pmic->irq = i2c->irq; | |
5a7b909c KK |
288 | |
289 | pdata = sec_pmic_i2c_parse_dt_pdata(sec_pmic->dev); | |
290 | if (IS_ERR(pdata)) { | |
291 | ret = PTR_ERR(pdata); | |
292 | return ret; | |
0f5f7078 | 293 | } |
5a7b909c | 294 | |
294fb2ce | 295 | sec_pmic->device_type = (unsigned long)of_device_get_match_data(sec_pmic->dev); |
5a7b909c | 296 | sec_pmic->pdata = pdata; |
0f5f7078 | 297 | |
25f311fa | 298 | switch (sec_pmic->device_type) { |
ce24991e SK |
299 | case S2MPA01: |
300 | regmap = &s2mpa01_regmap_config; | |
301 | break; | |
25f311fa MB |
302 | case S2MPS11X: |
303 | regmap = &s2mps11_regmap_config; | |
dc691966 | 304 | break; |
76b9840b CC |
305 | case S2MPS13X: |
306 | regmap = &s2mps13_regmap_config; | |
307 | break; | |
dc691966 KK |
308 | case S2MPS14X: |
309 | regmap = &s2mps14_regmap_config; | |
25f311fa | 310 | break; |
9e4808d2 TA |
311 | case S2MPS15X: |
312 | regmap = &s2mps15_regmap_config; | |
313 | break; | |
25f311fa MB |
314 | case S5M8767X: |
315 | regmap = &s5m8767_regmap_config; | |
316 | break; | |
00e2573d CC |
317 | case S2MPU02: |
318 | regmap = &s2mpu02_regmap_config; | |
319 | break; | |
25f311fa MB |
320 | default: |
321 | regmap = &sec_regmap_config; | |
322 | break; | |
323 | } | |
324 | ||
3e1e4a5f KK |
325 | sec_pmic->regmap_pmic = devm_regmap_init_i2c(i2c, regmap); |
326 | if (IS_ERR(sec_pmic->regmap_pmic)) { | |
327 | ret = PTR_ERR(sec_pmic->regmap_pmic); | |
0f5f7078 | 328 | dev_err(&i2c->dev, "Failed to allocate register map: %d\n", |
61a2af30 | 329 | ret); |
1092e1c7 | 330 | return ret; |
0f5f7078 SK |
331 | } |
332 | ||
63063bfb | 333 | sec_irq_init(sec_pmic); |
0f5f7078 | 334 | |
63063bfb | 335 | pm_runtime_set_active(sec_pmic->dev); |
0f5f7078 | 336 | |
63063bfb | 337 | switch (sec_pmic->device_type) { |
88a1cfd6 | 338 | case S5M8767X: |
54e8827d CC |
339 | sec_devs = s5m8767_devs; |
340 | num_sec_devs = ARRAY_SIZE(s5m8767_devs); | |
88a1cfd6 | 341 | break; |
ce24991e | 342 | case S2MPA01: |
54e8827d CC |
343 | sec_devs = s2mpa01_devs; |
344 | num_sec_devs = ARRAY_SIZE(s2mpa01_devs); | |
ce24991e | 345 | break; |
9b6d1343 | 346 | case S2MPS11X: |
54e8827d CC |
347 | sec_devs = s2mps11_devs; |
348 | num_sec_devs = ARRAY_SIZE(s2mps11_devs); | |
9b6d1343 | 349 | break; |
3bc2ee91 CC |
350 | case S2MPS13X: |
351 | sec_devs = s2mps13_devs; | |
352 | num_sec_devs = ARRAY_SIZE(s2mps13_devs); | |
353 | break; | |
dc691966 | 354 | case S2MPS14X: |
54e8827d CC |
355 | sec_devs = s2mps14_devs; |
356 | num_sec_devs = ARRAY_SIZE(s2mps14_devs); | |
357 | break; | |
9e4808d2 TA |
358 | case S2MPS15X: |
359 | sec_devs = s2mps15_devs; | |
360 | num_sec_devs = ARRAY_SIZE(s2mps15_devs); | |
361 | break; | |
54e8827d CC |
362 | case S2MPU02: |
363 | sec_devs = s2mpu02_devs; | |
364 | num_sec_devs = ARRAY_SIZE(s2mpu02_devs); | |
dc691966 | 365 | break; |
88a1cfd6 | 366 | default: |
83761f96 KK |
367 | dev_err(&i2c->dev, "Unsupported device type (%lu)\n", |
368 | sec_pmic->device_type); | |
369 | return -ENODEV; | |
88a1cfd6 | 370 | } |
3dc6f4aa LD |
371 | ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs, |
372 | NULL, 0, NULL); | |
15447a46 | 373 | if (ret) |
3dc6f4aa | 374 | return ret; |
0f5f7078 | 375 | |
8a97d428 | 376 | sec_pmic_configure(sec_pmic); |
c2c9f1fe | 377 | sec_pmic_dump_rev(sec_pmic); |
f6d6daaf | 378 | |
0f5f7078 | 379 | return ret; |
0f5f7078 SK |
380 | } |
381 | ||
d785334a KK |
382 | static void sec_pmic_shutdown(struct i2c_client *i2c) |
383 | { | |
384 | struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c); | |
385 | unsigned int reg, mask; | |
386 | ||
387 | if (!sec_pmic->pdata->manual_poweroff) | |
388 | return; | |
389 | ||
390 | switch (sec_pmic->device_type) { | |
391 | case S2MPS11X: | |
392 | reg = S2MPS11_REG_CTRL1; | |
393 | mask = S2MPS11_CTRL1_PWRHOLD_MASK; | |
394 | break; | |
395 | default: | |
396 | /* | |
397 | * Currently only one board with S2MPS11 needs this, so just | |
398 | * ignore the rest. | |
399 | */ | |
400 | dev_warn(sec_pmic->dev, | |
401 | "Unsupported device %lu for manual power off\n", | |
402 | sec_pmic->device_type); | |
403 | return; | |
404 | } | |
405 | ||
406 | regmap_update_bits(sec_pmic->regmap_pmic, reg, mask, 0); | |
407 | } | |
408 | ||
f6d6daaf KK |
409 | static int sec_pmic_suspend(struct device *dev) |
410 | { | |
1b5420e1 | 411 | struct i2c_client *i2c = to_i2c_client(dev); |
f6d6daaf KK |
412 | struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c); |
413 | ||
360d15d6 | 414 | if (device_may_wakeup(dev)) |
f6d6daaf | 415 | enable_irq_wake(sec_pmic->irq); |
360d15d6 KK |
416 | /* |
417 | * PMIC IRQ must be disabled during suspend for RTC alarm | |
418 | * to work properly. | |
419 | * When device is woken up from suspend, an | |
420 | * interrupt occurs before resuming I2C bus controller. | |
421 | * The interrupt is handled by regmap_irq_thread which tries | |
422 | * to read RTC registers. This read fails (I2C is still | |
423 | * suspended) and RTC Alarm interrupt is disabled. | |
424 | */ | |
425 | disable_irq(sec_pmic->irq); | |
f6d6daaf KK |
426 | |
427 | return 0; | |
428 | } | |
429 | ||
430 | static int sec_pmic_resume(struct device *dev) | |
431 | { | |
1b5420e1 | 432 | struct i2c_client *i2c = to_i2c_client(dev); |
f6d6daaf KK |
433 | struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c); |
434 | ||
360d15d6 | 435 | if (device_may_wakeup(dev)) |
f6d6daaf | 436 | disable_irq_wake(sec_pmic->irq); |
360d15d6 | 437 | enable_irq(sec_pmic->irq); |
f6d6daaf KK |
438 | |
439 | return 0; | |
440 | } | |
441 | ||
270a7c3e PC |
442 | static DEFINE_SIMPLE_DEV_PM_OPS(sec_pmic_pm_ops, |
443 | sec_pmic_suspend, sec_pmic_resume); | |
f6d6daaf | 444 | |
63063bfb | 445 | static struct i2c_driver sec_pmic_driver = { |
0f5f7078 | 446 | .driver = { |
63063bfb | 447 | .name = "sec_pmic", |
270a7c3e | 448 | .pm = pm_sleep_ptr(&sec_pmic_pm_ops), |
5a7b909c | 449 | .of_match_table = sec_dt_match, |
0f5f7078 | 450 | }, |
9816d859 | 451 | .probe = sec_pmic_probe, |
d785334a | 452 | .shutdown = sec_pmic_shutdown, |
0f5f7078 | 453 | }; |
0c8f2d10 | 454 | module_i2c_driver(sec_pmic_driver); |
0f5f7078 SK |
455 | |
456 | MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); | |
457 | MODULE_DESCRIPTION("Core support for the S5M MFD"); | |
458 | MODULE_LICENSE("GPL"); |