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
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)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
cros_pchg_cmd_ver_check(const struct charger_data * charger)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
cros_pchg_port_count(const struct charger_data * charger)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
cros_pchg_get_status(struct port_data * port)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
cros_pchg_get_port_status(struct port_data * port,bool ratelimit)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
cros_pchg_get_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)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
cros_pchg_event(const struct charger_data * charger,unsigned long host_event)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
cros_get_device_event(const struct charger_data * charger)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
cros_ec_notify(struct notifier_block * nb,unsigned long queued_during_suspend,void * data)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
cros_pchg_probe(struct platform_device * pdev)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