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

usb: typec: Register a device for every mode

Before a device was created for every discovered SVID, but
this will create a device for every discovered mode of every
SVID. The idea is to make it easier to create mode specific
drivers once a bus for the alternate mode is added.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Tested-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Heikki Krogerus and committed by
Greg Kroah-Hartman
4ab8c18d 93dd2112

+88 -176
+58 -122
drivers/usb/typec/class.c
··· 13 13 #include <linux/usb/typec.h> 14 14 #include <linux/usb/typec_mux.h> 15 15 16 - struct typec_mode { 17 - int index; 18 - u32 vdo; 19 - char *desc; 20 - enum typec_port_type roles; 21 - 22 - struct typec_altmode *alt_mode; 23 - 24 - unsigned int active:1; 25 - 26 - char group_name[6]; 27 - struct attribute_group group; 28 - struct attribute *attrs[5]; 29 - struct device_attribute vdo_attr; 30 - struct device_attribute desc_attr; 31 - struct device_attribute active_attr; 32 - struct device_attribute roles_attr; 33 - }; 34 - 35 16 struct typec_altmode { 36 17 struct device dev; 37 18 u16 svid; 38 - int n_modes; 39 - struct typec_mode modes[ALTMODE_MAX_MODES]; 40 - const struct attribute_group *mode_groups[ALTMODE_MAX_MODES]; 19 + u8 mode; 20 + 21 + u32 vdo; 22 + char *desc; 23 + enum typec_port_type roles; 24 + unsigned int active:1; 25 + 26 + struct attribute *attrs[5]; 27 + char group_name[6]; 28 + struct attribute_group group; 29 + const struct attribute_group *groups[2]; 41 30 }; 42 31 43 32 struct typec_plug { ··· 166 177 /** 167 178 * typec_altmode_update_active - Report Enter/Exit mode 168 179 * @alt: Handle to the alternate mode 169 - * @mode: Mode index 170 180 * @active: True when the mode has been entered 171 181 * 172 182 * If a partner or cable plug executes Enter/Exit Mode command successfully, the 173 183 * drivers use this routine to report the updated state of the mode. 174 184 */ 175 - void typec_altmode_update_active(struct typec_altmode *alt, int mode, 176 - bool active) 185 + void typec_altmode_update_active(struct typec_altmode *alt, bool active) 177 186 { 178 - struct typec_mode *m = &alt->modes[mode]; 179 187 char dir[6]; 180 188 181 - if (m->active == active) 189 + if (alt->active == active) 182 190 return; 183 191 184 - m->active = active; 185 - snprintf(dir, sizeof(dir), "mode%d", mode); 192 + alt->active = active; 193 + snprintf(dir, sizeof(dir), "mode%d", alt->mode); 186 194 sysfs_notify(&alt->dev.kobj, dir, "active"); 187 195 kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE); 188 196 } ··· 206 220 EXPORT_SYMBOL_GPL(typec_altmode2port); 207 221 208 222 static ssize_t 209 - typec_altmode_vdo_show(struct device *dev, struct device_attribute *attr, 210 - char *buf) 223 + vdo_show(struct device *dev, struct device_attribute *attr, char *buf) 211 224 { 212 - struct typec_mode *mode = container_of(attr, struct typec_mode, 213 - vdo_attr); 225 + struct typec_altmode *alt = to_altmode(dev); 214 226 215 - return sprintf(buf, "0x%08x\n", mode->vdo); 227 + return sprintf(buf, "0x%08x\n", alt->vdo); 216 228 } 229 + static DEVICE_ATTR_RO(vdo); 217 230 218 231 static ssize_t 219 - typec_altmode_desc_show(struct device *dev, struct device_attribute *attr, 220 - char *buf) 232 + description_show(struct device *dev, struct device_attribute *attr, char *buf) 221 233 { 222 - struct typec_mode *mode = container_of(attr, struct typec_mode, 223 - desc_attr); 234 + struct typec_altmode *alt = to_altmode(dev); 224 235 225 - return sprintf(buf, "%s\n", mode->desc ? mode->desc : ""); 236 + return sprintf(buf, "%s\n", alt->desc ? alt->desc : ""); 226 237 } 238 + static DEVICE_ATTR_RO(description); 227 239 228 240 static ssize_t 229 - typec_altmode_active_show(struct device *dev, struct device_attribute *attr, 230 - char *buf) 241 + active_show(struct device *dev, struct device_attribute *attr, char *buf) 231 242 { 232 - struct typec_mode *mode = container_of(attr, struct typec_mode, 233 - active_attr); 243 + struct typec_altmode *alt = to_altmode(dev); 234 244 235 - return sprintf(buf, "%s\n", mode->active ? "yes" : "no"); 245 + return sprintf(buf, "%s\n", alt->active ? "yes" : "no"); 236 246 } 237 247 238 - static ssize_t 239 - typec_altmode_active_store(struct device *dev, struct device_attribute *attr, 240 - const char *buf, size_t size) 248 + static ssize_t active_store(struct device *dev, struct device_attribute *attr, 249 + const char *buf, size_t size) 241 250 { 242 - struct typec_mode *mode = container_of(attr, struct typec_mode, 243 - active_attr); 244 - struct typec_port *port = typec_altmode2port(mode->alt_mode); 251 + struct typec_altmode *alt = to_altmode(dev); 252 + struct typec_port *port = typec_altmode2port(alt); 245 253 bool activate; 246 254 int ret; 247 255 ··· 246 266 if (ret) 247 267 return ret; 248 268 249 - ret = port->cap->activate_mode(port->cap, mode->index, activate); 269 + ret = port->cap->activate_mode(port->cap, alt->mode, activate); 250 270 if (ret) 251 271 return ret; 252 272 253 273 return size; 254 274 } 275 + static DEVICE_ATTR_RW(active); 255 276 256 277 static ssize_t 257 - typec_altmode_roles_show(struct device *dev, struct device_attribute *attr, 258 - char *buf) 278 + supported_roles_show(struct device *dev, struct device_attribute *attr, 279 + char *buf) 259 280 { 260 - struct typec_mode *mode = container_of(attr, struct typec_mode, 261 - roles_attr); 281 + struct typec_altmode *alt = to_altmode(dev); 262 282 ssize_t ret; 263 283 264 - switch (mode->roles) { 284 + switch (alt->roles) { 265 285 case TYPEC_PORT_SRC: 266 286 ret = sprintf(buf, "source\n"); 267 287 break; ··· 275 295 } 276 296 return ret; 277 297 } 298 + static DEVICE_ATTR_RO(supported_roles); 278 299 279 - static void typec_init_modes(struct typec_altmode *alt, 280 - const struct typec_mode_desc *desc, bool is_port) 300 + static void typec_altmode_release(struct device *dev) 281 301 { 282 - int i; 302 + struct typec_altmode *alt = to_altmode(dev); 283 303 284 - for (i = 0; i < alt->n_modes; i++, desc++) { 285 - struct typec_mode *mode = &alt->modes[i]; 286 - 287 - /* Not considering the human readable description critical */ 288 - mode->desc = kstrdup(desc->desc, GFP_KERNEL); 289 - if (desc->desc && !mode->desc) 290 - dev_err(&alt->dev, "failed to copy mode%d desc\n", i); 291 - 292 - mode->alt_mode = alt; 293 - mode->vdo = desc->vdo; 294 - mode->roles = desc->roles; 295 - mode->index = desc->index; 296 - sprintf(mode->group_name, "mode%d", desc->index); 297 - 298 - sysfs_attr_init(&mode->vdo_attr.attr); 299 - mode->vdo_attr.attr.name = "vdo"; 300 - mode->vdo_attr.attr.mode = 0444; 301 - mode->vdo_attr.show = typec_altmode_vdo_show; 302 - 303 - sysfs_attr_init(&mode->desc_attr.attr); 304 - mode->desc_attr.attr.name = "description"; 305 - mode->desc_attr.attr.mode = 0444; 306 - mode->desc_attr.show = typec_altmode_desc_show; 307 - 308 - sysfs_attr_init(&mode->active_attr.attr); 309 - mode->active_attr.attr.name = "active"; 310 - mode->active_attr.attr.mode = 0644; 311 - mode->active_attr.show = typec_altmode_active_show; 312 - mode->active_attr.store = typec_altmode_active_store; 313 - 314 - mode->attrs[0] = &mode->vdo_attr.attr; 315 - mode->attrs[1] = &mode->desc_attr.attr; 316 - mode->attrs[2] = &mode->active_attr.attr; 317 - 318 - /* With ports, list the roles that the mode is supported with */ 319 - if (is_port) { 320 - sysfs_attr_init(&mode->roles_attr.attr); 321 - mode->roles_attr.attr.name = "supported_roles"; 322 - mode->roles_attr.attr.mode = 0444; 323 - mode->roles_attr.show = typec_altmode_roles_show; 324 - 325 - mode->attrs[3] = &mode->roles_attr.attr; 326 - } 327 - 328 - mode->group.attrs = mode->attrs; 329 - mode->group.name = mode->group_name; 330 - 331 - alt->mode_groups[i] = &mode->group; 332 - } 304 + kfree(alt); 333 305 } 334 306 335 307 static ssize_t svid_show(struct device *dev, struct device_attribute *attr, ··· 298 366 NULL 299 367 }; 300 368 ATTRIBUTE_GROUPS(typec_altmode); 301 - 302 - static void typec_altmode_release(struct device *dev) 303 - { 304 - struct typec_altmode *alt = to_altmode(dev); 305 - int i; 306 - 307 - for (i = 0; i < alt->n_modes; i++) 308 - kfree(alt->modes[i].desc); 309 - kfree(alt); 310 - } 311 369 312 370 static const struct device_type typec_altmode_dev_type = { 313 371 .name = "typec_alternate_mode", ··· 317 395 return ERR_PTR(-ENOMEM); 318 396 319 397 alt->svid = desc->svid; 320 - alt->n_modes = desc->n_modes; 321 - typec_init_modes(alt, desc->modes, is_typec_port(parent)); 398 + alt->mode = desc->mode; 399 + alt->vdo = desc->vdo; 400 + alt->roles = desc->roles; 401 + 402 + alt->attrs[0] = &dev_attr_vdo.attr; 403 + alt->attrs[1] = &dev_attr_description.attr; 404 + alt->attrs[2] = &dev_attr_active.attr; 405 + 406 + if (is_typec_port(parent)) 407 + alt->attrs[3] = &dev_attr_supported_roles.attr; 408 + 409 + sprintf(alt->group_name, "mode%d", desc->mode); 410 + alt->group.name = alt->group_name; 411 + alt->group.attrs = alt->attrs; 412 + alt->groups[0] = &alt->group; 322 413 323 414 alt->dev.parent = parent; 324 - alt->dev.groups = alt->mode_groups; 415 + alt->dev.groups = alt->groups; 325 416 alt->dev.type = &typec_altmode_dev_type; 326 - dev_set_name(&alt->dev, "svid-%04x", alt->svid); 417 + dev_set_name(&alt->dev, "%s-%04x:%u", dev_name(parent), 418 + alt->svid, alt->mode); 327 419 328 420 ret = device_register(&alt->dev); 329 421 if (ret) {
+20 -25
drivers/usb/typec/tcpm.c
··· 310 310 311 311 /* Alternate mode data */ 312 312 struct pd_mode_data mode_data; 313 - struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX]; 314 - struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX]; 313 + struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX * 6]; 314 + struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX * 6]; 315 315 316 316 /* Deadline in jiffies to exit src_try_wait state */ 317 317 unsigned long max_wait; ··· 995 995 { 996 996 struct pd_mode_data *pmdata = &port->mode_data; 997 997 struct typec_altmode_desc *paltmode; 998 - struct typec_mode_desc *pmode; 999 998 int i; 1000 999 1001 1000 if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) { ··· 1002 1003 return; 1003 1004 } 1004 1005 1005 - paltmode = &pmdata->altmode_desc[pmdata->altmodes]; 1006 - memset(paltmode, 0, sizeof(*paltmode)); 1006 + for (i = 1; i < cnt; i++) { 1007 + paltmode = &pmdata->altmode_desc[pmdata->altmodes]; 1008 + memset(paltmode, 0, sizeof(*paltmode)); 1007 1009 1008 - paltmode->svid = pmdata->svids[pmdata->svid_index]; 1010 + paltmode->svid = pmdata->svids[pmdata->svid_index]; 1011 + paltmode->mode = i; 1012 + paltmode->vdo = le32_to_cpu(payload[i]); 1009 1013 1010 - tcpm_log(port, " Alternate mode %d: SVID 0x%04x", 1011 - pmdata->altmodes, paltmode->svid); 1014 + tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x", 1015 + pmdata->altmodes, paltmode->svid, 1016 + paltmode->mode, paltmode->vdo); 1012 1017 1013 - for (i = 1; i < cnt && paltmode->n_modes < ALTMODE_MAX_MODES; i++) { 1014 - pmode = &paltmode->modes[paltmode->n_modes]; 1015 - memset(pmode, 0, sizeof(*pmode)); 1016 - pmode->vdo = le32_to_cpu(payload[i]); 1017 - pmode->index = i - 1; 1018 - paltmode->n_modes++; 1019 - tcpm_log(port, " VDO %d: 0x%08x", 1020 - pmode->index, pmode->vdo); 1018 + port->partner_altmode[pmdata->altmodes] = 1019 + typec_partner_register_altmode(port->partner, paltmode); 1020 + if (!port->partner_altmode[pmdata->altmodes]) { 1021 + tcpm_log(port, 1022 + "Failed to register modes for SVID 0x%04x", 1023 + paltmode->svid); 1024 + return; 1025 + } 1026 + pmdata->altmodes++; 1021 1027 } 1022 - port->partner_altmode[pmdata->altmodes] = 1023 - typec_partner_register_altmode(port->partner, paltmode); 1024 - if (!port->partner_altmode[pmdata->altmodes]) { 1025 - tcpm_log(port, 1026 - "Failed to register alternate modes for SVID 0x%04x", 1027 - paltmode->svid); 1028 - return; 1029 - } 1030 - pmdata->altmodes++; 1031 1028 } 1032 1029 1033 1030 #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
+10 -29
include/linux/usb/typec.h
··· 93 93 int typec_cable_set_identity(struct typec_cable *cable); 94 94 95 95 /* 96 - * struct typec_mode_desc - Individual Mode of an Alternate Mode 97 - * @index: Index of the Mode within the SVID 98 - * @vdo: VDO returned by Discover Modes USB PD command 99 - * @desc: Optional human readable description of the mode 100 - * @roles: Only for ports. DRP if the mode is available in both roles 101 - * 102 - * Description of a mode of an Alternate Mode which a connector, cable plug or 103 - * partner supports. Every mode will have it's own sysfs group. The details are 104 - * the VDO returned by discover modes command, description for the mode and 105 - * active flag telling has the mode being entered or not. 106 - */ 107 - struct typec_mode_desc { 108 - int index; 109 - u32 vdo; 110 - char *desc; 111 - /* Only used with ports */ 112 - enum typec_port_type roles; 113 - }; 114 - 115 - /* 116 96 * struct typec_altmode_desc - USB Type-C Alternate Mode Descriptor 117 97 * @svid: Standard or Vendor ID 118 - * @n_modes: Number of modes 119 - * @modes: Array of modes supported by the Alternate Mode 98 + * @mode: Index of the Mode 99 + * @vdo: VDO returned by Discover Modes USB PD command 100 + * @roles: Only for ports. DRP if the mode is available in both roles 120 101 * 121 - * Representation of an Alternate Mode that has SVID assigned by USB-IF. The 122 - * array of modes will list the modes of a particular SVID that are supported by 123 - * a connector, partner of a cable plug. 102 + * Description of an Alternate Mode which a connector, cable plug or partner 103 + * supports. 124 104 */ 125 105 struct typec_altmode_desc { 126 106 u16 svid; 127 - int n_modes; 128 - struct typec_mode_desc modes[ALTMODE_MAX_MODES]; 107 + u8 mode; 108 + u32 vdo; 109 + /* Only used with ports */ 110 + enum typec_port_type roles; 129 111 }; 130 112 131 113 struct typec_altmode ··· 123 141 124 142 struct typec_port *typec_altmode2port(struct typec_altmode *alt); 125 143 126 - void typec_altmode_update_active(struct typec_altmode *alt, int mode, 127 - bool active); 144 + void typec_altmode_update_active(struct typec_altmode *alt, bool active); 128 145 129 146 enum typec_plug_index { 130 147 TYPEC_PLUG_SOP_P,