Commit | Line | Data |
---|---|---|
4107da2a HZ |
1 | /* |
2 | * Base driver for Marvell 88PM8607 | |
3 | * | |
4 | * Copyright (C) 2009 Marvell International Ltd. | |
5 | * Haojian Zhuang <haojian.zhuang@marvell.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | ||
12 | #include <linux/kernel.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/i2c.h> | |
17 | #include <linux/mfd/core.h> | |
18 | #include <linux/mfd/88pm8607.h> | |
19 | ||
20 | ||
21 | #define PM8607_REG_RESOURCE(_start, _end) \ | |
22 | { \ | |
23 | .start = PM8607_##_start, \ | |
24 | .end = PM8607_##_end, \ | |
25 | .flags = IORESOURCE_IO, \ | |
26 | } | |
27 | ||
28 | static struct resource pm8607_regulator_resources[] = { | |
29 | PM8607_REG_RESOURCE(BUCK1, BUCK1), | |
30 | PM8607_REG_RESOURCE(BUCK2, BUCK2), | |
31 | PM8607_REG_RESOURCE(BUCK3, BUCK3), | |
32 | PM8607_REG_RESOURCE(LDO1, LDO1), | |
33 | PM8607_REG_RESOURCE(LDO2, LDO2), | |
34 | PM8607_REG_RESOURCE(LDO3, LDO3), | |
35 | PM8607_REG_RESOURCE(LDO4, LDO4), | |
36 | PM8607_REG_RESOURCE(LDO5, LDO5), | |
37 | PM8607_REG_RESOURCE(LDO6, LDO6), | |
38 | PM8607_REG_RESOURCE(LDO7, LDO7), | |
39 | PM8607_REG_RESOURCE(LDO8, LDO8), | |
40 | PM8607_REG_RESOURCE(LDO9, LDO9), | |
41 | PM8607_REG_RESOURCE(LDO10, LDO10), | |
42 | PM8607_REG_RESOURCE(LDO12, LDO12), | |
43 | PM8607_REG_RESOURCE(LDO14, LDO14), | |
44 | }; | |
45 | ||
46 | #define PM8607_REG_DEVS(_name, _id) \ | |
47 | { \ | |
48 | .name = "88pm8607-" #_name, \ | |
49 | .num_resources = 1, \ | |
50 | .resources = &pm8607_regulator_resources[PM8607_ID_##_id], \ | |
51 | } | |
52 | ||
53 | static struct mfd_cell pm8607_devs[] = { | |
54 | PM8607_REG_DEVS(buck1, BUCK1), | |
55 | PM8607_REG_DEVS(buck2, BUCK2), | |
56 | PM8607_REG_DEVS(buck3, BUCK3), | |
57 | PM8607_REG_DEVS(ldo1, LDO1), | |
58 | PM8607_REG_DEVS(ldo2, LDO2), | |
59 | PM8607_REG_DEVS(ldo3, LDO3), | |
60 | PM8607_REG_DEVS(ldo4, LDO4), | |
61 | PM8607_REG_DEVS(ldo5, LDO5), | |
62 | PM8607_REG_DEVS(ldo6, LDO6), | |
63 | PM8607_REG_DEVS(ldo7, LDO7), | |
64 | PM8607_REG_DEVS(ldo8, LDO8), | |
65 | PM8607_REG_DEVS(ldo9, LDO9), | |
66 | PM8607_REG_DEVS(ldo10, LDO10), | |
67 | PM8607_REG_DEVS(ldo12, LDO12), | |
68 | PM8607_REG_DEVS(ldo14, LDO14), | |
69 | }; | |
70 | ||
71 | static inline int pm8607_read_device(struct pm8607_chip *chip, | |
72 | int reg, int bytes, void *dest) | |
73 | { | |
74 | struct i2c_client *i2c = chip->client; | |
75 | unsigned char data; | |
76 | int ret; | |
77 | ||
78 | data = (unsigned char)reg; | |
79 | ret = i2c_master_send(i2c, &data, 1); | |
80 | if (ret < 0) | |
81 | return ret; | |
82 | ||
83 | ret = i2c_master_recv(i2c, dest, bytes); | |
84 | if (ret < 0) | |
85 | return ret; | |
86 | return 0; | |
87 | } | |
88 | ||
89 | static inline int pm8607_write_device(struct pm8607_chip *chip, | |
90 | int reg, int bytes, void *src) | |
91 | { | |
92 | struct i2c_client *i2c = chip->client; | |
93 | unsigned char buf[bytes + 1]; | |
94 | int ret; | |
95 | ||
96 | buf[0] = (unsigned char)reg; | |
97 | memcpy(&buf[1], src, bytes); | |
98 | ||
99 | ret = i2c_master_send(i2c, buf, bytes + 1); | |
100 | if (ret < 0) | |
101 | return ret; | |
102 | return 0; | |
103 | } | |
104 | ||
105 | int pm8607_reg_read(struct pm8607_chip *chip, int reg) | |
106 | { | |
107 | unsigned char data; | |
108 | int ret; | |
109 | ||
110 | mutex_lock(&chip->io_lock); | |
111 | ret = chip->read(chip, reg, 1, &data); | |
112 | mutex_unlock(&chip->io_lock); | |
113 | ||
114 | if (ret < 0) | |
115 | return ret; | |
116 | else | |
117 | return (int)data; | |
118 | } | |
119 | EXPORT_SYMBOL(pm8607_reg_read); | |
120 | ||
121 | int pm8607_reg_write(struct pm8607_chip *chip, int reg, | |
122 | unsigned char data) | |
123 | { | |
124 | int ret; | |
125 | ||
126 | mutex_lock(&chip->io_lock); | |
127 | ret = chip->write(chip, reg, 1, &data); | |
128 | mutex_unlock(&chip->io_lock); | |
129 | ||
130 | return ret; | |
131 | } | |
132 | EXPORT_SYMBOL(pm8607_reg_write); | |
133 | ||
134 | int pm8607_bulk_read(struct pm8607_chip *chip, int reg, | |
135 | int count, unsigned char *buf) | |
136 | { | |
137 | int ret; | |
138 | ||
139 | mutex_lock(&chip->io_lock); | |
140 | ret = chip->read(chip, reg, count, buf); | |
141 | mutex_unlock(&chip->io_lock); | |
142 | ||
143 | return ret; | |
144 | } | |
145 | EXPORT_SYMBOL(pm8607_bulk_read); | |
146 | ||
147 | int pm8607_bulk_write(struct pm8607_chip *chip, int reg, | |
148 | int count, unsigned char *buf) | |
149 | { | |
150 | int ret; | |
151 | ||
152 | mutex_lock(&chip->io_lock); | |
153 | ret = chip->write(chip, reg, count, buf); | |
154 | mutex_unlock(&chip->io_lock); | |
155 | ||
156 | return ret; | |
157 | } | |
158 | EXPORT_SYMBOL(pm8607_bulk_write); | |
159 | ||
160 | int pm8607_set_bits(struct pm8607_chip *chip, int reg, | |
161 | unsigned char mask, unsigned char data) | |
162 | { | |
163 | unsigned char value; | |
164 | int ret; | |
165 | ||
166 | mutex_lock(&chip->io_lock); | |
167 | ret = chip->read(chip, reg, 1, &value); | |
168 | if (ret < 0) | |
169 | goto out; | |
170 | value &= ~mask; | |
171 | value |= data; | |
172 | ret = chip->write(chip, reg, 1, &value); | |
173 | out: | |
174 | mutex_unlock(&chip->io_lock); | |
175 | return ret; | |
176 | } | |
177 | EXPORT_SYMBOL(pm8607_set_bits); | |
178 | ||
179 | ||
180 | static const struct i2c_device_id pm8607_id_table[] = { | |
181 | { "88PM8607", 0 }, | |
182 | {} | |
183 | }; | |
184 | MODULE_DEVICE_TABLE(i2c, pm8607_id_table); | |
185 | ||
186 | ||
187 | static int __devinit pm8607_probe(struct i2c_client *client, | |
188 | const struct i2c_device_id *id) | |
189 | { | |
190 | struct pm8607_platform_data *pdata = client->dev.platform_data; | |
191 | struct pm8607_chip *chip; | |
192 | int i, count; | |
193 | int ret; | |
194 | ||
195 | chip = kzalloc(sizeof(struct pm8607_chip), GFP_KERNEL); | |
196 | if (chip == NULL) | |
197 | return -ENOMEM; | |
198 | ||
199 | chip->client = client; | |
200 | chip->dev = &client->dev; | |
201 | chip->read = pm8607_read_device; | |
202 | chip->write = pm8607_write_device; | |
203 | i2c_set_clientdata(client, chip); | |
204 | ||
205 | mutex_init(&chip->io_lock); | |
206 | dev_set_drvdata(chip->dev, chip); | |
207 | ||
208 | ret = pm8607_reg_read(chip, PM8607_CHIP_ID); | |
209 | if (ret < 0) { | |
210 | dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); | |
211 | goto out; | |
212 | } | |
213 | if ((ret & CHIP_ID_MASK) == CHIP_ID) | |
214 | dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n", | |
215 | ret); | |
216 | else { | |
217 | dev_err(chip->dev, "Failed to detect Marvell 88PM8607. " | |
218 | "Chip ID: %02x\n", ret); | |
219 | goto out; | |
220 | } | |
221 | chip->chip_id = ret; | |
222 | ||
223 | ret = pm8607_reg_read(chip, PM8607_BUCK3); | |
224 | if (ret < 0) { | |
225 | dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret); | |
226 | goto out; | |
227 | } | |
228 | if (ret & PM8607_BUCK3_DOUBLE) | |
229 | chip->buck3_double = 1; | |
230 | ||
231 | ret = pm8607_reg_read(chip, PM8607_MISC1); | |
232 | if (ret < 0) { | |
233 | dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret); | |
234 | goto out; | |
235 | } | |
236 | if (pdata->i2c_port == PI2C_PORT) | |
237 | ret |= PM8607_MISC1_PI2C; | |
238 | else | |
239 | ret &= ~PM8607_MISC1_PI2C; | |
240 | ret = pm8607_reg_write(chip, PM8607_MISC1, ret); | |
241 | if (ret < 0) { | |
242 | dev_err(chip->dev, "Failed to write MISC1 register: %d\n", ret); | |
243 | goto out; | |
244 | } | |
245 | ||
246 | ||
247 | count = ARRAY_SIZE(pm8607_devs); | |
248 | for (i = 0; i < count; i++) { | |
249 | ret = mfd_add_devices(chip->dev, i, &pm8607_devs[i], | |
250 | 1, NULL, 0); | |
251 | if (ret != 0) { | |
252 | dev_err(chip->dev, "Failed to add subdevs\n"); | |
253 | goto out; | |
254 | } | |
255 | } | |
256 | ||
257 | return 0; | |
258 | ||
259 | out: | |
260 | i2c_set_clientdata(client, NULL); | |
261 | kfree(chip); | |
262 | return ret; | |
263 | } | |
264 | ||
265 | static int __devexit pm8607_remove(struct i2c_client *client) | |
266 | { | |
267 | struct pm8607_chip *chip = i2c_get_clientdata(client); | |
268 | ||
269 | mfd_remove_devices(chip->dev); | |
270 | kfree(chip); | |
271 | return 0; | |
272 | } | |
273 | ||
274 | static struct i2c_driver pm8607_driver = { | |
275 | .driver = { | |
276 | .name = "88PM8607", | |
277 | .owner = THIS_MODULE, | |
278 | }, | |
279 | .probe = pm8607_probe, | |
280 | .remove = __devexit_p(pm8607_remove), | |
281 | .id_table = pm8607_id_table, | |
282 | }; | |
283 | ||
284 | static int __init pm8607_init(void) | |
285 | { | |
286 | int ret; | |
287 | ret = i2c_add_driver(&pm8607_driver); | |
288 | if (ret != 0) | |
289 | pr_err("Failed to register 88PM8607 I2C driver: %d\n", ret); | |
290 | return ret; | |
291 | } | |
292 | subsys_initcall(pm8607_init); | |
293 | ||
294 | static void __exit pm8607_exit(void) | |
295 | { | |
296 | i2c_del_driver(&pm8607_driver); | |
297 | } | |
298 | module_exit(pm8607_exit); | |
299 | ||
300 | MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM8607"); | |
301 | MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); | |
302 | MODULE_LICENSE("GPL"); |