Registering real device entries (struct device) for the mode muxes as well as for the orientation switches.
The Type-C mux code was deliberately attempting to avoid creation of separate device entries for the orientation switch and the mode switch (alternate modes) because they are not physical devices. They are functions of a single physical multiplexer/demultiplexer switch device.
Unfortunately because of the dependency we still have on the underlying mux device driver, we had to put in hacks like the one in the commit 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") to make sure the driver does not disappear from underneath us. Even with those hacks we were still left with a potential NUll pointer dereference scenario, so just creating the device entries, and letting the core take care of the dependencies. No more hacks needed.
Fixes: 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") Cc: v4.19.x stable@vger.kernel.org # v4.19.x+ Signed-off-by: Heikki Krogerus heikki.krogerus@linux.intel.com --- drivers/usb/typec/bus.h | 15 ++ drivers/usb/typec/class.c | 16 ++- drivers/usb/typec/mux.c | 207 +++++++++++++++++++--------- drivers/usb/typec/mux/pi3usb30532.c | 46 ++++--- include/linux/usb/typec_mux.h | 58 ++++---- 5 files changed, 220 insertions(+), 122 deletions(-)
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h index db40e61d8b72..0c9661c96473 100644 --- a/drivers/usb/typec/bus.h +++ b/drivers/usb/typec/bus.h @@ -35,4 +35,19 @@ extern const struct device_type typec_port_dev_type; #define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type) #define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type)
+extern struct class typec_mux_class; + +struct typec_switch { + struct device dev; + typec_switch_set_fn_t set; +}; + +struct typec_mux { + struct device dev; + typec_mux_set_fn_t set; +}; + +#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev) +#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev) + #endif /* __USB_TYPEC_ALTMODE_H__ */ diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 2eb623841847..8f5223879d91 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1646,13 +1646,25 @@ static int __init typec_init(void) if (ret) return ret;
+ ret = class_register(&typec_mux_class); + if (ret) + goto err_unregister_bus; + typec_class = class_create(THIS_MODULE, "typec"); if (IS_ERR(typec_class)) { - bus_unregister(&typec_bus); - return PTR_ERR(typec_class); + ret = PTR_ERR(typec_class); + goto err_unregister_mux_class; }
return 0; + +err_unregister_mux_class: + class_unregister(&typec_mux_class); + +err_unregister_bus: + bus_unregister(&typec_bus); + + return ret; } subsys_initcall(typec_init);
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 2ce54f3fc79c..f945abd23222 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -15,35 +15,35 @@ #include <linux/slab.h> #include <linux/usb/typec_mux.h>
-static DEFINE_MUTEX(switch_lock); -static DEFINE_MUTEX(mux_lock); -static LIST_HEAD(switch_list); -static LIST_HEAD(mux_list); +#include "bus.h" + +static int name_match(struct device *dev, const void *name) +{ + return !strcmp((const char *)name, dev_name(dev)); +} + +static int fwnode_match(struct device *dev, const void *fwnode) +{ + return dev_fwnode(dev) == fwnode; +}
static void *typec_switch_match(struct device_connection *con, int ep, void *data) { - struct typec_switch *sw; + struct device *dev;
- if (!con->fwnode) { - list_for_each_entry(sw, &switch_list, entry) - if (!strcmp(con->endpoint[ep], dev_name(sw->dev))) - return sw; - return ERR_PTR(-EPROBE_DEFER); - } - - /* - * With OF graph the mux node must have a boolean device property named - * "orientation-switch". - */ - if (con->id && !fwnode_property_present(con->fwnode, con->id)) - return NULL; + if (con->fwnode) { + if (con->id && !fwnode_property_present(con->fwnode, con->id)) + return NULL;
- list_for_each_entry(sw, &switch_list, entry) - if (dev_fwnode(sw->dev) == con->fwnode) - return sw; + dev = class_find_device(&typec_mux_class, NULL, con->fwnode, + fwnode_match); + } else { + dev = class_find_device(&typec_mux_class, NULL, + con->endpoint[ep], name_match); + }
- return con->id ? ERR_PTR(-EPROBE_DEFER) : NULL; + return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); }
/** @@ -59,14 +59,10 @@ struct typec_switch *typec_switch_get(struct device *dev) { struct typec_switch *sw;
- mutex_lock(&switch_lock); sw = device_connection_find_match(dev, "orientation-switch", NULL, typec_switch_match); - if (!IS_ERR_OR_NULL(sw)) { - WARN_ON(!try_module_get(sw->dev->driver->owner)); - get_device(sw->dev); - } - mutex_unlock(&switch_lock); + if (!IS_ERR_OR_NULL(sw)) + get_device(&sw->dev);
return sw; } @@ -80,13 +76,21 @@ EXPORT_SYMBOL_GPL(typec_switch_get); */ void typec_switch_put(struct typec_switch *sw) { - if (!IS_ERR_OR_NULL(sw)) { - module_put(sw->dev->driver->owner); - put_device(sw->dev); - } + if (!IS_ERR_OR_NULL(sw)) + put_device(&sw->dev); } EXPORT_SYMBOL_GPL(typec_switch_put);
+static void typec_switch_release(struct device *dev) +{ + kfree(to_typec_switch(dev)); +} + +static const struct device_type typec_switch_dev_type = { + .name = "orientation_switch", + .release = typec_switch_release, +}; + /** * typec_switch_register - Register USB Type-C orientation switch * @sw: USB Type-C orientation switch @@ -96,13 +100,38 @@ EXPORT_SYMBOL_GPL(typec_switch_put); * connector to the USB controllers. USB Type-C plugs can be inserted * right-side-up or upside-down. */ -int typec_switch_register(struct typec_switch *sw) +struct typec_switch * +typec_switch_register(struct device *parent, + const struct typec_switch_desc *desc) { - mutex_lock(&switch_lock); - list_add_tail(&sw->entry, &switch_list); - mutex_unlock(&switch_lock); + struct typec_switch *sw; + int ret; + + if (!desc || !desc->set) + return ERR_PTR(-EINVAL); + + sw = kzalloc(sizeof(*sw), GFP_KERNEL); + if (!sw) + return ERR_PTR(-ENOMEM); + + sw->set = desc->set; + + device_initialize(&sw->dev); + sw->dev.parent = parent; + sw->dev.class = &typec_mux_class; + sw->dev.type = &typec_switch_dev_type; + sw->dev.driver_data = desc->drvdata; + sw->dev.fwnode = desc->fwnode; + dev_set_name(&sw->dev, "%s-switch", dev_name(parent)); + + ret = device_add(&sw->dev); + if (ret) { + dev_err(parent, "failed to register switch (%d)\n", ret); + put_device(&sw->dev); + return ERR_PTR(ret); + }
- return 0; + return sw; } EXPORT_SYMBOL_GPL(typec_switch_register);
@@ -114,28 +143,33 @@ EXPORT_SYMBOL_GPL(typec_switch_register); */ void typec_switch_unregister(struct typec_switch *sw) { - mutex_lock(&switch_lock); - list_del(&sw->entry); - mutex_unlock(&switch_lock); + if (!IS_ERR_OR_NULL(sw)) + device_unregister(&sw->dev); } EXPORT_SYMBOL_GPL(typec_switch_unregister);
+void *typec_switch_get_drvdata(struct typec_switch *sw) +{ + return dev_get_drvdata(&sw->dev); +} +EXPORT_SYMBOL_GPL(typec_switch_get_drvdata); + /* ------------------------------------------------------------------------- */
static void *typec_mux_match(struct device_connection *con, int ep, void *data) { const struct typec_altmode_desc *desc = data; - struct typec_mux *mux; - int nval; + struct device *dev; bool match; + int nval; u16 *val; int i;
if (!con->fwnode) { - list_for_each_entry(mux, &mux_list, entry) - if (!strcmp(con->endpoint[ep], dev_name(mux->dev))) - return mux; - return ERR_PTR(-EPROBE_DEFER); + dev = class_find_device(&typec_mux_class, NULL, + con->endpoint[ep], name_match); + + return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); }
/* @@ -180,11 +214,10 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data) return NULL;
find_mux: - list_for_each_entry(mux, &mux_list, entry) - if (dev_fwnode(mux->dev) == con->fwnode) - return mux; + dev = class_find_device(&typec_mux_class, NULL, con->fwnode, + fwnode_match);
- return ERR_PTR(-EPROBE_DEFER); + return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); }
/** @@ -202,14 +235,10 @@ struct typec_mux *typec_mux_get(struct device *dev, { struct typec_mux *mux;
- mutex_lock(&mux_lock); mux = device_connection_find_match(dev, "mode-switch", (void *)desc, typec_mux_match); - if (!IS_ERR_OR_NULL(mux)) { - WARN_ON(!try_module_get(mux->dev->driver->owner)); - get_device(mux->dev); - } - mutex_unlock(&mux_lock); + if (!IS_ERR_OR_NULL(mux)) + get_device(&mux->dev);
return mux; } @@ -223,13 +252,21 @@ EXPORT_SYMBOL_GPL(typec_mux_get); */ void typec_mux_put(struct typec_mux *mux) { - if (!IS_ERR_OR_NULL(mux)) { - module_put(mux->dev->driver->owner); - put_device(mux->dev); - } + if (!IS_ERR_OR_NULL(mux)) + put_device(&mux->dev); } EXPORT_SYMBOL_GPL(typec_mux_put);
+static void typec_mux_release(struct device *dev) +{ + kfree(to_typec_mux(dev)); +} + +static const struct device_type typec_mux_dev_type = { + .name = "mode_switch", + .release = typec_mux_release, +}; + /** * typec_mux_register - Register Multiplexer routing USB Type-C pins * @mux: USB Type-C Connector Multiplexer/DeMultiplexer @@ -239,13 +276,37 @@ EXPORT_SYMBOL_GPL(typec_mux_put); * the pins on the connector need to be reconfigured. This function registers * multiplexer switches routing the pins on the connector. */ -int typec_mux_register(struct typec_mux *mux) +struct typec_mux * +typec_mux_register(struct device *parent, const struct typec_mux_desc *desc) { - mutex_lock(&mux_lock); - list_add_tail(&mux->entry, &mux_list); - mutex_unlock(&mux_lock); + struct typec_mux *mux; + int ret; + + if (!desc || !desc->set) + return ERR_PTR(-EINVAL); + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->set = desc->set; + + device_initialize(&mux->dev); + mux->dev.parent = parent; + mux->dev.class = &typec_mux_class; + mux->dev.type = &typec_mux_dev_type; + mux->dev.fwnode = desc->fwnode; + mux->dev.driver_data = desc->drvdata; + dev_set_name(&mux->dev, "%s-mux", dev_name(parent)); + + ret = device_add(&mux->dev); + if (ret) { + dev_err(parent, "failed to register mux (%d)\n", ret); + put_device(&mux->dev); + return ERR_PTR(ret); + }
- return 0; + return mux; } EXPORT_SYMBOL_GPL(typec_mux_register);
@@ -257,8 +318,18 @@ EXPORT_SYMBOL_GPL(typec_mux_register); */ void typec_mux_unregister(struct typec_mux *mux) { - mutex_lock(&mux_lock); - list_del(&mux->entry); - mutex_unlock(&mux_lock); + if (!IS_ERR_OR_NULL(mux)) + device_unregister(&mux->dev); } EXPORT_SYMBOL_GPL(typec_mux_unregister); + +void *typec_mux_get_drvdata(struct typec_mux *mux) +{ + return dev_get_drvdata(&mux->dev); +} +EXPORT_SYMBOL_GPL(typec_mux_get_drvdata); + +struct class typec_mux_class = { + .name = "typec_mux", + .owner = THIS_MODULE, +}; diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c index 9294e85fd34b..5585b109095b 100644 --- a/drivers/usb/typec/mux/pi3usb30532.c +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -23,8 +23,8 @@ struct pi3usb30532 { struct i2c_client *client; struct mutex lock; /* protects the cached conf register */ - struct typec_switch sw; - struct typec_mux mux; + struct typec_switch *sw; + struct typec_mux *mux; u8 conf; };
@@ -48,7 +48,7 @@ static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf) static int pi3usb30532_sw_set(struct typec_switch *sw, enum typec_orientation orientation) { - struct pi3usb30532 *pi = container_of(sw, struct pi3usb30532, sw); + struct pi3usb30532 *pi = typec_switch_get_drvdata(sw); u8 new_conf; int ret;
@@ -75,7 +75,7 @@ static int pi3usb30532_sw_set(struct typec_switch *sw,
static int pi3usb30532_mux_set(struct typec_mux *mux, int state) { - struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux); + struct pi3usb30532 *pi = typec_mux_get_drvdata(mux); u8 new_conf; int ret;
@@ -113,6 +113,8 @@ static int pi3usb30532_mux_set(struct typec_mux *mux, int state) static int pi3usb30532_probe(struct i2c_client *client) { struct device *dev = &client->dev; + struct typec_switch_desc sw_desc; + struct typec_mux_desc mux_desc; struct pi3usb30532 *pi; int ret;
@@ -121,10 +123,6 @@ static int pi3usb30532_probe(struct i2c_client *client) return -ENOMEM;
pi->client = client; - pi->sw.dev = dev; - pi->sw.set = pi3usb30532_sw_set; - pi->mux.dev = dev; - pi->mux.set = pi3usb30532_mux_set; mutex_init(&pi->lock);
ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF); @@ -134,17 +132,27 @@ static int pi3usb30532_probe(struct i2c_client *client) } pi->conf = ret;
- ret = typec_switch_register(&pi->sw); - if (ret) { - dev_err(dev, "Error registering typec switch: %d\n", ret); - return ret; + sw_desc.drvdata = pi; + sw_desc.fwnode = dev->fwnode; + sw_desc.set = pi3usb30532_sw_set; + + pi->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(pi->sw)) { + dev_err(dev, "Error registering typec switch: %ld\n", + PTR_ERR(pi->sw)); + return PTR_ERR(pi->sw); }
- ret = typec_mux_register(&pi->mux); - if (ret) { - typec_switch_unregister(&pi->sw); - dev_err(dev, "Error registering typec mux: %d\n", ret); - return ret; + mux_desc.drvdata = pi; + mux_desc.fwnode = dev->fwnode; + mux_desc.set = pi3usb30532_mux_set; + + pi->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(pi->mux)) { + typec_switch_unregister(pi->sw); + dev_err(dev, "Error registering typec mux: %ld\n", + PTR_ERR(pi->mux)); + return PTR_ERR(pi->mux); }
i2c_set_clientdata(client, pi); @@ -155,8 +163,8 @@ static int pi3usb30532_remove(struct i2c_client *client) { struct pi3usb30532 *pi = i2c_get_clientdata(client);
- typec_mux_unregister(&pi->mux); - typec_switch_unregister(&pi->sw); + typec_mux_unregister(pi->mux); + typec_switch_unregister(pi->sw); return 0; }
diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index 43f40685e53c..621e0811e794 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -3,54 +3,46 @@ #ifndef __USB_TYPEC_MUX #define __USB_TYPEC_MUX
-#include <linux/list.h> #include <linux/usb/typec.h>
struct device; +struct typec_mux; +struct typec_switch; +struct fwnode_handle;
-/** - * struct typec_switch - USB Type-C cable orientation switch - * @dev: Switch device - * @entry: List entry - * @set: Callback to the driver for setting the orientation - * - * USB Type-C pin flipper switch routing the correct data pairs from the - * connector to the USB controller depending on the orientation of the cable - * plug. - */ -struct typec_switch { - struct device *dev; - struct list_head entry; - - int (*set)(struct typec_switch *sw, enum typec_orientation orientation); +typedef int (*typec_switch_set_fn_t)(struct typec_switch *sw, + enum typec_orientation orientation); + +struct typec_switch_desc { + void *drvdata; + struct fwnode_handle *fwnode; + typec_switch_set_fn_t set; };
-/** - * struct typec_switch - USB Type-C connector pin mux - * @dev: Mux device - * @entry: List entry - * @set: Callback to the driver for setting the state of the mux - * - * Pin Multiplexer/DeMultiplexer switch routing the USB Type-C connector pins to - * different components depending on the requested mode of operation. Used with - * Accessory/Alternate modes. - */ -struct typec_mux { - struct device *dev; - struct list_head entry; - - int (*set)(struct typec_mux *mux, int state); +typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux, int state); + +struct typec_mux_desc { + void *drvdata; + struct fwnode_handle *fwnode; + typec_mux_set_fn_t set; };
struct typec_switch *typec_switch_get(struct device *dev); void typec_switch_put(struct typec_switch *sw); -int typec_switch_register(struct typec_switch *sw); +struct typec_switch * +typec_switch_register(struct device *parent, + const struct typec_switch_desc *desc); void typec_switch_unregister(struct typec_switch *sw);
+void *typec_switch_get_drvdata(struct typec_switch *sw); + struct typec_mux * typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc); void typec_mux_put(struct typec_mux *mux); -int typec_mux_register(struct typec_mux *mux); +struct typec_mux * +typec_mux_register(struct device *parent, const struct typec_mux_desc *desc); void typec_mux_unregister(struct typec_mux *mux);
+void *typec_mux_get_drvdata(struct typec_mux *mux); + #endif /* __USB_TYPEC_MUX */
On Mon, Apr 01, 2019 at 01:15:53PM +0300, Heikki Krogerus wrote:
Registering real device entries (struct device) for the mode muxes as well as for the orientation switches.
The Type-C mux code was deliberately attempting to avoid creation of separate device entries for the orientation switch and the mode switch (alternate modes) because they are not physical devices. They are functions of a single physical multiplexer/demultiplexer switch device.
Unfortunately because of the dependency we still have on the underlying mux device driver, we had to put in hacks like the one in the commit 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") to make sure the driver does not disappear from underneath us. Even with those hacks we were still left with a potential NUll pointer dereference scenario, so just creating the device entries, and letting the core take care of the dependencies. No more hacks needed.
Fixes: 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") Cc: v4.19.x stable@vger.kernel.org # v4.19.x+ Signed-off-by: Heikki Krogerus heikki.krogerus@linux.intel.com
This looks good to me, nice work!
But, it would be nice if someone who has this hardware can test it to verify it does actually work :)
thanks,
greg k-h
On Mon, Apr 01, 2019 at 12:34:29PM +0200, Greg KH wrote:
On Mon, Apr 01, 2019 at 01:15:53PM +0300, Heikki Krogerus wrote:
Registering real device entries (struct device) for the mode muxes as well as for the orientation switches.
The Type-C mux code was deliberately attempting to avoid creation of separate device entries for the orientation switch and the mode switch (alternate modes) because they are not physical devices. They are functions of a single physical multiplexer/demultiplexer switch device.
Unfortunately because of the dependency we still have on the underlying mux device driver, we had to put in hacks like the one in the commit 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") to make sure the driver does not disappear from underneath us. Even with those hacks we were still left with a potential NUll pointer dereference scenario, so just creating the device entries, and letting the core take care of the dependencies. No more hacks needed.
Fixes: 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") Cc: v4.19.x stable@vger.kernel.org # v4.19.x+ Signed-off-by: Heikki Krogerus heikki.krogerus@linux.intel.com
This looks good to me, nice work!
But, it would be nice if someone who has this hardware can test it to verify it does actually work :)
This alone does not work on Intel Cherrytrail platforms. I need to make the Intel Cherrytrail MFD driver (intel_cht_int33fe.c) to use the new device names that we now have for the muxes. Sorry for the mistake.
I'll resend this and include the needed modifications to intel_cht_int33fe.c. Hans should be able to test this once I do that. I hope he has time.
thanks,
HI,
On 01-04-19 14:40, Heikki Krogerus wrote:
On Mon, Apr 01, 2019 at 12:34:29PM +0200, Greg KH wrote:
On Mon, Apr 01, 2019 at 01:15:53PM +0300, Heikki Krogerus wrote:
Registering real device entries (struct device) for the mode muxes as well as for the orientation switches.
The Type-C mux code was deliberately attempting to avoid creation of separate device entries for the orientation switch and the mode switch (alternate modes) because they are not physical devices. They are functions of a single physical multiplexer/demultiplexer switch device.
Unfortunately because of the dependency we still have on the underlying mux device driver, we had to put in hacks like the one in the commit 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") to make sure the driver does not disappear from underneath us. Even with those hacks we were still left with a potential NUll pointer dereference scenario, so just creating the device entries, and letting the core take care of the dependencies. No more hacks needed.
Fixes: 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") Cc: v4.19.x stable@vger.kernel.org # v4.19.x+ Signed-off-by: Heikki Krogerus heikki.krogerus@linux.intel.com
This looks good to me, nice work!
But, it would be nice if someone who has this hardware can test it to verify it does actually work :)
This alone does not work on Intel Cherrytrail platforms. I need to make the Intel Cherrytrail MFD driver (intel_cht_int33fe.c) to use the new device names that we now have for the muxes. Sorry for the mistake.
I'll resend this and include the needed modifications to intel_cht_int33fe.c. Hans should be able to test this once I do that. I hope he has time.
Yes I need to get back to the CherryTrail Type-C stuff we discussed a while back anyways. On which tree/branch should I test v2 of this patch(series) ?
Regards,
Hans
On Mon, Apr 01, 2019 at 02:45:11PM +0200, Hans de Goede wrote:
HI,
On 01-04-19 14:40, Heikki Krogerus wrote:
On Mon, Apr 01, 2019 at 12:34:29PM +0200, Greg KH wrote:
On Mon, Apr 01, 2019 at 01:15:53PM +0300, Heikki Krogerus wrote:
Registering real device entries (struct device) for the mode muxes as well as for the orientation switches.
The Type-C mux code was deliberately attempting to avoid creation of separate device entries for the orientation switch and the mode switch (alternate modes) because they are not physical devices. They are functions of a single physical multiplexer/demultiplexer switch device.
Unfortunately because of the dependency we still have on the underlying mux device driver, we had to put in hacks like the one in the commit 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") to make sure the driver does not disappear from underneath us. Even with those hacks we were still left with a potential NUll pointer dereference scenario, so just creating the device entries, and letting the core take care of the dependencies. No more hacks needed.
Fixes: 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") Cc: v4.19.x stable@vger.kernel.org # v4.19.x+ Signed-off-by: Heikki Krogerus heikki.krogerus@linux.intel.com
This looks good to me, nice work!
But, it would be nice if someone who has this hardware can test it to verify it does actually work :)
This alone does not work on Intel Cherrytrail platforms. I need to make the Intel Cherrytrail MFD driver (intel_cht_int33fe.c) to use the new device names that we now have for the muxes. Sorry for the mistake.
I'll resend this and include the needed modifications to intel_cht_int33fe.c. Hans should be able to test this once I do that. I hope he has time.
Yes I need to get back to the CherryTrail Type-C stuff we discussed a while back anyways. On which tree/branch should I test v2 of this patch(series) ?
Greg's usb-next.
thanks,
On Tue, Apr 02, 2019 at 03:47:47PM +0300, Heikki Krogerus wrote:
On Mon, Apr 01, 2019 at 02:45:11PM +0200, Hans de Goede wrote:
HI,
On 01-04-19 14:40, Heikki Krogerus wrote:
On Mon, Apr 01, 2019 at 12:34:29PM +0200, Greg KH wrote:
On Mon, Apr 01, 2019 at 01:15:53PM +0300, Heikki Krogerus wrote:
Registering real device entries (struct device) for the mode muxes as well as for the orientation switches.
The Type-C mux code was deliberately attempting to avoid creation of separate device entries for the orientation switch and the mode switch (alternate modes) because they are not physical devices. They are functions of a single physical multiplexer/demultiplexer switch device.
Unfortunately because of the dependency we still have on the underlying mux device driver, we had to put in hacks like the one in the commit 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") to make sure the driver does not disappear from underneath us. Even with those hacks we were still left with a potential NUll pointer dereference scenario, so just creating the device entries, and letting the core take care of the dependencies. No more hacks needed.
Fixes: 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") Cc: v4.19.x stable@vger.kernel.org # v4.19.x+ Signed-off-by: Heikki Krogerus heikki.krogerus@linux.intel.com
This looks good to me, nice work!
But, it would be nice if someone who has this hardware can test it to verify it does actually work :)
This alone does not work on Intel Cherrytrail platforms. I need to make the Intel Cherrytrail MFD driver (intel_cht_int33fe.c) to use the new device names that we now have for the muxes. Sorry for the mistake.
I'll resend this and include the needed modifications to intel_cht_int33fe.c. Hans should be able to test this once I do that. I hope he has time.
Yes I need to get back to the CherryTrail Type-C stuff we discussed a while back anyways. On which tree/branch should I test v2 of this patch(series) ?
Greg's usb-next.
I just realized that the potential NULL pointer dereference that this patch is meant to fix can't happen until my "software node reference" modifications [1] have been applied. I don't think this needs to be considered a fix.
I think the correct way to handle this patch is to make just it part of that series. I'll resend the series, and include this patch in it.
[1] https://lkml.org/lkml/2019/3/15/457
thanks,
Hi Heikki,
I love your patch! Perhaps something to improve:
[auto build test WARNING on usb/usb-testing] [also build test WARNING on v5.1-rc3 next-20190401] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Heikki-Krogerus/usb-typec-Registeri... base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing reproduce: make htmldocs
All warnings (new ones prefixed by >>):
WARNING: convert(1) not found, for SVG to PDF conversion install ImageMagick (https://www.imagemagick.org) include/linux/generic-radix-tree.h:1: warning: no structured comments found kernel/rcu/tree_plugin.h:1: warning: no structured comments found kernel/rcu/tree_plugin.h:1: warning: no structured comments found include/linux/firmware/intel/stratix10-svc-client.h:1: warning: no structured comments found include/linux/gpio/driver.h:371: warning: Function parameter or member 'init_valid_mask' not described in 'gpio_chip' include/linux/i2c.h:343: warning: Function parameter or member 'init_irq' not described in 'i2c_client' include/linux/iio/hw-consumer.h:1: warning: no structured comments found include/linux/input/sparse-keymap.h:46: warning: Function parameter or member 'sw' not described in 'key_entry' include/linux/regulator/machine.h:199: warning: Function parameter or member 'max_uV_step' not described in 'regulation_constraints' include/linux/regulator/driver.h:228: warning: Function parameter or member 'resume' not described in 'regulator_ops' drivers/slimbus/stream.c:1: warning: no structured comments found include/linux/spi/spi.h:188: warning: Function parameter or member 'driver_override' not described in 'spi_device' drivers/target/target_core_device.c:1: warning: no structured comments found
drivers/usb/typec/mux.c:106: warning: Function parameter or member 'parent' not described in 'typec_switch_register' drivers/usb/typec/mux.c:106: warning: Function parameter or member 'desc' not described in 'typec_switch_register'
drivers/usb/typec/mux.c:106: warning: Excess function parameter 'sw' description in 'typec_switch_register'
drivers/usb/typec/mux.c:281: warning: Function parameter or member 'parent' not described in 'typec_mux_register' drivers/usb/typec/mux.c:281: warning: Function parameter or member 'desc' not described in 'typec_mux_register'
drivers/usb/typec/mux.c:281: warning: Excess function parameter 'mux' description in 'typec_mux_register' drivers/usb/typec/bus.c:1: warning: no structured comments found drivers/usb/typec/class.c:1: warning: no structured comments found include/linux/w1.h:281: warning: Function parameter or member 'of_match_table' not described in 'w1_family' fs/direct-io.c:257: warning: Excess function parameter 'offset' description in 'dio_complete' fs/file_table.c:1: warning: no structured comments found fs/libfs.c:477: warning: Excess function parameter 'available' description in 'simple_write_end' fs/posix_acl.c:646: warning: Function parameter or member 'inode' not described in 'posix_acl_update_mode' fs/posix_acl.c:646: warning: Function parameter or member 'mode_p' not described in 'posix_acl_update_mode' fs/posix_acl.c:646: warning: Function parameter or member 'acl' not described in 'posix_acl_update_mode' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:294: warning: Excess function parameter 'mm' description in 'amdgpu_mn_invalidate_range_start_hsa' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:294: warning: Excess function parameter 'start' description in 'amdgpu_mn_invalidate_range_start_hsa' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:294: warning: Excess function parameter 'end' description in 'amdgpu_mn_invalidate_range_start_hsa' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:343: warning: Excess function parameter 'mm' description in 'amdgpu_mn_invalidate_range_end' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:343: warning: Excess function parameter 'start' description in 'amdgpu_mn_invalidate_range_end' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:343: warning: Excess function parameter 'end' description in 'amdgpu_mn_invalidate_range_end' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:183: warning: Function parameter or member 'blockable' not described in 'amdgpu_mn_read_lock' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:295: warning: Function parameter or member 'range' not described in 'amdgpu_mn_invalidate_range_start_hsa' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:295: warning: Excess function parameter 'mm' description in 'amdgpu_mn_invalidate_range_start_hsa' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:295: warning: Excess function parameter 'start' description in 'amdgpu_mn_invalidate_range_start_hsa' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:295: warning: Excess function parameter 'end' description in 'amdgpu_mn_invalidate_range_start_hsa' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:344: warning: Function parameter or member 'range' not described in 'amdgpu_mn_invalidate_range_end' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:344: warning: Excess function parameter 'mm' description in 'amdgpu_mn_invalidate_range_end' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:344: warning: Excess function parameter 'start' description in 'amdgpu_mn_invalidate_range_end' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:344: warning: Excess function parameter 'end' description in 'amdgpu_mn_invalidate_range_end' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:374: warning: cannot understand function prototype: 'struct amdgpu_vm_pt_cursor ' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:375: warning: cannot understand function prototype: 'struct amdgpu_vm_pt_cursor ' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:547: warning: Function parameter or member 'adev' not described in 'for_each_amdgpu_vm_pt_leaf' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:547: warning: Function parameter or member 'vm' not described in 'for_each_amdgpu_vm_pt_leaf' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:547: warning: Function parameter or member 'start' not described in 'for_each_amdgpu_vm_pt_leaf' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:547: warning: Function parameter or member 'end' not described in 'for_each_amdgpu_vm_pt_leaf' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:547: warning: Function parameter or member 'cursor' not described in 'for_each_amdgpu_vm_pt_leaf' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:595: warning: Function parameter or member 'adev' not described in 'for_each_amdgpu_vm_pt_dfs_safe' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:595: warning: Function parameter or member 'vm' not described in 'for_each_amdgpu_vm_pt_dfs_safe' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:595: warning: Function parameter or member 'cursor' not described in 'for_each_amdgpu_vm_pt_dfs_safe' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:595: warning: Function parameter or member 'entry' not described in 'for_each_amdgpu_vm_pt_dfs_safe' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:868: warning: Function parameter or member 'level' not described in 'amdgpu_vm_bo_param' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1348: warning: Function parameter or member 'params' not described in 'amdgpu_vm_update_func' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1348: warning: Function parameter or member 'bo' not described in 'amdgpu_vm_update_func' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1348: warning: Function parameter or member 'pe' not described in 'amdgpu_vm_update_func' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1348: warning: Function parameter or member 'addr' not described in 'amdgpu_vm_update_func' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1348: warning: Function parameter or member 'count' not described in 'amdgpu_vm_update_func' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1348: warning: Function parameter or member 'incr' not described in 'amdgpu_vm_update_func' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1348: warning: Function parameter or member 'flags' not described in 'amdgpu_vm_update_func' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1516: warning: Function parameter or member 'params' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1516: warning: Function parameter or member 'bo' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1516: warning: Function parameter or member 'level' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1516: warning: Function parameter or member 'pe' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1516: warning: Function parameter or member 'addr' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1516: warning: Function parameter or member 'count' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1516: warning: Function parameter or member 'incr' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1516: warning: Function parameter or member 'flags' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:3107: warning: Function parameter or member 'pasid' not described in 'amdgpu_vm_make_compute' drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c:375: warning: Excess function parameter 'entry' description in 'amdgpu_irq_dispatch' drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c:376: warning: Function parameter or member 'ih' not described in 'amdgpu_irq_dispatch' drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c:376: warning: Excess function parameter 'entry' description in 'amdgpu_irq_dispatch' drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c:1: warning: no structured comments found drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:128: warning: Incorrect use of kernel-doc format: Documentation Makefile include scripts source @atomic_obj drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:203: warning: Function parameter or member 'atomic_obj' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:203: warning: Function parameter or member 'atomic_obj_lock' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:203: warning: Function parameter or member 'backlight_link' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:203: warning: Function parameter or member 'backlight_caps' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:203: warning: Function parameter or member 'freesync_module' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:203: warning: Function parameter or member 'fw_dmcu' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:203: warning: Function parameter or member 'dmcu_fw_version' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c:1: warning: no structured comments found include/drm/drm_drv.h:715: warning: Function parameter or member 'gem_prime_pin' not described in 'drm_driver' include/drm/drm_drv.h:715: warning: Function parameter or member 'gem_prime_unpin' not described in 'drm_driver' include/drm/drm_drv.h:715: warning: Function parameter or member 'gem_prime_res_obj' not described in 'drm_driver' include/drm/drm_drv.h:715: warning: Function parameter or member 'gem_prime_get_sg_table' not described in 'drm_driver' include/drm/drm_drv.h:715: warning: Function parameter or member 'gem_prime_import_sg_table' not described in 'drm_driver' include/drm/drm_drv.h:715: warning: Function parameter or member 'gem_prime_vmap' not described in 'drm_driver' include/drm/drm_drv.h:715: warning: Function parameter or member 'gem_prime_vunmap' not described in 'drm_driver' include/drm/drm_drv.h:715: warning: Function parameter or member 'gem_prime_mmap' not described in 'drm_driver' include/drm/drm_atomic_state_helper.h:1: warning: no structured comments found drivers/gpu/drm/scheduler/sched_main.c:376: warning: Excess function parameter 'bad' description in 'drm_sched_stop' drivers/gpu/drm/scheduler/sched_main.c:377: warning: Excess function parameter 'bad' description in 'drm_sched_stop' drivers/gpu/drm/scheduler/sched_main.c:420: warning: Function parameter or member 'full_recovery' not described in 'drm_sched_start' drivers/gpu/drm/i915/i915_vma.h:50: warning: cannot understand function prototype: 'struct i915_vma ' drivers/gpu/drm/i915/i915_vma.h:1: warning: no structured comments found drivers/gpu/drm/i915/intel_guc_fwif.h:536: warning: cannot understand function prototype: 'struct guc_log_buffer_state ' drivers/gpu/drm/i915/i915_trace.h:1: warning: no structured comments found drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:126: warning: Function parameter or member 'hw_id' not described in 'komeda_component' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:126: warning: Function parameter or member 'max_active_outputs' not described in 'komeda_component' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:126: warning: Function parameter or member 'supported_outputs' not described in 'komeda_component' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:142: warning: Function parameter or member 'output_port' not described in 'komeda_component_output' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:196: warning: Function parameter or member 'component' not described in 'komeda_component_state' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:196: warning: Function parameter or member 'crtc' not described in 'komeda_component_state' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:196: warning: Function parameter or member 'plane' not described in 'komeda_component_state' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:196: warning: Function parameter or member 'wb_conn' not described in 'komeda_component_state' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:196: warning: Function parameter or member 'changed_active_inputs' not described in 'komeda_component_state' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:196: warning: Function parameter or member 'affected_inputs' not described in 'komeda_component_state' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:300: warning: Function parameter or member 'n_layers' not described in 'komeda_pipeline' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:300: warning: Function parameter or member 'layers' not described in 'komeda_pipeline' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:300: warning: Function parameter or member 'n_scalers' not described in 'komeda_pipeline' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:300: warning: Function parameter or member 'scalers' not described in 'komeda_pipeline' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:300: warning: Function parameter or member 'compiz' not described in 'komeda_pipeline' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:300: warning: Function parameter or member 'wb_layer' not described in 'komeda_pipeline' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:300: warning: Function parameter or member 'improc' not described in 'komeda_pipeline' drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h:300: warning: Function parameter or member 'ctrlr' not described in 'komeda_pipeline'
vim +106 drivers/usb/typec/mux.c
e900bf53 Heikki Krogerus 2019-04-01 93 bdecb33a Heikki Krogerus 2018-03-20 94 /** bdecb33a Heikki Krogerus 2018-03-20 95 * typec_switch_register - Register USB Type-C orientation switch bdecb33a Heikki Krogerus 2018-03-20 96 * @sw: USB Type-C orientation switch bdecb33a Heikki Krogerus 2018-03-20 97 * bdecb33a Heikki Krogerus 2018-03-20 98 * This function registers a switch that can be used for routing the correct bdecb33a Heikki Krogerus 2018-03-20 99 * data pairs depending on the cable plug orientation from the USB Type-C bdecb33a Heikki Krogerus 2018-03-20 100 * connector to the USB controllers. USB Type-C plugs can be inserted bdecb33a Heikki Krogerus 2018-03-20 101 * right-side-up or upside-down. bdecb33a Heikki Krogerus 2018-03-20 102 */ e900bf53 Heikki Krogerus 2019-04-01 103 struct typec_switch * e900bf53 Heikki Krogerus 2019-04-01 104 typec_switch_register(struct device *parent, e900bf53 Heikki Krogerus 2019-04-01 105 const struct typec_switch_desc *desc) bdecb33a Heikki Krogerus 2018-03-20 @106 { e900bf53 Heikki Krogerus 2019-04-01 107 struct typec_switch *sw; e900bf53 Heikki Krogerus 2019-04-01 108 int ret; e900bf53 Heikki Krogerus 2019-04-01 109 e900bf53 Heikki Krogerus 2019-04-01 110 if (!desc || !desc->set) e900bf53 Heikki Krogerus 2019-04-01 111 return ERR_PTR(-EINVAL); e900bf53 Heikki Krogerus 2019-04-01 112 e900bf53 Heikki Krogerus 2019-04-01 113 sw = kzalloc(sizeof(*sw), GFP_KERNEL); e900bf53 Heikki Krogerus 2019-04-01 114 if (!sw) e900bf53 Heikki Krogerus 2019-04-01 115 return ERR_PTR(-ENOMEM); e900bf53 Heikki Krogerus 2019-04-01 116 e900bf53 Heikki Krogerus 2019-04-01 117 sw->set = desc->set; e900bf53 Heikki Krogerus 2019-04-01 118 e900bf53 Heikki Krogerus 2019-04-01 119 device_initialize(&sw->dev); e900bf53 Heikki Krogerus 2019-04-01 120 sw->dev.parent = parent; e900bf53 Heikki Krogerus 2019-04-01 121 sw->dev.class = &typec_mux_class; e900bf53 Heikki Krogerus 2019-04-01 122 sw->dev.type = &typec_switch_dev_type; e900bf53 Heikki Krogerus 2019-04-01 123 sw->dev.driver_data = desc->drvdata; e900bf53 Heikki Krogerus 2019-04-01 124 sw->dev.fwnode = desc->fwnode; e900bf53 Heikki Krogerus 2019-04-01 125 dev_set_name(&sw->dev, "%s-switch", dev_name(parent)); e900bf53 Heikki Krogerus 2019-04-01 126 e900bf53 Heikki Krogerus 2019-04-01 127 ret = device_add(&sw->dev); e900bf53 Heikki Krogerus 2019-04-01 128 if (ret) { e900bf53 Heikki Krogerus 2019-04-01 129 dev_err(parent, "failed to register switch (%d)\n", ret); e900bf53 Heikki Krogerus 2019-04-01 130 put_device(&sw->dev); e900bf53 Heikki Krogerus 2019-04-01 131 return ERR_PTR(ret); e900bf53 Heikki Krogerus 2019-04-01 132 } bdecb33a Heikki Krogerus 2018-03-20 133 e900bf53 Heikki Krogerus 2019-04-01 134 return sw; bdecb33a Heikki Krogerus 2018-03-20 135 } bdecb33a Heikki Krogerus 2018-03-20 136 EXPORT_SYMBOL_GPL(typec_switch_register); bdecb33a Heikki Krogerus 2018-03-20 137 bdecb33a Heikki Krogerus 2018-03-20 138 /** bdecb33a Heikki Krogerus 2018-03-20 139 * typec_switch_unregister - Unregister USB Type-C orientation switch bdecb33a Heikki Krogerus 2018-03-20 140 * @sw: USB Type-C orientation switch bdecb33a Heikki Krogerus 2018-03-20 141 * bdecb33a Heikki Krogerus 2018-03-20 142 * Unregister switch that was registered with typec_switch_register(). bdecb33a Heikki Krogerus 2018-03-20 143 */ bdecb33a Heikki Krogerus 2018-03-20 144 void typec_switch_unregister(struct typec_switch *sw) bdecb33a Heikki Krogerus 2018-03-20 145 { e900bf53 Heikki Krogerus 2019-04-01 146 if (!IS_ERR_OR_NULL(sw)) e900bf53 Heikki Krogerus 2019-04-01 147 device_unregister(&sw->dev); bdecb33a Heikki Krogerus 2018-03-20 148 } bdecb33a Heikki Krogerus 2018-03-20 149 EXPORT_SYMBOL_GPL(typec_switch_unregister); bdecb33a Heikki Krogerus 2018-03-20 150 e900bf53 Heikki Krogerus 2019-04-01 151 void *typec_switch_get_drvdata(struct typec_switch *sw) e900bf53 Heikki Krogerus 2019-04-01 152 { e900bf53 Heikki Krogerus 2019-04-01 153 return dev_get_drvdata(&sw->dev); e900bf53 Heikki Krogerus 2019-04-01 154 } e900bf53 Heikki Krogerus 2019-04-01 155 EXPORT_SYMBOL_GPL(typec_switch_get_drvdata); e900bf53 Heikki Krogerus 2019-04-01 156 bdecb33a Heikki Krogerus 2018-03-20 157 /* ------------------------------------------------------------------------- */ bdecb33a Heikki Krogerus 2018-03-20 158 bdecb33a Heikki Krogerus 2018-03-20 159 static void *typec_mux_match(struct device_connection *con, int ep, void *data) bdecb33a Heikki Krogerus 2018-03-20 160 { 96a6d031 Heikki Krogerus 2019-02-13 161 const struct typec_altmode_desc *desc = data; e900bf53 Heikki Krogerus 2019-04-01 162 struct device *dev; 96a6d031 Heikki Krogerus 2019-02-13 163 bool match; e900bf53 Heikki Krogerus 2019-04-01 164 int nval; 96a6d031 Heikki Krogerus 2019-02-13 165 u16 *val; 96a6d031 Heikki Krogerus 2019-02-13 166 int i; bdecb33a Heikki Krogerus 2018-03-20 167 96a6d031 Heikki Krogerus 2019-02-13 168 if (!con->fwnode) { e900bf53 Heikki Krogerus 2019-04-01 169 dev = class_find_device(&typec_mux_class, NULL, e900bf53 Heikki Krogerus 2019-04-01 170 con->endpoint[ep], name_match); e900bf53 Heikki Krogerus 2019-04-01 171 e900bf53 Heikki Krogerus 2019-04-01 172 return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); 96a6d031 Heikki Krogerus 2019-02-13 173 } bdecb33a Heikki Krogerus 2018-03-20 174 bdecb33a Heikki Krogerus 2018-03-20 175 /* 96a6d031 Heikki Krogerus 2019-02-13 176 * Check has the identifier already been "consumed". If it 96a6d031 Heikki Krogerus 2019-02-13 177 * has, no need to do any extra connection identification. bdecb33a Heikki Krogerus 2018-03-20 178 */ 96a6d031 Heikki Krogerus 2019-02-13 179 match = !con->id; 96a6d031 Heikki Krogerus 2019-02-13 180 if (match) 96a6d031 Heikki Krogerus 2019-02-13 181 goto find_mux; 96a6d031 Heikki Krogerus 2019-02-13 182 96a6d031 Heikki Krogerus 2019-02-13 183 /* Accessory Mode muxes */ 96a6d031 Heikki Krogerus 2019-02-13 184 if (!desc) { 96a6d031 Heikki Krogerus 2019-02-13 185 match = fwnode_property_present(con->fwnode, "accessory"); 96a6d031 Heikki Krogerus 2019-02-13 186 if (match) 96a6d031 Heikki Krogerus 2019-02-13 187 goto find_mux; 96a6d031 Heikki Krogerus 2019-02-13 188 return NULL; 96a6d031 Heikki Krogerus 2019-02-13 189 } 96a6d031 Heikki Krogerus 2019-02-13 190 96a6d031 Heikki Krogerus 2019-02-13 191 /* Alternate Mode muxes */ 96a6d031 Heikki Krogerus 2019-02-13 192 nval = fwnode_property_read_u16_array(con->fwnode, "svid", NULL, 0); 96a6d031 Heikki Krogerus 2019-02-13 193 if (nval <= 0) 96a6d031 Heikki Krogerus 2019-02-13 194 return NULL; 96a6d031 Heikki Krogerus 2019-02-13 195 96a6d031 Heikki Krogerus 2019-02-13 196 val = kcalloc(nval, sizeof(*val), GFP_KERNEL); 96a6d031 Heikki Krogerus 2019-02-13 197 if (!val) 96a6d031 Heikki Krogerus 2019-02-13 198 return ERR_PTR(-ENOMEM); 96a6d031 Heikki Krogerus 2019-02-13 199 96a6d031 Heikki Krogerus 2019-02-13 200 nval = fwnode_property_read_u16_array(con->fwnode, "svid", val, nval); 96a6d031 Heikki Krogerus 2019-02-13 201 if (nval < 0) { 96a6d031 Heikki Krogerus 2019-02-13 202 kfree(val); 96a6d031 Heikki Krogerus 2019-02-13 203 return ERR_PTR(nval); 96a6d031 Heikki Krogerus 2019-02-13 204 } 96a6d031 Heikki Krogerus 2019-02-13 205 96a6d031 Heikki Krogerus 2019-02-13 206 for (i = 0; i < nval; i++) { 96a6d031 Heikki Krogerus 2019-02-13 207 match = val[i] == desc->svid; 96a6d031 Heikki Krogerus 2019-02-13 208 if (match) { 96a6d031 Heikki Krogerus 2019-02-13 209 kfree(val); 96a6d031 Heikki Krogerus 2019-02-13 210 goto find_mux; 96a6d031 Heikki Krogerus 2019-02-13 211 } 96a6d031 Heikki Krogerus 2019-02-13 212 } 96a6d031 Heikki Krogerus 2019-02-13 213 kfree(val); 96a6d031 Heikki Krogerus 2019-02-13 214 return NULL; 96a6d031 Heikki Krogerus 2019-02-13 215 96a6d031 Heikki Krogerus 2019-02-13 216 find_mux: e900bf53 Heikki Krogerus 2019-04-01 217 dev = class_find_device(&typec_mux_class, NULL, con->fwnode, e900bf53 Heikki Krogerus 2019-04-01 218 fwnode_match); 96a6d031 Heikki Krogerus 2019-02-13 219 e900bf53 Heikki Krogerus 2019-04-01 220 return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); bdecb33a Heikki Krogerus 2018-03-20 221 } bdecb33a Heikki Krogerus 2018-03-20 222 bdecb33a Heikki Krogerus 2018-03-20 223 /** bdecb33a Heikki Krogerus 2018-03-20 224 * typec_mux_get - Find USB Type-C Multiplexer bdecb33a Heikki Krogerus 2018-03-20 225 * @dev: The caller device 540bfab7 Heikki Krogerus 2019-02-13 226 * @desc: Alt Mode description bdecb33a Heikki Krogerus 2018-03-20 227 * bdecb33a Heikki Krogerus 2018-03-20 228 * Finds a mux linked to the caller. This function is primarily meant for the bdecb33a Heikki Krogerus 2018-03-20 229 * Type-C drivers. Returns a reference to the mux on success, NULL if no bdecb33a Heikki Krogerus 2018-03-20 230 * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection bdecb33a Heikki Krogerus 2018-03-20 231 * was found but the mux has not been enumerated yet. bdecb33a Heikki Krogerus 2018-03-20 232 */ 540bfab7 Heikki Krogerus 2019-02-13 233 struct typec_mux *typec_mux_get(struct device *dev, 540bfab7 Heikki Krogerus 2019-02-13 234 const struct typec_altmode_desc *desc) bdecb33a Heikki Krogerus 2018-03-20 235 { bdecb33a Heikki Krogerus 2018-03-20 236 struct typec_mux *mux; bdecb33a Heikki Krogerus 2018-03-20 237 540bfab7 Heikki Krogerus 2019-02-13 238 mux = device_connection_find_match(dev, "mode-switch", (void *)desc, 540bfab7 Heikki Krogerus 2019-02-13 239 typec_mux_match); e900bf53 Heikki Krogerus 2019-04-01 240 if (!IS_ERR_OR_NULL(mux)) e900bf53 Heikki Krogerus 2019-04-01 241 get_device(&mux->dev); bdecb33a Heikki Krogerus 2018-03-20 242 bdecb33a Heikki Krogerus 2018-03-20 243 return mux; bdecb33a Heikki Krogerus 2018-03-20 244 } bdecb33a Heikki Krogerus 2018-03-20 245 EXPORT_SYMBOL_GPL(typec_mux_get); bdecb33a Heikki Krogerus 2018-03-20 246 bdecb33a Heikki Krogerus 2018-03-20 247 /** bdecb33a Heikki Krogerus 2018-03-20 248 * typec_mux_put - Release handle to a Multiplexer bdecb33a Heikki Krogerus 2018-03-20 249 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer bdecb33a Heikki Krogerus 2018-03-20 250 * bdecb33a Heikki Krogerus 2018-03-20 251 * Decrements reference count for @mux. bdecb33a Heikki Krogerus 2018-03-20 252 */ bdecb33a Heikki Krogerus 2018-03-20 253 void typec_mux_put(struct typec_mux *mux) bdecb33a Heikki Krogerus 2018-03-20 254 { e900bf53 Heikki Krogerus 2019-04-01 255 if (!IS_ERR_OR_NULL(mux)) e900bf53 Heikki Krogerus 2019-04-01 256 put_device(&mux->dev); 3e3b8196 Heikki Krogerus 2018-09-19 257 } bdecb33a Heikki Krogerus 2018-03-20 258 EXPORT_SYMBOL_GPL(typec_mux_put); bdecb33a Heikki Krogerus 2018-03-20 259 e900bf53 Heikki Krogerus 2019-04-01 260 static void typec_mux_release(struct device *dev) e900bf53 Heikki Krogerus 2019-04-01 261 { e900bf53 Heikki Krogerus 2019-04-01 262 kfree(to_typec_mux(dev)); e900bf53 Heikki Krogerus 2019-04-01 263 } e900bf53 Heikki Krogerus 2019-04-01 264 e900bf53 Heikki Krogerus 2019-04-01 265 static const struct device_type typec_mux_dev_type = { e900bf53 Heikki Krogerus 2019-04-01 266 .name = "mode_switch", e900bf53 Heikki Krogerus 2019-04-01 267 .release = typec_mux_release, e900bf53 Heikki Krogerus 2019-04-01 268 }; e900bf53 Heikki Krogerus 2019-04-01 269 bdecb33a Heikki Krogerus 2018-03-20 270 /** bdecb33a Heikki Krogerus 2018-03-20 271 * typec_mux_register - Register Multiplexer routing USB Type-C pins bdecb33a Heikki Krogerus 2018-03-20 272 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer bdecb33a Heikki Krogerus 2018-03-20 273 * bdecb33a Heikki Krogerus 2018-03-20 274 * USB Type-C connectors can be used for alternate modes of operation besides bdecb33a Heikki Krogerus 2018-03-20 275 * USB when Accessory/Alternate Modes are supported. With some of those modes, bdecb33a Heikki Krogerus 2018-03-20 276 * the pins on the connector need to be reconfigured. This function registers bdecb33a Heikki Krogerus 2018-03-20 277 * multiplexer switches routing the pins on the connector. bdecb33a Heikki Krogerus 2018-03-20 278 */ e900bf53 Heikki Krogerus 2019-04-01 279 struct typec_mux * e900bf53 Heikki Krogerus 2019-04-01 280 typec_mux_register(struct device *parent, const struct typec_mux_desc *desc) bdecb33a Heikki Krogerus 2018-03-20 @281 { e900bf53 Heikki Krogerus 2019-04-01 282 struct typec_mux *mux; e900bf53 Heikki Krogerus 2019-04-01 283 int ret; e900bf53 Heikki Krogerus 2019-04-01 284 e900bf53 Heikki Krogerus 2019-04-01 285 if (!desc || !desc->set) e900bf53 Heikki Krogerus 2019-04-01 286 return ERR_PTR(-EINVAL); bdecb33a Heikki Krogerus 2018-03-20 287 e900bf53 Heikki Krogerus 2019-04-01 288 mux = kzalloc(sizeof(*mux), GFP_KERNEL); e900bf53 Heikki Krogerus 2019-04-01 289 if (!mux) e900bf53 Heikki Krogerus 2019-04-01 290 return ERR_PTR(-ENOMEM); e900bf53 Heikki Krogerus 2019-04-01 291 e900bf53 Heikki Krogerus 2019-04-01 292 mux->set = desc->set; e900bf53 Heikki Krogerus 2019-04-01 293 e900bf53 Heikki Krogerus 2019-04-01 294 device_initialize(&mux->dev); e900bf53 Heikki Krogerus 2019-04-01 295 mux->dev.parent = parent; e900bf53 Heikki Krogerus 2019-04-01 296 mux->dev.class = &typec_mux_class; e900bf53 Heikki Krogerus 2019-04-01 297 mux->dev.type = &typec_mux_dev_type; e900bf53 Heikki Krogerus 2019-04-01 298 mux->dev.fwnode = desc->fwnode; e900bf53 Heikki Krogerus 2019-04-01 299 mux->dev.driver_data = desc->drvdata; e900bf53 Heikki Krogerus 2019-04-01 300 dev_set_name(&mux->dev, "%s-mux", dev_name(parent)); e900bf53 Heikki Krogerus 2019-04-01 301 e900bf53 Heikki Krogerus 2019-04-01 302 ret = device_add(&mux->dev); e900bf53 Heikki Krogerus 2019-04-01 303 if (ret) { e900bf53 Heikki Krogerus 2019-04-01 304 dev_err(parent, "failed to register mux (%d)\n", ret); e900bf53 Heikki Krogerus 2019-04-01 305 put_device(&mux->dev); e900bf53 Heikki Krogerus 2019-04-01 306 return ERR_PTR(ret); e900bf53 Heikki Krogerus 2019-04-01 307 } e900bf53 Heikki Krogerus 2019-04-01 308 e900bf53 Heikki Krogerus 2019-04-01 309 return mux; bdecb33a Heikki Krogerus 2018-03-20 310 } bdecb33a Heikki Krogerus 2018-03-20 311 EXPORT_SYMBOL_GPL(typec_mux_register); bdecb33a Heikki Krogerus 2018-03-20 312
:::::: The code at line 106 was first introduced by commit :::::: bdecb33af34f79cbfbb656661210f77c8b8b5b5f usb: typec: API for controlling USB Type-C Multiplexers
:::::: TO: Heikki Krogerus heikki.krogerus@linux.intel.com :::::: CC: Greg Kroah-Hartman gregkh@linuxfoundation.org
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
linux-stable-mirror@lists.linaro.org