xref: /linux/drivers/power/supply/cros_usbpd-charger.c (revision 85d46148f8a07aa80199e52a2755304a27837caa)
1f68b883eSSameer Nanda // SPDX-License-Identifier: GPL-2.0+
2f68b883eSSameer Nanda /*
3f68b883eSSameer Nanda  * Power supply driver for ChromeOS EC based USB PD Charger.
4f68b883eSSameer Nanda  *
5f68b883eSSameer Nanda  * Copyright (c) 2014 - 2018 Google, Inc
6f68b883eSSameer Nanda  */
7f68b883eSSameer Nanda 
8*0f8678c3STzung-Bi Shih #include <linux/mod_devicetable.h>
9840d9f13SEnric Balletbo i Serra #include <linux/module.h>
10840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_commands.h>
11840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_proto.h>
12f2437e48SJon Flatley #include <linux/platform_data/cros_usbpd_notify.h>
13f68b883eSSameer Nanda #include <linux/platform_device.h>
14f68b883eSSameer Nanda #include <linux/power_supply.h>
15f68b883eSSameer Nanda #include <linux/slab.h>
16f68b883eSSameer Nanda 
173af15cfaSFabien Parent #define CHARGER_USBPD_DIR_NAME			"CROS_USBPD_CHARGER%d"
183af15cfaSFabien Parent #define CHARGER_DEDICATED_DIR_NAME		"CROS_DEDICATED_CHARGER"
193af15cfaSFabien Parent #define CHARGER_DIR_NAME_LENGTH		(sizeof(CHARGER_USBPD_DIR_NAME) >= \
203af15cfaSFabien Parent 					 sizeof(CHARGER_DEDICATED_DIR_NAME) ? \
213af15cfaSFabien Parent 					 sizeof(CHARGER_USBPD_DIR_NAME) : \
223af15cfaSFabien Parent 					 sizeof(CHARGER_DEDICATED_DIR_NAME))
23f68b883eSSameer Nanda #define CHARGER_CACHE_UPDATE_DELAY		msecs_to_jiffies(500)
24f68b883eSSameer Nanda #define CHARGER_MANUFACTURER_MODEL_LENGTH	32
25f68b883eSSameer Nanda 
26f68b883eSSameer Nanda #define DRV_NAME "cros-usbpd-charger"
27f68b883eSSameer Nanda 
28f68b883eSSameer Nanda struct port_data {
29f68b883eSSameer Nanda 	int port_number;
30f68b883eSSameer Nanda 	char name[CHARGER_DIR_NAME_LENGTH];
31f68b883eSSameer Nanda 	char manufacturer[CHARGER_MANUFACTURER_MODEL_LENGTH];
32f68b883eSSameer Nanda 	char model_name[CHARGER_MANUFACTURER_MODEL_LENGTH];
33f68b883eSSameer Nanda 	struct power_supply *psy;
34f68b883eSSameer Nanda 	struct power_supply_desc psy_desc;
35f68b883eSSameer Nanda 	int psy_usb_type;
36f68b883eSSameer Nanda 	int psy_online;
37f68b883eSSameer Nanda 	int psy_status;
38f68b883eSSameer Nanda 	int psy_current_max;
39f68b883eSSameer Nanda 	int psy_voltage_max_design;
40f68b883eSSameer Nanda 	int psy_voltage_now;
41f68b883eSSameer Nanda 	int psy_power_max;
42f68b883eSSameer Nanda 	struct charger_data *charger;
43f68b883eSSameer Nanda 	unsigned long last_update;
44f68b883eSSameer Nanda };
45f68b883eSSameer Nanda 
46f68b883eSSameer Nanda struct charger_data {
47f68b883eSSameer Nanda 	struct device *dev;
48f68b883eSSameer Nanda 	struct cros_ec_dev *ec_dev;
49f68b883eSSameer Nanda 	struct cros_ec_device *ec_device;
50f68b883eSSameer Nanda 	int num_charger_ports;
513af15cfaSFabien Parent 	int num_usbpd_ports;
52f68b883eSSameer Nanda 	int num_registered_psy;
53f68b883eSSameer Nanda 	struct port_data *ports[EC_USB_PD_MAX_PORTS];
54f68b883eSSameer Nanda 	struct notifier_block notifier;
55f68b883eSSameer Nanda };
56f68b883eSSameer Nanda 
57f68b883eSSameer Nanda static enum power_supply_property cros_usbpd_charger_props[] = {
582ffb500dSEnric Balletbo i Serra 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
592ffb500dSEnric Balletbo i Serra 	POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
60f68b883eSSameer Nanda 	POWER_SUPPLY_PROP_ONLINE,
61f68b883eSSameer Nanda 	POWER_SUPPLY_PROP_STATUS,
62f68b883eSSameer Nanda 	POWER_SUPPLY_PROP_CURRENT_MAX,
63f68b883eSSameer Nanda 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
64f68b883eSSameer Nanda 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
65f68b883eSSameer Nanda 	POWER_SUPPLY_PROP_MODEL_NAME,
66f68b883eSSameer Nanda 	POWER_SUPPLY_PROP_MANUFACTURER,
67f68b883eSSameer Nanda 	POWER_SUPPLY_PROP_USB_TYPE
68f68b883eSSameer Nanda };
69f68b883eSSameer Nanda 
703af15cfaSFabien Parent static enum power_supply_property cros_usbpd_dedicated_charger_props[] = {
713af15cfaSFabien Parent 	POWER_SUPPLY_PROP_ONLINE,
723af15cfaSFabien Parent 	POWER_SUPPLY_PROP_STATUS,
733af15cfaSFabien Parent 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
743af15cfaSFabien Parent };
753af15cfaSFabien Parent 
76f68b883eSSameer Nanda static enum power_supply_usb_type cros_usbpd_charger_usb_types[] = {
77f68b883eSSameer Nanda 	POWER_SUPPLY_USB_TYPE_UNKNOWN,
78f68b883eSSameer Nanda 	POWER_SUPPLY_USB_TYPE_SDP,
79f68b883eSSameer Nanda 	POWER_SUPPLY_USB_TYPE_DCP,
80f68b883eSSameer Nanda 	POWER_SUPPLY_USB_TYPE_CDP,
81f68b883eSSameer Nanda 	POWER_SUPPLY_USB_TYPE_C,
82f68b883eSSameer Nanda 	POWER_SUPPLY_USB_TYPE_PD,
83f68b883eSSameer Nanda 	POWER_SUPPLY_USB_TYPE_PD_DRP,
84f68b883eSSameer Nanda 	POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID
85f68b883eSSameer Nanda };
86f68b883eSSameer Nanda 
872ffb500dSEnric Balletbo i Serra /* Input voltage/current limit in mV/mA. Default to none. */
882ffb500dSEnric Balletbo i Serra static u16 input_voltage_limit = EC_POWER_LIMIT_NONE;
892ffb500dSEnric Balletbo i Serra static u16 input_current_limit = EC_POWER_LIMIT_NONE;
902ffb500dSEnric Balletbo i Serra 
cros_usbpd_charger_port_is_dedicated(struct port_data * port)913af15cfaSFabien Parent static bool cros_usbpd_charger_port_is_dedicated(struct port_data *port)
923af15cfaSFabien Parent {
933af15cfaSFabien Parent 	return port->port_number >= port->charger->num_usbpd_ports;
943af15cfaSFabien Parent }
953af15cfaSFabien Parent 
cros_usbpd_charger_ec_command(struct charger_data * charger,unsigned int version,unsigned int command,void * outdata,unsigned int outsize,void * indata,unsigned int insize)96f68b883eSSameer Nanda static int cros_usbpd_charger_ec_command(struct charger_data *charger,
97f68b883eSSameer Nanda 					 unsigned int version,
98f68b883eSSameer Nanda 					 unsigned int command,
99f68b883eSSameer Nanda 					 void *outdata,
100f68b883eSSameer Nanda 					 unsigned int outsize,
101f68b883eSSameer Nanda 					 void *indata,
102f68b883eSSameer Nanda 					 unsigned int insize)
103f68b883eSSameer Nanda {
104f68b883eSSameer Nanda 	struct cros_ec_dev *ec_dev = charger->ec_dev;
105f68b883eSSameer Nanda 	struct cros_ec_command *msg;
106f68b883eSSameer Nanda 	int ret;
107f68b883eSSameer Nanda 
108441d38c6SGustavo A. R. Silva 	msg = kzalloc(struct_size(msg, data, max(outsize, insize)), GFP_KERNEL);
109f68b883eSSameer Nanda 	if (!msg)
110f68b883eSSameer Nanda 		return -ENOMEM;
111f68b883eSSameer Nanda 
112f68b883eSSameer Nanda 	msg->version = version;
113f68b883eSSameer Nanda 	msg->command = ec_dev->cmd_offset + command;
114f68b883eSSameer Nanda 	msg->outsize = outsize;
115f68b883eSSameer Nanda 	msg->insize = insize;
116f68b883eSSameer Nanda 
117f68b883eSSameer Nanda 	if (outsize)
118f68b883eSSameer Nanda 		memcpy(msg->data, outdata, outsize);
119f68b883eSSameer Nanda 
120f68b883eSSameer Nanda 	ret = cros_ec_cmd_xfer_status(charger->ec_device, msg);
121f68b883eSSameer Nanda 	if (ret >= 0 && insize)
122f68b883eSSameer Nanda 		memcpy(indata, msg->data, insize);
123f68b883eSSameer Nanda 
124f68b883eSSameer Nanda 	kfree(msg);
125f68b883eSSameer Nanda 	return ret;
126f68b883eSSameer Nanda }
127f68b883eSSameer Nanda 
cros_usbpd_charger_get_num_ports(struct charger_data * charger)128f68b883eSSameer Nanda static int cros_usbpd_charger_get_num_ports(struct charger_data *charger)
129f68b883eSSameer Nanda {
1303af15cfaSFabien Parent 	struct ec_response_charge_port_count resp;
1313af15cfaSFabien Parent 	int ret;
1323af15cfaSFabien Parent 
1333af15cfaSFabien Parent 	ret = cros_usbpd_charger_ec_command(charger, 0,
1343af15cfaSFabien Parent 					    EC_CMD_CHARGE_PORT_COUNT,
1353af15cfaSFabien Parent 					    NULL, 0, &resp, sizeof(resp));
136464aca16SEnric Balletbo i Serra 	if (ret < 0)
1373af15cfaSFabien Parent 		return ret;
1383af15cfaSFabien Parent 
1393af15cfaSFabien Parent 	return resp.port_count;
1403af15cfaSFabien Parent }
1413af15cfaSFabien Parent 
cros_usbpd_charger_get_usbpd_num_ports(struct charger_data * charger)1423af15cfaSFabien Parent static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger)
1433af15cfaSFabien Parent {
144f68b883eSSameer Nanda 	struct ec_response_usb_pd_ports resp;
145f68b883eSSameer Nanda 	int ret;
146f68b883eSSameer Nanda 
147f68b883eSSameer Nanda 	ret = cros_usbpd_charger_ec_command(charger, 0, EC_CMD_USB_PD_PORTS,
148f68b883eSSameer Nanda 					    NULL, 0, &resp, sizeof(resp));
149464aca16SEnric Balletbo i Serra 	if (ret < 0)
150f68b883eSSameer Nanda 		return ret;
151f68b883eSSameer Nanda 
152f68b883eSSameer Nanda 	return resp.num_ports;
153f68b883eSSameer Nanda }
154f68b883eSSameer Nanda 
cros_usbpd_charger_get_discovery_info(struct port_data * port)155f68b883eSSameer Nanda static int cros_usbpd_charger_get_discovery_info(struct port_data *port)
156f68b883eSSameer Nanda {
157f68b883eSSameer Nanda 	struct charger_data *charger = port->charger;
158f68b883eSSameer Nanda 	struct ec_params_usb_pd_discovery_entry resp;
159f68b883eSSameer Nanda 	struct ec_params_usb_pd_info_request req;
160f68b883eSSameer Nanda 	int ret;
161f68b883eSSameer Nanda 
162f68b883eSSameer Nanda 	req.port = port->port_number;
163f68b883eSSameer Nanda 
164f68b883eSSameer Nanda 	ret = cros_usbpd_charger_ec_command(charger, 0,
165f68b883eSSameer Nanda 					    EC_CMD_USB_PD_DISCOVERY,
166f68b883eSSameer Nanda 					    &req, sizeof(req),
167f68b883eSSameer Nanda 					    &resp, sizeof(resp));
168f68b883eSSameer Nanda 	if (ret < 0) {
169f68b883eSSameer Nanda 		dev_err(charger->dev,
170f68b883eSSameer Nanda 			"Unable to query discovery info (err:0x%x)\n", ret);
171f68b883eSSameer Nanda 		return ret;
172f68b883eSSameer Nanda 	}
173f68b883eSSameer Nanda 
174f68b883eSSameer Nanda 	dev_dbg(charger->dev, "Port %d: VID = 0x%x, PID=0x%x, PTYPE=0x%x\n",
175f68b883eSSameer Nanda 		port->port_number, resp.vid, resp.pid, resp.ptype);
176f68b883eSSameer Nanda 
177f68b883eSSameer Nanda 	snprintf(port->manufacturer, sizeof(port->manufacturer), "%x",
178f68b883eSSameer Nanda 		 resp.vid);
179f68b883eSSameer Nanda 	snprintf(port->model_name, sizeof(port->model_name), "%x", resp.pid);
180f68b883eSSameer Nanda 
181f68b883eSSameer Nanda 	return 0;
182f68b883eSSameer Nanda }
183f68b883eSSameer Nanda 
cros_usbpd_charger_get_power_info(struct port_data * port)184f68b883eSSameer Nanda static int cros_usbpd_charger_get_power_info(struct port_data *port)
185f68b883eSSameer Nanda {
186f68b883eSSameer Nanda 	struct charger_data *charger = port->charger;
187f68b883eSSameer Nanda 	struct ec_response_usb_pd_power_info resp;
188f68b883eSSameer Nanda 	struct ec_params_usb_pd_power_info req;
189f68b883eSSameer Nanda 	int last_psy_status, last_psy_usb_type;
190f68b883eSSameer Nanda 	struct device *dev = charger->dev;
191f68b883eSSameer Nanda 	int ret;
192f68b883eSSameer Nanda 
193f68b883eSSameer Nanda 	req.port = port->port_number;
194f68b883eSSameer Nanda 	ret = cros_usbpd_charger_ec_command(charger, 0,
195f68b883eSSameer Nanda 					    EC_CMD_USB_PD_POWER_INFO,
196f68b883eSSameer Nanda 					    &req, sizeof(req),
197f68b883eSSameer Nanda 					    &resp, sizeof(resp));
198f68b883eSSameer Nanda 	if (ret < 0) {
199f68b883eSSameer Nanda 		dev_err(dev, "Unable to query PD power info (err:0x%x)\n", ret);
200f68b883eSSameer Nanda 		return ret;
201f68b883eSSameer Nanda 	}
202f68b883eSSameer Nanda 
203f68b883eSSameer Nanda 	last_psy_status = port->psy_status;
204f68b883eSSameer Nanda 	last_psy_usb_type = port->psy_usb_type;
205f68b883eSSameer Nanda 
206f68b883eSSameer Nanda 	switch (resp.role) {
207f68b883eSSameer Nanda 	case USB_PD_PORT_POWER_DISCONNECTED:
208f68b883eSSameer Nanda 		port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
209f68b883eSSameer Nanda 		port->psy_online = 0;
210f68b883eSSameer Nanda 		break;
211f68b883eSSameer Nanda 	case USB_PD_PORT_POWER_SOURCE:
212f68b883eSSameer Nanda 		port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
213f68b883eSSameer Nanda 		port->psy_online = 0;
214f68b883eSSameer Nanda 		break;
215f68b883eSSameer Nanda 	case USB_PD_PORT_POWER_SINK:
216f68b883eSSameer Nanda 		port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
217f68b883eSSameer Nanda 		port->psy_online = 1;
218f68b883eSSameer Nanda 		break;
219f68b883eSSameer Nanda 	case USB_PD_PORT_POWER_SINK_NOT_CHARGING:
220f68b883eSSameer Nanda 		port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
221f68b883eSSameer Nanda 		port->psy_online = 1;
222f68b883eSSameer Nanda 		break;
223f68b883eSSameer Nanda 	default:
224f68b883eSSameer Nanda 		dev_err(dev, "Unknown role %d\n", resp.role);
225f68b883eSSameer Nanda 		break;
226f68b883eSSameer Nanda 	}
227f68b883eSSameer Nanda 
228f68b883eSSameer Nanda 	port->psy_voltage_max_design = resp.meas.voltage_max;
229f68b883eSSameer Nanda 	port->psy_voltage_now = resp.meas.voltage_now;
230f68b883eSSameer Nanda 	port->psy_current_max = resp.meas.current_max;
231f68b883eSSameer Nanda 	port->psy_power_max = resp.max_power;
232f68b883eSSameer Nanda 
233f68b883eSSameer Nanda 	switch (resp.type) {
234f68b883eSSameer Nanda 	case USB_CHG_TYPE_BC12_SDP:
235f68b883eSSameer Nanda 	case USB_CHG_TYPE_VBUS:
236f68b883eSSameer Nanda 		port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
237f68b883eSSameer Nanda 		break;
238f68b883eSSameer Nanda 	case USB_CHG_TYPE_NONE:
239f68b883eSSameer Nanda 		/*
240f68b883eSSameer Nanda 		 * For dual-role devices when we are a source, the firmware
241f68b883eSSameer Nanda 		 * reports the type as NONE. Report such chargers as type
242f68b883eSSameer Nanda 		 * USB_PD_DRP.
243f68b883eSSameer Nanda 		 */
244f68b883eSSameer Nanda 		if (resp.role == USB_PD_PORT_POWER_SOURCE && resp.dualrole)
245f68b883eSSameer Nanda 			port->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD_DRP;
246f68b883eSSameer Nanda 		else
247f68b883eSSameer Nanda 			port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
248f68b883eSSameer Nanda 		break;
249f68b883eSSameer Nanda 	case USB_CHG_TYPE_OTHER:
250f68b883eSSameer Nanda 	case USB_CHG_TYPE_PROPRIETARY:
251f68b883eSSameer Nanda 		port->psy_usb_type = POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID;
252f68b883eSSameer Nanda 		break;
253f68b883eSSameer Nanda 	case USB_CHG_TYPE_C:
254f68b883eSSameer Nanda 		port->psy_usb_type = POWER_SUPPLY_USB_TYPE_C;
255f68b883eSSameer Nanda 		break;
256f68b883eSSameer Nanda 	case USB_CHG_TYPE_BC12_DCP:
257f68b883eSSameer Nanda 		port->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP;
258f68b883eSSameer Nanda 		break;
259f68b883eSSameer Nanda 	case USB_CHG_TYPE_BC12_CDP:
260f68b883eSSameer Nanda 		port->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP;
261f68b883eSSameer Nanda 		break;
262f68b883eSSameer Nanda 	case USB_CHG_TYPE_PD:
263f68b883eSSameer Nanda 		if (resp.dualrole)
264f68b883eSSameer Nanda 			port->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD_DRP;
265f68b883eSSameer Nanda 		else
266f68b883eSSameer Nanda 			port->psy_usb_type = POWER_SUPPLY_USB_TYPE_PD;
267f68b883eSSameer Nanda 		break;
268f68b883eSSameer Nanda 	case USB_CHG_TYPE_UNKNOWN:
269f68b883eSSameer Nanda 		/*
270f68b883eSSameer Nanda 		 * While the EC is trying to determine the type of charger that
271f68b883eSSameer Nanda 		 * has been plugged in, it will report the charger type as
272f68b883eSSameer Nanda 		 * unknown. Additionally since the power capabilities are
273f68b883eSSameer Nanda 		 * unknown, report the max current and voltage as zero.
274f68b883eSSameer Nanda 		 */
275f68b883eSSameer Nanda 		port->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
276f68b883eSSameer Nanda 		port->psy_voltage_max_design = 0;
277f68b883eSSameer Nanda 		port->psy_current_max = 0;
278f68b883eSSameer Nanda 		break;
279f68b883eSSameer Nanda 	default:
28014c76b2eSGrant Grundler 		dev_dbg(dev, "Port %d: default case!\n", port->port_number);
281f68b883eSSameer Nanda 		port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
282f68b883eSSameer Nanda 	}
283f68b883eSSameer Nanda 
2843af15cfaSFabien Parent 	if (cros_usbpd_charger_port_is_dedicated(port))
2853af15cfaSFabien Parent 		port->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
2863af15cfaSFabien Parent 	else
287f68b883eSSameer Nanda 		port->psy_desc.type = POWER_SUPPLY_TYPE_USB;
288f68b883eSSameer Nanda 
289f68b883eSSameer Nanda 	dev_dbg(dev,
290f68b883eSSameer Nanda 		"Port %d: type=%d vmax=%d vnow=%d cmax=%d clim=%d pmax=%d\n",
291f68b883eSSameer Nanda 		port->port_number, resp.type, resp.meas.voltage_max,
292f68b883eSSameer Nanda 		resp.meas.voltage_now, resp.meas.current_max,
293f68b883eSSameer Nanda 		resp.meas.current_lim, resp.max_power);
294f68b883eSSameer Nanda 
295f68b883eSSameer Nanda 	/*
296f68b883eSSameer Nanda 	 * If power supply type or status changed, explicitly call
297f68b883eSSameer Nanda 	 * power_supply_changed. This results in udev event getting generated
298f68b883eSSameer Nanda 	 * and allows user mode apps to react quicker instead of waiting for
299f68b883eSSameer Nanda 	 * their next poll of power supply status.
300f68b883eSSameer Nanda 	 */
301f68b883eSSameer Nanda 	if (last_psy_usb_type != port->psy_usb_type ||
302f68b883eSSameer Nanda 	    last_psy_status != port->psy_status)
303f68b883eSSameer Nanda 		power_supply_changed(port->psy);
304f68b883eSSameer Nanda 
305f68b883eSSameer Nanda 	return 0;
306f68b883eSSameer Nanda }
307f68b883eSSameer Nanda 
cros_usbpd_charger_get_port_status(struct port_data * port,bool ratelimit)308f68b883eSSameer Nanda static int cros_usbpd_charger_get_port_status(struct port_data *port,
309f68b883eSSameer Nanda 					      bool ratelimit)
310f68b883eSSameer Nanda {
311f68b883eSSameer Nanda 	int ret;
312f68b883eSSameer Nanda 
313f68b883eSSameer Nanda 	if (ratelimit &&
314f68b883eSSameer Nanda 	    time_is_after_jiffies(port->last_update +
315f68b883eSSameer Nanda 				  CHARGER_CACHE_UPDATE_DELAY))
316f68b883eSSameer Nanda 		return 0;
317f68b883eSSameer Nanda 
318f68b883eSSameer Nanda 	ret = cros_usbpd_charger_get_power_info(port);
319f68b883eSSameer Nanda 	if (ret < 0)
320f68b883eSSameer Nanda 		return ret;
321f68b883eSSameer Nanda 
3223af15cfaSFabien Parent 	if (!cros_usbpd_charger_port_is_dedicated(port))
323f68b883eSSameer Nanda 		ret = cros_usbpd_charger_get_discovery_info(port);
324f68b883eSSameer Nanda 	port->last_update = jiffies;
325f68b883eSSameer Nanda 
326f68b883eSSameer Nanda 	return ret;
327f68b883eSSameer Nanda }
328f68b883eSSameer Nanda 
cros_usbpd_charger_set_ext_power_limit(struct charger_data * charger,u16 current_lim,u16 voltage_lim)3292ffb500dSEnric Balletbo i Serra static int cros_usbpd_charger_set_ext_power_limit(struct charger_data *charger,
3302ffb500dSEnric Balletbo i Serra 						  u16 current_lim,
3312ffb500dSEnric Balletbo i Serra 						  u16 voltage_lim)
3322ffb500dSEnric Balletbo i Serra {
3332ffb500dSEnric Balletbo i Serra 	struct ec_params_external_power_limit_v1 req;
3342ffb500dSEnric Balletbo i Serra 	int ret;
3352ffb500dSEnric Balletbo i Serra 
3362ffb500dSEnric Balletbo i Serra 	req.current_lim = current_lim;
3372ffb500dSEnric Balletbo i Serra 	req.voltage_lim = voltage_lim;
3382ffb500dSEnric Balletbo i Serra 
3392ffb500dSEnric Balletbo i Serra 	ret = cros_usbpd_charger_ec_command(charger, 0,
3402ffb500dSEnric Balletbo i Serra 					    EC_CMD_EXTERNAL_POWER_LIMIT,
3412ffb500dSEnric Balletbo i Serra 					    &req, sizeof(req), NULL, 0);
3422ffb500dSEnric Balletbo i Serra 	if (ret < 0)
3432ffb500dSEnric Balletbo i Serra 		dev_err(charger->dev,
3442ffb500dSEnric Balletbo i Serra 			"Unable to set the 'External Power Limit': %d\n", ret);
3452ffb500dSEnric Balletbo i Serra 
3462ffb500dSEnric Balletbo i Serra 	return ret;
3472ffb500dSEnric Balletbo i Serra }
3482ffb500dSEnric Balletbo i Serra 
cros_usbpd_charger_power_changed(struct power_supply * psy)349f68b883eSSameer Nanda static void cros_usbpd_charger_power_changed(struct power_supply *psy)
350f68b883eSSameer Nanda {
351f68b883eSSameer Nanda 	struct port_data *port = power_supply_get_drvdata(psy);
352f68b883eSSameer Nanda 	struct charger_data *charger = port->charger;
353f68b883eSSameer Nanda 	int i;
354f68b883eSSameer Nanda 
355f68b883eSSameer Nanda 	for (i = 0; i < charger->num_registered_psy; i++)
356f68b883eSSameer Nanda 		cros_usbpd_charger_get_port_status(charger->ports[i], false);
357f68b883eSSameer Nanda }
358f68b883eSSameer Nanda 
cros_usbpd_charger_get_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)359f68b883eSSameer Nanda static int cros_usbpd_charger_get_prop(struct power_supply *psy,
360f68b883eSSameer Nanda 				       enum power_supply_property psp,
361f68b883eSSameer Nanda 				       union power_supply_propval *val)
362f68b883eSSameer Nanda {
363f68b883eSSameer Nanda 	struct port_data *port = power_supply_get_drvdata(psy);
364f68b883eSSameer Nanda 	struct charger_data *charger = port->charger;
365f68b883eSSameer Nanda 	struct cros_ec_device *ec_device = charger->ec_device;
366f68b883eSSameer Nanda 	struct device *dev = charger->dev;
367f68b883eSSameer Nanda 	int ret;
368f68b883eSSameer Nanda 
369f68b883eSSameer Nanda 	/* Only refresh ec_port_status for dynamic properties */
370f68b883eSSameer Nanda 	switch (psp) {
371f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_ONLINE:
372f68b883eSSameer Nanda 		/*
373f68b883eSSameer Nanda 		 * If mkbp_event_supported, then we can be assured that
374f68b883eSSameer Nanda 		 * the driver's state for the online property is consistent
375f68b883eSSameer Nanda 		 * with the hardware. However, if we aren't event driven,
376f68b883eSSameer Nanda 		 * the optimization before to skip an ec_port_status get
377f68b883eSSameer Nanda 		 * and only returned cached values of the online property will
378f68b883eSSameer Nanda 		 * cause a delay in detecting a cable attach until one of the
379f68b883eSSameer Nanda 		 * other properties are read.
380f68b883eSSameer Nanda 		 *
381f68b883eSSameer Nanda 		 * Allow an ec_port_status refresh for online property check
382f68b883eSSameer Nanda 		 * if we're not already online to check for plug events if
383f68b883eSSameer Nanda 		 * not mkbp_event_supported.
384f68b883eSSameer Nanda 		 */
385f68b883eSSameer Nanda 		if (ec_device->mkbp_event_supported || port->psy_online)
386f68b883eSSameer Nanda 			break;
387df561f66SGustavo A. R. Silva 		fallthrough;
388f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_CURRENT_MAX:
389f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
390f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
391f68b883eSSameer Nanda 		ret = cros_usbpd_charger_get_port_status(port, true);
392f68b883eSSameer Nanda 		if (ret < 0) {
393f68b883eSSameer Nanda 			dev_err(dev, "Failed to get port status (err:0x%x)\n",
394f68b883eSSameer Nanda 				ret);
395f68b883eSSameer Nanda 			return -EINVAL;
396f68b883eSSameer Nanda 		}
397f68b883eSSameer Nanda 		break;
398f68b883eSSameer Nanda 	default:
399f68b883eSSameer Nanda 		break;
400f68b883eSSameer Nanda 	}
401f68b883eSSameer Nanda 
402f68b883eSSameer Nanda 	switch (psp) {
403f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_ONLINE:
404f68b883eSSameer Nanda 		val->intval = port->psy_online;
405f68b883eSSameer Nanda 		break;
406f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_STATUS:
407f68b883eSSameer Nanda 		val->intval = port->psy_status;
408f68b883eSSameer Nanda 		break;
409f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_CURRENT_MAX:
410f68b883eSSameer Nanda 		val->intval = port->psy_current_max * 1000;
411f68b883eSSameer Nanda 		break;
412f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
413f68b883eSSameer Nanda 		val->intval = port->psy_voltage_max_design * 1000;
414f68b883eSSameer Nanda 		break;
415f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
416f68b883eSSameer Nanda 		val->intval = port->psy_voltage_now * 1000;
417f68b883eSSameer Nanda 		break;
418f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_USB_TYPE:
419f68b883eSSameer Nanda 		val->intval = port->psy_usb_type;
420f68b883eSSameer Nanda 		break;
4212ffb500dSEnric Balletbo i Serra 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
4222ffb500dSEnric Balletbo i Serra 		if (input_current_limit == EC_POWER_LIMIT_NONE)
4232ffb500dSEnric Balletbo i Serra 			val->intval = -1;
4242ffb500dSEnric Balletbo i Serra 		else
4252ffb500dSEnric Balletbo i Serra 			val->intval = input_current_limit * 1000;
4262ffb500dSEnric Balletbo i Serra 		break;
4272ffb500dSEnric Balletbo i Serra 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
4282ffb500dSEnric Balletbo i Serra 		if (input_voltage_limit == EC_POWER_LIMIT_NONE)
4292ffb500dSEnric Balletbo i Serra 			val->intval = -1;
4302ffb500dSEnric Balletbo i Serra 		else
4312ffb500dSEnric Balletbo i Serra 			val->intval = input_voltage_limit * 1000;
4322ffb500dSEnric Balletbo i Serra 		break;
433f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_MODEL_NAME:
434f68b883eSSameer Nanda 		val->strval = port->model_name;
435f68b883eSSameer Nanda 		break;
436f68b883eSSameer Nanda 	case POWER_SUPPLY_PROP_MANUFACTURER:
437f68b883eSSameer Nanda 		val->strval = port->manufacturer;
438f68b883eSSameer Nanda 		break;
439f68b883eSSameer Nanda 	default:
440f68b883eSSameer Nanda 		return -EINVAL;
441f68b883eSSameer Nanda 	}
442f68b883eSSameer Nanda 
443f68b883eSSameer Nanda 	return 0;
444f68b883eSSameer Nanda }
445f68b883eSSameer Nanda 
cros_usbpd_charger_set_prop(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)4462ffb500dSEnric Balletbo i Serra static int cros_usbpd_charger_set_prop(struct power_supply *psy,
4472ffb500dSEnric Balletbo i Serra 				       enum power_supply_property psp,
4482ffb500dSEnric Balletbo i Serra 				       const union power_supply_propval *val)
4492ffb500dSEnric Balletbo i Serra {
4502ffb500dSEnric Balletbo i Serra 	struct port_data *port = power_supply_get_drvdata(psy);
4512ffb500dSEnric Balletbo i Serra 	struct charger_data *charger = port->charger;
4522ffb500dSEnric Balletbo i Serra 	struct device *dev = charger->dev;
4532ffb500dSEnric Balletbo i Serra 	u16 intval;
4542ffb500dSEnric Balletbo i Serra 	int ret;
4552ffb500dSEnric Balletbo i Serra 
4562ffb500dSEnric Balletbo i Serra 	/* U16_MAX in mV/mA is the maximum supported value */
4572ffb500dSEnric Balletbo i Serra 	if (val->intval >= U16_MAX * 1000)
4582ffb500dSEnric Balletbo i Serra 		return -EINVAL;
4592ffb500dSEnric Balletbo i Serra 	/* A negative number is used to clear the limit */
4602ffb500dSEnric Balletbo i Serra 	if (val->intval < 0)
4612ffb500dSEnric Balletbo i Serra 		intval = EC_POWER_LIMIT_NONE;
4622ffb500dSEnric Balletbo i Serra 	else	/* Convert from uA/uV to mA/mV */
4632ffb500dSEnric Balletbo i Serra 		intval = val->intval / 1000;
4642ffb500dSEnric Balletbo i Serra 
4652ffb500dSEnric Balletbo i Serra 	switch (psp) {
4662ffb500dSEnric Balletbo i Serra 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
4672ffb500dSEnric Balletbo i Serra 		ret = cros_usbpd_charger_set_ext_power_limit(charger, intval,
4682ffb500dSEnric Balletbo i Serra 							input_voltage_limit);
4692ffb500dSEnric Balletbo i Serra 		if (ret < 0)
4702ffb500dSEnric Balletbo i Serra 			break;
4712ffb500dSEnric Balletbo i Serra 
4722ffb500dSEnric Balletbo i Serra 		input_current_limit = intval;
4732ffb500dSEnric Balletbo i Serra 		if (input_current_limit == EC_POWER_LIMIT_NONE)
4742ffb500dSEnric Balletbo i Serra 			dev_info(dev,
4752ffb500dSEnric Balletbo i Serra 			  "External Current Limit cleared for all ports\n");
4762ffb500dSEnric Balletbo i Serra 		else
4772ffb500dSEnric Balletbo i Serra 			dev_info(dev,
4782ffb500dSEnric Balletbo i Serra 			  "External Current Limit set to %dmA for all ports\n",
4792ffb500dSEnric Balletbo i Serra 			  input_current_limit);
4802ffb500dSEnric Balletbo i Serra 		break;
4812ffb500dSEnric Balletbo i Serra 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
4822ffb500dSEnric Balletbo i Serra 		ret = cros_usbpd_charger_set_ext_power_limit(charger,
4832ffb500dSEnric Balletbo i Serra 							input_current_limit,
4842ffb500dSEnric Balletbo i Serra 							intval);
4852ffb500dSEnric Balletbo i Serra 		if (ret < 0)
4862ffb500dSEnric Balletbo i Serra 			break;
4872ffb500dSEnric Balletbo i Serra 
4882ffb500dSEnric Balletbo i Serra 		input_voltage_limit = intval;
4892ffb500dSEnric Balletbo i Serra 		if (input_voltage_limit == EC_POWER_LIMIT_NONE)
4902ffb500dSEnric Balletbo i Serra 			dev_info(dev,
4912ffb500dSEnric Balletbo i Serra 			  "External Voltage Limit cleared for all ports\n");
4922ffb500dSEnric Balletbo i Serra 		else
4932ffb500dSEnric Balletbo i Serra 			dev_info(dev,
4942ffb500dSEnric Balletbo i Serra 			  "External Voltage Limit set to %dmV for all ports\n",
4952ffb500dSEnric Balletbo i Serra 			  input_voltage_limit);
4962ffb500dSEnric Balletbo i Serra 		break;
4972ffb500dSEnric Balletbo i Serra 	default:
4982ffb500dSEnric Balletbo i Serra 		ret = -EINVAL;
4992ffb500dSEnric Balletbo i Serra 	}
5002ffb500dSEnric Balletbo i Serra 
5012ffb500dSEnric Balletbo i Serra 	return ret;
5022ffb500dSEnric Balletbo i Serra }
5032ffb500dSEnric Balletbo i Serra 
cros_usbpd_charger_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)5042ffb500dSEnric Balletbo i Serra static int cros_usbpd_charger_property_is_writeable(struct power_supply *psy,
5052ffb500dSEnric Balletbo i Serra 						enum power_supply_property psp)
5062ffb500dSEnric Balletbo i Serra {
5072ffb500dSEnric Balletbo i Serra 	int ret;
5082ffb500dSEnric Balletbo i Serra 
5092ffb500dSEnric Balletbo i Serra 	switch (psp) {
5102ffb500dSEnric Balletbo i Serra 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
5112ffb500dSEnric Balletbo i Serra 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
5122ffb500dSEnric Balletbo i Serra 		ret = 1;
5132ffb500dSEnric Balletbo i Serra 		break;
5142ffb500dSEnric Balletbo i Serra 	default:
5152ffb500dSEnric Balletbo i Serra 		ret = 0;
5162ffb500dSEnric Balletbo i Serra 	}
5172ffb500dSEnric Balletbo i Serra 
5182ffb500dSEnric Balletbo i Serra 	return ret;
5192ffb500dSEnric Balletbo i Serra }
5202ffb500dSEnric Balletbo i Serra 
cros_usbpd_charger_ec_event(struct notifier_block * nb,unsigned long host_event,void * _notify)521f68b883eSSameer Nanda static int cros_usbpd_charger_ec_event(struct notifier_block *nb,
522f2437e48SJon Flatley 				       unsigned long host_event,
523f68b883eSSameer Nanda 				       void *_notify)
524f68b883eSSameer Nanda {
525f2437e48SJon Flatley 	struct charger_data *charger = container_of(nb, struct charger_data,
526f2437e48SJon Flatley 						    notifier);
527f68b883eSSameer Nanda 
528f68b883eSSameer Nanda 	cros_usbpd_charger_power_changed(charger->ports[0]->psy);
529f68b883eSSameer Nanda 	return NOTIFY_OK;
530f68b883eSSameer Nanda }
531f68b883eSSameer Nanda 
cros_usbpd_charger_unregister_notifier(void * data)532f68b883eSSameer Nanda static void cros_usbpd_charger_unregister_notifier(void *data)
533f68b883eSSameer Nanda {
534f68b883eSSameer Nanda 	struct charger_data *charger = data;
535f68b883eSSameer Nanda 
536f2437e48SJon Flatley 	cros_usbpd_unregister_notify(&charger->notifier);
537f68b883eSSameer Nanda }
538f68b883eSSameer Nanda 
cros_usbpd_charger_probe(struct platform_device * pd)539f68b883eSSameer Nanda static int cros_usbpd_charger_probe(struct platform_device *pd)
540f68b883eSSameer Nanda {
541f68b883eSSameer Nanda 	struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
542f68b883eSSameer Nanda 	struct cros_ec_device *ec_device = ec_dev->ec_dev;
543f68b883eSSameer Nanda 	struct power_supply_desc *psy_desc;
544f68b883eSSameer Nanda 	struct device *dev = &pd->dev;
545f68b883eSSameer Nanda 	struct charger_data *charger;
546f68b883eSSameer Nanda 	struct power_supply *psy;
547f68b883eSSameer Nanda 	struct port_data *port;
548f68b883eSSameer Nanda 	int ret = -EINVAL;
549f68b883eSSameer Nanda 	int i;
550f68b883eSSameer Nanda 
551f68b883eSSameer Nanda 	charger = devm_kzalloc(dev, sizeof(struct charger_data),
552f68b883eSSameer Nanda 			       GFP_KERNEL);
553f68b883eSSameer Nanda 	if (!charger)
554f68b883eSSameer Nanda 		return -ENOMEM;
555f68b883eSSameer Nanda 
556f68b883eSSameer Nanda 	charger->dev = dev;
557f68b883eSSameer Nanda 	charger->ec_dev = ec_dev;
558f68b883eSSameer Nanda 	charger->ec_device = ec_device;
559f68b883eSSameer Nanda 
560f68b883eSSameer Nanda 	platform_set_drvdata(pd, charger);
561f68b883eSSameer Nanda 
5623af15cfaSFabien Parent 	/*
5633af15cfaSFabien Parent 	 * We need to know the number of USB PD ports in order to know whether
5643af15cfaSFabien Parent 	 * there is a dedicated port. The dedicated port will always be
5653af15cfaSFabien Parent 	 * after the USB PD ports, and there should be only one.
5663af15cfaSFabien Parent 	 */
5673af15cfaSFabien Parent 	charger->num_usbpd_ports =
5683af15cfaSFabien Parent 		cros_usbpd_charger_get_usbpd_num_ports(charger);
5693af15cfaSFabien Parent 	if (charger->num_usbpd_ports <= 0) {
570f68b883eSSameer Nanda 		/*
571f68b883eSSameer Nanda 		 * This can happen on a system that doesn't support USB PD.
572f68b883eSSameer Nanda 		 * Log a message, but no need to warn.
573f68b883eSSameer Nanda 		 */
5743af15cfaSFabien Parent 		dev_info(dev, "No USB PD charging ports found\n");
5753af15cfaSFabien Parent 	}
5763af15cfaSFabien Parent 
5773af15cfaSFabien Parent 	charger->num_charger_ports = cros_usbpd_charger_get_num_ports(charger);
5783af15cfaSFabien Parent 	if (charger->num_charger_ports < 0) {
5793af15cfaSFabien Parent 		/*
5803af15cfaSFabien Parent 		 * This can happen on a system that doesn't support USB PD.
5813af15cfaSFabien Parent 		 * Log a message, but no need to warn.
5823af15cfaSFabien Parent 		 * Older ECs do not support the above command, in that case
5833af15cfaSFabien Parent 		 * let's set up the number of charger ports equal to the number
5843af15cfaSFabien Parent 		 * of USB PD ports
5853af15cfaSFabien Parent 		 */
5863af15cfaSFabien Parent 		dev_info(dev, "Could not get charger port count\n");
5873af15cfaSFabien Parent 		charger->num_charger_ports = charger->num_usbpd_ports;
5883af15cfaSFabien Parent 	}
5893af15cfaSFabien Parent 
5903af15cfaSFabien Parent 	if (charger->num_charger_ports <= 0) {
5913af15cfaSFabien Parent 		/*
5923af15cfaSFabien Parent 		 * This can happen on a system that doesn't support USB PD and
5933af15cfaSFabien Parent 		 * doesn't have a dedicated port.
5943af15cfaSFabien Parent 		 * Log a message, but no need to warn.
5953af15cfaSFabien Parent 		 */
596f68b883eSSameer Nanda 		dev_info(dev, "No charging ports found\n");
597f68b883eSSameer Nanda 		ret = -ENODEV;
598f68b883eSSameer Nanda 		goto fail_nowarn;
599f68b883eSSameer Nanda 	}
600f68b883eSSameer Nanda 
6013af15cfaSFabien Parent 	/*
6023af15cfaSFabien Parent 	 * Sanity checks on the number of ports:
6033af15cfaSFabien Parent 	 *  there should be at most 1 dedicated port
6043af15cfaSFabien Parent 	 */
6053af15cfaSFabien Parent 	if (charger->num_charger_ports < charger->num_usbpd_ports ||
6063af15cfaSFabien Parent 	    charger->num_charger_ports > (charger->num_usbpd_ports + 1)) {
6073af15cfaSFabien Parent 		dev_err(dev, "Unexpected number of charge port count\n");
6083af15cfaSFabien Parent 		ret = -EPROTO;
6093af15cfaSFabien Parent 		goto fail_nowarn;
6103af15cfaSFabien Parent 	}
6113af15cfaSFabien Parent 
612f68b883eSSameer Nanda 	for (i = 0; i < charger->num_charger_ports; i++) {
613f68b883eSSameer Nanda 		struct power_supply_config psy_cfg = {};
614f68b883eSSameer Nanda 
615f68b883eSSameer Nanda 		port = devm_kzalloc(dev, sizeof(struct port_data), GFP_KERNEL);
616f68b883eSSameer Nanda 		if (!port) {
617f68b883eSSameer Nanda 			ret = -ENOMEM;
618f68b883eSSameer Nanda 			goto fail;
619f68b883eSSameer Nanda 		}
620f68b883eSSameer Nanda 
621f68b883eSSameer Nanda 		port->charger = charger;
622f68b883eSSameer Nanda 		port->port_number = i;
623f68b883eSSameer Nanda 
624f68b883eSSameer Nanda 		psy_desc = &port->psy_desc;
625f68b883eSSameer Nanda 		psy_desc->get_property = cros_usbpd_charger_get_prop;
6262ffb500dSEnric Balletbo i Serra 		psy_desc->set_property = cros_usbpd_charger_set_prop;
6272ffb500dSEnric Balletbo i Serra 		psy_desc->property_is_writeable =
6282ffb500dSEnric Balletbo i Serra 				cros_usbpd_charger_property_is_writeable;
629f68b883eSSameer Nanda 		psy_desc->external_power_changed =
630f68b883eSSameer Nanda 					cros_usbpd_charger_power_changed;
6313af15cfaSFabien Parent 		psy_cfg.drv_data = port;
6323af15cfaSFabien Parent 
6333af15cfaSFabien Parent 		if (cros_usbpd_charger_port_is_dedicated(port)) {
6343af15cfaSFabien Parent 			sprintf(port->name, CHARGER_DEDICATED_DIR_NAME);
6353af15cfaSFabien Parent 			psy_desc->type = POWER_SUPPLY_TYPE_MAINS;
6363af15cfaSFabien Parent 			psy_desc->properties =
6373af15cfaSFabien Parent 				cros_usbpd_dedicated_charger_props;
6383af15cfaSFabien Parent 			psy_desc->num_properties =
6393af15cfaSFabien Parent 				ARRAY_SIZE(cros_usbpd_dedicated_charger_props);
6403af15cfaSFabien Parent 		} else {
6413af15cfaSFabien Parent 			sprintf(port->name, CHARGER_USBPD_DIR_NAME, i);
6423af15cfaSFabien Parent 			psy_desc->type = POWER_SUPPLY_TYPE_USB;
643f68b883eSSameer Nanda 			psy_desc->properties = cros_usbpd_charger_props;
644f68b883eSSameer Nanda 			psy_desc->num_properties =
645f68b883eSSameer Nanda 				ARRAY_SIZE(cros_usbpd_charger_props);
646f68b883eSSameer Nanda 			psy_desc->usb_types = cros_usbpd_charger_usb_types;
647f68b883eSSameer Nanda 			psy_desc->num_usb_types =
648f68b883eSSameer Nanda 				ARRAY_SIZE(cros_usbpd_charger_usb_types);
6493af15cfaSFabien Parent 		}
6503af15cfaSFabien Parent 
6513af15cfaSFabien Parent 		psy_desc->name = port->name;
652f68b883eSSameer Nanda 
653f68b883eSSameer Nanda 		psy = devm_power_supply_register_no_ws(dev, psy_desc,
654f68b883eSSameer Nanda 						       &psy_cfg);
655f68b883eSSameer Nanda 		if (IS_ERR(psy)) {
656f68b883eSSameer Nanda 			dev_err(dev, "Failed to register power supply\n");
657f68b883eSSameer Nanda 			continue;
658f68b883eSSameer Nanda 		}
659f68b883eSSameer Nanda 		port->psy = psy;
660f68b883eSSameer Nanda 
661f68b883eSSameer Nanda 		charger->ports[charger->num_registered_psy++] = port;
662f68b883eSSameer Nanda 	}
663f68b883eSSameer Nanda 
664f68b883eSSameer Nanda 	if (!charger->num_registered_psy) {
665f68b883eSSameer Nanda 		ret = -ENODEV;
666f68b883eSSameer Nanda 		dev_err(dev, "No power supplies registered\n");
667f68b883eSSameer Nanda 		goto fail;
668f68b883eSSameer Nanda 	}
669f68b883eSSameer Nanda 
670f68b883eSSameer Nanda 	/* Get PD events from the EC */
671f68b883eSSameer Nanda 	charger->notifier.notifier_call = cros_usbpd_charger_ec_event;
672f2437e48SJon Flatley 	ret = cros_usbpd_register_notify(&charger->notifier);
673f68b883eSSameer Nanda 	if (ret < 0) {
674f68b883eSSameer Nanda 		dev_warn(dev, "failed to register notifier\n");
675f68b883eSSameer Nanda 	} else {
676f68b883eSSameer Nanda 		ret = devm_add_action_or_reset(dev,
677f68b883eSSameer Nanda 				cros_usbpd_charger_unregister_notifier,
678f68b883eSSameer Nanda 				charger);
679f68b883eSSameer Nanda 		if (ret < 0)
680f68b883eSSameer Nanda 			goto fail;
681f68b883eSSameer Nanda 	}
682f68b883eSSameer Nanda 
683f68b883eSSameer Nanda 	return 0;
684f68b883eSSameer Nanda 
685f68b883eSSameer Nanda fail:
686f68b883eSSameer Nanda 	WARN(1, "%s: Failing probe (err:0x%x)\n", dev_name(dev), ret);
687f68b883eSSameer Nanda 
688f68b883eSSameer Nanda fail_nowarn:
689f68b883eSSameer Nanda 	dev_info(dev, "Failing probe (err:0x%x)\n", ret);
690f68b883eSSameer Nanda 	return ret;
691f68b883eSSameer Nanda }
692f68b883eSSameer Nanda 
693f68b883eSSameer Nanda #ifdef CONFIG_PM_SLEEP
cros_usbpd_charger_resume(struct device * dev)694f68b883eSSameer Nanda static int cros_usbpd_charger_resume(struct device *dev)
695f68b883eSSameer Nanda {
696f68b883eSSameer Nanda 	struct charger_data *charger = dev_get_drvdata(dev);
697f68b883eSSameer Nanda 	int i;
698f68b883eSSameer Nanda 
699f68b883eSSameer Nanda 	if (!charger)
700f68b883eSSameer Nanda 		return 0;
701f68b883eSSameer Nanda 
702f68b883eSSameer Nanda 	for (i = 0; i < charger->num_registered_psy; i++) {
703f68b883eSSameer Nanda 		power_supply_changed(charger->ports[i]->psy);
704f68b883eSSameer Nanda 		charger->ports[i]->last_update =
705f68b883eSSameer Nanda 				jiffies - CHARGER_CACHE_UPDATE_DELAY;
706f68b883eSSameer Nanda 	}
707f68b883eSSameer Nanda 
708f68b883eSSameer Nanda 	return 0;
709f68b883eSSameer Nanda }
710f68b883eSSameer Nanda #endif
711f68b883eSSameer Nanda 
712f68b883eSSameer Nanda static SIMPLE_DEV_PM_OPS(cros_usbpd_charger_pm_ops, NULL,
713f68b883eSSameer Nanda 			 cros_usbpd_charger_resume);
714f68b883eSSameer Nanda 
715*0f8678c3STzung-Bi Shih static const struct platform_device_id cros_usbpd_charger_id[] = {
716*0f8678c3STzung-Bi Shih 	{ DRV_NAME, 0 },
717*0f8678c3STzung-Bi Shih 	{}
718*0f8678c3STzung-Bi Shih };
719*0f8678c3STzung-Bi Shih MODULE_DEVICE_TABLE(platform, cros_usbpd_charger_id);
720*0f8678c3STzung-Bi Shih 
721f68b883eSSameer Nanda static struct platform_driver cros_usbpd_charger_driver = {
722f68b883eSSameer Nanda 	.driver = {
723f68b883eSSameer Nanda 		.name = DRV_NAME,
724f68b883eSSameer Nanda 		.pm = &cros_usbpd_charger_pm_ops,
725f68b883eSSameer Nanda 	},
726*0f8678c3STzung-Bi Shih 	.probe = cros_usbpd_charger_probe,
727*0f8678c3STzung-Bi Shih 	.id_table = cros_usbpd_charger_id,
728f68b883eSSameer Nanda };
729f68b883eSSameer Nanda 
730f68b883eSSameer Nanda module_platform_driver(cros_usbpd_charger_driver);
731f68b883eSSameer Nanda 
732f68b883eSSameer Nanda MODULE_LICENSE("GPL");
733f68b883eSSameer Nanda MODULE_DESCRIPTION("ChromeOS EC USBPD charger");
734