blob: 1ca5331e13332f849309fcdb52643e0a3702ed6d [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070016#include <linux/mfd/core.h>
Anirudh Ghayalc8051a82011-11-17 09:28:24 +053017#include <linux/msm_ssbi.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018#include <linux/mfd/pmic8901.h>
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053019#include <linux/mfd/pm8xxx/core.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070020#include <linux/module.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
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053034#define SINGLE_IRQ_RESOURCE(_name, _irq) \
35{ \
36 .name = _name, \
37 .start = _irq, \
38 .end = _irq, \
39 .flags = IORESOURCE_IRQ, \
40}
41
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042struct pm8901_chip {
43 struct pm8901_platform_data pdata;
Anirudh Ghayalc8051a82011-11-17 09:28:24 +053044 struct device *dev;
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053045 struct pm_irq_chip *irq_chip;
46 struct mfd_cell *mfd_regulators;
47 u8 revision;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048};
49
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053050static int pm8901_readb(const struct device *dev, u16 addr, u8 *val)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053052 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
53 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053055 return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056}
57
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053058static int pm8901_writeb(const struct device *dev, u16 addr, u8 val)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053060 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
61 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053063 return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064}
65
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053066static int pm8901_read_buf(const struct device *dev, u16 addr, u8 *buf,
67 int cnt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053069 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
70 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053072 return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073}
74
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053075static int pm8901_write_buf(const struct device *dev, u16 addr, u8 *buf,
76 int cnt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053078 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
79 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053081 return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082}
83
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053084static int pm8901_read_irq_stat(const struct device *dev, int irq)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053086 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
87 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070088
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053089 return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070090
91 return 0;
92}
93
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053094static enum pm8xxx_version pm8901_get_version(const struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +053096 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
97 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
98 enum pm8xxx_version version = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530100 if ((pmic->revision & PM8901_VERSION_MASK) == PM8901_VERSION_VALUE)
101 version = PM8XXX_VERSION_8901;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530103 return version;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104}
105
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530106static int pm8901_get_revision(const struct device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530108 const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
109 const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530111 return pmic->revision & PM8901_REVISION_MASK;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112}
113
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530114static struct pm8xxx_drvdata pm8901_drvdata = {
115 .pmic_readb = pm8901_readb,
116 .pmic_writeb = pm8901_writeb,
117 .pmic_read_buf = pm8901_read_buf,
118 .pmic_write_buf = pm8901_write_buf,
119 .pmic_read_irq_stat = pm8901_read_irq_stat,
120 .pmic_get_version = pm8901_get_version,
121 .pmic_get_revision = pm8901_get_revision,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122};
123
Anirudh Ghayal934b2712011-12-13 12:49:51 +0530124static struct mfd_cell misc_cell = {
125 .name = PM8XXX_MISC_DEV_NAME,
126 .id = 1,
127};
128
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530129static struct mfd_cell debugfs_cell = {
130 .name = "pm8xxx-debug",
131 .id = 1,
132 .platform_data = "pm8901-dbg",
133 .pdata_size = sizeof("pm8901-dbg"),
134};
135
136static const struct resource thermal_alarm_cell_resources[] = {
137 SINGLE_IRQ_RESOURCE("pm8901_tempstat_irq", PM8901_TEMPSTAT_IRQ),
138 SINGLE_IRQ_RESOURCE("pm8901_overtemp_irq", PM8901_OVERTEMP_IRQ),
139};
140
141static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
142 .adc_type = PM8XXX_TM_ADC_NONE,
143 .reg_addr_temp_alarm_ctrl = REG_TEMP_ALRM_CTRL,
144 .reg_addr_temp_alarm_pwm = REG_TEMP_ALRM_PWM,
145 .tm_name = "pm8901_tz",
146 .irq_name_temp_stat = "pm8901_tempstat_irq",
147 .irq_name_over_temp = "pm8901_overtemp_irq",
148};
149
150static struct mfd_cell thermal_alarm_cell = {
151 .name = PM8XXX_TM_DEV_NAME,
152 .id = 1,
153 .resources = thermal_alarm_cell_resources,
154 .num_resources = ARRAY_SIZE(thermal_alarm_cell_resources),
155 .platform_data = &thermal_alarm_cdata,
156 .pdata_size = sizeof(struct pm8xxx_tm_core_data),
157};
158
159static const struct resource mpp_cell_resources[] = {
160 {
161 .start = PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0),
162 .end = PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0)
163 + PM8901_MPPS - 1,
164 .flags = IORESOURCE_IRQ,
165 },
166};
167
168static struct mfd_cell mpp_cell = {
169 .name = PM8XXX_MPP_DEV_NAME,
170 .id = 1,
171 .resources = mpp_cell_resources,
172 .num_resources = ARRAY_SIZE(mpp_cell_resources),
173};
174
175static int __devinit
176pm8901_add_subdevices(const struct pm8901_platform_data *pdata,
177 struct pm8901_chip *pmic)
178{
179 int rc = 0, irq_base = 0, i;
180 struct pm_irq_chip *irq_chip;
181 static struct mfd_cell *mfd_regulators;
182
183 if (pdata->irq_pdata) {
184 pdata->irq_pdata->irq_cdata.nirqs = PM8901_NR_IRQS;
185 pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
186 irq_base = pdata->irq_pdata->irq_base;
187 irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
188
189 if (IS_ERR(irq_chip)) {
190 pr_err("Failed to init interrupts ret=%ld\n",
191 PTR_ERR(irq_chip));
192 return PTR_ERR(irq_chip);
193 }
194 pmic->irq_chip = irq_chip;
195 }
196
197 if (pdata->mpp_pdata) {
198 pdata->mpp_pdata->core_data.nmpps = PM8901_MPPS;
199 pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
200 mpp_cell.platform_data = pdata->mpp_pdata;
201 mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
202 rc = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
203 irq_base);
204 if (rc) {
205 pr_err("Failed to add mpp subdevice ret=%d\n", rc);
206 goto bail;
207 }
208 }
209
210 if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
211 mfd_regulators = kzalloc(sizeof(struct mfd_cell)
212 * (pdata->num_regulators), GFP_KERNEL);
213 if (!mfd_regulators) {
214 pr_err("Cannot allocate %d bytes for pm8901 regulator "
215 "mfd cells\n", sizeof(struct mfd_cell)
216 * (pdata->num_regulators));
217 rc = -ENOMEM;
218 goto bail;
219 }
220 for (i = 0; i < pdata->num_regulators; i++) {
221 mfd_regulators[i].name = "pm8901-regulator";
222 mfd_regulators[i].id = pdata->regulator_pdatas[i].id;
223 mfd_regulators[i].platform_data =
224 &(pdata->regulator_pdatas[i]);
225 mfd_regulators[i].pdata_size =
226 sizeof(struct pm8901_vreg_pdata);
227 }
228 rc = mfd_add_devices(pmic->dev, 0, mfd_regulators,
229 pdata->num_regulators, NULL, irq_base);
230 if (rc) {
231 pr_err("Failed to add regulator subdevices ret=%d\n",
232 rc);
233 kfree(mfd_regulators);
234 goto bail;
235 }
236 pmic->mfd_regulators = mfd_regulators;
237 }
238
239 rc = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
240 irq_base);
241 if (rc) {
242 pr_err("Failed to add thermal alarm subdevice ret=%d\n",
243 rc);
244 goto bail;
245 }
246
247 rc = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
248 if (rc) {
249 pr_err("Failed to add debugfs subdevice ret=%d\n", rc);
250 goto bail;
251 }
252
Anirudh Ghayal934b2712011-12-13 12:49:51 +0530253 if (pdata->misc_pdata) {
254 misc_cell.platform_data = pdata->misc_pdata;
255 misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
256 rc = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
257 irq_base);
258 if (rc) {
259 pr_err("Failed to add misc subdevice ret=%d\n", rc);
260 goto bail;
261 }
262 }
263
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530264 return rc;
265
266bail:
267 if (pmic->irq_chip) {
268 pm8xxx_irq_exit(pmic->irq_chip);
269 pmic->irq_chip = NULL;
270 }
271 return rc;
272}
273
David Collinse41d1db2012-04-05 10:08:10 -0700274static const char * const pm8901_rev_names[] = {
275 [PM8XXX_REVISION_8901_TEST] = "test",
276 [PM8XXX_REVISION_8901_1p0] = "1.0",
277 [PM8XXX_REVISION_8901_1p1] = "1.1",
278 [PM8XXX_REVISION_8901_2p0] = "2.0",
279 [PM8XXX_REVISION_8901_2p1] = "2.1",
280 [PM8XXX_REVISION_8901_2p2] = "2.2",
281 [PM8XXX_REVISION_8901_2p3] = "2.3",
282};
283
Stephen Boyd9a8f4eb2012-02-21 23:51:00 -0800284static int __devinit pm8901_probe(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530286 int rc;
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530287 struct pm8901_platform_data *pdata = pdev->dev.platform_data;
David Collinse41d1db2012-04-05 10:08:10 -0700288 const char *revision_name = "unknown";
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530289 struct pm8901_chip *pmic;
David Collinse41d1db2012-04-05 10:08:10 -0700290 int revision;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530292 if (pdata == NULL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 pr_err("%s: No platform_data or IRQ.\n", __func__);
294 return -ENODEV;
295 }
296
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530297 pmic = kzalloc(sizeof *pmic, GFP_KERNEL);
298 if (pmic == NULL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700299 pr_err("%s: kzalloc() failed.\n", __func__);
300 return -ENOMEM;
301 }
302
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530303 pmic->dev = &pdev->dev;
304
305 pm8901_drvdata.pm_chip_data = pmic;
306 platform_set_drvdata(pdev, &pm8901_drvdata);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307
308 /* Read PMIC chip revision */
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530309 rc = pm8901_readb(pmic->dev, PM8901_REG_REV, &pmic->revision);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 if (rc)
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530311 pr_err("%s: Failed reading version register rc=%d.\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312 __func__, rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313
David Collinse41d1db2012-04-05 10:08:10 -0700314 pr_info("%s: PMIC revision reg: %02X\n", __func__, pmic->revision);
315 revision = pm8xxx_get_revision(pmic->dev);
316 if (revision >= 0 && revision < ARRAY_SIZE(pm8901_rev_names))
317 revision_name = pm8901_rev_names[revision];
318 pr_info("%s: PMIC version: PM8901 rev %s\n", __func__, revision_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530320 (void) memcpy((void *)&pmic->pdata, (const void *)pdata,
321 sizeof(pmic->pdata));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700322
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530323 rc = pm8901_add_subdevices(pdata, pmic);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324 if (rc) {
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530325 pr_err("Cannot add subdevices rc=%d\n", rc);
326 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700327 }
328
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530329 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530331err:
332 platform_set_drvdata(pdev, NULL);
333 kfree(pmic);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700334 return rc;
335}
336
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530337static int __devexit pm8901_remove(struct platform_device *pdev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700338{
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530339 struct pm8xxx_drvdata *drvdata;
340 struct pm8901_chip *pmic = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700341
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530342 drvdata = platform_get_drvdata(pdev);
343 if (drvdata)
344 pmic = drvdata->pm_chip_data;
345 if (pmic) {
346 if (pmic->dev)
347 mfd_remove_devices(pmic->dev);
348 if (pmic->irq_chip)
349 pm8xxx_irq_exit(pmic->irq_chip);
350 kfree(pmic->mfd_regulators);
351 kfree(pmic);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352 }
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530353 platform_set_drvdata(pdev, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700354
355 return 0;
356}
357
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530358static struct platform_driver pm8901_driver = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359 .probe = pm8901_probe,
360 .remove = __devexit_p(pm8901_remove),
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530361 .driver = {
362 .name = "pm8901-core",
363 .owner = THIS_MODULE,
364 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365};
366
367static int __init pm8901_init(void)
368{
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530369 return platform_driver_register(&pm8901_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370}
Anirudh Ghayal9f77e962011-12-06 12:38:21 +0530371postcore_initcall(pm8901_init);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372
373static void __exit pm8901_exit(void)
374{
Anirudh Ghayalc8051a82011-11-17 09:28:24 +0530375 platform_driver_unregister(&pm8901_driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377module_exit(pm8901_exit);
378
379MODULE_LICENSE("GPL v2");
380MODULE_DESCRIPTION("PMIC8901 core driver");
381MODULE_VERSION("1.0");
382MODULE_ALIAS("platform:pmic8901-core");