diff options
author | Sonic Zhang <sonic.zhang@analog.com> | 2010-06-03 23:46:04 -0400 |
---|---|---|
committer | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-08-11 06:38:00 -0400 |
commit | 8b385d9b97a63ba621342858f9921324032a9167 (patch) | |
tree | 669c8d0048765f01fc09238a1b80955d65409221 /drivers/regulator/ad5398.c | |
parent | 3d30701b58970425e1d45994d6cb82f828924fdd (diff) |
regulator: new drivers for AD5398 and AD5821
The AD5398 and AD5821 are single 10-bit DAC with 120 mA output current
sink capability. They feature an internal reference and operates from
a single 2.7 V to 5.5 V supply.
This driver supports both the AD5398 and the AD5821. It adapts into the
voltage and current framework.
Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'drivers/regulator/ad5398.c')
-rw-r--r-- | drivers/regulator/ad5398.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c new file mode 100644 index 00000000000..6de6bdaeff7 --- /dev/null +++ b/drivers/regulator/ad5398.c | |||
@@ -0,0 +1,288 @@ | |||
1 | /* | ||
2 | * Voltage and current regulation for AD5398 and AD5821 | ||
3 | * | ||
4 | * Copyright 2010 Analog Devices Inc. | ||
5 | * | ||
6 | * Enter bugs at http://blackfin.uclinux.org/ | ||
7 | * | ||
8 | * Licensed under the GPL-2 or later. | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/err.h> | ||
13 | #include <linux/i2c.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/regulator/driver.h> | ||
17 | #include <linux/regulator/machine.h> | ||
18 | |||
19 | #define AD5398_CURRENT_EN_MASK 0x8000 | ||
20 | |||
21 | struct ad5398_chip_info { | ||
22 | struct i2c_client *client; | ||
23 | int min_uA; | ||
24 | int max_uA; | ||
25 | unsigned int current_level; | ||
26 | unsigned int current_mask; | ||
27 | unsigned int current_offset; | ||
28 | struct regulator_dev rdev; | ||
29 | }; | ||
30 | |||
31 | static int ad5398_calc_current(struct ad5398_chip_info *chip, | ||
32 | unsigned selector) | ||
33 | { | ||
34 | unsigned range_uA = chip->max_uA - chip->min_uA; | ||
35 | |||
36 | return chip->min_uA + (selector * range_uA / chip->current_level); | ||
37 | } | ||
38 | |||
39 | static int ad5398_read_reg(struct i2c_client *client, unsigned short *data) | ||
40 | { | ||
41 | unsigned short val; | ||
42 | int ret; | ||
43 | |||
44 | ret = i2c_master_recv(client, (char *)&val, 2); | ||
45 | if (ret < 0) { | ||
46 | dev_err(&client->dev, "I2C read error\n"); | ||
47 | return ret; | ||
48 | } | ||
49 | *data = be16_to_cpu(val); | ||
50 | |||
51 | return ret; | ||
52 | } | ||
53 | |||
54 | static int ad5398_write_reg(struct i2c_client *client, const unsigned short data) | ||
55 | { | ||
56 | unsigned short val; | ||
57 | int ret; | ||
58 | |||
59 | val = cpu_to_be16(data); | ||
60 | ret = i2c_master_send(client, (char *)&val, 2); | ||
61 | if (ret < 0) | ||
62 | dev_err(&client->dev, "I2C write error\n"); | ||
63 | |||
64 | return ret; | ||
65 | } | ||
66 | |||
67 | static int ad5398_get_current_limit(struct regulator_dev *rdev) | ||
68 | { | ||
69 | struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); | ||
70 | struct i2c_client *client = chip->client; | ||
71 | unsigned short data; | ||
72 | int ret; | ||
73 | |||
74 | ret = ad5398_read_reg(client, &data); | ||
75 | if (ret < 0) | ||
76 | return ret; | ||
77 | |||
78 | ret = (data & chip->current_mask) >> chip->current_offset; | ||
79 | |||
80 | return ad5398_calc_current(chip, ret); | ||
81 | } | ||
82 | |||
83 | static int ad5398_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) | ||
84 | { | ||
85 | struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); | ||
86 | struct i2c_client *client = chip->client; | ||
87 | unsigned range_uA = chip->max_uA - chip->min_uA; | ||
88 | unsigned selector; | ||
89 | unsigned short data; | ||
90 | int ret; | ||
91 | |||
92 | if (min_uA > chip->max_uA || min_uA < chip->min_uA) | ||
93 | return -EINVAL; | ||
94 | if (max_uA > chip->max_uA || max_uA < chip->min_uA) | ||
95 | return -EINVAL; | ||
96 | |||
97 | selector = ((min_uA - chip->min_uA) * chip->current_level + | ||
98 | range_uA - 1) / range_uA; | ||
99 | if (ad5398_calc_current(chip, selector) > max_uA) | ||
100 | return -EINVAL; | ||
101 | |||
102 | dev_dbg(&client->dev, "changing current %dmA\n", | ||
103 | ad5398_calc_current(chip, selector) / 1000); | ||
104 | |||
105 | /* read chip enable bit */ | ||
106 | ret = ad5398_read_reg(client, &data); | ||
107 | if (ret < 0) | ||
108 | return ret; | ||
109 | |||
110 | /* prepare register data */ | ||
111 | selector = (selector << chip->current_offset) & chip->current_mask; | ||
112 | data = (unsigned short)selector | (data & AD5398_CURRENT_EN_MASK); | ||
113 | |||
114 | /* write the new current value back as well as enable bit */ | ||
115 | ret = ad5398_write_reg(client, data); | ||
116 | |||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | static int ad5398_is_enabled(struct regulator_dev *rdev) | ||
121 | { | ||
122 | struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); | ||
123 | struct i2c_client *client = chip->client; | ||
124 | unsigned short data; | ||
125 | int ret; | ||
126 | |||
127 | ret = ad5398_read_reg(client, &data); | ||
128 | if (ret < 0) | ||
129 | return ret; | ||
130 | |||
131 | if (data & AD5398_CURRENT_EN_MASK) | ||
132 | return 1; | ||
133 | else | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static int ad5398_enable(struct regulator_dev *rdev) | ||
138 | { | ||
139 | struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); | ||
140 | struct i2c_client *client = chip->client; | ||
141 | unsigned short data; | ||
142 | int ret; | ||
143 | |||
144 | ret = ad5398_read_reg(client, &data); | ||
145 | if (ret < 0) | ||
146 | return ret; | ||
147 | |||
148 | if (data & AD5398_CURRENT_EN_MASK) | ||
149 | return 0; | ||
150 | |||
151 | data |= AD5398_CURRENT_EN_MASK; | ||
152 | |||
153 | ret = ad5398_write_reg(client, data); | ||
154 | |||
155 | return ret; | ||
156 | } | ||
157 | |||
158 | static int ad5398_disable(struct regulator_dev *rdev) | ||
159 | { | ||
160 | struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); | ||
161 | struct i2c_client *client = chip->client; | ||
162 | unsigned short data; | ||
163 | int ret; | ||
164 | |||
165 | ret = ad5398_read_reg(client, &data); | ||
166 | if (ret < 0) | ||
167 | return ret; | ||
168 | |||
169 | if (!(data & AD5398_CURRENT_EN_MASK)) | ||
170 | return 0; | ||
171 | |||
172 | data &= ~AD5398_CURRENT_EN_MASK; | ||
173 | |||
174 | ret = ad5398_write_reg(client, data); | ||
175 | |||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | static struct regulator_ops ad5398_ops = { | ||
180 | .get_current_limit = ad5398_get_current_limit, | ||
181 | .set_current_limit = ad5398_set_current_limit, | ||
182 | .enable = ad5398_enable, | ||
183 | .disable = ad5398_disable, | ||
184 | .is_enabled = ad5398_is_enabled, | ||
185 | }; | ||
186 | |||
187 | static struct regulator_desc ad5398_reg = { | ||
188 | .name = "isink", | ||
189 | .id = 0, | ||
190 | .ops = &ad5398_ops, | ||
191 | .type = REGULATOR_CURRENT, | ||
192 | .owner = THIS_MODULE, | ||
193 | }; | ||
194 | |||
195 | struct ad5398_current_data_format { | ||
196 | int current_bits; | ||
197 | int current_offset; | ||
198 | int min_uA; | ||
199 | int max_uA; | ||
200 | }; | ||
201 | |||
202 | static const struct ad5398_current_data_format df_10_4_120 = {10, 4, 0, 120000}; | ||
203 | |||
204 | static const struct i2c_device_id ad5398_id[] = { | ||
205 | { "ad5398", (kernel_ulong_t)&df_10_4_120 }, | ||
206 | { "ad5821", (kernel_ulong_t)&df_10_4_120 }, | ||
207 | { } | ||
208 | }; | ||
209 | MODULE_DEVICE_TABLE(i2c, ad5398_id); | ||
210 | |||
211 | static int __devinit ad5398_probe(struct i2c_client *client, | ||
212 | const struct i2c_device_id *id) | ||
213 | { | ||
214 | struct regulator_dev *rdev; | ||
215 | struct regulator_init_data *init_data = client->dev.platform_data; | ||
216 | struct ad5398_chip_info *chip; | ||
217 | const struct ad5398_current_data_format *df = | ||
218 | (struct ad5398_current_data_format *)id->driver_data; | ||
219 | int ret; | ||
220 | |||
221 | if (!init_data) | ||
222 | return -EINVAL; | ||
223 | |||
224 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | ||
225 | if (!chip) | ||
226 | return -ENOMEM; | ||
227 | |||
228 | chip->client = client; | ||
229 | |||
230 | chip->min_uA = df->min_uA; | ||
231 | chip->max_uA = df->max_uA; | ||
232 | chip->current_level = 1 << df->current_bits; | ||
233 | chip->current_offset = df->current_offset; | ||
234 | chip->current_mask = (chip->current_level - 1) << chip->current_offset; | ||
235 | |||
236 | rdev = regulator_register(&ad5398_reg, &client->dev, init_data, chip); | ||
237 | if (IS_ERR(rdev)) { | ||
238 | ret = PTR_ERR(rdev); | ||
239 | dev_err(&client->dev, "failed to register %s %s\n", | ||
240 | id->name, ad5398_reg.name); | ||
241 | goto err; | ||
242 | } | ||
243 | |||
244 | i2c_set_clientdata(client, chip); | ||
245 | dev_dbg(&client->dev, "%s regulator driver is registered.\n", id->name); | ||
246 | return 0; | ||
247 | |||
248 | err: | ||
249 | kfree(chip); | ||
250 | return ret; | ||
251 | } | ||
252 | |||
253 | static int __devexit ad5398_remove(struct i2c_client *client) | ||
254 | { | ||
255 | struct ad5398_chip_info *chip = i2c_get_clientdata(client); | ||
256 | |||
257 | regulator_unregister(&chip->rdev); | ||
258 | kfree(chip); | ||
259 | i2c_set_clientdata(client, NULL); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static struct i2c_driver ad5398_driver = { | ||
265 | .probe = ad5398_probe, | ||
266 | .remove = __devexit_p(ad5398_remove), | ||
267 | .driver = { | ||
268 | .name = "ad5398", | ||
269 | }, | ||
270 | .id_table = ad5398_id, | ||
271 | }; | ||
272 | |||
273 | static int __init ad5398_init(void) | ||
274 | { | ||
275 | return i2c_add_driver(&ad5398_driver); | ||
276 | } | ||
277 | module_init(ad5398_init); | ||
278 | |||
279 | static void __exit ad5398_exit(void) | ||
280 | { | ||
281 | i2c_del_driver(&ad5398_driver); | ||
282 | } | ||
283 | module_exit(ad5398_exit); | ||
284 | |||
285 | MODULE_DESCRIPTION("AD5398 and AD5821 current regulator driver"); | ||
286 | MODULE_AUTHOR("Sonic Zhang"); | ||
287 | MODULE_LICENSE("GPL"); | ||
288 | MODULE_ALIAS("i2c:ad5398-regulator"); | ||