blob: 4271a2a9b958932a4b169984af4aa3db9148f9ef [file] [log] [blame]
Jay Chokshi9d36a3d2011-10-26 15:57:51 -07001/*
David Keitelb0f40362012-01-16 13:29:29 -08002 * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Jay Chokshi9d36a3d2011-10-26 15:57:51 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "%s: " fmt, __func__
15
16#include <linux/kernel.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070017#include <linux/module.h>
Jay Chokshi9d36a3d2011-10-26 15:57:51 -070018#include <linux/platform_device.h>
19#include <linux/slab.h>
20#include <linux/err.h>
David Collins045a76e2011-12-06 11:35:28 -080021#include <linux/string.h>
Jay Chokshi9d36a3d2011-10-26 15:57:51 -070022#include <linux/msm_ssbi.h>
23#include <linux/mfd/core.h>
24#include <linux/mfd/pm8xxx/pm8038.h>
David Keitelb0f40362012-01-16 13:29:29 -080025#include <linux/mfd/pm8xxx/pm8921.h>
Jay Chokshi9d36a3d2011-10-26 15:57:51 -070026#include <linux/mfd/pm8xxx/core.h>
David Collins045a76e2011-12-06 11:35:28 -080027#include <linux/mfd/pm8xxx/regulator.h>
Jay Chokshi9d36a3d2011-10-26 15:57:51 -070028
29#define REG_HWREV 0x002 /* PMIC4 revision */
30#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
31
32#define REG_MPP_BASE 0x050
33#define REG_RTC_BASE 0x11D
34#define REG_IRQ_BASE 0x1BB
35
Asish Bhattacharyadfe0f572012-03-19 02:45:03 -070036#define REG_SPK_BASE 0x253
37#define REG_SPK_REGISTERS 3
38
Jay Chokshi9d36a3d2011-10-26 15:57:51 -070039#define PM8038_VERSION_MASK 0xFFF0
40#define PM8038_VERSION_VALUE 0x09F0
41#define PM8038_REVISION_MASK 0x000F
42
43#define REG_PM8038_PON_CNTRL_3 0x01D
44#define PM8038_RESTART_REASON_MASK 0x07
45
46#define SINGLE_IRQ_RESOURCE(_name, _irq) \
47{ \
48 .name = _name, \
49 .start = _irq, \
50 .end = _irq, \
51 .flags = IORESOURCE_IRQ, \
52}
53
54struct pm8038 {
David Collins045a76e2011-12-06 11:35:28 -080055 struct device *dev;
56 struct pm_irq_chip *irq_chip;
57 struct mfd_cell *mfd_regulators;
58 struct pm8xxx_regulator_core_platform_data *regulator_cdata;
59 u32 rev_registers;
Jay Chokshi9d36a3d2011-10-26 15:57:51 -070060};
61
62static int pm8038_readb(const struct device *dev, u16 addr, u8 *val)
63{
64 const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
65 const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
66
67 return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
68}
69
70static int pm8038_writeb(const struct device *dev, u16 addr, u8 val)
71{
72 const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
73 const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
74
75 return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
76}
77
78static int pm8038_read_buf(const struct device *dev, u16 addr, u8 *buf,
79 int cnt)
80{
81 const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
82 const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
83
84 return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
85}
86
87static int pm8038_write_buf(const struct device *dev, u16 addr, u8 *buf,
88 int cnt)
89{
90 const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
91 const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
92
93 return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
94}
95
96static int pm8038_read_irq_stat(const struct device *dev, int irq)
97{
98 const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
99 const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
100
101 return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
102}
103
104static enum pm8xxx_version pm8038_get_version(const struct device *dev)
105{
106 const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
107 const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
108 enum pm8xxx_version version = -ENODEV;
109
110 if ((pmic->rev_registers & PM8038_VERSION_MASK) == PM8038_VERSION_VALUE)
111 version = PM8XXX_VERSION_8038;
112
113 return version;
114}
115
116static int pm8038_get_revision(const struct device *dev)
117{
118 const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
119 const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
120
121 return pmic->rev_registers & PM8038_REVISION_MASK;
122}
123
124static struct pm8xxx_drvdata pm8038_drvdata = {
125 .pmic_readb = pm8038_readb,
126 .pmic_writeb = pm8038_writeb,
127 .pmic_read_buf = pm8038_read_buf,
128 .pmic_write_buf = pm8038_write_buf,
129 .pmic_read_irq_stat = pm8038_read_irq_stat,
130 .pmic_get_version = pm8038_get_version,
131 .pmic_get_revision = pm8038_get_revision,
132};
133
134static const struct resource gpio_cell_resources[] __devinitconst = {
135 [0] = {
136 .start = PM8038_IRQ_BLOCK_BIT(PM8038_GPIO_BLOCK_START, 0),
137 .end = PM8038_IRQ_BLOCK_BIT(PM8038_GPIO_BLOCK_START, 0)
138 + PM8038_NR_GPIOS - 1,
139 .flags = IORESOURCE_IRQ,
140 },
141};
142
143static struct mfd_cell gpio_cell __devinitdata = {
144 .name = PM8XXX_GPIO_DEV_NAME,
145 .id = -1,
146 .resources = gpio_cell_resources,
147 .num_resources = ARRAY_SIZE(gpio_cell_resources),
148};
149
Siddartha Mohanadossef102132011-12-19 11:30:32 -0800150static const struct resource adc_cell_resources[] __devinitconst = {
151 SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_EOC_USR_IRQ),
152 SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_BATT_TEMP_WARM_IRQ),
153 SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_BATT_TEMP_COLD_IRQ),
154};
155
156static struct mfd_cell adc_cell __devinitdata = {
157 .name = PM8XXX_ADC_DEV_NAME,
158 .id = -1,
159 .resources = adc_cell_resources,
160 .num_resources = ARRAY_SIZE(adc_cell_resources),
161};
162
David Keitelb0f40362012-01-16 13:29:29 -0800163static const struct resource charger_cell_resources[] __devinitconst = {
164 SINGLE_IRQ_RESOURCE("USBIN_VALID_IRQ", PM8921_USBIN_VALID_IRQ),
165 SINGLE_IRQ_RESOURCE("USBIN_OV_IRQ", PM8921_USBIN_OV_IRQ),
166 SINGLE_IRQ_RESOURCE("BATT_INSERTED_IRQ", PM8921_BATT_INSERTED_IRQ),
167 SINGLE_IRQ_RESOURCE("VBATDET_LOW_IRQ", PM8921_VBATDET_LOW_IRQ),
168 SINGLE_IRQ_RESOURCE("USBIN_UV_IRQ", PM8921_USBIN_UV_IRQ),
169 SINGLE_IRQ_RESOURCE("VBAT_OV_IRQ", PM8921_VBAT_OV_IRQ),
170 SINGLE_IRQ_RESOURCE("CHGWDOG_IRQ", PM8921_CHGWDOG_IRQ),
171 SINGLE_IRQ_RESOURCE("VCP_IRQ", PM8921_VCP_IRQ),
172 SINGLE_IRQ_RESOURCE("ATCDONE_IRQ", PM8921_ATCDONE_IRQ),
173 SINGLE_IRQ_RESOURCE("ATCFAIL_IRQ", PM8921_ATCFAIL_IRQ),
174 SINGLE_IRQ_RESOURCE("CHGDONE_IRQ", PM8921_CHGDONE_IRQ),
175 SINGLE_IRQ_RESOURCE("CHGFAIL_IRQ", PM8921_CHGFAIL_IRQ),
176 SINGLE_IRQ_RESOURCE("CHGSTATE_IRQ", PM8921_CHGSTATE_IRQ),
177 SINGLE_IRQ_RESOURCE("LOOP_CHANGE_IRQ", PM8921_LOOP_CHANGE_IRQ),
178 SINGLE_IRQ_RESOURCE("FASTCHG_IRQ", PM8921_FASTCHG_IRQ),
179 SINGLE_IRQ_RESOURCE("TRKLCHG_IRQ", PM8921_TRKLCHG_IRQ),
180 SINGLE_IRQ_RESOURCE("BATT_REMOVED_IRQ", PM8921_BATT_REMOVED_IRQ),
181 SINGLE_IRQ_RESOURCE("BATTTEMP_HOT_IRQ", PM8921_BATTTEMP_HOT_IRQ),
182 SINGLE_IRQ_RESOURCE("CHGHOT_IRQ", PM8921_CHGHOT_IRQ),
183 SINGLE_IRQ_RESOURCE("BATTTEMP_COLD_IRQ", PM8921_BATTTEMP_COLD_IRQ),
184 SINGLE_IRQ_RESOURCE("CHG_GONE_IRQ", PM8921_CHG_GONE_IRQ),
185 SINGLE_IRQ_RESOURCE("BAT_TEMP_OK_IRQ", PM8921_BAT_TEMP_OK_IRQ),
186 SINGLE_IRQ_RESOURCE("COARSE_DET_LOW_IRQ", PM8921_COARSE_DET_LOW_IRQ),
187 SINGLE_IRQ_RESOURCE("VDD_LOOP_IRQ", PM8921_VDD_LOOP_IRQ),
188 SINGLE_IRQ_RESOURCE("VREG_OV_IRQ", PM8921_VREG_OV_IRQ),
189 SINGLE_IRQ_RESOURCE("VBATDET_IRQ", PM8921_VBATDET_IRQ),
190 SINGLE_IRQ_RESOURCE("BATFET_IRQ", PM8921_BATFET_IRQ),
191 SINGLE_IRQ_RESOURCE("PSI_IRQ", PM8921_PSI_IRQ),
192 SINGLE_IRQ_RESOURCE("DCIN_VALID_IRQ", PM8921_DCIN_VALID_IRQ),
193 SINGLE_IRQ_RESOURCE("DCIN_OV_IRQ", PM8921_DCIN_OV_IRQ),
194 SINGLE_IRQ_RESOURCE("DCIN_UV_IRQ", PM8921_DCIN_UV_IRQ),
195};
196
197static const struct resource bms_cell_resources[] __devinitconst = {
198 SINGLE_IRQ_RESOURCE("PM8921_BMS_SBI_WRITE_OK", PM8921_BMS_SBI_WRITE_OK),
199 SINGLE_IRQ_RESOURCE("PM8921_BMS_CC_THR", PM8921_BMS_CC_THR),
200 SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_THR", PM8921_BMS_VSENSE_THR),
201 SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_FOR_R", PM8921_BMS_VSENSE_FOR_R),
202 SINGLE_IRQ_RESOURCE("PM8921_BMS_OCV_FOR_R", PM8921_BMS_OCV_FOR_R),
203 SINGLE_IRQ_RESOURCE("PM8921_BMS_GOOD_OCV", PM8921_BMS_GOOD_OCV),
204 SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_AVG", PM8921_BMS_VSENSE_AVG),
205};
206
207static struct mfd_cell charger_cell __devinitdata = {
208 .name = PM8921_CHARGER_DEV_NAME,
209 .id = -1,
210 .resources = charger_cell_resources,
211 .num_resources = ARRAY_SIZE(charger_cell_resources),
212};
213
214static struct mfd_cell bms_cell __devinitdata = {
215 .name = PM8921_BMS_DEV_NAME,
216 .id = -1,
217 .resources = bms_cell_resources,
218 .num_resources = ARRAY_SIZE(bms_cell_resources),
219};
Jay Chokshi9d36a3d2011-10-26 15:57:51 -0700220static const struct resource mpp_cell_resources[] __devinitconst = {
221 {
222 .start = PM8038_IRQ_BLOCK_BIT(PM8038_MPP_BLOCK_START, 0),
223 .end = PM8038_IRQ_BLOCK_BIT(PM8038_MPP_BLOCK_START, 0)
224 + PM8038_NR_MPPS - 1,
225 .flags = IORESOURCE_IRQ,
226 },
227};
228
229static struct mfd_cell mpp_cell __devinitdata = {
230 .name = PM8XXX_MPP_DEV_NAME,
231 .id = 1,
232 .resources = mpp_cell_resources,
233 .num_resources = ARRAY_SIZE(mpp_cell_resources),
234};
235
236static const struct resource rtc_cell_resources[] __devinitconst = {
237 [0] = SINGLE_IRQ_RESOURCE(NULL, PM8038_RTC_ALARM_IRQ),
238 [1] = {
239 .name = "pmic_rtc_base",
240 .start = REG_RTC_BASE,
241 .end = REG_RTC_BASE,
242 .flags = IORESOURCE_IO,
243 },
244};
245
246static struct mfd_cell rtc_cell __devinitdata = {
247 .name = PM8XXX_RTC_DEV_NAME,
248 .id = -1,
249 .resources = rtc_cell_resources,
250 .num_resources = ARRAY_SIZE(rtc_cell_resources),
251};
252
253static const struct resource resources_pwrkey[] __devinitconst = {
254 SINGLE_IRQ_RESOURCE(NULL, PM8038_PWRKEY_REL_IRQ),
255 SINGLE_IRQ_RESOURCE(NULL, PM8038_PWRKEY_PRESS_IRQ),
256};
257
258static struct mfd_cell pwrkey_cell __devinitdata = {
259 .name = PM8XXX_PWRKEY_DEV_NAME,
260 .id = -1,
261 .num_resources = ARRAY_SIZE(resources_pwrkey),
262 .resources = resources_pwrkey,
263};
264
265static struct mfd_cell pwm_cell __devinitdata = {
266 .name = PM8XXX_PWM_DEV_NAME,
267 .id = -1,
268};
269
270static struct mfd_cell misc_cell __devinitdata = {
271 .name = PM8XXX_MISC_DEV_NAME,
272 .id = -1,
273};
274
Amy Maloche89501012012-01-27 14:11:29 -0800275static struct mfd_cell leds_cell __devinitdata = {
276 .name = PM8XXX_LEDS_DEV_NAME,
277 .id = -1,
278};
279
Asish Bhattacharyadfe0f572012-03-19 02:45:03 -0700280static const struct resource resources_spk[] __devinitconst = {
281 [0] = {
282 .name = PM8XXX_SPK_DEV_NAME,
283 .start = REG_SPK_BASE,
284 .end = REG_SPK_BASE + REG_SPK_REGISTERS,
285 .flags = IORESOURCE_IO,
286 },
287};
288
289static struct mfd_cell spk_cell __devinitdata = {
290 .name = PM8XXX_SPK_DEV_NAME,
291 .id = -1,
292 .num_resources = ARRAY_SIZE(resources_spk),
293 .resources = resources_spk,
294};
295
Jay Chokshi9d36a3d2011-10-26 15:57:51 -0700296static struct mfd_cell debugfs_cell __devinitdata = {
297 .name = "pm8xxx-debug",
298 .id = 0,
299 .platform_data = "pm8038-dbg",
300 .pdata_size = sizeof("pm8038-dbg"),
301};
302
David Collins045a76e2011-12-06 11:35:28 -0800303static struct pm8xxx_vreg regulator_data[] = {
304 /* name pc_name ctrl test hpm_min */
305 NLDO1200("8038_l1", 0x0AE, 0x0AF, LDO_1200),
306 NLDO("8038_l2", "8038_l2_pc", 0x0B0, 0x0B1, LDO_150),
307 PLDO("8038_l3", "8038_l3_pc", 0x0B2, 0x0B3, LDO_50),
308 PLDO("8038_l4", "8038_l4_pc", 0x0B4, 0x0B5, LDO_50),
309 PLDO("8038_l5", "8038_l5_pc", 0x0B6, 0x0B7, LDO_600),
310 PLDO("8038_l6", "8038_l6_pc", 0x0B8, 0x0B9, LDO_600),
311 PLDO("8038_l7", "8038_l7_pc", 0x0BA, 0x0BB, LDO_600),
312 PLDO("8038_l8", "8038_l8_pc", 0x0BC, 0x0BD, LDO_300),
313 PLDO("8038_l9", "8038_l9_pc", 0x0BE, 0x0BF, LDO_300),
314 PLDO("8038_l10", "8038_l10_pc", 0x0C0, 0x0C1, LDO_600),
315 PLDO("8038_l11", "8038_l11_pc", 0x0C2, 0x0C3, LDO_600),
316 NLDO("8038_l12", "8038_l12_pc", 0x0C4, 0x0C5, LDO_300),
317 PLDO("8038_l14", "8038_l14_pc", 0x0C8, 0x0C9, LDO_50),
318 PLDO("8038_l15", "8038_l15_pc", 0x0CA, 0x0CB, LDO_150),
319 NLDO1200("8038_l16", 0x0CC, 0x0CD, LDO_1200),
320 PLDO("8038_l17", "8038_l17_pc", 0x0CE, 0x0CF, LDO_150),
321 PLDO("8038_l18", "8038_l18_pc", 0x0D0, 0x0D1, LDO_50),
322 NLDO1200("8038_l19", 0x0D2, 0x0D3, LDO_1200),
323 NLDO1200("8038_l20", 0x0D4, 0x0D5, LDO_1200),
324 PLDO("8038_l21", "8038_l21_pc", 0x0D6, 0x0D7, LDO_150),
325 PLDO("8038_l22", "8038_l22_pc", 0x0D8, 0x0D9, LDO_50),
326 PLDO("8038_l23", "8038_l23_pc", 0x0DA, 0x0DB, LDO_50),
327 NLDO1200("8038_l24", 0x0DC, 0x0DD, LDO_1200),
328 NLDO("8038_l26", "8038_l26_pc", 0x0E0, 0x0E1, LDO_150),
329 NLDO1200("8038_l27", 0x0E2, 0x0E3, LDO_1200),
330
331 /* name pc_name ctrl test2 clk sleep hpm_min */
332 SMPS("8038_s1", "8038_s1_pc", 0x1E0, 0x1E5, 0x009, 0x1E2, SMPS_1500),
333 SMPS("8038_s2", "8038_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
334 SMPS("8038_s3", "8038_s3_pc", 0x1D0, 0x1D5, 0x00B, 0x1D2, SMPS_1500),
335 SMPS("8038_s4", "8038_s4_pc", 0x1E8, 0x1ED, 0x00C, 0x1EA, SMPS_1500),
336
337 /* name ctrl fts_cnfg1 pfm pwr_cnfg hpm_min */
338 FTSMPS("8038_s5", 0x025, 0x02E, 0x026, 0x032, SMPS_2000),
339 FTSMPS("8038_s6", 0x036, 0x03F, 0x037, 0x043, SMPS_2000),
340
David Collins1b570d22012-01-30 15:40:55 -0800341 /* name pc_name ctrl test */
342 VS("8038_lvs1", "8038_lvs1_pc", 0x060, 0x061),
343 VS("8038_lvs2", "8038_lvs2_pc", 0x062, 0x063),
David Collins045a76e2011-12-06 11:35:28 -0800344};
345
346#define MAX_NAME_COMPARISON_LEN 32
347
348static int __devinit match_regulator(
Mark Brown5d028cd2011-10-14 13:36:04 +0100349 struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
David Collins045a76e2011-12-06 11:35:28 -0800350{
351 int found = 0;
352 int i;
353
354 for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
355 if (regulator_data[i].rdesc.name
356 && strncmp(regulator_data[i].rdesc.name, name,
357 MAX_NAME_COMPARISON_LEN) == 0) {
358 core_data->is_pin_controlled = false;
359 core_data->vreg = &regulator_data[i];
360 found = 1;
361 break;
362 } else if (regulator_data[i].rdesc_pc.name
363 && strncmp(regulator_data[i].rdesc_pc.name, name,
364 MAX_NAME_COMPARISON_LEN) == 0) {
365 core_data->is_pin_controlled = true;
366 core_data->vreg = &regulator_data[i];
367 found = 1;
368 break;
369 }
370 }
371
372 if (!found)
373 pr_err("could not find a match for regulator: %s\n", name);
374
375 return found;
376}
377
378static int __devinit
379pm8038_add_regulators(const struct pm8038_platform_data *pdata,
380 struct pm8038 *pmic, int irq_base)
381{
382 int ret = 0;
383 struct mfd_cell *mfd_regulators;
384 struct pm8xxx_regulator_core_platform_data *cdata;
385 int i;
386
387 /* Add one device for each regulator used by the board. */
388 mfd_regulators = kzalloc(sizeof(struct mfd_cell)
389 * (pdata->num_regulators), GFP_KERNEL);
390 if (!mfd_regulators) {
391 pr_err("Cannot allocate %d bytes for pm8038 regulator "
392 "mfd cells\n", sizeof(struct mfd_cell)
393 * (pdata->num_regulators));
394 return -ENOMEM;
395 }
396 cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
397 * pdata->num_regulators, GFP_KERNEL);
398 if (!cdata) {
399 pr_err("Cannot allocate %d bytes for pm8038 regulator "
400 "core data\n", pdata->num_regulators
401 * sizeof(struct pm8xxx_regulator_core_platform_data));
402 kfree(mfd_regulators);
403 return -ENOMEM;
404 }
405 for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
406 mutex_init(&regulator_data[i].pc_lock);
407
408 for (i = 0; i < pdata->num_regulators; i++) {
409 if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
410 pr_err("name missing for regulator %d\n", i);
411 ret = -EINVAL;
412 goto bail;
413 }
414 if (!match_regulator(&cdata[i],
415 pdata->regulator_pdatas[i].init_data.constraints.name)) {
416 ret = -ENODEV;
417 goto bail;
418 }
419 cdata[i].pdata = &(pdata->regulator_pdatas[i]);
420 mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
421 mfd_regulators[i].id = cdata[i].pdata->id;
422 mfd_regulators[i].platform_data = &cdata[i];
423 mfd_regulators[i].pdata_size =
424 sizeof(struct pm8xxx_regulator_core_platform_data);
425 }
426 ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
427 pdata->num_regulators, NULL, irq_base);
428 if (ret)
429 goto bail;
430
431 pmic->mfd_regulators = mfd_regulators;
432 pmic->regulator_cdata = cdata;
433 return ret;
434
435bail:
436 for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
437 mutex_destroy(&regulator_data[i].pc_lock);
438 kfree(mfd_regulators);
439 kfree(cdata);
440 return ret;
441}
Jay Chokshi9d36a3d2011-10-26 15:57:51 -0700442
443static int __devinit
444pm8038_add_subdevices(const struct pm8038_platform_data *pdata,
445 struct pm8038 *pmic)
446{
447 int ret = 0, irq_base = 0;
448 struct pm_irq_chip *irq_chip;
449
450 if (pdata->irq_pdata) {
451 pdata->irq_pdata->irq_cdata.nirqs = PM8038_NR_IRQS;
452 pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
453 irq_base = pdata->irq_pdata->irq_base;
454 irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
455
456 if (IS_ERR(irq_chip)) {
457 pr_err("Failed to init interrupts ret=%ld\n",
458 PTR_ERR(irq_chip));
459 return PTR_ERR(irq_chip);
460 }
461 pmic->irq_chip = irq_chip;
462 }
463
464 if (pdata->gpio_pdata) {
465 pdata->gpio_pdata->gpio_cdata.ngpios = PM8038_NR_GPIOS;
466 gpio_cell.platform_data = pdata->gpio_pdata;
467 gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
468 ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
469 NULL, irq_base);
470 if (ret) {
471 pr_err("Failed to add gpio subdevice ret=%d\n", ret);
472 goto bail;
473 }
474 }
475
476 if (pdata->mpp_pdata) {
477 pdata->mpp_pdata->core_data.nmpps = PM8038_NR_MPPS;
478 pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
479 mpp_cell.platform_data = pdata->mpp_pdata;
480 mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
481 ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
482 irq_base);
483 if (ret) {
484 pr_err("Failed to add mpp subdevice ret=%d\n", ret);
485 goto bail;
486 }
487 }
488
489 if (pdata->rtc_pdata) {
490 rtc_cell.platform_data = pdata->rtc_pdata;
491 rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
492 ret = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
493 irq_base);
494 if (ret) {
495 pr_err("Failed to add rtc subdevice ret=%d\n", ret);
496 goto bail;
497 }
498 }
499
500 if (pdata->pwrkey_pdata) {
501 pwrkey_cell.platform_data = pdata->pwrkey_pdata;
502 pwrkey_cell.pdata_size =
503 sizeof(struct pm8xxx_pwrkey_platform_data);
504 ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
505 irq_base);
506 if (ret) {
507 pr_err("Failed to add pwrkey subdevice ret=%d\n", ret);
508 goto bail;
509 }
510 }
511
512 ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
513 if (ret) {
514 pr_err("Failed to add pwm subdevice ret=%d\n", ret);
515 goto bail;
516 }
517
518 if (pdata->misc_pdata) {
519 misc_cell.platform_data = pdata->misc_pdata;
520 misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
521 ret = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
522 irq_base);
523 if (ret) {
524 pr_err("Failed to add misc subdevice ret=%d\n", ret);
525 goto bail;
526 }
527 }
528
Amy Maloche89501012012-01-27 14:11:29 -0800529 if (pdata->leds_pdata) {
530 leds_cell.platform_data = pdata->leds_pdata;
531 leds_cell.pdata_size = sizeof(struct pm8xxx_led_platform_data);
532 ret = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL, 0);
533 if (ret) {
534 pr_err("Failed to add leds subdevice ret=%d\n", ret);
535 goto bail;
536 }
537 }
538
Asish Bhattacharyadfe0f572012-03-19 02:45:03 -0700539 if (pdata->spk_pdata) {
540 spk_cell.platform_data = pdata->spk_pdata;
541 spk_cell.pdata_size = sizeof(struct pm8xxx_spk_platform_data);
542 ret = mfd_add_devices(pmic->dev, 0, &spk_cell, 1, NULL, 0);
543 if (ret) {
544 pr_err("Failed to add spk subdevice ret=%d\n", ret);
545 goto bail;
546 }
547 }
548
David Collins045a76e2011-12-06 11:35:28 -0800549 if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
550 ret = pm8038_add_regulators(pdata, pmic, irq_base);
551 if (ret) {
552 pr_err("Failed to add regulator subdevices ret=%d\n",
553 ret);
554 goto bail;
555 }
556 }
557
Jay Chokshi9d36a3d2011-10-26 15:57:51 -0700558 ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
559 if (ret) {
560 pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
561 goto bail;
562 }
563
Siddartha Mohanadossef102132011-12-19 11:30:32 -0800564 if (pdata->adc_pdata) {
565 adc_cell.platform_data = pdata->adc_pdata;
566 adc_cell.pdata_size =
567 sizeof(struct pm8xxx_adc_platform_data);
568 ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
569 irq_base);
570 if (ret) {
571 pr_err("Failed to add adc subdevices ret=%d\n",
572 ret);
573 }
574 }
575
David Keitelb0f40362012-01-16 13:29:29 -0800576 if (pdata->charger_pdata) {
577 pdata->charger_pdata->charger_cdata.vbat_channel = CHANNEL_VBAT;
578 pdata->charger_pdata->charger_cdata.batt_temp_channel
579 = CHANNEL_BATT_THERM;
580 pdata->charger_pdata->charger_cdata.batt_id_channel
581 = CHANNEL_BATT_ID;
582 charger_cell.platform_data = pdata->charger_pdata;
583 charger_cell.pdata_size =
584 sizeof(struct pm8921_charger_platform_data);
585 ret = mfd_add_devices(pmic->dev, 0, &charger_cell, 1, NULL,
586 irq_base);
587 if (ret) {
588 pr_err("Failed to add charger subdevice ret=%d\n", ret);
589 goto bail;
590 }
591 }
592
593 if (pdata->bms_pdata) {
594 pdata->bms_pdata->bms_cdata.batt_temp_channel
595 = CHANNEL_BATT_THERM;
596 pdata->bms_pdata->bms_cdata.vbat_channel = CHANNEL_VBAT;
597 pdata->bms_pdata->bms_cdata.ref625mv_channel = CHANNEL_625MV;
598 pdata->bms_pdata->bms_cdata.ref1p25v_channel = CHANNEL_125V;
599 pdata->bms_pdata->bms_cdata.batt_id_channel = CHANNEL_BATT_ID;
600 bms_cell.platform_data = pdata->bms_pdata;
601 bms_cell.pdata_size = sizeof(struct pm8921_bms_platform_data);
602 ret = mfd_add_devices(pmic->dev, 0, &bms_cell, 1, NULL,
603 irq_base);
604 if (ret) {
605 pr_err("Failed to add bms subdevice ret=%d\n", ret);
606 goto bail;
607 }
608 }
Jay Chokshi9d36a3d2011-10-26 15:57:51 -0700609 return 0;
610bail:
611 if (pmic->irq_chip) {
612 pm8xxx_irq_exit(pmic->irq_chip);
613 pmic->irq_chip = NULL;
614 }
615 return ret;
616}
617
618static const char * const pm8038_restart_reason[] = {
619 [0] = "Unknown",
620 [1] = "Triggered from CBL (external charger)",
621 [2] = "Triggered from KPD (power key press)",
622 [3] = "Triggered from CHG (usb charger insertion)",
623 [4] = "Triggered from SMPL (sudden momentary power loss)",
624 [5] = "Triggered from RTC (real time clock)",
625 [6] = "Triggered by Hard Reset",
626 [7] = "Triggered by General Purpose Trigger",
627};
628
629static const char * const pm8038_rev_names[] = {
630 [PM8XXX_REVISION_8038_TEST] = "test",
631 [PM8XXX_REVISION_8038_1p0] = "1.0",
632 [PM8XXX_REVISION_8038_2p0] = "2.0",
633 [PM8XXX_REVISION_8038_2p1] = "2.1",
634};
635
636static int __devinit pm8038_probe(struct platform_device *pdev)
637{
638 const struct pm8038_platform_data *pdata = pdev->dev.platform_data;
639 const char *revision_name = "unknown";
640 struct pm8038 *pmic;
641 enum pm8xxx_version version;
642 int revision;
643 int rc;
644 u8 val;
645
646 if (!pdata) {
647 pr_err("missing platform data\n");
648 return -EINVAL;
649 }
650
651 pmic = kzalloc(sizeof(struct pm8038), GFP_KERNEL);
652 if (!pmic) {
653 pr_err("Cannot alloc pm8038 struct\n");
654 return -ENOMEM;
655 }
656
657 /* Read PMIC chip revision */
658 rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
659 if (rc) {
660 pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
661 goto err_read_rev;
662 }
663 pr_info("PMIC revision 1: PM8038 rev %02X\n", val);
664 pmic->rev_registers = val;
665
666 /* Read PMIC chip revision 2 */
667 rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
668 if (rc) {
669 pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
670 REG_HWREV_2, rc);
671 goto err_read_rev;
672 }
673 pr_info("PMIC revision 2: PM8038 rev %02X\n", val);
674 pmic->rev_registers |= val << BITS_PER_BYTE;
675
676 pmic->dev = &pdev->dev;
677 pm8038_drvdata.pm_chip_data = pmic;
678 platform_set_drvdata(pdev, &pm8038_drvdata);
679
680 /* Print out human readable version and revision names. */
681 version = pm8xxx_get_version(pmic->dev);
682 if (version == PM8XXX_VERSION_8038) {
683 revision = pm8xxx_get_revision(pmic->dev);
684 if (revision >= 0 && revision < ARRAY_SIZE(pm8038_rev_names))
685 revision_name = pm8038_rev_names[revision];
686 pr_info("PMIC version: PM8038 ver %s\n", revision_name);
687 } else {
688 WARN_ON(version != PM8XXX_VERSION_8038);
689 }
690
691 /* Log human readable restart reason */
692 rc = msm_ssbi_read(pdev->dev.parent, REG_PM8038_PON_CNTRL_3, &val, 1);
693 if (rc) {
694 pr_err("Cannot read restart reason rc=%d\n", rc);
695 goto err_read_rev;
696 }
697 val &= PM8038_RESTART_REASON_MASK;
698 pr_info("PMIC Restart Reason: %s\n", pm8038_restart_reason[val]);
699
700 rc = pm8038_add_subdevices(pdata, pmic);
701 if (rc) {
702 pr_err("Cannot add subdevices rc=%d\n", rc);
703 goto err;
704 }
705
706 return 0;
707
708err:
709 mfd_remove_devices(pmic->dev);
710 platform_set_drvdata(pdev, NULL);
David Collins045a76e2011-12-06 11:35:28 -0800711 kfree(pmic->mfd_regulators);
712 kfree(pmic->regulator_cdata);
Jay Chokshi9d36a3d2011-10-26 15:57:51 -0700713err_read_rev:
714 kfree(pmic);
715 return rc;
716}
717
718static int __devexit pm8038_remove(struct platform_device *pdev)
719{
720 struct pm8xxx_drvdata *drvdata;
721 struct pm8038 *pmic = NULL;
David Collins045a76e2011-12-06 11:35:28 -0800722 int i;
Jay Chokshi9d36a3d2011-10-26 15:57:51 -0700723
724 drvdata = platform_get_drvdata(pdev);
725
726 if (drvdata)
727 pmic = drvdata->pm_chip_data;
728
729 if (pmic) {
730 if (pmic->dev)
731 mfd_remove_devices(pmic->dev);
732
733 if (pmic->irq_chip) {
734 pm8xxx_irq_exit(pmic->irq_chip);
735 pmic->irq_chip = NULL;
736 }
David Collins045a76e2011-12-06 11:35:28 -0800737 if (pmic->mfd_regulators) {
738 for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
739 mutex_destroy(&regulator_data[i].pc_lock);
740 }
741 kfree(pmic->mfd_regulators);
742 kfree(pmic->regulator_cdata);
Jay Chokshi9d36a3d2011-10-26 15:57:51 -0700743 kfree(pmic);
744 }
745
746 platform_set_drvdata(pdev, NULL);
747
748 return 0;
749}
750
751static struct platform_driver pm8038_driver = {
752 .probe = pm8038_probe,
753 .remove = __devexit_p(pm8038_remove),
754 .driver = {
755 .name = PM8038_CORE_DEV_NAME,
756 .owner = THIS_MODULE,
757 },
758};
759
760static int __init pm8038_init(void)
761{
762 return platform_driver_register(&pm8038_driver);
763}
764postcore_initcall(pm8038_init);
765
766static void __exit pm8038_exit(void)
767{
768 platform_driver_unregister(&pm8038_driver);
769}
770module_exit(pm8038_exit);
771
772MODULE_LICENSE("GPL v2");
773MODULE_DESCRIPTION("PMIC 8038 core driver");
774MODULE_VERSION("1.0");
775MODULE_ALIAS("platform:pm8038-core");