Commit | Line | Data |
---|---|---|
8171c93b CH |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | ||
3 | #include <linux/bits.h> | |
4 | #include <linux/gpio/consumer.h> | |
5 | #include <linux/interrupt.h> | |
6 | #include <linux/kernel.h> | |
46ae6fb5 | 7 | #include <linux/mod_devicetable.h> |
8171c93b CH |
8 | #include <linux/module.h> |
9 | #include <linux/of.h> | |
10 | #include <linux/platform_device.h> | |
11 | #include <linux/regmap.h> | |
12 | #include <linux/regulator/driver.h> | |
13 | #include <linux/regulator/machine.h> | |
14 | ||
15 | enum { | |
16 | MT6370_IDX_DSVBOOST = 0, | |
17 | MT6370_IDX_DSVPOS, | |
18 | MT6370_IDX_DSVNEG, | |
19 | MT6370_IDX_VIBLDO, | |
20 | MT6370_MAX_IDX | |
21 | }; | |
22 | ||
23 | #define MT6370_REG_LDO_CFG 0x180 | |
24 | #define MT6370_REG_LDO_VOUT 0x181 | |
25 | #define MT6370_REG_DB_CTRL1 0x1B0 | |
26 | #define MT6370_REG_DB_CTRL2 0x1B1 | |
27 | #define MT6370_REG_DB_VBST 0x1B2 | |
28 | #define MT6370_REG_DB_VPOS 0x1B3 | |
29 | #define MT6370_REG_DB_VNEG 0x1B4 | |
30 | #define MT6370_REG_LDO_STAT 0x1DC | |
31 | #define MT6370_REG_DB_STAT 0x1DF | |
32 | ||
33 | #define MT6370_LDOOMS_MASK BIT(7) | |
34 | #define MT6370_LDOEN_MASK BIT(7) | |
35 | #define MT6370_LDOVOUT_MASK GENMASK(3, 0) | |
36 | #define MT6370_DBPERD_MASK (BIT(7) | BIT(4)) | |
37 | #define MT6370_DBEXTEN_MASK BIT(0) | |
38 | #define MT6370_DBVPOSEN_MASK BIT(6) | |
39 | #define MT6370_DBVPOSDISG_MASK BIT(5) | |
40 | #define MT6370_DBVNEGEN_MASK BIT(3) | |
41 | #define MT6370_DBVNEGDISG_MASK BIT(2) | |
42 | #define MT6370_DBALLON_MASK (MT6370_DBVPOSEN_MASK | MT6370_DBVNEGEN_MASK) | |
43 | #define MT6370_DBSLEW_MASK GENMASK(7, 6) | |
44 | #define MT6370_DBVOUT_MASK GENMASK(5, 0) | |
45 | #define MT6370_LDOOC_EVT_MASK BIT(7) | |
46 | #define MT6370_POSSCP_EVT_MASK BIT(7) | |
47 | #define MT6370_NEGSCP_EVT_MASK BIT(6) | |
48 | #define MT6370_BSTOCP_EVT_MASK BIT(5) | |
49 | #define MT6370_POSOCP_EVT_MASK BIT(4) | |
50 | #define MT6370_NEGOCP_EVT_MASK BIT(3) | |
51 | ||
52 | #define MT6370_LDO_MINUV 1600000 | |
53 | #define MT6370_LDO_STPUV 200000 | |
54 | #define MT6370_LDO_N_VOLT 13 | |
55 | #define MT6370_DBVBOOST_MINUV 4000000 | |
56 | #define MT6370_DBVBOOST_STPUV 50000 | |
57 | #define MT6370_DBVBOOST_N_VOLT 45 | |
58 | #define MT6370_DBVOUT_MINUV 4000000 | |
59 | #define MT6370_DBVOUT_STPUV 50000 | |
60 | #define MT6370_DBVOUT_N_VOLT 41 | |
61 | ||
62 | struct mt6370_priv { | |
63 | struct device *dev; | |
64 | struct regmap *regmap; | |
65 | struct regulator_dev *rdev[MT6370_MAX_IDX]; | |
66 | bool use_external_ctrl; | |
67 | }; | |
68 | ||
69 | static const unsigned int mt6370_vpos_ramp_tbl[] = { 8540, 5840, 4830, 3000 }; | |
70 | static const unsigned int mt6370_vneg_ramp_tbl[] = { 10090, 6310, 5050, 3150 }; | |
71 | ||
72 | static int mt6370_get_error_flags(struct regulator_dev *rdev, | |
73 | unsigned int *flags) | |
74 | { | |
75 | struct regmap *regmap = rdev_get_regmap(rdev); | |
76 | unsigned int stat_reg, stat, rpt_flags = 0; | |
77 | int rid = rdev_get_id(rdev), ret; | |
78 | ||
79 | if (rid == MT6370_IDX_VIBLDO) | |
80 | stat_reg = MT6370_REG_LDO_STAT; | |
81 | else | |
82 | stat_reg = MT6370_REG_DB_STAT; | |
83 | ||
84 | ret = regmap_read(regmap, stat_reg, &stat); | |
85 | if (ret) | |
86 | return ret; | |
87 | ||
88 | switch (rid) { | |
89 | case MT6370_IDX_DSVBOOST: | |
90 | if (stat & MT6370_BSTOCP_EVT_MASK) | |
91 | rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; | |
92 | break; | |
93 | case MT6370_IDX_DSVPOS: | |
94 | if (stat & MT6370_POSSCP_EVT_MASK) | |
95 | rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE; | |
96 | ||
97 | if (stat & MT6370_POSOCP_EVT_MASK) | |
98 | rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; | |
99 | break; | |
100 | case MT6370_IDX_DSVNEG: | |
101 | if (stat & MT6370_NEGSCP_EVT_MASK) | |
102 | rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE; | |
103 | ||
104 | if (stat & MT6370_NEGOCP_EVT_MASK) | |
105 | rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; | |
106 | break; | |
107 | default: | |
108 | if (stat & MT6370_LDOOC_EVT_MASK) | |
109 | rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; | |
110 | break; | |
111 | } | |
112 | ||
113 | *flags = rpt_flags; | |
114 | return 0; | |
115 | } | |
116 | ||
117 | static const struct regulator_ops mt6370_dbvboost_ops = { | |
118 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
119 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
120 | .list_voltage = regulator_list_voltage_linear, | |
121 | .get_bypass = regulator_get_bypass_regmap, | |
122 | .set_bypass = regulator_set_bypass_regmap, | |
123 | .get_error_flags = mt6370_get_error_flags, | |
124 | }; | |
125 | ||
126 | static const struct regulator_ops mt6370_dbvout_ops = { | |
127 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
128 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
129 | .list_voltage = regulator_list_voltage_linear, | |
130 | .is_enabled = regulator_is_enabled_regmap, | |
131 | .enable = regulator_enable_regmap, | |
132 | .disable = regulator_disable_regmap, | |
133 | .set_active_discharge = regulator_set_active_discharge_regmap, | |
134 | .set_ramp_delay = regulator_set_ramp_delay_regmap, | |
135 | .get_error_flags = mt6370_get_error_flags, | |
136 | }; | |
137 | ||
138 | static const struct regulator_ops mt6370_ldo_ops = { | |
139 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
140 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
141 | .list_voltage = regulator_list_voltage_linear, | |
142 | .is_enabled = regulator_is_enabled_regmap, | |
143 | .enable = regulator_enable_regmap, | |
144 | .disable = regulator_disable_regmap, | |
145 | .set_active_discharge = regulator_set_active_discharge_regmap, | |
146 | .get_error_flags = mt6370_get_error_flags, | |
147 | }; | |
148 | ||
149 | static int mt6370_of_parse_cb(struct device_node *np, | |
150 | const struct regulator_desc *desc, | |
151 | struct regulator_config *config) | |
152 | { | |
153 | struct mt6370_priv *priv = config->driver_data; | |
154 | struct gpio_desc *enable_gpio; | |
155 | int ret; | |
156 | ||
4806c991 CH |
157 | enable_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0, |
158 | GPIOD_OUT_HIGH | | |
8171c93b CH |
159 | GPIOD_FLAGS_BIT_NONEXCLUSIVE, |
160 | desc->name); | |
161 | if (IS_ERR(enable_gpio)) { | |
162 | config->ena_gpiod = NULL; | |
163 | return 0; | |
164 | } | |
165 | ||
166 | /* | |
167 | * RG control by default | |
168 | * Only if all are using external pin, change all by external control | |
169 | */ | |
170 | if (priv->use_external_ctrl) { | |
171 | ret = regmap_update_bits(priv->regmap, MT6370_REG_DB_CTRL1, | |
172 | MT6370_DBEXTEN_MASK, | |
173 | MT6370_DBEXTEN_MASK); | |
174 | if (ret) | |
175 | return ret; | |
176 | } | |
177 | ||
178 | config->ena_gpiod = enable_gpio; | |
179 | priv->use_external_ctrl = true; | |
180 | return 0; | |
181 | } | |
182 | ||
183 | static const struct regulator_desc mt6370_regulator_descs[] = { | |
184 | { | |
185 | .name = "mt6370-dsv-vbst", | |
186 | .of_match = of_match_ptr("dsvbst"), | |
187 | .regulators_node = of_match_ptr("regulators"), | |
188 | .id = MT6370_IDX_DSVBOOST, | |
189 | .type = REGULATOR_VOLTAGE, | |
190 | .owner = THIS_MODULE, | |
191 | .ops = &mt6370_dbvboost_ops, | |
192 | .min_uV = MT6370_DBVBOOST_MINUV, | |
193 | .uV_step = MT6370_DBVBOOST_STPUV, | |
194 | .n_voltages = MT6370_DBVBOOST_N_VOLT, | |
195 | .vsel_reg = MT6370_REG_DB_VBST, | |
196 | .vsel_mask = MT6370_DBVOUT_MASK, | |
197 | .bypass_reg = MT6370_REG_DB_CTRL1, | |
198 | .bypass_mask = MT6370_DBPERD_MASK, | |
199 | .bypass_val_on = MT6370_DBPERD_MASK, | |
200 | }, | |
201 | { | |
202 | .name = "mt6370-dsv-vpos", | |
203 | .of_match = of_match_ptr("dsvpos"), | |
204 | .regulators_node = of_match_ptr("regulators"), | |
205 | .id = MT6370_IDX_DSVPOS, | |
206 | .type = REGULATOR_VOLTAGE, | |
207 | .owner = THIS_MODULE, | |
208 | .of_parse_cb = mt6370_of_parse_cb, | |
209 | .ops = &mt6370_dbvout_ops, | |
210 | .min_uV = MT6370_DBVOUT_MINUV, | |
211 | .uV_step = MT6370_DBVOUT_STPUV, | |
212 | .n_voltages = MT6370_DBVOUT_N_VOLT, | |
213 | .vsel_reg = MT6370_REG_DB_VPOS, | |
214 | .vsel_mask = MT6370_DBVOUT_MASK, | |
215 | .enable_reg = MT6370_REG_DB_CTRL2, | |
216 | .enable_mask = MT6370_DBVPOSEN_MASK, | |
217 | .ramp_reg = MT6370_REG_DB_VPOS, | |
218 | .ramp_mask = MT6370_DBSLEW_MASK, | |
219 | .ramp_delay_table = mt6370_vpos_ramp_tbl, | |
220 | .n_ramp_values = ARRAY_SIZE(mt6370_vpos_ramp_tbl), | |
221 | .active_discharge_reg = MT6370_REG_DB_CTRL2, | |
222 | .active_discharge_mask = MT6370_DBVPOSDISG_MASK, | |
223 | .active_discharge_on = MT6370_DBVPOSDISG_MASK, | |
224 | }, | |
225 | { | |
226 | .name = "mt6370-dsv-vneg", | |
227 | .of_match = of_match_ptr("dsvneg"), | |
228 | .regulators_node = of_match_ptr("regulators"), | |
229 | .id = MT6370_IDX_DSVNEG, | |
230 | .type = REGULATOR_VOLTAGE, | |
231 | .owner = THIS_MODULE, | |
232 | .of_parse_cb = mt6370_of_parse_cb, | |
233 | .ops = &mt6370_dbvout_ops, | |
234 | .min_uV = MT6370_DBVOUT_MINUV, | |
235 | .uV_step = MT6370_DBVOUT_STPUV, | |
236 | .n_voltages = MT6370_DBVOUT_N_VOLT, | |
237 | .vsel_reg = MT6370_REG_DB_VNEG, | |
238 | .vsel_mask = MT6370_DBVOUT_MASK, | |
239 | .enable_reg = MT6370_REG_DB_CTRL2, | |
240 | .enable_mask = MT6370_DBVNEGEN_MASK, | |
241 | .ramp_reg = MT6370_REG_DB_VNEG, | |
242 | .ramp_mask = MT6370_DBSLEW_MASK, | |
243 | .ramp_delay_table = mt6370_vneg_ramp_tbl, | |
244 | .n_ramp_values = ARRAY_SIZE(mt6370_vneg_ramp_tbl), | |
245 | .active_discharge_reg = MT6370_REG_DB_CTRL2, | |
246 | .active_discharge_mask = MT6370_DBVNEGDISG_MASK, | |
247 | .active_discharge_on = MT6370_DBVNEGDISG_MASK, | |
248 | }, | |
249 | { | |
250 | .name = "mt6370-vib-ldo", | |
251 | .of_match = of_match_ptr("vibldo"), | |
252 | .regulators_node = of_match_ptr("regulators"), | |
253 | .id = MT6370_IDX_VIBLDO, | |
254 | .type = REGULATOR_VOLTAGE, | |
255 | .owner = THIS_MODULE, | |
256 | .ops = &mt6370_ldo_ops, | |
257 | .min_uV = MT6370_LDO_MINUV, | |
258 | .uV_step = MT6370_LDO_STPUV, | |
259 | .n_voltages = MT6370_LDO_N_VOLT, | |
260 | .vsel_reg = MT6370_REG_LDO_VOUT, | |
261 | .vsel_mask = MT6370_LDOVOUT_MASK, | |
262 | .enable_reg = MT6370_REG_LDO_VOUT, | |
263 | .enable_mask = MT6370_LDOEN_MASK, | |
264 | .active_discharge_reg = MT6370_REG_LDO_CFG, | |
265 | .active_discharge_mask = MT6370_LDOOMS_MASK, | |
266 | .active_discharge_on = MT6370_LDOOMS_MASK, | |
267 | } | |
268 | }; | |
269 | ||
270 | static irqreturn_t mt6370_scp_handler(int irq, void *data) | |
271 | { | |
272 | struct regulator_dev *rdev = data; | |
273 | ||
274 | regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, | |
275 | NULL); | |
276 | return IRQ_HANDLED; | |
277 | } | |
278 | ||
279 | static irqreturn_t mt6370_ocp_handler(int irq, void *data) | |
280 | { | |
281 | struct regulator_dev *rdev = data; | |
282 | ||
283 | regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); | |
284 | return IRQ_HANDLED; | |
285 | } | |
286 | ||
287 | static int mt6370_regulator_irq_register(struct mt6370_priv *priv) | |
288 | { | |
289 | struct platform_device *pdev = to_platform_device(priv->dev); | |
290 | static const struct { | |
291 | const char *name; | |
292 | int rid; | |
293 | irq_handler_t handler; | |
294 | } mt6370_irqs[] = { | |
295 | { "db_vpos_scp", MT6370_IDX_DSVPOS, mt6370_scp_handler }, | |
296 | { "db_vneg_scp", MT6370_IDX_DSVNEG, mt6370_scp_handler }, | |
297 | { "db_vbst_ocp", MT6370_IDX_DSVBOOST, mt6370_ocp_handler }, | |
298 | { "db_vpos_ocp", MT6370_IDX_DSVPOS, mt6370_ocp_handler }, | |
299 | { "db_vneg_ocp", MT6370_IDX_DSVNEG, mt6370_ocp_handler }, | |
300 | { "ldo_oc", MT6370_IDX_VIBLDO, mt6370_ocp_handler } | |
301 | }; | |
302 | struct regulator_dev *rdev; | |
303 | int i, irq, ret; | |
304 | ||
305 | for (i = 0; i < ARRAY_SIZE(mt6370_irqs); i++) { | |
306 | irq = platform_get_irq_byname(pdev, mt6370_irqs[i].name); | |
307 | ||
308 | rdev = priv->rdev[mt6370_irqs[i].rid]; | |
309 | ||
310 | ret = devm_request_threaded_irq(priv->dev, irq, NULL, | |
311 | mt6370_irqs[i].handler, 0, | |
312 | mt6370_irqs[i].name, rdev); | |
313 | if (ret) { | |
314 | dev_err(priv->dev, | |
315 | "Failed to register (%d) interrupt\n", i); | |
316 | return ret; | |
317 | } | |
318 | } | |
319 | ||
320 | return 0; | |
321 | } | |
322 | ||
323 | static int mt6370_regualtor_register(struct mt6370_priv *priv) | |
324 | { | |
325 | struct regulator_dev *rdev; | |
326 | struct regulator_config cfg = {}; | |
327 | struct device *parent = priv->dev->parent; | |
328 | int i; | |
329 | ||
330 | cfg.dev = parent; | |
331 | cfg.driver_data = priv; | |
332 | ||
333 | for (i = 0; i < MT6370_MAX_IDX; i++) { | |
334 | rdev = devm_regulator_register(priv->dev, | |
335 | mt6370_regulator_descs + i, | |
336 | &cfg); | |
337 | if (IS_ERR(rdev)) { | |
338 | dev_err(priv->dev, | |
339 | "Failed to register (%d) regulator\n", i); | |
340 | return PTR_ERR(rdev); | |
341 | } | |
342 | ||
343 | priv->rdev[i] = rdev; | |
344 | } | |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
349 | static int mt6370_regulator_probe(struct platform_device *pdev) | |
350 | { | |
351 | struct mt6370_priv *priv; | |
352 | int ret; | |
353 | ||
354 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | |
355 | if (!priv) | |
356 | return -ENOMEM; | |
357 | ||
358 | priv->dev = &pdev->dev; | |
359 | ||
360 | priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); | |
361 | if (!priv->regmap) { | |
362 | dev_err(&pdev->dev, "Failed to init regmap\n"); | |
363 | return -ENODEV; | |
364 | } | |
365 | ||
366 | ret = mt6370_regualtor_register(priv); | |
367 | if (ret) | |
368 | return ret; | |
369 | ||
370 | return mt6370_regulator_irq_register(priv); | |
371 | } | |
372 | ||
373 | static const struct platform_device_id mt6370_devid_table[] = { | |
374 | { "mt6370-regulator", 0}, | |
375 | {} | |
376 | }; | |
377 | MODULE_DEVICE_TABLE(platform, mt6370_devid_table); | |
378 | ||
379 | static struct platform_driver mt6370_regulator_driver = { | |
380 | .driver = { | |
381 | .name = "mt6370-regulator", | |
41cff178 | 382 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
8171c93b CH |
383 | }, |
384 | .id_table = mt6370_devid_table, | |
385 | .probe = mt6370_regulator_probe, | |
386 | }; | |
387 | module_platform_driver(mt6370_regulator_driver); | |
388 | ||
389 | MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); | |
390 | MODULE_DESCRIPTION("Mediatek MT6370 Regulator Driver"); | |
391 | MODULE_LICENSE("GPL v2"); |