d1: add new target
[openwrt/staging/mans0n.git] / target / linux / d1 / patches-6.1 / 0021-regulator-sun20i-Add-support-for-Allwinner-D1-LDOs.patch
1 From ad842bfb2eb10a75050dd69145ca59de982eb0e9 Mon Sep 17 00:00:00 2001
2 From: Samuel Holland <samuel@sholland.org>
3 Date: Sun, 17 Jul 2022 11:46:52 -0500
4 Subject: [PATCH 021/117] regulator: sun20i: Add support for Allwinner D1 LDOs
5
6 D1 contains two pairs of LDOs. Since they have similar bindings, and
7 they always exist together, put them in a single driver.
8
9 The analog LDOs are relatively boring, with a single linear range. Their
10 one quirk is that a bandgap reference must be calibrated for them to
11 produce the correct voltage.
12
13 The system LDOs have the complication that their voltage step is not an
14 integer, so a custom .list_voltage is needed to get the rounding right.
15
16 Series-changes: 2
17 - Use decimal numbers for .n_voltages instead of field widths
18 - Get the regmap from the parent device instead of a property/phandle
19
20 Series-changes: 3
21 - Adjust control flow in sun20i_regulator_get_regmap() for clarity
22
23 Reviewed-by: Heiko Stuebner <heiko@sntech.de>
24 Tested-by: Heiko Stuebner <heiko@sntech.de>
25 Signed-off-by: Samuel Holland <samuel@sholland.org>
26 ---
27 drivers/regulator/Kconfig | 8 +
28 drivers/regulator/Makefile | 1 +
29 drivers/regulator/sun20i-regulator.c | 232 +++++++++++++++++++++++++++
30 3 files changed, 241 insertions(+)
31 create mode 100644 drivers/regulator/sun20i-regulator.c
32
33 --- a/drivers/regulator/Kconfig
34 +++ b/drivers/regulator/Kconfig
35 @@ -1280,6 +1280,14 @@ config REGULATOR_STW481X_VMMC
36 This driver supports the internal VMMC regulator in the STw481x
37 PMIC chips.
38
39 +config REGULATOR_SUN20I
40 + tristate "Allwinner D1 internal LDOs"
41 + depends on ARCH_SUNXI || COMPILE_TEST
42 + depends on MFD_SYSCON && NVMEM
43 + default ARCH_SUNXI
44 + help
45 + This driver supports the internal LDOs in the Allwinner D1 SoC.
46 +
47 config REGULATOR_SY7636A
48 tristate "Silergy SY7636A voltage regulator"
49 depends on MFD_SY7636A
50 --- a/drivers/regulator/Makefile
51 +++ b/drivers/regulator/Makefile
52 @@ -150,6 +150,7 @@ obj-$(CONFIG_REGULATOR_STM32_VREFBUF) +=
53 obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
54 obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o
55 obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
56 +obj-$(CONFIG_REGULATOR_SUN20I) += sun20i-regulator.o
57 obj-$(CONFIG_REGULATOR_SY7636A) += sy7636a-regulator.o
58 obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o
59 obj-$(CONFIG_REGULATOR_SY8824X) += sy8824x.o
60 --- /dev/null
61 +++ b/drivers/regulator/sun20i-regulator.c
62 @@ -0,0 +1,232 @@
63 +// SPDX-License-Identifier: GPL-2.0-only
64 +//
65 +// Copyright (c) 2021-2022 Samuel Holland <samuel@sholland.org>
66 +//
67 +
68 +#include <linux/mfd/syscon.h>
69 +#include <linux/module.h>
70 +#include <linux/nvmem-consumer.h>
71 +#include <linux/of_device.h>
72 +#include <linux/platform_device.h>
73 +#include <linux/regmap.h>
74 +#include <linux/regulator/driver.h>
75 +
76 +#define SUN20I_POWER_REG 0x348
77 +
78 +#define SUN20I_SYS_LDO_CTRL_REG 0x150
79 +
80 +struct sun20i_regulator_data {
81 + int (*init)(struct device *dev,
82 + struct regmap *regmap);
83 + const struct regulator_desc *descs;
84 + unsigned int ndescs;
85 +};
86 +
87 +static int sun20i_d1_analog_ldos_init(struct device *dev, struct regmap *regmap)
88 +{
89 + u8 bg_trim;
90 + int ret;
91 +
92 + ret = nvmem_cell_read_u8(dev, "bg_trim", &bg_trim);
93 + if (ret)
94 + return dev_err_probe(dev, ret, "Failed to get bg_trim value\n");
95 +
96 + /* The default value corresponds to 900 mV. */
97 + if (!bg_trim)
98 + bg_trim = 0x19;
99 +
100 + return regmap_update_bits(regmap, SUN20I_POWER_REG,
101 + GENMASK(7, 0), bg_trim);
102 +}
103 +
104 +static const struct regulator_ops sun20i_d1_analog_ldo_ops = {
105 + .list_voltage = regulator_list_voltage_linear,
106 + .map_voltage = regulator_map_voltage_linear,
107 + .set_voltage_sel = regulator_set_voltage_sel_regmap,
108 + .get_voltage_sel = regulator_get_voltage_sel_regmap,
109 + .enable = regulator_enable_regmap,
110 + .disable = regulator_disable_regmap,
111 + .is_enabled = regulator_is_enabled_regmap,
112 +};
113 +
114 +static const struct regulator_desc sun20i_d1_analog_ldo_descs[] = {
115 + {
116 + .name = "aldo",
117 + .supply_name = "vdd33",
118 + .of_match = "aldo",
119 + .ops = &sun20i_d1_analog_ldo_ops,
120 + .type = REGULATOR_VOLTAGE,
121 + .owner = THIS_MODULE,
122 + .n_voltages = 8,
123 + .min_uV = 1650000,
124 + .uV_step = 50000,
125 + .vsel_reg = SUN20I_POWER_REG,
126 + .vsel_mask = GENMASK(14, 12),
127 + .enable_reg = SUN20I_POWER_REG,
128 + .enable_mask = BIT(31),
129 + },
130 + {
131 + .name = "hpldo",
132 + .supply_name = "hpldoin",
133 + .of_match = "hpldo",
134 + .ops = &sun20i_d1_analog_ldo_ops,
135 + .type = REGULATOR_VOLTAGE,
136 + .owner = THIS_MODULE,
137 + .n_voltages = 8,
138 + .min_uV = 1650000,
139 + .uV_step = 50000,
140 + .vsel_reg = SUN20I_POWER_REG,
141 + .vsel_mask = GENMASK(10, 8),
142 + .enable_reg = SUN20I_POWER_REG,
143 + .enable_mask = BIT(30),
144 + },
145 +};
146 +
147 +static const struct sun20i_regulator_data sun20i_d1_analog_ldos = {
148 + .init = sun20i_d1_analog_ldos_init,
149 + .descs = sun20i_d1_analog_ldo_descs,
150 + .ndescs = ARRAY_SIZE(sun20i_d1_analog_ldo_descs),
151 +};
152 +
153 +/* regulator_list_voltage_linear() modified for the non-integral uV_step. */
154 +static int sun20i_d1_system_ldo_list_voltage(struct regulator_dev *rdev,
155 + unsigned int selector)
156 +{
157 + const struct regulator_desc *desc = rdev->desc;
158 + unsigned int uV;
159 +
160 + if (selector >= desc->n_voltages)
161 + return -EINVAL;
162 +
163 + uV = desc->min_uV + (desc->uV_step * selector);
164 +
165 + /* Produce correctly-rounded absolute voltages. */
166 + return uV + ((selector + 1 + (desc->min_uV % 4)) / 3);
167 +}
168 +
169 +static const struct regulator_ops sun20i_d1_system_ldo_ops = {
170 + .list_voltage = sun20i_d1_system_ldo_list_voltage,
171 + .map_voltage = regulator_map_voltage_ascend,
172 + .set_voltage_sel = regulator_set_voltage_sel_regmap,
173 + .get_voltage_sel = regulator_get_voltage_sel_regmap,
174 +};
175 +
176 +static const struct regulator_desc sun20i_d1_system_ldo_descs[] = {
177 + {
178 + .name = "ldoa",
179 + .supply_name = "ldo-in",
180 + .of_match = "ldoa",
181 + .ops = &sun20i_d1_system_ldo_ops,
182 + .type = REGULATOR_VOLTAGE,
183 + .owner = THIS_MODULE,
184 + .n_voltages = 32,
185 + .min_uV = 1600000,
186 + .uV_step = 13333, /* repeating */
187 + .vsel_reg = SUN20I_SYS_LDO_CTRL_REG,
188 + .vsel_mask = GENMASK(7, 0),
189 + },
190 + {
191 + .name = "ldob",
192 + .supply_name = "ldo-in",
193 + .of_match = "ldob",
194 + .ops = &sun20i_d1_system_ldo_ops,
195 + .type = REGULATOR_VOLTAGE,
196 + .owner = THIS_MODULE,
197 + .n_voltages = 64,
198 + .min_uV = 1166666,
199 + .uV_step = 13333, /* repeating */
200 + .vsel_reg = SUN20I_SYS_LDO_CTRL_REG,
201 + .vsel_mask = GENMASK(15, 8),
202 + },
203 +};
204 +
205 +static const struct sun20i_regulator_data sun20i_d1_system_ldos = {
206 + .descs = sun20i_d1_system_ldo_descs,
207 + .ndescs = ARRAY_SIZE(sun20i_d1_system_ldo_descs),
208 +};
209 +
210 +static const struct of_device_id sun20i_regulator_of_match[] = {
211 + {
212 + .compatible = "allwinner,sun20i-d1-analog-ldos",
213 + .data = &sun20i_d1_analog_ldos,
214 + },
215 + {
216 + .compatible = "allwinner,sun20i-d1-system-ldos",
217 + .data = &sun20i_d1_system_ldos,
218 + },
219 + { },
220 +};
221 +MODULE_DEVICE_TABLE(of, sun20i_regulator_of_match);
222 +
223 +static struct regmap *sun20i_regulator_get_regmap(struct device *dev)
224 +{
225 + struct regmap *regmap;
226 +
227 + /*
228 + * First try the syscon interface. The system control device is not
229 + * compatible with "syscon", so fall back to getting the regmap from
230 + * its platform device. This is ugly, but required for devicetree
231 + * backward compatibility.
232 + */
233 + regmap = syscon_node_to_regmap(dev->parent->of_node);
234 + if (!IS_ERR(regmap))
235 + return regmap;
236 +
237 + regmap = dev_get_regmap(dev->parent, NULL);
238 + if (regmap)
239 + return regmap;
240 +
241 + return ERR_PTR(-EPROBE_DEFER);
242 +}
243 +
244 +static int sun20i_regulator_probe(struct platform_device *pdev)
245 +{
246 + const struct sun20i_regulator_data *data;
247 + struct device *dev = &pdev->dev;
248 + struct regulator_config config;
249 + struct regmap *regmap;
250 + int ret;
251 +
252 + data = of_device_get_match_data(dev);
253 + if (!data)
254 + return -EINVAL;
255 +
256 + regmap = sun20i_regulator_get_regmap(dev);
257 + if (IS_ERR(regmap))
258 + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to get regmap\n");
259 +
260 + if (data->init) {
261 + ret = data->init(dev, regmap);
262 + if (ret)
263 + return ret;
264 + }
265 +
266 + config = (struct regulator_config) {
267 + .dev = dev,
268 + .regmap = regmap,
269 + };
270 +
271 + for (unsigned int i = 0; i < data->ndescs; ++i) {
272 + const struct regulator_desc *desc = &data->descs[i];
273 + struct regulator_dev *rdev;
274 +
275 + rdev = devm_regulator_register(dev, desc, &config);
276 + if (IS_ERR(rdev))
277 + return PTR_ERR(rdev);
278 + }
279 +
280 + return 0;
281 +}
282 +
283 +static struct platform_driver sun20i_regulator_driver = {
284 + .probe = sun20i_regulator_probe,
285 + .driver = {
286 + .name = "sun20i-regulator",
287 + .of_match_table = sun20i_regulator_of_match,
288 + },
289 +};
290 +module_platform_driver(sun20i_regulator_driver);
291 +
292 +MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
293 +MODULE_DESCRIPTION("Allwinner D1 internal LDO driver");
294 +MODULE_LICENSE("GPL");