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