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