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