1a0db6b0aShenryc.chen // SPDX-License-Identifier: GPL-2.0
2a0db6b0aShenryc.chen //
3a0db6b0aShenryc.chen // Copyright (c) 2020 MediaTek Inc.
4a0db6b0aShenryc.chen 
5a0db6b0aShenryc.chen #include <linux/err.h>
6a0db6b0aShenryc.chen #include <linux/init.h>
7a0db6b0aShenryc.chen #include <linux/module.h>
8a0db6b0aShenryc.chen #include <linux/platform_device.h>
9*045a44d4SRob Herring #include <linux/of_.h>
10a0db6b0aShenryc.chen #include <linux/regulator/driver.h>
11a0db6b0aShenryc.chen #include <linux/regulator/of_regulator.h>
12a0db6b0aShenryc.chen #include <linux/soc/mediatek/mtk_dvfsrc.h>
13a0db6b0aShenryc.chen 
14a0db6b0aShenryc.chen #define DVFSRC_ID_VCORE		0
15a0db6b0aShenryc.chen #define DVFSRC_ID_VSCP		1
16a0db6b0aShenryc.chen 
17a0db6b0aShenryc.chen #define MT_DVFSRC_REGULAR(match, _name,	_volt_table)	\
18a0db6b0aShenryc.chen [DVFSRC_ID_##_name] = {					\
19a0db6b0aShenryc.chen 	.desc = {					\
20a0db6b0aShenryc.chen 		.name = match,				\
21a0db6b0aShenryc.chen 		.of_match = of_match_ptr(match),	\
22a0db6b0aShenryc.chen 		.ops = &dvfsrc_vcore_ops,		\
23a0db6b0aShenryc.chen 		.type = REGULATOR_VOLTAGE,		\
24a0db6b0aShenryc.chen 		.id = DVFSRC_ID_##_name,		\
25a0db6b0aShenryc.chen 		.owner = THIS_MODULE,			\
26a0db6b0aShenryc.chen 		.n_voltages = ARRAY_SIZE(_volt_table),	\
27a0db6b0aShenryc.chen 		.volt_table = _volt_table,		\
28a0db6b0aShenryc.chen 	},	\
29a0db6b0aShenryc.chen }
30a0db6b0aShenryc.chen 
31a0db6b0aShenryc.chen /*
32a0db6b0aShenryc.chen  * DVFSRC regulators' information
33a0db6b0aShenryc.chen  *
34a0db6b0aShenryc.chen  * @desc: standard fields of regulator description.
35a0db6b0aShenryc.chen  * @voltage_selector:  Selector used for get_voltage_sel() and
36a0db6b0aShenryc.chen  *			   set_voltage_sel() callbacks
37a0db6b0aShenryc.chen  */
38a0db6b0aShenryc.chen 
39a0db6b0aShenryc.chen struct dvfsrc_regulator {
40a0db6b0aShenryc.chen 	struct regulator_desc	desc;
41a0db6b0aShenryc.chen };
42a0db6b0aShenryc.chen 
43a0db6b0aShenryc.chen /*
44a0db6b0aShenryc.chen  * MTK DVFSRC regulators' init data
45a0db6b0aShenryc.chen  *
46a0db6b0aShenryc.chen  * @size: num of regulators
47a0db6b0aShenryc.chen  * @regulator_info: regulator info.
48a0db6b0aShenryc.chen  */
49a0db6b0aShenryc.chen struct dvfsrc_regulator_init_data {
50a0db6b0aShenryc.chen 	u32 size;
51a0db6b0aShenryc.chen 	struct dvfsrc_regulator *regulator_info;
52a0db6b0aShenryc.chen };
53a0db6b0aShenryc.chen 
to_dvfsrc_dev(struct regulator_dev * rdev)54a0db6b0aShenryc.chen static inline struct device *to_dvfsrc_dev(struct regulator_dev *rdev)
55a0db6b0aShenryc.chen {
56a0db6b0aShenryc.chen 	return rdev_get_dev(rdev)->parent;
57a0db6b0aShenryc.chen }
58a0db6b0aShenryc.chen 
dvfsrc_set_voltage_sel(struct regulator_dev * rdev,unsigned int selector)59a0db6b0aShenryc.chen static int dvfsrc_set_voltage_sel(struct regulator_dev *rdev,
60a0db6b0aShenryc.chen 				  unsigned int selector)
61a0db6b0aShenryc.chen {
62a0db6b0aShenryc.chen 	struct device *dvfsrc_dev = to_dvfsrc_dev(rdev);
63a0db6b0aShenryc.chen 	int id = rdev_get_id(rdev);
64a0db6b0aShenryc.chen 
65a0db6b0aShenryc.chen 	if (id == DVFSRC_ID_VCORE)
66a0db6b0aShenryc.chen 		mtk_dvfsrc_send_request(dvfsrc_dev,
67a0db6b0aShenryc.chen 					MTK_DVFSRC_CMD_VCORE_REQUEST,
68a0db6b0aShenryc.chen 					selector);
69a0db6b0aShenryc.chen 	else if (id == DVFSRC_ID_VSCP)
70a0db6b0aShenryc.chen 		mtk_dvfsrc_send_request(dvfsrc_dev,
71a0db6b0aShenryc.chen 					MTK_DVFSRC_CMD_VSCP_REQUEST,
72a0db6b0aShenryc.chen 					selector);
73a0db6b0aShenryc.chen 	else
74a0db6b0aShenryc.chen 		return -EINVAL;
75a0db6b0aShenryc.chen 
76a0db6b0aShenryc.chen 	return 0;
77a0db6b0aShenryc.chen }
78a0db6b0aShenryc.chen 
dvfsrc_get_voltage_sel(struct regulator_dev * rdev)79a0db6b0aShenryc.chen static int dvfsrc_get_voltage_sel(struct regulator_dev *rdev)
80a0db6b0aShenryc.chen {
81a0db6b0aShenryc.chen 	struct device *dvfsrc_dev = to_dvfsrc_dev(rdev);
82a0db6b0aShenryc.chen 	int id = rdev_get_id(rdev);
83a0db6b0aShenryc.chen 	int val, ret;
84a0db6b0aShenryc.chen 
85a0db6b0aShenryc.chen 	if (id == DVFSRC_ID_VCORE)
86a0db6b0aShenryc.chen 		ret = mtk_dvfsrc_query_info(dvfsrc_dev,
87a0db6b0aShenryc.chen 					    MTK_DVFSRC_CMD_VCORE_LEVEL_QUERY,
88a0db6b0aShenryc.chen 					    &val);
89a0db6b0aShenryc.chen 	else if (id == DVFSRC_ID_VSCP)
90a0db6b0aShenryc.chen 		ret = mtk_dvfsrc_query_info(dvfsrc_dev,
91a0db6b0aShenryc.chen 					    MTK_DVFSRC_CMD_VSCP_LEVEL_QUERY,
92a0db6b0aShenryc.chen 					    &val);
93a0db6b0aShenryc.chen 	else
94a0db6b0aShenryc.chen 		return -EINVAL;
95a0db6b0aShenryc.chen 
96a0db6b0aShenryc.chen 	if (ret != 0)
97a0db6b0aShenryc.chen 		return ret;
98a0db6b0aShenryc.chen 
99a0db6b0aShenryc.chen 	return val;
100a0db6b0aShenryc.chen }
101a0db6b0aShenryc.chen 
102a0db6b0aShenryc.chen static const struct regulator_ops dvfsrc_vcore_ops = {
103a0db6b0aShenryc.chen 	.list_voltage = regulator_list_voltage_table,
104a0db6b0aShenryc.chen 	.get_voltage_sel = dvfsrc_get_voltage_sel,
105a0db6b0aShenryc.chen 	.set_voltage_sel = dvfsrc_set_voltage_sel,
106a0db6b0aShenryc.chen };
107a0db6b0aShenryc.chen 
108a0db6b0aShenryc.chen static const unsigned int mt8183_voltages[] = {
109a0db6b0aShenryc.chen 	725000,
110a0db6b0aShenryc.chen 	800000,
111a0db6b0aShenryc.chen };
112a0db6b0aShenryc.chen 
113a0db6b0aShenryc.chen static struct dvfsrc_regulator mt8183_regulators[] = {
114a0db6b0aShenryc.chen 	MT_DVFSRC_REGULAR("dvfsrc-vcore", VCORE,
115a0db6b0aShenryc.chen 			  mt8183_voltages),
116a0db6b0aShenryc.chen };
117a0db6b0aShenryc.chen 
118a0db6b0aShenryc.chen static const struct dvfsrc_regulator_init_data regulator_mt8183_data = {
119a0db6b0aShenryc.chen 	.size = ARRAY_SIZE(mt8183_regulators),
120a0db6b0aShenryc.chen 	.regulator_info = &mt8183_regulators[0],
121a0db6b0aShenryc.chen };
122a0db6b0aShenryc.chen 
123a0db6b0aShenryc.chen static const unsigned int mt6873_voltages[] = {
124a0db6b0aShenryc.chen 	575000,
125a0db6b0aShenryc.chen 	600000,
126a0db6b0aShenryc.chen 	650000,
127a0db6b0aShenryc.chen 	725000,
128a0db6b0aShenryc.chen };
129a0db6b0aShenryc.chen 
130a0db6b0aShenryc.chen static struct dvfsrc_regulator mt6873_regulators[] = {
131a0db6b0aShenryc.chen 	MT_DVFSRC_REGULAR("dvfsrc-vcore", VCORE,
132a0db6b0aShenryc.chen 			  mt6873_voltages),
133a0db6b0aShenryc.chen 	MT_DVFSRC_REGULAR("dvfsrc-vscp", VSCP,
134a0db6b0aShenryc.chen 			  mt6873_voltages),
135a0db6b0aShenryc.chen };
136a0db6b0aShenryc.chen 
137a0db6b0aShenryc.chen static const struct dvfsrc_regulator_init_data regulator_mt6873_data = {
138a0db6b0aShenryc.chen 	.size = ARRAY_SIZE(mt6873_regulators),
139a0db6b0aShenryc.chen 	.regulator_info = &mt6873_regulators[0],
140a0db6b0aShenryc.chen };
141a0db6b0aShenryc.chen 
142a0db6b0aShenryc.chen static const struct of_device_id mtk_dvfsrc_regulator_match[] = {
143a0db6b0aShenryc.chen 	{
144a0db6b0aShenryc.chen 		.compatible = "mediatek,mt8183-dvfsrc",
145a0db6b0aShenryc.chen 		.data = &regulator_mt8183_data,
146a0db6b0aShenryc.chen 	}, {
147a0db6b0aShenryc.chen 		.compatible = "mediatek,mt8192-dvfsrc",
148a0db6b0aShenryc.chen 		.data = &regulator_mt6873_data,
149a0db6b0aShenryc.chen 	}, {
150a0db6b0aShenryc.chen 		.compatible = "mediatek,mt6873-dvfsrc",
151a0db6b0aShenryc.chen 		.data = &regulator_mt6873_data,
152a0db6b0aShenryc.chen 	}, {
153a0db6b0aShenryc.chen 		/* sentinel */
154a0db6b0aShenryc.chen 	},
155a0db6b0aShenryc.chen };
156a0db6b0aShenryc.chen MODULE_DEVICE_TABLE(of, mtk_dvfsrc_regulator_match);
157a0db6b0aShenryc.chen 
dvfsrc_vcore_regulator_probe(struct platform_device * pdev)158a0db6b0aShenryc.chen static int dvfsrc_vcore_regulator_probe(struct platform_device *pdev)
159a0db6b0aShenryc.chen {
160a0db6b0aShenryc.chen 	const struct of_device_id *match;
161a0db6b0aShenryc.chen 	struct device *dev = &pdev->dev;
162a0db6b0aShenryc.chen 	struct regulator_config config = { };
163a0db6b0aShenryc.chen 	struct regulator_dev *rdev;
164a0db6b0aShenryc.chen 	const struct dvfsrc_regulator_init_data *regulator_init_data;
165a0db6b0aShenryc.chen 	struct dvfsrc_regulator *mt_regulators;
166a0db6b0aShenryc.chen 	int i;
167a0db6b0aShenryc.chen 
168a0db6b0aShenryc.chen 	match = of_match_node(mtk_dvfsrc_regulator_match, dev->parent->of_node);
169a0db6b0aShenryc.chen 
170a0db6b0aShenryc.chen 	if (!match) {
171a0db6b0aShenryc.chen 		dev_err(dev, "invalid compatible string\n");
172a0db6b0aShenryc.chen 		return -ENODEV;
173a0db6b0aShenryc.chen 	}
174a0db6b0aShenryc.chen 
175a0db6b0aShenryc.chen 	regulator_init_data = match->data;
176a0db6b0aShenryc.chen 
177a0db6b0aShenryc.chen 	mt_regulators = regulator_init_data->regulator_info;
178a0db6b0aShenryc.chen 	for (i = 0; i < regulator_init_data->size; i++) {
179a0db6b0aShenryc.chen 		config.dev = dev->parent;
180a0db6b0aShenryc.chen 		config.driver_data = (mt_regulators + i);
181ea986908SAxel Lin 		rdev = devm_regulator_register(dev, &(mt_regulators + i)->desc,
182a0db6b0aShenryc.chen 					       &config);
183a0db6b0aShenryc.chen 		if (IS_ERR(rdev)) {
184a0db6b0aShenryc.chen 			dev_err(dev, "failed to register %s\n",
185a0db6b0aShenryc.chen 				(mt_regulators + i)->desc.name);
186a0db6b0aShenryc.chen 			return PTR_ERR(rdev);
187a0db6b0aShenryc.chen 		}
188a0db6b0aShenryc.chen 	}
189a0db6b0aShenryc.chen 
190a0db6b0aShenryc.chen 	return 0;
191a0db6b0aShenryc.chen }
192a0db6b0aShenryc.chen 
193a0db6b0aShenryc.chen static struct platform_driver mtk_dvfsrc_regulator_driver = {
194a0db6b0aShenryc.chen 	.driver = {
195a0db6b0aShenryc.chen 		.name  = "mtk-dvfsrc-regulator",
19646600ab1SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
197a0db6b0aShenryc.chen 	},
198a0db6b0aShenryc.chen 	.probe = dvfsrc_vcore_regulator_probe,
199a0db6b0aShenryc.chen };
200a0db6b0aShenryc.chen 
mtk_dvfsrc_regulator_init(void)201a0db6b0aShenryc.chen static int __init mtk_dvfsrc_regulator_init(void)
202a0db6b0aShenryc.chen {
203a0db6b0aShenryc.chen 	return platform_driver_register(&mtk_dvfsrc_regulator_driver);
204a0db6b0aShenryc.chen }
205a0db6b0aShenryc.chen subsys_initcall(mtk_dvfsrc_regulator_init);
206a0db6b0aShenryc.chen 
mtk_dvfsrc_regulator_exit(void)207a0db6b0aShenryc.chen static void __exit mtk_dvfsrc_regulator_exit(void)
208a0db6b0aShenryc.chen {
209a0db6b0aShenryc.chen 	platform_driver_unregister(&mtk_dvfsrc_regulator_driver);
210a0db6b0aShenryc.chen }
211a0db6b0aShenryc.chen module_exit(mtk_dvfsrc_regulator_exit);
212a0db6b0aShenryc.chen 
213a0db6b0aShenryc.chen MODULE_AUTHOR("Arvin wang <arvin.wang@mediatek.com>");
214a0db6b0aShenryc.chen MODULE_LICENSE("GPL v2");
215