1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Power supply driver for ChromeOS EC based USB PD Charger. |
4 | * |
5 | * Copyright (c) 2014 - 2018 Google, Inc |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/platform_data/cros_ec_commands.h> |
10 | #include <linux/platform_data/cros_ec_proto.h> |
11 | #include <linux/platform_data/cros_usbpd_notify.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/power_supply.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #define CHARGER_USBPD_DIR_NAME "CROS_USBPD_CHARGER%d" |
17 | #define CHARGER_DEDICATED_DIR_NAME "CROS_DEDICATED_CHARGER" |
18 | #define CHARGER_DIR_NAME_LENGTH (sizeof(CHARGER_USBPD_DIR_NAME) >= \ |
19 | sizeof(CHARGER_DEDICATED_DIR_NAME) ? \ |
20 | sizeof(CHARGER_USBPD_DIR_NAME) : \ |
21 | sizeof(CHARGER_DEDICATED_DIR_NAME)) |
22 | #define CHARGER_CACHE_UPDATE_DELAY msecs_to_jiffies(500) |
23 | #define CHARGER_MANUFACTURER_MODEL_LENGTH 32 |
24 | |
25 | #define DRV_NAME "cros-usbpd-charger" |
26 | |
27 | struct port_data { |
28 | int port_number; |
29 | char name[CHARGER_DIR_NAME_LENGTH]; |
30 | char manufacturer[CHARGER_MANUFACTURER_MODEL_LENGTH]; |
31 | char model_name[CHARGER_MANUFACTURER_MODEL_LENGTH]; |
32 | struct power_supply *psy; |
33 | struct power_supply_desc psy_desc; |
34 | int psy_usb_type; |
35 | int psy_online; |
36 | int psy_status; |
37 | int psy_current_max; |
38 | int psy_voltage_max_design; |
39 | int psy_voltage_now; |
40 | int psy_power_max; |
41 | struct charger_data *charger; |
42 | unsigned long last_update; |
43 | }; |
44 | |
45 | struct charger_data { |
46 | struct device *dev; |
47 | struct cros_ec_dev *ec_dev; |
48 | struct cros_ec_device *ec_device; |
49 | int num_charger_ports; |
50 | int num_usbpd_ports; |
51 | int num_registered_psy; |
52 | struct port_data *ports[EC_USB_PD_MAX_PORTS]; |
53 | struct notifier_block notifier; |
54 | }; |
55 | |
56 | static enum power_supply_property cros_usbpd_charger_props[] = { |
57 | POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, |
58 | POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, |
59 | POWER_SUPPLY_PROP_ONLINE, |
60 | POWER_SUPPLY_PROP_STATUS, |
61 | POWER_SUPPLY_PROP_CURRENT_MAX, |
62 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, |
63 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
64 | POWER_SUPPLY_PROP_MODEL_NAME, |
65 | POWER_SUPPLY_PROP_MANUFACTURER, |
66 | POWER_SUPPLY_PROP_USB_TYPE |
67 | }; |
68 | |
69 | static enum power_supply_property cros_usbpd_dedicated_charger_props[] = { |
70 | POWER_SUPPLY_PROP_ONLINE, |
71 | POWER_SUPPLY_PROP_STATUS, |
72 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
73 | }; |
74 | |
75 | static enum power_supply_usb_type cros_usbpd_charger_usb_types[] = { |
76 | POWER_SUPPLY_USB_TYPE_UNKNOWN, |
77 | POWER_SUPPLY_USB_TYPE_SDP, |
78 | POWER_SUPPLY_USB_TYPE_DCP, |
79 | POWER_SUPPLY_USB_TYPE_CDP, |
80 | POWER_SUPPLY_USB_TYPE_C, |
81 | POWER_SUPPLY_USB_TYPE_PD, |
82 | POWER_SUPPLY_USB_TYPE_PD_DRP, |
83 | POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID |
84 | }; |
85 | |
86 | /* Input voltage/current limit in mV/mA. Default to none. */ |
87 | static u16 input_voltage_limit = EC_POWER_LIMIT_NONE; |
88 | static u16 input_current_limit = EC_POWER_LIMIT_NONE; |
89 | |
90 | static bool cros_usbpd_charger_port_is_dedicated(struct port_data *port) |
91 | { |
92 | return port->port_number >= port->charger->num_usbpd_ports; |
93 | } |
94 | |
95 | static int cros_usbpd_charger_ec_command(struct charger_data *charger, |
96 | unsigned int version, |
97 | unsigned int command, |
98 | void *outdata, |
99 | unsigned int outsize, |
100 | void *indata, |
101 | unsigned int insize) |
102 | { |
103 | struct cros_ec_dev *ec_dev = charger->ec_dev; |
104 | struct cros_ec_command *msg; |
105 | int ret; |
106 | |
107 | msg = kzalloc(struct_size(msg, data, max(outsize, insize)), GFP_KERNEL); |
108 | if (!msg) |
109 | return -ENOMEM; |
110 | |
111 | msg->version = version; |
112 | msg->command = ec_dev->cmd_offset + command; |
113 | msg->outsize = outsize; |
114 | msg->insize = insize; |
115 | |
116 | if (outsize) |
117 | memcpy(msg->data, outdata, outsize); |
118 | |
119 | ret = cros_ec_cmd_xfer_status(ec_dev: charger->ec_device, msg); |
120 | if (ret >= 0 && insize) |
121 | memcpy(indata, msg->data, insize); |
122 | |
123 | kfree(objp: msg); |
124 | return ret; |
125 | } |
126 | |
127 | static int cros_usbpd_charger_get_num_ports(struct charger_data *charger) |
128 | { |
129 | struct ec_response_charge_port_count resp; |
130 | int ret; |
131 | |
132 | ret = cros_usbpd_charger_ec_command(charger, version: 0, |
133 | EC_CMD_CHARGE_PORT_COUNT, |
134 | NULL, outsize: 0, indata: &resp, insize: sizeof(resp)); |
135 | if (ret < 0) |
136 | return ret; |
137 | |
138 | return resp.port_count; |
139 | } |
140 | |
141 | static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger) |
142 | { |
143 | struct ec_response_usb_pd_ports resp; |
144 | int ret; |
145 | |
146 | ret = cros_usbpd_charger_ec_command(charger, version: 0, EC_CMD_USB_PD_PORTS, |
147 | NULL, outsize: 0, indata: &resp, insize: sizeof(resp)); |
148 | if (ret < 0) |
149 | return ret; |
150 | |
151 | return resp.num_ports; |
152 | } |
153 | |
154 | static int cros_usbpd_charger_get_discovery_info(struct port_data *port) |
155 | { |
156 | struct charger_data *charger = port->charger; |
157 | struct ec_params_usb_pd_discovery_entry resp; |
158 | struct ec_params_usb_pd_info_request req; |
159 | int ret; |
160 | |
161 | req.port = port->port_number; |
162 | |
163 | ret = cros_usbpd_charger_ec_command(charger, version: 0, |
164 | EC_CMD_USB_PD_DISCOVERY, |
165 | outdata: &req, outsize: sizeof(req), |
166 | indata: &resp, insize: sizeof(resp)); |
167 | if (ret < 0) { |
168 | dev_err(charger->dev, |
169 | "Unable to query discovery info (err:0x%x)\n" , ret); |
170 | return ret; |
171 | } |
172 | |
173 | dev_dbg(charger->dev, "Port %d: VID = 0x%x, PID=0x%x, PTYPE=0x%x\n" , |
174 | port->port_number, resp.vid, resp.pid, resp.ptype); |
175 | |
176 | snprintf(buf: port->manufacturer, size: sizeof(port->manufacturer), fmt: "%x" , |
177 | resp.vid); |
178 | snprintf(buf: port->model_name, size: sizeof(port->model_name), fmt: "%x" , resp.pid); |
179 | |
180 | return 0; |
181 | } |
182 | |
183 | static int cros_usbpd_charger_get_power_info(struct port_data *port) |
184 | { |
185 | struct charger_data *charger = port->charger; |
186 | struct ec_response_usb_pd_power_info resp; |
187 | struct ec_params_usb_pd_power_info req; |
188 | int last_psy_status, last_psy_usb_type; |
189 | struct device *dev = charger->dev; |
190 | int ret; |
191 | |
192 | req.port = port->port_number; |
193 | ret = cros_usbpd_charger_ec_command(charger, version: 0, |
194 | EC_CMD_USB_PD_POWER_INFO, |
195 | outdata: &req, outsize: sizeof(req), |
196 | indata: &resp, insize: sizeof(resp)); |
197 | if (ret < 0) { |
198 | dev_err(dev, "Unable to query PD power info (err:0x%x)\n" , ret); |
199 | return ret; |
200 | } |
201 | |
202 | last_psy_status = port->psy_status; |
203 | last_psy_usb_type = port->psy_usb_type; |
204 | |
205 | switch (resp.role) { |
206 | case USB_PD_PORT_POWER_DISCONNECTED: |
207 | port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
208 | port->psy_online = 0; |
209 | break; |
210 | case USB_PD_PORT_POWER_SOURCE: |
211 | port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
212 | port->psy_online = 0; |
213 | break; |
214 | case USB_PD_PORT_POWER_SINK: |
215 | port->psy_status = POWER_SUPPLY_STATUS_CHARGING; |
216 | port->psy_online = 1; |
217 | break; |
218 | case USB_PD_PORT_POWER_SINK_NOT_CHARGING: |
219 | port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
220 | port->psy_online = 1; |
221 | break; |
222 | default: |
223 | dev_err(dev, "Unknown role %d\n" , resp.role); |
224 | break; |
225 | } |
226 | |
227 | port->psy_voltage_max_design = resp.meas.voltage_max; |
228 | port->psy_voltage_now = resp.meas.voltage_now; |
229 | port->psy_current_max = resp.meas.current_max; |
230 | port->psy_power_max = resp.max_power; |
231 | |
232 | switch (resp.type) { |
233 | case USB_CHG_TYPE_BC12_SDP: |
234 | case USB_CHG_TYPE_VBUS: |
235 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; |
236 | break; |
237 | case USB_CHG_TYPE_NONE: |
238 | /* |
239 | * For dual-role devices when we are a source, the firmware |
240 | * reports the type as NONE. Report such chargers as type |
241 | * USB_PD_DRP. |
242 | */ |
243 | if (resp.role == USB_PD_PORT_POWER_SOURCE && resp.dualrole) |
244 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD_DRP; |
245 | else |
246 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; |
247 | break; |
248 | case USB_CHG_TYPE_OTHER: |
249 | case USB_CHG_TYPE_PROPRIETARY: |
250 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID; |
251 | break; |
252 | case USB_CHG_TYPE_C: |
253 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_C; |
254 | break; |
255 | case USB_CHG_TYPE_BC12_DCP: |
256 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP; |
257 | break; |
258 | case USB_CHG_TYPE_BC12_CDP: |
259 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP; |
260 | break; |
261 | case USB_CHG_TYPE_PD: |
262 | if (resp.dualrole) |
263 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD_DRP; |
264 | else |
265 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD; |
266 | break; |
267 | case USB_CHG_TYPE_UNKNOWN: |
268 | /* |
269 | * While the EC is trying to determine the type of charger that |
270 | * has been plugged in, it will report the charger type as |
271 | * unknown. Additionally since the power capabilities are |
272 | * unknown, report the max current and voltage as zero. |
273 | */ |
274 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; |
275 | port->psy_voltage_max_design = 0; |
276 | port->psy_current_max = 0; |
277 | break; |
278 | default: |
279 | dev_dbg(dev, "Port %d: default case!\n" , port->port_number); |
280 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; |
281 | } |
282 | |
283 | if (cros_usbpd_charger_port_is_dedicated(port)) |
284 | port->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; |
285 | else |
286 | port->psy_desc.type = POWER_SUPPLY_TYPE_USB; |
287 | |
288 | dev_dbg(dev, |
289 | "Port %d: type=%d vmax=%d vnow=%d cmax=%d clim=%d pmax=%d\n" , |
290 | port->port_number, resp.type, resp.meas.voltage_max, |
291 | resp.meas.voltage_now, resp.meas.current_max, |
292 | resp.meas.current_lim, resp.max_power); |
293 | |
294 | /* |
295 | * If power supply type or status changed, explicitly call |
296 | * power_supply_changed. This results in udev event getting generated |
297 | * and allows user mode apps to react quicker instead of waiting for |
298 | * their next poll of power supply status. |
299 | */ |
300 | if (last_psy_usb_type != port->psy_usb_type || |
301 | last_psy_status != port->psy_status) |
302 | power_supply_changed(psy: port->psy); |
303 | |
304 | return 0; |
305 | } |
306 | |
307 | static int cros_usbpd_charger_get_port_status(struct port_data *port, |
308 | bool ratelimit) |
309 | { |
310 | int ret; |
311 | |
312 | if (ratelimit && |
313 | time_is_after_jiffies(port->last_update + |
314 | CHARGER_CACHE_UPDATE_DELAY)) |
315 | return 0; |
316 | |
317 | ret = cros_usbpd_charger_get_power_info(port); |
318 | if (ret < 0) |
319 | return ret; |
320 | |
321 | if (!cros_usbpd_charger_port_is_dedicated(port)) |
322 | ret = cros_usbpd_charger_get_discovery_info(port); |
323 | port->last_update = jiffies; |
324 | |
325 | return ret; |
326 | } |
327 | |
328 | static int cros_usbpd_charger_set_ext_power_limit(struct charger_data *charger, |
329 | u16 current_lim, |
330 | u16 voltage_lim) |
331 | { |
332 | struct ec_params_external_power_limit_v1 req; |
333 | int ret; |
334 | |
335 | req.current_lim = current_lim; |
336 | req.voltage_lim = voltage_lim; |
337 | |
338 | ret = cros_usbpd_charger_ec_command(charger, version: 0, |
339 | EC_CMD_EXTERNAL_POWER_LIMIT, |
340 | outdata: &req, outsize: sizeof(req), NULL, insize: 0); |
341 | if (ret < 0) |
342 | dev_err(charger->dev, |
343 | "Unable to set the 'External Power Limit': %d\n" , ret); |
344 | |
345 | return ret; |
346 | } |
347 | |
348 | static void cros_usbpd_charger_power_changed(struct power_supply *psy) |
349 | { |
350 | struct port_data *port = power_supply_get_drvdata(psy); |
351 | struct charger_data *charger = port->charger; |
352 | int i; |
353 | |
354 | for (i = 0; i < charger->num_registered_psy; i++) |
355 | cros_usbpd_charger_get_port_status(port: charger->ports[i], ratelimit: false); |
356 | } |
357 | |
358 | static int cros_usbpd_charger_get_prop(struct power_supply *psy, |
359 | enum power_supply_property psp, |
360 | union power_supply_propval *val) |
361 | { |
362 | struct port_data *port = power_supply_get_drvdata(psy); |
363 | struct charger_data *charger = port->charger; |
364 | struct cros_ec_device *ec_device = charger->ec_device; |
365 | struct device *dev = charger->dev; |
366 | int ret; |
367 | |
368 | /* Only refresh ec_port_status for dynamic properties */ |
369 | switch (psp) { |
370 | case POWER_SUPPLY_PROP_ONLINE: |
371 | /* |
372 | * If mkbp_event_supported, then we can be assured that |
373 | * the driver's state for the online property is consistent |
374 | * with the hardware. However, if we aren't event driven, |
375 | * the optimization before to skip an ec_port_status get |
376 | * and only returned cached values of the online property will |
377 | * cause a delay in detecting a cable attach until one of the |
378 | * other properties are read. |
379 | * |
380 | * Allow an ec_port_status refresh for online property check |
381 | * if we're not already online to check for plug events if |
382 | * not mkbp_event_supported. |
383 | */ |
384 | if (ec_device->mkbp_event_supported || port->psy_online) |
385 | break; |
386 | fallthrough; |
387 | case POWER_SUPPLY_PROP_CURRENT_MAX: |
388 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: |
389 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
390 | ret = cros_usbpd_charger_get_port_status(port, ratelimit: true); |
391 | if (ret < 0) { |
392 | dev_err(dev, "Failed to get port status (err:0x%x)\n" , |
393 | ret); |
394 | return -EINVAL; |
395 | } |
396 | break; |
397 | default: |
398 | break; |
399 | } |
400 | |
401 | switch (psp) { |
402 | case POWER_SUPPLY_PROP_ONLINE: |
403 | val->intval = port->psy_online; |
404 | break; |
405 | case POWER_SUPPLY_PROP_STATUS: |
406 | val->intval = port->psy_status; |
407 | break; |
408 | case POWER_SUPPLY_PROP_CURRENT_MAX: |
409 | val->intval = port->psy_current_max * 1000; |
410 | break; |
411 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: |
412 | val->intval = port->psy_voltage_max_design * 1000; |
413 | break; |
414 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
415 | val->intval = port->psy_voltage_now * 1000; |
416 | break; |
417 | case POWER_SUPPLY_PROP_USB_TYPE: |
418 | val->intval = port->psy_usb_type; |
419 | break; |
420 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
421 | if (input_current_limit == EC_POWER_LIMIT_NONE) |
422 | val->intval = -1; |
423 | else |
424 | val->intval = input_current_limit * 1000; |
425 | break; |
426 | case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: |
427 | if (input_voltage_limit == EC_POWER_LIMIT_NONE) |
428 | val->intval = -1; |
429 | else |
430 | val->intval = input_voltage_limit * 1000; |
431 | break; |
432 | case POWER_SUPPLY_PROP_MODEL_NAME: |
433 | val->strval = port->model_name; |
434 | break; |
435 | case POWER_SUPPLY_PROP_MANUFACTURER: |
436 | val->strval = port->manufacturer; |
437 | break; |
438 | default: |
439 | return -EINVAL; |
440 | } |
441 | |
442 | return 0; |
443 | } |
444 | |
445 | static int cros_usbpd_charger_set_prop(struct power_supply *psy, |
446 | enum power_supply_property psp, |
447 | const union power_supply_propval *val) |
448 | { |
449 | struct port_data *port = power_supply_get_drvdata(psy); |
450 | struct charger_data *charger = port->charger; |
451 | struct device *dev = charger->dev; |
452 | u16 intval; |
453 | int ret; |
454 | |
455 | /* U16_MAX in mV/mA is the maximum supported value */ |
456 | if (val->intval >= U16_MAX * 1000) |
457 | return -EINVAL; |
458 | /* A negative number is used to clear the limit */ |
459 | if (val->intval < 0) |
460 | intval = EC_POWER_LIMIT_NONE; |
461 | else /* Convert from uA/uV to mA/mV */ |
462 | intval = val->intval / 1000; |
463 | |
464 | switch (psp) { |
465 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
466 | ret = cros_usbpd_charger_set_ext_power_limit(charger, current_lim: intval, |
467 | voltage_lim: input_voltage_limit); |
468 | if (ret < 0) |
469 | break; |
470 | |
471 | input_current_limit = intval; |
472 | if (input_current_limit == EC_POWER_LIMIT_NONE) |
473 | dev_info(dev, |
474 | "External Current Limit cleared for all ports\n" ); |
475 | else |
476 | dev_info(dev, |
477 | "External Current Limit set to %dmA for all ports\n" , |
478 | input_current_limit); |
479 | break; |
480 | case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: |
481 | ret = cros_usbpd_charger_set_ext_power_limit(charger, |
482 | current_lim: input_current_limit, |
483 | voltage_lim: intval); |
484 | if (ret < 0) |
485 | break; |
486 | |
487 | input_voltage_limit = intval; |
488 | if (input_voltage_limit == EC_POWER_LIMIT_NONE) |
489 | dev_info(dev, |
490 | "External Voltage Limit cleared for all ports\n" ); |
491 | else |
492 | dev_info(dev, |
493 | "External Voltage Limit set to %dmV for all ports\n" , |
494 | input_voltage_limit); |
495 | break; |
496 | default: |
497 | ret = -EINVAL; |
498 | } |
499 | |
500 | return ret; |
501 | } |
502 | |
503 | static int cros_usbpd_charger_property_is_writeable(struct power_supply *psy, |
504 | enum power_supply_property psp) |
505 | { |
506 | int ret; |
507 | |
508 | switch (psp) { |
509 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
510 | case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: |
511 | ret = 1; |
512 | break; |
513 | default: |
514 | ret = 0; |
515 | } |
516 | |
517 | return ret; |
518 | } |
519 | |
520 | static int cros_usbpd_charger_ec_event(struct notifier_block *nb, |
521 | unsigned long host_event, |
522 | void *_notify) |
523 | { |
524 | struct charger_data *charger = container_of(nb, struct charger_data, |
525 | notifier); |
526 | |
527 | cros_usbpd_charger_power_changed(psy: charger->ports[0]->psy); |
528 | return NOTIFY_OK; |
529 | } |
530 | |
531 | static void cros_usbpd_charger_unregister_notifier(void *data) |
532 | { |
533 | struct charger_data *charger = data; |
534 | |
535 | cros_usbpd_unregister_notify(nb: &charger->notifier); |
536 | } |
537 | |
538 | static int cros_usbpd_charger_probe(struct platform_device *pd) |
539 | { |
540 | struct cros_ec_dev *ec_dev = dev_get_drvdata(dev: pd->dev.parent); |
541 | struct cros_ec_device *ec_device = ec_dev->ec_dev; |
542 | struct power_supply_desc *psy_desc; |
543 | struct device *dev = &pd->dev; |
544 | struct charger_data *charger; |
545 | struct power_supply *psy; |
546 | struct port_data *port; |
547 | int ret = -EINVAL; |
548 | int i; |
549 | |
550 | charger = devm_kzalloc(dev, size: sizeof(struct charger_data), |
551 | GFP_KERNEL); |
552 | if (!charger) |
553 | return -ENOMEM; |
554 | |
555 | charger->dev = dev; |
556 | charger->ec_dev = ec_dev; |
557 | charger->ec_device = ec_device; |
558 | |
559 | platform_set_drvdata(pdev: pd, data: charger); |
560 | |
561 | /* |
562 | * We need to know the number of USB PD ports in order to know whether |
563 | * there is a dedicated port. The dedicated port will always be |
564 | * after the USB PD ports, and there should be only one. |
565 | */ |
566 | charger->num_usbpd_ports = |
567 | cros_usbpd_charger_get_usbpd_num_ports(charger); |
568 | if (charger->num_usbpd_ports <= 0) { |
569 | /* |
570 | * This can happen on a system that doesn't support USB PD. |
571 | * Log a message, but no need to warn. |
572 | */ |
573 | dev_info(dev, "No USB PD charging ports found\n" ); |
574 | } |
575 | |
576 | charger->num_charger_ports = cros_usbpd_charger_get_num_ports(charger); |
577 | if (charger->num_charger_ports < 0) { |
578 | /* |
579 | * This can happen on a system that doesn't support USB PD. |
580 | * Log a message, but no need to warn. |
581 | * Older ECs do not support the above command, in that case |
582 | * let's set up the number of charger ports equal to the number |
583 | * of USB PD ports |
584 | */ |
585 | dev_info(dev, "Could not get charger port count\n" ); |
586 | charger->num_charger_ports = charger->num_usbpd_ports; |
587 | } |
588 | |
589 | if (charger->num_charger_ports <= 0) { |
590 | /* |
591 | * This can happen on a system that doesn't support USB PD and |
592 | * doesn't have a dedicated port. |
593 | * Log a message, but no need to warn. |
594 | */ |
595 | dev_info(dev, "No charging ports found\n" ); |
596 | ret = -ENODEV; |
597 | goto fail_nowarn; |
598 | } |
599 | |
600 | /* |
601 | * Sanity checks on the number of ports: |
602 | * there should be at most 1 dedicated port |
603 | */ |
604 | if (charger->num_charger_ports < charger->num_usbpd_ports || |
605 | charger->num_charger_ports > (charger->num_usbpd_ports + 1)) { |
606 | dev_err(dev, "Unexpected number of charge port count\n" ); |
607 | ret = -EPROTO; |
608 | goto fail_nowarn; |
609 | } |
610 | |
611 | for (i = 0; i < charger->num_charger_ports; i++) { |
612 | struct power_supply_config psy_cfg = {}; |
613 | |
614 | port = devm_kzalloc(dev, size: sizeof(struct port_data), GFP_KERNEL); |
615 | if (!port) { |
616 | ret = -ENOMEM; |
617 | goto fail; |
618 | } |
619 | |
620 | port->charger = charger; |
621 | port->port_number = i; |
622 | |
623 | psy_desc = &port->psy_desc; |
624 | psy_desc->get_property = cros_usbpd_charger_get_prop; |
625 | psy_desc->set_property = cros_usbpd_charger_set_prop; |
626 | psy_desc->property_is_writeable = |
627 | cros_usbpd_charger_property_is_writeable; |
628 | psy_desc->external_power_changed = |
629 | cros_usbpd_charger_power_changed; |
630 | psy_cfg.drv_data = port; |
631 | |
632 | if (cros_usbpd_charger_port_is_dedicated(port)) { |
633 | sprintf(buf: port->name, CHARGER_DEDICATED_DIR_NAME); |
634 | psy_desc->type = POWER_SUPPLY_TYPE_MAINS; |
635 | psy_desc->properties = |
636 | cros_usbpd_dedicated_charger_props; |
637 | psy_desc->num_properties = |
638 | ARRAY_SIZE(cros_usbpd_dedicated_charger_props); |
639 | } else { |
640 | sprintf(buf: port->name, CHARGER_USBPD_DIR_NAME, i); |
641 | psy_desc->type = POWER_SUPPLY_TYPE_USB; |
642 | psy_desc->properties = cros_usbpd_charger_props; |
643 | psy_desc->num_properties = |
644 | ARRAY_SIZE(cros_usbpd_charger_props); |
645 | psy_desc->usb_types = cros_usbpd_charger_usb_types; |
646 | psy_desc->num_usb_types = |
647 | ARRAY_SIZE(cros_usbpd_charger_usb_types); |
648 | } |
649 | |
650 | psy_desc->name = port->name; |
651 | |
652 | psy = devm_power_supply_register_no_ws(parent: dev, desc: psy_desc, |
653 | cfg: &psy_cfg); |
654 | if (IS_ERR(ptr: psy)) { |
655 | dev_err(dev, "Failed to register power supply\n" ); |
656 | continue; |
657 | } |
658 | port->psy = psy; |
659 | |
660 | charger->ports[charger->num_registered_psy++] = port; |
661 | } |
662 | |
663 | if (!charger->num_registered_psy) { |
664 | ret = -ENODEV; |
665 | dev_err(dev, "No power supplies registered\n" ); |
666 | goto fail; |
667 | } |
668 | |
669 | /* Get PD events from the EC */ |
670 | charger->notifier.notifier_call = cros_usbpd_charger_ec_event; |
671 | ret = cros_usbpd_register_notify(nb: &charger->notifier); |
672 | if (ret < 0) { |
673 | dev_warn(dev, "failed to register notifier\n" ); |
674 | } else { |
675 | ret = devm_add_action_or_reset(dev, |
676 | cros_usbpd_charger_unregister_notifier, |
677 | charger); |
678 | if (ret < 0) |
679 | goto fail; |
680 | } |
681 | |
682 | return 0; |
683 | |
684 | fail: |
685 | WARN(1, "%s: Failing probe (err:0x%x)\n" , dev_name(dev), ret); |
686 | |
687 | fail_nowarn: |
688 | dev_info(dev, "Failing probe (err:0x%x)\n" , ret); |
689 | return ret; |
690 | } |
691 | |
692 | #ifdef CONFIG_PM_SLEEP |
693 | static int cros_usbpd_charger_resume(struct device *dev) |
694 | { |
695 | struct charger_data *charger = dev_get_drvdata(dev); |
696 | int i; |
697 | |
698 | if (!charger) |
699 | return 0; |
700 | |
701 | for (i = 0; i < charger->num_registered_psy; i++) { |
702 | power_supply_changed(psy: charger->ports[i]->psy); |
703 | charger->ports[i]->last_update = |
704 | jiffies - CHARGER_CACHE_UPDATE_DELAY; |
705 | } |
706 | |
707 | return 0; |
708 | } |
709 | #endif |
710 | |
711 | static SIMPLE_DEV_PM_OPS(cros_usbpd_charger_pm_ops, NULL, |
712 | cros_usbpd_charger_resume); |
713 | |
714 | static struct platform_driver cros_usbpd_charger_driver = { |
715 | .driver = { |
716 | .name = DRV_NAME, |
717 | .pm = &cros_usbpd_charger_pm_ops, |
718 | }, |
719 | .probe = cros_usbpd_charger_probe |
720 | }; |
721 | |
722 | module_platform_driver(cros_usbpd_charger_driver); |
723 | |
724 | MODULE_LICENSE("GPL" ); |
725 | MODULE_DESCRIPTION("ChromeOS EC USBPD charger" ); |
726 | MODULE_ALIAS("platform:" DRV_NAME); |
727 | |