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