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 | ||
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++) { | |
b9e5d11a | 168 | pmic->rdev[i] = regulator_register(&isl_rd[i], &i2c->dev, |
51bd6943 MV |
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 | ||
51bd6943 MV |
194 | for (i = 0; i < 3; i++) |
195 | regulator_unregister(pmic->rdev[i]); | |
196 | ||
197 | kfree(pmic); | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
202 | static const struct i2c_device_id isl6271a_id[] = { | |
203 | {.name = "isl6271a", 0 }, | |
204 | { }, | |
205 | }; | |
206 | ||
207 | MODULE_DEVICE_TABLE(i2c, isl6271a_id); | |
208 | ||
209 | static struct i2c_driver isl6271a_i2c_driver = { | |
210 | .driver = { | |
211 | .name = "isl6271a", | |
212 | .owner = THIS_MODULE, | |
213 | }, | |
214 | .probe = isl6271a_probe, | |
215 | .remove = __devexit_p(isl6271a_remove), | |
216 | .id_table = isl6271a_id, | |
217 | }; | |
218 | ||
219 | static int __init isl6271a_init(void) | |
220 | { | |
221 | return i2c_add_driver(&isl6271a_i2c_driver); | |
222 | } | |
223 | ||
224 | static void __exit isl6271a_cleanup(void) | |
225 | { | |
226 | i2c_del_driver(&isl6271a_i2c_driver); | |
227 | } | |
228 | ||
229 | subsys_initcall(isl6271a_init); | |
230 | module_exit(isl6271a_cleanup); | |
231 | ||
232 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | |
233 | MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver"); | |
234 | MODULE_LICENSE("GPL v2"); |