xref: /linux/drivers/regulator/cros-ec-regulator.c (revision 164666fa66669d437bdcc8d5f1744a2aee73be41)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright 2020 Google LLC.
4 
5 #include <linux/module.h>
6 #include <linux/of.h>
7 #include <linux/platform_data/cros_ec_proto.h>
8 #include <linux/platform_device.h>
9 #include <linux/regulator/driver.h>
10 #include <linux/regulator/machine.h>
11 #include <linux/regulator/of_regulator.h>
12 #include <linux/slab.h>
13 
14 struct cros_ec_regulator_data {
15 	struct regulator_desc desc;
16 	struct regulator_dev *dev;
17 	struct cros_ec_device *ec_dev;
18 
19 	u32 index;
20 
21 	u16 *voltages_mV;
22 	u16 num_voltages;
23 };
24 
25 static int cros_ec_cmd(struct cros_ec_device *ec, u32 version, u32 command,
26 		       void *outdata, u32 outsize, void *indata, u32 insize)
27 {
28 	struct cros_ec_command *msg;
29 	int ret;
30 
31 	msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
32 	if (!msg)
33 		return -ENOMEM;
34 
35 	msg->version = version;
36 	msg->command = command;
37 	msg->outsize = outsize;
38 	msg->insize = insize;
39 
40 	if (outdata && outsize > 0)
41 		memcpy(msg->data, outdata, outsize);
42 
43 	ret = cros_ec_cmd_xfer_status(ec, msg);
44 	if (ret < 0)
45 		goto cleanup;
46 
47 	if (insize)
48 		memcpy(indata, msg->data, insize);
49 
50 cleanup:
51 	kfree(msg);
52 	return ret;
53 }
54 
55 static int cros_ec_regulator_enable(struct regulator_dev *dev)
56 {
57 	struct cros_ec_regulator_data *data = rdev_get_drvdata(dev);
58 	struct ec_params_regulator_enable cmd = {
59 		.index = data->index,
60 		.enable = 1,
61 	};
62 
63 	return cros_ec_cmd(data->ec_dev, 0, EC_CMD_REGULATOR_ENABLE, &cmd,
64 			  sizeof(cmd), NULL, 0);
65 }
66 
67 static int cros_ec_regulator_disable(struct regulator_dev *dev)
68 {
69 	struct cros_ec_regulator_data *data = rdev_get_drvdata(dev);
70 	struct ec_params_regulator_enable cmd = {
71 		.index = data->index,
72 		.enable = 0,
73 	};
74 
75 	return cros_ec_cmd(data->ec_dev, 0, EC_CMD_REGULATOR_ENABLE, &cmd,
76 			  sizeof(cmd), NULL, 0);
77 }
78 
79 static int cros_ec_regulator_is_enabled(struct regulator_dev *dev)
80 {
81 	struct cros_ec_regulator_data *data = rdev_get_drvdata(dev);
82 	struct ec_params_regulator_is_enabled cmd = {
83 		.index = data->index,
84 	};
85 	struct ec_response_regulator_is_enabled resp;
86 	int ret;
87 
88 	ret = cros_ec_cmd(data->ec_dev, 0, EC_CMD_REGULATOR_IS_ENABLED, &cmd,
89 			  sizeof(cmd), &resp, sizeof(resp));
90 	if (ret < 0)
91 		return ret;
92 	return resp.enabled;
93 }
94 
95 static int cros_ec_regulator_list_voltage(struct regulator_dev *dev,
96 					  unsigned int selector)
97 {
98 	struct cros_ec_regulator_data *data = rdev_get_drvdata(dev);
99 
100 	if (selector >= data->num_voltages)
101 		return -EINVAL;
102 
103 	return data->voltages_mV[selector] * 1000;
104 }
105 
106 static int cros_ec_regulator_get_voltage(struct regulator_dev *dev)
107 {
108 	struct cros_ec_regulator_data *data = rdev_get_drvdata(dev);
109 	struct ec_params_regulator_get_voltage cmd = {
110 		.index = data->index,
111 	};
112 	struct ec_response_regulator_get_voltage resp;
113 	int ret;
114 
115 	ret = cros_ec_cmd(data->ec_dev, 0, EC_CMD_REGULATOR_GET_VOLTAGE, &cmd,
116 			  sizeof(cmd), &resp, sizeof(resp));
117 	if (ret < 0)
118 		return ret;
119 	return resp.voltage_mv * 1000;
120 }
121 
122 static int cros_ec_regulator_set_voltage(struct regulator_dev *dev, int min_uV,
123 					 int max_uV, unsigned int *selector)
124 {
125 	struct cros_ec_regulator_data *data = rdev_get_drvdata(dev);
126 	int min_mV = DIV_ROUND_UP(min_uV, 1000);
127 	int max_mV = max_uV / 1000;
128 	struct ec_params_regulator_set_voltage cmd = {
129 		.index = data->index,
130 		.min_mv = min_mV,
131 		.max_mv = max_mV,
132 	};
133 
134 	/*
135 	 * This can happen when the given range [min_uV, max_uV] doesn't
136 	 * contain any voltage that can be represented exactly in mV.
137 	 */
138 	if (min_mV > max_mV)
139 		return -EINVAL;
140 
141 	return cros_ec_cmd(data->ec_dev, 0, EC_CMD_REGULATOR_SET_VOLTAGE, &cmd,
142 			   sizeof(cmd), NULL, 0);
143 }
144 
145 static const struct regulator_ops cros_ec_regulator_voltage_ops = {
146 	.enable = cros_ec_regulator_enable,
147 	.disable = cros_ec_regulator_disable,
148 	.is_enabled = cros_ec_regulator_is_enabled,
149 	.list_voltage = cros_ec_regulator_list_voltage,
150 	.get_voltage = cros_ec_regulator_get_voltage,
151 	.set_voltage = cros_ec_regulator_set_voltage,
152 };
153 
154 static int cros_ec_regulator_init_info(struct device *dev,
155 				       struct cros_ec_regulator_data *data)
156 {
157 	struct ec_params_regulator_get_info cmd = {
158 		.index = data->index,
159 	};
160 	struct ec_response_regulator_get_info resp;
161 	int ret;
162 
163 	ret = cros_ec_cmd(data->ec_dev, 0, EC_CMD_REGULATOR_GET_INFO, &cmd,
164 			   sizeof(cmd), &resp, sizeof(resp));
165 	if (ret < 0)
166 		return ret;
167 
168 	data->num_voltages =
169 		min_t(u16, ARRAY_SIZE(resp.voltages_mv), resp.num_voltages);
170 	data->voltages_mV =
171 		devm_kmemdup(dev, resp.voltages_mv,
172 			     sizeof(u16) * data->num_voltages, GFP_KERNEL);
173 	if (!data->voltages_mV)
174 		return -ENOMEM;
175 
176 	data->desc.n_voltages = data->num_voltages;
177 
178 	/* Make sure the returned name is always a valid string */
179 	resp.name[ARRAY_SIZE(resp.name) - 1] = '\0';
180 	data->desc.name = devm_kstrdup(dev, resp.name, GFP_KERNEL);
181 	if (!data->desc.name)
182 		return -ENOMEM;
183 
184 	return 0;
185 }
186 
187 static int cros_ec_regulator_probe(struct platform_device *pdev)
188 {
189 	struct device *dev = &pdev->dev;
190 	struct device_node *np = dev->of_node;
191 	struct cros_ec_regulator_data *drvdata;
192 	struct regulator_init_data *init_data;
193 	struct regulator_config cfg = {};
194 	struct regulator_desc *desc;
195 	int ret;
196 
197 	drvdata = devm_kzalloc(
198 		&pdev->dev, sizeof(struct cros_ec_regulator_data), GFP_KERNEL);
199 	if (!drvdata)
200 		return -ENOMEM;
201 
202 	drvdata->ec_dev = dev_get_drvdata(dev->parent);
203 	desc = &drvdata->desc;
204 
205 	init_data = of_get_regulator_init_data(dev, np, desc);
206 	if (!init_data)
207 		return -EINVAL;
208 
209 	ret = of_property_read_u32(np, "reg", &drvdata->index);
210 	if (ret < 0)
211 		return ret;
212 
213 	desc->owner = THIS_MODULE;
214 	desc->type = REGULATOR_VOLTAGE;
215 	desc->ops = &cros_ec_regulator_voltage_ops;
216 
217 	ret = cros_ec_regulator_init_info(dev, drvdata);
218 	if (ret < 0)
219 		return ret;
220 
221 	cfg.dev = &pdev->dev;
222 	cfg.init_data = init_data;
223 	cfg.driver_data = drvdata;
224 	cfg.of_node = np;
225 
226 	drvdata->dev = devm_regulator_register(dev, &drvdata->desc, &cfg);
227 	if (IS_ERR(drvdata->dev)) {
228 		ret = PTR_ERR(drvdata->dev);
229 		dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret);
230 		return ret;
231 	}
232 
233 	platform_set_drvdata(pdev, drvdata);
234 
235 	return 0;
236 }
237 
238 static const struct of_device_id regulator_cros_ec_of_match[] = {
239 	{ .compatible = "google,cros-ec-regulator", },
240 	{}
241 };
242 MODULE_DEVICE_TABLE(of, regulator_cros_ec_of_match);
243 
244 static struct platform_driver cros_ec_regulator_driver = {
245 	.probe		= cros_ec_regulator_probe,
246 	.driver		= {
247 		.name		= "cros-ec-regulator",
248 		.of_match_table = regulator_cros_ec_of_match,
249 	},
250 };
251 
252 module_platform_driver(cros_ec_regulator_driver);
253 
254 MODULE_LICENSE("GPL v2");
255 MODULE_DESCRIPTION("ChromeOS EC controlled regulator");
256 MODULE_AUTHOR("Pi-Hsun Shih <pihsun@chromium.org>");
257