blob: 48ec7482777d1d096480a221e262587605abcd99 [file] [log] [blame]
Jay Chokshi24fc9b62011-07-18 10:51:05 -07001/*
Duy Truong790f06d2013-02-13 16:38:12 -08002 * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
Jay Chokshi24fc9b62011-07-18 10:51:05 -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 Chokshi24fc9b62011-07-18 10:51:05 -070018#include <linux/platform_device.h>
19#include <linux/slab.h>
20#include <linux/err.h>
21#include <linux/msm_ssbi.h>
22#include <linux/mfd/core.h>
23#include <linux/mfd/pm8xxx/pm8821.h>
24#include <linux/mfd/pm8xxx/core.h>
25
26#define REG_HWREV 0x002 /* PMIC4 revision */
27#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
28
29#define REG_MPP_BASE 0x050
Jay Chokshidf821212012-06-07 15:37:58 -070030#define REG_IRQ_BASE 0x100
Jay Chokshi24fc9b62011-07-18 10:51:05 -070031
David Collins16047792012-08-30 16:18:56 -070032#define REG_TEMP_ALARM_CTRL 0x01B
33#define REG_TEMP_ALARM_PWM 0x09B
34
Jay Chokshi24fc9b62011-07-18 10:51:05 -070035#define PM8821_VERSION_MASK 0xFFF0
Jay Chokshi3f8463c2012-02-01 11:47:06 -080036#define PM8821_VERSION_VALUE 0x0BF0
Jay Chokshi24fc9b62011-07-18 10:51:05 -070037#define PM8821_REVISION_MASK 0x000F
38
39#define SINGLE_IRQ_RESOURCE(_name, _irq) \
40{ \
41 .name = _name, \
42 .start = _irq, \
43 .end = _irq, \
44 .flags = IORESOURCE_IRQ, \
45}
46
47struct pm8821 {
48 struct device *dev;
49 struct pm_irq_chip *irq_chip;
50 u32 rev_registers;
51};
52
53static int pm8821_readb(const struct device *dev, u16 addr, u8 *val)
54{
55 const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
56 const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
57
58 return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
59}
60
61static int pm8821_writeb(const struct device *dev, u16 addr, u8 val)
62{
63 const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
64 const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
65
66 return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
67}
68
69static int pm8821_read_buf(const struct device *dev, u16 addr, u8 *buf,
70 int cnt)
71{
72 const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
73 const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
74
75 return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
76}
77
78static int pm8821_write_buf(const struct device *dev, u16 addr, u8 *buf,
79 int cnt)
80{
81 const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
82 const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
83
84 return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
85}
86
87static int pm8821_read_irq_stat(const struct device *dev, int irq)
88{
89 const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
90 const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
91
Jay Chokshidf821212012-06-07 15:37:58 -070092 return pm8821_get_irq_stat(pmic->irq_chip, irq);
Jay Chokshi24fc9b62011-07-18 10:51:05 -070093}
94
95static enum pm8xxx_version pm8821_get_version(const struct device *dev)
96{
97 const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
98 const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
99 enum pm8xxx_version version = -ENODEV;
100
101 if ((pmic->rev_registers & PM8821_VERSION_MASK) == PM8821_VERSION_VALUE)
102 version = PM8XXX_VERSION_8821;
103
104 return version;
105}
106
107static int pm8821_get_revision(const struct device *dev)
108{
109 const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
110 const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
111
112 return pmic->rev_registers & PM8821_REVISION_MASK;
113}
114
115static struct pm8xxx_drvdata pm8821_drvdata = {
116 .pmic_readb = pm8821_readb,
117 .pmic_writeb = pm8821_writeb,
118 .pmic_read_buf = pm8821_read_buf,
119 .pmic_write_buf = pm8821_write_buf,
120 .pmic_read_irq_stat = pm8821_read_irq_stat,
121 .pmic_get_version = pm8821_get_version,
122 .pmic_get_revision = pm8821_get_revision,
123};
124
125static const struct resource mpp_cell_resources[] __devinitconst = {
126 {
127 .start = PM8821_IRQ_BLOCK_BIT(PM8821_MPP_BLOCK_START, 0),
128 .end = PM8821_IRQ_BLOCK_BIT(PM8821_MPP_BLOCK_START, 0)
129 + PM8821_NR_MPPS - 1,
130 .flags = IORESOURCE_IRQ,
131 },
132};
133
134static struct mfd_cell mpp_cell __devinitdata = {
135 .name = PM8XXX_MPP_DEV_NAME,
136 .id = 1,
137 .resources = mpp_cell_resources,
138 .num_resources = ARRAY_SIZE(mpp_cell_resources),
139};
140
141static struct mfd_cell debugfs_cell __devinitdata = {
142 .name = "pm8xxx-debug",
143 .id = 1,
144 .platform_data = "pm8821-dbg",
Jay Chokshi520d4732011-10-11 12:22:02 -0700145 .pdata_size = sizeof("pm8821-dbg"),
Jay Chokshi24fc9b62011-07-18 10:51:05 -0700146};
147
David Collins16047792012-08-30 16:18:56 -0700148static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
149 SINGLE_IRQ_RESOURCE("pm8821_tempstat_irq", PM8821_TEMPSTAT_IRQ),
150 SINGLE_IRQ_RESOURCE("pm8821_overtemp_irq", PM8821_OVERTEMP_IRQ),
151};
152
153static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
154 .adc_type = PM8XXX_TM_ADC_NONE,
155 .reg_addr_temp_alarm_ctrl = REG_TEMP_ALARM_CTRL,
156 .reg_addr_temp_alarm_pwm = REG_TEMP_ALARM_PWM,
157 .tm_name = "pm8821_tz",
158 .irq_name_temp_stat = "pm8821_tempstat_irq",
159 .irq_name_over_temp = "pm8821_overtemp_irq",
160 .default_no_adc_temp = 37000,
161};
162
163static struct mfd_cell thermal_alarm_cell __devinitdata = {
164 .name = PM8XXX_TM_DEV_NAME,
165 .id = 1,
166 .resources = thermal_alarm_cell_resources,
167 .num_resources = ARRAY_SIZE(thermal_alarm_cell_resources),
168 .platform_data = &thermal_alarm_cdata,
169 .pdata_size = sizeof(struct pm8xxx_tm_core_data),
170};
Jay Chokshi24fc9b62011-07-18 10:51:05 -0700171
172static int __devinit
173pm8821_add_subdevices(const struct pm8821_platform_data *pdata,
174 struct pm8821 *pmic)
175{
176 int ret = 0, irq_base = 0;
177 struct pm_irq_chip *irq_chip;
178
179 if (pdata->irq_pdata) {
180 pdata->irq_pdata->irq_cdata.nirqs = PM8821_NR_IRQS;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530181 pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
Jay Chokshi24fc9b62011-07-18 10:51:05 -0700182 irq_base = pdata->irq_pdata->irq_base;
Jay Chokshidf821212012-06-07 15:37:58 -0700183 irq_chip = pm8821_irq_init(pmic->dev, pdata->irq_pdata);
Jay Chokshi24fc9b62011-07-18 10:51:05 -0700184
185 if (IS_ERR(irq_chip)) {
186 pr_err("Failed to init interrupts ret=%ld\n",
187 PTR_ERR(irq_chip));
188 return PTR_ERR(irq_chip);
189 }
190 pmic->irq_chip = irq_chip;
191 }
192
193 if (pdata->mpp_pdata) {
194 pdata->mpp_pdata->core_data.nmpps = PM8821_NR_MPPS;
195 pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
196 mpp_cell.platform_data = pdata->mpp_pdata;
Jay Chokshi520d4732011-10-11 12:22:02 -0700197 mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
Jay Chokshi24fc9b62011-07-18 10:51:05 -0700198 ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
199 irq_base);
200 if (ret) {
201 pr_err("Failed to add mpp subdevice ret=%d\n", ret);
202 goto bail;
203 }
204 }
205
206 ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
207 if (ret) {
208 pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
209 goto bail;
210 }
211
David Collins16047792012-08-30 16:18:56 -0700212 ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
213 irq_base);
214 if (ret) {
215 pr_err("Failed to add thermal alarm subdevice ret=%d\n",
216 ret);
217 goto bail;
218 }
219
Jay Chokshi24fc9b62011-07-18 10:51:05 -0700220 return 0;
221bail:
222 if (pmic->irq_chip) {
Jay Chokshidf821212012-06-07 15:37:58 -0700223 pm8821_irq_exit(pmic->irq_chip);
Jay Chokshi24fc9b62011-07-18 10:51:05 -0700224 pmic->irq_chip = NULL;
225 }
226 return ret;
227}
228
229static const char * const pm8821_rev_names[] = {
230 [PM8XXX_REVISION_8821_TEST] = "test",
231 [PM8XXX_REVISION_8821_1p0] = "1.0",
232 [PM8XXX_REVISION_8821_2p0] = "2.0",
233 [PM8XXX_REVISION_8821_2p1] = "2.1",
234};
235
236static int __devinit pm8821_probe(struct platform_device *pdev)
237{
238 const struct pm8821_platform_data *pdata = pdev->dev.platform_data;
239 const char *revision_name = "unknown";
240 struct pm8821 *pmic;
241 enum pm8xxx_version version;
242 int revision;
243 int rc;
244 u8 val;
245
246 if (!pdata) {
247 pr_err("missing platform data\n");
248 return -EINVAL;
249 }
250
251 pmic = kzalloc(sizeof(struct pm8821), GFP_KERNEL);
252 if (!pmic) {
253 pr_err("Cannot alloc pm8821 struct\n");
254 return -ENOMEM;
255 }
256
257 /* Read PMIC chip revision */
258 rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
259 if (rc) {
260 pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
261 goto err_read_rev;
262 }
263 pr_info("PMIC revision 1: PM8821 rev %02X\n", val);
264 pmic->rev_registers = val;
265
266 /* Read PMIC chip revision 2 */
267 rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
268 if (rc) {
269 pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
270 REG_HWREV_2, rc);
271 goto err_read_rev;
272 }
273 pr_info("PMIC revision 2: PM8821 rev %02X\n", val);
274 pmic->rev_registers |= val << BITS_PER_BYTE;
275
276 pmic->dev = &pdev->dev;
277 pm8821_drvdata.pm_chip_data = pmic;
278 platform_set_drvdata(pdev, &pm8821_drvdata);
279
280 /* Print out human readable version and revision names. */
281 version = pm8xxx_get_version(pmic->dev);
282 if (version == PM8XXX_VERSION_8821) {
283 revision = pm8xxx_get_revision(pmic->dev);
284 if (revision >= 0 && revision < ARRAY_SIZE(pm8821_rev_names))
285 revision_name = pm8821_rev_names[revision];
286 pr_info("PMIC version: PM8821 ver %s\n", revision_name);
287 } else {
288 WARN_ON(version != PM8XXX_VERSION_8821);
289 }
290
291 rc = pm8821_add_subdevices(pdata, pmic);
292 if (rc) {
293 pr_err("Cannot add subdevices rc=%d\n", rc);
294 goto err;
295 }
296
297 return 0;
298
299err:
300 mfd_remove_devices(pmic->dev);
301 platform_set_drvdata(pdev, NULL);
302err_read_rev:
303 kfree(pmic);
304 return rc;
305}
306
307static int __devexit pm8821_remove(struct platform_device *pdev)
308{
309 struct pm8xxx_drvdata *drvdata;
310 struct pm8821 *pmic = NULL;
311
312 drvdata = platform_get_drvdata(pdev);
313 if (drvdata)
314 pmic = drvdata->pm_chip_data;
Willie Ruanb0b94392012-11-15 15:55:15 -0800315 if (pmic) {
316 if (pmic->dev)
317 mfd_remove_devices(pmic->dev);
318 if (pmic->irq_chip)
319 pm8821_irq_exit(pmic->irq_chip);
Jay Chokshi24fc9b62011-07-18 10:51:05 -0700320 }
321 platform_set_drvdata(pdev, NULL);
322 kfree(pmic);
323
324 return 0;
325}
326
327static struct platform_driver pm8821_driver = {
328 .probe = pm8821_probe,
329 .remove = __devexit_p(pm8821_remove),
330 .driver = {
331 .name = "pm8821-core",
332 .owner = THIS_MODULE,
333 },
334};
335
336static int __init pm8821_init(void)
337{
338 return platform_driver_register(&pm8821_driver);
339}
340postcore_initcall(pm8821_init);
341
342static void __exit pm8821_exit(void)
343{
344 platform_driver_unregister(&pm8821_driver);
345}
346module_exit(pm8821_exit);
347
348MODULE_LICENSE("GPL v2");
349MODULE_DESCRIPTION("PMIC 8821 core driver");
350MODULE_VERSION("1.0");
351MODULE_ALIAS("platform:pm8821-core");