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 = ®ulator_mt8183_data,
146a0db6b0aShenryc.chen }, {
147a0db6b0aShenryc.chen .compatible = "mediatek,mt8192-dvfsrc",
148a0db6b0aShenryc.chen .data = ®ulator_mt6873_data,
149a0db6b0aShenryc.chen }, {
150a0db6b0aShenryc.chen .compatible = "mediatek,mt6873-dvfsrc",
151a0db6b0aShenryc.chen .data = ®ulator_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