Commit | Line | Data |
---|---|---|
f68b883e SN |
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 | ||
840d9f13 EBS |
8 | #include <linux/module.h> |
9 | #include <linux/platform_data/cros_ec_commands.h> | |
10 | #include <linux/platform_data/cros_ec_proto.h> | |
f2437e48 | 11 | #include <linux/platform_data/cros_usbpd_notify.h> |
f68b883e SN |
12 | #include <linux/platform_device.h> |
13 | #include <linux/power_supply.h> | |
14 | #include <linux/slab.h> | |
15 | ||
3af15cfa FP |
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)) | |
f68b883e SN |
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; | |
3af15cfa | 50 | int num_usbpd_ports; |
f68b883e SN |
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[] = { | |
2ffb500d EBS |
57 | POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, |
58 | POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, | |
f68b883e SN |
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 | ||
3af15cfa FP |
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 | ||
f68b883e SN |
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 | ||
2ffb500d EBS |
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 | ||
3af15cfa FP |
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 | ||
f68b883e SN |
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 | ||
441d38c6 | 107 | msg = kzalloc(struct_size(msg, data, max(outsize, insize)), GFP_KERNEL); |
f68b883e SN |
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(charger->ec_device, msg); | |
120 | if (ret >= 0 && insize) | |
121 | memcpy(indata, msg->data, insize); | |
122 | ||
123 | kfree(msg); | |
124 | return ret; | |
125 | } | |
126 | ||
127 | static int cros_usbpd_charger_get_num_ports(struct charger_data *charger) | |
3af15cfa FP |
128 | { |
129 | struct ec_response_charge_port_count resp; | |
130 | int ret; | |
131 | ||
132 | ret = cros_usbpd_charger_ec_command(charger, 0, | |
133 | EC_CMD_CHARGE_PORT_COUNT, | |
134 | NULL, 0, &resp, sizeof(resp)); | |
464aca16 | 135 | if (ret < 0) |
3af15cfa | 136 | return ret; |
3af15cfa FP |
137 | |
138 | return resp.port_count; | |
139 | } | |
140 | ||
141 | static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger) | |
f68b883e SN |
142 | { |
143 | struct ec_response_usb_pd_ports resp; | |
144 | int ret; | |
145 | ||
146 | ret = cros_usbpd_charger_ec_command(charger, 0, EC_CMD_USB_PD_PORTS, | |
147 | NULL, 0, &resp, sizeof(resp)); | |
464aca16 | 148 | if (ret < 0) |
f68b883e | 149 | return ret; |
f68b883e SN |
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, 0, | |
164 | EC_CMD_USB_PD_DISCOVERY, | |
165 | &req, sizeof(req), | |
166 | &resp, 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(port->manufacturer, sizeof(port->manufacturer), "%x", | |
177 | resp.vid); | |
178 | snprintf(port->model_name, sizeof(port->model_name), "%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, 0, | |
194 | EC_CMD_USB_PD_POWER_INFO, | |
195 | &req, sizeof(req), | |
196 | &resp, 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: | |
14c76b2e | 279 | dev_dbg(dev, "Port %d: default case!\n", port->port_number); |
f68b883e SN |
280 | port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; |
281 | } | |
282 | ||
3af15cfa FP |
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; | |
f68b883e SN |
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(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 | ||
3af15cfa FP |
321 | if (!cros_usbpd_charger_port_is_dedicated(port)) |
322 | ret = cros_usbpd_charger_get_discovery_info(port); | |
f68b883e SN |
323 | port->last_update = jiffies; |
324 | ||
325 | return ret; | |
326 | } | |
327 | ||
2ffb500d EBS |
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, 0, | |
339 | EC_CMD_EXTERNAL_POWER_LIMIT, | |
340 | &req, sizeof(req), NULL, 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 | ||
f68b883e SN |
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(charger->ports[i], 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; | |
df561f66 | 386 | fallthrough; |
f68b883e SN |
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, 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; | |
2ffb500d EBS |
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; | |
f68b883e SN |
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 | ||
2ffb500d EBS |
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, intval, | |
467 | 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 | input_current_limit, | |
483 | 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 | ||
f68b883e | 520 | static int cros_usbpd_charger_ec_event(struct notifier_block *nb, |
f2437e48 | 521 | unsigned long host_event, |
f68b883e SN |
522 | void *_notify) |
523 | { | |
f2437e48 JF |
524 | struct charger_data *charger = container_of(nb, struct charger_data, |
525 | notifier); | |
f68b883e | 526 | |
f2437e48 JF |
527 | cros_usbpd_charger_power_changed(charger->ports[0]->psy); |
528 | return NOTIFY_OK; | |
f68b883e SN |
529 | } |
530 | ||
531 | static void cros_usbpd_charger_unregister_notifier(void *data) | |
532 | { | |
533 | struct charger_data *charger = data; | |
f68b883e | 534 | |
f2437e48 | 535 | cros_usbpd_unregister_notify(&charger->notifier); |
f68b883e SN |
536 | } |
537 | ||
538 | static int cros_usbpd_charger_probe(struct platform_device *pd) | |
539 | { | |
540 | struct cros_ec_dev *ec_dev = dev_get_drvdata(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, 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(pd, charger); | |
560 | ||
3af15cfa FP |
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 | ||
f68b883e | 576 | charger->num_charger_ports = cros_usbpd_charger_get_num_ports(charger); |
3af15cfa | 577 | if (charger->num_charger_ports < 0) { |
f68b883e SN |
578 | /* |
579 | * This can happen on a system that doesn't support USB PD. | |
580 | * Log a message, but no need to warn. | |
3af15cfa FP |
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. | |
f68b883e SN |
594 | */ |
595 | dev_info(dev, "No charging ports found\n"); | |
596 | ret = -ENODEV; | |
597 | goto fail_nowarn; | |
598 | } | |
599 | ||
3af15cfa FP |
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 | ||
f68b883e SN |
611 | for (i = 0; i < charger->num_charger_ports; i++) { |
612 | struct power_supply_config psy_cfg = {}; | |
613 | ||
614 | port = devm_kzalloc(dev, 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; | |
f68b883e SN |
622 | |
623 | psy_desc = &port->psy_desc; | |
f68b883e | 624 | psy_desc->get_property = cros_usbpd_charger_get_prop; |
2ffb500d EBS |
625 | psy_desc->set_property = cros_usbpd_charger_set_prop; |
626 | psy_desc->property_is_writeable = | |
627 | cros_usbpd_charger_property_is_writeable; | |
f68b883e SN |
628 | psy_desc->external_power_changed = |
629 | cros_usbpd_charger_power_changed; | |
f68b883e SN |
630 | psy_cfg.drv_data = port; |
631 | ||
3af15cfa FP |
632 | if (cros_usbpd_charger_port_is_dedicated(port)) { |
633 | sprintf(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(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 | ||
f68b883e SN |
652 | psy = devm_power_supply_register_no_ws(dev, psy_desc, |
653 | &psy_cfg); | |
654 | if (IS_ERR(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 | ||
f2437e48 JF |
669 | /* Get PD events from the EC */ |
670 | charger->notifier.notifier_call = cros_usbpd_charger_ec_event; | |
671 | ret = cros_usbpd_register_notify(&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; | |
f68b883e SN |
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(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); |