blob: 00ac2ab7eee067aa0ffe12596ef1f9ba424e2736 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
3 *
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>
17#include <linux/slab.h>
18#include <linux/spinlock.h>
19#include <linux/platform_device.h>
20#include <linux/mfd/pm8xxx/core.h>
21#include <linux/mfd/pm8xxx/misc.h>
22
23/* PON CTRL 1 register */
24#define REG_PM8058_PON_CTRL_1 0x01C
25#define REG_PM8921_PON_CTRL_1 0x01C
Jay Chokshi86580f22011-10-17 12:27:52 -070026#define REG_PM8018_PON_CTRL_1 0x01C
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027
28#define PON_CTRL_1_PULL_UP_MASK 0xE0
29#define PON_CTRL_1_USB_PWR_EN 0x10
30
31#define PON_CTRL_1_WD_EN_MASK 0x08
32#define PON_CTRL_1_WD_EN_RESET 0x08
33#define PON_CTRL_1_WD_EN_PWR_OFF 0x00
34
35/* Regulator L22 control register */
36#define REG_PM8058_L22_CTRL 0x121
37
38/* SLEEP CTRL register */
39#define REG_PM8058_SLEEP_CTRL 0x02B
40#define REG_PM8921_SLEEP_CTRL 0x10A
Jay Chokshi86580f22011-10-17 12:27:52 -070041#define REG_PM8018_SLEEP_CTRL 0x10A
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042
43#define SLEEP_CTRL_SMPL_EN_MASK 0x04
44#define SLEEP_CTRL_SMPL_EN_RESET 0x04
45#define SLEEP_CTRL_SMPL_EN_PWR_OFF 0x00
46
47/* FTS regulator PMR registers */
48#define REG_PM8901_REGULATOR_S1_PMR 0xA7
49#define REG_PM8901_REGULATOR_S2_PMR 0xA8
50#define REG_PM8901_REGULATOR_S3_PMR 0xA9
51#define REG_PM8901_REGULATOR_S4_PMR 0xAA
52
53#define PM8901_REGULATOR_PMR_STATE_MASK 0x60
54#define PM8901_REGULATOR_PMR_STATE_OFF 0x20
55
56struct pm8xxx_misc_chip {
57 struct list_head link;
58 struct pm8xxx_misc_platform_data pdata;
59 struct device *dev;
60 enum pm8xxx_version version;
61};
62
63static LIST_HEAD(pm8xxx_misc_chips);
64static DEFINE_SPINLOCK(pm8xxx_misc_chips_lock);
65
66static int pm8xxx_misc_masked_write(struct pm8xxx_misc_chip *chip, u16 addr,
67 u8 mask, u8 val)
68{
69 int rc;
70 u8 reg;
71
72 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
73 if (rc) {
74 pr_err("pm8xxx_readb(0x%03X) failed, rc=%d\n", addr, rc);
75 return rc;
76 }
77 reg &= ~mask;
78 reg |= val & mask;
79 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
80 if (rc)
81 pr_err("pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n", addr,
82 reg, rc);
83 return rc;
84}
85
Jay Chokshi86580f22011-10-17 12:27:52 -070086static int __pm8018_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
87{
88 int rc;
89
90 /* Enable SMPL if resetting is desired. */
91 rc = pm8xxx_misc_masked_write(chip, REG_PM8018_SLEEP_CTRL,
92 SLEEP_CTRL_SMPL_EN_MASK,
93 (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
94 if (rc) {
95 pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
96 return rc;
97 }
98
99 /*
100 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
101 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
102 * USB charging is enabled.
103 */
104 rc = pm8xxx_misc_masked_write(chip, REG_PM8018_PON_CTRL_1,
105 PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
106 | PON_CTRL_1_WD_EN_MASK,
107 PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
108 | (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
109 if (rc)
110 pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
111
112 return rc;
113}
114
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115static int __pm8058_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
116{
117 int rc;
118
119 /*
120 * Fix-up: Set regulator LDO22 to 1.225 V in high power mode. Leave its
121 * pull-down state intact. This ensures a safe shutdown.
122 */
123 rc = pm8xxx_misc_masked_write(chip, REG_PM8058_L22_CTRL, 0xBF, 0x93);
124 if (rc) {
125 pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
126 goto read_write_err;
127 }
128
129 /* Enable SMPL if resetting is desired. */
130 rc = pm8xxx_misc_masked_write(chip, REG_PM8058_SLEEP_CTRL,
131 SLEEP_CTRL_SMPL_EN_MASK,
132 (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
133 if (rc) {
134 pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
135 goto read_write_err;
136 }
137
138 /*
139 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
140 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
141 * USB charging is enabled.
142 */
143 rc = pm8xxx_misc_masked_write(chip, REG_PM8058_PON_CTRL_1,
144 PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
145 | PON_CTRL_1_WD_EN_MASK,
146 PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
147 | (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
148 if (rc) {
149 pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
150 goto read_write_err;
151 }
152
153read_write_err:
154 return rc;
155}
156
157static int __pm8901_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
158{
159 int rc = 0, i;
160 u8 pmr_addr[4] = {
161 REG_PM8901_REGULATOR_S2_PMR,
162 REG_PM8901_REGULATOR_S3_PMR,
163 REG_PM8901_REGULATOR_S4_PMR,
164 REG_PM8901_REGULATOR_S1_PMR,
165 };
166
167 /* Fix-up: Turn off regulators S1, S2, S3, S4 when shutting down. */
168 if (!reset) {
169 for (i = 0; i < 4; i++) {
170 rc = pm8xxx_misc_masked_write(chip, pmr_addr[i],
171 PM8901_REGULATOR_PMR_STATE_MASK,
172 PM8901_REGULATOR_PMR_STATE_OFF);
173 if (rc) {
174 pr_err("pm8xxx_misc_masked_write failed, "
175 "rc=%d\n", rc);
176 goto read_write_err;
177 }
178 }
179 }
180
181read_write_err:
182 return rc;
183}
184
185static int __pm8921_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
186{
187 int rc;
188
189 /* Enable SMPL if resetting is desired. */
190 rc = pm8xxx_misc_masked_write(chip, REG_PM8921_SLEEP_CTRL,
191 SLEEP_CTRL_SMPL_EN_MASK,
192 (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
193 if (rc) {
194 pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
195 goto read_write_err;
196 }
197
198 /*
199 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
200 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
201 * USB charging is enabled.
202 */
203 rc = pm8xxx_misc_masked_write(chip, REG_PM8921_PON_CTRL_1,
204 PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
205 | PON_CTRL_1_WD_EN_MASK,
206 PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
207 | (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
208 if (rc) {
209 pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
210 goto read_write_err;
211 }
212
213read_write_err:
214 return rc;
215}
216
217/**
218 * pm8xxx_reset_pwr_off - switch all PM8XXX PMIC chips attached to the system to
219 * either reset or shutdown when they are turned off
220 * @reset: 0 = shudown the PMICs, 1 = shutdown and then restart the PMICs
221 *
222 * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
223 */
224int pm8xxx_reset_pwr_off(int reset)
225{
226 struct pm8xxx_misc_chip *chip;
227 unsigned long flags;
228 int rc = 0;
229
230 spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
231
232 /* Loop over all attached PMICs and call specific functions for them. */
233 list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
234 switch (chip->version) {
Jay Chokshi86580f22011-10-17 12:27:52 -0700235 case PM8XXX_VERSION_8018:
236 rc = __pm8018_reset_pwr_off(chip, reset);
237 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700238 case PM8XXX_VERSION_8058:
239 rc = __pm8058_reset_pwr_off(chip, reset);
240 break;
241 case PM8XXX_VERSION_8901:
242 rc = __pm8901_reset_pwr_off(chip, reset);
243 break;
244 case PM8XXX_VERSION_8921:
245 rc = __pm8921_reset_pwr_off(chip, reset);
246 break;
247 default:
248 /* PMIC doesn't have reset_pwr_off; do nothing. */
249 break;
250 }
251 if (rc) {
252 pr_err("reset_pwr_off failed, rc=%d\n", rc);
253 break;
254 }
255 }
256
257 spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
258
259 return rc;
260}
261EXPORT_SYMBOL_GPL(pm8xxx_reset_pwr_off);
262
263static int __devinit pm8xxx_misc_probe(struct platform_device *pdev)
264{
265 const struct pm8xxx_misc_platform_data *pdata = pdev->dev.platform_data;
266 struct pm8xxx_misc_chip *chip;
267 struct pm8xxx_misc_chip *sibling;
268 struct list_head *prev;
269 unsigned long flags;
270 int rc = 0;
271
272 if (!pdata) {
273 pr_err("missing platform data\n");
274 return -EINVAL;
275 }
276
277 chip = kzalloc(sizeof(struct pm8xxx_misc_chip), GFP_KERNEL);
278 if (!chip) {
279 pr_err("Cannot allocate %d bytes\n",
280 sizeof(struct pm8xxx_misc_chip));
281 return -ENOMEM;
282 }
283
284 chip->dev = &pdev->dev;
285 chip->version = pm8xxx_get_version(chip->dev->parent);
286 memcpy(&(chip->pdata), pdata, sizeof(struct pm8xxx_misc_platform_data));
287
288 /* Insert PMICs in priority order (lowest value first). */
289 spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
290 prev = &pm8xxx_misc_chips;
291 list_for_each_entry(sibling, &pm8xxx_misc_chips, link) {
292 if (chip->pdata.priority < sibling->pdata.priority)
293 break;
294 else
295 prev = &sibling->link;
296 }
297 list_add(&chip->link, prev);
298 spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
299
300 platform_set_drvdata(pdev, chip);
301
302 return rc;
303}
304
305static int __devexit pm8xxx_misc_remove(struct platform_device *pdev)
306{
307 struct pm8xxx_misc_chip *chip = platform_get_drvdata(pdev);
308 unsigned long flags;
309
310 spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
311 list_del(&chip->link);
312 spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
313
314 platform_set_drvdata(pdev, NULL);
315 kfree(chip);
316
317 return 0;
318}
319
320static struct platform_driver pm8xxx_misc_driver = {
321 .probe = pm8xxx_misc_probe,
322 .remove = __devexit_p(pm8xxx_misc_remove),
323 .driver = {
324 .name = PM8XXX_MISC_DEV_NAME,
325 .owner = THIS_MODULE,
326 },
327};
328
329static int __init pm8xxx_misc_init(void)
330{
331 return platform_driver_register(&pm8xxx_misc_driver);
332}
333postcore_initcall(pm8xxx_misc_init);
334
335static void __exit pm8xxx_misc_exit(void)
336{
337 platform_driver_unregister(&pm8xxx_misc_driver);
338}
339module_exit(pm8xxx_misc_exit);
340
341MODULE_LICENSE("GPL v2");
342MODULE_DESCRIPTION("PMIC 8XXX misc driver");
343MODULE_VERSION("1.0");
344MODULE_ALIAS("platform:" PM8XXX_MISC_DEV_NAME);