[PATCH V3] usb: gadget: composite: Fix possible double free memory bug

2019-10-01 Thread Chandana Kishori Chiluveru
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

2019-10-01 Thread Johan Hovold
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

2019-10-01 Thread Yegor Yefremov
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

2019-10-01 Thread Heikki Krogerus
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

2019-10-01 Thread Heikki Krogerus
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

2019-10-01 Thread Heikki Krogerus
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

2019-10-01 Thread Heikki Krogerus
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

2019-10-01 Thread Heikki Krogerus
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

2019-10-01 Thread Heikki Krogerus
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

2019-10-01 Thread Heikki Krogerus
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()

2019-10-01 Thread Heikki Krogerus
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()

2019-10-01 Thread Dan Carpenter
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()

2019-10-01 Thread Heikki Krogerus
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

2019-10-01 Thread Guenter Roeck

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

2019-10-01 Thread Guenter Roeck

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

2019-10-01 Thread Guenter Roeck

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

2019-10-01 Thread Guenter Roeck

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

2019-10-01 Thread Guenter Roeck

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

2019-10-01 Thread Guenter Roeck

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

2019-10-01 Thread Guenter Roeck

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()

2019-10-01 Thread Guenter Roeck

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

2019-10-01 Thread Mika Westerberg
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

2019-10-01 Thread Matthias Maennich

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

2019-10-01 Thread Tony Lindgren
* 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

2019-10-01 Thread Ajay Gupta
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

2019-10-01 Thread Emmanuel Vadot


 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

2019-10-01 Thread Tony Lindgren
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

2019-10-01 Thread Yegor Yefremov
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