0001
0002
0003
0004
0005
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
0288
0289
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);