1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Power supply driver for ChromeOS EC based Peripheral Device Charger.
4  *
5  * Copyright 2020 Google LLC.
6  */
7 
8 #include <linux/module.h>
9 #include <linux/notifier.h>
10 #include <linux/platform_data/cros_ec_commands.h>
11 #include <linux/platform_data/cros_ec_proto.h>
12 #include <linux/platform_device.h>
13 #include <linux/power_supply.h>
14 #include <linux/slab.h>
15 #include <linux/stringify.h>
16 #include <linux/types.h>
17 
18 #define DRV_NAME		"cros-ec-pchg"
19 #define PCHG_DIR_PREFIX		"peripheral"
20 #define PCHG_DIR_NAME		PCHG_DIR_PREFIX "%d"
21 #define PCHG_DIR_NAME_LENGTH \
22 		sizeof(PCHG_DIR_PREFIX __stringify(EC_PCHG_MAX_PORTS))
23 #define PCHG_CACHE_UPDATE_DELAY	msecs_to_jiffies(500)
24 
25 struct port_data {
26 	int port_number;
27 	char name[PCHG_DIR_NAME_LENGTH];
28 	struct power_supply *psy;
29 	struct power_supply_desc psy_desc;
30 	int psy_status;
31 	int battery_percentage;
32 	int charge_type;
33 	struct charger_data *charger;
34 	unsigned long last_update;
35 };
36 
37 struct charger_data {
38 	struct device *dev;
39 	struct cros_ec_dev *ec_dev;
40 	struct cros_ec_device *ec_device;
41 	int num_registered_psy;
42 	struct port_data *ports[EC_PCHG_MAX_PORTS];
43 	struct notifier_block notifier;
44 };
45 
46 static enum power_supply_property cros_pchg_props[] = {
47 	POWER_SUPPLY_PROP_STATUS,
48 	POWER_SUPPLY_PROP_CHARGE_TYPE,
49 	POWER_SUPPLY_PROP_CAPACITY,
50 	POWER_SUPPLY_PROP_SCOPE,
51 };
52 
53 static int cros_pchg_ec_command(const struct charger_data *charger,
54 				unsigned int version,
55 				unsigned int command,
56 				const void *outdata,
57 				unsigned int outsize,
58 				void *indata,
59 				unsigned int insize)
60 {
61 	struct cros_ec_dev *ec_dev = charger->ec_dev;
62 	struct cros_ec_command *msg;
63 	int ret;
64 
65 	msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
66 	if (!msg)
67 		return -ENOMEM;
68 
69 	msg->version = version;
70 	msg->command = ec_dev->cmd_offset + command;
71 	msg->outsize = outsize;
72 	msg->insize = insize;
73 
74 	if (outsize)
75 		memcpy(msg->data, outdata, outsize);
76 
77 	ret = cros_ec_cmd_xfer_status(charger->ec_device, msg);
78 	if (ret >= 0 && insize)
79 		memcpy(indata, msg->data, insize);
80 
81 	kfree(msg);
82 	return ret;
83 }
84 
85 static const unsigned int pchg_cmd_version = 1;
86 
87 static bool cros_pchg_cmd_ver_check(const struct charger_data *charger)
88 {
89 	struct ec_params_get_cmd_versions_v1 req;
90 	struct ec_response_get_cmd_versions rsp;
91 	int ret;
92 
93 	req.cmd = EC_CMD_PCHG;
94 	ret = cros_pchg_ec_command(charger, 1, EC_CMD_GET_CMD_VERSIONS,
95 				   &req, sizeof(req), &rsp, sizeof(rsp));
96 	if (ret < 0) {
97 		dev_warn(charger->dev,
98 			 "Unable to get versions of EC_CMD_PCHG (err:%d)\n",
99 			 ret);
100 		return false;
101 	}
102 
103 	return !!(rsp.version_mask & BIT(pchg_cmd_version));
104 }
105 
106 static int cros_pchg_port_count(const struct charger_data *charger)
107 {
108 	struct ec_response_pchg_count rsp;
109 	int ret;
110 
111 	ret = cros_pchg_ec_command(charger, 0, EC_CMD_PCHG_COUNT,
112 				   NULL, 0, &rsp, sizeof(rsp));
113 	if (ret < 0) {
114 		dev_warn(charger->dev,
115 			 "Unable to get number or ports (err:%d)\n", ret);
116 		return ret;
117 	}
118 
119 	return rsp.port_count;
120 }
121 
122 static int cros_pchg_get_status(struct port_data *port)
123 {
124 	struct charger_data *charger = port->charger;
125 	struct ec_params_pchg req;
126 	struct ec_response_pchg rsp;
127 	struct device *dev = charger->dev;
128 	int old_status = port->psy_status;
129 	int old_percentage = port->battery_percentage;
130 	int ret;
131 
132 	req.port = port->port_number;
133 	ret = cros_pchg_ec_command(charger, pchg_cmd_version, EC_CMD_PCHG,
134 				   &req, sizeof(req), &rsp, sizeof(rsp));
135 	if (ret < 0) {
136 		dev_err(dev, "Unable to get port.%d status (err:%d)\n",
137 			port->port_number, ret);
138 		return ret;
139 	}
140 
141 	switch (rsp.state) {
142 	case PCHG_STATE_RESET:
143 	case PCHG_STATE_INITIALIZED:
144 	case PCHG_STATE_ENABLED:
145 	default:
146 		port->psy_status = POWER_SUPPLY_STATUS_UNKNOWN;
147 		port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
148 		break;
149 	case PCHG_STATE_DETECTED:
150 		port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
151 		port->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
152 		break;
153 	case PCHG_STATE_CHARGING:
154 		port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
155 		port->charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
156 		break;
157 	case PCHG_STATE_FULL:
158 		port->psy_status = POWER_SUPPLY_STATUS_FULL;
159 		port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
160 		break;
161 	}
162 
163 	port->battery_percentage = rsp.battery_percentage;
164 
165 	if (port->psy_status != old_status ||
166 			port->battery_percentage != old_percentage)
167 		power_supply_changed(port->psy);
168 
169 	dev_dbg(dev,
170 		"Port %d: state=%d battery=%d%%\n",
171 		port->port_number, rsp.state, rsp.battery_percentage);
172 
173 	return 0;
174 }
175 
176 static int cros_pchg_get_port_status(struct port_data *port, bool ratelimit)
177 {
178 	int ret;
179 
180 	if (ratelimit &&
181 	    time_is_after_jiffies(port->last_update + PCHG_CACHE_UPDATE_DELAY))
182 		return 0;
183 
184 	ret = cros_pchg_get_status(port);
185 	if (ret < 0)
186 		return ret;
187 
188 	port->last_update = jiffies;
189 
190 	return ret;
191 }
192 
193 static int cros_pchg_get_prop(struct power_supply *psy,
194 			      enum power_supply_property psp,
195 			      union power_supply_propval *val)
196 {
197 	struct port_data *port = power_supply_get_drvdata(psy);
198 
199 	switch (psp) {
200 	case POWER_SUPPLY_PROP_STATUS:
201 	case POWER_SUPPLY_PROP_CAPACITY:
202 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
203 		cros_pchg_get_port_status(port, true);
204 		break;
205 	default:
206 		break;
207 	}
208 
209 	switch (psp) {
210 	case POWER_SUPPLY_PROP_STATUS:
211 		val->intval = port->psy_status;
212 		break;
213 	case POWER_SUPPLY_PROP_CAPACITY:
214 		val->intval = port->battery_percentage;
215 		break;
216 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
217 		val->intval = port->charge_type;
218 		break;
219 	case POWER_SUPPLY_PROP_SCOPE:
220 		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
221 		break;
222 	default:
223 		return -EINVAL;
224 	}
225 
226 	return 0;
227 }
228 
229 static int cros_pchg_event(const struct charger_data *charger,
230 			   unsigned long host_event)
231 {
232 	int i;
233 
234 	for (i = 0; i < charger->num_registered_psy; i++)
235 		cros_pchg_get_port_status(charger->ports[i], false);
236 
237 	return NOTIFY_OK;
238 }
239 
240 static u32 cros_get_device_event(const struct charger_data *charger)
241 {
242 	struct ec_params_device_event req;
243 	struct ec_response_device_event rsp;
244 	struct device *dev = charger->dev;
245 	int ret;
246 
247 	req.param = EC_DEVICE_EVENT_PARAM_GET_CURRENT_EVENTS;
248 	ret = cros_pchg_ec_command(charger, 0, EC_CMD_DEVICE_EVENT,
249 				   &req, sizeof(req), &rsp, sizeof(rsp));
250 	if (ret < 0) {
251 		dev_warn(dev, "Unable to get device events (err:%d)\n", ret);
252 		return 0;
253 	}
254 
255 	return rsp.event_mask;
256 }
257 
258 static int cros_ec_notify(struct notifier_block *nb,
259 			  unsigned long queued_during_suspend,
260 			  void *data)
261 {
262 	struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
263 	u32 host_event = cros_ec_get_host_event(ec_dev);
264 	struct charger_data *charger =
265 			container_of(nb, struct charger_data, notifier);
266 	u32 device_event_mask;
267 
268 	if (!host_event)
269 		return NOTIFY_DONE;
270 
271 	if (!(host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_DEVICE)))
272 		return NOTIFY_DONE;
273 
274 	/*
275 	 * todo: Retrieve device event mask in common place
276 	 * (e.g. cros_ec_proto.c).
277 	 */
278 	device_event_mask = cros_get_device_event(charger);
279 	if (!(device_event_mask & EC_DEVICE_EVENT_MASK(EC_DEVICE_EVENT_WLC)))
280 		return NOTIFY_DONE;
281 
282 	return cros_pchg_event(charger, host_event);
283 }
284 
285 static int cros_pchg_probe(struct platform_device *pdev)
286 {
287 	struct device *dev = &pdev->dev;
288 	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
289 	struct cros_ec_device *ec_device = ec_dev->ec_dev;
290 	struct power_supply_desc *psy_desc;
291 	struct charger_data *charger;
292 	struct power_supply *psy;
293 	struct port_data *port;
294 	struct notifier_block *nb;
295 	int num_ports;
296 	int ret;
297 	int i;
298 
299 	charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL);
300 	if (!charger)
301 		return -ENOMEM;
302 
303 	charger->dev = dev;
304 	charger->ec_dev = ec_dev;
305 	charger->ec_device = ec_device;
306 
307 	ret = cros_pchg_port_count(charger);
308 	if (ret <= 0) {
309 		/*
310 		 * This feature is enabled by the EC and the kernel driver is
311 		 * included by default for CrOS devices. Don't need to be loud
312 		 * since this error can be normal.
313 		 */
314 		dev_info(dev, "No peripheral charge ports (err:%d)\n", ret);
315 		return -ENODEV;
316 	}
317 
318 	if (!cros_pchg_cmd_ver_check(charger)) {
319 		dev_err(dev, "EC_CMD_PCHG version %d isn't available.\n",
320 			pchg_cmd_version);
321 		return -EOPNOTSUPP;
322 	}
323 
324 	num_ports = ret;
325 	if (num_ports > EC_PCHG_MAX_PORTS) {
326 		dev_err(dev, "Too many peripheral charge ports (%d)\n",
327 			num_ports);
328 		return -ENOBUFS;
329 	}
330 
331 	dev_info(dev, "%d peripheral charge ports found\n", num_ports);
332 
333 	for (i = 0; i < num_ports; i++) {
334 		struct power_supply_config psy_cfg = {};
335 
336 		port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
337 		if (!port)
338 			return -ENOMEM;
339 
340 		port->charger = charger;
341 		port->port_number = i;
342 		snprintf(port->name, sizeof(port->name), PCHG_DIR_NAME, i);
343 
344 		psy_desc = &port->psy_desc;
345 		psy_desc->name = port->name;
346 		psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
347 		psy_desc->get_property = cros_pchg_get_prop;
348 		psy_desc->external_power_changed = NULL;
349 		psy_desc->properties = cros_pchg_props;
350 		psy_desc->num_properties = ARRAY_SIZE(cros_pchg_props);
351 		psy_cfg.drv_data = port;
352 
353 		psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
354 		if (IS_ERR(psy))
355 			return dev_err_probe(dev, PTR_ERR(psy),
356 					"Failed to register power supply\n");
357 		port->psy = psy;
358 
359 		charger->ports[charger->num_registered_psy++] = port;
360 	}
361 
362 	if (!charger->num_registered_psy)
363 		return -ENODEV;
364 
365 	nb = &charger->notifier;
366 	nb->notifier_call = cros_ec_notify;
367 	ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
368 					       nb);
369 	if (ret < 0)
370 		dev_err(dev, "Failed to register notifier (err:%d)\n", ret);
371 
372 	return 0;
373 }
374 
375 static struct platform_driver cros_pchg_driver = {
376 	.driver = {
377 		.name = DRV_NAME,
378 	},
379 	.probe = cros_pchg_probe
380 };
381 
382 module_platform_driver(cros_pchg_driver);
383 
384 MODULE_LICENSE("GPL");
385 MODULE_DESCRIPTION("ChromeOS EC peripheral device charger");
386 MODULE_ALIAS("platform:" DRV_NAME);
387