diff --git a/drivers/power/cros_usbpd-charger.c b/drivers/power/cros_usbpd-charger.c index 82ce65528f65..7c47cbe986d8 100644 --- a/drivers/power/cros_usbpd-charger.c +++ b/drivers/power/cros_usbpd-charger.c @@ -30,14 +30,18 @@ */ #include "../mfd/cros_ec_dev.h" +#define CROS_USB_PD_MAX_PORTS 8 + #define CHARGER_DIR_NAME "CROS_USB_PD_CHARGER%d" #define CHARGER_DIR_NAME_LENGTH sizeof(CHARGER_DIR_NAME) -#define CROS_USB_PD_MAX_PORTS 8 +#define MANUFACTURER_MODEL_LENGTH 32 struct port_data { int port_number; char name[CHARGER_DIR_NAME_LENGTH]; + char manufacturer[MANUFACTURER_MODEL_LENGTH]; + char model_name[MANUFACTURER_MODEL_LENGTH]; struct power_supply psy; int psy_type; int psy_online; @@ -89,6 +93,26 @@ int ec_command(struct charger_data *charger, int command, return cros_ec_cmd_xfer(ec_device, &msg); } +static int set_ec_usb_pd_override_ports(struct charger_data *charger, + int port_num) +{ + struct device *dev = charger->dev; + struct ec_params_charge_port_override req; + int ret; + + req.override_port = port_num; + + ret = ec_command(charger, EC_CMD_PD_CHARGE_PORT_OVERRIDE, + (uint8_t *)&req, sizeof(req), + NULL, 0); + if (ret < 0) { + dev_info(dev, "Port Override command returned 0x%x\n", ret); + return -EINVAL; + } + + return 0; +} + static int get_ec_num_ports(struct charger_data *charger, int *num_ports) { struct device *dev = charger->dev; @@ -109,7 +133,35 @@ static int get_ec_num_ports(struct charger_data *charger, int *num_ports) return 0; } -static int get_ec_port_status(struct port_data *port) +static int get_ec_usb_pd_discovery_info(struct port_data *port) +{ + struct charger_data *charger = port->charger; + struct device *dev = charger->dev; + struct ec_params_usb_pd_info_request req; + struct ec_params_usb_pd_discovery_entry resp; + int ret; + + req.port = port->port_number; + + ret = ec_command(charger, EC_CMD_USB_PD_DISCOVERY, + (uint8_t *)&req, sizeof(req), + (uint8_t *)&resp, sizeof(resp)); + if (ret < 0) { + dev_err(dev, "Unable to query Discovery info (err:0x%x)\n", + ret); + return -EINVAL; + } + + dev_dbg(dev, "Port %d: VID = 0x%x, PID=0x%x, PTYPE=0x%x\n", + port->port_number, resp.vid, resp.pid, resp.ptype); + + snprintf(port->manufacturer, MANUFACTURER_MODEL_LENGTH, "%x", resp.vid); + snprintf(port->model_name, MANUFACTURER_MODEL_LENGTH, "%x", resp.pid); + + return 0; +} + +static int get_ec_usb_pd_power_info(struct port_data *port) { struct charger_data *charger = port->charger; struct device *dev = charger->dev; @@ -121,9 +173,8 @@ static int get_ec_port_status(struct port_data *port) ret = ec_command(charger, EC_CMD_USB_PD_POWER_INFO, (uint8_t *)&req, sizeof(req), (uint8_t *)&resp, sizeof(resp)); - if (!ret) { - WARN(1, "%s: Unable to query PD power info (err:0x%x)\n", - dev_name(dev), ret); + if (ret < 0) { + dev_err(dev, "Unable to query PD power info (err:0x%x)\n", ret); return -EINVAL; } @@ -150,6 +201,7 @@ static int get_ec_port_status(struct port_data *port) break; default: dev_err(dev, "Unknown role %d\n", resp.role); + break; } switch (resp.type) { @@ -192,10 +244,20 @@ static int get_ec_port_status(struct port_data *port) port->port_number, resp.max_power); port->psy_power_max = resp.max_power; - return 0; } +static int get_ec_port_status(struct port_data *port) +{ + int ret; + + ret = get_ec_usb_pd_power_info(port); + if (ret < 0) + return ret; + + return get_ec_usb_pd_discovery_info(port); +} + static void cros_usb_pd_charger_power_changed(struct power_supply *psy) { struct port_data *port = container_of(psy, struct port_data, psy); @@ -219,7 +281,7 @@ static int cros_usb_pd_charger_get_prop(struct power_supply *psy, /* TODO: use cached values instead? */ ret = get_ec_port_status(port); - if (ret) { + if (ret < 0) { dev_err(dev, "Failed to get port status (err:0x%x)\n", ret); return -EINVAL; } @@ -246,10 +308,10 @@ static int cros_usb_pd_charger_get_prop(struct power_supply *psy, val->intval = 0; break; case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = NULL; + val->strval = port->model_name; break; case POWER_SUPPLY_PROP_MANUFACTURER: - val->strval = NULL; + val->strval = port->manufacturer; break; default: return -EINVAL; @@ -262,18 +324,21 @@ static int cros_usb_pd_charger_set_prop(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { + struct port_data *port = container_of(psy, struct port_data, psy); + struct charger_data *charger = port->charger; + int port_number; + switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - /* - * TODO: Send a TBD host command to the EC to change the - * charging role of the port associated with this psy. - */ - break; case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: /* - * TODO: Send a TBD host command to the EC with port number - * set to -1. + * A value of -1 implies switching to battery as the power + * source. Any other value implies using this port as the + * power source. */ + port_number = val->intval; + if (port_number != -1) + port_number = port->port_number; + return set_ec_usb_pd_override_ports(charger, port_number); break; default: return -EINVAL; @@ -287,7 +352,6 @@ static int cros_usb_pd_charger_is_writeable(struct power_supply *psy, int ret; switch (psp) { - case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: ret = 1; break; @@ -326,7 +390,7 @@ static int cros_usb_pd_charger_probe(struct platform_device *pd) charger = devm_kzalloc(dev, sizeof(struct charger_data), GFP_KERNEL); if (!charger) { - WARN(1, "%s: Failed to alloc charger\n", dev_name(dev)); + dev_err(dev, "Failed to alloc charger. Failing probe.\n"); return -ENOMEM; } @@ -336,9 +400,10 @@ static int cros_usb_pd_charger_probe(struct platform_device *pd) platform_set_drvdata(pd, charger); - if (get_ec_num_ports(charger, &charger->num_charger_ports)) { + if ((get_ec_num_ports(charger, &charger->num_charger_ports) < 0) || + !charger->num_charger_ports) { /* - * This can happen on a system that doesn't supprt USB PD. + * This can happen on a system that doesn't support USB PD. * Log a message, but no need to warn. */ dev_info(dev, "No charging ports found\n"); @@ -382,7 +447,7 @@ static int cros_usb_pd_charger_probe(struct platform_device *pd) if (!charger->num_registered_psy) { ret = -ENODEV; - WARN(1, "%s: No power supplies registered\n", dev_name(dev)); + dev_err(dev, "No power supplies registered\n"); goto fail; } @@ -403,6 +468,7 @@ fail_nowarn: devm_kfree(dev, charger); } + dev_info(dev, "Failing probe (err:0x%x)\n", ret); return ret; }