Commit | Line | Data |
---|---|---|
51bd6943 MV |
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 | ||
3a93f2a9 MB |
61 | static int isl6271a_set_voltage(struct regulator_dev *dev, |
62 | int minuV, int maxuV, | |
63 | unsigned *selector) | |
51bd6943 MV |
64 | { |
65 | struct isl_pmic *pmic = rdev_get_drvdata(dev); | |
66 | int vsel, err, data; | |
67 | ||
68 | if (minuV < ISL6271A_VOLTAGE_MIN || minuV > ISL6271A_VOLTAGE_MAX) | |
69 | return -EINVAL; | |
70 | if (maxuV < ISL6271A_VOLTAGE_MIN || maxuV > ISL6271A_VOLTAGE_MAX) | |
71 | return -EINVAL; | |
72 | ||
73 | /* Align to 50000 mV */ | |
74 | vsel = minuV - (minuV % ISL6271A_VOLTAGE_STEP); | |
75 | ||
76 | /* If the result fell out of [minuV,maxuV] range, put it back */ | |
77 | if (vsel < minuV) | |
78 | vsel += ISL6271A_VOLTAGE_STEP; | |
79 | ||
80 | /* Convert the microvolts to data for the chip */ | |
81 | data = (vsel - ISL6271A_VOLTAGE_MIN) / ISL6271A_VOLTAGE_STEP; | |
82 | ||
3a93f2a9 MB |
83 | *selector = data; |
84 | ||
51bd6943 MV |
85 | mutex_lock(&pmic->mtx); |
86 | ||
87 | err = i2c_smbus_write_byte(pmic->client, data); | |
88 | if (err < 0) | |
89 | dev_err(&pmic->client->dev, "Error setting voltage\n"); | |
90 | ||
91 | mutex_unlock(&pmic->mtx); | |
92 | return err; | |
93 | } | |
94 | ||
95 | static int isl6271a_list_voltage(struct regulator_dev *dev, unsigned selector) | |
96 | { | |
97 | return ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * selector); | |
98 | } | |
99 | ||
100 | static struct regulator_ops isl_core_ops = { | |
101 | .get_voltage = isl6271a_get_voltage, | |
102 | .set_voltage = isl6271a_set_voltage, | |
103 | .list_voltage = isl6271a_list_voltage, | |
104 | }; | |
105 | ||
106 | static int isl6271a_get_fixed_voltage(struct regulator_dev *dev) | |
107 | { | |
108 | int id = rdev_get_id(dev); | |
109 | return (id == 1) ? 1100000 : 1300000; | |
110 | } | |
111 | ||
112 | static int isl6271a_list_fixed_voltage(struct regulator_dev *dev, unsigned selector) | |
113 | { | |
114 | int id = rdev_get_id(dev); | |
115 | return (id == 1) ? 1100000 : 1300000; | |
116 | } | |
117 | ||
118 | static struct regulator_ops isl_fixed_ops = { | |
119 | .get_voltage = isl6271a_get_fixed_voltage, | |
120 | .list_voltage = isl6271a_list_fixed_voltage, | |
121 | }; | |
122 | ||
123 | static struct regulator_desc isl_rd[] = { | |
124 | { | |
125 | .name = "Core Buck", | |
126 | .id = 0, | |
127 | .n_voltages = 16, | |
128 | .ops = &isl_core_ops, | |
129 | .type = REGULATOR_VOLTAGE, | |
130 | .owner = THIS_MODULE, | |
131 | }, { | |
132 | .name = "LDO1", | |
133 | .id = 1, | |
134 | .n_voltages = 1, | |
135 | .ops = &isl_fixed_ops, | |
136 | .type = REGULATOR_VOLTAGE, | |
137 | .owner = THIS_MODULE, | |
138 | }, { | |
139 | .name = "LDO2", | |
140 | .id = 2, | |
141 | .n_voltages = 1, | |
142 | .ops = &isl_fixed_ops, | |
143 | .type = REGULATOR_VOLTAGE, | |
144 | .owner = THIS_MODULE, | |
145 | }, | |
146 | }; | |
147 | ||
148 | static int __devinit isl6271a_probe(struct i2c_client *i2c, | |
149 | const struct i2c_device_id *id) | |
150 | { | |
151 | struct regulator_init_data *init_data = i2c->dev.platform_data; | |
152 | struct isl_pmic *pmic; | |
153 | int err, i; | |
154 | ||
155 | if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | |
156 | return -EIO; | |
157 | ||
158 | if (!init_data) { | |
159 | dev_err(&i2c->dev, "no platform data supplied\n"); | |
160 | return -EIO; | |
161 | } | |
162 | ||
163 | pmic = kzalloc(sizeof(struct isl_pmic), GFP_KERNEL); | |
164 | if (!pmic) | |
165 | return -ENOMEM; | |
166 | ||
167 | pmic->client = i2c; | |
168 | ||
169 | mutex_init(&pmic->mtx); | |
170 | ||
171 | for (i = 0; i < 3; i++) { | |
b9e5d11a | 172 | pmic->rdev[i] = regulator_register(&isl_rd[i], &i2c->dev, |
51bd6943 MV |
173 | init_data, pmic); |
174 | if (IS_ERR(pmic->rdev[i])) { | |
175 | dev_err(&i2c->dev, "failed to register %s\n", id->name); | |
fa63bd4a | 176 | err = PTR_ERR(pmic->rdev[i]); |
51bd6943 MV |
177 | goto error; |
178 | } | |
179 | } | |
180 | ||
181 | i2c_set_clientdata(i2c, pmic); | |
182 | ||
183 | return 0; | |
184 | ||
185 | error: | |
186 | while (--i >= 0) | |
187 | regulator_unregister(pmic->rdev[i]); | |
188 | ||
189 | kfree(pmic); | |
190 | return err; | |
191 | } | |
192 | ||
193 | static int __devexit isl6271a_remove(struct i2c_client *i2c) | |
194 | { | |
195 | struct isl_pmic *pmic = i2c_get_clientdata(i2c); | |
196 | int i; | |
197 | ||
51bd6943 MV |
198 | for (i = 0; i < 3; i++) |
199 | regulator_unregister(pmic->rdev[i]); | |
200 | ||
201 | kfree(pmic); | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
206 | static const struct i2c_device_id isl6271a_id[] = { | |
207 | {.name = "isl6271a", 0 }, | |
208 | { }, | |
209 | }; | |
210 | ||
211 | MODULE_DEVICE_TABLE(i2c, isl6271a_id); | |
212 | ||
213 | static struct i2c_driver isl6271a_i2c_driver = { | |
214 | .driver = { | |
215 | .name = "isl6271a", | |
216 | .owner = THIS_MODULE, | |
217 | }, | |
218 | .probe = isl6271a_probe, | |
219 | .remove = __devexit_p(isl6271a_remove), | |
220 | .id_table = isl6271a_id, | |
221 | }; | |
222 | ||
223 | static int __init isl6271a_init(void) | |
224 | { | |
225 | return i2c_add_driver(&isl6271a_i2c_driver); | |
226 | } | |
227 | ||
228 | static void __exit isl6271a_cleanup(void) | |
229 | { | |
230 | i2c_del_driver(&isl6271a_i2c_driver); | |
231 | } | |
232 | ||
233 | subsys_initcall(isl6271a_init); | |
234 | module_exit(isl6271a_cleanup); | |
235 | ||
236 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | |
237 | MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver"); | |
238 | MODULE_LICENSE("GPL v2"); |