1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Expose a PWM controlled by the ChromeOS EC to the host processor. |
4 | * |
5 | * Copyright (C) 2016 Google, Inc. |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/of.h> |
10 | #include <linux/platform_data/cros_ec_commands.h> |
11 | #include <linux/platform_data/cros_ec_proto.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/pwm.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #include <dt-bindings/mfd/cros_ec.h> |
17 | |
18 | /** |
19 | * struct cros_ec_pwm_device - Driver data for EC PWM |
20 | * |
21 | * @dev: Device node |
22 | * @ec: Pointer to EC device |
23 | * @chip: PWM controller chip |
24 | * @use_pwm_type: Use PWM types instead of generic channels |
25 | */ |
26 | struct cros_ec_pwm_device { |
27 | struct device *dev; |
28 | struct cros_ec_device *ec; |
29 | struct pwm_chip chip; |
30 | bool use_pwm_type; |
31 | }; |
32 | |
33 | /** |
34 | * struct cros_ec_pwm - per-PWM driver data |
35 | * @duty_cycle: cached duty cycle |
36 | */ |
37 | struct cros_ec_pwm { |
38 | u16 duty_cycle; |
39 | }; |
40 | |
41 | static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *chip) |
42 | { |
43 | return container_of(chip, struct cros_ec_pwm_device, chip); |
44 | } |
45 | |
46 | static int cros_ec_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) |
47 | { |
48 | struct cros_ec_pwm *channel; |
49 | |
50 | channel = kzalloc(size: sizeof(*channel), GFP_KERNEL); |
51 | if (!channel) |
52 | return -ENOMEM; |
53 | |
54 | pwm_set_chip_data(pwm, data: channel); |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static void cros_ec_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) |
60 | { |
61 | struct cros_ec_pwm *channel = pwm_get_chip_data(pwm); |
62 | |
63 | kfree(objp: channel); |
64 | } |
65 | |
66 | static int cros_ec_dt_type_to_pwm_type(u8 dt_index, u8 *pwm_type) |
67 | { |
68 | switch (dt_index) { |
69 | case CROS_EC_PWM_DT_KB_LIGHT: |
70 | *pwm_type = EC_PWM_TYPE_KB_LIGHT; |
71 | return 0; |
72 | case CROS_EC_PWM_DT_DISPLAY_LIGHT: |
73 | *pwm_type = EC_PWM_TYPE_DISPLAY_LIGHT; |
74 | return 0; |
75 | default: |
76 | return -EINVAL; |
77 | } |
78 | } |
79 | |
80 | static int cros_ec_pwm_set_duty(struct cros_ec_pwm_device *ec_pwm, u8 index, |
81 | u16 duty) |
82 | { |
83 | struct cros_ec_device *ec = ec_pwm->ec; |
84 | struct { |
85 | struct cros_ec_command msg; |
86 | struct ec_params_pwm_set_duty params; |
87 | } __packed buf; |
88 | struct ec_params_pwm_set_duty *params = &buf.params; |
89 | struct cros_ec_command *msg = &buf.msg; |
90 | int ret; |
91 | |
92 | memset(&buf, 0, sizeof(buf)); |
93 | |
94 | msg->version = 0; |
95 | msg->command = EC_CMD_PWM_SET_DUTY; |
96 | msg->insize = 0; |
97 | msg->outsize = sizeof(*params); |
98 | |
99 | params->duty = duty; |
100 | |
101 | if (ec_pwm->use_pwm_type) { |
102 | ret = cros_ec_dt_type_to_pwm_type(dt_index: index, pwm_type: ¶ms->pwm_type); |
103 | if (ret) { |
104 | dev_err(ec->dev, "Invalid PWM type index: %d\n" , index); |
105 | return ret; |
106 | } |
107 | params->index = 0; |
108 | } else { |
109 | params->pwm_type = EC_PWM_TYPE_GENERIC; |
110 | params->index = index; |
111 | } |
112 | |
113 | return cros_ec_cmd_xfer_status(ec_dev: ec, msg); |
114 | } |
115 | |
116 | static int cros_ec_pwm_get_duty(struct cros_ec_pwm_device *ec_pwm, u8 index) |
117 | { |
118 | struct cros_ec_device *ec = ec_pwm->ec; |
119 | struct { |
120 | struct cros_ec_command msg; |
121 | union { |
122 | struct ec_params_pwm_get_duty params; |
123 | struct ec_response_pwm_get_duty resp; |
124 | }; |
125 | } __packed buf; |
126 | struct ec_params_pwm_get_duty *params = &buf.params; |
127 | struct ec_response_pwm_get_duty *resp = &buf.resp; |
128 | struct cros_ec_command *msg = &buf.msg; |
129 | int ret; |
130 | |
131 | memset(&buf, 0, sizeof(buf)); |
132 | |
133 | msg->version = 0; |
134 | msg->command = EC_CMD_PWM_GET_DUTY; |
135 | msg->insize = sizeof(*resp); |
136 | msg->outsize = sizeof(*params); |
137 | |
138 | if (ec_pwm->use_pwm_type) { |
139 | ret = cros_ec_dt_type_to_pwm_type(dt_index: index, pwm_type: ¶ms->pwm_type); |
140 | if (ret) { |
141 | dev_err(ec->dev, "Invalid PWM type index: %d\n" , index); |
142 | return ret; |
143 | } |
144 | params->index = 0; |
145 | } else { |
146 | params->pwm_type = EC_PWM_TYPE_GENERIC; |
147 | params->index = index; |
148 | } |
149 | |
150 | ret = cros_ec_cmd_xfer_status(ec_dev: ec, msg); |
151 | if (ret < 0) |
152 | return ret; |
153 | |
154 | return resp->duty; |
155 | } |
156 | |
157 | static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, |
158 | const struct pwm_state *state) |
159 | { |
160 | struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); |
161 | struct cros_ec_pwm *channel = pwm_get_chip_data(pwm); |
162 | u16 duty_cycle; |
163 | int ret; |
164 | |
165 | /* The EC won't let us change the period */ |
166 | if (state->period != EC_PWM_MAX_DUTY) |
167 | return -EINVAL; |
168 | |
169 | if (state->polarity != PWM_POLARITY_NORMAL) |
170 | return -EINVAL; |
171 | |
172 | /* |
173 | * EC doesn't separate the concept of duty cycle and enabled, but |
174 | * kernel does. Translate. |
175 | */ |
176 | duty_cycle = state->enabled ? state->duty_cycle : 0; |
177 | |
178 | ret = cros_ec_pwm_set_duty(ec_pwm, index: pwm->hwpwm, duty: duty_cycle); |
179 | if (ret < 0) |
180 | return ret; |
181 | |
182 | channel->duty_cycle = state->duty_cycle; |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, |
188 | struct pwm_state *state) |
189 | { |
190 | struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); |
191 | struct cros_ec_pwm *channel = pwm_get_chip_data(pwm); |
192 | int ret; |
193 | |
194 | ret = cros_ec_pwm_get_duty(ec_pwm, index: pwm->hwpwm); |
195 | if (ret < 0) { |
196 | dev_err(chip->dev, "error getting initial duty: %d\n" , ret); |
197 | return ret; |
198 | } |
199 | |
200 | state->enabled = (ret > 0); |
201 | state->period = EC_PWM_MAX_DUTY; |
202 | state->polarity = PWM_POLARITY_NORMAL; |
203 | |
204 | /* |
205 | * Note that "disabled" and "duty cycle == 0" are treated the same. If |
206 | * the cached duty cycle is not zero, used the cached duty cycle. This |
207 | * ensures that the configured duty cycle is kept across a disable and |
208 | * enable operation and avoids potentially confusing consumers. |
209 | * |
210 | * For the case of the initial hardware readout, channel->duty_cycle |
211 | * will be 0 and the actual duty cycle read from the EC is used. |
212 | */ |
213 | if (ret == 0 && channel->duty_cycle > 0) |
214 | state->duty_cycle = channel->duty_cycle; |
215 | else |
216 | state->duty_cycle = ret; |
217 | |
218 | return 0; |
219 | } |
220 | |
221 | static struct pwm_device * |
222 | cros_ec_pwm_xlate(struct pwm_chip *chip, const struct of_phandle_args *args) |
223 | { |
224 | struct pwm_device *pwm; |
225 | |
226 | if (args->args[0] >= chip->npwm) |
227 | return ERR_PTR(error: -EINVAL); |
228 | |
229 | pwm = pwm_request_from_chip(chip, index: args->args[0], NULL); |
230 | if (IS_ERR(ptr: pwm)) |
231 | return pwm; |
232 | |
233 | /* The EC won't let us change the period */ |
234 | pwm->args.period = EC_PWM_MAX_DUTY; |
235 | |
236 | return pwm; |
237 | } |
238 | |
239 | static const struct pwm_ops cros_ec_pwm_ops = { |
240 | .request = cros_ec_pwm_request, |
241 | .free = cros_ec_pwm_free, |
242 | .get_state = cros_ec_pwm_get_state, |
243 | .apply = cros_ec_pwm_apply, |
244 | .owner = THIS_MODULE, |
245 | }; |
246 | |
247 | /* |
248 | * Determine the number of supported PWMs. The EC does not return the number |
249 | * of PWMs it supports directly, so we have to read the pwm duty cycle for |
250 | * subsequent channels until we get an error. |
251 | */ |
252 | static int cros_ec_num_pwms(struct cros_ec_pwm_device *ec_pwm) |
253 | { |
254 | int i, ret; |
255 | |
256 | /* The index field is only 8 bits */ |
257 | for (i = 0; i <= U8_MAX; i++) { |
258 | ret = cros_ec_pwm_get_duty(ec_pwm, index: i); |
259 | /* |
260 | * We look for SUCCESS, INVALID_COMMAND, or INVALID_PARAM |
261 | * responses; everything else is treated as an error. |
262 | * The EC error codes map to -EOPNOTSUPP and -EINVAL, |
263 | * so check for those. |
264 | */ |
265 | switch (ret) { |
266 | case -EOPNOTSUPP: /* invalid command */ |
267 | return -ENODEV; |
268 | case -EINVAL: /* invalid parameter */ |
269 | return i; |
270 | default: |
271 | if (ret < 0) |
272 | return ret; |
273 | break; |
274 | } |
275 | } |
276 | |
277 | return U8_MAX; |
278 | } |
279 | |
280 | static int cros_ec_pwm_probe(struct platform_device *pdev) |
281 | { |
282 | struct cros_ec_device *ec = dev_get_drvdata(dev: pdev->dev.parent); |
283 | struct device *dev = &pdev->dev; |
284 | struct device_node *np = pdev->dev.of_node; |
285 | struct cros_ec_pwm_device *ec_pwm; |
286 | struct pwm_chip *chip; |
287 | int ret; |
288 | |
289 | if (!ec) { |
290 | dev_err(dev, "no parent EC device\n" ); |
291 | return -EINVAL; |
292 | } |
293 | |
294 | ec_pwm = devm_kzalloc(dev, size: sizeof(*ec_pwm), GFP_KERNEL); |
295 | if (!ec_pwm) |
296 | return -ENOMEM; |
297 | chip = &ec_pwm->chip; |
298 | ec_pwm->ec = ec; |
299 | |
300 | if (of_device_is_compatible(device: np, "google,cros-ec-pwm-type" )) |
301 | ec_pwm->use_pwm_type = true; |
302 | |
303 | /* PWM chip */ |
304 | chip->dev = dev; |
305 | chip->ops = &cros_ec_pwm_ops; |
306 | chip->of_xlate = cros_ec_pwm_xlate; |
307 | chip->of_pwm_n_cells = 1; |
308 | |
309 | if (ec_pwm->use_pwm_type) { |
310 | chip->npwm = CROS_EC_PWM_DT_COUNT; |
311 | } else { |
312 | ret = cros_ec_num_pwms(ec_pwm); |
313 | if (ret < 0) { |
314 | dev_err(dev, "Couldn't find PWMs: %d\n" , ret); |
315 | return ret; |
316 | } |
317 | chip->npwm = ret; |
318 | } |
319 | |
320 | dev_dbg(dev, "Probed %u PWMs\n" , chip->npwm); |
321 | |
322 | ret = pwmchip_add(chip); |
323 | if (ret < 0) { |
324 | dev_err(dev, "cannot register PWM: %d\n" , ret); |
325 | return ret; |
326 | } |
327 | |
328 | platform_set_drvdata(pdev, data: ec_pwm); |
329 | |
330 | return ret; |
331 | } |
332 | |
333 | static void cros_ec_pwm_remove(struct platform_device *dev) |
334 | { |
335 | struct cros_ec_pwm_device *ec_pwm = platform_get_drvdata(pdev: dev); |
336 | struct pwm_chip *chip = &ec_pwm->chip; |
337 | |
338 | pwmchip_remove(chip); |
339 | } |
340 | |
341 | #ifdef CONFIG_OF |
342 | static const struct of_device_id cros_ec_pwm_of_match[] = { |
343 | { .compatible = "google,cros-ec-pwm" }, |
344 | { .compatible = "google,cros-ec-pwm-type" }, |
345 | {}, |
346 | }; |
347 | MODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match); |
348 | #endif |
349 | |
350 | static struct platform_driver cros_ec_pwm_driver = { |
351 | .probe = cros_ec_pwm_probe, |
352 | .remove_new = cros_ec_pwm_remove, |
353 | .driver = { |
354 | .name = "cros-ec-pwm" , |
355 | .of_match_table = of_match_ptr(cros_ec_pwm_of_match), |
356 | }, |
357 | }; |
358 | module_platform_driver(cros_ec_pwm_driver); |
359 | |
360 | MODULE_ALIAS("platform:cros-ec-pwm" ); |
361 | MODULE_DESCRIPTION("ChromeOS EC PWM driver" ); |
362 | MODULE_LICENSE("GPL v2" ); |
363 | |