]>
Commit | Line | Data |
---|---|---|
3a8d1a73 MK |
1 | /* |
2 | * TI LM363X Regulator Driver | |
3 | * | |
4 | * Copyright 2015 Texas Instruments | |
5 | * | |
6 | * Author: Milo Kim <milo.kim@ti.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/err.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/mfd/ti-lmu.h> | |
16 | #include <linux/mfd/ti-lmu-register.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/of_gpio.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/regulator/driver.h> | |
22 | #include <linux/regulator/of_regulator.h> | |
23 | #include <linux/slab.h> | |
24 | ||
25 | /* LM3631 */ | |
26 | #define LM3631_BOOST_VSEL_MAX 0x25 | |
27 | #define LM3631_LDO_VSEL_MAX 0x28 | |
28 | #define LM3631_CONT_VSEL_MAX 0x03 | |
29 | #define LM3631_VBOOST_MIN 4500000 | |
30 | #define LM3631_VCONT_MIN 1800000 | |
31 | #define LM3631_VLDO_MIN 4000000 | |
32 | #define ENABLE_TIME_USEC 1000 | |
33 | ||
34 | /* LM3632 */ | |
35 | #define LM3632_BOOST_VSEL_MAX 0x26 | |
325a6fb3 | 36 | #define LM3632_LDO_VSEL_MAX 0x28 |
3a8d1a73 MK |
37 | #define LM3632_VBOOST_MIN 4500000 |
38 | #define LM3632_VLDO_MIN 4000000 | |
39 | ||
40 | /* Common */ | |
41 | #define LM363X_STEP_50mV 50000 | |
42 | #define LM363X_STEP_500mV 500000 | |
43 | ||
faa5cf3a | 44 | static const int ldo_cont_enable_time[] = { |
3a8d1a73 MK |
45 | 0, 2000, 5000, 10000, 20000, 50000, 100000, 200000, |
46 | }; | |
47 | ||
48 | static int lm363x_regulator_enable_time(struct regulator_dev *rdev) | |
49 | { | |
3a8d1a73 MK |
50 | enum lm363x_regulator_id id = rdev_get_id(rdev); |
51 | u8 val, addr, mask; | |
52 | ||
53 | switch (id) { | |
54 | case LM3631_LDO_CONT: | |
55 | addr = LM3631_REG_ENTIME_VCONT; | |
56 | mask = LM3631_ENTIME_CONT_MASK; | |
57 | break; | |
58 | case LM3631_LDO_OREF: | |
59 | addr = LM3631_REG_ENTIME_VOREF; | |
60 | mask = LM3631_ENTIME_MASK; | |
61 | break; | |
62 | case LM3631_LDO_POS: | |
63 | addr = LM3631_REG_ENTIME_VPOS; | |
64 | mask = LM3631_ENTIME_MASK; | |
65 | break; | |
66 | case LM3631_LDO_NEG: | |
67 | addr = LM3631_REG_ENTIME_VNEG; | |
68 | mask = LM3631_ENTIME_MASK; | |
69 | break; | |
70 | default: | |
71 | return 0; | |
72 | } | |
73 | ||
a8bc6ca0 | 74 | if (regmap_read(rdev->regmap, addr, (unsigned int *)&val)) |
3a8d1a73 MK |
75 | return -EINVAL; |
76 | ||
77 | val = (val & mask) >> LM3631_ENTIME_SHIFT; | |
78 | ||
79 | if (id == LM3631_LDO_CONT) | |
80 | return ldo_cont_enable_time[val]; | |
81 | else | |
82 | return ENABLE_TIME_USEC * val; | |
83 | } | |
84 | ||
85 | static struct regulator_ops lm363x_boost_voltage_table_ops = { | |
86 | .list_voltage = regulator_list_voltage_linear, | |
87 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
88 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
89 | }; | |
90 | ||
91 | static struct regulator_ops lm363x_regulator_voltage_table_ops = { | |
92 | .list_voltage = regulator_list_voltage_linear, | |
93 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
94 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
95 | .enable = regulator_enable_regmap, | |
96 | .disable = regulator_disable_regmap, | |
97 | .is_enabled = regulator_is_enabled_regmap, | |
98 | .enable_time = lm363x_regulator_enable_time, | |
99 | }; | |
100 | ||
101 | static const struct regulator_desc lm363x_regulator_desc[] = { | |
102 | /* LM3631 */ | |
103 | { | |
104 | .name = "vboost", | |
105 | .of_match = "vboost", | |
106 | .id = LM3631_BOOST, | |
107 | .ops = &lm363x_boost_voltage_table_ops, | |
108 | .n_voltages = LM3631_BOOST_VSEL_MAX + 1, | |
109 | .min_uV = LM3631_VBOOST_MIN, | |
110 | .uV_step = LM363X_STEP_50mV, | |
111 | .type = REGULATOR_VOLTAGE, | |
112 | .owner = THIS_MODULE, | |
113 | .vsel_reg = LM3631_REG_VOUT_BOOST, | |
114 | .vsel_mask = LM3631_VOUT_MASK, | |
115 | }, | |
116 | { | |
117 | .name = "ldo_cont", | |
118 | .of_match = "vcont", | |
119 | .id = LM3631_LDO_CONT, | |
120 | .ops = &lm363x_regulator_voltage_table_ops, | |
121 | .n_voltages = LM3631_CONT_VSEL_MAX + 1, | |
122 | .min_uV = LM3631_VCONT_MIN, | |
123 | .uV_step = LM363X_STEP_500mV, | |
124 | .type = REGULATOR_VOLTAGE, | |
125 | .owner = THIS_MODULE, | |
126 | .vsel_reg = LM3631_REG_VOUT_CONT, | |
127 | .vsel_mask = LM3631_VOUT_CONT_MASK, | |
128 | .enable_reg = LM3631_REG_LDO_CTRL2, | |
129 | .enable_mask = LM3631_EN_CONT_MASK, | |
130 | }, | |
131 | { | |
132 | .name = "ldo_oref", | |
133 | .of_match = "voref", | |
134 | .id = LM3631_LDO_OREF, | |
135 | .ops = &lm363x_regulator_voltage_table_ops, | |
136 | .n_voltages = LM3631_LDO_VSEL_MAX + 1, | |
137 | .min_uV = LM3631_VLDO_MIN, | |
138 | .uV_step = LM363X_STEP_50mV, | |
139 | .type = REGULATOR_VOLTAGE, | |
140 | .owner = THIS_MODULE, | |
141 | .vsel_reg = LM3631_REG_VOUT_OREF, | |
142 | .vsel_mask = LM3631_VOUT_MASK, | |
143 | .enable_reg = LM3631_REG_LDO_CTRL1, | |
144 | .enable_mask = LM3631_EN_OREF_MASK, | |
145 | }, | |
146 | { | |
147 | .name = "ldo_vpos", | |
148 | .of_match = "vpos", | |
149 | .id = LM3631_LDO_POS, | |
150 | .ops = &lm363x_regulator_voltage_table_ops, | |
151 | .n_voltages = LM3631_LDO_VSEL_MAX + 1, | |
152 | .min_uV = LM3631_VLDO_MIN, | |
153 | .uV_step = LM363X_STEP_50mV, | |
154 | .type = REGULATOR_VOLTAGE, | |
155 | .owner = THIS_MODULE, | |
156 | .vsel_reg = LM3631_REG_VOUT_POS, | |
157 | .vsel_mask = LM3631_VOUT_MASK, | |
158 | .enable_reg = LM3631_REG_LDO_CTRL1, | |
159 | .enable_mask = LM3631_EN_VPOS_MASK, | |
160 | }, | |
161 | { | |
162 | .name = "ldo_vneg", | |
163 | .of_match = "vneg", | |
164 | .id = LM3631_LDO_NEG, | |
165 | .ops = &lm363x_regulator_voltage_table_ops, | |
166 | .n_voltages = LM3631_LDO_VSEL_MAX + 1, | |
167 | .min_uV = LM3631_VLDO_MIN, | |
168 | .uV_step = LM363X_STEP_50mV, | |
169 | .type = REGULATOR_VOLTAGE, | |
170 | .owner = THIS_MODULE, | |
171 | .vsel_reg = LM3631_REG_VOUT_NEG, | |
172 | .vsel_mask = LM3631_VOUT_MASK, | |
173 | .enable_reg = LM3631_REG_LDO_CTRL1, | |
174 | .enable_mask = LM3631_EN_VNEG_MASK, | |
175 | }, | |
176 | /* LM3632 */ | |
177 | { | |
178 | .name = "vboost", | |
179 | .of_match = "vboost", | |
180 | .id = LM3632_BOOST, | |
181 | .ops = &lm363x_boost_voltage_table_ops, | |
182 | .n_voltages = LM3632_BOOST_VSEL_MAX + 1, | |
183 | .min_uV = LM3632_VBOOST_MIN, | |
184 | .uV_step = LM363X_STEP_50mV, | |
185 | .type = REGULATOR_VOLTAGE, | |
186 | .owner = THIS_MODULE, | |
187 | .vsel_reg = LM3632_REG_VOUT_BOOST, | |
188 | .vsel_mask = LM3632_VOUT_MASK, | |
189 | }, | |
190 | { | |
191 | .name = "ldo_vpos", | |
192 | .of_match = "vpos", | |
193 | .id = LM3632_LDO_POS, | |
194 | .ops = &lm363x_regulator_voltage_table_ops, | |
195 | .n_voltages = LM3632_LDO_VSEL_MAX + 1, | |
196 | .min_uV = LM3632_VLDO_MIN, | |
197 | .uV_step = LM363X_STEP_50mV, | |
198 | .type = REGULATOR_VOLTAGE, | |
199 | .owner = THIS_MODULE, | |
200 | .vsel_reg = LM3632_REG_VOUT_POS, | |
201 | .vsel_mask = LM3632_VOUT_MASK, | |
202 | .enable_reg = LM3632_REG_BIAS_CONFIG, | |
203 | .enable_mask = LM3632_EN_VPOS_MASK, | |
204 | }, | |
205 | { | |
206 | .name = "ldo_vneg", | |
207 | .of_match = "vneg", | |
208 | .id = LM3632_LDO_NEG, | |
209 | .ops = &lm363x_regulator_voltage_table_ops, | |
210 | .n_voltages = LM3632_LDO_VSEL_MAX + 1, | |
211 | .min_uV = LM3632_VLDO_MIN, | |
212 | .uV_step = LM363X_STEP_50mV, | |
213 | .type = REGULATOR_VOLTAGE, | |
214 | .owner = THIS_MODULE, | |
215 | .vsel_reg = LM3632_REG_VOUT_NEG, | |
216 | .vsel_mask = LM3632_VOUT_MASK, | |
217 | .enable_reg = LM3632_REG_BIAS_CONFIG, | |
218 | .enable_mask = LM3632_EN_VNEG_MASK, | |
219 | }, | |
220 | }; | |
221 | ||
222 | static int lm363x_regulator_of_get_enable_gpio(struct device_node *np, int id) | |
223 | { | |
224 | /* | |
225 | * Check LCM_EN1/2_GPIO is configured. | |
226 | * Those pins are used for enabling VPOS/VNEG LDOs. | |
227 | */ | |
228 | switch (id) { | |
229 | case LM3632_LDO_POS: | |
9503b508 | 230 | return of_get_named_gpio(np, "enable-gpios", 0); |
3a8d1a73 | 231 | case LM3632_LDO_NEG: |
9503b508 | 232 | return of_get_named_gpio(np, "enable-gpios", 1); |
3a8d1a73 MK |
233 | default: |
234 | return -EINVAL; | |
235 | } | |
236 | } | |
237 | ||
238 | static int lm363x_regulator_probe(struct platform_device *pdev) | |
239 | { | |
240 | struct ti_lmu *lmu = dev_get_drvdata(pdev->dev.parent); | |
3a8d1a73 MK |
241 | struct regmap *regmap = lmu->regmap; |
242 | struct regulator_config cfg = { }; | |
243 | struct regulator_dev *rdev; | |
244 | struct device *dev = &pdev->dev; | |
245 | int id = pdev->id; | |
246 | int ret, ena_gpio; | |
247 | ||
3a8d1a73 | 248 | cfg.dev = dev; |
3a8d1a73 MK |
249 | cfg.regmap = regmap; |
250 | ||
251 | /* | |
252 | * LM3632 LDOs can be controlled by external pin. | |
253 | * Register update is required if the pin is used. | |
254 | */ | |
255 | ena_gpio = lm363x_regulator_of_get_enable_gpio(dev->of_node, id); | |
256 | if (gpio_is_valid(ena_gpio)) { | |
257 | cfg.ena_gpio = ena_gpio; | |
258 | cfg.ena_gpio_flags = GPIOF_OUT_INIT_LOW; | |
259 | ||
260 | ret = regmap_update_bits(regmap, LM3632_REG_BIAS_CONFIG, | |
261 | LM3632_EXT_EN_MASK, | |
262 | LM3632_EXT_EN_MASK); | |
263 | if (ret) { | |
264 | dev_err(dev, "External pin err: %d\n", ret); | |
265 | return ret; | |
266 | } | |
267 | } | |
268 | ||
269 | rdev = devm_regulator_register(dev, &lm363x_regulator_desc[id], &cfg); | |
270 | if (IS_ERR(rdev)) { | |
271 | ret = PTR_ERR(rdev); | |
272 | dev_err(dev, "[%d] regulator register err: %d\n", id, ret); | |
273 | return ret; | |
274 | } | |
275 | ||
3a8d1a73 MK |
276 | return 0; |
277 | } | |
278 | ||
279 | static struct platform_driver lm363x_regulator_driver = { | |
280 | .probe = lm363x_regulator_probe, | |
281 | .driver = { | |
282 | .name = "lm363x-regulator", | |
283 | }, | |
284 | }; | |
285 | ||
286 | module_platform_driver(lm363x_regulator_driver); | |
287 | ||
288 | MODULE_DESCRIPTION("TI LM363X Regulator Driver"); | |
289 | MODULE_AUTHOR("Milo Kim"); | |
290 | MODULE_LICENSE("GPL v2"); | |
291 | MODULE_ALIAS("platform:lm363x-regulator"); |