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