aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/88pm805.c
diff options
context:
space:
mode:
authorQiao Zhou <zhouqiao@marvell.com>2012-07-09 02:37:32 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2012-07-09 09:12:51 -0400
commit70c6cce040661204986ebbf22224cb24bd77ea71 (patch)
treefe60851749c386e65735818e397850f2b3bd7d3e /drivers/mfd/88pm805.c
parent49003a68926e073fc71062d210c6f9febc8665a2 (diff)
mfd: Support 88pm80x in 80x driver
88PM800 and 88PM805 are two discrete chips used for power management. Hardware designer can use them together or only one of them according to requirement. 88pm80x.c provides common i2c driver handling for both 800 and 805, such as i2c_driver init, regmap init, read/write api etc. 88pm800.c handles specifically for 800, such as chip init, irq init/handle, mfd device register, including rtc, onkey, regulator( to be add later) etc. besides that, 800 has three i2c device, one regular i2c client, two other i2c dummy for gpadc and power purpose. 88pm805.c handles specifically for 805, such as chip init, irq init/handle, mfd device register, including codec, headset/mic detect etc. the i2c operation of both 800 and 805 are via regmap, and 88pm80x-i2c exported a group of r/w bulk r/w and bits set API for facility. Signed-off-by: Qiao Zhou <zhouqiao@marvell.com> Reviewed-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd/88pm805.c')
-rw-r--r--drivers/mfd/88pm805.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c
new file mode 100644
index 00000000000..d93c3091cb9
--- /dev/null
+++ b/drivers/mfd/88pm805.c
@@ -0,0 +1,299 @@
1/*
2 * Base driver for Marvell 88PM805
3 *
4 * Copyright (C) 2012 Marvell International Ltd.
5 * Haojian Zhuang <haojian.zhuang@marvell.com>
6 * Joseph(Yossi) Hanin <yhanin@marvell.com>
7 * Qiao Zhou <zhouqiao@marvell.com>
8 *
9 * This file is subject to the terms and conditions of the GNU General
10 * Public License. See the file "COPYING" in the main directory of this
11 * archive for more details.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/i2c.h>
26#include <linux/irq.h>
27#include <linux/mfd/core.h>
28#include <linux/mfd/88pm80x.h>
29#include <linux/slab.h>
30#include <linux/delay.h>
31
32#define PM805_CHIP_ID (0x00)
33
34static const struct i2c_device_id pm80x_id_table[] = {
35 {"88PM805", CHIP_PM805},
36};
37MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
38
39/* Interrupt Number in 88PM805 */
40enum {
41 PM805_IRQ_LDO_OFF, /*0 */
42 PM805_IRQ_SRC_DPLL_LOCK, /*1 */
43 PM805_IRQ_CLIP_FAULT,
44 PM805_IRQ_MIC_CONFLICT,
45 PM805_IRQ_HP2_SHRT,
46 PM805_IRQ_HP1_SHRT, /*5 */
47 PM805_IRQ_FINE_PLL_FAULT,
48 PM805_IRQ_RAW_PLL_FAULT,
49 PM805_IRQ_VOLP_BTN_DET,
50 PM805_IRQ_VOLM_BTN_DET,
51 PM805_IRQ_SHRT_BTN_DET, /*10 */
52 PM805_IRQ_MIC_DET, /*11 */
53
54 PM805_MAX_IRQ,
55};
56
57static struct resource codec_resources[] = {
58 {
59 /* Headset microphone insertion or removal */
60 .name = "micin",
61 .start = PM805_IRQ_MIC_DET,
62 .end = PM805_IRQ_MIC_DET,
63 .flags = IORESOURCE_IRQ,
64 },
65 {
66 /* Audio short HP1 */
67 .name = "audio-short1",
68 .start = PM805_IRQ_HP1_SHRT,
69 .end = PM805_IRQ_HP1_SHRT,
70 .flags = IORESOURCE_IRQ,
71 },
72 {
73 /* Audio short HP2 */
74 .name = "audio-short2",
75 .start = PM805_IRQ_HP2_SHRT,
76 .end = PM805_IRQ_HP2_SHRT,
77 .flags = IORESOURCE_IRQ,
78 },
79};
80
81static struct mfd_cell codec_devs[] = {
82 {
83 .name = "88pm80x-codec",
84 .num_resources = ARRAY_SIZE(codec_resources),
85 .resources = &codec_resources[0],
86 .id = -1,
87 },
88};
89
90static struct regmap_irq pm805_irqs[] = {
91 /* INT0 */
92 [PM805_IRQ_LDO_OFF] = {
93 .mask = PM805_INT1_HP1_SHRT,
94 },
95 [PM805_IRQ_SRC_DPLL_LOCK] = {
96 .mask = PM805_INT1_HP2_SHRT,
97 },
98 [PM805_IRQ_CLIP_FAULT] = {
99 .mask = PM805_INT1_MIC_CONFLICT,
100 },
101 [PM805_IRQ_MIC_CONFLICT] = {
102 .mask = PM805_INT1_CLIP_FAULT,
103 },
104 [PM805_IRQ_HP2_SHRT] = {
105 .mask = PM805_INT1_LDO_OFF,
106 },
107 [PM805_IRQ_HP1_SHRT] = {
108 .mask = PM805_INT1_SRC_DPLL_LOCK,
109 },
110 /* INT1 */
111 [PM805_IRQ_FINE_PLL_FAULT] = {
112 .reg_offset = 1,
113 .mask = PM805_INT2_MIC_DET,
114 },
115 [PM805_IRQ_RAW_PLL_FAULT] = {
116 .reg_offset = 1,
117 .mask = PM805_INT2_SHRT_BTN_DET,
118 },
119 [PM805_IRQ_VOLP_BTN_DET] = {
120 .reg_offset = 1,
121 .mask = PM805_INT2_VOLM_BTN_DET,
122 },
123 [PM805_IRQ_VOLM_BTN_DET] = {
124 .reg_offset = 1,
125 .mask = PM805_INT2_VOLP_BTN_DET,
126 },
127 [PM805_IRQ_SHRT_BTN_DET] = {
128 .reg_offset = 1,
129 .mask = PM805_INT2_RAW_PLL_FAULT,
130 },
131 [PM805_IRQ_MIC_DET] = {
132 .reg_offset = 1,
133 .mask = PM805_INT2_FINE_PLL_FAULT,
134 },
135};
136
137static int __devinit device_irq_init_805(struct pm80x_chip *chip)
138{
139 struct regmap *map = chip->regmap;
140 unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
141 int data, mask, ret = -EINVAL;
142
143 if (!map || !chip->irq) {
144 dev_err(chip->dev, "incorrect parameters\n");
145 return -EINVAL;
146 }
147
148 /*
149 * irq_mode defines the way of clearing interrupt. it's read-clear by
150 * default.
151 */
152 mask =
153 PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT |
154 PM800_STATUS0_INT_MASK;
155
156 data = PM805_STATUS0_INT_CLEAR;
157 ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data);
158 /*
159 * PM805_INT_STATUS is under 32K clock domain, so need to
160 * add proper delay before the next I2C register access.
161 */
162 msleep(1);
163
164 if (ret < 0)
165 goto out;
166
167 ret =
168 regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
169 chip->regmap_irq_chip, &chip->irq_data);
170
171out:
172 return ret;
173}
174
175static void device_irq_exit_805(struct pm80x_chip *chip)
176{
177 regmap_del_irq_chip(chip->irq, chip->irq_data);
178}
179
180static struct regmap_irq_chip pm805_irq_chip = {
181 .name = "88pm805",
182 .irqs = pm805_irqs,
183 .num_irqs = ARRAY_SIZE(pm805_irqs),
184
185 .num_regs = 2,
186 .status_base = PM805_INT_STATUS1,
187 .mask_base = PM805_INT_MASK1,
188 .ack_base = PM805_INT_STATUS1,
189};
190
191static int __devinit device_805_init(struct pm80x_chip *chip)
192{
193 int ret = 0;
194 struct regmap *map = chip->regmap;
195
196 if (!map) {
197 dev_err(chip->dev, "regmap is invalid\n");
198 return -EINVAL;
199 }
200
201 regmap_read(map, PM805_CHIP_ID, &ret);
202 if (ret < 0) {
203 dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
204 goto out_irq_init;
205 }
206 chip->version = ret;
207
208 chip->regmap_irq_chip = &pm805_irq_chip;
209
210 ret = device_irq_init_805(chip);
211 if (ret < 0) {
212 dev_err(chip->dev, "Failed to init pm805 irq!\n");
213 goto out_irq_init;
214 }
215
216 ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
217 ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
218 if (ret < 0) {
219 dev_err(chip->dev, "Failed to add codec subdev\n");
220 goto out_codec;
221 } else
222 dev_info(chip->dev, "[%s]:Added mfd codec_devs\n", __func__);
223
224 return 0;
225
226out_codec:
227 device_irq_exit_805(chip);
228out_irq_init:
229 return ret;
230}
231
232static int __devinit pm805_probe(struct i2c_client *client,
233 const struct i2c_device_id *id)
234{
235 int ret = 0;
236 struct pm80x_chip *chip;
237 struct pm80x_platform_data *pdata = client->dev.platform_data;
238
239 ret = pm80x_init(client, id);
240 if (ret) {
241 dev_err(&client->dev, "pm805_init fail!\n");
242 goto out_init;
243 }
244
245 chip = i2c_get_clientdata(client);
246
247 ret = device_805_init(chip);
248 if (ret) {
249 dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
250 goto err_805_init;
251 }
252
253 if (pdata->plat_config)
254 pdata->plat_config(chip, pdata);
255
256err_805_init:
257 pm80x_deinit(client);
258out_init:
259 return ret;
260}
261
262static int __devexit pm805_remove(struct i2c_client *client)
263{
264 struct pm80x_chip *chip = i2c_get_clientdata(client);
265
266 mfd_remove_devices(chip->dev);
267 device_irq_exit_805(chip);
268
269 pm80x_deinit(client);
270
271 return 0;
272}
273
274static struct i2c_driver pm805_driver = {
275 .driver = {
276 .name = "88PM80X",
277 .owner = THIS_MODULE,
278 .pm = &pm80x_pm_ops,
279 },
280 .probe = pm805_probe,
281 .remove = __devexit_p(pm805_remove),
282 .id_table = pm80x_id_table,
283};
284
285static int __init pm805_i2c_init(void)
286{
287 return i2c_add_driver(&pm805_driver);
288}
289subsys_initcall(pm805_i2c_init);
290
291static void __exit pm805_i2c_exit(void)
292{
293 i2c_del_driver(&pm805_driver);
294}
295module_exit(pm805_i2c_exit);
296
297MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805");
298MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
299MODULE_LICENSE("GPL");