Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge remote-tracking branch 'origin/ib-extcon-mfd-4.14' into extcon-next

+522
+24
Documentation/devicetree/bindings/extcon/extcon-usbc-cros-ec.txt
··· 1 + ChromeOS EC USB Type-C cable and accessories detection 2 + 3 + On ChromeOS systems with USB Type C ports, the ChromeOS Embedded Controller is 4 + able to detect the state of external accessories such as display adapters 5 + or USB devices when said accessories are attached or detached. 6 + 7 + The node for this device must be under a cros-ec node like google,cros-ec-spi 8 + or google,cros-ec-i2c. 9 + 10 + Required properties: 11 + - compatible: Should be "google,extcon-usbc-cros-ec". 12 + - google,usb-port-id: Specifies the USB port ID to use. 13 + 14 + Example: 15 + cros-ec@0 { 16 + compatible = "google,cros-ec-i2c"; 17 + 18 + ... 19 + 20 + extcon { 21 + compatible = "google,extcon-usbc-cros-ec"; 22 + google,usb-port-id = <0>; 23 + }; 24 + }
+7
drivers/extcon/Kconfig
··· 150 150 Say Y here to enable GPIO based USB cable detection extcon support. 151 151 Used typically if GPIO is used for USB ID pin detection. 152 152 153 + config EXTCON_USBC_CROS_EC 154 + tristate "ChromeOS Embedded Controller EXTCON support" 155 + depends on MFD_CROS_EC 156 + help 157 + Say Y here to enable USB Type C cable detection extcon support when 158 + using Chrome OS EC based USB Type-C ports. 159 + 153 160 endif
+1
drivers/extcon/Makefile
··· 20 20 obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o 21 21 obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o 22 22 obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o 23 + obj-$(CONFIG_EXTCON_USBC_CROS_EC) += extcon-usbc-cros-ec.o
+415
drivers/extcon/extcon-usbc-cros-ec.c
··· 1 + /** 2 + * drivers/extcon/extcon-usbc-cros-ec - ChromeOS Embedded Controller extcon 3 + * 4 + * Copyright (C) 2017 Google, Inc 5 + * Author: Benson Leung <bleung@chromium.org> 6 + * 7 + * This software is licensed under the terms of the GNU General Public 8 + * License version 2, as published by the Free Software Foundation, and 9 + * may be copied, distributed, and modified under those terms. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + */ 16 + 17 + #include <linux/extcon.h> 18 + #include <linux/kernel.h> 19 + #include <linux/mfd/cros_ec.h> 20 + #include <linux/module.h> 21 + #include <linux/notifier.h> 22 + #include <linux/of.h> 23 + #include <linux/platform_device.h> 24 + #include <linux/slab.h> 25 + #include <linux/sched.h> 26 + 27 + struct cros_ec_extcon_info { 28 + struct device *dev; 29 + struct extcon_dev *edev; 30 + 31 + int port_id; 32 + 33 + struct cros_ec_device *ec; 34 + 35 + struct notifier_block notifier; 36 + 37 + bool dp; /* DisplayPort enabled */ 38 + bool mux; /* SuperSpeed (usb3) enabled */ 39 + unsigned int power_type; 40 + }; 41 + 42 + static const unsigned int usb_type_c_cable[] = { 43 + EXTCON_DISP_DP, 44 + EXTCON_NONE, 45 + }; 46 + 47 + /** 48 + * cros_ec_pd_command() - Send a command to the EC. 49 + * @info: pointer to struct cros_ec_extcon_info 50 + * @command: EC command 51 + * @version: EC command version 52 + * @outdata: EC command output data 53 + * @outsize: Size of outdata 54 + * @indata: EC command input data 55 + * @insize: Size of indata 56 + * 57 + * Return: 0 on success, <0 on failure. 58 + */ 59 + static int cros_ec_pd_command(struct cros_ec_extcon_info *info, 60 + unsigned int command, 61 + unsigned int version, 62 + void *outdata, 63 + unsigned int outsize, 64 + void *indata, 65 + unsigned int insize) 66 + { 67 + struct cros_ec_command *msg; 68 + int ret; 69 + 70 + msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); 71 + 72 + msg->version = version; 73 + msg->command = command; 74 + msg->outsize = outsize; 75 + msg->insize = insize; 76 + 77 + if (outsize) 78 + memcpy(msg->data, outdata, outsize); 79 + 80 + ret = cros_ec_cmd_xfer_status(info->ec, msg); 81 + if (ret >= 0 && insize) 82 + memcpy(indata, msg->data, insize); 83 + 84 + kfree(msg); 85 + return ret; 86 + } 87 + 88 + /** 89 + * cros_ec_usb_get_power_type() - Get power type info about PD device attached 90 + * to given port. 91 + * @info: pointer to struct cros_ec_extcon_info 92 + * 93 + * Return: power type on success, <0 on failure. 94 + */ 95 + static int cros_ec_usb_get_power_type(struct cros_ec_extcon_info *info) 96 + { 97 + struct ec_params_usb_pd_power_info req; 98 + struct ec_response_usb_pd_power_info resp; 99 + int ret; 100 + 101 + req.port = info->port_id; 102 + ret = cros_ec_pd_command(info, EC_CMD_USB_PD_POWER_INFO, 0, 103 + &req, sizeof(req), &resp, sizeof(resp)); 104 + if (ret < 0) 105 + return ret; 106 + 107 + return resp.type; 108 + } 109 + 110 + /** 111 + * cros_ec_usb_get_pd_mux_state() - Get PD mux state for given port. 112 + * @info: pointer to struct cros_ec_extcon_info 113 + * 114 + * Return: PD mux state on success, <0 on failure. 115 + */ 116 + static int cros_ec_usb_get_pd_mux_state(struct cros_ec_extcon_info *info) 117 + { 118 + struct ec_params_usb_pd_mux_info req; 119 + struct ec_response_usb_pd_mux_info resp; 120 + int ret; 121 + 122 + req.port = info->port_id; 123 + ret = cros_ec_pd_command(info, EC_CMD_USB_PD_MUX_INFO, 0, 124 + &req, sizeof(req), 125 + &resp, sizeof(resp)); 126 + if (ret < 0) 127 + return ret; 128 + 129 + return resp.flags; 130 + } 131 + 132 + /** 133 + * cros_ec_usb_get_role() - Get role info about possible PD device attached to a 134 + * given port. 135 + * @info: pointer to struct cros_ec_extcon_info 136 + * @polarity: pointer to cable polarity (return value) 137 + * 138 + * Return: role info on success, -ENOTCONN if no cable is connected, <0 on 139 + * failure. 140 + */ 141 + static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info, 142 + bool *polarity) 143 + { 144 + struct ec_params_usb_pd_control pd_control; 145 + struct ec_response_usb_pd_control_v1 resp; 146 + int ret; 147 + 148 + pd_control.port = info->port_id; 149 + pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE; 150 + pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE; 151 + ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1, 152 + &pd_control, sizeof(pd_control), 153 + &resp, sizeof(resp)); 154 + if (ret < 0) 155 + return ret; 156 + 157 + if (!(resp.enabled & PD_CTRL_RESP_ENABLED_CONNECTED)) 158 + return -ENOTCONN; 159 + 160 + *polarity = resp.polarity; 161 + 162 + return resp.role; 163 + } 164 + 165 + /** 166 + * cros_ec_pd_get_num_ports() - Get number of EC charge ports. 167 + * @info: pointer to struct cros_ec_extcon_info 168 + * 169 + * Return: number of ports on success, <0 on failure. 170 + */ 171 + static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info) 172 + { 173 + struct ec_response_usb_pd_ports resp; 174 + int ret; 175 + 176 + ret = cros_ec_pd_command(info, EC_CMD_USB_PD_PORTS, 177 + 0, NULL, 0, &resp, sizeof(resp)); 178 + if (ret < 0) 179 + return ret; 180 + 181 + return resp.num_ports; 182 + } 183 + 184 + static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, 185 + bool force) 186 + { 187 + struct device *dev = info->dev; 188 + int role, power_type; 189 + bool polarity = false; 190 + bool dp = false; 191 + bool mux = false; 192 + bool hpd = false; 193 + 194 + power_type = cros_ec_usb_get_power_type(info); 195 + if (power_type < 0) { 196 + dev_err(dev, "failed getting power type err = %d\n", 197 + power_type); 198 + return power_type; 199 + } 200 + 201 + role = cros_ec_usb_get_role(info, &polarity); 202 + if (role < 0) { 203 + if (role != -ENOTCONN) { 204 + dev_err(dev, "failed getting role err = %d\n", role); 205 + return role; 206 + } 207 + } else { 208 + int pd_mux_state; 209 + 210 + pd_mux_state = cros_ec_usb_get_pd_mux_state(info); 211 + if (pd_mux_state < 0) 212 + pd_mux_state = USB_PD_MUX_USB_ENABLED; 213 + 214 + dp = pd_mux_state & USB_PD_MUX_DP_ENABLED; 215 + mux = pd_mux_state & USB_PD_MUX_USB_ENABLED; 216 + hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ; 217 + } 218 + 219 + if (force || info->dp != dp || info->mux != mux || 220 + info->power_type != power_type) { 221 + 222 + info->dp = dp; 223 + info->mux = mux; 224 + info->power_type = power_type; 225 + 226 + extcon_set_state(info->edev, EXTCON_DISP_DP, dp); 227 + 228 + extcon_set_property(info->edev, EXTCON_DISP_DP, 229 + EXTCON_PROP_USB_TYPEC_POLARITY, 230 + (union extcon_property_value)(int)polarity); 231 + extcon_set_property(info->edev, EXTCON_DISP_DP, 232 + EXTCON_PROP_USB_SS, 233 + (union extcon_property_value)(int)mux); 234 + extcon_set_property(info->edev, EXTCON_DISP_DP, 235 + EXTCON_PROP_DISP_HPD, 236 + (union extcon_property_value)(int)hpd); 237 + 238 + extcon_sync(info->edev, EXTCON_DISP_DP); 239 + 240 + } else if (hpd) { 241 + extcon_set_property(info->edev, EXTCON_DISP_DP, 242 + EXTCON_PROP_DISP_HPD, 243 + (union extcon_property_value)(int)hpd); 244 + extcon_sync(info->edev, EXTCON_DISP_DP); 245 + } 246 + 247 + return 0; 248 + } 249 + 250 + static int extcon_cros_ec_event(struct notifier_block *nb, 251 + unsigned long queued_during_suspend, 252 + void *_notify) 253 + { 254 + struct cros_ec_extcon_info *info; 255 + struct cros_ec_device *ec; 256 + u32 host_event; 257 + 258 + info = container_of(nb, struct cros_ec_extcon_info, notifier); 259 + ec = info->ec; 260 + 261 + host_event = cros_ec_get_host_event(ec); 262 + if (host_event & (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) | 263 + EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX))) { 264 + extcon_cros_ec_detect_cable(info, false); 265 + return NOTIFY_OK; 266 + } 267 + 268 + return NOTIFY_DONE; 269 + } 270 + 271 + static int extcon_cros_ec_probe(struct platform_device *pdev) 272 + { 273 + struct cros_ec_extcon_info *info; 274 + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); 275 + struct device *dev = &pdev->dev; 276 + struct device_node *np = dev->of_node; 277 + int numports, ret; 278 + 279 + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); 280 + if (!info) 281 + return -ENOMEM; 282 + 283 + info->dev = dev; 284 + info->ec = ec; 285 + 286 + if (np) { 287 + u32 port; 288 + 289 + ret = of_property_read_u32(np, "google,usb-port-id", &port); 290 + if (ret < 0) { 291 + dev_err(dev, "Missing google,usb-port-id property\n"); 292 + return ret; 293 + } 294 + info->port_id = port; 295 + } else { 296 + info->port_id = pdev->id; 297 + } 298 + 299 + numports = cros_ec_pd_get_num_ports(info); 300 + if (numports < 0) { 301 + dev_err(dev, "failed getting number of ports! ret = %d\n", 302 + numports); 303 + return numports; 304 + } 305 + 306 + if (info->port_id >= numports) { 307 + dev_err(dev, "This system only supports %d ports\n", numports); 308 + return -ENODEV; 309 + } 310 + 311 + info->edev = devm_extcon_dev_allocate(dev, usb_type_c_cable); 312 + if (IS_ERR(info->edev)) { 313 + dev_err(dev, "failed to allocate extcon device\n"); 314 + return -ENOMEM; 315 + } 316 + 317 + ret = devm_extcon_dev_register(dev, info->edev); 318 + if (ret < 0) { 319 + dev_err(dev, "failed to register extcon device\n"); 320 + return ret; 321 + } 322 + 323 + extcon_set_property_capability(info->edev, EXTCON_DISP_DP, 324 + EXTCON_PROP_USB_TYPEC_POLARITY); 325 + extcon_set_property_capability(info->edev, EXTCON_DISP_DP, 326 + EXTCON_PROP_USB_SS); 327 + extcon_set_property_capability(info->edev, EXTCON_DISP_DP, 328 + EXTCON_PROP_DISP_HPD); 329 + 330 + platform_set_drvdata(pdev, info); 331 + 332 + /* Get PD events from the EC */ 333 + info->notifier.notifier_call = extcon_cros_ec_event; 334 + ret = blocking_notifier_chain_register(&info->ec->event_notifier, 335 + &info->notifier); 336 + if (ret < 0) { 337 + dev_err(dev, "failed to register notifier\n"); 338 + return ret; 339 + } 340 + 341 + /* Perform initial detection */ 342 + ret = extcon_cros_ec_detect_cable(info, true); 343 + if (ret < 0) { 344 + dev_err(dev, "failed to detect initial cable state\n"); 345 + goto unregister_notifier; 346 + } 347 + 348 + return 0; 349 + 350 + unregister_notifier: 351 + blocking_notifier_chain_unregister(&info->ec->event_notifier, 352 + &info->notifier); 353 + return ret; 354 + } 355 + 356 + static int extcon_cros_ec_remove(struct platform_device *pdev) 357 + { 358 + struct cros_ec_extcon_info *info = platform_get_drvdata(pdev); 359 + 360 + blocking_notifier_chain_unregister(&info->ec->event_notifier, 361 + &info->notifier); 362 + 363 + return 0; 364 + } 365 + 366 + #ifdef CONFIG_PM_SLEEP 367 + static int extcon_cros_ec_suspend(struct device *dev) 368 + { 369 + return 0; 370 + } 371 + 372 + static int extcon_cros_ec_resume(struct device *dev) 373 + { 374 + int ret; 375 + struct cros_ec_extcon_info *info = dev_get_drvdata(dev); 376 + 377 + ret = extcon_cros_ec_detect_cable(info, true); 378 + if (ret < 0) 379 + dev_err(dev, "failed to detect cable state on resume\n"); 380 + 381 + return 0; 382 + } 383 + 384 + static const struct dev_pm_ops extcon_cros_ec_dev_pm_ops = { 385 + SET_SYSTEM_SLEEP_PM_OPS(extcon_cros_ec_suspend, extcon_cros_ec_resume) 386 + }; 387 + 388 + #define DEV_PM_OPS (&extcon_cros_ec_dev_pm_ops) 389 + #else 390 + #define DEV_PM_OPS NULL 391 + #endif /* CONFIG_PM_SLEEP */ 392 + 393 + #ifdef CONFIG_OF 394 + static const struct of_device_id extcon_cros_ec_of_match[] = { 395 + { .compatible = "google,extcon-usbc-cros-ec" }, 396 + { /* sentinel */ } 397 + }; 398 + MODULE_DEVICE_TABLE(of, extcon_cros_ec_of_match); 399 + #endif /* CONFIG_OF */ 400 + 401 + static struct platform_driver extcon_cros_ec_driver = { 402 + .driver = { 403 + .name = "extcon-usbc-cros-ec", 404 + .of_match_table = of_match_ptr(extcon_cros_ec_of_match), 405 + .pm = DEV_PM_OPS, 406 + }, 407 + .remove = extcon_cros_ec_remove, 408 + .probe = extcon_cros_ec_probe, 409 + }; 410 + 411 + module_platform_driver(extcon_cros_ec_driver); 412 + 413 + MODULE_DESCRIPTION("ChromeOS Embedded Controller extcon driver"); 414 + MODULE_AUTHOR("Benson Leung <bleung@chromium.org>"); 415 + MODULE_LICENSE("GPL");
+75
include/linux/mfd/cros_ec_commands.h
··· 285 285 EC_HOST_EVENT_HANG_DETECT = 20, 286 286 /* Hang detect logic detected a hang and warm rebooted the AP */ 287 287 EC_HOST_EVENT_HANG_REBOOT = 21, 288 + /* PD MCU triggering host event */ 289 + EC_HOST_EVENT_PD_MCU = 22, 290 + 291 + /* EC desires to change state of host-controlled USB mux */ 292 + EC_HOST_EVENT_USB_MUX = 28, 288 293 289 294 /* 290 295 * The high bit of the event mask is not used as a host event code. If ··· 2908 2903 uint8_t port; 2909 2904 uint8_t role; 2910 2905 uint8_t mux; 2906 + } __packed; 2907 + 2908 + #define PD_CTRL_RESP_ENABLED_COMMS (1 << 0) /* Communication enabled */ 2909 + #define PD_CTRL_RESP_ENABLED_CONNECTED (1 << 1) /* Device connected */ 2910 + #define PD_CTRL_RESP_ENABLED_PD_CAPABLE (1 << 2) /* Partner is PD capable */ 2911 + 2912 + struct ec_response_usb_pd_control_v1 { 2913 + uint8_t enabled; 2914 + uint8_t role; 2915 + uint8_t polarity; 2916 + char state[32]; 2917 + } __packed; 2918 + 2919 + #define EC_CMD_USB_PD_PORTS 0x102 2920 + 2921 + struct ec_response_usb_pd_ports { 2922 + uint8_t num_ports; 2923 + } __packed; 2924 + 2925 + #define EC_CMD_USB_PD_POWER_INFO 0x103 2926 + 2927 + #define PD_POWER_CHARGING_PORT 0xff 2928 + struct ec_params_usb_pd_power_info { 2929 + uint8_t port; 2930 + } __packed; 2931 + 2932 + enum usb_chg_type { 2933 + USB_CHG_TYPE_NONE, 2934 + USB_CHG_TYPE_PD, 2935 + USB_CHG_TYPE_C, 2936 + USB_CHG_TYPE_PROPRIETARY, 2937 + USB_CHG_TYPE_BC12_DCP, 2938 + USB_CHG_TYPE_BC12_CDP, 2939 + USB_CHG_TYPE_BC12_SDP, 2940 + USB_CHG_TYPE_OTHER, 2941 + USB_CHG_TYPE_VBUS, 2942 + USB_CHG_TYPE_UNKNOWN, 2943 + }; 2944 + 2945 + struct usb_chg_measures { 2946 + uint16_t voltage_max; 2947 + uint16_t voltage_now; 2948 + uint16_t current_max; 2949 + uint16_t current_lim; 2950 + } __packed; 2951 + 2952 + struct ec_response_usb_pd_power_info { 2953 + uint8_t role; 2954 + uint8_t type; 2955 + uint8_t dualrole; 2956 + uint8_t reserved1; 2957 + struct usb_chg_measures meas; 2958 + uint32_t max_power; 2959 + } __packed; 2960 + 2961 + /* Get info about USB-C SS muxes */ 2962 + #define EC_CMD_USB_PD_MUX_INFO 0x11a 2963 + 2964 + struct ec_params_usb_pd_mux_info { 2965 + uint8_t port; /* USB-C port number */ 2966 + } __packed; 2967 + 2968 + /* Flags representing mux state */ 2969 + #define USB_PD_MUX_USB_ENABLED (1 << 0) 2970 + #define USB_PD_MUX_DP_ENABLED (1 << 1) 2971 + #define USB_PD_MUX_POLARITY_INVERTED (1 << 2) 2972 + #define USB_PD_MUX_HPD_IRQ (1 << 3) 2973 + 2974 + struct ec_response_usb_pd_mux_info { 2975 + uint8_t flags; /* USB_PD_MUX_*-encoded USB mux state */ 2911 2976 } __packed; 2912 2977 2913 2978 /*****************************************************************************/