blob: 08aff1fb9d02082b4abad3c6bd7eba71f7861a2c [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/interrupt.h>
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053014#include <linux/platform_device.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/slab.h>
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053016#include <linux/delay.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070017#include <linux/mfd/core.h>
Anirudh Ghayalc8051a82011-11-17 09:28:24 +053018#include <linux/msm_ssbi.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070019#include <linux/mfd/pmic8901.h>
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053020#include <linux/mfd/pm8xxx/core.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021
22/* PMIC8901 Revision */
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053023#define PM8901_REG_REV 0x002
24#define PM8901_VERSION_MASK 0xF0
25#define PM8901_REVISION_MASK 0x0F
26#define PM8901_VERSION_VALUE 0xF0
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053028#define REG_IRQ_BASE 0xD5
29#define REG_MPP_BASE 0x27
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053031#define REG_TEMP_ALRM_CTRL 0x23
32#define REG_TEMP_ALRM_PWM 0x24
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033
34/* FTS regulator PMR registers */
35#define SSBI_REG_ADDR_S1_PMR (0xA7)
36#define SSBI_REG_ADDR_S2_PMR (0xA8)
37#define SSBI_REG_ADDR_S3_PMR (0xA9)
38#define SSBI_REG_ADDR_S4_PMR (0xAA)
39
40#define REGULATOR_PMR_STATE_MASK 0x60
41#define REGULATOR_PMR_STATE_OFF 0x20
42
David Collins88ebc9c2011-11-15 08:46:08 -080043/* Shutdown/restart delays to allow for LDO 7/dVdd regulator load settling. */
44#define DELAY_AFTER_REG_DISABLE_MS 4
45#define DELAY_BEFORE_SHUTDOWN_MS 8
46
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053047#define SINGLE_IRQ_RESOURCE(_name, _irq) \
48{ \
49 .name = _name, \
50 .start = _irq, \
51 .end = _irq, \
52 .flags = IORESOURCE_IRQ, \
53}
54
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055struct pm8901_chip {
56 struct pm8901_platform_data pdata;
Anirudh Ghayalc8051a82011-11-17 09:28:24 +053057 struct device *dev;
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053058 struct pm_irq_chip *irq_chip;
59 struct mfd_cell *mfd_regulators;
60 u8 revision;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061};
62
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063static struct pm8901_chip *pmic_chip;
64
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065static inline int
Anirudh Ghayalc8051a82011-11-17 09:28:24 +053066ssbi_read(struct device *dev, u16 addr, u8 *buf, size_t len)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067{
Anirudh Ghayalc8051a82011-11-17 09:28:24 +053068 return msm_ssbi_read(dev->parent, addr, buf, len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069}
70
71static inline int
Anirudh Ghayalc8051a82011-11-17 09:28:24 +053072ssbi_write(struct device *dev, u16 addr, u8 *buf, size_t len)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073{
Anirudh Ghayalc8051a82011-11-17 09:28:24 +053074 return msm_ssbi_write(dev->parent, addr, buf, len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075}
76
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077int pm8901_reset_pwr_off(int reset)
78{
79 int rc = 0, i;
80 u8 pmr;
81 u8 pmr_addr[4] = {
82 SSBI_REG_ADDR_S2_PMR,
83 SSBI_REG_ADDR_S3_PMR,
84 SSBI_REG_ADDR_S4_PMR,
85 SSBI_REG_ADDR_S1_PMR,
86 };
87
88 if (pmic_chip == NULL)
89 return -ENODEV;
90
91 /* Turn off regulators S1, S2, S3, S4 when shutting down. */
92 if (!reset) {
93 for (i = 0; i < 4; i++) {
94 rc = ssbi_read(pmic_chip->dev, pmr_addr[i], &pmr, 1);
95 if (rc) {
96 pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
97 __func__, pmr_addr[i], rc);
98 goto get_out;
99 }
100
101 pmr &= ~REGULATOR_PMR_STATE_MASK;
102 pmr |= REGULATOR_PMR_STATE_OFF;
103
104 rc = ssbi_write(pmic_chip->dev, pmr_addr[i], &pmr, 1);
105 if (rc) {
106 pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d"
107 "\n", __func__, pmr_addr[i], pmr, rc);
108 goto get_out;
109 }
David Collins88ebc9c2011-11-15 08:46:08 -0800110 mdelay(DELAY_AFTER_REG_DISABLE_MS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 }
112 }
113
114get_out:
David Collins88ebc9c2011-11-15 08:46:08 -0800115 mdelay(DELAY_BEFORE_SHUTDOWN_MS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116 return rc;
117}
118EXPORT_SYMBOL(pm8901_reset_pwr_off);
119
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530120static int pm8901_readb(const struct device *dev, u16 addr, u8 *val)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530122 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
123 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530125 return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126}
127
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530128static int pm8901_writeb(const struct device *dev, u16 addr, u8 val)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530130 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
131 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530133 return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134}
135
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530136static int pm8901_read_buf(const struct device *dev, u16 addr, u8 *buf,
137 int cnt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530139 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
140 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530142 return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143}
144
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530145static int pm8901_write_buf(const struct device *dev, u16 addr, u8 *buf,
146 int cnt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530148 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
149 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530151 return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152}
153
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530154static int pm8901_read_irq_stat(const struct device *dev, int irq)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530156 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
157 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530159 return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160
161 return 0;
162}
163
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530164static enum pm8xxx_version pm8901_get_version(const struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530166 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
167 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
168 enum pm8xxx_version version = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530170 if ((pmic->revision & PM8901_VERSION_MASK) == PM8901_VERSION_VALUE)
171 version = PM8XXX_VERSION_8901;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530173 return version;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174}
175
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530176static int pm8901_get_revision(const struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700177{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530178 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
179 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530181 return pmic->revision & PM8901_REVISION_MASK;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182}
183
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530184static struct pm8xxx_drvdata pm8901_drvdata = {
185 .pmic_readb = pm8901_readb,
186 .pmic_writeb = pm8901_writeb,
187 .pmic_read_buf = pm8901_read_buf,
188 .pmic_write_buf = pm8901_write_buf,
189 .pmic_read_irq_stat = pm8901_read_irq_stat,
190 .pmic_get_version = pm8901_get_version,
191 .pmic_get_revision = pm8901_get_revision,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192};
193
Anirudh Ghayal934b2712011-12-13 12:49:51 +0530194static struct mfd_cell misc_cell = {
195 .name = PM8XXX_MISC_DEV_NAME,
196 .id = 1,
197};
198
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530199static struct mfd_cell debugfs_cell = {
200 .name = "pm8xxx-debug",
201 .id = 1,
202 .platform_data = "pm8901-dbg",
203 .pdata_size = sizeof("pm8901-dbg"),
204};
205
206static const struct resource thermal_alarm_cell_resources[] = {
207 SINGLE_IRQ_RESOURCE("pm8901_tempstat_irq", PM8901_TEMPSTAT_IRQ),
208 SINGLE_IRQ_RESOURCE("pm8901_overtemp_irq", PM8901_OVERTEMP_IRQ),
209};
210
211static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
212 .adc_type = PM8XXX_TM_ADC_NONE,
213 .reg_addr_temp_alarm_ctrl = REG_TEMP_ALRM_CTRL,
214 .reg_addr_temp_alarm_pwm = REG_TEMP_ALRM_PWM,
215 .tm_name = "pm8901_tz",
216 .irq_name_temp_stat = "pm8901_tempstat_irq",
217 .irq_name_over_temp = "pm8901_overtemp_irq",
218};
219
220static struct mfd_cell thermal_alarm_cell = {
221 .name = PM8XXX_TM_DEV_NAME,
222 .id = 1,
223 .resources = thermal_alarm_cell_resources,
224 .num_resources = ARRAY_SIZE(thermal_alarm_cell_resources),
225 .platform_data = &thermal_alarm_cdata,
226 .pdata_size = sizeof(struct pm8xxx_tm_core_data),
227};
228
229static const struct resource mpp_cell_resources[] = {
230 {
231 .start = PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0),
232 .end = PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0)
233 + PM8901_MPPS - 1,
234 .flags = IORESOURCE_IRQ,
235 },
236};
237
238static struct mfd_cell mpp_cell = {
239 .name = PM8XXX_MPP_DEV_NAME,
240 .id = 1,
241 .resources = mpp_cell_resources,
242 .num_resources = ARRAY_SIZE(mpp_cell_resources),
243};
244
245static int __devinit
246pm8901_add_subdevices(const struct pm8901_platform_data *pdata,
247 struct pm8901_chip *pmic)
248{
249 int rc = 0, irq_base = 0, i;
250 struct pm_irq_chip *irq_chip;
251 static struct mfd_cell *mfd_regulators;
252
253 if (pdata->irq_pdata) {
254 pdata->irq_pdata->irq_cdata.nirqs = PM8901_NR_IRQS;
255 pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
256 irq_base = pdata->irq_pdata->irq_base;
257 irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
258
259 if (IS_ERR(irq_chip)) {
260 pr_err("Failed to init interrupts ret=%ld\n",
261 PTR_ERR(irq_chip));
262 return PTR_ERR(irq_chip);
263 }
264 pmic->irq_chip = irq_chip;
265 }
266
267 if (pdata->mpp_pdata) {
268 pdata->mpp_pdata->core_data.nmpps = PM8901_MPPS;
269 pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
270 mpp_cell.platform_data = pdata->mpp_pdata;
271 mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
272 rc = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
273 irq_base);
274 if (rc) {
275 pr_err("Failed to add mpp subdevice ret=%d\n", rc);
276 goto bail;
277 }
278 }
279
280 if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
281 mfd_regulators = kzalloc(sizeof(struct mfd_cell)
282 * (pdata->num_regulators), GFP_KERNEL);
283 if (!mfd_regulators) {
284 pr_err("Cannot allocate %d bytes for pm8901 regulator "
285 "mfd cells\n", sizeof(struct mfd_cell)
286 * (pdata->num_regulators));
287 rc = -ENOMEM;
288 goto bail;
289 }
290 for (i = 0; i < pdata->num_regulators; i++) {
291 mfd_regulators[i].name = "pm8901-regulator";
292 mfd_regulators[i].id = pdata->regulator_pdatas[i].id;
293 mfd_regulators[i].platform_data =
294 &(pdata->regulator_pdatas[i]);
295 mfd_regulators[i].pdata_size =
296 sizeof(struct pm8901_vreg_pdata);
297 }
298 rc = mfd_add_devices(pmic->dev, 0, mfd_regulators,
299 pdata->num_regulators, NULL, irq_base);
300 if (rc) {
301 pr_err("Failed to add regulator subdevices ret=%d\n",
302 rc);
303 kfree(mfd_regulators);
304 goto bail;
305 }
306 pmic->mfd_regulators = mfd_regulators;
307 }
308
309 rc = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
310 irq_base);
311 if (rc) {
312 pr_err("Failed to add thermal alarm subdevice ret=%d\n",
313 rc);
314 goto bail;
315 }
316
317 rc = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
318 if (rc) {
319 pr_err("Failed to add debugfs subdevice ret=%d\n", rc);
320 goto bail;
321 }
322
Anirudh Ghayal934b2712011-12-13 12:49:51 +0530323 if (pdata->misc_pdata) {
324 misc_cell.platform_data = pdata->misc_pdata;
325 misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
326 rc = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
327 irq_base);
328 if (rc) {
329 pr_err("Failed to add misc subdevice ret=%d\n", rc);
330 goto bail;
331 }
332 }
333
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530334 return rc;
335
336bail:
337 if (pmic->irq_chip) {
338 pm8xxx_irq_exit(pmic->irq_chip);
339 pmic->irq_chip = NULL;
340 }
341 return rc;
342}
343
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530344static int pm8901_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530346 int rc;
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530347 struct pm8901_platform_data *pdata = pdev->dev.platform_data;
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530348 struct pm8901_chip *pmic;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530350 if (pdata == NULL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351 pr_err("%s: No platform_data or IRQ.\n", __func__);
352 return -ENODEV;
353 }
354
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530355 pmic = kzalloc(sizeof *pmic, GFP_KERNEL);
356 if (pmic == NULL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357 pr_err("%s: kzalloc() failed.\n", __func__);
358 return -ENOMEM;
359 }
360
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530361 pmic->dev = &pdev->dev;
362
363 pm8901_drvdata.pm_chip_data = pmic;
364 platform_set_drvdata(pdev, &pm8901_drvdata);
365 pmic_chip = pmic;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700366
367 /* Read PMIC chip revision */
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530368 rc = pm8901_readb(pmic->dev, PM8901_REG_REV, &pmic->revision);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369 if (rc)
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530370 pr_err("%s: Failed reading version register rc=%d.\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700371 __func__, rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530373 pr_info("%s: PMIC REVISION = %X\n", __func__, pmic->revision);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700374
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530375 (void) memcpy((void *)&pmic->pdata, (const void *)pdata,
376 sizeof(pmic->pdata));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530378 rc = pm8901_add_subdevices(pdata, pmic);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 if (rc) {
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530380 pr_err("Cannot add subdevices rc=%d\n", rc);
381 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382 }
383
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530384 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530386err:
387 platform_set_drvdata(pdev, NULL);
388 kfree(pmic);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 return rc;
390}
391
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530392static int __devexit pm8901_remove(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700393{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530394 struct pm8xxx_drvdata *drvdata;
395 struct pm8901_chip *pmic = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530397 drvdata = platform_get_drvdata(pdev);
398 if (drvdata)
399 pmic = drvdata->pm_chip_data;
400 if (pmic) {
401 if (pmic->dev)
402 mfd_remove_devices(pmic->dev);
403 if (pmic->irq_chip)
404 pm8xxx_irq_exit(pmic->irq_chip);
405 kfree(pmic->mfd_regulators);
406 kfree(pmic);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 }
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530408 platform_set_drvdata(pdev, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409
410 return 0;
411}
412
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530413static struct platform_driver pm8901_driver = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414 .probe = pm8901_probe,
415 .remove = __devexit_p(pm8901_remove),
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530416 .driver = {
417 .name = "pm8901-core",
418 .owner = THIS_MODULE,
419 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700420};
421
422static int __init pm8901_init(void)
423{
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530424 return platform_driver_register(&pm8901_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425}
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530426postcore_initcall(pm8901_init);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427
428static void __exit pm8901_exit(void)
429{
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530430 platform_driver_unregister(&pm8901_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700432module_exit(pm8901_exit);
433
434MODULE_LICENSE("GPL v2");
435MODULE_DESCRIPTION("PMIC8901 core driver");
436MODULE_VERSION("1.0");
437MODULE_ALIAS("platform:pmic8901-core");