Skip to content

Commit 4e70b26

Browse files
smaeulbroonie
authored andcommitted
regulator: sun20i: Add Allwinner D1 LDOs driver
D1 contains two pairs of LDOs, "analog" LDOs and "system" LDOs. They are similar and can share a driver, but only the system LDOs have a DT binding defined so far. The system LDOs have a single linear range. The voltage step is not an integer, so a custom .list_voltage is needed to get the rounding right. Signed-off-by: Samuel Holland <[email protected]> Reviewed-by: Andre Przywara <[email protected]> Reviewed-by: Jernej Skrabec <[email protected]> Signed-off-by: Chen-Yu Tsai <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 622bab1 commit 4e70b26

File tree

3 files changed

+166
-0
lines changed

3 files changed

+166
-0
lines changed

drivers/regulator/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,14 @@ config REGULATOR_STW481X_VMMC
14151415
This driver supports the internal VMMC regulator in the STw481x
14161416
PMIC chips.
14171417

1418+
config REGULATOR_SUN20I
1419+
tristate "Allwinner D1 internal LDOs"
1420+
depends on ARCH_SUNXI || COMPILE_TEST
1421+
select MFD_SYSCON
1422+
default ARCH_SUNXI
1423+
help
1424+
This driver supports the internal LDOs in the Allwinner D1 SoC.
1425+
14181426
config REGULATOR_SY7636A
14191427
tristate "Silergy SY7636A voltage regulator"
14201428
depends on MFD_SY7636A

drivers/regulator/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
163163
obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
164164
obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o
165165
obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
166+
obj-$(CONFIG_REGULATOR_SUN20I) += sun20i-regulator.o
166167
obj-$(CONFIG_REGULATOR_SY7636A) += sy7636a-regulator.o
167168
obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o
168169
obj-$(CONFIG_REGULATOR_SY8824X) += sy8824x.o

drivers/regulator/sun20i-regulator.c

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
//
3+
// Copyright (c) 2021-2022 Samuel Holland <[email protected]>
4+
//
5+
6+
#include <linux/mfd/syscon.h>
7+
#include <linux/module.h>
8+
#include <linux/of.h>
9+
#include <linux/of_device.h>
10+
#include <linux/platform_device.h>
11+
#include <linux/regmap.h>
12+
#include <linux/regulator/driver.h>
13+
14+
#define SUN20I_SYS_LDO_CTRL_REG 0x150
15+
16+
struct sun20i_regulator_data {
17+
const struct regulator_desc *descs;
18+
unsigned int ndescs;
19+
};
20+
21+
/* regulator_list_voltage_linear() modified for the non-integral uV_step. */
22+
static int sun20i_d1_system_ldo_list_voltage(struct regulator_dev *rdev,
23+
unsigned int selector)
24+
{
25+
const struct regulator_desc *desc = rdev->desc;
26+
unsigned int fraction, uV;
27+
28+
if (selector >= desc->n_voltages)
29+
return -EINVAL;
30+
31+
uV = desc->min_uV + (desc->uV_step * selector);
32+
fraction = selector + (desc->min_uV % 4);
33+
34+
if (uV > 1606667)
35+
uV += 6667;
36+
else
37+
fraction++;
38+
39+
/* Produce correctly-rounded absolute voltages. */
40+
return uV + (fraction / 3);
41+
}
42+
43+
static const struct regulator_ops sun20i_d1_system_ldo_ops = {
44+
.list_voltage = sun20i_d1_system_ldo_list_voltage,
45+
.map_voltage = regulator_map_voltage_ascend,
46+
.set_voltage_sel = regulator_set_voltage_sel_regmap,
47+
.get_voltage_sel = regulator_get_voltage_sel_regmap,
48+
};
49+
50+
static const struct regulator_desc sun20i_d1_system_ldo_descs[] = {
51+
{
52+
.name = "ldoa",
53+
.supply_name = "ldo-in",
54+
.of_match = "ldoa",
55+
.ops = &sun20i_d1_system_ldo_ops,
56+
.type = REGULATOR_VOLTAGE,
57+
.owner = THIS_MODULE,
58+
.n_voltages = 32,
59+
.min_uV = 1593333,
60+
.uV_step = 13333, /* repeating */
61+
.vsel_reg = SUN20I_SYS_LDO_CTRL_REG,
62+
.vsel_mask = GENMASK(7, 0),
63+
},
64+
{
65+
.name = "ldob",
66+
.supply_name = "ldo-in",
67+
.of_match = "ldob",
68+
.ops = &sun20i_d1_system_ldo_ops,
69+
.type = REGULATOR_VOLTAGE,
70+
.owner = THIS_MODULE,
71+
.n_voltages = 64,
72+
.min_uV = 1166666,
73+
.uV_step = 13333, /* repeating */
74+
.vsel_reg = SUN20I_SYS_LDO_CTRL_REG,
75+
.vsel_mask = GENMASK(15, 8),
76+
},
77+
};
78+
79+
static const struct sun20i_regulator_data sun20i_d1_system_ldos = {
80+
.descs = sun20i_d1_system_ldo_descs,
81+
.ndescs = ARRAY_SIZE(sun20i_d1_system_ldo_descs),
82+
};
83+
84+
static struct regmap *sun20i_regulator_get_regmap(struct device *dev)
85+
{
86+
struct regmap *regmap;
87+
88+
/*
89+
* First try the syscon interface. The system control device is not
90+
* compatible with "syscon", so fall back to getting the regmap from
91+
* its platform device. This is ugly, but required for devicetree
92+
* backward compatibility.
93+
*/
94+
regmap = syscon_node_to_regmap(dev->parent->of_node);
95+
if (!IS_ERR(regmap))
96+
return regmap;
97+
98+
regmap = dev_get_regmap(dev->parent, NULL);
99+
if (regmap)
100+
return regmap;
101+
102+
return ERR_PTR(-EPROBE_DEFER);
103+
}
104+
105+
static int sun20i_regulator_probe(struct platform_device *pdev)
106+
{
107+
const struct sun20i_regulator_data *data;
108+
struct device *dev = &pdev->dev;
109+
struct regulator_config config;
110+
struct regmap *regmap;
111+
112+
data = of_device_get_match_data(dev);
113+
if (!data)
114+
return -EINVAL;
115+
116+
regmap = sun20i_regulator_get_regmap(dev);
117+
if (IS_ERR(regmap))
118+
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to get regmap\n");
119+
120+
config = (struct regulator_config) {
121+
.dev = dev,
122+
.regmap = regmap,
123+
};
124+
125+
for (unsigned int i = 0; i < data->ndescs; ++i) {
126+
const struct regulator_desc *desc = &data->descs[i];
127+
struct regulator_dev *rdev;
128+
129+
rdev = devm_regulator_register(dev, desc, &config);
130+
if (IS_ERR(rdev))
131+
return PTR_ERR(rdev);
132+
}
133+
134+
return 0;
135+
}
136+
137+
static const struct of_device_id sun20i_regulator_of_match[] = {
138+
{
139+
.compatible = "allwinner,sun20i-d1-system-ldos",
140+
.data = &sun20i_d1_system_ldos,
141+
},
142+
{ },
143+
};
144+
MODULE_DEVICE_TABLE(of, sun20i_regulator_of_match);
145+
146+
static struct platform_driver sun20i_regulator_driver = {
147+
.probe = sun20i_regulator_probe,
148+
.driver = {
149+
.name = "sun20i-regulator",
150+
.of_match_table = sun20i_regulator_of_match,
151+
},
152+
};
153+
module_platform_driver(sun20i_regulator_driver);
154+
155+
MODULE_AUTHOR("Samuel Holland <[email protected]>");
156+
MODULE_DESCRIPTION("Allwinner D1 internal LDO driver");
157+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)