diff options
| author | Benson Leung <bleung@chromium.org> | 2017-12-13 05:32:15 -0500 |
|---|---|---|
| committer | Chanwoo Choi <cw00.choi@samsung.com> | 2017-12-15 03:21:49 -0500 |
| commit | c7eb47f9e45226571be31212f6efd4b307d3b59d (patch) | |
| tree | c4277b27d85431d5021a4516cace435d0b384153 /drivers/extcon | |
| parent | 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323 (diff) | |
extcon: usbc-cros-ec: add support to notify USB type cables.
Extend the driver to notify host and device type cables and the presence
of power.
Signed-off-by: Benson Leung <bleung@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Diffstat (limited to 'drivers/extcon')
| -rw-r--r-- | drivers/extcon/extcon-usbc-cros-ec.c | 142 |
1 files changed, 138 insertions, 4 deletions
diff --git a/drivers/extcon/extcon-usbc-cros-ec.c b/drivers/extcon/extcon-usbc-cros-ec.c index 6187f731b29d..6721ab01fe7d 100644 --- a/drivers/extcon/extcon-usbc-cros-ec.c +++ b/drivers/extcon/extcon-usbc-cros-ec.c | |||
| @@ -34,16 +34,26 @@ struct cros_ec_extcon_info { | |||
| 34 | 34 | ||
| 35 | struct notifier_block notifier; | 35 | struct notifier_block notifier; |
| 36 | 36 | ||
| 37 | unsigned int dr; /* data role */ | ||
| 38 | bool pr; /* power role (true if VBUS enabled) */ | ||
| 37 | bool dp; /* DisplayPort enabled */ | 39 | bool dp; /* DisplayPort enabled */ |
| 38 | bool mux; /* SuperSpeed (usb3) enabled */ | 40 | bool mux; /* SuperSpeed (usb3) enabled */ |
| 39 | unsigned int power_type; | 41 | unsigned int power_type; |
| 40 | }; | 42 | }; |
| 41 | 43 | ||
| 42 | static const unsigned int usb_type_c_cable[] = { | 44 | static const unsigned int usb_type_c_cable[] = { |
| 45 | EXTCON_USB, | ||
| 46 | EXTCON_USB_HOST, | ||
| 43 | EXTCON_DISP_DP, | 47 | EXTCON_DISP_DP, |
| 44 | EXTCON_NONE, | 48 | EXTCON_NONE, |
| 45 | }; | 49 | }; |
| 46 | 50 | ||
| 51 | enum usb_data_roles { | ||
| 52 | DR_NONE, | ||
| 53 | DR_HOST, | ||
| 54 | DR_DEVICE, | ||
| 55 | }; | ||
| 56 | |||
| 47 | /** | 57 | /** |
| 48 | * cros_ec_pd_command() - Send a command to the EC. | 58 | * cros_ec_pd_command() - Send a command to the EC. |
| 49 | * @info: pointer to struct cros_ec_extcon_info | 59 | * @info: pointer to struct cros_ec_extcon_info |
| @@ -150,6 +160,7 @@ static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info, | |||
| 150 | pd_control.port = info->port_id; | 160 | pd_control.port = info->port_id; |
| 151 | pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE; | 161 | pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE; |
| 152 | pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE; | 162 | pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE; |
| 163 | pd_control.swap = USB_PD_CTRL_SWAP_NONE; | ||
| 153 | ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1, | 164 | ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1, |
| 154 | &pd_control, sizeof(pd_control), | 165 | &pd_control, sizeof(pd_control), |
| 155 | &resp, sizeof(resp)); | 166 | &resp, sizeof(resp)); |
| @@ -183,11 +194,72 @@ static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info) | |||
| 183 | return resp.num_ports; | 194 | return resp.num_ports; |
| 184 | } | 195 | } |
| 185 | 196 | ||
| 197 | static const char *cros_ec_usb_role_string(unsigned int role) | ||
| 198 | { | ||
| 199 | return role == DR_NONE ? "DISCONNECTED" : | ||
| 200 | (role == DR_HOST ? "DFP" : "UFP"); | ||
| 201 | } | ||
| 202 | |||
| 203 | static const char *cros_ec_usb_power_type_string(unsigned int type) | ||
| 204 | { | ||
| 205 | switch (type) { | ||
| 206 | case USB_CHG_TYPE_NONE: | ||
| 207 | return "USB_CHG_TYPE_NONE"; | ||
| 208 | case USB_CHG_TYPE_PD: | ||
| 209 | return "USB_CHG_TYPE_PD"; | ||
| 210 | case USB_CHG_TYPE_PROPRIETARY: | ||
| 211 | return "USB_CHG_TYPE_PROPRIETARY"; | ||
| 212 | case USB_CHG_TYPE_C: | ||
| 213 | return "USB_CHG_TYPE_C"; | ||
| 214 | case USB_CHG_TYPE_BC12_DCP: | ||
| 215 | return "USB_CHG_TYPE_BC12_DCP"; | ||
| 216 | case USB_CHG_TYPE_BC12_CDP: | ||
| 217 | return "USB_CHG_TYPE_BC12_CDP"; | ||
| 218 | case USB_CHG_TYPE_BC12_SDP: | ||
| 219 | return "USB_CHG_TYPE_BC12_SDP"; | ||
| 220 | case USB_CHG_TYPE_OTHER: | ||
| 221 | return "USB_CHG_TYPE_OTHER"; | ||
| 222 | case USB_CHG_TYPE_VBUS: | ||
| 223 | return "USB_CHG_TYPE_VBUS"; | ||
| 224 | case USB_CHG_TYPE_UNKNOWN: | ||
| 225 | return "USB_CHG_TYPE_UNKNOWN"; | ||
| 226 | default: | ||
| 227 | return "USB_CHG_TYPE_UNKNOWN"; | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | static bool cros_ec_usb_power_type_is_wall_wart(unsigned int type, | ||
| 232 | unsigned int role) | ||
| 233 | { | ||
| 234 | switch (type) { | ||
| 235 | /* FIXME : Guppy, Donnettes, and other chargers will be miscategorized | ||
| 236 | * because they identify with USB_CHG_TYPE_C, but we can't return true | ||
| 237 | * here from that code because that breaks Suzy-Q and other kinds of | ||
| 238 | * USB Type-C cables and peripherals. | ||
| 239 | */ | ||
| 240 | case USB_CHG_TYPE_PROPRIETARY: | ||
| 241 | case USB_CHG_TYPE_BC12_DCP: | ||
| 242 | return true; | ||
| 243 | case USB_CHG_TYPE_PD: | ||
| 244 | case USB_CHG_TYPE_C: | ||
| 245 | case USB_CHG_TYPE_BC12_CDP: | ||
| 246 | case USB_CHG_TYPE_BC12_SDP: | ||
| 247 | case USB_CHG_TYPE_OTHER: | ||
| 248 | case USB_CHG_TYPE_VBUS: | ||
| 249 | case USB_CHG_TYPE_UNKNOWN: | ||
| 250 | case USB_CHG_TYPE_NONE: | ||
| 251 | default: | ||
| 252 | return false; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 186 | static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, | 256 | static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, |
| 187 | bool force) | 257 | bool force) |
| 188 | { | 258 | { |
| 189 | struct device *dev = info->dev; | 259 | struct device *dev = info->dev; |
| 190 | int role, power_type; | 260 | int role, power_type; |
| 261 | unsigned int dr = DR_NONE; | ||
| 262 | bool pr = false; | ||
| 191 | bool polarity = false; | 263 | bool polarity = false; |
| 192 | bool dp = false; | 264 | bool dp = false; |
| 193 | bool mux = false; | 265 | bool mux = false; |
| @@ -206,9 +278,12 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, | |||
| 206 | dev_err(dev, "failed getting role err = %d\n", role); | 278 | dev_err(dev, "failed getting role err = %d\n", role); |
| 207 | return role; | 279 | return role; |
| 208 | } | 280 | } |
| 281 | dev_dbg(dev, "disconnected\n"); | ||
| 209 | } else { | 282 | } else { |
| 210 | int pd_mux_state; | 283 | int pd_mux_state; |
| 211 | 284 | ||
| 285 | dr = (role & PD_CTRL_RESP_ROLE_DATA) ? DR_HOST : DR_DEVICE; | ||
| 286 | pr = (role & PD_CTRL_RESP_ROLE_POWER); | ||
| 212 | pd_mux_state = cros_ec_usb_get_pd_mux_state(info); | 287 | pd_mux_state = cros_ec_usb_get_pd_mux_state(info); |
| 213 | if (pd_mux_state < 0) | 288 | if (pd_mux_state < 0) |
| 214 | pd_mux_state = USB_PD_MUX_USB_ENABLED; | 289 | pd_mux_state = USB_PD_MUX_USB_ENABLED; |
| @@ -216,20 +291,62 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, | |||
| 216 | dp = pd_mux_state & USB_PD_MUX_DP_ENABLED; | 291 | dp = pd_mux_state & USB_PD_MUX_DP_ENABLED; |
| 217 | mux = pd_mux_state & USB_PD_MUX_USB_ENABLED; | 292 | mux = pd_mux_state & USB_PD_MUX_USB_ENABLED; |
| 218 | hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ; | 293 | hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ; |
| 219 | } | ||
| 220 | 294 | ||
| 221 | if (force || info->dp != dp || info->mux != mux || | 295 | dev_dbg(dev, |
| 222 | info->power_type != power_type) { | 296 | "connected role 0x%x pwr type %d dr %d pr %d pol %d mux %d dp %d hpd %d\n", |
| 297 | role, power_type, dr, pr, polarity, mux, dp, hpd); | ||
| 298 | } | ||
| 223 | 299 | ||
| 300 | /* | ||
| 301 | * When there is no USB host (e.g. USB PD charger), | ||
| 302 | * we are not really a UFP for the AP. | ||
| 303 | */ | ||
| 304 | if (dr == DR_DEVICE && | ||
| 305 | cros_ec_usb_power_type_is_wall_wart(power_type, role)) | ||
| 306 | dr = DR_NONE; | ||
| 307 | |||
| 308 | if (force || info->dr != dr || info->pr != pr || info->dp != dp || | ||
| 309 | info->mux != mux || info->power_type != power_type) { | ||
| 310 | bool host_connected = false, device_connected = false; | ||
| 311 | |||
| 312 | dev_dbg(dev, "Type/Role switch! type = %s role = %s\n", | ||
| 313 | cros_ec_usb_power_type_string(power_type), | ||
| 314 | cros_ec_usb_role_string(dr)); | ||
| 315 | info->dr = dr; | ||
| 316 | info->pr = pr; | ||
| 224 | info->dp = dp; | 317 | info->dp = dp; |
| 225 | info->mux = mux; | 318 | info->mux = mux; |
| 226 | info->power_type = power_type; | 319 | info->power_type = power_type; |
| 227 | 320 | ||
| 228 | extcon_set_state(info->edev, EXTCON_DISP_DP, dp); | 321 | if (dr == DR_DEVICE) |
| 322 | device_connected = true; | ||
| 323 | else if (dr == DR_HOST) | ||
| 324 | host_connected = true; | ||
| 229 | 325 | ||
| 326 | extcon_set_state(info->edev, EXTCON_USB, device_connected); | ||
| 327 | extcon_set_state(info->edev, EXTCON_USB_HOST, host_connected); | ||
| 328 | extcon_set_state(info->edev, EXTCON_DISP_DP, dp); | ||
| 329 | extcon_set_property(info->edev, EXTCON_USB, | ||
| 330 | EXTCON_PROP_USB_VBUS, | ||
| 331 | (union extcon_property_value)(int)pr); | ||
| 332 | extcon_set_property(info->edev, EXTCON_USB_HOST, | ||
| 333 | EXTCON_PROP_USB_VBUS, | ||
| 334 | (union extcon_property_value)(int)pr); | ||
| 335 | extcon_set_property(info->edev, EXTCON_USB, | ||
| 336 | EXTCON_PROP_USB_TYPEC_POLARITY, | ||
| 337 | (union extcon_property_value)(int)polarity); | ||
| 338 | extcon_set_property(info->edev, EXTCON_USB_HOST, | ||
| 339 | EXTCON_PROP_USB_TYPEC_POLARITY, | ||
| 340 | (union extcon_property_value)(int)polarity); | ||
| 230 | extcon_set_property(info->edev, EXTCON_DISP_DP, | 341 | extcon_set_property(info->edev, EXTCON_DISP_DP, |
| 231 | EXTCON_PROP_USB_TYPEC_POLARITY, | 342 | EXTCON_PROP_USB_TYPEC_POLARITY, |
| 232 | (union extcon_property_value)(int)polarity); | 343 | (union extcon_property_value)(int)polarity); |
| 344 | extcon_set_property(info->edev, EXTCON_USB, | ||
| 345 | EXTCON_PROP_USB_SS, | ||
| 346 | (union extcon_property_value)(int)mux); | ||
| 347 | extcon_set_property(info->edev, EXTCON_USB_HOST, | ||
| 348 | EXTCON_PROP_USB_SS, | ||
| 349 | (union extcon_property_value)(int)mux); | ||
| 233 | extcon_set_property(info->edev, EXTCON_DISP_DP, | 350 | extcon_set_property(info->edev, EXTCON_DISP_DP, |
| 234 | EXTCON_PROP_USB_SS, | 351 | EXTCON_PROP_USB_SS, |
| 235 | (union extcon_property_value)(int)mux); | 352 | (union extcon_property_value)(int)mux); |
| @@ -237,6 +354,8 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, | |||
| 237 | EXTCON_PROP_DISP_HPD, | 354 | EXTCON_PROP_DISP_HPD, |
| 238 | (union extcon_property_value)(int)hpd); | 355 | (union extcon_property_value)(int)hpd); |
| 239 | 356 | ||
| 357 | extcon_sync(info->edev, EXTCON_USB); | ||
| 358 | extcon_sync(info->edev, EXTCON_USB_HOST); | ||
| 240 | extcon_sync(info->edev, EXTCON_DISP_DP); | 359 | extcon_sync(info->edev, EXTCON_DISP_DP); |
| 241 | 360 | ||
| 242 | } else if (hpd) { | 361 | } else if (hpd) { |
| @@ -322,13 +441,28 @@ static int extcon_cros_ec_probe(struct platform_device *pdev) | |||
| 322 | return ret; | 441 | return ret; |
| 323 | } | 442 | } |
| 324 | 443 | ||
| 444 | extcon_set_property_capability(info->edev, EXTCON_USB, | ||
| 445 | EXTCON_PROP_USB_VBUS); | ||
| 446 | extcon_set_property_capability(info->edev, EXTCON_USB_HOST, | ||
| 447 | EXTCON_PROP_USB_VBUS); | ||
| 448 | extcon_set_property_capability(info->edev, EXTCON_USB, | ||
| 449 | EXTCON_PROP_USB_TYPEC_POLARITY); | ||
| 450 | extcon_set_property_capability(info->edev, EXTCON_USB_HOST, | ||
| 451 | EXTCON_PROP_USB_TYPEC_POLARITY); | ||
| 325 | extcon_set_property_capability(info->edev, EXTCON_DISP_DP, | 452 | extcon_set_property_capability(info->edev, EXTCON_DISP_DP, |
| 326 | EXTCON_PROP_USB_TYPEC_POLARITY); | 453 | EXTCON_PROP_USB_TYPEC_POLARITY); |
| 454 | extcon_set_property_capability(info->edev, EXTCON_USB, | ||
| 455 | EXTCON_PROP_USB_SS); | ||
| 456 | extcon_set_property_capability(info->edev, EXTCON_USB_HOST, | ||
| 457 | EXTCON_PROP_USB_SS); | ||
| 327 | extcon_set_property_capability(info->edev, EXTCON_DISP_DP, | 458 | extcon_set_property_capability(info->edev, EXTCON_DISP_DP, |
| 328 | EXTCON_PROP_USB_SS); | 459 | EXTCON_PROP_USB_SS); |
| 329 | extcon_set_property_capability(info->edev, EXTCON_DISP_DP, | 460 | extcon_set_property_capability(info->edev, EXTCON_DISP_DP, |
| 330 | EXTCON_PROP_DISP_HPD); | 461 | EXTCON_PROP_DISP_HPD); |
| 331 | 462 | ||
| 463 | info->dr = DR_NONE; | ||
| 464 | info->pr = false; | ||
| 465 | |||
| 332 | platform_set_drvdata(pdev, info); | 466 | platform_set_drvdata(pdev, info); |
| 333 | 467 | ||
| 334 | /* Get PD events from the EC */ | 468 | /* Get PD events from the EC */ |
