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>
This commit is contained in:
parent
93dd2112c7
commit
4ab8c18d4d
|
@ -13,31 +13,20 @@
|
||||||
#include <linux/usb/typec.h>
|
#include <linux/usb/typec.h>
|
||||||
#include <linux/usb/typec_mux.h>
|
#include <linux/usb/typec_mux.h>
|
||||||
|
|
||||||
struct typec_mode {
|
|
||||||
int index;
|
|
||||||
u32 vdo;
|
|
||||||
char *desc;
|
|
||||||
enum typec_port_type roles;
|
|
||||||
|
|
||||||
struct typec_altmode *alt_mode;
|
|
||||||
|
|
||||||
unsigned int active:1;
|
|
||||||
|
|
||||||
char group_name[6];
|
|
||||||
struct attribute_group group;
|
|
||||||
struct attribute *attrs[5];
|
|
||||||
struct device_attribute vdo_attr;
|
|
||||||
struct device_attribute desc_attr;
|
|
||||||
struct device_attribute active_attr;
|
|
||||||
struct device_attribute roles_attr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct typec_altmode {
|
struct typec_altmode {
|
||||||
struct device dev;
|
struct device dev;
|
||||||
u16 svid;
|
u16 svid;
|
||||||
int n_modes;
|
u8 mode;
|
||||||
struct typec_mode modes[ALTMODE_MAX_MODES];
|
|
||||||
const struct attribute_group *mode_groups[ALTMODE_MAX_MODES];
|
u32 vdo;
|
||||||
|
char *desc;
|
||||||
|
enum typec_port_type roles;
|
||||||
|
unsigned int active:1;
|
||||||
|
|
||||||
|
struct attribute *attrs[5];
|
||||||
|
char group_name[6];
|
||||||
|
struct attribute_group group;
|
||||||
|
const struct attribute_group *groups[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct typec_plug {
|
struct typec_plug {
|
||||||
|
@ -177,23 +166,20 @@ static void typec_report_identity(struct device *dev)
|
||||||
/**
|
/**
|
||||||
* typec_altmode_update_active - Report Enter/Exit mode
|
* typec_altmode_update_active - Report Enter/Exit mode
|
||||||
* @alt: Handle to the alternate mode
|
* @alt: Handle to the alternate mode
|
||||||
* @mode: Mode index
|
|
||||||
* @active: True when the mode has been entered
|
* @active: True when the mode has been entered
|
||||||
*
|
*
|
||||||
* If a partner or cable plug executes Enter/Exit Mode command successfully, the
|
* If a partner or cable plug executes Enter/Exit Mode command successfully, the
|
||||||
* drivers use this routine to report the updated state of the mode.
|
* drivers use this routine to report the updated state of the mode.
|
||||||
*/
|
*/
|
||||||
void typec_altmode_update_active(struct typec_altmode *alt, int mode,
|
void typec_altmode_update_active(struct typec_altmode *alt, bool active)
|
||||||
bool active)
|
|
||||||
{
|
{
|
||||||
struct typec_mode *m = &alt->modes[mode];
|
|
||||||
char dir[6];
|
char dir[6];
|
||||||
|
|
||||||
if (m->active == active)
|
if (alt->active == active)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m->active = active;
|
alt->active = active;
|
||||||
snprintf(dir, sizeof(dir), "mode%d", mode);
|
snprintf(dir, sizeof(dir), "mode%d", alt->mode);
|
||||||
sysfs_notify(&alt->dev.kobj, dir, "active");
|
sysfs_notify(&alt->dev.kobj, dir, "active");
|
||||||
kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE);
|
kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE);
|
||||||
}
|
}
|
||||||
|
@ -220,42 +206,36 @@ struct typec_port *typec_altmode2port(struct typec_altmode *alt)
|
||||||
EXPORT_SYMBOL_GPL(typec_altmode2port);
|
EXPORT_SYMBOL_GPL(typec_altmode2port);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
typec_altmode_vdo_show(struct device *dev, struct device_attribute *attr,
|
vdo_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
char *buf)
|
|
||||||
{
|
{
|
||||||
struct typec_mode *mode = container_of(attr, struct typec_mode,
|
struct typec_altmode *alt = to_altmode(dev);
|
||||||
vdo_attr);
|
|
||||||
|
|
||||||
return sprintf(buf, "0x%08x\n", mode->vdo);
|
return sprintf(buf, "0x%08x\n", alt->vdo);
|
||||||
}
|
}
|
||||||
|
static DEVICE_ATTR_RO(vdo);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
typec_altmode_desc_show(struct device *dev, struct device_attribute *attr,
|
description_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
char *buf)
|
|
||||||
{
|
{
|
||||||
struct typec_mode *mode = container_of(attr, struct typec_mode,
|
struct typec_altmode *alt = to_altmode(dev);
|
||||||
desc_attr);
|
|
||||||
|
|
||||||
return sprintf(buf, "%s\n", mode->desc ? mode->desc : "");
|
return sprintf(buf, "%s\n", alt->desc ? alt->desc : "");
|
||||||
}
|
}
|
||||||
|
static DEVICE_ATTR_RO(description);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
typec_altmode_active_show(struct device *dev, struct device_attribute *attr,
|
active_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
char *buf)
|
|
||||||
{
|
{
|
||||||
struct typec_mode *mode = container_of(attr, struct typec_mode,
|
struct typec_altmode *alt = to_altmode(dev);
|
||||||
active_attr);
|
|
||||||
|
|
||||||
return sprintf(buf, "%s\n", mode->active ? "yes" : "no");
|
return sprintf(buf, "%s\n", alt->active ? "yes" : "no");
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t active_store(struct device *dev, struct device_attribute *attr,
|
||||||
typec_altmode_active_store(struct device *dev, struct device_attribute *attr,
|
const char *buf, size_t size)
|
||||||
const char *buf, size_t size)
|
|
||||||
{
|
{
|
||||||
struct typec_mode *mode = container_of(attr, struct typec_mode,
|
struct typec_altmode *alt = to_altmode(dev);
|
||||||
active_attr);
|
struct typec_port *port = typec_altmode2port(alt);
|
||||||
struct typec_port *port = typec_altmode2port(mode->alt_mode);
|
|
||||||
bool activate;
|
bool activate;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -266,22 +246,22 @@ typec_altmode_active_store(struct device *dev, struct device_attribute *attr,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = port->cap->activate_mode(port->cap, mode->index, activate);
|
ret = port->cap->activate_mode(port->cap, alt->mode, activate);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
static DEVICE_ATTR_RW(active);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
typec_altmode_roles_show(struct device *dev, struct device_attribute *attr,
|
supported_roles_show(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct typec_mode *mode = container_of(attr, struct typec_mode,
|
struct typec_altmode *alt = to_altmode(dev);
|
||||||
roles_attr);
|
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
switch (mode->roles) {
|
switch (alt->roles) {
|
||||||
case TYPEC_PORT_SRC:
|
case TYPEC_PORT_SRC:
|
||||||
ret = sprintf(buf, "source\n");
|
ret = sprintf(buf, "source\n");
|
||||||
break;
|
break;
|
||||||
|
@ -295,61 +275,13 @@ typec_altmode_roles_show(struct device *dev, struct device_attribute *attr,
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
static DEVICE_ATTR_RO(supported_roles);
|
||||||
|
|
||||||
static void typec_init_modes(struct typec_altmode *alt,
|
static void typec_altmode_release(struct device *dev)
|
||||||
const struct typec_mode_desc *desc, bool is_port)
|
|
||||||
{
|
{
|
||||||
int i;
|
struct typec_altmode *alt = to_altmode(dev);
|
||||||
|
|
||||||
for (i = 0; i < alt->n_modes; i++, desc++) {
|
kfree(alt);
|
||||||
struct typec_mode *mode = &alt->modes[i];
|
|
||||||
|
|
||||||
/* Not considering the human readable description critical */
|
|
||||||
mode->desc = kstrdup(desc->desc, GFP_KERNEL);
|
|
||||||
if (desc->desc && !mode->desc)
|
|
||||||
dev_err(&alt->dev, "failed to copy mode%d desc\n", i);
|
|
||||||
|
|
||||||
mode->alt_mode = alt;
|
|
||||||
mode->vdo = desc->vdo;
|
|
||||||
mode->roles = desc->roles;
|
|
||||||
mode->index = desc->index;
|
|
||||||
sprintf(mode->group_name, "mode%d", desc->index);
|
|
||||||
|
|
||||||
sysfs_attr_init(&mode->vdo_attr.attr);
|
|
||||||
mode->vdo_attr.attr.name = "vdo";
|
|
||||||
mode->vdo_attr.attr.mode = 0444;
|
|
||||||
mode->vdo_attr.show = typec_altmode_vdo_show;
|
|
||||||
|
|
||||||
sysfs_attr_init(&mode->desc_attr.attr);
|
|
||||||
mode->desc_attr.attr.name = "description";
|
|
||||||
mode->desc_attr.attr.mode = 0444;
|
|
||||||
mode->desc_attr.show = typec_altmode_desc_show;
|
|
||||||
|
|
||||||
sysfs_attr_init(&mode->active_attr.attr);
|
|
||||||
mode->active_attr.attr.name = "active";
|
|
||||||
mode->active_attr.attr.mode = 0644;
|
|
||||||
mode->active_attr.show = typec_altmode_active_show;
|
|
||||||
mode->active_attr.store = typec_altmode_active_store;
|
|
||||||
|
|
||||||
mode->attrs[0] = &mode->vdo_attr.attr;
|
|
||||||
mode->attrs[1] = &mode->desc_attr.attr;
|
|
||||||
mode->attrs[2] = &mode->active_attr.attr;
|
|
||||||
|
|
||||||
/* With ports, list the roles that the mode is supported with */
|
|
||||||
if (is_port) {
|
|
||||||
sysfs_attr_init(&mode->roles_attr.attr);
|
|
||||||
mode->roles_attr.attr.name = "supported_roles";
|
|
||||||
mode->roles_attr.attr.mode = 0444;
|
|
||||||
mode->roles_attr.show = typec_altmode_roles_show;
|
|
||||||
|
|
||||||
mode->attrs[3] = &mode->roles_attr.attr;
|
|
||||||
}
|
|
||||||
|
|
||||||
mode->group.attrs = mode->attrs;
|
|
||||||
mode->group.name = mode->group_name;
|
|
||||||
|
|
||||||
alt->mode_groups[i] = &mode->group;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t svid_show(struct device *dev, struct device_attribute *attr,
|
static ssize_t svid_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
@ -367,16 +299,6 @@ static struct attribute *typec_altmode_attrs[] = {
|
||||||
};
|
};
|
||||||
ATTRIBUTE_GROUPS(typec_altmode);
|
ATTRIBUTE_GROUPS(typec_altmode);
|
||||||
|
|
||||||
static void typec_altmode_release(struct device *dev)
|
|
||||||
{
|
|
||||||
struct typec_altmode *alt = to_altmode(dev);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < alt->n_modes; i++)
|
|
||||||
kfree(alt->modes[i].desc);
|
|
||||||
kfree(alt);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct device_type typec_altmode_dev_type = {
|
static const struct device_type typec_altmode_dev_type = {
|
||||||
.name = "typec_alternate_mode",
|
.name = "typec_alternate_mode",
|
||||||
.groups = typec_altmode_groups,
|
.groups = typec_altmode_groups,
|
||||||
|
@ -395,13 +317,27 @@ typec_register_altmode(struct device *parent,
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
alt->svid = desc->svid;
|
alt->svid = desc->svid;
|
||||||
alt->n_modes = desc->n_modes;
|
alt->mode = desc->mode;
|
||||||
typec_init_modes(alt, desc->modes, is_typec_port(parent));
|
alt->vdo = desc->vdo;
|
||||||
|
alt->roles = desc->roles;
|
||||||
|
|
||||||
|
alt->attrs[0] = &dev_attr_vdo.attr;
|
||||||
|
alt->attrs[1] = &dev_attr_description.attr;
|
||||||
|
alt->attrs[2] = &dev_attr_active.attr;
|
||||||
|
|
||||||
|
if (is_typec_port(parent))
|
||||||
|
alt->attrs[3] = &dev_attr_supported_roles.attr;
|
||||||
|
|
||||||
|
sprintf(alt->group_name, "mode%d", desc->mode);
|
||||||
|
alt->group.name = alt->group_name;
|
||||||
|
alt->group.attrs = alt->attrs;
|
||||||
|
alt->groups[0] = &alt->group;
|
||||||
|
|
||||||
alt->dev.parent = parent;
|
alt->dev.parent = parent;
|
||||||
alt->dev.groups = alt->mode_groups;
|
alt->dev.groups = alt->groups;
|
||||||
alt->dev.type = &typec_altmode_dev_type;
|
alt->dev.type = &typec_altmode_dev_type;
|
||||||
dev_set_name(&alt->dev, "svid-%04x", alt->svid);
|
dev_set_name(&alt->dev, "%s-%04x:%u", dev_name(parent),
|
||||||
|
alt->svid, alt->mode);
|
||||||
|
|
||||||
ret = device_register(&alt->dev);
|
ret = device_register(&alt->dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
|
@ -310,8 +310,8 @@ struct tcpm_port {
|
||||||
|
|
||||||
/* Alternate mode data */
|
/* Alternate mode data */
|
||||||
struct pd_mode_data mode_data;
|
struct pd_mode_data mode_data;
|
||||||
struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX];
|
struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX * 6];
|
||||||
struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX];
|
struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX * 6];
|
||||||
|
|
||||||
/* Deadline in jiffies to exit src_try_wait state */
|
/* Deadline in jiffies to exit src_try_wait state */
|
||||||
unsigned long max_wait;
|
unsigned long max_wait;
|
||||||
|
@ -995,7 +995,6 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
|
||||||
{
|
{
|
||||||
struct pd_mode_data *pmdata = &port->mode_data;
|
struct pd_mode_data *pmdata = &port->mode_data;
|
||||||
struct typec_altmode_desc *paltmode;
|
struct typec_altmode_desc *paltmode;
|
||||||
struct typec_mode_desc *pmode;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
|
if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
|
||||||
|
@ -1003,32 +1002,28 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
paltmode = &pmdata->altmode_desc[pmdata->altmodes];
|
for (i = 1; i < cnt; i++) {
|
||||||
memset(paltmode, 0, sizeof(*paltmode));
|
paltmode = &pmdata->altmode_desc[pmdata->altmodes];
|
||||||
|
memset(paltmode, 0, sizeof(*paltmode));
|
||||||
|
|
||||||
paltmode->svid = pmdata->svids[pmdata->svid_index];
|
paltmode->svid = pmdata->svids[pmdata->svid_index];
|
||||||
|
paltmode->mode = i;
|
||||||
|
paltmode->vdo = le32_to_cpu(payload[i]);
|
||||||
|
|
||||||
tcpm_log(port, " Alternate mode %d: SVID 0x%04x",
|
tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
|
||||||
pmdata->altmodes, paltmode->svid);
|
pmdata->altmodes, paltmode->svid,
|
||||||
|
paltmode->mode, paltmode->vdo);
|
||||||
|
|
||||||
for (i = 1; i < cnt && paltmode->n_modes < ALTMODE_MAX_MODES; i++) {
|
port->partner_altmode[pmdata->altmodes] =
|
||||||
pmode = &paltmode->modes[paltmode->n_modes];
|
typec_partner_register_altmode(port->partner, paltmode);
|
||||||
memset(pmode, 0, sizeof(*pmode));
|
if (!port->partner_altmode[pmdata->altmodes]) {
|
||||||
pmode->vdo = le32_to_cpu(payload[i]);
|
tcpm_log(port,
|
||||||
pmode->index = i - 1;
|
"Failed to register modes for SVID 0x%04x",
|
||||||
paltmode->n_modes++;
|
paltmode->svid);
|
||||||
tcpm_log(port, " VDO %d: 0x%08x",
|
return;
|
||||||
pmode->index, pmode->vdo);
|
}
|
||||||
|
pmdata->altmodes++;
|
||||||
}
|
}
|
||||||
port->partner_altmode[pmdata->altmodes] =
|
|
||||||
typec_partner_register_altmode(port->partner, paltmode);
|
|
||||||
if (!port->partner_altmode[pmdata->altmodes]) {
|
|
||||||
tcpm_log(port,
|
|
||||||
"Failed to register alternate modes for SVID 0x%04x",
|
|
||||||
paltmode->svid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pmdata->altmodes++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
|
#define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
|
||||||
|
|
|
@ -92,40 +92,22 @@ struct usb_pd_identity {
|
||||||
int typec_partner_set_identity(struct typec_partner *partner);
|
int typec_partner_set_identity(struct typec_partner *partner);
|
||||||
int typec_cable_set_identity(struct typec_cable *cable);
|
int typec_cable_set_identity(struct typec_cable *cable);
|
||||||
|
|
||||||
/*
|
|
||||||
* struct typec_mode_desc - Individual Mode of an Alternate Mode
|
|
||||||
* @index: Index of the Mode within the SVID
|
|
||||||
* @vdo: VDO returned by Discover Modes USB PD command
|
|
||||||
* @desc: Optional human readable description of the mode
|
|
||||||
* @roles: Only for ports. DRP if the mode is available in both roles
|
|
||||||
*
|
|
||||||
* Description of a mode of an Alternate Mode which a connector, cable plug or
|
|
||||||
* partner supports. Every mode will have it's own sysfs group. The details are
|
|
||||||
* the VDO returned by discover modes command, description for the mode and
|
|
||||||
* active flag telling has the mode being entered or not.
|
|
||||||
*/
|
|
||||||
struct typec_mode_desc {
|
|
||||||
int index;
|
|
||||||
u32 vdo;
|
|
||||||
char *desc;
|
|
||||||
/* Only used with ports */
|
|
||||||
enum typec_port_type roles;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* struct typec_altmode_desc - USB Type-C Alternate Mode Descriptor
|
* struct typec_altmode_desc - USB Type-C Alternate Mode Descriptor
|
||||||
* @svid: Standard or Vendor ID
|
* @svid: Standard or Vendor ID
|
||||||
* @n_modes: Number of modes
|
* @mode: Index of the Mode
|
||||||
* @modes: Array of modes supported by the Alternate Mode
|
* @vdo: VDO returned by Discover Modes USB PD command
|
||||||
|
* @roles: Only for ports. DRP if the mode is available in both roles
|
||||||
*
|
*
|
||||||
* Representation of an Alternate Mode that has SVID assigned by USB-IF. The
|
* Description of an Alternate Mode which a connector, cable plug or partner
|
||||||
* array of modes will list the modes of a particular SVID that are supported by
|
* supports.
|
||||||
* a connector, partner of a cable plug.
|
|
||||||
*/
|
*/
|
||||||
struct typec_altmode_desc {
|
struct typec_altmode_desc {
|
||||||
u16 svid;
|
u16 svid;
|
||||||
int n_modes;
|
u8 mode;
|
||||||
struct typec_mode_desc modes[ALTMODE_MAX_MODES];
|
u32 vdo;
|
||||||
|
/* Only used with ports */
|
||||||
|
enum typec_port_type roles;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct typec_altmode
|
struct typec_altmode
|
||||||
|
@ -141,8 +123,7 @@ void typec_unregister_altmode(struct typec_altmode *altmode);
|
||||||
|
|
||||||
struct typec_port *typec_altmode2port(struct typec_altmode *alt);
|
struct typec_port *typec_altmode2port(struct typec_altmode *alt);
|
||||||
|
|
||||||
void typec_altmode_update_active(struct typec_altmode *alt, int mode,
|
void typec_altmode_update_active(struct typec_altmode *alt, bool active);
|
||||||
bool active);
|
|
||||||
|
|
||||||
enum typec_plug_index {
|
enum typec_plug_index {
|
||||||
TYPEC_PLUG_SOP_P,
|
TYPEC_PLUG_SOP_P,
|
||||||
|
|
Loading…
Reference in New Issue