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