Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Power supply driver for ChromeOS EC based Peripheral Device Charger.
0004  *
0005  * Copyright 2020 Google LLC.
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/notifier.h>
0010 #include <linux/platform_data/cros_ec_commands.h>
0011 #include <linux/platform_data/cros_ec_proto.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/power_supply.h>
0014 #include <linux/slab.h>
0015 #include <linux/stringify.h>
0016 #include <linux/types.h>
0017 #include <asm/unaligned.h>
0018 
0019 #define DRV_NAME        "cros-ec-pchg"
0020 #define PCHG_DIR_PREFIX     "peripheral"
0021 #define PCHG_DIR_NAME       PCHG_DIR_PREFIX "%d"
0022 #define PCHG_DIR_NAME_LENGTH \
0023         sizeof(PCHG_DIR_PREFIX __stringify(EC_PCHG_MAX_PORTS))
0024 #define PCHG_CACHE_UPDATE_DELAY msecs_to_jiffies(500)
0025 
0026 struct port_data {
0027     int port_number;
0028     char name[PCHG_DIR_NAME_LENGTH];
0029     struct power_supply *psy;
0030     struct power_supply_desc psy_desc;
0031     int psy_status;
0032     int battery_percentage;
0033     int charge_type;
0034     struct charger_data *charger;
0035     unsigned long last_update;
0036 };
0037 
0038 struct charger_data {
0039     struct device *dev;
0040     struct cros_ec_dev *ec_dev;
0041     struct cros_ec_device *ec_device;
0042     int num_registered_psy;
0043     struct port_data *ports[EC_PCHG_MAX_PORTS];
0044     struct notifier_block notifier;
0045 };
0046 
0047 static enum power_supply_property cros_pchg_props[] = {
0048     POWER_SUPPLY_PROP_STATUS,
0049     POWER_SUPPLY_PROP_CHARGE_TYPE,
0050     POWER_SUPPLY_PROP_CAPACITY,
0051     POWER_SUPPLY_PROP_SCOPE,
0052 };
0053 
0054 static int cros_pchg_ec_command(const struct charger_data *charger,
0055                 unsigned int version,
0056                 unsigned int command,
0057                 const void *outdata,
0058                 unsigned int outsize,
0059                 void *indata,
0060                 unsigned int insize)
0061 {
0062     struct cros_ec_dev *ec_dev = charger->ec_dev;
0063     struct cros_ec_command *msg;
0064     int ret;
0065 
0066     msg = kzalloc(struct_size(msg, data, max(outsize, insize)), GFP_KERNEL);
0067     if (!msg)
0068         return -ENOMEM;
0069 
0070     msg->version = version;
0071     msg->command = ec_dev->cmd_offset + command;
0072     msg->outsize = outsize;
0073     msg->insize = insize;
0074 
0075     if (outsize)
0076         memcpy(msg->data, outdata, outsize);
0077 
0078     ret = cros_ec_cmd_xfer_status(charger->ec_device, msg);
0079     if (ret >= 0 && insize)
0080         memcpy(indata, msg->data, insize);
0081 
0082     kfree(msg);
0083     return ret;
0084 }
0085 
0086 static const unsigned int pchg_cmd_version = 1;
0087 
0088 static bool cros_pchg_cmd_ver_check(const struct charger_data *charger)
0089 {
0090     struct ec_params_get_cmd_versions_v1 req;
0091     struct ec_response_get_cmd_versions rsp;
0092     int ret;
0093 
0094     req.cmd = EC_CMD_PCHG;
0095     ret = cros_pchg_ec_command(charger, 1, EC_CMD_GET_CMD_VERSIONS,
0096                    &req, sizeof(req), &rsp, sizeof(rsp));
0097     if (ret < 0) {
0098         dev_warn(charger->dev,
0099              "Unable to get versions of EC_CMD_PCHG (err:%d)\n",
0100              ret);
0101         return false;
0102     }
0103 
0104     return !!(rsp.version_mask & BIT(pchg_cmd_version));
0105 }
0106 
0107 static int cros_pchg_port_count(const struct charger_data *charger)
0108 {
0109     struct ec_response_pchg_count rsp;
0110     int ret;
0111 
0112     ret = cros_pchg_ec_command(charger, 0, EC_CMD_PCHG_COUNT,
0113                    NULL, 0, &rsp, sizeof(rsp));
0114     if (ret < 0) {
0115         dev_warn(charger->dev,
0116              "Unable to get number or ports (err:%d)\n", ret);
0117         return ret;
0118     }
0119 
0120     return rsp.port_count;
0121 }
0122 
0123 static int cros_pchg_get_status(struct port_data *port)
0124 {
0125     struct charger_data *charger = port->charger;
0126     struct ec_params_pchg req;
0127     struct ec_response_pchg rsp;
0128     struct device *dev = charger->dev;
0129     int old_status = port->psy_status;
0130     int old_percentage = port->battery_percentage;
0131     int ret;
0132 
0133     req.port = port->port_number;
0134     ret = cros_pchg_ec_command(charger, pchg_cmd_version, EC_CMD_PCHG,
0135                    &req, sizeof(req), &rsp, sizeof(rsp));
0136     if (ret < 0) {
0137         dev_err(dev, "Unable to get port.%d status (err:%d)\n",
0138             port->port_number, ret);
0139         return ret;
0140     }
0141 
0142     switch (rsp.state) {
0143     case PCHG_STATE_RESET:
0144     case PCHG_STATE_INITIALIZED:
0145     case PCHG_STATE_ENABLED:
0146     default:
0147         port->psy_status = POWER_SUPPLY_STATUS_UNKNOWN;
0148         port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
0149         break;
0150     case PCHG_STATE_DETECTED:
0151         port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
0152         port->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
0153         break;
0154     case PCHG_STATE_CHARGING:
0155         port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
0156         port->charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
0157         break;
0158     case PCHG_STATE_FULL:
0159         port->psy_status = POWER_SUPPLY_STATUS_FULL;
0160         port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
0161         break;
0162     }
0163 
0164     port->battery_percentage = rsp.battery_percentage;
0165 
0166     if (port->psy_status != old_status ||
0167             port->battery_percentage != old_percentage)
0168         power_supply_changed(port->psy);
0169 
0170     dev_dbg(dev,
0171         "Port %d: state=%d battery=%d%%\n",
0172         port->port_number, rsp.state, rsp.battery_percentage);
0173 
0174     return 0;
0175 }
0176 
0177 static int cros_pchg_get_port_status(struct port_data *port, bool ratelimit)
0178 {
0179     int ret;
0180 
0181     if (ratelimit &&
0182         time_is_after_jiffies(port->last_update + PCHG_CACHE_UPDATE_DELAY))
0183         return 0;
0184 
0185     ret = cros_pchg_get_status(port);
0186     if (ret < 0)
0187         return ret;
0188 
0189     port->last_update = jiffies;
0190 
0191     return ret;
0192 }
0193 
0194 static int cros_pchg_get_prop(struct power_supply *psy,
0195                   enum power_supply_property psp,
0196                   union power_supply_propval *val)
0197 {
0198     struct port_data *port = power_supply_get_drvdata(psy);
0199 
0200     switch (psp) {
0201     case POWER_SUPPLY_PROP_STATUS:
0202     case POWER_SUPPLY_PROP_CAPACITY:
0203     case POWER_SUPPLY_PROP_CHARGE_TYPE:
0204         cros_pchg_get_port_status(port, true);
0205         break;
0206     default:
0207         break;
0208     }
0209 
0210     switch (psp) {
0211     case POWER_SUPPLY_PROP_STATUS:
0212         val->intval = port->psy_status;
0213         break;
0214     case POWER_SUPPLY_PROP_CAPACITY:
0215         val->intval = port->battery_percentage;
0216         break;
0217     case POWER_SUPPLY_PROP_CHARGE_TYPE:
0218         val->intval = port->charge_type;
0219         break;
0220     case POWER_SUPPLY_PROP_SCOPE:
0221         val->intval = POWER_SUPPLY_SCOPE_DEVICE;
0222         break;
0223     default:
0224         return -EINVAL;
0225     }
0226 
0227     return 0;
0228 }
0229 
0230 static int cros_pchg_event(const struct charger_data *charger,
0231                unsigned long host_event)
0232 {
0233     int i;
0234 
0235     for (i = 0; i < charger->num_registered_psy; i++)
0236         cros_pchg_get_port_status(charger->ports[i], false);
0237 
0238     return NOTIFY_OK;
0239 }
0240 
0241 static int cros_ec_notify(struct notifier_block *nb,
0242               unsigned long queued_during_suspend,
0243               void *data)
0244 {
0245     struct cros_ec_device *ec_dev = data;
0246     struct charger_data *charger =
0247             container_of(nb, struct charger_data, notifier);
0248     u32 host_event;
0249 
0250     if (ec_dev->event_data.event_type != EC_MKBP_EVENT_PCHG ||
0251             ec_dev->event_size != sizeof(host_event))
0252         return NOTIFY_DONE;
0253 
0254     host_event = get_unaligned_le32(&ec_dev->event_data.data.host_event);
0255 
0256     if (!(host_event & EC_MKBP_PCHG_DEVICE_EVENT))
0257         return NOTIFY_DONE;
0258 
0259     return cros_pchg_event(charger, host_event);
0260 }
0261 
0262 static int cros_pchg_probe(struct platform_device *pdev)
0263 {
0264     struct device *dev = &pdev->dev;
0265     struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
0266     struct cros_ec_device *ec_device = ec_dev->ec_dev;
0267     struct power_supply_desc *psy_desc;
0268     struct charger_data *charger;
0269     struct power_supply *psy;
0270     struct port_data *port;
0271     struct notifier_block *nb;
0272     int num_ports;
0273     int ret;
0274     int i;
0275 
0276     charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL);
0277     if (!charger)
0278         return -ENOMEM;
0279 
0280     charger->dev = dev;
0281     charger->ec_dev = ec_dev;
0282     charger->ec_device = ec_device;
0283 
0284     ret = cros_pchg_port_count(charger);
0285     if (ret <= 0) {
0286         /*
0287          * This feature is enabled by the EC and the kernel driver is
0288          * included by default for CrOS devices. Don't need to be loud
0289          * since this error can be normal.
0290          */
0291         dev_info(dev, "No peripheral charge ports (err:%d)\n", ret);
0292         return -ENODEV;
0293     }
0294 
0295     if (!cros_pchg_cmd_ver_check(charger)) {
0296         dev_err(dev, "EC_CMD_PCHG version %d isn't available.\n",
0297             pchg_cmd_version);
0298         return -EOPNOTSUPP;
0299     }
0300 
0301     num_ports = ret;
0302     if (num_ports > EC_PCHG_MAX_PORTS) {
0303         dev_err(dev, "Too many peripheral charge ports (%d)\n",
0304             num_ports);
0305         return -ENOBUFS;
0306     }
0307 
0308     dev_info(dev, "%d peripheral charge ports found\n", num_ports);
0309 
0310     for (i = 0; i < num_ports; i++) {
0311         struct power_supply_config psy_cfg = {};
0312 
0313         port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
0314         if (!port)
0315             return -ENOMEM;
0316 
0317         port->charger = charger;
0318         port->port_number = i;
0319         snprintf(port->name, sizeof(port->name), PCHG_DIR_NAME, i);
0320 
0321         psy_desc = &port->psy_desc;
0322         psy_desc->name = port->name;
0323         psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
0324         psy_desc->get_property = cros_pchg_get_prop;
0325         psy_desc->external_power_changed = NULL;
0326         psy_desc->properties = cros_pchg_props;
0327         psy_desc->num_properties = ARRAY_SIZE(cros_pchg_props);
0328         psy_cfg.drv_data = port;
0329 
0330         psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
0331         if (IS_ERR(psy))
0332             return dev_err_probe(dev, PTR_ERR(psy),
0333                     "Failed to register power supply\n");
0334         port->psy = psy;
0335 
0336         charger->ports[charger->num_registered_psy++] = port;
0337     }
0338 
0339     if (!charger->num_registered_psy)
0340         return -ENODEV;
0341 
0342     nb = &charger->notifier;
0343     nb->notifier_call = cros_ec_notify;
0344     ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
0345                            nb);
0346     if (ret < 0)
0347         dev_err(dev, "Failed to register notifier (err:%d)\n", ret);
0348 
0349     return 0;
0350 }
0351 
0352 static struct platform_driver cros_pchg_driver = {
0353     .driver = {
0354         .name = DRV_NAME,
0355     },
0356     .probe = cros_pchg_probe
0357 };
0358 
0359 module_platform_driver(cros_pchg_driver);
0360 
0361 MODULE_LICENSE("GPL");
0362 MODULE_DESCRIPTION("ChromeOS EC peripheral device charger");
0363 MODULE_ALIAS("platform:" DRV_NAME);