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>
7#include <linux/mod_devicetable.h>
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
15enum {
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
62struct 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
69static const unsigned int mt6370_vpos_ramp_tbl[] = { 8540, 5840, 4830, 3000 };
70static const unsigned int mt6370_vneg_ramp_tbl[] = { 10090, 6310, 5050, 3150 };
71
72static 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(map: regmap, reg: stat_reg, val: &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
117static 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
126static 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
138static 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
149static 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
157 enable_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), con_id: "enable", index: 0,
158 flags: GPIOD_OUT_HIGH |
159 GPIOD_FLAGS_BIT_NONEXCLUSIVE,
160 label: desc->name);
161 if (IS_ERR(ptr: 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(map: 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
183static 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
270static 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
279static 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
287static 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(dev: priv->dev, irq, NULL,
311 thread_fn: mt6370_irqs[i].handler, irqflags: 0,
312 devname: mt6370_irqs[i].name, dev_id: 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
323static 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(dev: priv->dev,
335 regulator_desc: mt6370_regulator_descs + i,
336 config: &cfg);
337 if (IS_ERR(ptr: rdev)) {
338 dev_err(priv->dev,
339 "Failed to register (%d) regulator\n", i);
340 return PTR_ERR(ptr: rdev);
341 }
342
343 priv->rdev[i] = rdev;
344 }
345
346 return 0;
347}
348
349static int mt6370_regulator_probe(struct platform_device *pdev)
350{
351 struct mt6370_priv *priv;
352 int ret;
353
354 priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL);
355 if (!priv)
356 return -ENOMEM;
357
358 priv->dev = &pdev->dev;
359
360 priv->regmap = dev_get_regmap(dev: 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
373static const struct platform_device_id mt6370_devid_table[] = {
374 { "mt6370-regulator", 0},
375 {}
376};
377MODULE_DEVICE_TABLE(platform, mt6370_devid_table);
378
379static struct platform_driver mt6370_regulator_driver = {
380 .driver = {
381 .name = "mt6370-regulator",
382 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
383 },
384 .id_table = mt6370_devid_table,
385 .probe = mt6370_regulator_probe,
386};
387module_platform_driver(mt6370_regulator_driver);
388
389MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
390MODULE_DESCRIPTION("Mediatek MT6370 Regulator Driver");
391MODULE_LICENSE("GPL v2");
392

source code of linux/drivers/regulator/mt6370-regulator.c