[PATCH V3] usb: gadget: composite: Fix possible double free memory bug
composite_dev_cleanup call from the failure of configfs_composite_bind frees up the cdev->os_desc_req and cdev->req. If the previous calls of bind and unbind is successful these will carry stale values. Consider the below sequence of function calls: configfs_composite_bind() composite_dev_prepare() - Allocate cdev->req, cdev->req->buf composite_os_desc_req_prepare() - Allocate cdev->os_desc_req, cdev->os_desc_req->buf configfs_composite_unbind() composite_dev_cleanup() - free the cdev->os_desc_req->buf and cdev->req->buf Next composition switch configfs_composite_bind() - If it fails goto err_comp_cleanup will call the composite_dev_cleanup() function composite_dev_cleanup() - calls kfree up with the stale values of cdev->req->buf and cdev->os_desc_req from the previous configfs_composite_bind call. The free call on these stale values leads to double free. Hence, Fix this issue by setting request and buffer pointer to NULL after kfree. Signed-off-by: Chandana Kishori Chiluveru --- Changes in v3: - As suggested by balbi, Removed changelog from commit text. Changes in v2: - Modified commit text. drivers/usb/gadget/composite.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index b8a1584..992f1e2 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2155,14 +2155,18 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req); kfree(cdev->os_desc_req->buf); + cdev->os_desc_req->buf = NULL; usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); + cdev->os_desc_req = NULL; } if (cdev->req) { if (cdev->setup_pending) usb_ep_dequeue(cdev->gadget->ep0, cdev->req); kfree(cdev->req->buf); + cdev->req->buf = NULL; usb_ep_free_request(cdev->gadget->ep0, cdev->req); + cdev->req = NULL; } cdev->next_string_id = 0; device_remove_file(&cdev->gadget->dev, &dev_attr_suspended); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc., is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: musb: cppi41: broken high speed FTDI functionality when connected to musb directly
On Mon, Sep 30, 2019 at 09:54:11PM +0200, Sebastian Reichel wrote: > Hi, > > On Mon, Sep 30, 2019 at 08:23:30AM -0700, Tony Lindgren wrote: > > Actually playing with the cppi41 timeout might be more suitable here, > > they use the same module clock from what I remember though. So > > maybe increase the cppi41 autosuspend_timeout from 100 ms to 500 ms > > or higher: > > > > # echo 500 > > > /sys/bus/platform/drivers/cppi41-dma-engine/4740.dma-controller/power/autosuspend_delay_ms > > > > If changing the autosuspend_timeout_ms value does not help, then > > try setting control to on there. > > I did not check the details, but from the cover-letter this might be > woth looking into: > > https://lore.kernel.org/lkml/20190930161205.18803-1-jo...@kernel.org/ No, that one should be unrelated as it would only prevent later suspends after a driver has been unbound (and rebound). Johan signature.asc Description: PGP signature
Re: musb: cppi41: broken high speed FTDI functionality when connected to musb directly
On Tue, Oct 1, 2019 at 10:03 AM Johan Hovold wrote: > > On Mon, Sep 30, 2019 at 09:54:11PM +0200, Sebastian Reichel wrote: > > Hi, > > > > On Mon, Sep 30, 2019 at 08:23:30AM -0700, Tony Lindgren wrote: > > > > Actually playing with the cppi41 timeout might be more suitable here, > > > they use the same module clock from what I remember though. So > > > maybe increase the cppi41 autosuspend_timeout from 100 ms to 500 ms > > > or higher: > > > > > > # echo 500 > > > > /sys/bus/platform/drivers/cppi41-dma-engine/4740.dma-controller/power/autosuspend_delay_ms > > > > > > If changing the autosuspend_timeout_ms value does not help, then > > > try setting control to on there. > > > > I did not check the details, but from the cover-letter this might be > > woth looking into: > > > > https://lore.kernel.org/lkml/20190930161205.18803-1-jo...@kernel.org/ > > No, that one should be unrelated as it would only prevent later suspends after > a driver has been unbound (and rebound). I've tried to increase the autosuspend_delay_ms and to set control to "on" but nothing changes. Below you can see the output of my testing script [1] (Py2 only). As one can see, the first cycle i.e. after the port is open for the first time, fails. But the subsequent cycle is successful. If you invoke the script again, everything repeats. I've also made printk() in cppi41_run_queue() and it looks like this routine will be called from cppi41_dma_issue_pending() only in the beginning of the second test cycle. # serialtest.py -c 2 /dev/ttyUSB0 /dev/ttyUSB0 [ 662.333862] cppi41_runtime_resume Rx/Tx test: baudrate 115200 Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: string length 0 received Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: FAILED Rx/Tx test: baudrate 115200 Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: string length 0 received Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: FAILED Rx/Tx test: baudrate 1200 Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: string length 0 received Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: FAILED Rx/Tx test: baudrate 1200 Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: string length 0 received Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: FAILED Rx/Tx test: baudrate 115200 Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: O.K. Rx/Tx test: baudrate 115200 Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: O.K. Rx/Tx test: baudrate 1200 Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: O.K. Rx/Tx test: baudrate 1200 Rx/Tx /dev/ttyUSB0 > /dev/ttyUSB0: O.K. # [ 672.720806] cppi41_runtime_suspend [1] http://ftp.visionsystems.de/temp/serialtest.py Regards, Yegor
[PATCH 4/7] usb: typec: tcpm: Start using struct typec_operations
Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/tcpm/tcpm.c | 47 --- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 96562744101c..0fde7e042d5f 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -390,12 +390,6 @@ static enum tcpm_state tcpm_default_state(struct tcpm_port *port) return SRC_UNATTACHED; } -static inline -struct tcpm_port *typec_cap_to_tcpm(const struct typec_capability *cap) -{ - return container_of(cap, struct tcpm_port, typec_caps); -} - static bool tcpm_port_is_disconnected(struct tcpm_port *port) { return (!port->attached && port->cc1 == TYPEC_CC_OPEN && @@ -3970,10 +3964,9 @@ void tcpm_pd_hard_reset(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset); -static int tcpm_dr_set(const struct typec_capability *cap, - enum typec_data_role data) +static int tcpm_dr_set(struct typec_port *p, enum typec_data_role data) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); int ret; mutex_lock(&port->swap_lock); @@ -4038,10 +4031,9 @@ static int tcpm_dr_set(const struct typec_capability *cap, return ret; } -static int tcpm_pr_set(const struct typec_capability *cap, - enum typec_role role) +static int tcpm_pr_set(struct typec_port *p, enum typec_role role) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); int ret; mutex_lock(&port->swap_lock); @@ -4082,10 +4074,9 @@ static int tcpm_pr_set(const struct typec_capability *cap, return ret; } -static int tcpm_vconn_set(const struct typec_capability *cap, - enum typec_role role) +static int tcpm_vconn_set(struct typec_port *p, bool source) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); int ret; mutex_lock(&port->swap_lock); @@ -4096,7 +4087,7 @@ static int tcpm_vconn_set(const struct typec_capability *cap, goto port_unlock; } - if (role == port->vconn_role) { + if (source == port->vconn_role) { ret = 0; goto port_unlock; } @@ -4122,9 +4113,9 @@ static int tcpm_vconn_set(const struct typec_capability *cap, return ret; } -static int tcpm_try_role(const struct typec_capability *cap, int role) +static int tcpm_try_role(struct typec_port *p, int role) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); struct tcpc_dev *tcpc = port->tcpc; int ret = 0; @@ -4331,10 +4322,9 @@ static void tcpm_init(struct tcpm_port *port) tcpm_set_state(port, PORT_RESET, 0); } -static int tcpm_port_type_set(const struct typec_capability *cap, - enum typec_port_type type) +static int tcpm_port_type_set(struct typec_port *p, enum typec_port_type type) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); mutex_lock(&port->lock); if (type == port->port_type) @@ -4359,6 +4349,14 @@ static int tcpm_port_type_set(const struct typec_capability *cap, return 0; } +static const struct typec_operations tcpm_ops = { + .try_role = tcpm_try_role, + .dr_set = tcpm_dr_set, + .pr_set = tcpm_pr_set, + .vconn_set = tcpm_vconn_set, + .port_type_set = tcpm_port_type_set +}; + void tcpm_tcpc_reset(struct tcpm_port *port) { mutex_lock(&port->lock); @@ -4770,11 +4768,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.fwnode = tcpc->fwnode; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ - port->typec_caps.dr_set = tcpm_dr_set; - port->typec_caps.pr_set = tcpm_pr_set; - port->typec_caps.vconn_set = tcpm_vconn_set; - port->typec_caps.try_role = tcpm_try_role; - port->typec_caps.port_type_set = tcpm_port_type_set; + port->typec_caps.driver_data = port; + port->typec_caps.ops = &tcpm_ops; port->partner_desc.identity = &port->partner_ident; port->port_type = port->typec_caps.type; -- 2.23.0
[PATCH 7/7] usb: typec: Remove the callback members from struct typec_capability
There are no more users for them. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/class.c | 65 +-- include/linux/usb/typec.h | 17 -- 2 files changed, 22 insertions(+), 60 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 542be63795db..58e83fc54aa6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -58,7 +58,6 @@ struct typec_port { struct typec_switch *sw; struct typec_mux*mux; - const struct typec_capability *cap; const struct typec_operations *ops; }; @@ -970,19 +969,15 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EINVAL; } - if (port->ops && port->ops->try_role) { - ret = port->ops->try_role(port, role); - if (ret) - return ret; - } else if (port->cap && port->cap->try_role) { - ret = port->cap->try_role(port->cap, role); - if (ret) - return ret; - } else { + if (!port->ops || !port->ops->try_role) { dev_dbg(dev, "Setting preferred role not supported\n"); return -EOPNOTSUPP; } + ret = port->ops->try_role(port, role); + if (ret) + return ret; + port->prefer_role = role; return size; } @@ -1020,20 +1015,16 @@ static ssize_t data_role_store(struct device *dev, goto unlock_and_ret; } - if (port->ops && port->ops->dr_set) { - ret = port->ops->dr_set(port, ret); - if (ret) - goto unlock_and_ret; - } else if (port->cap && port->cap->dr_set) { - ret = port->cap->dr_set(port->cap, ret); - if (ret) - goto unlock_and_ret; - } else { + if (!port->ops || !port->ops->dr_set) { dev_dbg(dev, "data role swapping not supported\n"); ret = -EOPNOTSUPP; goto unlock_and_ret; } + ret = port->ops->dr_set(port, ret); + if (ret) + goto unlock_and_ret; + ret = size; unlock_and_ret: mutex_unlock(&port->port_type_lock); @@ -1082,21 +1073,17 @@ static ssize_t power_role_store(struct device *dev, goto unlock_and_ret; } - if (port->ops && port->ops->pr_set) { - ret = port->ops->pr_set(port, ret); - if (ret) - goto unlock_and_ret; - } else if (port->cap && port->cap->pr_set) { - ret = port->cap->pr_set(port->cap, ret); - if (ret) - goto unlock_and_ret; - } else { + if (!port->ops || !port->ops->pr_set) { dev_dbg(dev, "power role swapping not supported\n"); ret = -EOPNOTSUPP; goto unlock_and_ret; } ret = size; + ret = port->ops->pr_set(port, ret); + if (ret) + goto unlock_and_ret; + unlock_and_ret: mutex_unlock(&port->port_type_lock); return ret; @@ -1124,7 +,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, enum typec_port_type type; if ((!port->ops || !port->ops->port_type_set) || - !port->cap->port_type_set || port->fixed_role != TYPEC_PORT_DRP) { + port->fixed_role != TYPEC_PORT_DRP) { dev_dbg(dev, "changing port type not supported\n"); return -EOPNOTSUPP; } @@ -1141,10 +1128,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, goto unlock_and_ret; } - if (port->ops && port->ops->port_type_set) - ret = port->ops->port_type_set(port, type); - else - ret = port->cap->port_type_set(port->cap, type); + ret = port->ops->port_type_set(port, type); if (ret) goto unlock_and_ret; @@ -1204,19 +1188,15 @@ static ssize_t vconn_source_store(struct device *dev, if (ret) return ret; - if (port->ops && port->ops->vconn_set) { - ret = port->ops->vconn_set(port, source); - if (ret) - return ret; - } else if (port->cap && port->cap->vconn_set) { - ret = port->cap->vconn_set(port->cap, (enum typec_role)source); - if (ret) - return ret; - } else { + if (!port->ops || !port->ops->vconn_set) { dev_dbg(dev, "VCONN swapping not supported\n"); return -EOPNOTSUPP; } + ret = port->ops->vconn_set(port, source); + if (ret) + return ret; + return size; } @@ -1619,7 +1599,6 @@ struct typec_port *typec_register_port(struct device *parent, mutex_init(&port->po
[PATCH 5/7] usb: typec: tps6598x: Start using struct typec_operations
Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. After this there is not need to keep the capabilities stored anywhere in the driver. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/tps6598x.c | 49 +++- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index a38d1409f15b..0698addd1185 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -94,7 +94,6 @@ struct tps6598x { struct typec_port *port; struct typec_partner *partner; struct usb_pd_identity partner_identity; - struct typec_capability typec_cap; }; /* @@ -307,11 +306,10 @@ static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, return 0; } -static int -tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role) +static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role) { - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); const char *cmd = (role == TYPEC_DEVICE) ? "SWUF" : "SWDF"; + struct tps6598x *tps = typec_get_drvdata(port); u32 status; int ret; @@ -338,11 +336,10 @@ tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role) return ret; } -static int -tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role) +static int tps6598x_pr_set(struct typec_port *port, enum typec_role role) { - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); const char *cmd = (role == TYPEC_SINK) ? "SWSk" : "SWSr"; + struct tps6598x *tps = typec_get_drvdata(port); u32 status; int ret; @@ -369,6 +366,11 @@ tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role) return ret; } +static const struct typec_operations tps6598x_ops = { + .dr_set = tps6598x_dr_set, + .pr_set = tps6598x_pr_set, +}; + static irqreturn_t tps6598x_interrupt(int irq, void *data) { struct tps6598x *tps = data; @@ -448,6 +450,7 @@ static const struct regmap_config tps6598x_regmap_config = { static int tps6598x_probe(struct i2c_client *client) { + struct typec_capability typec_cap = { }; struct tps6598x *tps; u32 status; u32 conf; @@ -492,40 +495,40 @@ static int tps6598x_probe(struct i2c_client *client) if (ret < 0) return ret; - tps->typec_cap.revision = USB_TYPEC_REV_1_2; - tps->typec_cap.pd_revision = 0x200; - tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; - tps->typec_cap.pr_set = tps6598x_pr_set; - tps->typec_cap.dr_set = tps6598x_dr_set; + typec_cap.revision = USB_TYPEC_REV_1_2; + typec_cap.pd_revision = 0x200; + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + typec_cap.driver_data = tps; + typec_cap.ops = &tps6598x_ops; switch (TPS_SYSCONF_PORTINFO(conf)) { case TPS_PORTINFO_SINK_ACCESSORY: case TPS_PORTINFO_SINK: - tps->typec_cap.type = TYPEC_PORT_SNK; - tps->typec_cap.data = TYPEC_PORT_UFP; + typec_cap.type = TYPEC_PORT_SNK; + typec_cap.data = TYPEC_PORT_UFP; break; case TPS_PORTINFO_DRP_UFP_DRD: case TPS_PORTINFO_DRP_DFP_DRD: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_DRD; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_DRD; break; case TPS_PORTINFO_DRP_UFP: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_UFP; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_UFP; break; case TPS_PORTINFO_DRP_DFP: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_DFP; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_DFP; break; case TPS_PORTINFO_SOURCE: - tps->typec_cap.type = TYPEC_PORT_SRC; - tps->typec_cap.data = TYPEC_PORT_DFP; + typec_cap.type = TYPEC_PORT_SRC; + typec_cap.data = TYPEC_PORT_DFP; break; default: return -ENODEV; } - tps->port = typec_register_port(&client->dev, &tps->typec_cap); + tps->port = typec_register_port(&client->dev, &typec_cap); if (IS_ERR(tps->port)) return PTR_ERR(tps->port); -- 2.23.0
[PATCH 6/7] usb: typec: ucsi: Start using struct typec_operations
Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/ucsi/ucsi.c | 22 +++--- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index ba288b964dc8..edd722fb88b8 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -17,9 +17,6 @@ #include "ucsi.h" #include "trace.h" -#define to_ucsi_connector(_cap_) container_of(_cap_, struct ucsi_connector, \ - typec_cap) - /* * UCSI_TIMEOUT_MS - PPM communication timeout * @@ -713,10 +710,9 @@ static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) return ret; } -static int -ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role) +static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) { - struct ucsi_connector *con = to_ucsi_connector(cap); + struct ucsi_connector *con = typec_get_drvdata(port); struct ucsi_control ctrl; int ret = 0; @@ -748,10 +744,9 @@ ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role) return ret < 0 ? ret : 0; } -static int -ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role) +static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) { - struct ucsi_connector *con = to_ucsi_connector(cap); + struct ucsi_connector *con = typec_get_drvdata(port); struct ucsi_control ctrl; int ret = 0; @@ -788,6 +783,11 @@ ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role) return ret; } +static const struct typec_operations ucsi_ops = { + .dr_set = ucsi_dr_swap, + .pr_set = ucsi_pr_swap +}; + static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) { struct fwnode_handle *fwnode; @@ -843,8 +843,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) *accessory = TYPEC_ACCESSORY_DEBUG; cap->fwnode = ucsi_find_fwnode(con); - cap->dr_set = ucsi_dr_swap; - cap->pr_set = ucsi_pr_swap; + cap->driver_data = con; + cap->ops = &ucsi_ops; /* Register the connector */ con->port = typec_register_port(ucsi->dev, cap); -- 2.23.0
[PATCH 3/7] usb: typec: Separate the operations vector
Introducing struct typec_operations which has the same callbacks as struct typec_capability. The old callbacks are kept for now, but after all users have been converted, they will be removed. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/class.c | 90 +-- include/linux/usb/typec.h | 19 + 2 files changed, 76 insertions(+), 33 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 9fab0be8f08c..542be63795db 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -59,6 +59,7 @@ struct typec_port { struct typec_mux*mux; const struct typec_capability *cap; + const struct typec_operations *ops; }; #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev) @@ -961,11 +962,6 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EOPNOTSUPP; } - if (!port->cap->try_role) { - dev_dbg(dev, "Setting preferred role not supported\n"); - return -EOPNOTSUPP; - } - role = sysfs_match_string(typec_roles, buf); if (role < 0) { if (sysfs_streq(buf, "none")) @@ -974,9 +970,18 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EINVAL; } - ret = port->cap->try_role(port->cap, role); - if (ret) - return ret; + if (port->ops && port->ops->try_role) { + ret = port->ops->try_role(port, role); + if (ret) + return ret; + } else if (port->cap && port->cap->try_role) { + ret = port->cap->try_role(port->cap, role); + if (ret) + return ret; + } else { + dev_dbg(dev, "Setting preferred role not supported\n"); + return -EOPNOTSUPP; + } port->prefer_role = role; return size; @@ -1005,11 +1010,6 @@ static ssize_t data_role_store(struct device *dev, struct typec_port *port = to_typec_port(dev); int ret; - if (!port->cap->dr_set) { - dev_dbg(dev, "data role swapping not supported\n"); - return -EOPNOTSUPP; - } - ret = sysfs_match_string(typec_data_roles, buf); if (ret < 0) return ret; @@ -1020,9 +1020,19 @@ static ssize_t data_role_store(struct device *dev, goto unlock_and_ret; } - ret = port->cap->dr_set(port->cap, ret); - if (ret) + if (port->ops && port->ops->dr_set) { + ret = port->ops->dr_set(port, ret); + if (ret) + goto unlock_and_ret; + } else if (port->cap && port->cap->dr_set) { + ret = port->cap->dr_set(port->cap, ret); + if (ret) + goto unlock_and_ret; + } else { + dev_dbg(dev, "data role swapping not supported\n"); + ret = -EOPNOTSUPP; goto unlock_and_ret; + } ret = size; unlock_and_ret: @@ -1055,11 +1065,6 @@ static ssize_t power_role_store(struct device *dev, return -EOPNOTSUPP; } - if (!port->cap->pr_set) { - dev_dbg(dev, "power role swapping not supported\n"); - return -EOPNOTSUPP; - } - if (port->pwr_opmode != TYPEC_PWR_MODE_PD) { dev_dbg(dev, "partner unable to swap power role\n"); return -EIO; @@ -1077,11 +1082,21 @@ static ssize_t power_role_store(struct device *dev, goto unlock_and_ret; } - ret = port->cap->pr_set(port->cap, ret); - if (ret) + if (port->ops && port->ops->pr_set) { + ret = port->ops->pr_set(port, ret); + if (ret) + goto unlock_and_ret; + } else if (port->cap && port->cap->pr_set) { + ret = port->cap->pr_set(port->cap, ret); + if (ret) + goto unlock_and_ret; + } else { + dev_dbg(dev, "power role swapping not supported\n"); + ret = -EOPNOTSUPP; goto unlock_and_ret; - + } ret = size; + unlock_and_ret: mutex_unlock(&port->port_type_lock); return ret; @@ -1108,7 +1123,8 @@ port_type_store(struct device *dev, struct device_attribute *attr, int ret; enum typec_port_type type; - if (!port->cap->port_type_set || port->fixed_role != TYPEC_PORT_DRP) { + if ((!port->ops || !port->ops->port_type_set) || + !port->cap->port_type_set || port->fixed_role != TYPEC_PORT_DRP) { dev_dbg(dev, "changing port type not supported\n"); return -EOPNOTSUPP; } @@ -1125,7 +1141,10 @@ port_type_store(struct device *dev, struct device_attribute *attr, goto unlock_an
[PATCH 0/7] usb: typec: Small API improvement
Hi guys, This series moves the callback members from struct typec_capabilities to a new struct typec_operations. That removes the need for the drivers to keep a copy of the struct typec_capabilites if they don't need it, and struct typec_operations can probable always be constified. The change is small, however I think the code ends up being a bit more cleaner looking this way. Let me know if it's OK. thanks, Heikki Krogerus (7): usb: typec: Copy everything from struct typec_capability during registration usb: typec: Introduce typec_get_drvdata() usb: typec: Separate the operations vector usb: typec: tcpm: Start using struct typec_operations usb: typec: tps6598x: Start using struct typec_operations usb: typec: ucsi: Start using struct typec_operations usb: typec: Remove the callback members from struct typec_capability drivers/usb/typec/class.c | 125 +- drivers/usb/typec/tcpm/tcpm.c | 47 ++--- drivers/usb/typec/tps6598x.c | 49 ++--- drivers/usb/typec/ucsi/ucsi.c | 22 +++--- include/linux/usb/typec.h | 38 ++- 5 files changed, 157 insertions(+), 124 deletions(-) -- 2.23.0
[PATCH 1/7] usb: typec: Copy everything from struct typec_capability during registration
Copying everything from struct typec_capability to struct typec_port during port registration. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/class.c | 55 +-- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 94a3eda62add..3835e2d9fba6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -46,8 +46,14 @@ struct typec_port { enum typec_role vconn_role; enum typec_pwr_opmode pwr_opmode; enum typec_port_typeport_type; + enum typec_port_typefixed_role; + enum typec_port_dataport_roles; + enum typec_accessoryaccessory[TYPEC_MAX_ACCESSORY]; struct mutexport_type_lock; + u16 revision; + u16 pd_revision; + enum typec_orientation orientation; struct typec_switch *sw; struct typec_mux*mux; @@ -950,7 +956,7 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, int role; int ret; - if (port->cap->type != TYPEC_PORT_DRP) { + if (port->fixed_role != TYPEC_PORT_DRP) { dev_dbg(dev, "Preferred role only supported with DRP ports\n"); return -EOPNOTSUPP; } @@ -982,7 +988,7 @@ preferred_role_show(struct device *dev, struct device_attribute *attr, { struct typec_port *port = to_typec_port(dev); - if (port->cap->type != TYPEC_PORT_DRP) + if (port->fixed_role != TYPEC_PORT_DRP) return 0; if (port->prefer_role < 0) @@ -1009,7 +1015,7 @@ static ssize_t data_role_store(struct device *dev, return ret; mutex_lock(&port->port_type_lock); - if (port->cap->data != TYPEC_PORT_DRD) { + if (port->port_roles != TYPEC_PORT_DRD) { ret = -EOPNOTSUPP; goto unlock_and_ret; } @@ -1029,7 +1035,7 @@ static ssize_t data_role_show(struct device *dev, { struct typec_port *port = to_typec_port(dev); - if (port->cap->data == TYPEC_PORT_DRD) + if (port->port_roles == TYPEC_PORT_DRD) return sprintf(buf, "%s\n", port->data_role == TYPEC_HOST ? "[host] device" : "host [device]"); @@ -1044,7 +1050,7 @@ static ssize_t power_role_store(struct device *dev, struct typec_port *port = to_typec_port(dev); int ret; - if (!port->cap->pd_revision) { + if (!port->pd_revision) { dev_dbg(dev, "USB Power Delivery not supported\n"); return -EOPNOTSUPP; } @@ -1064,9 +1070,9 @@ static ssize_t power_role_store(struct device *dev, return ret; mutex_lock(&port->port_type_lock); - if (port->port_type != TYPEC_PORT_DRP) { + if (port->fixed_role != TYPEC_PORT_DRP) { dev_dbg(dev, "port type fixed at \"%s\"", -typec_port_power_roles[port->port_type]); +typec_port_power_roles[port->fixed_role]); ret = -EOPNOTSUPP; goto unlock_and_ret; } @@ -1086,7 +1092,7 @@ static ssize_t power_role_show(struct device *dev, { struct typec_port *port = to_typec_port(dev); - if (port->cap->type == TYPEC_PORT_DRP) + if (port->fixed_role == TYPEC_PORT_DRP) return sprintf(buf, "%s\n", port->pwr_role == TYPEC_SOURCE ? "[source] sink" : "source [sink]"); @@ -1102,7 +1108,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, int ret; enum typec_port_type type; - if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) { + if (!port->cap->port_type_set || port->fixed_role != TYPEC_PORT_DRP) { dev_dbg(dev, "changing port type not supported\n"); return -EOPNOTSUPP; } @@ -1114,7 +1120,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, type = ret; mutex_lock(&port->port_type_lock); - if (port->port_type == type) { + if (port->fixed_role == type) { ret = size; goto unlock_and_ret; } @@ -1123,7 +1129,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, if (ret) goto unlock_and_ret; - port->port_type = type; + port->fixed_role = type; ret = size; unlock_and_ret: @@ -1137,11 +1143,11 @@ port_type_show(struct device *dev, struct device_attribute *attr, { struct typec_port *port = to_typec_port(dev); - if (port->cap->type == TYPEC_PORT_DRP) + if (port->fixed_role == TYPEC_PORT_DRP) return sprintf(buf, "%s\n", - typ
[PATCH 2/7] usb: typec: Introduce typec_get_drvdata()
Leaving the private driver_data pointer of the port device to the port drivers. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/class.c | 11 +++ include/linux/usb/typec.h | 4 2 files changed, 15 insertions(+) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 3835e2d9fba6..9fab0be8f08c 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1492,6 +1492,16 @@ EXPORT_SYMBOL_GPL(typec_set_mode); /* --- */ +/** + * typec_get_drvdata - Return private driver data pointer + * @port: USB Type-C port + */ +void *typec_get_drvdata(struct typec_port *port) +{ + return dev_get_drvdata(&port->dev); +} +EXPORT_SYMBOL_GPL(typec_get_drvdata); + /** * typec_port_register_altmode - Register USB Type-C Port Alternate Mode * @port: USB Type-C Port that supports the alternate mode @@ -1604,6 +1614,7 @@ struct typec_port *typec_register_port(struct device *parent, port->dev.fwnode = cap->fwnode; port->dev.type = &typec_port_dev_type; dev_set_name(&port->dev, "port%d", id); + dev_set_drvdata(&port->dev, cap->driver_data); port->sw = typec_switch_get(&port->dev); if (IS_ERR(port->sw)) { diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 7df4ecabc78a..8b90cd77331c 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -179,6 +179,7 @@ struct typec_partner_desc { * @sw: Cable plug orientation switch * @mux: Multiplexer switch for Alternate/Accessory Modes * @fwnode: Optional fwnode of the port + * @driver_data: Private pointer for driver specific info * @try_role: Set data role preference for DRP port * @dr_set: Set Data Role * @pr_set: Set Power Role @@ -198,6 +199,7 @@ struct typec_capability { struct typec_switch *sw; struct typec_mux*mux; struct fwnode_handle*fwnode; + void*driver_data; int (*try_role)(const struct typec_capability *, int role); @@ -241,6 +243,8 @@ int typec_set_orientation(struct typec_port *port, enum typec_orientation typec_get_orientation(struct typec_port *port); int typec_set_mode(struct typec_port *port, int mode); +void *typec_get_drvdata(struct typec_port *port); + int typec_find_port_power_role(const char *name); int typec_find_power_role(const char *name); int typec_find_port_data_role(const char *name); -- 2.23.0
[PATCH v2] usb: typec: tcpm: usb: typec: tcpm: Fix a signedness bug in tcpm_fw_get_caps()
The "port->typec_caps.data" and "port->typec_caps.type" variables are enums and in this context GCC will treat them as an unsigned int so they can never be less than zero. Fixes: ae8a2ca8a221 ("usb: typec: Group all TCPCI/TCPM code together") Signed-off-by: Dan Carpenter --- v2: preserve the error code drivers/usb/typec/tcpm/tcpm.c | 14 -- 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 96562744101c..5f61d9977a15 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4409,18 +4409,20 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, /* USB data support is optional */ ret = fwnode_property_read_string(fwnode, "data-role", &cap_str); if (ret == 0) { - port->typec_caps.data = typec_find_port_data_role(cap_str); - if (port->typec_caps.data < 0) - return -EINVAL; + ret = typec_find_port_data_role(cap_str); + if (ret < 0) + return ret; + port->typec_caps.data = ret; } ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); if (ret < 0) return ret; - port->typec_caps.type = typec_find_port_power_role(cap_str); - if (port->typec_caps.type < 0) - return -EINVAL; + ret = typec_find_port_power_role(cap_str); + if (ret < 0) + return ret; + port->typec_caps.type = ret; port->port_type = port->typec_caps.type; if (port->port_type == TYPEC_PORT_SNK) -- 2.20.1
Re: [PATCH v2] usb: typec: tcpm: usb: typec: tcpm: Fix a signedness bug in tcpm_fw_get_caps()
On Tue, Oct 01, 2019 at 03:01:17PM +0300, Dan Carpenter wrote: > The "port->typec_caps.data" and "port->typec_caps.type" variables are > enums and in this context GCC will treat them as an unsigned int so they > can never be less than zero. > > Fixes: ae8a2ca8a221 ("usb: typec: Group all TCPCI/TCPM code together") > Signed-off-by: Dan Carpenter Looks OK to me. Reviewed-by: Heikki Krogerus > --- > v2: preserve the error code > > drivers/usb/typec/tcpm/tcpm.c | 14 -- > 1 file changed, 8 insertions(+), 6 deletions(-) > > diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c > index 96562744101c..5f61d9977a15 100644 > --- a/drivers/usb/typec/tcpm/tcpm.c > +++ b/drivers/usb/typec/tcpm/tcpm.c > @@ -4409,18 +4409,20 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, > /* USB data support is optional */ > ret = fwnode_property_read_string(fwnode, "data-role", &cap_str); > if (ret == 0) { > - port->typec_caps.data = typec_find_port_data_role(cap_str); > - if (port->typec_caps.data < 0) > - return -EINVAL; > + ret = typec_find_port_data_role(cap_str); > + if (ret < 0) > + return ret; > + port->typec_caps.data = ret; > } > > ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); > if (ret < 0) > return ret; > > - port->typec_caps.type = typec_find_port_power_role(cap_str); > - if (port->typec_caps.type < 0) > - return -EINVAL; > + ret = typec_find_port_power_role(cap_str); > + if (ret < 0) > + return ret; > + port->typec_caps.type = ret; > port->port_type = port->typec_caps.type; > > if (port->port_type == TYPEC_PORT_SNK) > -- > 2.20.1 thanks, -- heikki
Re: [PATCH 1/7] usb: typec: Copy everything from struct typec_capability during registration
On 10/1/19 2:48 AM, Heikki Krogerus wrote: Copying everything from struct typec_capability to struct typec_port during port registration. What is the purpose of this patch ? To reduce the number of indirections at runtime, or to avoid having to have cap around ? FWIW, it looks like the code doesn't copy _all_ variables (eg cap->try_role), and it doesn't drop port->cap. Am I missing something ? Signed-off-by: Heikki Krogerus --- drivers/usb/typec/class.c | 55 +-- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 94a3eda62add..3835e2d9fba6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -46,8 +46,14 @@ struct typec_port { enum typec_role vconn_role; enum typec_pwr_opmode pwr_opmode; enum typec_port_typeport_type; + enum typec_port_typefixed_role; + enum typec_port_dataport_roles; + enum typec_accessoryaccessory[TYPEC_MAX_ACCESSORY]; Would a pointer to cap->accessory be sufficient ? Or is there a reason to copy the actual array ? struct mutexport_type_lock; + u16revision; + u16 pd_revision; + enum typec_orientation orientation; struct typec_switch *sw; struct typec_mux*mux; @@ -950,7 +956,7 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, int role; int ret; - if (port->cap->type != TYPEC_PORT_DRP) { + if (port->fixed_role != TYPEC_PORT_DRP) { dev_dbg(dev, "Preferred role only supported with DRP ports\n"); return -EOPNOTSUPP; } @@ -982,7 +988,7 @@ preferred_role_show(struct device *dev, struct device_attribute *attr, { struct typec_port *port = to_typec_port(dev); - if (port->cap->type != TYPEC_PORT_DRP) + if (port->fixed_role != TYPEC_PORT_DRP) return 0; if (port->prefer_role < 0) @@ -1009,7 +1015,7 @@ static ssize_t data_role_store(struct device *dev, return ret; mutex_lock(&port->port_type_lock); - if (port->cap->data != TYPEC_PORT_DRD) { + if (port->port_roles != TYPEC_PORT_DRD) { ret = -EOPNOTSUPP; goto unlock_and_ret; } @@ -1029,7 +1035,7 @@ static ssize_t data_role_show(struct device *dev, { struct typec_port *port = to_typec_port(dev); - if (port->cap->data == TYPEC_PORT_DRD) + if (port->port_roles == TYPEC_PORT_DRD) return sprintf(buf, "%s\n", port->data_role == TYPEC_HOST ? "[host] device" : "host [device]"); @@ -1044,7 +1050,7 @@ static ssize_t power_role_store(struct device *dev, struct typec_port *port = to_typec_port(dev); int ret; - if (!port->cap->pd_revision) { + if (!port->pd_revision) { dev_dbg(dev, "USB Power Delivery not supported\n"); return -EOPNOTSUPP; } @@ -1064,9 +1070,9 @@ static ssize_t power_role_store(struct device *dev, return ret; mutex_lock(&port->port_type_lock); - if (port->port_type != TYPEC_PORT_DRP) { + if (port->fixed_role != TYPEC_PORT_DRP) { This is a semantic change: Previously, it compared the _current_ port type. Now it compares the initial (fixed) port type. Is this on purpose ? [ comment written before I noticed the change below. See there. ] dev_dbg(dev, "port type fixed at \"%s\"", -typec_port_power_roles[port->port_type]); +typec_port_power_roles[port->fixed_role]); ret = -EOPNOTSUPP; goto unlock_and_ret; } @@ -1086,7 +1092,7 @@ static ssize_t power_role_show(struct device *dev, { struct typec_port *port = to_typec_port(dev); - if (port->cap->type == TYPEC_PORT_DRP) + if (port->fixed_role == TYPEC_PORT_DRP) return sprintf(buf, "%s\n", port->pwr_role == TYPEC_SOURCE ? "[source] sink" : "source [sink]"); @@ -1102,7 +1108,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, int ret; enum typec_port_type type; - if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) { + if (!port->cap->port_type_set || port->fixed_role != TYPEC_PORT_DRP) { dev_dbg(dev, "changing port type not supported\n"); return -EOPNOTSUPP; } @@ -1114,7 +1120,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, type = ret; mutex_lock(&port->port_type_lock); - if (port->port_type == type) { + if (port->fixed_role == type) { This seems wrong. ret = size; goto unlock_and
Re: [PATCH 3/7] usb: typec: Separate the operations vector
On 10/1/19 2:48 AM, Heikki Krogerus wrote: Introducing struct typec_operations which has the same callbacks as struct typec_capability. The old callbacks are kept for now, but after all users have been converted, they will be removed. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/class.c | 90 +-- include/linux/usb/typec.h | 19 + 2 files changed, 76 insertions(+), 33 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 9fab0be8f08c..542be63795db 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -59,6 +59,7 @@ struct typec_port { struct typec_mux*mux; const struct typec_capability *cap; + const struct typec_operations *ops; }; #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev) @@ -961,11 +962,6 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EOPNOTSUPP; } - if (!port->cap->try_role) { - dev_dbg(dev, "Setting preferred role not supported\n"); - return -EOPNOTSUPP; - } - role = sysfs_match_string(typec_roles, buf); if (role < 0) { if (sysfs_streq(buf, "none")) @@ -974,9 +970,18 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EINVAL; } - ret = port->cap->try_role(port->cap, role); - if (ret) - return ret; + if (port->ops && port->ops->try_role) { + ret = port->ops->try_role(port, role); + if (ret) + return ret; + } else if (port->cap && port->cap->try_role) { + ret = port->cap->try_role(port->cap, role); + if (ret) + return ret; + } else { + dev_dbg(dev, "Setting preferred role not supported\n"); + return -EOPNOTSUPP; + } This is a semantic change: Support is now checked _after_ the string is evaluated. I understand the reason, but it should be noted in the patch description (not sure if it is worth it, though - it seems to me it makes the code more difficult to read). port->prefer_role = role; return size; @@ -1005,11 +1010,6 @@ static ssize_t data_role_store(struct device *dev, struct typec_port *port = to_typec_port(dev); int ret; - if (!port->cap->dr_set) { - dev_dbg(dev, "data role swapping not supported\n"); - return -EOPNOTSUPP; - } - ret = sysfs_match_string(typec_data_roles, buf); if (ret < 0) return ret; @@ -1020,9 +1020,19 @@ static ssize_t data_role_store(struct device *dev, goto unlock_and_ret; } - ret = port->cap->dr_set(port->cap, ret); - if (ret) + if (port->ops && port->ops->dr_set) { + ret = port->ops->dr_set(port, ret); + if (ret) + goto unlock_and_ret; + } else if (port->cap && port->cap->dr_set) { + ret = port->cap->dr_set(port->cap, ret); + if (ret) + goto unlock_and_ret; + } else { + dev_dbg(dev, "data role swapping not supported\n"); + ret = -EOPNOTSUPP; goto unlock_and_ret; This really makes me wonder if the semantic change makes sense. Support is now evaluated _after_ the lock has been obtained. That seems like a waste. + } ret = size; unlock_and_ret: @@ -1055,11 +1065,6 @@ static ssize_t power_role_store(struct device *dev, return -EOPNOTSUPP; } - if (!port->cap->pr_set) { - dev_dbg(dev, "power role swapping not supported\n"); - return -EOPNOTSUPP; - } - if (port->pwr_opmode != TYPEC_PWR_MODE_PD) { dev_dbg(dev, "partner unable to swap power role\n"); return -EIO; @@ -1077,11 +1082,21 @@ static ssize_t power_role_store(struct device *dev, goto unlock_and_ret; } - ret = port->cap->pr_set(port->cap, ret); - if (ret) + if (port->ops && port->ops->pr_set) { + ret = port->ops->pr_set(port, ret); + if (ret) + goto unlock_and_ret; + } else if (port->cap && port->cap->pr_set) { + ret = port->cap->pr_set(port->cap, ret); + if (ret) + goto unlock_and_ret; + } else { + dev_dbg(dev, "power role swapping not supported\n"); + ret = -EOPNOTSUPP; goto unlock_and_ret; - + } ret = size; + unlock_and_ret: mutex_unlock(&port->port_type_lock); return ret; @@ -1108,7 +1123,8 @@ port_type_store(struct device *dev, struct device_attribute *attr, int ret; enum typec_port_type type; - if (!port->ca
Re: [PATCH 4/7] usb: typec: tcpm: Start using struct typec_operations
On 10/1/19 2:48 AM, Heikki Krogerus wrote: Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/tcpm/tcpm.c | 47 --- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 96562744101c..0fde7e042d5f 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -390,12 +390,6 @@ static enum tcpm_state tcpm_default_state(struct tcpm_port *port) return SRC_UNATTACHED; } -static inline -struct tcpm_port *typec_cap_to_tcpm(const struct typec_capability *cap) -{ - return container_of(cap, struct tcpm_port, typec_caps); -} - static bool tcpm_port_is_disconnected(struct tcpm_port *port) { return (!port->attached && port->cc1 == TYPEC_CC_OPEN && @@ -3970,10 +3964,9 @@ void tcpm_pd_hard_reset(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset); -static int tcpm_dr_set(const struct typec_capability *cap, - enum typec_data_role data) +static int tcpm_dr_set(struct typec_port *p, enum typec_data_role data) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); int ret; mutex_lock(&port->swap_lock); @@ -4038,10 +4031,9 @@ static int tcpm_dr_set(const struct typec_capability *cap, return ret; } -static int tcpm_pr_set(const struct typec_capability *cap, - enum typec_role role) +static int tcpm_pr_set(struct typec_port *p, enum typec_role role) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); int ret; mutex_lock(&port->swap_lock); @@ -4082,10 +4074,9 @@ static int tcpm_pr_set(const struct typec_capability *cap, return ret; } -static int tcpm_vconn_set(const struct typec_capability *cap, - enum typec_role role) +static int tcpm_vconn_set(struct typec_port *p, bool source) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); int ret; mutex_lock(&port->swap_lock); @@ -4096,7 +4087,7 @@ static int tcpm_vconn_set(const struct typec_capability *cap, goto port_unlock; } - if (role == port->vconn_role) { + if (source == port->vconn_role) { source is boolean, vconn_role is enum typec_role. The original typec code took advantage of typec_role == TYPEC_SINK matching false, and typec_role == TYPEC_SOURCE matching true, but I don't think it is a good idea to carry that down to low level drivers. This will confuse everyone who wants to contribute a driver in the future. ret = 0; goto port_unlock; } @@ -4122,9 +4113,9 @@ static int tcpm_vconn_set(const struct typec_capability *cap, return ret; } -static int tcpm_try_role(const struct typec_capability *cap, int role) +static int tcpm_try_role(struct typec_port *p, int role) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); struct tcpc_dev *tcpc = port->tcpc; int ret = 0; @@ -4331,10 +4322,9 @@ static void tcpm_init(struct tcpm_port *port) tcpm_set_state(port, PORT_RESET, 0); } -static int tcpm_port_type_set(const struct typec_capability *cap, - enum typec_port_type type) +static int tcpm_port_type_set(struct typec_port *p, enum typec_port_type type) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); mutex_lock(&port->lock); if (type == port->port_type) @@ -4359,6 +4349,14 @@ static int tcpm_port_type_set(const struct typec_capability *cap, return 0; } +static const struct typec_operations tcpm_ops = { + .try_role = tcpm_try_role, + .dr_set = tcpm_dr_set, + .pr_set = tcpm_pr_set, + .vconn_set = tcpm_vconn_set, + .port_type_set = tcpm_port_type_set +}; + void tcpm_tcpc_reset(struct tcpm_port *port) { mutex_lock(&port->lock); @@ -4770,11 +4768,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.fwnode = tcpc->fwnode; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ - port->typec_caps.dr_set = tcpm_dr_set; - port->typec_caps.pr_set = tcpm_pr_set; - port->typec_caps.vconn_set = tcpm_vconn_set; - port->typec_caps.try_role = tcpm_try_role; - port->typec_caps.port_type_set = tcpm_port_type_set; + port->typec_caps.driver_data = port; + port->typec_caps.ops = &tcpm_ops; port->pa
Re: [PATCH 5/7] usb: typec: tps6598x: Start using struct typec_operations
On 10/1/19 2:48 AM, Heikki Krogerus wrote: Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. After this there is not need to keep the capabilities stored anywhere in the driver. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/tps6598x.c | 49 +++- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index a38d1409f15b..0698addd1185 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -94,7 +94,6 @@ struct tps6598x { struct typec_port *port; struct typec_partner *partner; struct usb_pd_identity partner_identity; - struct typec_capability typec_cap; }; /* @@ -307,11 +306,10 @@ static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, return 0; } -static int -tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role) +static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role) { - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); const char *cmd = (role == TYPEC_DEVICE) ? "SWUF" : "SWDF"; + struct tps6598x *tps = typec_get_drvdata(port); u32 status; int ret; @@ -338,11 +336,10 @@ tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role) return ret; } -static int -tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role) +static int tps6598x_pr_set(struct typec_port *port, enum typec_role role) { - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); const char *cmd = (role == TYPEC_SINK) ? "SWSk" : "SWSr"; + struct tps6598x *tps = typec_get_drvdata(port); u32 status; int ret; @@ -369,6 +366,11 @@ tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role) return ret; } +static const struct typec_operations tps6598x_ops = { + .dr_set = tps6598x_dr_set, + .pr_set = tps6598x_pr_set, +}; + static irqreturn_t tps6598x_interrupt(int irq, void *data) { struct tps6598x *tps = data; @@ -448,6 +450,7 @@ static const struct regmap_config tps6598x_regmap_config = { static int tps6598x_probe(struct i2c_client *client) { + struct typec_capability typec_cap = { }; struct tps6598x *tps; u32 status; u32 conf; @@ -492,40 +495,40 @@ static int tps6598x_probe(struct i2c_client *client) if (ret < 0) return ret; - tps->typec_cap.revision = USB_TYPEC_REV_1_2; - tps->typec_cap.pd_revision = 0x200; - tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; - tps->typec_cap.pr_set = tps6598x_pr_set; - tps->typec_cap.dr_set = tps6598x_dr_set; + typec_cap.revision = USB_TYPEC_REV_1_2; + typec_cap.pd_revision = 0x200; + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; Nitpick: struct typec_capability typec_cap = { .revision = USB_TYPEC_REV_1_2, .pd_revision = 0x200, .prefer_role = TYPEC_NO_PREFERRED_ROLE, }; Your call, though. + typec_cap.driver_data = tps; + typec_cap.ops = &tps6598x_ops; switch (TPS_SYSCONF_PORTINFO(conf)) { case TPS_PORTINFO_SINK_ACCESSORY: case TPS_PORTINFO_SINK: - tps->typec_cap.type = TYPEC_PORT_SNK; - tps->typec_cap.data = TYPEC_PORT_UFP; + typec_cap.type = TYPEC_PORT_SNK; + typec_cap.data = TYPEC_PORT_UFP; break; case TPS_PORTINFO_DRP_UFP_DRD: case TPS_PORTINFO_DRP_DFP_DRD: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_DRD; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_DRD; break; case TPS_PORTINFO_DRP_UFP: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_UFP; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_UFP; break; case TPS_PORTINFO_DRP_DFP: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_DFP; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_DFP; break; case TPS_PORTINFO_SOURCE: - tps->typec_cap.type = TYPEC_PORT_SRC; - tps->typec_cap.data = TYPEC_PORT_DFP; + typec_cap.type = TYPEC_PORT_SRC; + typec_cap.data = TYPEC_PORT_DFP; break; default: return -ENODEV; } - tps->port = typec_register_port(&client->dev, &tps->typec_cap); + tps->port = typec_register_port(&client->dev, &typec_cap);
Re: [PATCH 6/7] usb: typec: ucsi: Start using struct typec_operations
On 10/1/19 2:48 AM, Heikki Krogerus wrote: Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck --- drivers/usb/typec/ucsi/ucsi.c | 22 +++--- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index ba288b964dc8..edd722fb88b8 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -17,9 +17,6 @@ #include "ucsi.h" #include "trace.h" -#define to_ucsi_connector(_cap_) container_of(_cap_, struct ucsi_connector, \ - typec_cap) - /* * UCSI_TIMEOUT_MS - PPM communication timeout * @@ -713,10 +710,9 @@ static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) return ret; } -static int -ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role) +static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) { - struct ucsi_connector *con = to_ucsi_connector(cap); + struct ucsi_connector *con = typec_get_drvdata(port); struct ucsi_control ctrl; int ret = 0; @@ -748,10 +744,9 @@ ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role) return ret < 0 ? ret : 0; } -static int -ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role) +static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) { - struct ucsi_connector *con = to_ucsi_connector(cap); + struct ucsi_connector *con = typec_get_drvdata(port); struct ucsi_control ctrl; int ret = 0; @@ -788,6 +783,11 @@ ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role) return ret; } +static const struct typec_operations ucsi_ops = { + .dr_set = ucsi_dr_swap, + .pr_set = ucsi_pr_swap +}; + static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) { struct fwnode_handle *fwnode; @@ -843,8 +843,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) *accessory = TYPEC_ACCESSORY_DEBUG; cap->fwnode = ucsi_find_fwnode(con); - cap->dr_set = ucsi_dr_swap; - cap->pr_set = ucsi_pr_swap; + cap->driver_data = con; + cap->ops = &ucsi_ops; /* Register the connector */ con->port = typec_register_port(ucsi->dev, cap);
Re: [PATCH 5/7] usb: typec: tps6598x: Start using struct typec_operations
On 10/1/19 2:48 AM, Heikki Krogerus wrote: Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. After this there is not need to keep the capabilities stored anywhere in the driver. Signed-off-by: Heikki Krogerus Nitpick or not: Reviewed-by: Guenter Roeck --- drivers/usb/typec/tps6598x.c | 49 +++- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index a38d1409f15b..0698addd1185 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -94,7 +94,6 @@ struct tps6598x { struct typec_port *port; struct typec_partner *partner; struct usb_pd_identity partner_identity; - struct typec_capability typec_cap; }; /* @@ -307,11 +306,10 @@ static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, return 0; } -static int -tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role) +static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role) { - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); const char *cmd = (role == TYPEC_DEVICE) ? "SWUF" : "SWDF"; + struct tps6598x *tps = typec_get_drvdata(port); u32 status; int ret; @@ -338,11 +336,10 @@ tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role) return ret; } -static int -tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role) +static int tps6598x_pr_set(struct typec_port *port, enum typec_role role) { - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); const char *cmd = (role == TYPEC_SINK) ? "SWSk" : "SWSr"; + struct tps6598x *tps = typec_get_drvdata(port); u32 status; int ret; @@ -369,6 +366,11 @@ tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role) return ret; } +static const struct typec_operations tps6598x_ops = { + .dr_set = tps6598x_dr_set, + .pr_set = tps6598x_pr_set, +}; + static irqreturn_t tps6598x_interrupt(int irq, void *data) { struct tps6598x *tps = data; @@ -448,6 +450,7 @@ static const struct regmap_config tps6598x_regmap_config = { static int tps6598x_probe(struct i2c_client *client) { + struct typec_capability typec_cap = { }; struct tps6598x *tps; u32 status; u32 conf; @@ -492,40 +495,40 @@ static int tps6598x_probe(struct i2c_client *client) if (ret < 0) return ret; - tps->typec_cap.revision = USB_TYPEC_REV_1_2; - tps->typec_cap.pd_revision = 0x200; - tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; - tps->typec_cap.pr_set = tps6598x_pr_set; - tps->typec_cap.dr_set = tps6598x_dr_set; + typec_cap.revision = USB_TYPEC_REV_1_2; + typec_cap.pd_revision = 0x200; + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + typec_cap.driver_data = tps; + typec_cap.ops = &tps6598x_ops; switch (TPS_SYSCONF_PORTINFO(conf)) { case TPS_PORTINFO_SINK_ACCESSORY: case TPS_PORTINFO_SINK: - tps->typec_cap.type = TYPEC_PORT_SNK; - tps->typec_cap.data = TYPEC_PORT_UFP; + typec_cap.type = TYPEC_PORT_SNK; + typec_cap.data = TYPEC_PORT_UFP; break; case TPS_PORTINFO_DRP_UFP_DRD: case TPS_PORTINFO_DRP_DFP_DRD: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_DRD; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_DRD; break; case TPS_PORTINFO_DRP_UFP: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_UFP; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_UFP; break; case TPS_PORTINFO_DRP_DFP: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_DFP; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_DFP; break; case TPS_PORTINFO_SOURCE: - tps->typec_cap.type = TYPEC_PORT_SRC; - tps->typec_cap.data = TYPEC_PORT_DFP; + typec_cap.type = TYPEC_PORT_SRC; + typec_cap.data = TYPEC_PORT_DFP; break; default: return -ENODEV; } - tps->port = typec_register_port(&client->dev, &tps->typec_cap); + tps->port = typec_register_port(&client->dev, &typec_cap); if (IS_ERR(tps->port)) return PTR_ERR(tps->port);
Re: [PATCH 7/7] usb: typec: Remove the callback members from struct typec_capability
On 10/1/19 2:48 AM, Heikki Krogerus wrote: There are no more users for them. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/class.c | 65 +-- include/linux/usb/typec.h | 17 -- 2 files changed, 22 insertions(+), 60 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 542be63795db..58e83fc54aa6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -58,7 +58,6 @@ struct typec_port { struct typec_switch *sw; struct typec_mux*mux; - const struct typec_capability *cap; const struct typec_operations *ops; }; @@ -970,19 +969,15 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EINVAL; } - if (port->ops && port->ops->try_role) { - ret = port->ops->try_role(port, role); - if (ret) - return ret; - } else if (port->cap && port->cap->try_role) { - ret = port->cap->try_role(port->cap, role); - if (ret) - return ret; - } else { + if (!port->ops || !port->ops->try_role) { dev_dbg(dev, "Setting preferred role not supported\n"); return -EOPNOTSUPP; } This leaves the semantic change in place. I think it would have been better to keep the support checks as in the original code. + ret = port->ops->try_role(port, role); + if (ret) + return ret; + port->prefer_role = role; return size; } @@ -1020,20 +1015,16 @@ static ssize_t data_role_store(struct device *dev, goto unlock_and_ret; } - if (port->ops && port->ops->dr_set) { - ret = port->ops->dr_set(port, ret); - if (ret) - goto unlock_and_ret; - } else if (port->cap && port->cap->dr_set) { - ret = port->cap->dr_set(port->cap, ret); - if (ret) - goto unlock_and_ret; - } else { + if (!port->ops || !port->ops->dr_set) { dev_dbg(dev, "data role swapping not supported\n"); ret = -EOPNOTSUPP; goto unlock_and_ret; } + ret = port->ops->dr_set(port, ret); + if (ret) + goto unlock_and_ret; + ret = size; unlock_and_ret: mutex_unlock(&port->port_type_lock); @@ -1082,21 +1073,17 @@ static ssize_t power_role_store(struct device *dev, goto unlock_and_ret; } - if (port->ops && port->ops->pr_set) { - ret = port->ops->pr_set(port, ret); - if (ret) - goto unlock_and_ret; - } else if (port->cap && port->cap->pr_set) { - ret = port->cap->pr_set(port->cap, ret); - if (ret) - goto unlock_and_ret; - } else { + if (!port->ops || !port->ops->pr_set) { dev_dbg(dev, "power role swapping not supported\n"); ret = -EOPNOTSUPP; goto unlock_and_ret; } ret = size; + ret = port->ops->pr_set(port, ret); + if (ret) + goto unlock_and_ret; + unlock_and_ret: mutex_unlock(&port->port_type_lock); return ret; @@ -1124,7 +,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, enum typec_port_type type; if ((!port->ops || !port->ops->port_type_set) || - !port->cap->port_type_set || port->fixed_role != TYPEC_PORT_DRP) { + port->fixed_role != TYPEC_PORT_DRP) { dev_dbg(dev, "changing port type not supported\n"); return -EOPNOTSUPP; } @@ -1141,10 +1128,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, goto unlock_and_ret; } - if (port->ops && port->ops->port_type_set) - ret = port->ops->port_type_set(port, type); - else - ret = port->cap->port_type_set(port->cap, type); + ret = port->ops->port_type_set(port, type); if (ret) goto unlock_and_ret; @@ -1204,19 +1188,15 @@ static ssize_t vconn_source_store(struct device *dev, if (ret) return ret; - if (port->ops && port->ops->vconn_set) { - ret = port->ops->vconn_set(port, source); - if (ret) - return ret; - } else if (port->cap && port->cap->vconn_set) { - ret = port->cap->vconn_set(port->cap, (enum typec_role)source); - if (ret) - return ret; - } else { + if (!port->ops || !port->ops->vconn_set) { dev_dbg(dev, "VCONN swapping not supported\n"); return -EOPNOTSUPP; } + ret = port->ops->vconn_set(port, source); + if (ret) + return ret;
Re: [PATCH v2] usb: typec: tcpm: usb: typec: tcpm: Fix a signedness bug in tcpm_fw_get_caps()
On 10/1/19 5:01 AM, Dan Carpenter wrote: The "port->typec_caps.data" and "port->typec_caps.type" variables are enums and in this context GCC will treat them as an unsigned int so they can never be less than zero. Fixes: ae8a2ca8a221 ("usb: typec: Group all TCPCI/TCPM code together") Signed-off-by: Dan Carpenter Reviewed-by: Guenter Roeck --- v2: preserve the error code drivers/usb/typec/tcpm/tcpm.c | 14 -- 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 96562744101c..5f61d9977a15 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4409,18 +4409,20 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, /* USB data support is optional */ ret = fwnode_property_read_string(fwnode, "data-role", &cap_str); if (ret == 0) { - port->typec_caps.data = typec_find_port_data_role(cap_str); - if (port->typec_caps.data < 0) - return -EINVAL; + ret = typec_find_port_data_role(cap_str); + if (ret < 0) + return ret; + port->typec_caps.data = ret; } ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); if (ret < 0) return ret; - port->typec_caps.type = typec_find_port_power_role(cap_str); - if (port->typec_caps.type < 0) - return -EINVAL; + ret = typec_find_port_power_role(cap_str); + if (ret < 0) + return ret; + port->typec_caps.type = ret; port->port_type = port->typec_caps.type; if (port->port_type == TYPEC_PORT_SNK)
Re: [RFC PATCH 00/22] thunderbolt: Add support for USB4
On Tue, Oct 01, 2019 at 02:49:54PM +0200, Greg Kroah-Hartman wrote: > On Tue, Oct 01, 2019 at 02:38:08PM +0300, Mika Westerberg wrote: > > Hi all, > > > > I'm sending this as RFC because the series is still missing important > > features such as power management so not ready for merging. However, I > > think it is good to get any early feedback from the community. We are > > working to add support for the missing features. > > > > USB4 is the public specification of Thunderbolt 3 protocol and can be > > downloaded here: > > > > https://www.usb.org/sites/default/files/USB4%20Specification_1.zip > > > > USB4 is about tunneling different protocols over a single cable (in the > > same way as Thunderbolt). The current USB4 spec supports PCIe, Display Port > > and USB 3.x, and also software based protocols such as networking between > > domains (hosts). > > > > So far PCs have been using firmware based Connection Manager and Apple > > systems have been using software based one. A Connection Manager is the > > entity that handles creation of different tunnel types through the USB4 > > (and Thunderbolt) fabric. With USB4 the plan is to have software based > > Connection Manager everywhere but some early systems will also support > > firmware to allow OS downgrade for example. > > > > Current Linux Thunderbolt driver supports both "modes" and can detect which > > one to use dynamically. > > > > This series first adds support for Thunderbolt 3 devices to the software > > connection manager and then extends that to support USB4 compliant hosts > > and devices (this applies to both firmware and software based connection > > managers). With this series the following features are supported also for > > USB4 compliant devices: > > > > - PCIe tunneling > > - Display Port tunneling > > - USB 3.x tunneling > > - P2P networking (implemented in drivers/net/thunderbolt.c) > > - Host and device NVM firmware upgrade > > > > We also add two new sysfs attributes under each device that expose link > > speed and width to userspace. The rest of the userspace ABI stays the same. > > > > I'm including Linux USB folks as well because USB4 is officially coming > > from USB-IF which puts us on same boat now. > > > > While I changed the user visible Kconfig string to mention "USB4" (the > > Kconfig option is still CONFIG_THUNDERBOLT), I'm wondering whether we > > should move the whole Thunderbolt driver under drivers/usb/thunderbolt as > > well? > > Looks "interesting", nice work! Thanks! > I stopped at patch "Add initial support for USB4" as I don't think we > want to add USB4 code to a system that we know does not have it, right? You can connect a USB4 device to Thunderbolt 3 system. USB4 hubs specifically are required to support this by the spec. Of course then it is in Thunderbolt 3 alternate mode (not in USB4 native mode) but the USB4 register set is still there. So for example we still need to configure TMU (Time Management Unit) and access the DROM via router operations and so on. Do you mean we don't want that? > Everything up to then is just "normal" thunderbolt, and with the > exception of a few minor comments, all look fine to me. > > I didn't actually read the USB4 patch just yet, as I figured we needed > to argue about that some more :) Heh, sure and thanks for the feedback so far :)
Re: depmod warning: unknown symbol usb_stor_sense_invalidCDB in 5.4-rc1
On Mon, Sep 30, 2019 at 10:45:25PM +0200, Stefan Wahren wrote: Hi, during make modules_install for arm/multi_v7_defconfig on current Linux 5.4-rc1, i'm getting the following warnings: depmod: WARNING: /media/stefan/rootfs//lib/modules/5.4.0-rc1-6-g4a80125-dirty/kernel/drivers/usb/storage/uas.ko needs unknown symbol usb_stor_sense_invalidCDB depmod: WARNING: /media/stefan/rootfs//lib/modules/5.4.0-rc1-6-g4a80125-dirty/kernel/drivers/usb/storage/uas.ko needs unknown symbol usb_stor_adjust_quirks Reverting 32bca2df7da2 ("usb-storage: export symbols in USB_STORAGE namespace") makes the problem go away. Would be if someone can take care of it. Hi Stefan! Thanks for reporting this. I will take care of it! Cheers, Matthias
Re: musb: cppi41: broken high speed FTDI functionality when connected to musb directly
* Yegor Yefremov [191001 09:20]: > On Tue, Oct 1, 2019 at 10:03 AM Johan Hovold wrote: > > > > On Mon, Sep 30, 2019 at 09:54:11PM +0200, Sebastian Reichel wrote: > > > Hi, > > > > > > On Mon, Sep 30, 2019 at 08:23:30AM -0700, Tony Lindgren wrote: > > > > > > Actually playing with the cppi41 timeout might be more suitable here, > > > > they use the same module clock from what I remember though. So > > > > maybe increase the cppi41 autosuspend_timeout from 100 ms to 500 ms > > > > or higher: > > > > > > > > # echo 500 > > > > > /sys/bus/platform/drivers/cppi41-dma-engine/4740.dma-controller/power/autosuspend_delay_ms > > > > > > > > If changing the autosuspend_timeout_ms value does not help, then > > > > try setting control to on there. > > > > > > I did not check the details, but from the cover-letter this might be > > > woth looking into: > > > > > > https://lore.kernel.org/lkml/20190930161205.18803-1-jo...@kernel.org/ > > > > No, that one should be unrelated as it would only prevent later suspends > > after > > a driver has been unbound (and rebound). > > I've tried to increase the autosuspend_delay_ms and to set control to > "on" but nothing changes. Below you can see the output of my testing > script [1] (Py2 only). As one can see, the first cycle i.e. after the > port is open for the first time, fails. But the subsequent cycle is > successful. If you invoke the script again, everything repeats. > > I've also made printk() in cppi41_run_queue() and it looks like this > routine will be called from cppi41_dma_issue_pending() only in the > beginning of the second test cycle. So sounds like for you intially cppi41_dma_issue_pending() has !cdd->is_suspended and just adds the request to the queue. And then cppi41_run_queue() never gets called if this happens while we have cppi41_runtime_resume() is still running? Can you check that cppi41_dma_issue_pending() really gets called for the first request and it adds the request to the queue by adding a printk to cppi41_dma_issue_pending()? > [1] http://ftp.visionsystems.de/temp/serialtest.py For me this script somehow fails to configure the ports with: $ python2 serialtest.py -c2 /dev/ttyUSB0 /dev/ttyUSB0 Openning one of the serial ports failed Openning one of the serial ports failed The permissions are set properly as I have minicom working.. So still no luck reproducing. Regards, Tony
RE: [PATCH 00/14] usb: typec: UCSI driver overhaul
Hi Heikki > -Original Message- > From: Heikki Krogerus > Sent: Thursday, September 26, 2019 3:07 AM > To: Ajay Gupta > Cc: linux-usb@vger.kernel.org > Subject: [PATCH 00/14] usb: typec: UCSI driver overhaul > > Hi Ajay, > > Here's the pretty much complete rewrite of the I/O handling that I was > talking about. The first seven patches are not actually related to > this stuff, but I'm including them here because the rest of the series > is made on top of them. I'm including also that fix patch I send you > earlier. > > After this it should be easier to handle quirks. My idea how to handle > the multi-instance connector alt modes is that we "emulate" the PPM in > ucsi_ccg.c in order to handle them, so ucsi.c is not touched at all. > > We can now get the connector alternate modes that the actual > controller supplies during probe - before registering the ucsi > interface How can ucsi_ccg.c get the connector alternate modes before registering ucsi interface? PPM reset, notification enable, etc. is done during ucsi registration. UCSI spec says: " The only commands the PPM is required to process in the "PPM Idle (Notifications Disabled)" state are SET_NOTIFICATION_ENABLE and PPM_RESE" Also, it doesn't look correct if ucsi_ccg.c has to replicate most of the stuff done in ucsi_init() of ucsi.c. Thanks > nvpublic > - and squash all alt modes with the same SVID into one that > we supply to the ucsi.c when ever it sends GET_ALTERNATE_MODES > command. Also other alt mode commands like SET_NEW_CAM can have > special processing in ucsi_ccg.c and ucsi_ccg.c alone. There should > not be any problem with that anymore. > > thanks, > > Heikki Krogerus (14): > usb: typec: Copy everything from struct typec_capability during > registration > usb: typec: Introduce typec_get_drvdata() > usb: typec: Separate the operations vector > usb: typec: tcpm: Start using struct typec_operations > usb: typec: tps6598x: Start using struct typec_operations > usb: typec: ucsi: Start using struct typec_operations > usb: typec: Remove the callback members from struct typec_capability > usb: typec: ucsi: ccg: Remove run_isr flag > usb: typec: ucsi: Simplified interface registration and I/O API > usb: typec: ucsi: acpi: Move to the new API > usb: typec: ucsi: ccg: Move to the new API > usb: typec: ucsi: Remove the old API > usb: typec: ucsi: Remove struct ucsi_control > usb: typec: ucsi: Remove all bit-fields > > drivers/usb/typec/class.c| 125 +++--- > drivers/usb/typec/tcpm/tcpm.c| 47 +-- > drivers/usb/typec/tps6598x.c | 49 +-- > drivers/usb/typec/ucsi/displayport.c | 26 +- > drivers/usb/typec/ucsi/trace.c | 11 - > drivers/usb/typec/ucsi/trace.h | 79 +--- > drivers/usb/typec/ucsi/ucsi.c| 592 ++- > drivers/usb/typec/ucsi/ucsi.h| 410 +++ > drivers/usb/typec/ucsi/ucsi_acpi.c | 96 - > drivers/usb/typec/ucsi/ucsi_ccg.c| 214 -- > include/linux/usb/typec.h| 38 +- > 11 files changed, 785 insertions(+), 902 deletions(-) > > -- > 2.23.0
Re: [PATCH 4/6] ARM: dts: sunxi: Remove useless phy-names from EHCI and OHCI
Hi Maxime, On Tue, 16 Apr 2019 10:28:00 +0200 Maxime Ripard wrote: > Neither the OHCI or EHCI bindings are using the phy-names property, so we > can just drop it. > > Signed-off-by: Maxime Ripard > --- > arch/arm/boot/dts/sun4i-a10.dtsi | 4 > arch/arm/boot/dts/sun5i.dtsi | 2 -- > arch/arm/boot/dts/sun6i-a31.dtsi | 4 > arch/arm/boot/dts/sun7i-a20.dtsi | 4 > arch/arm/boot/dts/sun8i-a23-a33.dtsi | 2 -- > arch/arm/boot/dts/sun8i-a83t.dtsi| 3 --- > arch/arm/boot/dts/sun8i-r40.dtsi | 4 > arch/arm/boot/dts/sun9i-a80.dtsi | 5 - > 8 files changed, 28 deletions(-) > > diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi > b/arch/arm/boot/dts/sun4i-a10.dtsi > index ef6ec526f394..e88daa4ef1af 100644 > --- a/arch/arm/boot/dts/sun4i-a10.dtsi > +++ b/arch/arm/boot/dts/sun4i-a10.dtsi > @@ -520,7 +520,6 @@ > interrupts = <39>; > clocks = <&ccu CLK_AHB_EHCI0>; > phys = <&usbphy 1>; > - phy-names = "usb"; > status = "disabled"; > }; > > @@ -530,7 +529,6 @@ > interrupts = <64>; > clocks = <&ccu CLK_USB_OHCI0>, <&ccu CLK_AHB_OHCI0>; > phys = <&usbphy 1>; > - phy-names = "usb"; > status = "disabled"; > }; > > @@ -610,7 +608,6 @@ > interrupts = <40>; > clocks = <&ccu CLK_AHB_EHCI1>; > phys = <&usbphy 2>; > - phy-names = "usb"; > status = "disabled"; > }; > > @@ -620,7 +617,6 @@ > interrupts = <65>; > clocks = <&ccu CLK_USB_OHCI1>, <&ccu CLK_AHB_OHCI1>; > phys = <&usbphy 2>; > - phy-names = "usb"; > status = "disabled"; > }; > > diff --git a/arch/arm/boot/dts/sun5i.dtsi b/arch/arm/boot/dts/sun5i.dtsi > index cb820bd7974c..0d71b01967a3 100644 > --- a/arch/arm/boot/dts/sun5i.dtsi > +++ b/arch/arm/boot/dts/sun5i.dtsi > @@ -391,7 +391,6 @@ > interrupts = <39>; > clocks = <&ccu CLK_AHB_EHCI>; > phys = <&usbphy 1>; > - phy-names = "usb"; > status = "disabled"; > }; > > @@ -401,7 +400,6 @@ > interrupts = <40>; > clocks = <&ccu CLK_USB_OHCI>, <&ccu CLK_AHB_OHCI>; > phys = <&usbphy 1>; > - phy-names = "usb"; > status = "disabled"; > }; > > diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi > b/arch/arm/boot/dts/sun6i-a31.dtsi > index fa983f9ff5f5..c04efad81bbc 100644 > --- a/arch/arm/boot/dts/sun6i-a31.dtsi > +++ b/arch/arm/boot/dts/sun6i-a31.dtsi > @@ -543,7 +543,6 @@ > clocks = <&ccu CLK_AHB1_EHCI0>; > resets = <&ccu RST_AHB1_EHCI0>; > phys = <&usbphy 1>; > - phy-names = "usb"; > status = "disabled"; > }; > > @@ -554,7 +553,6 @@ > clocks = <&ccu CLK_AHB1_OHCI0>, <&ccu CLK_USB_OHCI0>; > resets = <&ccu RST_AHB1_OHCI0>; > phys = <&usbphy 1>; > - phy-names = "usb"; > status = "disabled"; > }; > > @@ -565,7 +563,6 @@ > clocks = <&ccu CLK_AHB1_EHCI1>; > resets = <&ccu RST_AHB1_EHCI1>; > phys = <&usbphy 2>; > - phy-names = "usb"; > status = "disabled"; > }; > > @@ -576,7 +573,6 @@ > clocks = <&ccu CLK_AHB1_OHCI1>, <&ccu CLK_USB_OHCI1>; > resets = <&ccu RST_AHB1_OHCI1>; > phys = <&usbphy 2>; > - phy-names = "usb"; > status = "disabled"; > }; > > diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi > b/arch/arm/boot/dts/sun7i-a20.dtsi > index 794c915f504b..9ad8e445b240 100644 > --- a/arch/arm/boot/dts/sun7i-a20.dtsi > +++ b/arch/arm/boot/dts/sun7i-a20.dtsi > @@ -612,7 +612,6 @@ > interrupts = ; > clocks = <&ccu CLK_AHB_EHCI0>; > phys = <&usbphy 1>; > - phy-names = "usb"; > status = "disabled"; > }; > > @@ -622,7 +621,6 @@ > interrupts = ; > clocks = <&ccu CLK_USB_OHCI0>, <&ccu CLK_AHB_OHCI0>; > phys = <&usbphy 1>; > - phy-names = "usb"; > status = "disabled"; > }; > > @@ -705,7 +703,6 @@ > interrupts = ; >
Re: musb: cppi41: broken high speed FTDI functionality when connected to musb directly
Hi, * Tony Lindgren [191001 16:52]: > * Yegor Yefremov [191001 09:20]: > > I've tried to increase the autosuspend_delay_ms and to set control to > > "on" but nothing changes. Below you can see the output of my testing > > script [1] (Py2 only). As one can see, the first cycle i.e. after the > > port is open for the first time, fails. But the subsequent cycle is > > successful. If you invoke the script again, everything repeats. > > > > I've also made printk() in cppi41_run_queue() and it looks like this > > routine will be called from cppi41_dma_issue_pending() only in the > > beginning of the second test cycle. > > So sounds like for you intially cppi41_dma_issue_pending() has > !cdd->is_suspended and just adds the request to the queue. And > then cppi41_run_queue() never gets called if this happens while > we have cppi41_runtime_resume() is still running? I got it reproducable here by adding msleep(500) to the beginning of cppi41_runtime_resume() :) Otherwise I'm only able to trigger the issue maybe one out of 20 tries it seems. Turns out the first dma call done by musb_ep_program() must wait if cppi41 is PM runtime suspended. Otherwise musb_ep_program() continues with other PIO packets before the DMA transfer is started. The patch below fixes it for me with a pm_runtime_get_sync() in device_prep_slave_sg, so adding Peter and Vinod to Cc. The other way to fix this would be to just wake up cpp41 in cppi41_dma_prep_slave_sg() and return NULL so that we can have musb_ep_program() continue with PIO while cppi41 is asleep. Anyways, care to try it out and see if it fixes your issue? Regards, Tony 8< -- diff --git a/drivers/dma/ti/cppi41.c b/drivers/dma/ti/cppi41.c --- a/drivers/dma/ti/cppi41.c +++ b/drivers/dma/ti/cppi41.c @@ -586,9 +586,18 @@ static struct dma_async_tx_descriptor *cppi41_dma_prep_slave_sg( enum dma_transfer_direction dir, unsigned long tx_flags, void *context) { struct cppi41_channel *c = to_cpp41_chan(chan); + struct cppi41_dd *cdd = c->cdd; struct cppi41_desc *d; struct scatterlist *sg; unsigned int i; + int error; + + error = pm_runtime_get_sync(cdd->ddev.dev); + if (error < 0) { + pm_runtime_put_noidle(cdd->ddev.dev); + + return NULL; + } d = c->desc; for_each_sg(sgl, sg, sg_len, i) { @@ -611,6 +620,9 @@ static struct dma_async_tx_descriptor *cppi41_dma_prep_slave_sg( d++; } + pm_runtime_mark_last_busy(cdd->ddev.dev); + pm_runtime_put_autosuspend(cdd->ddev.dev); + return &c->txd; } -- 2.23.0
Re: musb: cppi41: broken high speed FTDI functionality when connected to musb directly
On Wed, Oct 2, 2019 at 12:03 AM Tony Lindgren wrote: > > Hi, > > * Tony Lindgren [191001 16:52]: > > * Yegor Yefremov [191001 09:20]: > > > I've tried to increase the autosuspend_delay_ms and to set control to > > > "on" but nothing changes. Below you can see the output of my testing > > > script [1] (Py2 only). As one can see, the first cycle i.e. after the > > > port is open for the first time, fails. But the subsequent cycle is > > > successful. If you invoke the script again, everything repeats. > > > > > > I've also made printk() in cppi41_run_queue() and it looks like this > > > routine will be called from cppi41_dma_issue_pending() only in the > > > beginning of the second test cycle. > > > > So sounds like for you intially cppi41_dma_issue_pending() has > > !cdd->is_suspended and just adds the request to the queue. And > > then cppi41_run_queue() never gets called if this happens while > > we have cppi41_runtime_resume() is still running? > > I got it reproducable here by adding msleep(500) to the beginning > of cppi41_runtime_resume() :) Otherwise I'm only able to trigger > the issue maybe one out of 20 tries it seems. > > Turns out the first dma call done by musb_ep_program() must wait > if cppi41 is PM runtime suspended. Otherwise musb_ep_program() > continues with other PIO packets before the DMA transfer is > started. > > The patch below fixes it for me with a pm_runtime_get_sync() > in device_prep_slave_sg, so adding Peter and Vinod to Cc. > > The other way to fix this would be to just wake up cpp41 in > cppi41_dma_prep_slave_sg() and return NULL so that we can > have musb_ep_program() continue with PIO while cppi41 is > asleep. > > Anyways, care to try it out and see if it fixes your issue? > > Regards, > > Tony > > 8< -- > diff --git a/drivers/dma/ti/cppi41.c b/drivers/dma/ti/cppi41.c > --- a/drivers/dma/ti/cppi41.c > +++ b/drivers/dma/ti/cppi41.c > @@ -586,9 +586,18 @@ static struct dma_async_tx_descriptor > *cppi41_dma_prep_slave_sg( > enum dma_transfer_direction dir, unsigned long tx_flags, void > *context) > { > struct cppi41_channel *c = to_cpp41_chan(chan); > + struct cppi41_dd *cdd = c->cdd; > struct cppi41_desc *d; > struct scatterlist *sg; > unsigned int i; > + int error; > + > + error = pm_runtime_get_sync(cdd->ddev.dev); > + if (error < 0) { > + pm_runtime_put_noidle(cdd->ddev.dev); > + > + return NULL; > + } > > d = c->desc; > for_each_sg(sgl, sg, sg_len, i) { > @@ -611,6 +620,9 @@ static struct dma_async_tx_descriptor > *cppi41_dma_prep_slave_sg( > d++; > } > > + pm_runtime_mark_last_busy(cdd->ddev.dev); > + pm_runtime_put_autosuspend(cdd->ddev.dev); > + > return &c->txd; > } The fix is working but on the first invocation, I get this output (minicom provokes the same output): # serialtest.py -c 2 /dev/ttyUSB0 /dev/ttyUSB0 [ 210.674524] [ 210.676065] [ 210.680359] WARNING: inconsistent lock state [ 210.684660] 5.4.0-rc1 #5 Not tainted [ 210.688255] [ 210.692550] inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. [ 210.698592] serialtest.py/250 [HC0[0]:SC0[0]:HE1:SE1] takes: [ 210.704284] ccce4050 (&(&musb->lock)->rlock){?.-.}, at: musb_urb_enqueue+0x2e4/0x768 [musb_hdrc] [ 210.713299] {IN-HARDIRQ-W} state was registered at: [ 210.718229] _raw_spin_lock_irqsave+0x38/0x4c [ 210.722716] dsps_interrupt+0x28/0x288 [musb_dsps] [ 210.727631] __handle_irq_event_percpu+0x48/0x35c [ 210.732452] handle_irq_event_percpu+0x28/0x7c [ 210.737011] handle_irq_event+0x38/0x5c [ 210.740963] handle_level_irq+0xc8/0x158 [ 210.744999] generic_handle_irq+0x20/0x34 [ 210.749121] __handle_domain_irq+0x64/0xe0 [ 210.753334] __irq_usr+0x54/0x80 [ 210.756669] 0xb6dc6820 [ 210.759218] irq event stamp: 28856 [ 210.762655] hardirqs last enabled at (28855): [] kmem_cache_alloc_trace+0xc0/0x724 [ 210.771231] hardirqs last disabled at (28856): [] _raw_spin_lock_irqsave+0x1c/0x4c [ 210.779718] softirqs last enabled at (28774): [] __do_softirq+0x360/0x520 [ 210.787507] softirqs last disabled at (28759): [] irq_exit+0x148/0x180 [ 210.794939] [ 210.794939] other info that might help us debug this: [ 210.801499] Possible unsafe locking scenario: [ 210.801499] [ 210.807449]CPU0 [ 210.809909] [ 210.812367] lock(&(&musb->lock)->rlock); [ 210.816490] [ 210.819125] lock(&(&musb->lock)->rlock); [ 210.823422] [ 210.823422] *** DEADLOCK *** [ 210.823422] [ 210.829376] 4 locks held by serialtest.py/250: [ 210.833843] #0: ccf138dc (&tty->legacy_mutex){+.+.}, at: tty_init_dev+0x48/0x1cc [ 210.841395] #1: cce50160 (&port->mutex){+.+.}, at: tty_port_open+0x4c/0xc0 [ 210.848412] #2: ccb08d4c (&serial->disc_mutex){+.+.}, at: serial_port_activate+0x20/0x90 [usbserial] [ 210.857747] #3: ccc