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_regulator_enable(struct regulator_dev *dev) |
26 | { |
27 | struct cros_ec_regulator_data *data = rdev_get_drvdata(rdev: dev); |
28 | struct ec_params_regulator_enable cmd = { |
29 | .index = data->index, |
30 | .enable = 1, |
31 | }; |
32 | |
33 | return cros_ec_cmd(ec_dev: data->ec_dev, version: 0, EC_CMD_REGULATOR_ENABLE, outdata: &cmd, |
34 | outsize: sizeof(cmd), NULL, insize: 0); |
35 | } |
36 | |
37 | static int cros_ec_regulator_disable(struct regulator_dev *dev) |
38 | { |
39 | struct cros_ec_regulator_data *data = rdev_get_drvdata(rdev: dev); |
40 | struct ec_params_regulator_enable cmd = { |
41 | .index = data->index, |
42 | .enable = 0, |
43 | }; |
44 | |
45 | return cros_ec_cmd(ec_dev: data->ec_dev, version: 0, EC_CMD_REGULATOR_ENABLE, outdata: &cmd, |
46 | outsize: sizeof(cmd), NULL, insize: 0); |
47 | } |
48 | |
49 | static int cros_ec_regulator_is_enabled(struct regulator_dev *dev) |
50 | { |
51 | struct cros_ec_regulator_data *data = rdev_get_drvdata(rdev: dev); |
52 | struct ec_params_regulator_is_enabled cmd = { |
53 | .index = data->index, |
54 | }; |
55 | struct ec_response_regulator_is_enabled resp; |
56 | int ret; |
57 | |
58 | ret = cros_ec_cmd(ec_dev: data->ec_dev, version: 0, EC_CMD_REGULATOR_IS_ENABLED, outdata: &cmd, |
59 | outsize: sizeof(cmd), indata: &resp, insize: sizeof(resp)); |
60 | if (ret < 0) |
61 | return ret; |
62 | return resp.enabled; |
63 | } |
64 | |
65 | static int cros_ec_regulator_list_voltage(struct regulator_dev *dev, |
66 | unsigned int selector) |
67 | { |
68 | struct cros_ec_regulator_data *data = rdev_get_drvdata(rdev: dev); |
69 | |
70 | if (selector >= data->num_voltages) |
71 | return -EINVAL; |
72 | |
73 | return data->voltages_mV[selector] * 1000; |
74 | } |
75 | |
76 | static int cros_ec_regulator_get_voltage(struct regulator_dev *dev) |
77 | { |
78 | struct cros_ec_regulator_data *data = rdev_get_drvdata(rdev: dev); |
79 | struct ec_params_regulator_get_voltage cmd = { |
80 | .index = data->index, |
81 | }; |
82 | struct ec_response_regulator_get_voltage resp; |
83 | int ret; |
84 | |
85 | ret = cros_ec_cmd(ec_dev: data->ec_dev, version: 0, EC_CMD_REGULATOR_GET_VOLTAGE, outdata: &cmd, |
86 | outsize: sizeof(cmd), indata: &resp, insize: sizeof(resp)); |
87 | if (ret < 0) |
88 | return ret; |
89 | return resp.voltage_mv * 1000; |
90 | } |
91 | |
92 | static int cros_ec_regulator_set_voltage(struct regulator_dev *dev, int min_uV, |
93 | int max_uV, unsigned int *selector) |
94 | { |
95 | struct cros_ec_regulator_data *data = rdev_get_drvdata(rdev: dev); |
96 | int min_mV = DIV_ROUND_UP(min_uV, 1000); |
97 | int max_mV = max_uV / 1000; |
98 | struct ec_params_regulator_set_voltage cmd = { |
99 | .index = data->index, |
100 | .min_mv = min_mV, |
101 | .max_mv = max_mV, |
102 | }; |
103 | |
104 | /* |
105 | * This can happen when the given range [min_uV, max_uV] doesn't |
106 | * contain any voltage that can be represented exactly in mV. |
107 | */ |
108 | if (min_mV > max_mV) |
109 | return -EINVAL; |
110 | |
111 | return cros_ec_cmd(ec_dev: data->ec_dev, version: 0, EC_CMD_REGULATOR_SET_VOLTAGE, outdata: &cmd, |
112 | outsize: sizeof(cmd), NULL, insize: 0); |
113 | } |
114 | |
115 | static const struct regulator_ops cros_ec_regulator_voltage_ops = { |
116 | .enable = cros_ec_regulator_enable, |
117 | .disable = cros_ec_regulator_disable, |
118 | .is_enabled = cros_ec_regulator_is_enabled, |
119 | .list_voltage = cros_ec_regulator_list_voltage, |
120 | .get_voltage = cros_ec_regulator_get_voltage, |
121 | .set_voltage = cros_ec_regulator_set_voltage, |
122 | }; |
123 | |
124 | static int cros_ec_regulator_init_info(struct device *dev, |
125 | struct cros_ec_regulator_data *data) |
126 | { |
127 | struct ec_params_regulator_get_info cmd = { |
128 | .index = data->index, |
129 | }; |
130 | struct ec_response_regulator_get_info resp; |
131 | int ret; |
132 | |
133 | ret = cros_ec_cmd(ec_dev: data->ec_dev, version: 0, EC_CMD_REGULATOR_GET_INFO, outdata: &cmd, |
134 | outsize: sizeof(cmd), indata: &resp, insize: sizeof(resp)); |
135 | if (ret < 0) |
136 | return ret; |
137 | |
138 | data->num_voltages = |
139 | min_t(u16, ARRAY_SIZE(resp.voltages_mv), resp.num_voltages); |
140 | data->voltages_mV = |
141 | devm_kmemdup(dev, src: resp.voltages_mv, |
142 | len: sizeof(u16) * data->num_voltages, GFP_KERNEL); |
143 | if (!data->voltages_mV) |
144 | return -ENOMEM; |
145 | |
146 | data->desc.n_voltages = data->num_voltages; |
147 | |
148 | /* Make sure the returned name is always a valid string */ |
149 | resp.name[ARRAY_SIZE(resp.name) - 1] = '\0'; |
150 | data->desc.name = devm_kstrdup(dev, s: resp.name, GFP_KERNEL); |
151 | if (!data->desc.name) |
152 | return -ENOMEM; |
153 | |
154 | return 0; |
155 | } |
156 | |
157 | static int cros_ec_regulator_probe(struct platform_device *pdev) |
158 | { |
159 | struct device *dev = &pdev->dev; |
160 | struct device_node *np = dev->of_node; |
161 | struct cros_ec_regulator_data *drvdata; |
162 | struct regulator_init_data *init_data; |
163 | struct regulator_config cfg = {}; |
164 | struct regulator_desc *desc; |
165 | int ret; |
166 | |
167 | drvdata = devm_kzalloc( |
168 | dev: &pdev->dev, size: sizeof(struct cros_ec_regulator_data), GFP_KERNEL); |
169 | if (!drvdata) |
170 | return -ENOMEM; |
171 | |
172 | drvdata->ec_dev = dev_get_drvdata(dev: dev->parent); |
173 | desc = &drvdata->desc; |
174 | |
175 | init_data = of_get_regulator_init_data(dev, node: np, desc); |
176 | if (!init_data) |
177 | return -EINVAL; |
178 | |
179 | ret = of_property_read_u32(np, propname: "reg" , out_value: &drvdata->index); |
180 | if (ret < 0) |
181 | return ret; |
182 | |
183 | desc->owner = THIS_MODULE; |
184 | desc->type = REGULATOR_VOLTAGE; |
185 | desc->ops = &cros_ec_regulator_voltage_ops; |
186 | |
187 | ret = cros_ec_regulator_init_info(dev, data: drvdata); |
188 | if (ret < 0) |
189 | return ret; |
190 | |
191 | cfg.dev = &pdev->dev; |
192 | cfg.init_data = init_data; |
193 | cfg.driver_data = drvdata; |
194 | cfg.of_node = np; |
195 | |
196 | drvdata->dev = devm_regulator_register(dev, regulator_desc: &drvdata->desc, config: &cfg); |
197 | if (IS_ERR(ptr: drvdata->dev)) { |
198 | ret = PTR_ERR(ptr: drvdata->dev); |
199 | dev_err(&pdev->dev, "Failed to register regulator: %d\n" , ret); |
200 | return ret; |
201 | } |
202 | |
203 | platform_set_drvdata(pdev, data: drvdata); |
204 | |
205 | return 0; |
206 | } |
207 | |
208 | static const struct of_device_id regulator_cros_ec_of_match[] = { |
209 | { .compatible = "google,cros-ec-regulator" , }, |
210 | {} |
211 | }; |
212 | MODULE_DEVICE_TABLE(of, regulator_cros_ec_of_match); |
213 | |
214 | static struct platform_driver cros_ec_regulator_driver = { |
215 | .probe = cros_ec_regulator_probe, |
216 | .driver = { |
217 | .name = "cros-ec-regulator" , |
218 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
219 | .of_match_table = regulator_cros_ec_of_match, |
220 | }, |
221 | }; |
222 | |
223 | module_platform_driver(cros_ec_regulator_driver); |
224 | |
225 | MODULE_LICENSE("GPL v2" ); |
226 | MODULE_DESCRIPTION("ChromeOS EC controlled regulator" ); |
227 | MODULE_AUTHOR("Pi-Hsun Shih <pihsun@chromium.org>" ); |
228 | |