diff options
Diffstat (limited to 'drivers/regulator/isl6271a-regulator.c')
-rw-r--r-- | drivers/regulator/isl6271a-regulator.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c new file mode 100644 index 000000000000..e49d2bd393f2 --- /dev/null +++ b/drivers/regulator/isl6271a-regulator.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * isl6271a-regulator.c | ||
3 | * | ||
4 | * Support for Intersil ISL6271A voltage regulator | ||
5 | * | ||
6 | * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation version 2. | ||
11 | * | ||
12 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, | ||
13 | * whether express or implied; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/regulator/driver.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/slab.h> | ||
27 | |||
28 | #define ISL6271A_VOLTAGE_MIN 850000 | ||
29 | #define ISL6271A_VOLTAGE_MAX 1600000 | ||
30 | #define ISL6271A_VOLTAGE_STEP 50000 | ||
31 | |||
32 | /* PMIC details */ | ||
33 | struct isl_pmic { | ||
34 | struct i2c_client *client; | ||
35 | struct regulator_dev *rdev[3]; | ||
36 | struct mutex mtx; | ||
37 | }; | ||
38 | |||
39 | static int isl6271a_get_voltage(struct regulator_dev *dev) | ||
40 | { | ||
41 | struct isl_pmic *pmic = rdev_get_drvdata(dev); | ||
42 | int idx, data; | ||
43 | |||
44 | mutex_lock(&pmic->mtx); | ||
45 | |||
46 | idx = i2c_smbus_read_byte(pmic->client); | ||
47 | if (idx < 0) { | ||
48 | dev_err(&pmic->client->dev, "Error getting voltage\n"); | ||
49 | data = idx; | ||
50 | goto out; | ||
51 | } | ||
52 | |||
53 | /* Convert the data from chip to microvolts */ | ||
54 | data = ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * (idx & 0xf)); | ||
55 | |||
56 | out: | ||
57 | mutex_unlock(&pmic->mtx); | ||
58 | return data; | ||
59 | } | ||
60 | |||
61 | static int isl6271a_set_voltage(struct regulator_dev *dev, int minuV, int maxuV) | ||
62 | { | ||
63 | struct isl_pmic *pmic = rdev_get_drvdata(dev); | ||
64 | int vsel, err, data; | ||
65 | |||
66 | if (minuV < ISL6271A_VOLTAGE_MIN || minuV > ISL6271A_VOLTAGE_MAX) | ||
67 | return -EINVAL; | ||
68 | if (maxuV < ISL6271A_VOLTAGE_MIN || maxuV > ISL6271A_VOLTAGE_MAX) | ||
69 | return -EINVAL; | ||
70 | |||
71 | /* Align to 50000 mV */ | ||
72 | vsel = minuV - (minuV % ISL6271A_VOLTAGE_STEP); | ||
73 | |||
74 | /* If the result fell out of [minuV,maxuV] range, put it back */ | ||
75 | if (vsel < minuV) | ||
76 | vsel += ISL6271A_VOLTAGE_STEP; | ||
77 | |||
78 | /* Convert the microvolts to data for the chip */ | ||
79 | data = (vsel - ISL6271A_VOLTAGE_MIN) / ISL6271A_VOLTAGE_STEP; | ||
80 | |||
81 | mutex_lock(&pmic->mtx); | ||
82 | |||
83 | err = i2c_smbus_write_byte(pmic->client, data); | ||
84 | if (err < 0) | ||
85 | dev_err(&pmic->client->dev, "Error setting voltage\n"); | ||
86 | |||
87 | mutex_unlock(&pmic->mtx); | ||
88 | return err; | ||
89 | } | ||
90 | |||
91 | static int isl6271a_list_voltage(struct regulator_dev *dev, unsigned selector) | ||
92 | { | ||
93 | return ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * selector); | ||
94 | } | ||
95 | |||
96 | static struct regulator_ops isl_core_ops = { | ||
97 | .get_voltage = isl6271a_get_voltage, | ||
98 | .set_voltage = isl6271a_set_voltage, | ||
99 | .list_voltage = isl6271a_list_voltage, | ||
100 | }; | ||
101 | |||
102 | static int isl6271a_get_fixed_voltage(struct regulator_dev *dev) | ||
103 | { | ||
104 | int id = rdev_get_id(dev); | ||
105 | return (id == 1) ? 1100000 : 1300000; | ||
106 | } | ||
107 | |||
108 | static int isl6271a_list_fixed_voltage(struct regulator_dev *dev, unsigned selector) | ||
109 | { | ||
110 | int id = rdev_get_id(dev); | ||
111 | return (id == 1) ? 1100000 : 1300000; | ||
112 | } | ||
113 | |||
114 | static struct regulator_ops isl_fixed_ops = { | ||
115 | .get_voltage = isl6271a_get_fixed_voltage, | ||
116 | .list_voltage = isl6271a_list_fixed_voltage, | ||
117 | }; | ||
118 | |||
119 | static struct regulator_desc isl_rd[] = { | ||
120 | { | ||
121 | .name = "Core Buck", | ||
122 | .id = 0, | ||
123 | .n_voltages = 16, | ||
124 | .ops = &isl_core_ops, | ||
125 | .type = REGULATOR_VOLTAGE, | ||
126 | .owner = THIS_MODULE, | ||
127 | }, { | ||
128 | .name = "LDO1", | ||
129 | .id = 1, | ||
130 | .n_voltages = 1, | ||
131 | .ops = &isl_fixed_ops, | ||
132 | .type = REGULATOR_VOLTAGE, | ||
133 | .owner = THIS_MODULE, | ||
134 | }, { | ||
135 | .name = "LDO2", | ||
136 | .id = 2, | ||
137 | .n_voltages = 1, | ||
138 | .ops = &isl_fixed_ops, | ||
139 | .type = REGULATOR_VOLTAGE, | ||
140 | .owner = THIS_MODULE, | ||
141 | }, | ||
142 | }; | ||
143 | |||
144 | static int __devinit isl6271a_probe(struct i2c_client *i2c, | ||
145 | const struct i2c_device_id *id) | ||
146 | { | ||
147 | struct regulator_init_data *init_data = i2c->dev.platform_data; | ||
148 | struct isl_pmic *pmic; | ||
149 | int err, i; | ||
150 | |||
151 | if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
152 | return -EIO; | ||
153 | |||
154 | if (!init_data) { | ||
155 | dev_err(&i2c->dev, "no platform data supplied\n"); | ||
156 | return -EIO; | ||
157 | } | ||
158 | |||
159 | pmic = kzalloc(sizeof(struct isl_pmic), GFP_KERNEL); | ||
160 | if (!pmic) | ||
161 | return -ENOMEM; | ||
162 | |||
163 | pmic->client = i2c; | ||
164 | |||
165 | mutex_init(&pmic->mtx); | ||
166 | |||
167 | for (i = 0; i < 3; i++) { | ||
168 | pmic->rdev[i] = regulator_register(&isl_rd[0], &i2c->dev, | ||
169 | init_data, pmic); | ||
170 | if (IS_ERR(pmic->rdev[i])) { | ||
171 | dev_err(&i2c->dev, "failed to register %s\n", id->name); | ||
172 | err = PTR_ERR(pmic->rdev); | ||
173 | goto error; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | i2c_set_clientdata(i2c, pmic); | ||
178 | |||
179 | return 0; | ||
180 | |||
181 | error: | ||
182 | while (--i >= 0) | ||
183 | regulator_unregister(pmic->rdev[i]); | ||
184 | |||
185 | kfree(pmic); | ||
186 | return err; | ||
187 | } | ||
188 | |||
189 | static int __devexit isl6271a_remove(struct i2c_client *i2c) | ||
190 | { | ||
191 | struct isl_pmic *pmic = i2c_get_clientdata(i2c); | ||
192 | int i; | ||
193 | |||
194 | i2c_set_clientdata(i2c, NULL); | ||
195 | |||
196 | for (i = 0; i < 3; i++) | ||
197 | regulator_unregister(pmic->rdev[i]); | ||
198 | |||
199 | kfree(pmic); | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static const struct i2c_device_id isl6271a_id[] = { | ||
205 | {.name = "isl6271a", 0 }, | ||
206 | { }, | ||
207 | }; | ||
208 | |||
209 | MODULE_DEVICE_TABLE(i2c, isl6271a_id); | ||
210 | |||
211 | static struct i2c_driver isl6271a_i2c_driver = { | ||
212 | .driver = { | ||
213 | .name = "isl6271a", | ||
214 | .owner = THIS_MODULE, | ||
215 | }, | ||
216 | .probe = isl6271a_probe, | ||
217 | .remove = __devexit_p(isl6271a_remove), | ||
218 | .id_table = isl6271a_id, | ||
219 | }; | ||
220 | |||
221 | static int __init isl6271a_init(void) | ||
222 | { | ||
223 | return i2c_add_driver(&isl6271a_i2c_driver); | ||
224 | } | ||
225 | |||
226 | static void __exit isl6271a_cleanup(void) | ||
227 | { | ||
228 | i2c_del_driver(&isl6271a_i2c_driver); | ||
229 | } | ||
230 | |||
231 | subsys_initcall(isl6271a_init); | ||
232 | module_exit(isl6271a_cleanup); | ||
233 | |||
234 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | ||
235 | MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver"); | ||
236 | MODULE_LICENSE("GPL v2"); | ||