xref: /linux/drivers/regulator/qcom-refgen-regulator.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright (c) 2017, 2019-2020, The Linux Foundation. All rights reserved.
3 // Copyright (c) 2023, Linaro Limited
4 
5 #include <linux/bitfield.h>
6 #include <linux/clk.h>
7 #include <linux/module.h>
8 #include <linux/of.h>
9 #include <linux/platform_device.h>
10 #include <linux/regmap.h>
11 #include <linux/regulator/driver.h>
12 #include <linux/regulator/machine.h>
13 #include <linux/regulator/of_regulator.h>
14 
15 #define REFGEN_REG_BIAS_EN		0x08
16 #define REFGEN_BIAS_EN_MASK		GENMASK(2, 0)
17  #define REFGEN_BIAS_EN_ENABLE		0x7
18  #define REFGEN_BIAS_EN_DISABLE		0x6
19 
20 #define REFGEN_REG_REFGEN_STATUS	0xc
21 #define REFGEN_STATUS_OUT_MASK		BIT(3)
22 
23 #define REFGEN_REG_BG_CTRL		0x14
24 #define REFGEN_BG_CTRL_MASK		GENMASK(2, 1)
25  #define REFGEN_BG_CTRL_ENABLE		0x3
26  #define REFGEN_BG_CTRL_DISABLE		0x2
27 
28 #define REFGEN_REG_PWRDWN_CTRL5		0x80
29 #define REFGEN_PWRDWN_CTRL5_MASK	BIT(0)
30  #define REFGEN_PWRDWN_CTRL5_ENABLE	0x1
31 
32 struct qcom_refgen_regulator_data {
33 	const struct regulator_desc *rdesc;
34 	bool has_clocks;
35 };
36 
37 struct qcom_refgen_drvdata {
38 	struct clk_bulk_data *clks;
39 	int num_clks;
40 	bool is_enabled;
41 };
42 
43 static int qcom_sdm845_refgen_enable(struct regulator_dev *rdev)
44 {
45 	regmap_update_bits(rdev->regmap, REFGEN_REG_BG_CTRL, REFGEN_BG_CTRL_MASK,
46 			   FIELD_PREP(REFGEN_BG_CTRL_MASK, REFGEN_BG_CTRL_ENABLE));
47 
48 	regmap_write(rdev->regmap, REFGEN_REG_BIAS_EN,
49 		     FIELD_PREP(REFGEN_BIAS_EN_MASK, REFGEN_BIAS_EN_ENABLE));
50 
51 	return 0;
52 }
53 
54 static int qcom_sdm845_refgen_disable(struct regulator_dev *rdev)
55 {
56 	regmap_write(rdev->regmap, REFGEN_REG_BIAS_EN,
57 		     FIELD_PREP(REFGEN_BIAS_EN_MASK, REFGEN_BIAS_EN_DISABLE));
58 
59 	regmap_update_bits(rdev->regmap, REFGEN_REG_BG_CTRL, REFGEN_BG_CTRL_MASK,
60 			   FIELD_PREP(REFGEN_BG_CTRL_MASK, REFGEN_BG_CTRL_DISABLE));
61 
62 	return 0;
63 }
64 
65 static int qcom_sdm845_refgen_is_enabled(struct regulator_dev *rdev)
66 {
67 	u32 val;
68 
69 	regmap_read(rdev->regmap, REFGEN_REG_BG_CTRL, &val);
70 	if (FIELD_GET(REFGEN_BG_CTRL_MASK, val) != REFGEN_BG_CTRL_ENABLE)
71 		return 0;
72 
73 	regmap_read(rdev->regmap, REFGEN_REG_BIAS_EN, &val);
74 	if (FIELD_GET(REFGEN_BIAS_EN_MASK, val) != REFGEN_BIAS_EN_ENABLE)
75 		return 0;
76 
77 	return 1;
78 }
79 
80 static int qcom_ipq9650_refgen_enable(struct regulator_dev *rdev)
81 {
82 	struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev);
83 	int ret;
84 
85 	ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
86 	if (ret)
87 		return ret;
88 
89 	drvdata->is_enabled = true;
90 
91 	return 0;
92 }
93 
94 static int qcom_ipq9650_refgen_disable(struct regulator_dev *rdev)
95 {
96 	struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev);
97 
98 	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
99 
100 	drvdata->is_enabled = false;
101 
102 	return 0;
103 }
104 
105 static int qcom_ipq9650_refgen_is_enabled(struct regulator_dev *rdev)
106 {
107 	struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev);
108 
109 	return drvdata->is_enabled;
110 }
111 
112 static int qcom_ipq9650_refgen_get_status(struct regulator_dev *rdev)
113 {
114 	u32 val;
115 
116 	regmap_read(rdev->regmap, REFGEN_REG_REFGEN_STATUS, &val);
117 	if (FIELD_GET(REFGEN_STATUS_OUT_MASK, val))
118 		return REGULATOR_STATUS_ON;
119 
120 	return REGULATOR_STATUS_OFF;
121 }
122 
123 static const struct regulator_desc ipq9650_refgen_desc = {
124 	.enable_time = 5,
125 	.name = "refgen",
126 	.owner = THIS_MODULE,
127 	.type = REGULATOR_CURRENT,
128 	.ops = &(const struct regulator_ops) {
129 		.enable		= qcom_ipq9650_refgen_enable,
130 		.disable	= qcom_ipq9650_refgen_disable,
131 		.is_enabled	= qcom_ipq9650_refgen_is_enabled,
132 		.get_status	= qcom_ipq9650_refgen_get_status,
133 	},
134 };
135 
136 static const struct regulator_desc sdm845_refgen_desc = {
137 	.enable_time = 5,
138 	.name = "refgen",
139 	.owner = THIS_MODULE,
140 	.type = REGULATOR_CURRENT,
141 	.ops = &(const struct regulator_ops) {
142 		.enable		= qcom_sdm845_refgen_enable,
143 		.disable	= qcom_sdm845_refgen_disable,
144 		.is_enabled	= qcom_sdm845_refgen_is_enabled,
145 	},
146 };
147 
148 static const struct regulator_desc sm8250_refgen_desc = {
149 	.enable_reg = REFGEN_REG_PWRDWN_CTRL5,
150 	.enable_mask = REFGEN_PWRDWN_CTRL5_MASK,
151 	.enable_val = REFGEN_PWRDWN_CTRL5_ENABLE,
152 	.disable_val = 0,
153 	.enable_time = 5,
154 	.name = "refgen",
155 	.owner = THIS_MODULE,
156 	.type = REGULATOR_CURRENT,
157 	.ops = &(const struct regulator_ops) {
158 		.enable		= regulator_enable_regmap,
159 		.disable	= regulator_disable_regmap,
160 		.is_enabled	= regulator_is_enabled_regmap,
161 	},
162 };
163 
164 static const struct qcom_refgen_regulator_data ipq9650_data = {
165 	.rdesc = &ipq9650_refgen_desc,
166 	.has_clocks = true,
167 };
168 
169 static const struct qcom_refgen_regulator_data sdm845_data = {
170 	.rdesc = &sdm845_refgen_desc,
171 };
172 
173 static const struct qcom_refgen_regulator_data sm8250_data = {
174 	.rdesc = &sm8250_refgen_desc,
175 };
176 
177 static const struct regmap_config qcom_refgen_regmap_config = {
178 	.reg_bits = 32,
179 	.reg_stride = 4,
180 	.val_bits = 32,
181 };
182 
183 static int qcom_refgen_probe(struct platform_device *pdev)
184 {
185 	const struct qcom_refgen_regulator_data *data;
186 	struct qcom_refgen_drvdata *drvdata = NULL;
187 	struct regulator_init_data *init_data;
188 	struct regulator_config config = {};
189 	const struct regulator_desc *rdesc;
190 	struct device *dev = &pdev->dev;
191 	struct regulator_dev *rdev;
192 	struct regmap *regmap;
193 	void __iomem *base;
194 
195 	data = of_device_get_match_data(dev);
196 	if (!data)
197 		return -ENODATA;
198 
199 	if (data->has_clocks) {
200 		drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
201 		if (!drvdata)
202 			return -ENOMEM;
203 
204 		drvdata->num_clks = devm_clk_bulk_get_all(dev, &drvdata->clks);
205 		if (drvdata->num_clks < 0)
206 			return dev_err_probe(dev, drvdata->num_clks,
207 					     "failed to get clocks\n");
208 	}
209 
210 	rdesc = data->rdesc;
211 
212 	base = devm_platform_ioremap_resource(pdev, 0);
213 	if (IS_ERR(base))
214 		return PTR_ERR(base);
215 
216 	regmap = devm_regmap_init_mmio(dev, base, &qcom_refgen_regmap_config);
217 	if (IS_ERR(regmap))
218 		return PTR_ERR(regmap);
219 
220 	init_data = of_get_regulator_init_data(dev, dev->of_node, rdesc);
221 	if (!init_data)
222 		return -ENOMEM;
223 
224 	config.dev = dev;
225 	config.init_data = init_data;
226 	config.of_node = dev->of_node;
227 	config.regmap = regmap;
228 	config.driver_data = drvdata;
229 
230 	rdev = devm_regulator_register(dev, rdesc, &config);
231 	if (IS_ERR(rdev))
232 		return PTR_ERR(rdev);
233 
234 	return 0;
235 }
236 
237 static const struct of_device_id qcom_refgen_match_table[] = {
238 	{ .compatible = "qcom,ipq9650-refgen-regulator", .data = &ipq9650_data },
239 	{ .compatible = "qcom,sdm845-refgen-regulator", .data = &sdm845_data },
240 	{ .compatible = "qcom,sm8250-refgen-regulator", .data = &sm8250_data },
241 	{ }
242 };
243 MODULE_DEVICE_TABLE(of, qcom_refgen_match_table);
244 
245 static struct platform_driver qcom_refgen_driver = {
246 	.probe = qcom_refgen_probe,
247 	.driver = {
248 		.name = "qcom-refgen-regulator",
249 		.of_match_table = qcom_refgen_match_table,
250 	},
251 };
252 module_platform_driver(qcom_refgen_driver);
253 
254 MODULE_LICENSE("GPL");
255 MODULE_DESCRIPTION("Qualcomm REFGEN regulator driver");
256