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