From 974e2f6a0554685493cc44406bc7d8ba0a3b0e33 Mon Sep 17 00:00:00 2001 From: Jarkko Sonninen Date: Sat, 8 Jul 2023 17:56:50 +0300 Subject: USB: serial: xr: add TIOCGRS485 and TIOCSRS485 ioctls Exar devices like XR21B1411 can control an RS485 transceiver by automatically asserting the RTS#/RS485 pin before sending data and deasserting it when the last stop bit has been transmitted. The polarity of the RST#/RS485 signal is configurable and the hardware also supports half-duplex turn-around delay and address matching mode. Add support for enabling and disabling RS-485 mode and configuring the RST#/RS485 signal polarity using the TIOCGRS485 and TIOCSRS485 ioctls. Support for half-duplex turn-around delay and address matching mode are left unimplemented for now. User enables RS-485 mode by setting SER_RS485_ENABLED flag in struct serial_rs485 flags. User should also set either SER_RS485_RTS_ON_SEND or SER_RS485_RTS_AFTER_SEND to select the behaviour of the RTS#/RS485 pin. Setting SER_RS485_RTS_ON_SEND will drive RTS#/RS485 low during transmission. Signed-off-by: Jarkko Sonninen [ johan: let SER_RS485_RTS_ON_SEND determine SER_RS485_RTS_AFTER_SEND ] Signed-off-by: Johan Hovold --- drivers/usb/serial/xr_serial.c | 89 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index 4ec7c5892b84..1d9a12628f81 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -93,6 +93,7 @@ struct xr_txrx_clk_mask { #define XR_GPIO_MODE_SEL_DTR_DSR 0x2 #define XR_GPIO_MODE_SEL_RS485 0x3 #define XR_GPIO_MODE_SEL_RS485_ADDR 0x4 +#define XR_GPIO_MODE_RS485_TX_H 0x8 #define XR_GPIO_MODE_TX_TOGGLE 0x100 #define XR_GPIO_MODE_RX_TOGGLE 0x200 @@ -237,6 +238,7 @@ static const struct xr_type xr_types[] = { struct xr_data { const struct xr_type *type; u8 channel; /* zero-based index or interface number */ + struct serial_rs485 rs485; }; static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val) @@ -629,6 +631,7 @@ static void xr_set_flow_mode(struct tty_struct *tty, struct xr_data *data = usb_get_serial_port_data(port); const struct xr_type *type = data->type; u16 flow, gpio_mode; + bool rs485_enabled; int ret; ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); @@ -645,7 +648,17 @@ static void xr_set_flow_mode(struct tty_struct *tty, /* Set GPIO mode for controlling the pins manually by default. */ gpio_mode &= ~XR_GPIO_MODE_SEL_MASK; - if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { + rs485_enabled = !!(data->rs485.flags & SER_RS485_ENABLED); + if (rs485_enabled) { + dev_dbg(&port->dev, "Enabling RS-485\n"); + gpio_mode |= XR_GPIO_MODE_SEL_RS485; + if (data->rs485.flags & SER_RS485_RTS_ON_SEND) + gpio_mode &= ~XR_GPIO_MODE_RS485_TX_H; + else + gpio_mode |= XR_GPIO_MODE_RS485_TX_H; + } + + if (C_CRTSCTS(tty) && C_BAUD(tty) != B0 && !rs485_enabled) { dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS; flow = XR_UART_FLOW_MODE_HW; @@ -809,6 +822,79 @@ static void xr_cdc_set_line_coding(struct tty_struct *tty, kfree(lc); } +static void xr_sanitize_serial_rs485(struct serial_rs485 *rs485) +{ + if (!(rs485->flags & SER_RS485_ENABLED)) { + memset(rs485, 0, sizeof(*rs485)); + return; + } + + /* RTS always toggles after TX */ + if (rs485->flags & SER_RS485_RTS_ON_SEND) + rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; + else + rs485->flags |= SER_RS485_RTS_AFTER_SEND; + + /* Only the flags are implemented at the moment */ + rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND; + rs485->delay_rts_before_send = 0; + rs485->delay_rts_after_send = 0; + memset(rs485->padding, 0, sizeof(rs485->padding)); +} + +static int xr_get_rs485_config(struct tty_struct *tty, + struct serial_rs485 __user *argp) +{ + struct usb_serial_port *port = tty->driver_data; + struct xr_data *data = usb_get_serial_port_data(port); + + down_read(&tty->termios_rwsem); + if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) { + up_read(&tty->termios_rwsem); + return -EFAULT; + } + up_read(&tty->termios_rwsem); + + return 0; +} + +static int xr_set_rs485_config(struct tty_struct *tty, + struct serial_rs485 __user *argp) +{ + struct usb_serial_port *port = tty->driver_data; + struct xr_data *data = usb_get_serial_port_data(port); + struct serial_rs485 rs485; + + if (copy_from_user(&rs485, argp, sizeof(rs485))) + return -EFAULT; + xr_sanitize_serial_rs485(&rs485); + + down_write(&tty->termios_rwsem); + data->rs485 = rs485; + xr_set_flow_mode(tty, port, NULL); + up_write(&tty->termios_rwsem); + + if (copy_to_user(argp, &rs485, sizeof(rs485))) + return -EFAULT; + + return 0; +} + +static int xr_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + + switch (cmd) { + case TIOCGRS485: + return xr_get_rs485_config(tty, argp); + case TIOCSRS485: + return xr_set_rs485_config(tty, argp); + } + + return -ENOIOCTLCMD; +} + static void xr_set_termios(struct tty_struct *tty, struct usb_serial_port *port, const struct ktermios *old_termios) @@ -1010,6 +1096,7 @@ static struct usb_serial_driver xr_device = { .set_termios = xr_set_termios, .tiocmget = xr_tiocmget, .tiocmset = xr_tiocmset, + .ioctl = xr_ioctl, .dtr_rts = xr_dtr_rts }; -- cgit From 2319b9c87fe243327285f2fefd7374ffd75a65fc Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Fri, 16 Jun 2023 10:19:51 +0800 Subject: usb: cdns3: Put the cdns set active part outside the spin lock The device may be scheduled during the resume process, so this cannot appear in atomic operations. Since pm_runtime_set_active will resume suppliers, put set active outside the spin lock, which is only used to protect the struct cdns data structure, otherwise the kernel will report the following warning: BUG: sleeping function called from invalid context at drivers/base/power/runtime.c:1163 in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 651, name: sh preempt_count: 1, expected: 0 RCU nest depth: 0, expected: 0 CPU: 0 PID: 651 Comm: sh Tainted: G WC 6.1.20 #1 Hardware name: Freescale i.MX8QM MEK (DT) Call trace: dump_backtrace.part.0+0xe0/0xf0 show_stack+0x18/0x30 dump_stack_lvl+0x64/0x80 dump_stack+0x1c/0x38 __might_resched+0x1fc/0x240 __might_sleep+0x68/0xc0 __pm_runtime_resume+0x9c/0xe0 rpm_get_suppliers+0x68/0x1b0 __pm_runtime_set_status+0x298/0x560 cdns_resume+0xb0/0x1c0 cdns3_controller_resume.isra.0+0x1e0/0x250 cdns3_plat_resume+0x28/0x40 Signed-off-by: Xiaolei Wang Acked-by: Peter Chen Link: https://lore.kernel.org/r/20230616021952.1025854-1-xiaolei.wang@windriver.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-plat.c | 3 ++- drivers/usb/cdns3/cdnsp-pci.c | 3 ++- drivers/usb/cdns3/core.c | 15 +++++++++++---- drivers/usb/cdns3/core.h | 7 +++++-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 884e2301237f..1168dbeed2ce 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -255,9 +255,10 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg) cdns3_set_platform_suspend(cdns->dev, false, false); spin_lock_irqsave(&cdns->lock, flags); - cdns_resume(cdns, !PMSG_IS_AUTO(msg)); + cdns_resume(cdns); cdns->in_lpm = false; spin_unlock_irqrestore(&cdns->lock, flags); + cdns_set_active(cdns, !PMSG_IS_AUTO(msg)); if (cdns->wakeup_pending) { cdns->wakeup_pending = false; enable_irq(cdns->wakeup_irq); diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index 7b151f5af3cc..0725668ffea4 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -208,8 +208,9 @@ static int __maybe_unused cdnsp_pci_resume(struct device *dev) int ret; spin_lock_irqsave(&cdns->lock, flags); - ret = cdns_resume(cdns, 1); + ret = cdns_resume(cdns); spin_unlock_irqrestore(&cdns->lock, flags); + cdns_set_active(cdns, 1); return ret; } diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index dbcdf3b24b47..7b20d2d5c262 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -522,9 +522,8 @@ int cdns_suspend(struct cdns *cdns) } EXPORT_SYMBOL_GPL(cdns_suspend); -int cdns_resume(struct cdns *cdns, u8 set_active) +int cdns_resume(struct cdns *cdns) { - struct device *dev = cdns->dev; enum usb_role real_role; bool role_changed = false; int ret = 0; @@ -556,15 +555,23 @@ int cdns_resume(struct cdns *cdns, u8 set_active) if (cdns->roles[cdns->role]->resume) cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns)); + return 0; +} +EXPORT_SYMBOL_GPL(cdns_resume); + +void cdns_set_active(struct cdns *cdns, u8 set_active) +{ + struct device *dev = cdns->dev; + if (set_active) { pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); } - return 0; + return; } -EXPORT_SYMBOL_GPL(cdns_resume); +EXPORT_SYMBOL_GPL(cdns_set_active); #endif /* CONFIG_PM_SLEEP */ MODULE_AUTHOR("Peter Chen "); diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 2d332a788871..4a4dbc2c1561 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -125,10 +125,13 @@ int cdns_init(struct cdns *cdns); int cdns_remove(struct cdns *cdns); #ifdef CONFIG_PM_SLEEP -int cdns_resume(struct cdns *cdns, u8 set_active); +int cdns_resume(struct cdns *cdns); int cdns_suspend(struct cdns *cdns); +void cdns_set_active(struct cdns *cdns, u8 set_active); #else /* CONFIG_PM_SLEEP */ -static inline int cdns_resume(struct cdns *cdns, u8 set_active) +static inline int cdns_resume(struct cdns *cdns) +{ return 0; } +static inline int cdns_set_active(struct cdns *cdns, u8 set_active) { return 0; } static inline int cdns_suspend(struct cdns *cdns) { return 0; } -- cgit From 015fbddefcfbf8b44c89b2e9b0b3dd77631f1e51 Mon Sep 17 00:00:00 2001 From: Ivan Orlov Date: Thu, 22 Jun 2023 00:25:14 +0400 Subject: USB: make usb class a const structure Now that the driver core allows for struct class to be in read-only memory, remove the usb_class structure and create the usbmisc_class const class structure declared at build time which places it into read-only memory, instead of having it to be dynamically allocated at load time. Additionally, now we register usb class at startup and unregister it when shutting down, so we don't have to count uses of the class. Therefore we don't need the 'usb_class' structure anymore. Due to this fact, remove all static functions related to class initialization and deinitialization. We can't use them in 'usb.c' since they are static and we don't really need them anymore. Since we have to register the class in usb_init function in 'usb.c' and use it in 'file.c' as well, declare the usbmisc_class structure as 'export' in the 'usb.h' file. Debatable moment: the class registration and unregistration functions could be extracted to the 'file.c'. I think we don't want to do this since it would be one-line functions. They would make the code paths more confusing and add calling overhead. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ivan Orlov Link: https://lore.kernel.org/r/20230621202514.1223670-1-ivan.orlov0322@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/file.c | 68 +++++-------------------------------------------- drivers/usb/core/usb.c | 6 +++++ drivers/usb/core/usb.h | 1 + 3 files changed, 14 insertions(+), 61 deletions(-) diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index c4ed3310e069..a88ced93b5e7 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -29,7 +29,6 @@ #define MAX_USB_MINORS 256 static const struct file_operations *usb_minors[MAX_USB_MINORS]; static DECLARE_RWSEM(minor_rwsem); -static DEFINE_MUTEX(init_usb_class_mutex); static int usb_open(struct inode *inode, struct file *file) { @@ -57,11 +56,6 @@ static const struct file_operations usb_fops = { .llseek = noop_llseek, }; -static struct usb_class { - struct kref kref; - struct class *class; -} *usb_class; - static char *usb_devnode(const struct device *dev, umode_t *mode) { struct usb_class_driver *drv; @@ -72,50 +66,10 @@ static char *usb_devnode(const struct device *dev, umode_t *mode) return drv->devnode(dev, mode); } -static int init_usb_class(void) -{ - int result = 0; - - if (usb_class != NULL) { - kref_get(&usb_class->kref); - goto exit; - } - - usb_class = kmalloc(sizeof(*usb_class), GFP_KERNEL); - if (!usb_class) { - result = -ENOMEM; - goto exit; - } - - kref_init(&usb_class->kref); - usb_class->class = class_create("usbmisc"); - if (IS_ERR(usb_class->class)) { - result = PTR_ERR(usb_class->class); - printk(KERN_ERR "class_create failed for usb devices\n"); - kfree(usb_class); - usb_class = NULL; - goto exit; - } - usb_class->class->devnode = usb_devnode; - -exit: - return result; -} - -static void release_usb_class(struct kref *kref) -{ - /* Ok, we cheat as we know we only have one usb_class */ - class_destroy(usb_class->class); - kfree(usb_class); - usb_class = NULL; -} - -static void destroy_usb_class(void) -{ - mutex_lock(&init_usb_class_mutex); - kref_put(&usb_class->kref, release_usb_class); - mutex_unlock(&init_usb_class_mutex); -} +const struct class usbmisc_class = { + .name = "usbmisc", + .devnode = usb_devnode, +}; int usb_major_init(void) { @@ -156,7 +110,7 @@ void usb_major_cleanup(void) int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver) { - int retval; + int retval = 0; int minor_base = class_driver->minor_base; int minor; char name[20]; @@ -175,13 +129,6 @@ int usb_register_dev(struct usb_interface *intf, if (intf->minor >= 0) return -EADDRINUSE; - mutex_lock(&init_usb_class_mutex); - retval = init_usb_class(); - mutex_unlock(&init_usb_class_mutex); - - if (retval) - return retval; - dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base); down_write(&minor_rwsem); @@ -200,7 +147,7 @@ int usb_register_dev(struct usb_interface *intf, /* create a usb class device for this usb interface */ snprintf(name, sizeof(name), class_driver->name, minor - minor_base); - intf->usb_dev = device_create(usb_class->class, &intf->dev, + intf->usb_dev = device_create(&usbmisc_class, &intf->dev, MKDEV(USB_MAJOR, minor), class_driver, "%s", kbasename(name)); if (IS_ERR(intf->usb_dev)) { @@ -234,7 +181,7 @@ void usb_deregister_dev(struct usb_interface *intf, return; dev_dbg(&intf->dev, "removing %d minor\n", intf->minor); - device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); + device_destroy(&usbmisc_class, MKDEV(USB_MAJOR, intf->minor)); down_write(&minor_rwsem); usb_minors[intf->minor] = NULL; @@ -242,6 +189,5 @@ void usb_deregister_dev(struct usb_interface *intf, intf->usb_dev = NULL; intf->minor = -1; - destroy_usb_class(); } EXPORT_SYMBOL_GPL(usb_deregister_dev); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 901ec732321c..39bdf9589b06 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1101,6 +1101,9 @@ static int __init usb_init(void) retval = usb_major_init(); if (retval) goto major_init_failed; + retval = class_register(&usbmisc_class); + if (retval) + goto class_register_failed; retval = usb_register(&usbfs_driver); if (retval) goto driver_register_failed; @@ -1120,6 +1123,8 @@ hub_init_failed: usb_devio_init_failed: usb_deregister(&usbfs_driver); driver_register_failed: + class_unregister(&usbmisc_class); +class_register_failed: usb_major_cleanup(); major_init_failed: bus_unregister_notifier(&usb_bus_type, &usb_bus_nb); @@ -1147,6 +1152,7 @@ static void __exit usb_exit(void) usb_deregister(&usbfs_driver); usb_devio_cleanup(); usb_hub_cleanup(); + class_unregister(&usbmisc_class); bus_unregister_notifier(&usb_bus_type, &usb_bus_nb); bus_unregister(&usb_bus_type); usb_acpi_unregister(); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index ffe3f6818e9c..69ca59841083 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -141,6 +141,7 @@ static inline int usb_disable_usb2_hardware_lpm(struct usb_device *udev) #endif +extern const struct class usbmisc_class; extern const struct bus_type usb_bus_type; extern struct mutex usb_port_peer_mutex; extern struct device_type usb_device_type; -- cgit From 6744aa931a57eafc16ac656e2565591b7ed8fcd7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 26 Jun 2023 11:13:14 +0200 Subject: usb: typec: nb7vpq904m: Switch back to use struct i2c_driver::probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct i2c_driver::probe_new is about to go away. Switch the driver to use the probe callback with the same prototype. Signed-off-by: Uwe Kleine-König Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230626091314.557122-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/nb7vpq904m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c index 80e580d50129..9360b65e8b06 100644 --- a/drivers/usb/typec/mux/nb7vpq904m.c +++ b/drivers/usb/typec/mux/nb7vpq904m.c @@ -517,7 +517,7 @@ static struct i2c_driver nb7vpq904m_driver = { .name = "nb7vpq904m", .of_match_table = nb7vpq904m_of_table, }, - .probe_new = nb7vpq904m_probe, + .probe = nb7vpq904m_probe, .remove = nb7vpq904m_remove, .id_table = nb7vpq904m_table, }; -- cgit From 4939a04500f358393156ffe2bd58f67c19568795 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Fri, 23 Jun 2023 16:22:27 +0200 Subject: dt-bindings: usb: Add binding for Genesys Logic GL3523 hub Add the binding for the USB3.1 Genesys Logic GL3523 hub. Signed-off-by: Marco Felsch Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20230623142228.4069084-1-m.felsch@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/genesys,gl850g.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml index cc4cf92b70d1..383625c2ef00 100644 --- a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml +++ b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml @@ -17,6 +17,7 @@ properties: enum: - usb5e3,608 - usb5e3,610 + - usb5e3,620 reg: true -- cgit From d97b4b35adcecd4b747d3e1c262e10e4a093cefa Mon Sep 17 00:00:00 2001 From: Anand Moon Date: Fri, 23 Jun 2023 16:22:28 +0200 Subject: usb: misc: onboard_usb_hub: add Genesys Logic GL3523 hub support Genesys Logic GL3523 is a 4-port USB 3.1 hub that has a reset pin to toggle and a 5.0V core supply exported though an integrated LDO is available for powering it. Add the support for this hub, for controlling the reset pin and the core power supply. Signed-off-by: Anand Moon [m.felsch@pengutronix.de: include review feedback & port to 6.4] Signed-off-by: Marco Felsch Link: https://lore.kernel.org/r/20230623142228.4069084-2-m.felsch@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/onboard_usb_hub.c | 1 + drivers/usb/misc/onboard_usb_hub.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index 83f14ca1d38e..20b29db90758 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -409,6 +409,7 @@ static void onboard_hub_usbdev_disconnect(struct usb_device *udev) static const struct usb_device_id onboard_hub_id_table[] = { { USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 */ + { USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 */ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 */ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */ diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h index aca5f50eb0da..0134af1f9e42 100644 --- a/drivers/usb/misc/onboard_usb_hub.h +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -41,6 +41,7 @@ static const struct of_device_id onboard_hub_match[] = { { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, + { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, { .compatible = "usbbda,411", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,414", .data = &realtek_rts5411_data, }, -- cgit From e032368e8cb15ab1f11b92f078caa9bae995b8fe Mon Sep 17 00:00:00 2001 From: Madhu M Date: Tue, 4 Jul 2023 13:35:09 +0530 Subject: usb: typec: intel_pmc_mux: Add new ACPI ID for Lunar Lake IOM device Intel Lunar Lake IOM has a different IOM port status offset and size than Intel MTL. Intel Lunar Lake is the first platform to extend IOM port status from 32bit to 64bit by adding DDI port number into IOM port status. Added IOM_PORT_STATUS_REGS macro for using platform specific IOM port status offset and size. Reviewed-by: Andy Shevchenko Acked-by: Heikki Krogerus Signed-off-by: Madhu M Link: https://lore.kernel.org/r/20230704080509.14251-1-madhu.m@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/intel_pmc_mux.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 5e8edf3881c0..61a88f68b458 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -117,6 +117,16 @@ enum { IOM_PORT_STATUS_DHPD_HPD_STATUS_SHIFT) & \ IOM_PORT_STATUS_DHPD_HPD_STATUS_ASSERT) +/* IOM port status register */ +#define IOM_PORT_STATUS_REGS(_offset_, _size_) ((_offset_) | (_size_)) +#define IOM_PORT_STATUS_REGS_SZ_MASK BIT(0) +#define IOM_PORT_STATUS_REGS_SZ_4 0 +#define IOM_PORT_STATUS_REGS_SZ_8 1 +#define IOM_PORT_STATUS_REGS_OFFSET(_d_) \ + ((_d_) & ~IOM_PORT_STATUS_REGS_SZ_MASK) +#define IOM_PORT_STATUS_REGS_SIZE(_d_) \ + (4 << ((_d_) & IOM_PORT_STATUS_REGS_SZ_MASK)) + struct pmc_usb; struct pmc_usb_port { @@ -145,6 +155,7 @@ struct pmc_usb { struct acpi_device *iom_adev; void __iomem *iom_base; u32 iom_port_status_offset; + u8 iom_port_status_size; struct dentry *dentry; }; @@ -160,7 +171,7 @@ static void update_port_status(struct pmc_usb_port *port) port->iom_status = readl(port->pmc->iom_base + port->pmc->iom_port_status_offset + - port_num * sizeof(u32)); + port_num * port->pmc->iom_port_status_size); } static int sbu_orientation(struct pmc_usb_port *port) @@ -589,13 +600,16 @@ err_unregister_switch: /* IOM ACPI IDs and IOM_PORT_STATUS_OFFSET */ static const struct acpi_device_id iom_acpi_ids[] = { /* TigerLake */ - { "INTC1072", 0x560, }, + { "INTC1072", IOM_PORT_STATUS_REGS(0x560, IOM_PORT_STATUS_REGS_SZ_4) }, /* AlderLake */ - { "INTC1079", 0x160, }, + { "INTC1079", IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) }, /* Meteor Lake */ - { "INTC107A", 0x160, }, + { "INTC107A", IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) }, + + /* Lunar Lake */ + { "INTC10EA", IOM_PORT_STATUS_REGS(0x150, IOM_PORT_STATUS_REGS_SZ_8) }, {} }; @@ -615,7 +629,8 @@ static int pmc_usb_probe_iom(struct pmc_usb *pmc) if (!adev) return -ENODEV; - pmc->iom_port_status_offset = (u32)dev_id->driver_data; + pmc->iom_port_status_offset = IOM_PORT_STATUS_REGS_OFFSET(dev_id->driver_data); + pmc->iom_port_status_size = IOM_PORT_STATUS_REGS_SIZE(dev_id->driver_data); INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_memory_resources(adev, &resource_list); -- cgit From ce9daa2efc0872a9a68ea51dc8000df05893ef2e Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Wed, 28 Jun 2023 16:15:11 +0800 Subject: usb: gadget: fsl_qe_udc: validate endpoint index for ch9 udc We should verify the bound of the array to assure that host may not manipulate the index to point past endpoint array. Signed-off-by: Ma Ke Acked-by: Li Yang Link: https://lore.kernel.org/r/20230628081511.186850-1-make_ruc2021@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_qe_udc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index 9c5dc1c1a68e..4aae86b47edf 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -1959,6 +1959,8 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { /* Get endpoint status */ int pipe = index & USB_ENDPOINT_NUMBER_MASK; + if (pipe >= USB_MAX_ENDPOINTS) + goto stall; struct qe_ep *target_ep = &udc->eps[pipe]; u16 usep; -- cgit From 3eab3304ceea5857f5fff08d6e379c3177800ace Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 10 Jul 2023 12:46:45 +0300 Subject: usb: musb: Use read_poll_timeout() Use read_poll_timeout() instead of open coding it. In the same time, fix the typo in the error message. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230710094645.42111-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/tusb6010.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index cbc707fe570f..461587629bf2 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1029,7 +1030,7 @@ static int tusb_musb_start(struct musb *musb) void __iomem *tbase = musb->ctrl_base; unsigned long flags; u32 reg; - int i; + int ret; /* * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and @@ -1037,17 +1038,13 @@ static int tusb_musb_start(struct musb *musb) * provide then PGOOD signal to TUSB6010 which will release it from reset. */ gpiod_set_value(glue->enable, 1); - msleep(1); /* Wait for 100ms until TUSB6010 pulls INT pin down */ - i = 100; - while (i && gpiod_get_value(glue->intpin)) { - msleep(1); - i--; - } - if (!i) { - pr_err("tusb: Powerup respones failed\n"); - return -ENODEV; + ret = read_poll_timeout(gpiod_get_value, reg, !reg, 5000, 100000, true, + glue->intpin); + if (ret) { + pr_err("tusb: Powerup response failed\n"); + return ret; } spin_lock_irqsave(&musb->lock, flags); -- cgit From 2f4092298d3342cd7b1510aee745e32cda852f1b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Jul 2023 23:02:35 +0300 Subject: usb: typec: qcom: properly detect Audio Accessory mode peripherals Detect and report if the Audio Accessory device has been attached to the corresponding USB-C port. Acked-by: Bryan O'Donoghue Signed-off-by: Dmitry Baryshkov Reviewed-by: Guenter Roeck Reviewed-by: Caleb Connolly Link: https://lore.kernel.org/r/20230709200235.265674-1-dmitry.baryshkov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c index 94285f64b67d..56df04af2d2b 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -214,6 +214,11 @@ int qcom_pmic_typec_port_get_cc(struct pmic_typec_port *pmic_typec_port, if (ret) goto done; switch (val & DETECTED_SRC_TYPE_MASK) { + case AUDIO_ACCESS_RA_RA: + val = TYPEC_CC_RA; + *cc1 = TYPEC_CC_RA; + *cc2 = TYPEC_CC_RA; + break; case SRC_RD_OPEN: val = TYPEC_CC_RD; break; -- cgit From 48cb8ff3e250301a5a48925281a7096969ab3a48 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Jul 2023 09:36:53 +0200 Subject: usb: cdns3: starfive: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Roger Quadros Link: https://lore.kernel.org/r/20230707073653.3396988-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-starfive.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c index fc1f003b145d..a7265b86e427 100644 --- a/drivers/usb/cdns3/cdns3-starfive.c +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -166,7 +166,7 @@ static int cdns_starfive_remove_core(struct device *dev, void *c) return 0; } -static int cdns_starfive_remove(struct platform_device *pdev) +static void cdns_starfive_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cdns_starfive *data = dev_get_drvdata(dev); @@ -178,8 +178,6 @@ static int cdns_starfive_remove(struct platform_device *pdev) pm_runtime_put_noidle(dev); cdns_clk_rst_deinit(data); platform_set_drvdata(pdev, NULL); - - return 0; } #ifdef CONFIG_PM @@ -232,7 +230,7 @@ MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); static struct platform_driver cdns_starfive_driver = { .probe = cdns_starfive_probe, - .remove = cdns_starfive_remove, + .remove_new = cdns_starfive_remove, .driver = { .name = "cdns3-starfive", .of_match_table = cdns_starfive_of_match, -- cgit From 484468fb0f7dbac88f050009a5145ed1ee744a7e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 18 Jul 2023 08:30:23 -0600 Subject: usb: Explicitly include correct DT includes The DT of_device.h and of_platform.h date back to the separate of_platform_bus_type before it as merged into the regular platform bus. As part of that merge prepping Arm DT support 13 years ago, they "temporarily" include each other. They also include platform_device.h and of.h. As a result, there's a pretty much random mix of those include files used throughout the tree. In order to detangle these headers and replace the implicit includes with struct declarations, users need to explicitly include the correct includes. Acked-by: Herve Codina Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20230718143027.1064731-1-robh@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 1 + drivers/usb/cdns3/cdns3-plat.c | 1 + drivers/usb/cdns3/cdns3-ti.c | 1 + drivers/usb/cdns3/core.c | 1 + drivers/usb/chipidea/ci_hdrc_imx.c | 1 + drivers/usb/chipidea/ci_hdrc_tegra.c | 3 ++- drivers/usb/chipidea/usbmisc_imx.c | 3 ++- drivers/usb/common/common.c | 1 + drivers/usb/core/message.c | 1 + drivers/usb/core/of.c | 1 - drivers/usb/core/usb.c | 1 + drivers/usb/dwc2/gadget.c | 1 - drivers/usb/dwc2/platform.c | 2 +- drivers/usb/dwc3/dwc3-imx8mp.c | 1 + drivers/usb/dwc3/dwc3-keystone.c | 1 + drivers/usb/gadget/udc/fsl_udc_core.c | 1 - drivers/usb/gadget/udc/gr_udc.c | 5 ++--- drivers/usb/gadget/udc/max3420_udc.c | 4 +--- drivers/usb/gadget/udc/pxa27x_udc.c | 2 +- drivers/usb/gadget/udc/renesas_usb3.c | 2 +- drivers/usb/gadget/udc/renesas_usbf.c | 5 ++--- drivers/usb/gadget/udc/tegra-xudc.c | 1 - drivers/usb/gadget/udc/udc-xilinx.c | 6 ++---- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/ehci-orion.c | 2 -- drivers/usb/host/fhci-hcd.c | 3 ++- drivers/usb/host/fsl-mph-dr-of.c | 3 ++- drivers/usb/host/ohci-at91.c | 2 +- drivers/usb/host/ohci-da8xx.c | 1 + drivers/usb/host/ohci-ppc-of.c | 3 ++- drivers/usb/host/xhci-plat.c | 1 - drivers/usb/host/xhci-rcar.c | 1 - drivers/usb/host/xhci-tegra.c | 2 +- drivers/usb/misc/usb251xb.c | 2 +- drivers/usb/mtu3/mtu3.h | 1 + drivers/usb/mtu3/mtu3_host.c | 1 + drivers/usb/musb/jz4740.c | 2 +- drivers/usb/musb/mediatek.c | 1 + drivers/usb/musb/mpfs.c | 1 + drivers/usb/musb/musb_dsps.c | 2 -- drivers/usb/musb/sunxi.c | 1 - drivers/usb/phy/phy-mxs-usb.c | 2 +- drivers/usb/phy/phy-tegra-usb.c | 2 +- drivers/usb/renesas_usbhs/common.c | 2 +- drivers/usb/renesas_usbhs/rza.c | 2 +- drivers/usb/renesas_usbhs/rza2.c | 1 - drivers/usb/typec/tcpm/fusb302.c | 2 +- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c | 2 +- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c | 2 -- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c | 1 - drivers/usb/typec/ucsi/ucsi_glink.c | 1 - 51 files changed, 46 insertions(+), 48 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index ea19253fd2d0..e6f6aeb7b5bb 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -61,6 +61,7 @@ #include #include #include +#include #include "core.h" #include "gadget-export.h" diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 1168dbeed2ce..2c1aca84f226 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index 81b9132e3aaa..5945c4b1e11f 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -15,6 +15,7 @@ #include #include #include +#include /* USB Wrapper register offsets */ #define USBSS_PID 0x0 diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 7b20d2d5c262..33548771a0d3 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 336ef6dd8e7d..aa2aebed8e2d 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index ca36d11a69ea..8e78bf643e25 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -6,7 +6,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 9ee9621e2ccc..e8a712e5abad 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -4,10 +4,11 @@ */ #include -#include +#include #include #include #include +#include #include #include "ci_hdrc_imx.h" diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index c9bdeb4ddcb5..b84efae26e15 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index b5811620f1de..0d2bfc909019 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -9,6 +9,7 @@ #include /* for scatterlist macros */ #include #include +#include #include #include #include diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index 617e92569b2c..db4ccf9ce3d9 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -8,7 +8,6 @@ */ #include -#include #include /** diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 39bdf9589b06..bba87e5b7f8a 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 8b15742d9e8a..b517a7216de2 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 0a806f80217e..b1d48019e944 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 8b9a3bb587bf..4285bde58d2e 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 0a09aedc2573..4155e8d5a559 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index a67873a074b7..5265ca418cde 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 09762559912d..0c3969301a53 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -36,9 +37,7 @@ #include #include #include -#include -#include -#include +#include #include diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c index 12c519f32bf7..2d57786d3db7 100644 --- a/drivers/usb/gadget/udc/max3420_udc.c +++ b/drivers/usb/gadget/udc/max3420_udc.c @@ -19,9 +19,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index c4e1d957f913..61424cfd2e1c 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 59bb25de2015..3b01734ce1b7 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c index 6cd0af83e91e..3482b41d0646 100644 --- a/drivers/usb/gadget/udc/renesas_usbf.c +++ b/drivers/usb/gadget/udc/renesas_usbf.c @@ -12,10 +12,9 @@ #include #include #include +#include #include -#include -#include -#include +#include #include #include #include diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 83eaa65ddde3..065046f1c9cb 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index a4a7b90a97e7..0a025bf14e06 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -18,10 +18,8 @@ #include #include #include -#include -#include -#include -#include +#include +#include #include #include #include diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 81d60a695510..3c776f4de4b8 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include "ehci.h" diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 2cfb27dc943a..a5f4e2f98346 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -13,8 +13,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 66a045e01dad..9a1b5224f239 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -22,9 +22,10 @@ #include #include #include +#include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index a9877f2569f4..8508d37a2aff 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -10,7 +10,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index b9ce8d80f20b..3b023ea71f8d 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -17,13 +17,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index e4191a868944..9bd6cb9af364 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 35a7ad7e2569..f64bfe5f4d4d 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -15,9 +15,10 @@ */ #include +#include #include #include -#include +#include static int ohci_ppc_of_start(struct usb_hcd *hcd) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index b26ea7cb4357..28218c8f1837 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index bf5261fed32c..ab9c5969e462 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include "xhci.h" diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 6ca8a37e53e1..51e236c1ff71 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index e4edb486b69e..7da404f55a6d 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index b4a7662dded5..c11840b9a6f1 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index 177d2caf887c..9f2be22af844 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 5aabdd7e2511..b38df9226278 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c index 598ee5c0bf34..0a35aab3ab81 100644 --- a/drivers/usb/musb/mediatek.c +++ b/drivers/usb/musb/mediatek.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c index 24b98716f7fc..f0f56df38835 100644 --- a/drivers/usb/musb/mpfs.c +++ b/drivers/usb/musb/mpfs.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include "musb_core.h" diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 9119b1d51370..98b42dc04dee 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -26,9 +26,7 @@ #include #include -#include #include -#include #include #include diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index c5c6c4e09300..d54283fd026b 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index e1a2b2ea098b..50cf0003384a 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 8b2ff3a8882d..4ea47e6f835b 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 111b7ee152c4..dd1c17542439 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/renesas_usbhs/rza.c b/drivers/usb/renesas_usbhs/rza.c index 2d77edefb4b3..97b5217c5a90 100644 --- a/drivers/usb/renesas_usbhs/rza.c +++ b/drivers/usb/renesas_usbhs/rza.c @@ -8,7 +8,7 @@ #include #include -#include +#include #include "common.h" #include "rza.h" diff --git a/drivers/usb/renesas_usbhs/rza2.c b/drivers/usb/renesas_usbhs/rza2.c index 3eed3334a17f..f079817250bb 100644 --- a/drivers/usb/renesas_usbhs/rza2.c +++ b/drivers/usb/renesas_usbhs/rza2.c @@ -8,7 +8,6 @@ #include #include -#include #include #include "common.h" #include "rza.h" diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 7fc1ffa14f76..bc21006e979c 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c index a905160dd860..f2f3601cbbfb 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c index 4e1b846627d2..bb0b8479d80f 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c index 56df04af2d2b..a8f3f4d3a450 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 1fe9cb5b6bd9..bb1854b3311d 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -5,7 +5,6 @@ */ #include #include -#include #include #include #include -- cgit From 451054c38b50ebd0730fd4e7d1b99ffc83b783ac Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Fri, 21 Jul 2023 10:05:26 +0530 Subject: dt-bindings: usb: dwc3: Add IPQ5332 compatible Document the IPQ5332 dwc3 compatible. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Varadarajan Narayanan Link: https://lore.kernel.org/r/cb89ff518890117b82d41d29d058de6662753bc8.1689913334.git.quic_varada@quicinc.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/qcom,dwc3.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index ae24dac78d9a..9447b54bfb23 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -14,6 +14,7 @@ properties: items: - enum: - qcom,ipq4019-dwc3 + - qcom,ipq5332-dwc3 - qcom,ipq6018-dwc3 - qcom,ipq8064-dwc3 - qcom,ipq8074-dwc3 @@ -246,6 +247,7 @@ allOf: compatible: contains: enum: + - qcom,ipq5332-dwc3 - qcom,msm8994-dwc3 - qcom,qcs404-dwc3 then: @@ -410,6 +412,7 @@ allOf: compatible: contains: enum: + - qcom,ipq5332-dwc3 - qcom,sdm660-dwc3 then: properties: -- cgit From 8405bc521b768823ecc35f30792407dd247fb139 Mon Sep 17 00:00:00 2001 From: Utkarsh Patel Date: Mon, 17 Jul 2023 19:47:03 -0700 Subject: usb: typec: intel_pmc_mux: Configure Active and Retimer Cable type Cable type such as active and retimer received as a part of Thunderbolt3 or Thunderbolt4 cable discover mode VDO needs to be configured in the thunderbolt alternate mode. Configuring the register bits for this cable type is changed with Intel Meteor Lake platform. BIT2 for Retimer/Redriver cable and BIT22 for Active/Passive cable. Reviewed-by: Heikki Krogerus Signed-off-by: Utkarsh Patel Link: https://lore.kernel.org/r/20230718024703.1013367-3-utkarsh.h.patel@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/intel_pmc_mux.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 61a88f68b458..60ed1f809130 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -59,7 +59,7 @@ enum { }; /* Common Mode Data bits */ -#define PMC_USB_ALTMODE_ACTIVE_CABLE BIT(2) +#define PMC_USB_ALTMODE_RETIMER_CABLE BIT(2) #define PMC_USB_ALTMODE_ORI_SHIFT 1 #define PMC_USB_ALTMODE_UFP_SHIFT 3 @@ -71,6 +71,7 @@ enum { #define PMC_USB_ALTMODE_TBT_TYPE BIT(17) #define PMC_USB_ALTMODE_CABLE_TYPE BIT(18) #define PMC_USB_ALTMODE_ACTIVE_LINK BIT(20) +#define PMC_USB_ALTMODE_ACTIVE_CABLE BIT(22) #define PMC_USB_ALTMODE_FORCE_LSR BIT(23) #define PMC_USB_ALTMODE_CABLE_SPD(_s_) (((_s_) & GENMASK(2, 0)) << 25) #define PMC_USB_ALTMODE_CABLE_USB31 1 @@ -330,8 +331,18 @@ pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state) if (data->cable_mode & TBT_CABLE_LINK_TRAINING) req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK; - if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE) - req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE; + if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) || + acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL)) { + if ((data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE) || + (data->cable_mode & TBT_CABLE_RETIMER)) + req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE; + } else { + if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE) + req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE; + + if (data->cable_mode & TBT_CABLE_RETIMER) + req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE; + } req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed); @@ -370,8 +381,17 @@ pmc_usb_mux_usb4(struct pmc_usb_port *port, struct typec_mux_state *state) case EUDO_CABLE_TYPE_OPTICAL: req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE; fallthrough; + case EUDO_CABLE_TYPE_RE_TIMER: + if (!acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) || + !acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL)) + req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE; + fallthrough; default: - req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE; + if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) || + acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL)) + req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE; + else + req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE; /* Configure data rate to rounded in the case of Active TBT3 * and USB4 cables. -- cgit From 36668515d56bf73f06765c71e08c8f7465f1e5c4 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 27 Jun 2023 19:21:24 +0800 Subject: usb: chipidea: imx: improve logic if samsung,picophy-* parameter is 0 In current driver, the value of tuning parameter will not take effect if samsung,picophy-* is assigned as 0. Because 0 is also a valid value acccording to the description of USB_PHY_CFG1 register, this will improve the logic to let it work. Fixes: 58a3cefb3840 ("usb: chipidea: imx: add two samsung picophy parameters tuning implementation") cc: Signed-off-by: Xu Yang Acked-by: Peter Chen Link: https://lore.kernel.org/r/20230627112126.1882666-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 10 ++++++---- drivers/usb/chipidea/usbmisc_imx.c | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index aa2aebed8e2d..c251a4f34879 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -176,10 +176,12 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) data->ulpi = 1; - of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control", - &data->emp_curr_control); - of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust", - &data->dc_vol_level_adjust); + if (of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control", + &data->emp_curr_control)) + data->emp_curr_control = -1; + if (of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust", + &data->dc_vol_level_adjust)) + data->dc_vol_level_adjust = -1; return data; } diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index e8a712e5abad..c4165f061e42 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -660,13 +660,15 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) usbmisc->base + MX7D_USBNC_USB_CTRL2); /* PHY tuning for signal quality */ reg = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG1); - if (data->emp_curr_control && data->emp_curr_control <= + if (data->emp_curr_control >= 0 && + data->emp_curr_control <= (TXPREEMPAMPTUNE0_MASK >> TXPREEMPAMPTUNE0_BIT)) { reg &= ~TXPREEMPAMPTUNE0_MASK; reg |= (data->emp_curr_control << TXPREEMPAMPTUNE0_BIT); } - if (data->dc_vol_level_adjust && data->dc_vol_level_adjust <= + if (data->dc_vol_level_adjust >= 0 && + data->dc_vol_level_adjust <= (TXVREFTUNE0_MASK >> TXVREFTUNE0_BIT)) { reg &= ~TXVREFTUNE0_MASK; reg |= (data->dc_vol_level_adjust << TXVREFTUNE0_BIT); -- cgit From 8d2c452c9ee982ed26c742b9faab0f3202370f7d Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 27 Jun 2023 19:21:25 +0800 Subject: dt-bindings: usb: ci-hdrc-usb2: add fsl,picophy-rise-fall-time-adjust property The fsl,picophy-rise-fall-time-adjust property can help to adjust the rise/fall times of the high-speed transmitter waveform. The value can be 0~3. It has no unit. According to the description of USBNC_n_PHY_CFG1 register, the rise/fall time will be increased or decreased by a certain percentage relative to design default time if a value is given to this property. Signed-off-by: Xu Yang Reviewed-by: Krzysztof Kozlowski Acked-by: Peter Chen Link: https://lore.kernel.org/r/20230627112126.1882666-2-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml index 782402800d4a..532d6464c8b3 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml @@ -292,6 +292,18 @@ properties: minimum: 0x0 maximum: 0xf + fsl,picophy-rise-fall-time-adjust: + description: + HS Transmitter Rise/Fall Time Adjustment. Adjust the rise/fall times + of the high-speed transmitter waveform. It has no unit. The rise/fall + time will be increased or decreased by a certain percentage relative + to design default time. (0:-10%; 1:design default; 2:+15%; 3:+20%) + Details can refer to TXRISETUNE0 bit of USBNC_n_PHY_CFG1. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 1 + usb-phy: description: phandle for the PHY device. Use "phys" instead. $ref: /schemas/types.yaml#/definitions/phandle -- cgit From 3bd442e4d2a49cd45cfecefcf95af72a3a44049a Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 27 Jun 2023 19:21:26 +0800 Subject: usb: chipidea: imx: add one fsl picophy parameter tuning implementation In some cases, the user may need to tune the rise/fall time of the high-speed transmitter waveform for USB Certification. This will add a parameter for this purpose. The value will be fetched from dtb and finally written to the register. Signed-off-by: Xu Yang Acked-by: Peter Chen Link: https://lore.kernel.org/r/20230627112126.1882666-3-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 3 +++ drivers/usb/chipidea/ci_hdrc_imx.h | 1 + drivers/usb/chipidea/usbmisc_imx.c | 9 +++++++++ 3 files changed, 13 insertions(+) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index c251a4f34879..772bbdade994 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -182,6 +182,9 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) if (of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust", &data->dc_vol_level_adjust)) data->dc_vol_level_adjust = -1; + if (of_property_read_u32(np, "fsl,picophy-rise-fall-time-adjust", + &data->rise_fall_time_adjust)) + data->rise_fall_time_adjust = -1; return data; } diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 7135b9a5d913..88b8da79d518 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -28,6 +28,7 @@ struct imx_usbmisc_data { enum usb_dr_mode available_role; /* runtime usb dr mode */ int emp_curr_control; int dc_vol_level_adjust; + int rise_fall_time_adjust; }; int imx_usbmisc_init(struct imx_usbmisc_data *data); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index c4165f061e42..173c78afd502 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -131,6 +131,8 @@ #define MX7D_USB_OTG_PHY_CFG1 0x30 #define TXPREEMPAMPTUNE0_BIT 28 #define TXPREEMPAMPTUNE0_MASK (3 << 28) +#define TXRISETUNE0_BIT 24 +#define TXRISETUNE0_MASK (3 << 24) #define TXVREFTUNE0_BIT 20 #define TXVREFTUNE0_MASK (0xf << 20) @@ -674,6 +676,13 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) reg |= (data->dc_vol_level_adjust << TXVREFTUNE0_BIT); } + if (data->rise_fall_time_adjust >= 0 && + data->rise_fall_time_adjust <= + (TXRISETUNE0_MASK >> TXRISETUNE0_BIT)) { + reg &= ~TXRISETUNE0_MASK; + reg |= (data->rise_fall_time_adjust << TXRISETUNE0_BIT); + } + writel(reg, usbmisc->base + MX7D_USB_OTG_PHY_CFG1); } -- cgit From 26910f977c3f966ce0e513ada42c741f243085ce Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Fri, 21 Jul 2023 17:59:23 +0800 Subject: usb: cdns3: Add PHY mode switch to usb2 PHY cdns3 just set PHY mode switch for USB3.0 PHY. If USB 2.0 PHY contains PHY mode switch setting, USB 2.0 PHY mode function can't be called. So add PHY mode switch function for USB 2.0 PHY. Signed-off-by: Minda Chen Reviewed-by: Roger Quadros Link: https://lore.kernel.org/r/20230721095923.20445-1-minda.chen@starfivetech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/drd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index d00ff98dffab..04b6d12f2b9a 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -196,6 +196,7 @@ int cdns_drd_host_on(struct cdns *cdns) if (ret) dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); + phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST); phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST); return ret; } @@ -216,6 +217,7 @@ void cdns_drd_host_off(struct cdns *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_HOST_STATE_MASK), 1, 2000000); + phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } @@ -248,6 +250,7 @@ int cdns_drd_gadget_on(struct cdns *cdns) return ret; } + phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE); phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE); return 0; } @@ -273,6 +276,7 @@ void cdns_drd_gadget_off(struct cdns *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_DEV_STATE_MASK), 1, 2000000); + phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } EXPORT_SYMBOL_GPL(cdns_drd_gadget_off); -- cgit From 74a6f1e8fb40738e7816c1d16479590e3fbed42d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 23 Jul 2023 16:15:49 +0200 Subject: dt-bindings: usb: qcom,dwc3: drop assigned-clocks The binding does not have to specify assigned-clocks, because they are already allowed by core DT schema. On the other hand, fixed assigned-clocks in the binding will not fit different boards or SoCs. Exactly this is the case for Qualcomm SuperSpeed DWC3 USB SoC controller binding, where few boards have different assigned-clocks: ipq8074-hk10-c1.dtb: usb@8cf8800: assigned-clocks: [[5, 131], [5, 132], [5, 133]] is too long sdm660-xiaomi-lavender.dtb: usb@a8f8800: assigned-clocks: [[37, 92], [37, 91], [38, 64]] is too long Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230723141550.90223-1-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/qcom,dwc3.yaml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index 9447b54bfb23..33f299f0685d 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -83,15 +83,6 @@ properties: minItems: 1 maxItems: 9 - assigned-clocks: - items: - - description: Phandle and clock specifier of MOCK_UTMI_CLK. - - description: Phandle and clock specifoer of MASTER_CLK. - - assigned-clock-rates: - items: - - description: Must be 19.2MHz (19200000). - - description: Must be >= 60 MHz in HS mode, >= 125 MHz in SS mode. resets: maxItems: 1 -- cgit From 0497d34625167c0841d4828a28a26a10a2e9edde Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 23 Jul 2023 16:15:50 +0200 Subject: dt-bindings: usb: qcom,dwc3: correct SDM660 clocks SDM660 SoC has two instances of DWC3 USB controller: one supporting USB 3.0 and one supporting only up to USB 2.0. The latter one does not use iface clock, so allow such variant to fix dtbs_check warnings: sda660-inforce-ifc6560.dtb: usb@c2f8800: clocks: [[37, 48], [37, 88], [37, 89], [37, 90]] is too short sda660-inforce-ifc6560.dtb: usb@c2f8800: clock-names:2: 'iface' was expected Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230723141550.90223-2-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/qcom,dwc3.yaml | 24 ++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index 33f299f0685d..67591057f234 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -283,15 +283,23 @@ allOf: then: properties: clocks: - minItems: 6 + minItems: 5 + maxItems: 6 clock-names: - items: - - const: cfg_noc - - const: core - - const: iface - - const: sleep - - const: mock_utmi - - const: bus + oneOf: + - items: + - const: cfg_noc + - const: core + - const: iface + - const: sleep + - const: mock_utmi + - const: bus + - items: + - const: cfg_noc + - const: core + - const: sleep + - const: mock_utmi + - const: bus - if: properties: -- cgit From 7713aaf464599091d7a43fdae69fd69f0dd942f9 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 24 Jul 2023 11:28:43 +0200 Subject: USB: document ioctl USBDEVFS_GET_SPEED Documenting this ioctl along with the other ioctls Signed-off-by: Oliver Neukum Link: https://lore.kernel.org/r/20230724092843.18410-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/usb/usb.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/driver-api/usb/usb.rst b/Documentation/driver-api/usb/usb.rst index 2c94ff2f4385..0fcd75ee5897 100644 --- a/Documentation/driver-api/usb/usb.rst +++ b/Documentation/driver-api/usb/usb.rst @@ -420,6 +420,12 @@ USBDEVFS_CONNECTINFO know the devnum value already, it's the DDD value of the device file name. +USBDEVFS_GET_SPEED + Returns the speed of the device. The speed is returned as a + nummerical value in accordance with enum usb_device_speed + + File modification time is not updated by this request. + USBDEVFS_GETDRIVER Returns the name of the kernel driver bound to a given interface (a string). Parameter is a pointer to this structure, which is -- cgit From b7a62611fab72e585c729a7fcf666aa9c4144214 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 27 Jun 2023 19:03:51 +0800 Subject: usb: chipidea: add USB PHY event Add USB PHY event for below situation: - usb role changed - vbus connect - vbus disconnect - gadget driver is enumerated USB PHY driver can get the last event after above situation occurs and deal with different situations. Signed-off-by: Xu Yang Acked-by: Peter Chen Link: https://lore.kernel.org/r/20230627110353.1879477-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 18 ++++++++++++++++-- drivers/usb/chipidea/udc.c | 10 ++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index f210b7489fd5..d262b9df7b3d 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -281,8 +281,19 @@ static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role) return -ENXIO; ret = ci->roles[role]->start(ci); - if (!ret) - ci->role = role; + if (ret) + return ret; + + ci->role = role; + + if (ci->usb_phy) { + if (role == CI_ROLE_HOST) + usb_phy_set_event(ci->usb_phy, USB_EVENT_ID); + else + /* in device mode but vbus is invalid*/ + usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE); + } + return ret; } @@ -296,6 +307,9 @@ static inline void ci_role_stop(struct ci_hdrc *ci) ci->role = CI_ROLE_END; ci->roles[role]->stop(ci); + + if (ci->usb_phy) + usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE); } static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 54c09245ad05..d58355427eeb 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1718,6 +1718,13 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) ret = ci->platdata->notify_event(ci, CI_HDRC_CONTROLLER_VBUS_EVENT); + if (ci->usb_phy) { + if (is_active) + usb_phy_set_event(ci->usb_phy, USB_EVENT_VBUS); + else + usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE); + } + if (ci->driver) ci_hdrc_gadget_connect(_gadget, is_active); @@ -2034,6 +2041,9 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci) if (USBi_PCI & intr) { ci->gadget.speed = hw_port_is_high_speed(ci) ? USB_SPEED_HIGH : USB_SPEED_FULL; + if (ci->usb_phy) + usb_phy_set_event(ci->usb_phy, + USB_EVENT_ENUMERATED); if (ci->suspended) { if (ci->driver->resume) { spin_unlock(&ci->lock); -- cgit From 5eda42aebb7668b4dcff025cd3ccb0d3d7c53da6 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 27 Jun 2023 19:03:52 +0800 Subject: usb: phy: mxs: fix getting wrong state with mxs_phy_is_otg_host() The function mxs_phy_is_otg_host() will return true if OTG_ID_VALUE is 0 at USBPHY_CTRL register. However, OTG_ID_VALUE will not reflect the real state if the ID pin is float, such as Host-only or Type-C cases. The value of OTG_ID_VALUE is always 1 which means device mode. This patch will fix the issue by judging the current mode based on last_event. The controller will update last_event in time. Fixes: 7b09e67639d6 ("usb: phy: mxs: refine mxs_phy_disconnect_line") Signed-off-by: Xu Yang Acked-by: Peter Chen Link: https://lore.kernel.org/r/20230627110353.1879477-2-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-mxs-usb.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 50cf0003384a..6572b91702a5 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -388,14 +388,8 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy) { - void __iomem *base = mxs_phy->phy.io_priv; - u32 phyctrl = readl(base + HW_USBPHY_CTRL); - - if (IS_ENABLED(CONFIG_USB_OTG) && - !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE)) - return true; - - return false; + return IS_ENABLED(CONFIG_USB_OTG) && + mxs_phy->phy.last_event == USB_EVENT_ID; } static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) -- cgit From 87ed257acb0934e08644568df6495988631afd4c Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 27 Jun 2023 19:03:53 +0800 Subject: usb: phy: mxs: disconnect line when USB charger is attached For mxs PHY, if there is a vbus but the bus is not enumerated, we need to force the dp/dm as SE0 from the controller side. If not, there is possible USB wakeup due to unstable dp/dm, since there is possible no pull on dp/dm, such as there is a USB charger on the port. Signed-off-by: Xu Yang Acked-by: Peter Chen Link: https://lore.kernel.org/r/20230627110353.1879477-3-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-mxs-usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 6572b91702a5..acd46b72899e 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -395,6 +395,7 @@ static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy) static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) { bool vbus_is_on = false; + enum usb_phy_events last_event = mxs_phy->phy.last_event; /* If the SoCs don't need to disconnect line without vbus, quit */ if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS)) @@ -406,7 +407,8 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); - if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy)) + if (on && ((!vbus_is_on && !mxs_phy_is_otg_host(mxs_phy)) + || (last_event == USB_EVENT_VBUS))) __mxs_phy_disconnect_line(mxs_phy, true); else __mxs_phy_disconnect_line(mxs_phy, false); -- cgit From 3609699c32aa4f2710a6fe2b21afc6a9a3c66bc5 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sat, 15 Jul 2023 16:07:26 +0200 Subject: usb: dwc3-am62: Rename private data Rename dwc3_data to dwc3_am62 to make it consistent with other glue drivers, it's clearer that this is am62's specific. While there, do the same for data variable. Signed-off-by: Ladislav Michl Link: https://lore.kernel.org/r/ZLKoHhJvT+Y6aM+C@lenoch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-am62.c | 94 ++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index 1755f2f848c5..94dcbc443cf2 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -102,7 +102,7 @@ #define DWC3_AM62_AUTOSUSPEND_DELAY 100 -struct dwc3_data { +struct dwc3_am62 { struct device *dev; void __iomem *usbss; struct clk *usb2_refclk; @@ -129,19 +129,19 @@ static const int dwc3_ti_rate_table[] = { /* in KHZ */ 52000, }; -static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset) +static inline u32 dwc3_ti_readl(struct dwc3_am62 *am62, u32 offset) { - return readl((data->usbss) + offset); + return readl((am62->usbss) + offset); } -static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value) +static inline void dwc3_ti_writel(struct dwc3_am62 *am62, u32 offset, u32 value) { - writel(value, (data->usbss) + offset); + writel(value, (am62->usbss) + offset); } -static int phy_syscon_pll_refclk(struct dwc3_data *data) +static int phy_syscon_pll_refclk(struct dwc3_am62 *am62) { - struct device *dev = data->dev; + struct device *dev = am62->dev; struct device_node *node = dev->of_node; struct of_phandle_args args; struct regmap *syscon; @@ -153,16 +153,16 @@ static int phy_syscon_pll_refclk(struct dwc3_data *data) return PTR_ERR(syscon); } - data->syscon = syscon; + am62->syscon = syscon; ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1, 0, &args); if (ret) return ret; - data->offset = args.args[0]; + am62->offset = args.args[0]; - ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code); + ret = regmap_update_bits(am62->syscon, am62->offset, PHY_PLL_REFCLK_MASK, am62->rate_code); if (ret) { dev_err(dev, "failed to set phy pll reference clock rate\n"); return ret; @@ -175,32 +175,32 @@ static int dwc3_ti_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; - struct dwc3_data *data; + struct dwc3_am62 *am62; int i, ret; unsigned long rate; u32 reg; - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) + am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL); + if (!am62) return -ENOMEM; - data->dev = dev; - platform_set_drvdata(pdev, data); + am62->dev = dev; + platform_set_drvdata(pdev, am62); - data->usbss = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->usbss)) { + am62->usbss = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(am62->usbss)) { dev_err(dev, "can't map IOMEM resource\n"); - return PTR_ERR(data->usbss); + return PTR_ERR(am62->usbss); } - data->usb2_refclk = devm_clk_get(dev, "ref"); - if (IS_ERR(data->usb2_refclk)) { + am62->usb2_refclk = devm_clk_get(dev, "ref"); + if (IS_ERR(am62->usb2_refclk)) { dev_err(dev, "can't get usb2_refclk\n"); - return PTR_ERR(data->usb2_refclk); + return PTR_ERR(am62->usb2_refclk); } /* Calculate the rate code */ - rate = clk_get_rate(data->usb2_refclk); + rate = clk_get_rate(am62->usb2_refclk); rate /= 1000; // To KHz for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) { if (dwc3_ti_rate_table[i] == rate) @@ -212,20 +212,20 @@ static int dwc3_ti_probe(struct platform_device *pdev) return -EINVAL; } - data->rate_code = i; + am62->rate_code = i; /* Read the syscon property and set the rate code */ - ret = phy_syscon_pll_refclk(data); + ret = phy_syscon_pll_refclk(am62); if (ret) return ret; /* VBUS divider select */ - data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); - reg = dwc3_ti_readl(data, USBSS_PHY_CONFIG); - if (data->vbus_divider) + am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); + reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG); + if (am62->vbus_divider) reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT; - dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg); + dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg); pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -233,7 +233,7 @@ static int dwc3_ti_probe(struct platform_device *pdev) * Don't ignore its dependencies with its children */ pm_suspend_ignore_children(dev, false); - clk_prepare_enable(data->usb2_refclk); + clk_prepare_enable(am62->usb2_refclk); pm_runtime_get_noresume(dev); ret = of_platform_populate(node, NULL, NULL, dev); @@ -243,9 +243,9 @@ static int dwc3_ti_probe(struct platform_device *pdev) } /* Set mode valid bit to indicate role is valid */ - reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL); + reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL); reg |= USBSS_MODE_VALID; - dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg); + dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg); /* Device has capability to wakeup system from sleep */ device_set_wakeup_capable(dev, true); @@ -261,7 +261,7 @@ static int dwc3_ti_probe(struct platform_device *pdev) return 0; err_pm_disable: - clk_disable_unprepare(data->usb2_refclk); + clk_disable_unprepare(am62->usb2_refclk); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); return ret; @@ -278,18 +278,18 @@ static int dwc3_ti_remove_core(struct device *dev, void *c) static void dwc3_ti_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct dwc3_data *data = platform_get_drvdata(pdev); + struct dwc3_am62 *am62 = platform_get_drvdata(pdev); u32 reg; device_for_each_child(dev, NULL, dwc3_ti_remove_core); /* Clear mode valid bit */ - reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL); + reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL); reg &= ~USBSS_MODE_VALID; - dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg); + dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg); pm_runtime_put_sync(dev); - clk_disable_unprepare(data->usb2_refclk); + clk_disable_unprepare(am62->usb2_refclk); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); @@ -299,15 +299,15 @@ static void dwc3_ti_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int dwc3_ti_suspend_common(struct device *dev) { - struct dwc3_data *data = dev_get_drvdata(dev); + struct dwc3_am62 *am62 = dev_get_drvdata(dev); u32 reg, current_prtcap_dir; if (device_may_wakeup(dev)) { - reg = dwc3_ti_readl(data, USBSS_CORE_STAT); + reg = dwc3_ti_readl(am62, USBSS_CORE_STAT); current_prtcap_dir = (reg & USBSS_CORE_OPERATIONAL_MODE_MASK) >> USBSS_CORE_OPERATIONAL_MODE_SHIFT; /* Set wakeup config enable bits */ - reg = dwc3_ti_readl(data, USBSS_WAKEUP_CONFIG); + reg = dwc3_ti_readl(am62, USBSS_WAKEUP_CONFIG); if (current_prtcap_dir == DWC3_GCTL_PRTCAP_HOST) { reg = USBSS_WAKEUP_CFG_LINESTATE_EN | USBSS_WAKEUP_CFG_OVERCURRENT_EN; } else { @@ -317,30 +317,30 @@ static int dwc3_ti_suspend_common(struct device *dev) * and in U2/L3 state else it causes spurious wake-up. */ } - dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, reg); + dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, reg); /* clear wakeup status so we know what caused the wake up */ - dwc3_ti_writel(data, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR); + dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR); } - clk_disable_unprepare(data->usb2_refclk); + clk_disable_unprepare(am62->usb2_refclk); return 0; } static int dwc3_ti_resume_common(struct device *dev) { - struct dwc3_data *data = dev_get_drvdata(dev); + struct dwc3_am62 *am62 = dev_get_drvdata(dev); u32 reg; - clk_prepare_enable(data->usb2_refclk); + clk_prepare_enable(am62->usb2_refclk); if (device_may_wakeup(dev)) { /* Clear wakeup config enable bits */ - dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE); + dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE); } - reg = dwc3_ti_readl(data, USBSS_WAKEUP_STAT); - data->wakeup_stat = reg; + reg = dwc3_ti_readl(am62, USBSS_WAKEUP_STAT); + am62->wakeup_stat = reg; return 0; } -- cgit From a08799cf17c22375752abfad3b4a2b34b3acb287 Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Tue, 25 Jul 2023 11:31:52 +0800 Subject: usb: phy: add usb phy notify port status API In Realtek SoC, the parameter of usb phy is designed to can dynamic tuning base on port status. Therefore, add a notify callback of phy driver when usb port status change. The Realtek phy driver is designed to dynamically adjust disconnection level and calibrate phy parameters. When the device connected bit changes and when the disconnected bit changes, do port status change notification: Check if portstatus is USB_PORT_STAT_CONNECTION and portchange is USB_PORT_STAT_C_CONNECTION. 1. The device is connected, the driver lowers the disconnection level and calibrates the phy parameters. 2. The device disconnects, the driver increases the disconnect level and calibrates the phy parameters. When controller to notify connect that device is already ready. If we adjust the disconnection level in notify_connect, the disconnect may have been triggered at this stage. So we need to change that as early as possible. The status change of connection is before port reset. Therefore, we add an api to notify phy the port status changes. In this stage, the device is not port enable, and it will not trigger disconnection. Signed-off-by: Stanley Chang Link: https://lore.kernel.org/r/20230725033318.8361-1-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 23 +++++++++++++++++++++++ include/linux/usb/phy.h | 13 +++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a739403a9e45..fcbad9e86328 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -614,6 +614,29 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type, ret = 0; } mutex_unlock(&hub->status_mutex); + + /* + * There is no need to lock status_mutex here, because status_mutex + * protects hub->status, and the phy driver only checks the port + * status without changing the status. + */ + if (!ret) { + struct usb_device *hdev = hub->hdev; + + /* + * Only roothub will be notified of port state changes, + * since the USB PHY only cares about changes at the next + * level. + */ + if (is_root_hub(hdev)) { + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); + + if (hcd->usb_phy) + usb_phy_notify_port_status(hcd->usb_phy, + port1 - 1, *status, *change); + } + } + return ret; } diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index e4de6bc1f69b..b513749582d7 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -144,6 +144,10 @@ struct usb_phy { */ int (*set_wakeup)(struct usb_phy *x, bool enabled); + /* notify phy port status change */ + int (*notify_port_status)(struct usb_phy *x, int port, + u16 portstatus, u16 portchange); + /* notify phy connect status change */ int (*notify_connect)(struct usb_phy *x, enum usb_device_speed speed); @@ -316,6 +320,15 @@ usb_phy_set_wakeup(struct usb_phy *x, bool enabled) return 0; } +static inline int +usb_phy_notify_port_status(struct usb_phy *x, int port, u16 portstatus, u16 portchange) +{ + if (x && x->notify_port_status) + return x->notify_port_status(x, port, portstatus, portchange); + else + return 0; +} + static inline int usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed) { -- cgit From 134e6d25f6bd06071e5aac0a7eefcea6f7713955 Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Tue, 25 Jul 2023 11:31:53 +0800 Subject: phy: realtek: usb: Add driver for the Realtek SoC USB 2.0 PHY Realtek DHC (digital home center) RTD SoCs support DWC3 XHCI USB controller. Added the driver to drive the USB 2.0 PHY transceivers. Signed-off-by: Stanley Chang Link: https://lore.kernel.org/r/20230725033318.8361-2-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/realtek/Kconfig | 14 + drivers/phy/realtek/Makefile | 2 + drivers/phy/realtek/phy-rtk-usb2.c | 1329 ++++++++++++++++++++++++++++++++++++ 5 files changed, 1347 insertions(+) create mode 100644 drivers/phy/realtek/Kconfig create mode 100644 drivers/phy/realtek/Makefile create mode 100644 drivers/phy/realtek/phy-rtk-usb2.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 8dba9596408f..aac670b90589 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -87,6 +87,7 @@ source "drivers/phy/motorola/Kconfig" source "drivers/phy/mscc/Kconfig" source "drivers/phy/qualcomm/Kconfig" source "drivers/phy/ralink/Kconfig" +source "drivers/phy/realtek/Kconfig" source "drivers/phy/renesas/Kconfig" source "drivers/phy/rockchip/Kconfig" source "drivers/phy/samsung/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 54f312c10a40..ba7c100b14fc 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -26,6 +26,7 @@ obj-y += allwinner/ \ mscc/ \ qualcomm/ \ ralink/ \ + realtek/ \ renesas/ \ rockchip/ \ samsung/ \ diff --git a/drivers/phy/realtek/Kconfig b/drivers/phy/realtek/Kconfig new file mode 100644 index 000000000000..2f2f453729d5 --- /dev/null +++ b/drivers/phy/realtek/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Phy drivers for Realtek platforms +# +config PHY_RTK_RTD_USB2PHY + tristate "Realtek RTD USB2 PHY Transceiver Driver" + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this to support Realtek SoC USB2 phy transceiver. + The DHC (digital home center) RTD series SoCs used the Synopsys + DWC3 USB IP. This driver will do the PHY initialization + of the parameters. diff --git a/drivers/phy/realtek/Makefile b/drivers/phy/realtek/Makefile new file mode 100644 index 000000000000..cf5d440841a2 --- /dev/null +++ b/drivers/phy/realtek/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_RTK_RTD_USB2PHY) += phy-rtk-usb2.o diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c new file mode 100644 index 000000000000..ed47a1ce5d9c --- /dev/null +++ b/drivers/phy/realtek/phy-rtk-usb2.c @@ -0,0 +1,1329 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-rtk-usb2.c RTK usb2.0 PHY driver + * + * Copyright (C) 2023 Realtek Semiconductor Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* GUSB2PHYACCn register */ +#define PHY_NEW_REG_REQ BIT(25) +#define PHY_VSTS_BUSY BIT(23) +#define PHY_VCTRL_SHIFT 8 +#define PHY_REG_DATA_MASK 0xff + +#define GET_LOW_NIBBLE(addr) ((addr) & 0x0f) +#define GET_HIGH_NIBBLE(addr) (((addr) & 0xf0) >> 4) + +#define EFUS_USB_DC_CAL_RATE 2 +#define EFUS_USB_DC_CAL_MAX 7 + +#define EFUS_USB_DC_DIS_RATE 1 +#define EFUS_USB_DC_DIS_MAX 7 + +#define MAX_PHY_DATA_SIZE 20 +#define OFFEST_PHY_READ 0x20 + +#define MAX_USB_PHY_NUM 4 +#define MAX_USB_PHY_PAGE0_DATA_SIZE 16 +#define MAX_USB_PHY_PAGE1_DATA_SIZE 16 +#define MAX_USB_PHY_PAGE2_DATA_SIZE 8 + +#define SET_PAGE_OFFSET 0xf4 +#define SET_PAGE_0 0x9b +#define SET_PAGE_1 0xbb +#define SET_PAGE_2 0xdb + +#define PAGE_START 0xe0 +#define PAGE0_0XE4 0xe4 +#define PAGE0_0XE6 0xe6 +#define PAGE0_0XE7 0xe7 +#define PAGE1_0XE0 0xe0 +#define PAGE1_0XE2 0xe2 + +#define SENSITIVITY_CTRL (BIT(4) | BIT(5) | BIT(6)) +#define ENABLE_AUTO_SENSITIVITY_CALIBRATION BIT(2) +#define DEFAULT_DC_DRIVING_VALUE (0x8) +#define DEFAULT_DC_DISCONNECTION_VALUE (0x6) +#define HS_CLK_SELECT BIT(6) + +struct phy_reg { + void __iomem *reg_wrap_vstatus; + void __iomem *reg_gusb2phyacc0; + int vstatus_index; +}; + +struct phy_data { + u8 addr; + u8 data; +}; + +struct phy_cfg { + int page0_size; + struct phy_data page0[MAX_USB_PHY_PAGE0_DATA_SIZE]; + int page1_size; + struct phy_data page1[MAX_USB_PHY_PAGE1_DATA_SIZE]; + int page2_size; + struct phy_data page2[MAX_USB_PHY_PAGE2_DATA_SIZE]; + + int num_phy; + + bool check_efuse; + int check_efuse_version; +#define CHECK_EFUSE_V1 1 +#define CHECK_EFUSE_V2 2 + int efuse_dc_driving_rate; + int efuse_dc_disconnect_rate; + int dc_driving_mask; + int dc_disconnect_mask; + bool usb_dc_disconnect_at_page0; + int driving_updated_for_dev_dis; + + bool do_toggle; + bool do_toggle_driving; + bool use_default_parameter; + bool is_double_sensitivity_mode; +}; + +struct phy_parameter { + struct phy_reg phy_reg; + + /* Get from efuse */ + s8 efuse_usb_dc_cal; + s8 efuse_usb_dc_dis; + + /* Get from dts */ + bool inverse_hstx_sync_clock; + u32 driving_level; + s32 driving_level_compensate; + s32 disconnection_compensate; +}; + +struct rtk_phy { + struct usb_phy phy; + struct device *dev; + + struct phy_cfg *phy_cfg; + int num_phy; + struct phy_parameter *phy_parameter; + + struct dentry *debug_dir; +}; + +/* mapping 0xE0 to 0 ... 0xE7 to 7, 0xF0 to 8 ,,, 0xF7 to 15 */ +static inline int page_addr_to_array_index(u8 addr) +{ + return (int)((((addr) - PAGE_START) & 0x7) + + ((((addr) - PAGE_START) & 0x10) >> 1)); +} + +static inline u8 array_index_to_page_addr(int index) +{ + return ((((index) + PAGE_START) & 0x7) + + ((((index) & 0x8) << 1) + PAGE_START)); +} + +#define PHY_IO_TIMEOUT_USEC (50000) +#define PHY_IO_DELAY_US (100) + +static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) +{ + int ret; + unsigned int val; + + ret = read_poll_timeout(readl, val, ((val & mask) == result), + PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg); + if (ret) { + pr_err("%s can't program USB phy\n", __func__); + return -ETIMEDOUT; + } + + return 0; +} + +static char rtk_phy_read(struct phy_reg *phy_reg, char addr) +{ + void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0; + unsigned int val; + int ret = 0; + + addr -= OFFEST_PHY_READ; + + /* polling until VBusy == 0 */ + ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); + if (ret) + return (char)ret; + + /* VCtrl = low nibble of addr, and set PHY_NEW_REG_REQ */ + val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT); + writel(val, reg_gusb2phyacc0); + ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); + if (ret) + return (char)ret; + + /* VCtrl = high nibble of addr, and set PHY_NEW_REG_REQ */ + val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT); + writel(val, reg_gusb2phyacc0); + ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); + if (ret) + return (char)ret; + + val = readl(reg_gusb2phyacc0); + + return (char)(val & PHY_REG_DATA_MASK); +} + +static int rtk_phy_write(struct phy_reg *phy_reg, char addr, char data) +{ + unsigned int val; + void __iomem *reg_wrap_vstatus = phy_reg->reg_wrap_vstatus; + void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0; + int shift_bits = phy_reg->vstatus_index * 8; + int ret = 0; + + /* write data to VStatusOut2 (data output to phy) */ + writel((u32)data << shift_bits, reg_wrap_vstatus); + + ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); + if (ret) + return ret; + + /* VCtrl = low nibble of addr, set PHY_NEW_REG_REQ */ + val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT); + + writel(val, reg_gusb2phyacc0); + ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); + if (ret) + return ret; + + /* VCtrl = high nibble of addr, set PHY_NEW_REG_REQ */ + val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT); + + writel(val, reg_gusb2phyacc0); + ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); + if (ret) + return ret; + + return 0; +} + +static int rtk_phy_set_page(struct phy_reg *phy_reg, int page) +{ + switch (page) { + case 0: + return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_0); + case 1: + return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_1); + case 2: + return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_2); + default: + pr_err("%s error page=%d\n", __func__, page); + } + + return -EINVAL; +} + +static u8 __updated_dc_disconnect_level_page0_0xe4(struct phy_cfg *phy_cfg, + struct phy_parameter *phy_parameter, u8 data) +{ + u8 ret; + s32 val; + s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask; + int offset = 4; + + val = (s32)((data >> offset) & dc_disconnect_mask) + + phy_parameter->efuse_usb_dc_dis + + phy_parameter->disconnection_compensate; + + if (val > dc_disconnect_mask) + val = dc_disconnect_mask; + else if (val < 0) + val = 0; + + ret = (data & (~(dc_disconnect_mask << offset))) | + (val & dc_disconnect_mask) << offset; + + return ret; +} + +/* updated disconnect level at page0 */ +static void update_dc_disconnect_level_at_page0(struct rtk_phy *rtk_phy, + struct phy_parameter *phy_parameter, bool update) +{ + struct phy_cfg *phy_cfg; + struct phy_reg *phy_reg; + struct phy_data *phy_data_page; + struct phy_data *phy_data; + u8 addr, data; + int offset = 4; + s32 dc_disconnect_mask; + int i; + + phy_cfg = rtk_phy->phy_cfg; + phy_reg = &phy_parameter->phy_reg; + + /* Set page 0 */ + phy_data_page = phy_cfg->page0; + rtk_phy_set_page(phy_reg, 0); + + i = page_addr_to_array_index(PAGE0_0XE4); + phy_data = phy_data_page + i; + if (!phy_data->addr) { + phy_data->addr = PAGE0_0XE4; + phy_data->data = rtk_phy_read(phy_reg, PAGE0_0XE4); + } + + addr = phy_data->addr; + data = phy_data->data; + dc_disconnect_mask = phy_cfg->dc_disconnect_mask; + + if (update) + data = __updated_dc_disconnect_level_page0_0xe4(phy_cfg, phy_parameter, data); + else + data = (data & ~(dc_disconnect_mask << offset)) | + (DEFAULT_DC_DISCONNECTION_VALUE << offset); + + if (rtk_phy_write(phy_reg, addr, data)) + dev_err(rtk_phy->dev, + "%s: Error to set page1 parameter addr=0x%x value=0x%x\n", + __func__, addr, data); +} + +static u8 __updated_dc_disconnect_level_page1_0xe2(struct phy_cfg *phy_cfg, + struct phy_parameter *phy_parameter, u8 data) +{ + u8 ret; + s32 val; + s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask; + + if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { + val = (s32)(data & dc_disconnect_mask) + + phy_parameter->efuse_usb_dc_dis + + phy_parameter->disconnection_compensate; + } else { /* for CHECK_EFUSE_V2 or no efuse */ + if (phy_parameter->efuse_usb_dc_dis) + val = (s32)(phy_parameter->efuse_usb_dc_dis + + phy_parameter->disconnection_compensate); + else + val = (s32)((data & dc_disconnect_mask) + + phy_parameter->disconnection_compensate); + } + + if (val > dc_disconnect_mask) + val = dc_disconnect_mask; + else if (val < 0) + val = 0; + + ret = (data & (~dc_disconnect_mask)) | (val & dc_disconnect_mask); + + return ret; +} + +/* updated disconnect level at page1 */ +static void update_dc_disconnect_level_at_page1(struct rtk_phy *rtk_phy, + struct phy_parameter *phy_parameter, bool update) +{ + struct phy_cfg *phy_cfg; + struct phy_data *phy_data_page; + struct phy_data *phy_data; + struct phy_reg *phy_reg; + u8 addr, data; + s32 dc_disconnect_mask; + int i; + + phy_cfg = rtk_phy->phy_cfg; + phy_reg = &phy_parameter->phy_reg; + + /* Set page 1 */ + phy_data_page = phy_cfg->page1; + rtk_phy_set_page(phy_reg, 1); + + i = page_addr_to_array_index(PAGE1_0XE2); + phy_data = phy_data_page + i; + if (!phy_data->addr) { + phy_data->addr = PAGE1_0XE2; + phy_data->data = rtk_phy_read(phy_reg, PAGE1_0XE2); + } + + addr = phy_data->addr; + data = phy_data->data; + dc_disconnect_mask = phy_cfg->dc_disconnect_mask; + + if (update) + data = __updated_dc_disconnect_level_page1_0xe2(phy_cfg, phy_parameter, data); + else + data = (data & ~dc_disconnect_mask) | DEFAULT_DC_DISCONNECTION_VALUE; + + if (rtk_phy_write(phy_reg, addr, data)) + dev_err(rtk_phy->dev, + "%s: Error to set page1 parameter addr=0x%x value=0x%x\n", + __func__, addr, data); +} + +static void update_dc_disconnect_level(struct rtk_phy *rtk_phy, + struct phy_parameter *phy_parameter, bool update) +{ + struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; + + if (phy_cfg->usb_dc_disconnect_at_page0) + update_dc_disconnect_level_at_page0(rtk_phy, phy_parameter, update); + else + update_dc_disconnect_level_at_page1(rtk_phy, phy_parameter, update); +} + +static u8 __update_dc_driving_page0_0xe4(struct phy_cfg *phy_cfg, + struct phy_parameter *phy_parameter, u8 data) +{ + s32 driving_level_compensate = phy_parameter->driving_level_compensate; + s32 dc_driving_mask = phy_cfg->dc_driving_mask; + s32 val; + u8 ret; + + if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { + val = (s32)(data & dc_driving_mask) + driving_level_compensate + + phy_parameter->efuse_usb_dc_cal; + } else { /* for CHECK_EFUSE_V2 or no efuse */ + if (phy_parameter->efuse_usb_dc_cal) + val = (s32)((phy_parameter->efuse_usb_dc_cal & dc_driving_mask) + + driving_level_compensate); + else + val = (s32)(data & dc_driving_mask); + } + + if (val > dc_driving_mask) + val = dc_driving_mask; + else if (val < 0) + val = 0; + + ret = (data & (~dc_driving_mask)) | (val & dc_driving_mask); + + return ret; +} + +static void update_dc_driving_level(struct rtk_phy *rtk_phy, + struct phy_parameter *phy_parameter) +{ + struct phy_cfg *phy_cfg; + struct phy_reg *phy_reg; + + phy_reg = &phy_parameter->phy_reg; + phy_cfg = rtk_phy->phy_cfg; + if (!phy_cfg->page0[4].addr) { + rtk_phy_set_page(phy_reg, 0); + phy_cfg->page0[4].addr = PAGE0_0XE4; + phy_cfg->page0[4].data = rtk_phy_read(phy_reg, PAGE0_0XE4); + } + + if (phy_parameter->driving_level != DEFAULT_DC_DRIVING_VALUE) { + u32 dc_driving_mask; + u8 driving_level; + u8 data; + + data = phy_cfg->page0[4].data; + dc_driving_mask = phy_cfg->dc_driving_mask; + driving_level = data & dc_driving_mask; + + dev_dbg(rtk_phy->dev, "%s driving_level=%d => dts driving_level=%d\n", + __func__, driving_level, phy_parameter->driving_level); + + phy_cfg->page0[4].data = (data & (~dc_driving_mask)) | + (phy_parameter->driving_level & dc_driving_mask); + } + + phy_cfg->page0[4].data = __update_dc_driving_page0_0xe4(phy_cfg, + phy_parameter, + phy_cfg->page0[4].data); +} + +static void update_hs_clk_select(struct rtk_phy *rtk_phy, + struct phy_parameter *phy_parameter) +{ + struct phy_cfg *phy_cfg; + struct phy_reg *phy_reg; + + phy_cfg = rtk_phy->phy_cfg; + phy_reg = &phy_parameter->phy_reg; + + if (phy_parameter->inverse_hstx_sync_clock) { + if (!phy_cfg->page0[6].addr) { + rtk_phy_set_page(phy_reg, 0); + phy_cfg->page0[6].addr = PAGE0_0XE6; + phy_cfg->page0[6].data = rtk_phy_read(phy_reg, PAGE0_0XE6); + } + + phy_cfg->page0[6].data = phy_cfg->page0[6].data | HS_CLK_SELECT; + } +} + +static void do_rtk_phy_toggle(struct rtk_phy *rtk_phy, + int index, bool connect) +{ + struct phy_parameter *phy_parameter; + struct phy_cfg *phy_cfg; + struct phy_reg *phy_reg; + struct phy_data *phy_data_page; + u8 addr, data; + int i; + + phy_cfg = rtk_phy->phy_cfg; + phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; + phy_reg = &phy_parameter->phy_reg; + + if (!phy_cfg->do_toggle) + goto out; + + if (phy_cfg->is_double_sensitivity_mode) + goto do_toggle_driving; + + /* Set page 0 */ + rtk_phy_set_page(phy_reg, 0); + + addr = PAGE0_0XE7; + data = rtk_phy_read(phy_reg, addr); + + if (connect) + rtk_phy_write(phy_reg, addr, data & (~SENSITIVITY_CTRL)); + else + rtk_phy_write(phy_reg, addr, data | (SENSITIVITY_CTRL)); + +do_toggle_driving: + + if (!phy_cfg->do_toggle_driving) + goto do_toggle; + + /* Page 0 addr 0xE4 driving capability */ + + /* Set page 0 */ + phy_data_page = phy_cfg->page0; + rtk_phy_set_page(phy_reg, 0); + + i = page_addr_to_array_index(PAGE0_0XE4); + addr = phy_data_page[i].addr; + data = phy_data_page[i].data; + + if (connect) { + rtk_phy_write(phy_reg, addr, data); + } else { + u8 value; + s32 tmp; + s32 driving_updated = + phy_cfg->driving_updated_for_dev_dis; + s32 dc_driving_mask = phy_cfg->dc_driving_mask; + + tmp = (s32)(data & dc_driving_mask) + driving_updated; + + if (tmp > dc_driving_mask) + tmp = dc_driving_mask; + else if (tmp < 0) + tmp = 0; + + value = (data & (~dc_driving_mask)) | (tmp & dc_driving_mask); + + rtk_phy_write(phy_reg, addr, value); + } + +do_toggle: + /* restore dc disconnect level before toggle */ + update_dc_disconnect_level(rtk_phy, phy_parameter, false); + + /* Set page 1 */ + rtk_phy_set_page(phy_reg, 1); + + addr = PAGE1_0XE0; + data = rtk_phy_read(phy_reg, addr); + + rtk_phy_write(phy_reg, addr, data & + (~ENABLE_AUTO_SENSITIVITY_CALIBRATION)); + mdelay(1); + rtk_phy_write(phy_reg, addr, data | + (ENABLE_AUTO_SENSITIVITY_CALIBRATION)); + + /* update dc disconnect level after toggle */ + update_dc_disconnect_level(rtk_phy, phy_parameter, true); + +out: + return; +} + +static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index) +{ + struct phy_parameter *phy_parameter; + struct phy_cfg *phy_cfg; + struct phy_data *phy_data_page; + struct phy_reg *phy_reg; + int i; + + phy_cfg = rtk_phy->phy_cfg; + phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; + phy_reg = &phy_parameter->phy_reg; + + if (phy_cfg->use_default_parameter) { + dev_dbg(rtk_phy->dev, "%s phy#%d use default parameter\n", + __func__, index); + goto do_toggle; + } + + /* Set page 0 */ + phy_data_page = phy_cfg->page0; + rtk_phy_set_page(phy_reg, 0); + + for (i = 0; i < phy_cfg->page0_size; i++) { + struct phy_data *phy_data = phy_data_page + i; + u8 addr = phy_data->addr; + u8 data = phy_data->data; + + if (!addr) + continue; + + if (rtk_phy_write(phy_reg, addr, data)) { + dev_err(rtk_phy->dev, + "%s: Error to set page0 parameter addr=0x%x value=0x%x\n", + __func__, addr, data); + return -EINVAL; + } + } + + /* Set page 1 */ + phy_data_page = phy_cfg->page1; + rtk_phy_set_page(phy_reg, 1); + + for (i = 0; i < phy_cfg->page1_size; i++) { + struct phy_data *phy_data = phy_data_page + i; + u8 addr = phy_data->addr; + u8 data = phy_data->data; + + if (!addr) + continue; + + if (rtk_phy_write(phy_reg, addr, data)) { + dev_err(rtk_phy->dev, + "%s: Error to set page1 parameter addr=0x%x value=0x%x\n", + __func__, addr, data); + return -EINVAL; + } + } + + if (phy_cfg->page2_size == 0) + goto do_toggle; + + /* Set page 2 */ + phy_data_page = phy_cfg->page2; + rtk_phy_set_page(phy_reg, 2); + + for (i = 0; i < phy_cfg->page2_size; i++) { + struct phy_data *phy_data = phy_data_page + i; + u8 addr = phy_data->addr; + u8 data = phy_data->data; + + if (!addr) + continue; + + if (rtk_phy_write(phy_reg, addr, data)) { + dev_err(rtk_phy->dev, + "%s: Error to set page2 parameter addr=0x%x value=0x%x\n", + __func__, addr, data); + return -EINVAL; + } + } + +do_toggle: + do_rtk_phy_toggle(rtk_phy, index, false); + + return 0; +} + +static int rtk_phy_init(struct phy *phy) +{ + struct rtk_phy *rtk_phy = phy_get_drvdata(phy); + unsigned long phy_init_time = jiffies; + int i, ret = 0; + + if (!rtk_phy) + return -EINVAL; + + for (i = 0; i < rtk_phy->num_phy; i++) + ret = do_rtk_phy_init(rtk_phy, i); + + dev_dbg(rtk_phy->dev, "Initialized RTK USB 2.0 PHY (take %dms)\n", + jiffies_to_msecs(jiffies - phy_init_time)); + return ret; +} + +static int rtk_phy_exit(struct phy *phy) +{ + return 0; +} + +static const struct phy_ops ops = { + .init = rtk_phy_init, + .exit = rtk_phy_exit, + .owner = THIS_MODULE, +}; + +static void rtk_phy_toggle(struct usb_phy *usb2_phy, bool connect, int port) +{ + int index = port; + struct rtk_phy *rtk_phy = NULL; + + rtk_phy = dev_get_drvdata(usb2_phy->dev); + + if (index > rtk_phy->num_phy) { + dev_err(rtk_phy->dev, "%s: The port=%d is not in usb phy (num_phy=%d)\n", + __func__, index, rtk_phy->num_phy); + return; + } + + do_rtk_phy_toggle(rtk_phy, index, connect); +} + +static int rtk_phy_notify_port_status(struct usb_phy *x, int port, + u16 portstatus, u16 portchange) +{ + bool connect = false; + + pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n", + __func__, port, (int)portstatus, (int)portchange); + if (portstatus & USB_PORT_STAT_CONNECTION) + connect = true; + + if (portchange & USB_PORT_STAT_C_CONNECTION) + rtk_phy_toggle(x, connect, port); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static struct dentry *create_phy_debug_root(void) +{ + struct dentry *phy_debug_root; + + phy_debug_root = debugfs_lookup("phy", usb_debug_root); + if (!phy_debug_root) + phy_debug_root = debugfs_create_dir("phy", usb_debug_root); + + return phy_debug_root; +} + +static int rtk_usb2_parameter_show(struct seq_file *s, void *unused) +{ + struct rtk_phy *rtk_phy = s->private; + struct phy_cfg *phy_cfg; + int i, index; + + phy_cfg = rtk_phy->phy_cfg; + + seq_puts(s, "Property:\n"); + seq_printf(s, " check_efuse: %s\n", + phy_cfg->check_efuse ? "Enable" : "Disable"); + seq_printf(s, " check_efuse_version: %d\n", + phy_cfg->check_efuse_version); + seq_printf(s, " efuse_dc_driving_rate: %d\n", + phy_cfg->efuse_dc_driving_rate); + seq_printf(s, " dc_driving_mask: 0x%x\n", + phy_cfg->dc_driving_mask); + seq_printf(s, " efuse_dc_disconnect_rate: %d\n", + phy_cfg->efuse_dc_disconnect_rate); + seq_printf(s, " dc_disconnect_mask: 0x%x\n", + phy_cfg->dc_disconnect_mask); + seq_printf(s, " usb_dc_disconnect_at_page0: %s\n", + phy_cfg->usb_dc_disconnect_at_page0 ? "true" : "false"); + seq_printf(s, " do_toggle: %s\n", + phy_cfg->do_toggle ? "Enable" : "Disable"); + seq_printf(s, " do_toggle_driving: %s\n", + phy_cfg->do_toggle_driving ? "Enable" : "Disable"); + seq_printf(s, " driving_updated_for_dev_dis: 0x%x\n", + phy_cfg->driving_updated_for_dev_dis); + seq_printf(s, " use_default_parameter: %s\n", + phy_cfg->use_default_parameter ? "Enable" : "Disable"); + seq_printf(s, " is_double_sensitivity_mode: %s\n", + phy_cfg->is_double_sensitivity_mode ? "Enable" : "Disable"); + + for (index = 0; index < rtk_phy->num_phy; index++) { + struct phy_parameter *phy_parameter; + struct phy_reg *phy_reg; + struct phy_data *phy_data_page; + + phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; + phy_reg = &phy_parameter->phy_reg; + + seq_printf(s, "PHY %d:\n", index); + + seq_puts(s, "Page 0:\n"); + /* Set page 0 */ + phy_data_page = phy_cfg->page0; + rtk_phy_set_page(phy_reg, 0); + + for (i = 0; i < phy_cfg->page0_size; i++) { + struct phy_data *phy_data = phy_data_page + i; + u8 addr = array_index_to_page_addr(i); + u8 data = phy_data->data; + u8 value = rtk_phy_read(phy_reg, addr); + + if (phy_data->addr) + seq_printf(s, " Page 0: addr=0x%x data=0x%02x ==> read value=0x%02x\n", + addr, data, value); + else + seq_printf(s, " Page 0: addr=0x%x data=none ==> read value=0x%02x\n", + addr, value); + } + + seq_puts(s, "Page 1:\n"); + /* Set page 1 */ + phy_data_page = phy_cfg->page1; + rtk_phy_set_page(phy_reg, 1); + + for (i = 0; i < phy_cfg->page1_size; i++) { + struct phy_data *phy_data = phy_data_page + i; + u8 addr = array_index_to_page_addr(i); + u8 data = phy_data->data; + u8 value = rtk_phy_read(phy_reg, addr); + + if (phy_data->addr) + seq_printf(s, " Page 1: addr=0x%x data=0x%02x ==> read value=0x%02x\n", + addr, data, value); + else + seq_printf(s, " Page 1: addr=0x%x data=none ==> read value=0x%02x\n", + addr, value); + } + + if (phy_cfg->page2_size == 0) + goto out; + + seq_puts(s, "Page 2:\n"); + /* Set page 2 */ + phy_data_page = phy_cfg->page2; + rtk_phy_set_page(phy_reg, 2); + + for (i = 0; i < phy_cfg->page2_size; i++) { + struct phy_data *phy_data = phy_data_page + i; + u8 addr = array_index_to_page_addr(i); + u8 data = phy_data->data; + u8 value = rtk_phy_read(phy_reg, addr); + + if (phy_data->addr) + seq_printf(s, " Page 2: addr=0x%x data=0x%02x ==> read value=0x%02x\n", + addr, data, value); + else + seq_printf(s, " Page 2: addr=0x%x data=none ==> read value=0x%02x\n", + addr, value); + } + +out: + seq_puts(s, "PHY Property:\n"); + seq_printf(s, " efuse_usb_dc_cal: %d\n", + (int)phy_parameter->efuse_usb_dc_cal); + seq_printf(s, " efuse_usb_dc_dis: %d\n", + (int)phy_parameter->efuse_usb_dc_dis); + seq_printf(s, " inverse_hstx_sync_clock: %s\n", + phy_parameter->inverse_hstx_sync_clock ? "Enable" : "Disable"); + seq_printf(s, " driving_level: %d\n", + phy_parameter->driving_level); + seq_printf(s, " driving_level_compensate: %d\n", + phy_parameter->driving_level_compensate); + seq_printf(s, " disconnection_compensate: %d\n", + phy_parameter->disconnection_compensate); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(rtk_usb2_parameter); + +static inline void create_debug_files(struct rtk_phy *rtk_phy) +{ + struct dentry *phy_debug_root = NULL; + + phy_debug_root = create_phy_debug_root(); + if (!phy_debug_root) + return; + + rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev), + phy_debug_root); + if (!rtk_phy->debug_dir) + return; + + if (!debugfs_create_file("parameter", 0444, rtk_phy->debug_dir, rtk_phy, + &rtk_usb2_parameter_fops)) + goto file_error; + + return; + +file_error: + debugfs_remove_recursive(rtk_phy->debug_dir); +} + +static inline void remove_debug_files(struct rtk_phy *rtk_phy) +{ + debugfs_remove_recursive(rtk_phy->debug_dir); +} +#else +static inline void create_debug_files(struct rtk_phy *rtk_phy) { } +static inline void remove_debug_files(struct rtk_phy *rtk_phy) { } +#endif /* CONFIG_DEBUG_FS */ + +static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy, + struct phy_parameter *phy_parameter, int index) +{ + struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; + u8 value = 0; + struct nvmem_cell *cell; + struct soc_device_attribute rtk_soc_groot[] = { + { .family = "Realtek Groot",}, + { /* empty */ } }; + + if (!phy_cfg->check_efuse) + goto out; + + /* Read efuse for usb dc cal */ + cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-cal"); + if (IS_ERR(cell)) { + dev_dbg(rtk_phy->dev, "%s no usb-dc-cal: %ld\n", + __func__, PTR_ERR(cell)); + } else { + unsigned char *buf; + size_t buf_size; + + buf = nvmem_cell_read(cell, &buf_size); + value = buf[0] & phy_cfg->dc_driving_mask; + + kfree(buf); + nvmem_cell_put(cell); + } + + if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { + int rate = phy_cfg->efuse_dc_driving_rate; + + if (value <= EFUS_USB_DC_CAL_MAX) + phy_parameter->efuse_usb_dc_cal = (int8_t)(value * rate); + else + phy_parameter->efuse_usb_dc_cal = -(int8_t) + ((EFUS_USB_DC_CAL_MAX & value) * rate); + + if (soc_device_match(rtk_soc_groot)) { + dev_dbg(rtk_phy->dev, "For groot IC we need a workaround to adjust efuse_usb_dc_cal\n"); + + /* We don't multiple dc_cal_rate=2 for positive dc cal compensate */ + if (value <= EFUS_USB_DC_CAL_MAX) + phy_parameter->efuse_usb_dc_cal = (int8_t)(value); + + /* We set max dc cal compensate is 0x8 if otp is 0x7 */ + if (value == 0x7) + phy_parameter->efuse_usb_dc_cal = (int8_t)(value + 1); + } + } else { /* for CHECK_EFUSE_V2 */ + phy_parameter->efuse_usb_dc_cal = value & phy_cfg->dc_driving_mask; + } + + /* Read efuse for usb dc disconnect level */ + value = 0; + cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-dis"); + if (IS_ERR(cell)) { + dev_dbg(rtk_phy->dev, "%s no usb-dc-dis: %ld\n", + __func__, PTR_ERR(cell)); + } else { + unsigned char *buf; + size_t buf_size; + + buf = nvmem_cell_read(cell, &buf_size); + value = buf[0] & phy_cfg->dc_disconnect_mask; + + kfree(buf); + nvmem_cell_put(cell); + } + + if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { + int rate = phy_cfg->efuse_dc_disconnect_rate; + + if (value <= EFUS_USB_DC_DIS_MAX) + phy_parameter->efuse_usb_dc_dis = (int8_t)(value * rate); + else + phy_parameter->efuse_usb_dc_dis = -(int8_t) + ((EFUS_USB_DC_DIS_MAX & value) * rate); + } else { /* for CHECK_EFUSE_V2 */ + phy_parameter->efuse_usb_dc_dis = value & phy_cfg->dc_disconnect_mask; + } + +out: + return 0; +} + +static int parse_phy_data(struct rtk_phy *rtk_phy) +{ + struct device *dev = rtk_phy->dev; + struct device_node *np = dev->of_node; + struct phy_parameter *phy_parameter; + int ret = 0; + int index; + + rtk_phy->phy_parameter = devm_kzalloc(dev, sizeof(struct phy_parameter) * + rtk_phy->num_phy, GFP_KERNEL); + if (!rtk_phy->phy_parameter) + return -ENOMEM; + + for (index = 0; index < rtk_phy->num_phy; index++) { + phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; + + phy_parameter->phy_reg.reg_wrap_vstatus = of_iomap(np, 0); + phy_parameter->phy_reg.reg_gusb2phyacc0 = of_iomap(np, 1) + index; + phy_parameter->phy_reg.vstatus_index = index; + + if (of_property_read_bool(np, "realtek,inverse-hstx-sync-clock")) + phy_parameter->inverse_hstx_sync_clock = true; + else + phy_parameter->inverse_hstx_sync_clock = false; + + if (of_property_read_u32_index(np, "realtek,driving-level", + index, &phy_parameter->driving_level)) + phy_parameter->driving_level = DEFAULT_DC_DRIVING_VALUE; + + if (of_property_read_u32_index(np, "realtek,driving-level-compensate", + index, &phy_parameter->driving_level_compensate)) + phy_parameter->driving_level_compensate = 0; + + if (of_property_read_u32_index(np, "realtek,disconnection-compensate", + index, &phy_parameter->disconnection_compensate)) + phy_parameter->disconnection_compensate = 0; + + get_phy_data_by_efuse(rtk_phy, phy_parameter, index); + + update_dc_driving_level(rtk_phy, phy_parameter); + + update_hs_clk_select(rtk_phy, phy_parameter); + } + + return ret; +} + +static int rtk_usb2phy_probe(struct platform_device *pdev) +{ + struct rtk_phy *rtk_phy; + struct device *dev = &pdev->dev; + struct phy *generic_phy; + struct phy_provider *phy_provider; + const struct phy_cfg *phy_cfg; + int ret = 0; + + phy_cfg = of_device_get_match_data(dev); + if (!phy_cfg) { + dev_err(dev, "phy config are not assigned!\n"); + return -EINVAL; + } + + rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL); + if (!rtk_phy) + return -ENOMEM; + + rtk_phy->dev = &pdev->dev; + rtk_phy->phy.dev = rtk_phy->dev; + rtk_phy->phy.label = "rtk-usb2phy"; + rtk_phy->phy.notify_port_status = rtk_phy_notify_port_status; + + rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL); + + memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg)); + + rtk_phy->num_phy = phy_cfg->num_phy; + + ret = parse_phy_data(rtk_phy); + if (ret) + goto err; + + platform_set_drvdata(pdev, rtk_phy); + + generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops); + if (IS_ERR(generic_phy)) + return PTR_ERR(generic_phy); + + phy_set_drvdata(generic_phy, rtk_phy); + + phy_provider = devm_of_phy_provider_register(rtk_phy->dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + ret = usb_add_phy_dev(&rtk_phy->phy); + if (ret) + goto err; + + create_debug_files(rtk_phy); + +err: + return ret; +} + +static void rtk_usb2phy_remove(struct platform_device *pdev) +{ + struct rtk_phy *rtk_phy = platform_get_drvdata(pdev); + + remove_debug_files(rtk_phy); + + usb_remove_phy(&rtk_phy->phy); +} + +static const struct phy_cfg rtd1295_phy_cfg = { + .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, + .page0 = { [0] = {0xe0, 0x90}, + [3] = {0xe3, 0x3a}, + [4] = {0xe4, 0x68}, + [6] = {0xe6, 0x91}, + [13] = {0xf5, 0x81}, + [15] = {0xf7, 0x02}, }, + .page1_size = 8, + .page1 = { /* default parameter */ }, + .page2_size = 0, + .page2 = { /* no parameter */ }, + .num_phy = 1, + .check_efuse = false, + .check_efuse_version = CHECK_EFUSE_V1, + .efuse_dc_driving_rate = 1, + .dc_driving_mask = 0xf, + .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, + .dc_disconnect_mask = 0xf, + .usb_dc_disconnect_at_page0 = true, + .do_toggle = true, + .do_toggle_driving = false, + .driving_updated_for_dev_dis = 0xf, + .use_default_parameter = false, + .is_double_sensitivity_mode = false, +}; + +static const struct phy_cfg rtd1395_phy_cfg = { + .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, + .page0 = { [4] = {0xe4, 0xac}, + [13] = {0xf5, 0x00}, + [15] = {0xf7, 0x02}, }, + .page1_size = 8, + .page1 = { /* default parameter */ }, + .page2_size = 0, + .page2 = { /* no parameter */ }, + .num_phy = 1, + .check_efuse = false, + .check_efuse_version = CHECK_EFUSE_V1, + .efuse_dc_driving_rate = 1, + .dc_driving_mask = 0xf, + .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, + .dc_disconnect_mask = 0xf, + .usb_dc_disconnect_at_page0 = true, + .do_toggle = true, + .do_toggle_driving = false, + .driving_updated_for_dev_dis = 0xf, + .use_default_parameter = false, + .is_double_sensitivity_mode = false, +}; + +static const struct phy_cfg rtd1395_phy_cfg_2port = { + .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, + .page0 = { [4] = {0xe4, 0xac}, + [13] = {0xf5, 0x00}, + [15] = {0xf7, 0x02}, }, + .page1_size = 8, + .page1 = { /* default parameter */ }, + .page2_size = 0, + .page2 = { /* no parameter */ }, + .num_phy = 2, + .check_efuse = false, + .check_efuse_version = CHECK_EFUSE_V1, + .efuse_dc_driving_rate = 1, + .dc_driving_mask = 0xf, + .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, + .dc_disconnect_mask = 0xf, + .usb_dc_disconnect_at_page0 = true, + .do_toggle = true, + .do_toggle_driving = false, + .driving_updated_for_dev_dis = 0xf, + .use_default_parameter = false, + .is_double_sensitivity_mode = false, +}; + +static const struct phy_cfg rtd1619_phy_cfg = { + .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, + .page0 = { [4] = {0xe4, 0x68}, }, + .page1_size = 8, + .page1 = { /* default parameter */ }, + .page2_size = 0, + .page2 = { /* no parameter */ }, + .num_phy = 1, + .check_efuse = true, + .check_efuse_version = CHECK_EFUSE_V1, + .efuse_dc_driving_rate = 1, + .dc_driving_mask = 0xf, + .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, + .dc_disconnect_mask = 0xf, + .usb_dc_disconnect_at_page0 = true, + .do_toggle = true, + .do_toggle_driving = false, + .driving_updated_for_dev_dis = 0xf, + .use_default_parameter = false, + .is_double_sensitivity_mode = false, +}; + +static const struct phy_cfg rtd1319_phy_cfg = { + .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, + .page0 = { [0] = {0xe0, 0x18}, + [4] = {0xe4, 0x6a}, + [7] = {0xe7, 0x71}, + [13] = {0xf5, 0x15}, + [15] = {0xf7, 0x32}, }, + .page1_size = 8, + .page1 = { [3] = {0xe3, 0x44}, }, + .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, + .page2 = { [0] = {0xe0, 0x01}, }, + .num_phy = 1, + .check_efuse = true, + .check_efuse_version = CHECK_EFUSE_V1, + .efuse_dc_driving_rate = 1, + .dc_driving_mask = 0xf, + .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, + .dc_disconnect_mask = 0xf, + .usb_dc_disconnect_at_page0 = true, + .do_toggle = true, + .do_toggle_driving = true, + .driving_updated_for_dev_dis = 0xf, + .use_default_parameter = false, + .is_double_sensitivity_mode = true, +}; + +static const struct phy_cfg rtd1312c_phy_cfg = { + .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, + .page0 = { [0] = {0xe0, 0x14}, + [4] = {0xe4, 0x67}, + [5] = {0xe5, 0x55}, }, + .page1_size = 8, + .page1 = { [3] = {0xe3, 0x23}, + [6] = {0xe6, 0x58}, }, + .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, + .page2 = { /* default parameter */ }, + .num_phy = 1, + .check_efuse = true, + .check_efuse_version = CHECK_EFUSE_V1, + .efuse_dc_driving_rate = 1, + .dc_driving_mask = 0xf, + .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, + .dc_disconnect_mask = 0xf, + .usb_dc_disconnect_at_page0 = true, + .do_toggle = true, + .do_toggle_driving = true, + .driving_updated_for_dev_dis = 0xf, + .use_default_parameter = false, + .is_double_sensitivity_mode = true, +}; + +static const struct phy_cfg rtd1619b_phy_cfg = { + .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, + .page0 = { [0] = {0xe0, 0xa3}, + [4] = {0xe4, 0x88}, + [5] = {0xe5, 0x4f}, + [6] = {0xe6, 0x02}, }, + .page1_size = 8, + .page1 = { [3] = {0xe3, 0x64}, }, + .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, + .page2 = { [7] = {0xe7, 0x45}, }, + .num_phy = 1, + .check_efuse = true, + .check_efuse_version = CHECK_EFUSE_V1, + .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE, + .dc_driving_mask = 0x1f, + .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, + .dc_disconnect_mask = 0xf, + .usb_dc_disconnect_at_page0 = false, + .do_toggle = true, + .do_toggle_driving = true, + .driving_updated_for_dev_dis = 0x8, + .use_default_parameter = false, + .is_double_sensitivity_mode = true, +}; + +static const struct phy_cfg rtd1319d_phy_cfg = { + .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, + .page0 = { [0] = {0xe0, 0xa3}, + [4] = {0xe4, 0x8e}, + [5] = {0xe5, 0x4f}, + [6] = {0xe6, 0x02}, }, + .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE, + .page1 = { [14] = {0xf5, 0x1}, }, + .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, + .page2 = { [7] = {0xe7, 0x44}, }, + .check_efuse = true, + .num_phy = 1, + .check_efuse_version = CHECK_EFUSE_V1, + .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE, + .dc_driving_mask = 0x1f, + .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, + .dc_disconnect_mask = 0xf, + .usb_dc_disconnect_at_page0 = false, + .do_toggle = true, + .do_toggle_driving = false, + .driving_updated_for_dev_dis = 0x8, + .use_default_parameter = false, + .is_double_sensitivity_mode = true, +}; + +static const struct phy_cfg rtd1315e_phy_cfg = { + .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, + .page0 = { [0] = {0xe0, 0xa3}, + [4] = {0xe4, 0x8c}, + [5] = {0xe5, 0x4f}, + [6] = {0xe6, 0x02}, }, + .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE, + .page1 = { [3] = {0xe3, 0x7f}, + [14] = {0xf5, 0x01}, }, + .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, + .page2 = { [7] = {0xe7, 0x44}, }, + .num_phy = 1, + .check_efuse = true, + .check_efuse_version = CHECK_EFUSE_V2, + .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE, + .dc_driving_mask = 0x1f, + .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, + .dc_disconnect_mask = 0xf, + .usb_dc_disconnect_at_page0 = false, + .do_toggle = true, + .do_toggle_driving = false, + .driving_updated_for_dev_dis = 0x8, + .use_default_parameter = false, + .is_double_sensitivity_mode = true, +}; + +static const struct of_device_id usbphy_rtk_dt_match[] = { + { .compatible = "realtek,rtd1295-usb2phy", .data = &rtd1295_phy_cfg }, + { .compatible = "realtek,rtd1312c-usb2phy", .data = &rtd1312c_phy_cfg }, + { .compatible = "realtek,rtd1315e-usb2phy", .data = &rtd1315e_phy_cfg }, + { .compatible = "realtek,rtd1319-usb2phy", .data = &rtd1319_phy_cfg }, + { .compatible = "realtek,rtd1319d-usb2phy", .data = &rtd1319d_phy_cfg }, + { .compatible = "realtek,rtd1395-usb2phy", .data = &rtd1395_phy_cfg }, + { .compatible = "realtek,rtd1395-usb2phy-2port", .data = &rtd1395_phy_cfg_2port }, + { .compatible = "realtek,rtd1619-usb2phy", .data = &rtd1619_phy_cfg }, + { .compatible = "realtek,rtd1619b-usb2phy", .data = &rtd1619b_phy_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match); + +static struct platform_driver rtk_usb2phy_driver = { + .probe = rtk_usb2phy_probe, + .remove_new = rtk_usb2phy_remove, + .driver = { + .name = "rtk-usb2phy", + .of_match_table = usbphy_rtk_dt_match, + }, +}; + +module_platform_driver(rtk_usb2phy_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform: rtk-usb2phy"); +MODULE_AUTHOR("Stanley Chang "); +MODULE_DESCRIPTION("Realtek usb 2.0 phy driver"); -- cgit From adda6e82a7de7d6d478f6c8ef127f0ac51c510a1 Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Tue, 25 Jul 2023 11:31:54 +0800 Subject: phy: realtek: usb: Add driver for the Realtek SoC USB 3.0 PHY Realtek DHC (digital home center) RTD SoCs support DWC3 XHCI USB controller. Added the driver to drive the USB 3.0 PHY transceivers. Signed-off-by: Stanley Chang Link: https://lore.kernel.org/r/20230725033318.8361-3-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/phy/realtek/Kconfig | 11 + drivers/phy/realtek/Makefile | 1 + drivers/phy/realtek/phy-rtk-usb3.c | 766 +++++++++++++++++++++++++++++++++++++ 3 files changed, 778 insertions(+) create mode 100644 drivers/phy/realtek/phy-rtk-usb3.c diff --git a/drivers/phy/realtek/Kconfig b/drivers/phy/realtek/Kconfig index 2f2f453729d5..a5a5a71edc9c 100644 --- a/drivers/phy/realtek/Kconfig +++ b/drivers/phy/realtek/Kconfig @@ -12,3 +12,14 @@ config PHY_RTK_RTD_USB2PHY The DHC (digital home center) RTD series SoCs used the Synopsys DWC3 USB IP. This driver will do the PHY initialization of the parameters. + +config PHY_RTK_RTD_USB3PHY + tristate "Realtek RTD USB3 PHY Transceiver Driver" + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this to support Realtek SoC USB3 phy transceiver. + The DHC (digital home center) RTD series SoCs used the Synopsys + DWC3 USB IP. This driver will do the PHY initialization + of the parameters. diff --git a/drivers/phy/realtek/Makefile b/drivers/phy/realtek/Makefile index cf5d440841a2..ed7b47ff8a26 100644 --- a/drivers/phy/realtek/Makefile +++ b/drivers/phy/realtek/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PHY_RTK_RTD_USB2PHY) += phy-rtk-usb2.o +obj-$(CONFIG_PHY_RTK_RTD_USB3PHY) += phy-rtk-usb3.o diff --git a/drivers/phy/realtek/phy-rtk-usb3.c b/drivers/phy/realtek/phy-rtk-usb3.c new file mode 100644 index 000000000000..6050f1ef4f6b --- /dev/null +++ b/drivers/phy/realtek/phy-rtk-usb3.c @@ -0,0 +1,766 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-rtk-usb3.c RTK usb3.0 phy driver + * + * copyright (c) 2023 realtek semiconductor corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_MDIO_CTRL_PHY_BUSY BIT(7) +#define USB_MDIO_CTRL_PHY_WRITE BIT(0) +#define USB_MDIO_CTRL_PHY_ADDR_SHIFT 8 +#define USB_MDIO_CTRL_PHY_DATA_SHIFT 16 + +#define MAX_USB_PHY_DATA_SIZE 0x30 +#define PHY_ADDR_0X09 0x09 +#define PHY_ADDR_0X0B 0x0b +#define PHY_ADDR_0X0D 0x0d +#define PHY_ADDR_0X10 0x10 +#define PHY_ADDR_0X1F 0x1f +#define PHY_ADDR_0X20 0x20 +#define PHY_ADDR_0X21 0x21 +#define PHY_ADDR_0X30 0x30 + +#define REG_0X09_FORCE_CALIBRATION BIT(9) +#define REG_0X0B_RX_OFFSET_RANGE_MASK 0xc +#define REG_0X0D_RX_DEBUG_TEST_EN BIT(6) +#define REG_0X10_DEBUG_MODE_SETTING 0x3c0 +#define REG_0X10_DEBUG_MODE_SETTING_MASK 0x3f8 +#define REG_0X1F_RX_OFFSET_CODE_MASK 0x1e + +#define USB_U3_TX_LFPS_SWING_TRIM_SHIFT 4 +#define USB_U3_TX_LFPS_SWING_TRIM_MASK 0xf +#define AMPLITUDE_CONTROL_COARSE_MASK 0xff +#define AMPLITUDE_CONTROL_FINE_MASK 0xffff +#define AMPLITUDE_CONTROL_COARSE_DEFAULT 0xff +#define AMPLITUDE_CONTROL_FINE_DEFAULT 0xffff + +#define PHY_ADDR_MAP_ARRAY_INDEX(addr) (addr) +#define ARRAY_INDEX_MAP_PHY_ADDR(index) (index) + +struct phy_reg { + void __iomem *reg_mdio_ctl; +}; + +struct phy_data { + u8 addr; + u16 data; +}; + +struct phy_cfg { + int param_size; + struct phy_data param[MAX_USB_PHY_DATA_SIZE]; + + bool check_efuse; + bool do_toggle; + bool do_toggle_once; + bool use_default_parameter; + bool check_rx_front_end_offset; +}; + +struct phy_parameter { + struct phy_reg phy_reg; + + /* Get from efuse */ + u8 efuse_usb_u3_tx_lfps_swing_trim; + + /* Get from dts */ + u32 amplitude_control_coarse; + u32 amplitude_control_fine; +}; + +struct rtk_phy { + struct usb_phy phy; + struct device *dev; + + struct phy_cfg *phy_cfg; + int num_phy; + struct phy_parameter *phy_parameter; + + struct dentry *debug_dir; +}; + +#define PHY_IO_TIMEOUT_USEC (50000) +#define PHY_IO_DELAY_US (100) + +static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) +{ + int ret; + unsigned int val; + + ret = read_poll_timeout(readl, val, ((val & mask) == result), + PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg); + if (ret) { + pr_err("%s can't program USB phy\n", __func__); + return -ETIMEDOUT; + } + + return 0; +} + +static int rtk_phy3_wait_vbusy(struct phy_reg *phy_reg) +{ + return utmi_wait_register(phy_reg->reg_mdio_ctl, USB_MDIO_CTRL_PHY_BUSY, 0); +} + +static u16 rtk_phy_read(struct phy_reg *phy_reg, char addr) +{ + unsigned int tmp; + u32 value; + + tmp = (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT); + + writel(tmp, phy_reg->reg_mdio_ctl); + + rtk_phy3_wait_vbusy(phy_reg); + + value = readl(phy_reg->reg_mdio_ctl); + value = value >> USB_MDIO_CTRL_PHY_DATA_SHIFT; + + return (u16)value; +} + +static int rtk_phy_write(struct phy_reg *phy_reg, char addr, u16 data) +{ + unsigned int val; + + val = USB_MDIO_CTRL_PHY_WRITE | + (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT) | + (data << USB_MDIO_CTRL_PHY_DATA_SHIFT); + + writel(val, phy_reg->reg_mdio_ctl); + + rtk_phy3_wait_vbusy(phy_reg); + + return 0; +} + +static void do_rtk_usb3_phy_toggle(struct rtk_phy *rtk_phy, int index, bool connect) +{ + struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; + struct phy_reg *phy_reg; + struct phy_parameter *phy_parameter; + struct phy_data *phy_data; + u8 addr; + u16 data; + int i; + + phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; + phy_reg = &phy_parameter->phy_reg; + + if (!phy_cfg->do_toggle) + return; + + i = PHY_ADDR_MAP_ARRAY_INDEX(PHY_ADDR_0X09); + phy_data = phy_cfg->param + i; + addr = phy_data->addr; + data = phy_data->data; + + if (!addr && !data) { + addr = PHY_ADDR_0X09; + data = rtk_phy_read(phy_reg, addr); + phy_data->addr = addr; + phy_data->data = data; + } + + rtk_phy_write(phy_reg, addr, data & (~REG_0X09_FORCE_CALIBRATION)); + mdelay(1); + rtk_phy_write(phy_reg, addr, data | REG_0X09_FORCE_CALIBRATION); +} + +static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index) +{ + struct phy_cfg *phy_cfg; + struct phy_reg *phy_reg; + struct phy_parameter *phy_parameter; + int i = 0; + + phy_cfg = rtk_phy->phy_cfg; + phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; + phy_reg = &phy_parameter->phy_reg; + + if (phy_cfg->use_default_parameter) + goto do_toggle; + + for (i = 0; i < phy_cfg->param_size; i++) { + struct phy_data *phy_data = phy_cfg->param + i; + u8 addr = phy_data->addr; + u16 data = phy_data->data; + + if (!addr && !data) + continue; + + rtk_phy_write(phy_reg, addr, data); + } + +do_toggle: + if (phy_cfg->do_toggle_once) + phy_cfg->do_toggle = true; + + do_rtk_usb3_phy_toggle(rtk_phy, index, false); + + if (phy_cfg->do_toggle_once) { + u16 check_value = 0; + int count = 10; + u16 value_0x0d, value_0x10; + + /* Enable Debug mode by set 0x0D and 0x10 */ + value_0x0d = rtk_phy_read(phy_reg, PHY_ADDR_0X0D); + value_0x10 = rtk_phy_read(phy_reg, PHY_ADDR_0X10); + + rtk_phy_write(phy_reg, PHY_ADDR_0X0D, + value_0x0d | REG_0X0D_RX_DEBUG_TEST_EN); + rtk_phy_write(phy_reg, PHY_ADDR_0X10, + (value_0x10 & ~REG_0X10_DEBUG_MODE_SETTING_MASK) | + REG_0X10_DEBUG_MODE_SETTING); + + check_value = rtk_phy_read(phy_reg, PHY_ADDR_0X30); + + while (!(check_value & BIT(15))) { + check_value = rtk_phy_read(phy_reg, PHY_ADDR_0X30); + mdelay(1); + if (count-- < 0) + break; + } + + if (!(check_value & BIT(15))) + dev_info(rtk_phy->dev, "toggle fail addr=0x%02x, data=0x%04x\n", + PHY_ADDR_0X30, check_value); + + /* Disable Debug mode by set 0x0D and 0x10 to default*/ + rtk_phy_write(phy_reg, PHY_ADDR_0X0D, value_0x0d); + rtk_phy_write(phy_reg, PHY_ADDR_0X10, value_0x10); + + phy_cfg->do_toggle = false; + } + + if (phy_cfg->check_rx_front_end_offset) { + u16 rx_offset_code, rx_offset_range; + u16 code_mask = REG_0X1F_RX_OFFSET_CODE_MASK; + u16 range_mask = REG_0X0B_RX_OFFSET_RANGE_MASK; + bool do_update = false; + + rx_offset_code = rtk_phy_read(phy_reg, PHY_ADDR_0X1F); + if (((rx_offset_code & code_mask) == 0x0) || + ((rx_offset_code & code_mask) == code_mask)) + do_update = true; + + rx_offset_range = rtk_phy_read(phy_reg, PHY_ADDR_0X0B); + if (((rx_offset_range & range_mask) == range_mask) && do_update) { + dev_warn(rtk_phy->dev, "Don't update rx_offset_range (rx_offset_code=0x%x, rx_offset_range=0x%x)\n", + rx_offset_code, rx_offset_range); + do_update = false; + } + + if (do_update) { + u16 tmp1, tmp2; + + tmp1 = rx_offset_range & (~range_mask); + tmp2 = rx_offset_range & range_mask; + tmp2 += (1 << 2); + rx_offset_range = tmp1 | (tmp2 & range_mask); + rtk_phy_write(phy_reg, PHY_ADDR_0X0B, rx_offset_range); + goto do_toggle; + } + } + + return 0; +} + +static int rtk_phy_init(struct phy *phy) +{ + struct rtk_phy *rtk_phy = phy_get_drvdata(phy); + int ret = 0; + int i; + unsigned long phy_init_time = jiffies; + + for (i = 0; i < rtk_phy->num_phy; i++) + ret = do_rtk_phy_init(rtk_phy, i); + + dev_dbg(rtk_phy->dev, "Initialized RTK USB 3.0 PHY (take %dms)\n", + jiffies_to_msecs(jiffies - phy_init_time)); + + return ret; +} + +static int rtk_phy_exit(struct phy *phy) +{ + return 0; +} + +static const struct phy_ops ops = { + .init = rtk_phy_init, + .exit = rtk_phy_exit, + .owner = THIS_MODULE, +}; + +static void rtk_phy_toggle(struct usb_phy *usb3_phy, bool connect, int port) +{ + int index = port; + struct rtk_phy *rtk_phy = NULL; + + rtk_phy = dev_get_drvdata(usb3_phy->dev); + + if (index > rtk_phy->num_phy) { + dev_err(rtk_phy->dev, "%s: The port=%d is not in usb phy (num_phy=%d)\n", + __func__, index, rtk_phy->num_phy); + return; + } + + do_rtk_usb3_phy_toggle(rtk_phy, index, connect); +} + +static int rtk_phy_notify_port_status(struct usb_phy *x, int port, + u16 portstatus, u16 portchange) +{ + bool connect = false; + + pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n", + __func__, port, (int)portstatus, (int)portchange); + if (portstatus & USB_PORT_STAT_CONNECTION) + connect = true; + + if (portchange & USB_PORT_STAT_C_CONNECTION) + rtk_phy_toggle(x, connect, port); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static struct dentry *create_phy_debug_root(void) +{ + struct dentry *phy_debug_root; + + phy_debug_root = debugfs_lookup("phy", usb_debug_root); + if (!phy_debug_root) + phy_debug_root = debugfs_create_dir("phy", usb_debug_root); + + return phy_debug_root; +} + +static int rtk_usb3_parameter_show(struct seq_file *s, void *unused) +{ + struct rtk_phy *rtk_phy = s->private; + struct phy_cfg *phy_cfg; + int i, index; + + phy_cfg = rtk_phy->phy_cfg; + + seq_puts(s, "Property:\n"); + seq_printf(s, " check_efuse: %s\n", + phy_cfg->check_efuse ? "Enable" : "Disable"); + seq_printf(s, " do_toggle: %s\n", + phy_cfg->do_toggle ? "Enable" : "Disable"); + seq_printf(s, " do_toggle_once: %s\n", + phy_cfg->do_toggle_once ? "Enable" : "Disable"); + seq_printf(s, " use_default_parameter: %s\n", + phy_cfg->use_default_parameter ? "Enable" : "Disable"); + + for (index = 0; index < rtk_phy->num_phy; index++) { + struct phy_reg *phy_reg; + struct phy_parameter *phy_parameter; + + phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; + phy_reg = &phy_parameter->phy_reg; + + seq_printf(s, "PHY %d:\n", index); + + for (i = 0; i < phy_cfg->param_size; i++) { + struct phy_data *phy_data = phy_cfg->param + i; + u8 addr = ARRAY_INDEX_MAP_PHY_ADDR(i); + u16 data = phy_data->data; + + if (!phy_data->addr && !data) + seq_printf(s, " addr = 0x%02x, data = none ==> read value = 0x%04x\n", + addr, rtk_phy_read(phy_reg, addr)); + else + seq_printf(s, " addr = 0x%02x, data = 0x%04x ==> read value = 0x%04x\n", + addr, data, rtk_phy_read(phy_reg, addr)); + } + + seq_puts(s, "PHY Property:\n"); + seq_printf(s, " efuse_usb_u3_tx_lfps_swing_trim: 0x%x\n", + (int)phy_parameter->efuse_usb_u3_tx_lfps_swing_trim); + seq_printf(s, " amplitude_control_coarse: 0x%x\n", + (int)phy_parameter->amplitude_control_coarse); + seq_printf(s, " amplitude_control_fine: 0x%x\n", + (int)phy_parameter->amplitude_control_fine); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(rtk_usb3_parameter); + +static inline void create_debug_files(struct rtk_phy *rtk_phy) +{ + struct dentry *phy_debug_root = NULL; + + phy_debug_root = create_phy_debug_root(); + + if (!phy_debug_root) + return; + + rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev), phy_debug_root); + if (!rtk_phy->debug_dir) + return; + + if (!debugfs_create_file("parameter", 0444, rtk_phy->debug_dir, rtk_phy, + &rtk_usb3_parameter_fops)) + goto file_error; + + return; + +file_error: + debugfs_remove_recursive(rtk_phy->debug_dir); +} + +static inline void remove_debug_files(struct rtk_phy *rtk_phy) +{ + debugfs_remove_recursive(rtk_phy->debug_dir); +} +#else +static inline void create_debug_files(struct rtk_phy *rtk_phy) { } +static inline void remove_debug_files(struct rtk_phy *rtk_phy) { } +#endif /* CONFIG_DEBUG_FS */ + +static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy, + struct phy_parameter *phy_parameter, int index) +{ + struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; + u8 value = 0; + struct nvmem_cell *cell; + + if (!phy_cfg->check_efuse) + goto out; + + cell = nvmem_cell_get(rtk_phy->dev, "usb_u3_tx_lfps_swing_trim"); + if (IS_ERR(cell)) { + dev_dbg(rtk_phy->dev, "%s no usb_u3_tx_lfps_swing_trim: %ld\n", + __func__, PTR_ERR(cell)); + } else { + unsigned char *buf; + size_t buf_size; + + buf = nvmem_cell_read(cell, &buf_size); + value = buf[0] & USB_U3_TX_LFPS_SWING_TRIM_MASK; + + kfree(buf); + nvmem_cell_put(cell); + } + + if (value > 0 && value < 0x8) + phy_parameter->efuse_usb_u3_tx_lfps_swing_trim = 0x8; + else + phy_parameter->efuse_usb_u3_tx_lfps_swing_trim = (u8)value; + +out: + return 0; +} + +static void update_amplitude_control_value(struct rtk_phy *rtk_phy, + struct phy_parameter *phy_parameter) +{ + struct phy_cfg *phy_cfg; + struct phy_reg *phy_reg; + + phy_reg = &phy_parameter->phy_reg; + phy_cfg = rtk_phy->phy_cfg; + + if (phy_parameter->amplitude_control_coarse != AMPLITUDE_CONTROL_COARSE_DEFAULT) { + u16 val_mask = AMPLITUDE_CONTROL_COARSE_MASK; + u16 data; + + if (!phy_cfg->param[PHY_ADDR_0X20].addr && !phy_cfg->param[PHY_ADDR_0X20].data) { + phy_cfg->param[PHY_ADDR_0X20].addr = PHY_ADDR_0X20; + data = rtk_phy_read(phy_reg, PHY_ADDR_0X20); + } else { + data = phy_cfg->param[PHY_ADDR_0X20].data; + } + + data &= (~val_mask); + data |= (phy_parameter->amplitude_control_coarse & val_mask); + + phy_cfg->param[PHY_ADDR_0X20].data = data; + } + + if (phy_parameter->efuse_usb_u3_tx_lfps_swing_trim) { + u8 efuse_val = phy_parameter->efuse_usb_u3_tx_lfps_swing_trim; + u16 val_mask = USB_U3_TX_LFPS_SWING_TRIM_MASK; + int val_shift = USB_U3_TX_LFPS_SWING_TRIM_SHIFT; + u16 data; + + if (!phy_cfg->param[PHY_ADDR_0X20].addr && !phy_cfg->param[PHY_ADDR_0X20].data) { + phy_cfg->param[PHY_ADDR_0X20].addr = PHY_ADDR_0X20; + data = rtk_phy_read(phy_reg, PHY_ADDR_0X20); + } else { + data = phy_cfg->param[PHY_ADDR_0X20].data; + } + + data &= ~(val_mask << val_shift); + data |= ((efuse_val & val_mask) << val_shift); + + phy_cfg->param[PHY_ADDR_0X20].data = data; + } + + if (phy_parameter->amplitude_control_fine != AMPLITUDE_CONTROL_FINE_DEFAULT) { + u16 val_mask = AMPLITUDE_CONTROL_FINE_MASK; + + if (!phy_cfg->param[PHY_ADDR_0X21].addr && !phy_cfg->param[PHY_ADDR_0X21].data) + phy_cfg->param[PHY_ADDR_0X21].addr = PHY_ADDR_0X21; + + phy_cfg->param[PHY_ADDR_0X21].data = + phy_parameter->amplitude_control_fine & val_mask; + } +} + +static int parse_phy_data(struct rtk_phy *rtk_phy) +{ + struct device *dev = rtk_phy->dev; + struct phy_parameter *phy_parameter; + int ret = 0; + int index; + + rtk_phy->phy_parameter = devm_kzalloc(dev, sizeof(struct phy_parameter) * + rtk_phy->num_phy, GFP_KERNEL); + if (!rtk_phy->phy_parameter) + return -ENOMEM; + + for (index = 0; index < rtk_phy->num_phy; index++) { + phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; + + phy_parameter->phy_reg.reg_mdio_ctl = of_iomap(dev->of_node, 0) + index; + + /* Amplitude control address 0x20 bit 0 to bit 7 */ + if (of_property_read_u32(dev->of_node, "realtek,amplitude-control-coarse-tuning", + &phy_parameter->amplitude_control_coarse)) + phy_parameter->amplitude_control_coarse = AMPLITUDE_CONTROL_COARSE_DEFAULT; + + /* Amplitude control address 0x21 bit 0 to bit 16 */ + if (of_property_read_u32(dev->of_node, "realtek,amplitude-control-fine-tuning", + &phy_parameter->amplitude_control_fine)) + phy_parameter->amplitude_control_fine = AMPLITUDE_CONTROL_FINE_DEFAULT; + + get_phy_data_by_efuse(rtk_phy, phy_parameter, index); + + update_amplitude_control_value(rtk_phy, phy_parameter); + } + + return ret; +} + +static int rtk_usb3phy_probe(struct platform_device *pdev) +{ + struct rtk_phy *rtk_phy; + struct device *dev = &pdev->dev; + struct phy *generic_phy; + struct phy_provider *phy_provider; + const struct phy_cfg *phy_cfg; + int ret; + + phy_cfg = of_device_get_match_data(dev); + if (!phy_cfg) { + dev_err(dev, "phy config are not assigned!\n"); + return -EINVAL; + } + + rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL); + if (!rtk_phy) + return -ENOMEM; + + rtk_phy->dev = &pdev->dev; + rtk_phy->phy.dev = rtk_phy->dev; + rtk_phy->phy.label = "rtk-usb3phy"; + rtk_phy->phy.notify_port_status = rtk_phy_notify_port_status; + + rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL); + + memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg)); + + rtk_phy->num_phy = 1; + + ret = parse_phy_data(rtk_phy); + if (ret) + goto err; + + platform_set_drvdata(pdev, rtk_phy); + + generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops); + if (IS_ERR(generic_phy)) + return PTR_ERR(generic_phy); + + phy_set_drvdata(generic_phy, rtk_phy); + + phy_provider = devm_of_phy_provider_register(rtk_phy->dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + ret = usb_add_phy_dev(&rtk_phy->phy); + if (ret) + goto err; + + create_debug_files(rtk_phy); + +err: + return ret; +} + +static void rtk_usb3phy_remove(struct platform_device *pdev) +{ + struct rtk_phy *rtk_phy = platform_get_drvdata(pdev); + + remove_debug_files(rtk_phy); + + usb_remove_phy(&rtk_phy->phy); +} + +static const struct phy_cfg rtd1295_phy_cfg = { + .param_size = MAX_USB_PHY_DATA_SIZE, + .param = { [0] = {0x01, 0x4008}, [1] = {0x01, 0xe046}, + [2] = {0x02, 0x6046}, [3] = {0x03, 0x2779}, + [4] = {0x04, 0x72f5}, [5] = {0x05, 0x2ad3}, + [6] = {0x06, 0x000e}, [7] = {0x07, 0x2e00}, + [8] = {0x08, 0x3591}, [9] = {0x09, 0x525c}, + [10] = {0x0a, 0xa600}, [11] = {0x0b, 0xa904}, + [12] = {0x0c, 0xc000}, [13] = {0x0d, 0xef1c}, + [14] = {0x0e, 0x2000}, [15] = {0x0f, 0x0000}, + [16] = {0x10, 0x000c}, [17] = {0x11, 0x4c00}, + [18] = {0x12, 0xfc00}, [19] = {0x13, 0x0c81}, + [20] = {0x14, 0xde01}, [21] = {0x15, 0x0000}, + [22] = {0x16, 0x0000}, [23] = {0x17, 0x0000}, + [24] = {0x18, 0x0000}, [25] = {0x19, 0x4004}, + [26] = {0x1a, 0x1260}, [27] = {0x1b, 0xff00}, + [28] = {0x1c, 0xcb00}, [29] = {0x1d, 0xa03f}, + [30] = {0x1e, 0xc2e0}, [31] = {0x1f, 0x2807}, + [32] = {0x20, 0x947a}, [33] = {0x21, 0x88aa}, + [34] = {0x22, 0x0057}, [35] = {0x23, 0xab66}, + [36] = {0x24, 0x0800}, [37] = {0x25, 0x0000}, + [38] = {0x26, 0x040a}, [39] = {0x27, 0x01d6}, + [40] = {0x28, 0xf8c2}, [41] = {0x29, 0x3080}, + [42] = {0x2a, 0x3082}, [43] = {0x2b, 0x2078}, + [44] = {0x2c, 0xffff}, [45] = {0x2d, 0xffff}, + [46] = {0x2e, 0x0000}, [47] = {0x2f, 0x0040}, }, + .check_efuse = false, + .do_toggle = true, + .do_toggle_once = false, + .use_default_parameter = false, + .check_rx_front_end_offset = false, +}; + +static const struct phy_cfg rtd1619_phy_cfg = { + .param_size = MAX_USB_PHY_DATA_SIZE, + .param = { [8] = {0x08, 0x3591}, + [38] = {0x26, 0x840b}, + [40] = {0x28, 0xf842}, }, + .check_efuse = false, + .do_toggle = true, + .do_toggle_once = false, + .use_default_parameter = false, + .check_rx_front_end_offset = false, +}; + +static const struct phy_cfg rtd1319_phy_cfg = { + .param_size = MAX_USB_PHY_DATA_SIZE, + .param = { [1] = {0x01, 0xac86}, + [6] = {0x06, 0x0003}, + [9] = {0x09, 0x924c}, + [10] = {0x0a, 0xa608}, + [11] = {0x0b, 0xb905}, + [14] = {0x0e, 0x2010}, + [32] = {0x20, 0x705a}, + [33] = {0x21, 0xf645}, + [34] = {0x22, 0x0013}, + [35] = {0x23, 0xcb66}, + [41] = {0x29, 0xff00}, }, + .check_efuse = true, + .do_toggle = true, + .do_toggle_once = false, + .use_default_parameter = false, + .check_rx_front_end_offset = false, +}; + +static const struct phy_cfg rtd1619b_phy_cfg = { + .param_size = MAX_USB_PHY_DATA_SIZE, + .param = { [1] = {0x01, 0xac8c}, + [6] = {0x06, 0x0017}, + [9] = {0x09, 0x724c}, + [10] = {0x0a, 0xb610}, + [11] = {0x0b, 0xb90d}, + [13] = {0x0d, 0xef2a}, + [15] = {0x0f, 0x9050}, + [16] = {0x10, 0x000c}, + [32] = {0x20, 0x70ff}, + [34] = {0x22, 0x0013}, + [35] = {0x23, 0xdb66}, + [38] = {0x26, 0x8609}, + [41] = {0x29, 0xff13}, + [42] = {0x2a, 0x3070}, }, + .check_efuse = true, + .do_toggle = false, + .do_toggle_once = true, + .use_default_parameter = false, + .check_rx_front_end_offset = false, +}; + +static const struct phy_cfg rtd1319d_phy_cfg = { + .param_size = MAX_USB_PHY_DATA_SIZE, + .param = { [1] = {0x01, 0xac89}, + [4] = {0x04, 0xf2f5}, + [6] = {0x06, 0x0017}, + [9] = {0x09, 0x424c}, + [10] = {0x0a, 0x9610}, + [11] = {0x0b, 0x9901}, + [12] = {0x0c, 0xf000}, + [13] = {0x0d, 0xef2a}, + [14] = {0x0e, 0x1000}, + [15] = {0x0f, 0x9050}, + [32] = {0x20, 0x7077}, + [35] = {0x23, 0x0b62}, + [37] = {0x25, 0x10ec}, + [42] = {0x2a, 0x3070}, }, + .check_efuse = true, + .do_toggle = false, + .do_toggle_once = true, + .use_default_parameter = false, + .check_rx_front_end_offset = true, +}; + +static const struct of_device_id usbphy_rtk_dt_match[] = { + { .compatible = "realtek,rtd1295-usb3phy", .data = &rtd1295_phy_cfg }, + { .compatible = "realtek,rtd1319-usb3phy", .data = &rtd1319_phy_cfg }, + { .compatible = "realtek,rtd1319d-usb3phy", .data = &rtd1319d_phy_cfg }, + { .compatible = "realtek,rtd1619-usb3phy", .data = &rtd1619_phy_cfg }, + { .compatible = "realtek,rtd1619b-usb3phy", .data = &rtd1619b_phy_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match); + +static struct platform_driver rtk_usb3phy_driver = { + .probe = rtk_usb3phy_probe, + .remove_new = rtk_usb3phy_remove, + .driver = { + .name = "rtk-usb3phy", + .of_match_table = usbphy_rtk_dt_match, + }, +}; + +module_platform_driver(rtk_usb3phy_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform: rtk-usb3phy"); +MODULE_AUTHOR("Stanley Chang "); +MODULE_DESCRIPTION("Realtek usb 3.0 phy driver"); -- cgit From 612ad27a3045525feedc2f77b5807b16c402951a Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Tue, 25 Jul 2023 11:31:55 +0800 Subject: dt-bindings: phy: realtek: Add Realtek DHC RTD SoC USB 2.0 PHY Document the USB PHY bindings for Realtek SoCs. Realtek DHC (digital home center) RTD SoCs support DWC3 XHCI USB controller and using USB 2.0 PHY transceiver. Signed-off-by: Stanley Chang Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230725033318.8361-4-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/phy/realtek,usb2phy.yaml | 175 +++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml diff --git a/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml b/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml new file mode 100644 index 000000000000..9911ada39ee7 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml @@ -0,0 +1,175 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2023 Realtek Semiconductor Corporation +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/realtek,usb2phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek DHC SoCs USB 2.0 PHY + +maintainers: + - Stanley Chang + +description: | + Realtek USB 2.0 PHY support the digital home center (DHC) RTD series SoCs. + The USB 2.0 PHY driver is designed to support the XHCI controller. The SoCs + support multiple XHCI controllers. One PHY device node maps to one XHCI + controller. + + RTD1295/RTD1619 SoCs USB + The USB architecture includes three XHCI controllers. + Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on some + controllers. + XHCI controller#0 -- usb2phy -- phy#0 + |- usb3phy -- phy#0 + XHCI controller#1 -- usb2phy -- phy#0 + XHCI controller#2 -- usb2phy -- phy#0 + |- usb3phy -- phy#0 + + RTD1395 SoCs USB + The USB architecture includes two XHCI controllers. + The controller#0 has one USB 2.0 PHY. The controller#1 includes two USB 2.0 + PHY. + XHCI controller#0 -- usb2phy -- phy#0 + XHCI controller#1 -- usb2phy -- phy#0 + |- phy#1 + + RTD1319/RTD1619b SoCs USB + The USB architecture includes three XHCI controllers. + Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#2. + XHCI controller#0 -- usb2phy -- phy#0 + XHCI controller#1 -- usb2phy -- phy#0 + XHCI controller#2 -- usb2phy -- phy#0 + |- usb3phy -- phy#0 + + RTD1319d SoCs USB + The USB architecture includes three XHCI controllers. + Each xhci maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#0. + XHCI controller#0 -- usb2phy -- phy#0 + |- usb3phy -- phy#0 + XHCI controller#1 -- usb2phy -- phy#0 + XHCI controller#2 -- usb2phy -- phy#0 + + RTD1312c/RTD1315e SoCs USB + The USB architecture includes three XHCI controllers. + Each XHCI maps to one USB 2.0 PHY. + XHCI controller#0 -- usb2phy -- phy#0 + XHCI controller#1 -- usb2phy -- phy#0 + XHCI controller#2 -- usb2phy -- phy#0 + +properties: + compatible: + enum: + - realtek,rtd1295-usb2phy + - realtek,rtd1312c-usb2phy + - realtek,rtd1315e-usb2phy + - realtek,rtd1319-usb2phy + - realtek,rtd1319d-usb2phy + - realtek,rtd1395-usb2phy + - realtek,rtd1395-usb2phy-2port + - realtek,rtd1619-usb2phy + - realtek,rtd1619b-usb2phy + + reg: + items: + - description: PHY data registers + - description: PHY control registers + + "#phy-cells": + const: 0 + + nvmem-cells: + maxItems: 2 + description: + Phandles to nvmem cell that contains the trimming data. + If unspecified, default value is used. + + nvmem-cell-names: + items: + - const: usb-dc-cal + - const: usb-dc-dis + description: + The following names, which correspond to each nvmem-cells. + usb-dc-cal is the driving level for each phy specified via efuse. + usb-dc-dis is the disconnection level for each phy specified via efuse. + + realtek,inverse-hstx-sync-clock: + description: + For one of the phys of RTD1619b SoC, the synchronous clock of the + high-speed tx must be inverted. + type: boolean + + realtek,driving-level: + description: + Control the magnitude of High speed Dp/Dm output swing (mV). + For a different board or port, the original magnitude maybe not meet + the specification. In this situation we can adjust the value to meet + the specification. + $ref: /schemas/types.yaml#/definitions/uint32 + default: 8 + minimum: 0 + maximum: 31 + + realtek,driving-level-compensate: + description: + For RTD1315e SoC, the driving level can be adjusted by reading the + efuse table. This property provides drive compensation. + If the magnitude of High speed Dp/Dm output swing still not meet the + specification, then we can set this value to meet the specification. + $ref: /schemas/types.yaml#/definitions/int32 + default: 0 + minimum: -8 + maximum: 8 + + realtek,disconnection-compensate: + description: + This adjusts the disconnection level compensation for the different + boards with different disconnection level. + $ref: /schemas/types.yaml#/definitions/int32 + default: 0 + minimum: -8 + maximum: 8 + +required: + - compatible + - reg + - "#phy-cells" + +allOf: + - if: + not: + properties: + compatible: + contains: + enum: + - realtek,rtd1619b-usb2phy + then: + properties: + realtek,inverse-hstx-sync-clock: false + + - if: + not: + properties: + compatible: + contains: + enum: + - realtek,rtd1315e-usb2phy + then: + properties: + realtek,driving-level-compensate: false + +additionalProperties: false + +examples: + - | + usb-phy@13214 { + compatible = "realtek,rtd1619b-usb2phy"; + reg = <0x13214 0x4>, <0x28280 0x4>; + #phy-cells = <0>; + nvmem-cells = <&otp_usb_port0_dc_cal>, <&otp_usb_port0_dc_dis>; + nvmem-cell-names = "usb-dc-cal", "usb-dc-dis"; + + realtek,inverse-hstx-sync-clock; + realtek,driving-level = <0xa>; + realtek,disconnection-compensate = <(-1)>; + }; -- cgit From d6ef688786beafc0fda8f12afaee313cabb4456e Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Tue, 25 Jul 2023 11:31:56 +0800 Subject: dt-bindings: phy: realtek: Add Realtek DHC RTD SoC USB 3.0 PHY Document the USB PHY bindings for Realtek SoCs. Realtek DHC (digital home center) RTD SoCs support DWC3 XHCI USB controller and using USB 3.0 PHY transceiver. Signed-off-by: Stanley Chang Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230725033318.8361-5-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/phy/realtek,usb3phy.yaml | 107 +++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml diff --git a/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml b/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml new file mode 100644 index 000000000000..dfe2bb4e59e7 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2023 Realtek Semiconductor Corporation +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/realtek,usb3phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek DHC SoCs USB 3.0 PHY + +maintainers: + - Stanley Chang + +description: | + Realtek USB 3.0 PHY support the digital home center (DHC) RTD series SoCs. + The USB 3.0 PHY driver is designed to support the XHCI controller. The SoCs + support multiple XHCI controllers. One PHY device node maps to one XHCI + controller. + + RTD1295/RTD1619 SoCs USB + The USB architecture includes three XHCI controllers. + Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on some + controllers. + XHCI controller#0 -- usb2phy -- phy#0 + |- usb3phy -- phy#0 + XHCI controller#1 -- usb2phy -- phy#0 + XHCI controller#2 -- usb2phy -- phy#0 + |- usb3phy -- phy#0 + + RTD1319/RTD1619b SoCs USB + The USB architecture includes three XHCI controllers. + Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#2. + XHCI controller#0 -- usb2phy -- phy#0 + XHCI controller#1 -- usb2phy -- phy#0 + XHCI controller#2 -- usb2phy -- phy#0 + |- usb3phy -- phy#0 + + RTD1319d SoCs USB + The USB architecture includes three XHCI controllers. + Each xhci maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#0. + XHCI controller#0 -- usb2phy -- phy#0 + |- usb3phy -- phy#0 + XHCI controller#1 -- usb2phy -- phy#0 + XHCI controller#2 -- usb2phy -- phy#0 + +properties: + compatible: + enum: + - realtek,rtd1295-usb3phy + - realtek,rtd1319-usb3phy + - realtek,rtd1319d-usb3phy + - realtek,rtd1619-usb3phy + - realtek,rtd1619b-usb3phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + nvmem-cells: + maxItems: 1 + description: A phandle to the tx lfps swing trim data provided by + a nvmem device, if unspecified, default values shall be used. + + nvmem-cell-names: + items: + - const: usb_u3_tx_lfps_swing_trim + + realtek,amplitude-control-coarse-tuning: + description: + This adjusts the signal amplitude for normal operation and beacon LFPS. + This value is a parameter for coarse tuning. + For different boards, if the default value is inappropriate, this + property can be assigned to adjust. + $ref: /schemas/types.yaml#/definitions/uint32 + default: 255 + minimum: 0 + maximum: 255 + + realtek,amplitude-control-fine-tuning: + description: + This adjusts the signal amplitude for normal operation and beacon LFPS. + This value is used for fine-tuning parameters. + $ref: /schemas/types.yaml#/definitions/uint32 + default: 65535 + minimum: 0 + maximum: 65535 + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + usb-phy@13e10 { + compatible = "realtek,rtd1319d-usb3phy"; + reg = <0x13e10 0x4>; + #phy-cells = <0>; + + nvmem-cells = <&otp_usb_u3_tx_lfps_swing_trim>; + nvmem-cell-names = "usb_u3_tx_lfps_swing_trim"; + + realtek,amplitude-control-coarse-tuning = <0x77>; + }; -- cgit From 8b645922b22303cec4628dbbbf6c8553d1cdec87 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Jul 2023 08:22:00 +0200 Subject: usb: gadget: Add support for USB MIDI 2.0 function driver This patch adds the support for USB MIDI 2.0 gadget function driver. The driver emulates a USB MIDI 2.0 interface with one or more UMP Endpoints, where each of UMP Endpoint is a pair of MIDI Endpoints for handling MIDI 2.0 UMP packets. When the function driver is bound, the driver creates an ALSA card object with UMP rawmidi devices. This is a kind of loop-back where the incoming and upcoming UMP packets from/to the MIDI 2.0 UMP Endpoints are transferred as-is. In addition, legacy (MIDI 1.0) rawmidi devices are created, so that legacy applications can work in the gadget side, too. When a USB MIDI 2.0 gadget interface appears, the connected host can use it with the snd-usb-audio driver where MIDI 2.0 support is enabled. Both gadget and connected hosts will have the similar UMP Endpoint and Function Block (or Group Terminal Block) information. Slight differences are the direction and UI-hint bits; it's due to the nature of gadget driver, and the input/output direction is swapped in both sides (the input for gadget is the output for host, and vice versa). The driver supports the brand-new UMP v1.1 feature, including the UMP Stream message handling for providing UMP Endpoint and Function Block information as well as dealing with the MIDI protocol switch. The driver responds to UMP Stream messages by itself. OTOH, MIDI-CI message handling isn't implemented in the kernel driver; it should be processed in the user-space through the loopback UMP device. As of this patch, the whole configuration is fixed, providing only one bidirectional UMP Endpoint containing a single FB/GTB with a single UMP Group. The configuration will be dynamically changeable in the following patches. The traditional MIDI 1.0 is still provided in the altset 0 (which is mandatory per spec). But it's only about the configuration, and no actual I/O will be running for the altset 0 as of this patch. The proper support MIDI 1.0 altset will follow in later patches, too. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20230725062206.9674-2-tiwai@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 18 + drivers/usb/gadget/function/Makefile | 2 + drivers/usb/gadget/function/f_midi2.c | 1691 +++++++++++++++++++++++++++++++++ drivers/usb/gadget/function/u_midi2.h | 79 ++ 4 files changed, 1790 insertions(+) create mode 100644 drivers/usb/gadget/function/f_midi2.c create mode 100644 drivers/usb/gadget/function/u_midi2.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 336db8f92afa..b3592bcb0f96 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -208,6 +208,9 @@ config USB_F_UVC config USB_F_MIDI tristate +config USB_F_MIDI2 + tristate + config USB_F_HID tristate @@ -436,6 +439,21 @@ config USB_CONFIGFS_F_MIDI connections can then be made on the gadget system, using ALSA's aconnect utility etc. +config USB_CONFIGFS_F_MIDI2 + bool "MIDI 2.0 function" + depends on USB_CONFIGFS + depends on SND + select USB_LIBCOMPOSITE + select SND_UMP + select SND_UMP_LEGACY_RAWMIDI + select USB_F_MIDI2 + help + The MIDI 2.0 function driver provides the generic emulated + USB MIDI 2.0 interface, looped back to ALSA UMP rawmidi + device on the gadget host. It supports UMP 1.1 spec and + responds UMP Stream messages for UMP Endpoint and Function + Block information / configuration. + config USB_CONFIGFS_F_HID bool "HID function" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 5d3a6cf02218..87917a7d4a9b 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -44,6 +44,8 @@ usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o usb_f_midi-y := f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o +usb_f_midi2-y := f_midi2.o +obj-$(CONFIG_USB_F_MIDI2) += usb_f_midi2.o usb_f_hid-y := f_hid.o obj-$(CONFIG_USB_F_HID) += usb_f_hid.o usb_f_printer-y := f_printer.o diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c new file mode 100644 index 000000000000..848cb3150deb --- /dev/null +++ b/drivers/usb/gadget/function/f_midi2.c @@ -0,0 +1,1691 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * f_midi2.c -- USB MIDI 2.0 class function driver + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "u_f.h" +#include "u_midi2.h" + +struct f_midi2; +struct f_midi2_ep; +struct f_midi2_usb_ep; + +/* Context for each USB request */ +struct f_midi2_req_ctx { + struct f_midi2_usb_ep *usb_ep; /* belonging USB EP */ + unsigned int index; /* array index: 0-31 */ + struct usb_request *req; /* assigned request */ +}; + +/* Resources for a USB Endpoint */ +struct f_midi2_usb_ep { + struct f_midi2 *card; /* belonging card */ + struct f_midi2_ep *ep; /* belonging UMP EP (optional) */ + struct usb_ep *usb_ep; /* assigned USB EP */ + void (*complete)(struct usb_ep *usb_ep, struct usb_request *req); + unsigned long free_reqs; /* bitmap for unused requests */ + unsigned int num_reqs; /* number of allocated requests */ + struct f_midi2_req_ctx *reqs; /* request context array */ +}; + +/* Resources for UMP Function Block (and USB Group Terminal Block) */ +struct f_midi2_block { + struct f_midi2_block_info info; /* FB info, copied from configfs */ + struct snd_ump_block *fb; /* assigned FB */ + unsigned int gtb_id; /* assigned GTB id */ + unsigned int string_id; /* assigned string id */ +}; + +/* Resources for UMP Endpoint */ +struct f_midi2_ep { + struct snd_ump_endpoint *ump; /* assigned UMP EP */ + struct f_midi2 *card; /* belonging MIDI 2.0 device */ + + struct f_midi2_ep_info info; /* UMP EP info, copied from configfs */ + unsigned int num_blks; /* number of FBs */ + struct f_midi2_block blks[SNDRV_UMP_MAX_BLOCKS]; /* UMP FBs */ + + struct f_midi2_usb_ep ep_in; /* USB MIDI EP-in */ + struct f_midi2_usb_ep ep_out; /* USB MIDI EP-out */ +}; + +/* indices for USB strings */ +enum { + STR_IFACE = 0, + STR_GTB1 = 1, +}; + +/* 1-based GTB id to string id */ +#define gtb_to_str_id(id) (STR_GTB1 + (id) - 1) + +/* operation mode */ +enum { + MIDI_OP_MODE_UNSET, /* no altset set yet */ + MIDI_OP_MODE_MIDI1, /* MIDI 1.0 (altset 0) is used */ + MIDI_OP_MODE_MIDI2, /* MIDI 2.0 (altset 1) is used */ +}; + +/* Resources for MIDI 2.0 Device */ +struct f_midi2 { + struct usb_function func; + struct usb_gadget *gadget; + struct snd_card *card; + + /* MIDI 1.0 in/out USB EPs */ + struct f_midi2_usb_ep midi1_ep_in; + struct f_midi2_usb_ep midi1_ep_out; + + int midi_if; /* USB MIDI interface number */ + int operation_mode; /* current operation mode */ + + spinlock_t queue_lock; + + struct f_midi2_card_info info; /* card info, copied from configfs */ + + unsigned int num_eps; + struct f_midi2_ep midi2_eps[MAX_UMP_EPS]; + + unsigned int total_blocks; /* total number of blocks of all EPs */ + struct usb_string *string_defs; + struct usb_string *strings; +}; + +#define func_to_midi2(f) container_of(f, struct f_midi2, func) + +/* get EP name string */ +static const char *ump_ep_name(const struct f_midi2_ep *ep) +{ + return ep->info.ep_name ? ep->info.ep_name : "MIDI 2.0 Gadget"; +} + +/* get EP product ID string */ +static const char *ump_product_id(const struct f_midi2_ep *ep) +{ + return ep->info.product_id ? ep->info.product_id : "Unique Product ID"; +} + +/* get FB name string */ +static const char *ump_fb_name(const struct f_midi2_block_info *info) +{ + return info->name ? info->name : "MIDI 2.0 Gadget I/O"; +} + +/* + * USB Descriptor Definitions + */ +/* GTB header descriptor */ +static struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc = { + .bLength = sizeof(gtb_header_desc), + .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK, + .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK_HEADER, + .wTotalLength = __cpu_to_le16(0x12), // to be filled +}; + +/* GTB descriptor template: most items are replaced dynamically */ +static struct usb_ms20_gr_trm_block_descriptor gtb_desc = { + .bLength = sizeof(gtb_desc), + .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK, + .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK, + .bGrpTrmBlkID = 0x01, + .bGrpTrmBlkType = USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL, + .nGroupTrm = 0x00, + .nNumGroupTrm = 1, + .iBlockItem = 0, + .bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_64, + .wMaxInputBandwidth = 0, + .wMaxOutputBandwidth = 0, +}; + +DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); +DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); +DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); +DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32); + +#define EP_MAX_PACKET_INT 8 + +/* Audio Control Interface */ +static struct usb_interface_descriptor midi2_audio_if_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, // to be filled + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .bInterfaceProtocol = 0, + .iInterface = 0, +}; + +static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = { + .bLength = 0x09, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = 0x01, + .bcdADC = __cpu_to_le16(0x0100), + .wTotalLength = __cpu_to_le16(0x0009), + .bInCollection = 0x01, + .baInterfaceNr = { 0x01 }, // to be filled +}; + +/* MIDI 1.0 Streaming Interface (altset 0) */ +static struct usb_interface_descriptor midi2_midi1_if_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, // to be filled + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, + .bInterfaceProtocol = 0, + .iInterface = 0, // to be filled +}; + +static struct usb_ms_header_descriptor midi2_midi1_class_desc = { + .bLength = 0x07, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdMSC = __cpu_to_le16(0x0100), + .wTotalLength = __cpu_to_le16(0x41), // to be calculated +}; + +/* MIDI 1.0 IN (Embedded) Jack */ +static struct usb_midi_in_jack_descriptor midi2_midi1_in_jack1_desc = { + .bLength = 0x06, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, + .bJackType = USB_MS_EMBEDDED, + .bJackID = 0x01, + .iJack = 0, +}; + +/* MIDI 1.0 IN (External) Jack */ +static struct usb_midi_in_jack_descriptor midi2_midi1_in_jack2_desc = { + .bLength = 0x06, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, + .bJackType = USB_MS_EXTERNAL, + .bJackID = 0x02, + .iJack = 0, +}; + +/* MIDI 1.0 OUT (Embedded) Jack */ +static struct usb_midi_out_jack_descriptor_1 midi2_midi1_out_jack1_desc = { + .bLength = 0x09, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, + .bJackType = USB_MS_EMBEDDED, + .bJackID = 0x03, + .bNrInputPins = 1, + .pins = { { 0x02, 0x01 } }, + .iJack = 0, +}; + +/* MIDI 1.0 OUT (External) Jack */ +static struct usb_midi_out_jack_descriptor_1 midi2_midi1_out_jack2_desc = { + .bLength = 0x09, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, + .bJackType = USB_MS_EXTERNAL, + .bJackID = 0x04, + .bNrInputPins = 1, + .pins = { { 0x01, 0x01 } }, + .iJack = 0, +}; + +/* MIDI 1.0 EP OUT */ +static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | 0, // set up dynamically + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = { + .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_ms_endpoint_descriptor_1 midi2_midi1_ep_out_class_desc = { + .bLength = 0x05, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + .bNumEmbMIDIJack = 1, + .baAssocJackID = { 0x01 }, +}; + +/* MIDI 1.0 EP IN */ +static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | 0, // set up dynamically + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = { + .bLength = sizeof(midi2_midi1_ep_in_ss_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_ms_endpoint_descriptor_1 midi2_midi1_ep_in_class_desc = { + .bLength = 0x05, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + .bNumEmbMIDIJack = 1, + .baAssocJackID = { 0x03 }, +}; + +/* MIDI 2.0 Streaming Interface (altset 1) */ +static struct usb_interface_descriptor midi2_midi2_if_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, // to be filled + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, + .bInterfaceProtocol = 0, + .iInterface = 0, // to be filled +}; + +static struct usb_ms_header_descriptor midi2_midi2_class_desc = { + .bLength = 0x07, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdMSC = __cpu_to_le16(0x0200), + .wTotalLength = __cpu_to_le16(0x07), +}; + +/* MIDI 2.0 EP OUT */ +static struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS]; + +static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_out_ss_comp_desc = { + .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS]; + +/* MIDI 2.0 EP IN */ +static struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS]; + +static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_in_ss_comp_desc = { + .bLength = sizeof(midi2_midi2_ep_in_ss_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_in_class_desc[MAX_UMP_EPS]; + +/* Arrays of descriptors to be created */ +static void *midi2_audio_descs[] = { + &midi2_audio_if_desc, + &midi2_audio_class_desc, + NULL +}; + +static void *midi2_midi1_descs[] = { + &midi2_midi1_if_desc, + &midi2_midi1_class_desc, + &midi2_midi1_in_jack1_desc, + &midi2_midi1_in_jack2_desc, + &midi2_midi1_out_jack1_desc, + &midi2_midi1_out_jack2_desc, + NULL +}; + +static void *midi2_midi1_ep_descs[] = { + &midi2_midi1_ep_out_desc, + &midi2_midi1_ep_out_class_desc, + &midi2_midi1_ep_in_desc, + &midi2_midi1_ep_in_class_desc, + NULL +}; + +static void *midi2_midi1_ep_ss_descs[] = { + &midi2_midi1_ep_out_desc, + &midi2_midi1_ep_out_ss_comp_desc, + &midi2_midi1_ep_out_class_desc, + &midi2_midi1_ep_in_desc, + &midi2_midi1_ep_in_ss_comp_desc, + &midi2_midi1_ep_in_class_desc, + NULL +}; + +static void *midi2_midi2_descs[] = { + &midi2_midi2_if_desc, + &midi2_midi2_class_desc, + NULL +}; + +/* + * USB request handling + */ + +/* get an empty request for the given EP */ +static struct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep) +{ + struct usb_request *req = NULL; + unsigned long flags; + int index; + + spin_lock_irqsave(&usb_ep->card->queue_lock, flags); + if (!usb_ep->free_reqs) + goto unlock; + index = find_first_bit(&usb_ep->free_reqs, usb_ep->num_reqs); + if (index >= usb_ep->num_reqs) + goto unlock; + req = usb_ep->reqs[index].req; + if (!req) + goto unlock; + clear_bit(index, &usb_ep->free_reqs); + req->length = 0; + unlock: + spin_unlock_irqrestore(&usb_ep->card->queue_lock, flags); + return req; +} + +/* put the empty request back */ +static void put_empty_request(struct usb_request *req) +{ + struct f_midi2_req_ctx *ctx = req->context; + unsigned long flags; + + spin_lock_irqsave(&ctx->usb_ep->card->queue_lock, flags); + set_bit(ctx->index, &ctx->usb_ep->free_reqs); + spin_unlock_irqrestore(&ctx->usb_ep->card->queue_lock, flags); +} + +/* + * UMP v1.1 Stream message handling + */ + +/* queue a request to UMP EP; request is either queued or freed after this */ +static int queue_request_ep_raw(struct usb_request *req) +{ + struct f_midi2_req_ctx *ctx = req->context; + int err; + + req->complete = ctx->usb_ep->complete; + err = usb_ep_queue(ctx->usb_ep->usb_ep, req, GFP_ATOMIC); + if (err) { + put_empty_request(req); + return err; + } + return 0; +} + +/* queue a request with endianness conversion */ +static int queue_request_ep_in(struct usb_request *req) +{ + /* UMP packets have to be converted to little-endian */ + cpu_to_le32_array((u32 *)req->buf, req->length >> 2); + return queue_request_ep_raw(req); +} + +/* reply a UMP packet via EP-in */ +static int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len) +{ + struct f_midi2_usb_ep *usb_ep = &ep->ep_in; + struct usb_request *req; + + req = get_empty_request(usb_ep); + if (!req) + return -ENOSPC; + + req->length = len; + memcpy(req->buf, buf, len); + return queue_request_ep_in(req); +} + +/* reply a UMP stream EP info */ +static void reply_ump_stream_ep_info(struct f_midi2_ep *ep) +{ + struct snd_ump_stream_msg_ep_info rep = { + .type = UMP_MSG_TYPE_STREAM, + .status = UMP_STREAM_MSG_STATUS_EP_INFO, + .ump_version_major = 0x01, + .ump_version_minor = 0x01, + .num_function_blocks = ep->num_blks, + .static_function_block = !!ep->card->info.static_block, + .protocol = (UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 | + UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) >> 8, + }; + + reply_ep_in(ep, &rep, sizeof(rep)); +} + +/* reply a UMP EP device info */ +static void reply_ump_stream_ep_device(struct f_midi2_ep *ep) +{ + struct snd_ump_stream_msg_devince_info rep = { + .type = UMP_MSG_TYPE_STREAM, + .status = UMP_STREAM_MSG_STATUS_DEVICE_INFO, + .manufacture_id = ep->info.manufacturer, + .family_lsb = ep->info.family & 0xff, + .family_msb = (ep->info.family >> 8) & 0xff, + .model_lsb = ep->info.model & 0xff, + .model_msb = (ep->info.model >> 8) & 0xff, + .sw_revision = ep->info.sw_revision, + }; + + reply_ep_in(ep, &rep, sizeof(rep)); +} + +#define UMP_STREAM_PKT_BYTES 16 /* UMP stream packet size = 16 bytes*/ +#define UMP_STREAM_EP_STR_OFF 2 /* offset of name string for EP info */ +#define UMP_STREAM_FB_STR_OFF 3 /* offset of name string for FB info */ + +/* Helper to replay a string */ +static void reply_ump_stream_string(struct f_midi2_ep *ep, const u8 *name, + unsigned int type, unsigned int extra, + unsigned int start_ofs) +{ + struct f_midi2_usb_ep *usb_ep = &ep->ep_in; + struct f_midi2 *midi2 = ep->card; + struct usb_request *req; + unsigned int pos; + u32 *buf; + + if (!*name) + return; + req = get_empty_request(usb_ep); + if (!req) + return; + + buf = (u32 *)req->buf; + pos = start_ofs; + for (;;) { + if (pos == start_ofs) { + memset(buf, 0, UMP_STREAM_PKT_BYTES); + buf[0] = ump_stream_compose(type, 0) | extra; + } + buf[pos / 4] |= *name++ << ((3 - (pos % 4)) * 8); + if (!*name) { + if (req->length) + buf[0] |= UMP_STREAM_MSG_FORMAT_END << 26; + req->length += UMP_STREAM_PKT_BYTES; + break; + } + if (++pos == UMP_STREAM_PKT_BYTES) { + if (!req->length) + buf[0] |= UMP_STREAM_MSG_FORMAT_START << 26; + else + buf[0] |= UMP_STREAM_MSG_FORMAT_CONTINUE << 26; + req->length += UMP_STREAM_PKT_BYTES; + if (midi2->info.req_buf_size - req->length < UMP_STREAM_PKT_BYTES) + break; + buf += 4; + pos = start_ofs; + } + } + + if (req->length) + queue_request_ep_in(req); + else + put_empty_request(req); +} + +/* Reply a UMP EP name string */ +static void reply_ump_stream_ep_name(struct f_midi2_ep *ep) +{ + reply_ump_stream_string(ep, ump_ep_name(ep), + UMP_STREAM_MSG_STATUS_EP_NAME, 0, + UMP_STREAM_EP_STR_OFF); +} + +/* Reply a UMP EP product ID string */ +static void reply_ump_stream_ep_pid(struct f_midi2_ep *ep) +{ + reply_ump_stream_string(ep, ump_product_id(ep), + UMP_STREAM_MSG_STATUS_PRODUCT_ID, 0, + UMP_STREAM_EP_STR_OFF); +} + +/* Reply a UMP EP stream config */ +static void reply_ump_stream_ep_config(struct f_midi2_ep *ep) +{ + struct snd_ump_stream_msg_stream_cfg rep = { + .type = UMP_MSG_TYPE_STREAM, + .status = UMP_STREAM_MSG_STATUS_STREAM_CFG, + }; + + if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) == + SNDRV_UMP_EP_INFO_PROTO_MIDI2) + rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8; + else + rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8; + + reply_ep_in(ep, &rep, sizeof(rep)); +} + +/* Reply a UMP FB info */ +static void reply_ump_stream_fb_info(struct f_midi2_ep *ep, int blk) +{ + struct f_midi2_block_info *b = &ep->blks[blk].info; + struct snd_ump_stream_msg_fb_info rep = { + .type = UMP_MSG_TYPE_STREAM, + .status = UMP_STREAM_MSG_STATUS_FB_INFO, + .active = !!b->active, + .function_block_id = blk, + .ui_hint = b->ui_hint, + .midi_10 = b->is_midi1, + .direction = b->direction, + .first_group = b->first_group, + .num_groups = b->num_groups, + .midi_ci_version = b->midi_ci_version, + .sysex8_streams = b->sysex8_streams, + }; + + reply_ep_in(ep, &rep, sizeof(rep)); +} + +/* Reply a FB name string */ +static void reply_ump_stream_fb_name(struct f_midi2_ep *ep, unsigned int blk) +{ + reply_ump_stream_string(ep, ump_fb_name(&ep->blks[blk].info), + UMP_STREAM_MSG_STATUS_FB_NAME, blk << 8, + UMP_STREAM_FB_STR_OFF); +} + +/* Process a UMP Stream message */ +static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data) +{ + struct f_midi2 *midi2 = ep->card; + unsigned int format, status, blk; + + format = ump_stream_message_format(*data); + status = ump_stream_message_status(*data); + switch (status) { + case UMP_STREAM_MSG_STATUS_EP_DISCOVERY: + if (format) + return; // invalid + if (data[1] & UMP_STREAM_MSG_REQUEST_EP_INFO) + reply_ump_stream_ep_info(ep); + if (data[1] & UMP_STREAM_MSG_REQUEST_DEVICE_INFO) + reply_ump_stream_ep_device(ep); + if (data[1] & UMP_STREAM_MSG_REQUEST_EP_NAME) + reply_ump_stream_ep_name(ep); + if (data[1] & UMP_STREAM_MSG_REQUEST_PRODUCT_ID) + reply_ump_stream_ep_pid(ep); + if (data[1] & UMP_STREAM_MSG_REQUEST_STREAM_CFG) + reply_ump_stream_ep_config(ep); + return; + case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST: + if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) { + ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2; + DBG(midi2, "Switching Protocol to MIDI2\n"); + } else { + ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1; + DBG(midi2, "Switching Protocol to MIDI1\n"); + } + snd_ump_switch_protocol(ep->ump, ep->info.protocol); + reply_ump_stream_ep_config(ep); + return; + case UMP_STREAM_MSG_STATUS_FB_DISCOVERY: + if (format) + return; // invalid + blk = (*data >> 8) & 0xff; + if (blk >= ep->num_blks) + return; + if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO) + reply_ump_stream_fb_info(ep, blk); + if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME) + reply_ump_stream_fb_name(ep, blk); + return; + } +} + +/* Process UMP messages included in a USB request */ +static void process_ump(struct f_midi2_ep *ep, const struct usb_request *req) +{ + const u32 *data = (u32 *)req->buf; + int len = req->actual >> 2; + const u32 *in_buf = ep->ump->input_buf; + + for (; len > 0; len--, data++) { + if (snd_ump_receive_ump_val(ep->ump, *data) <= 0) + continue; + if (ump_message_type(*in_buf) == UMP_MSG_TYPE_STREAM) + process_ump_stream_msg(ep, in_buf); + } +} + +/* + * MIDI 2.0 UMP USB request handling + */ + +/* complete handler for UMP EP-out requests */ +static void f_midi2_ep_out_complete(struct usb_ep *usb_ep, + struct usb_request *req) +{ + struct f_midi2_req_ctx *ctx = req->context; + struct f_midi2_ep *ep = ctx->usb_ep->ep; + struct f_midi2 *midi2 = ep->card; + int status = req->status; + + if (status) { + DBG(midi2, "%s complete error %d: %d/%d\n", + usb_ep->name, status, req->actual, req->length); + goto error; + } + + /* convert to UMP packet in native endianness */ + le32_to_cpu_array((u32 *)req->buf, req->actual >> 2); + + if (midi2->info.process_ump) + process_ump(ep, req); + + snd_ump_receive(ep->ump, req->buf, req->actual & ~3); + + if (midi2->operation_mode != MIDI_OP_MODE_MIDI2) + goto error; + + if (queue_request_ep_raw(req)) + goto error; + return; + + error: + put_empty_request(req); +} + +/* Transmit UMP packets received from user-space to the gadget */ +static void process_ump_transmit(struct f_midi2_ep *ep) +{ + struct f_midi2_usb_ep *usb_ep = &ep->ep_in; + struct f_midi2 *midi2 = ep->card; + struct usb_request *req; + int len; + + if (!usb_ep->usb_ep->enabled) + return; + + for (;;) { + req = get_empty_request(usb_ep); + if (!req) + break; + len = snd_ump_transmit(ep->ump, (u32 *)req->buf, + midi2->info.req_buf_size); + if (len <= 0) { + put_empty_request(req); + break; + } + + req->length = len; + if (queue_request_ep_in(req) < 0) + break; + } +} + +/* Complete handler for UMP EP-in requests */ +static void f_midi2_ep_in_complete(struct usb_ep *usb_ep, + struct usb_request *req) +{ + struct f_midi2_req_ctx *ctx = req->context; + struct f_midi2_ep *ep = ctx->usb_ep->ep; + struct f_midi2 *midi2 = ep->card; + int status = req->status; + + put_empty_request(req); + + if (status) { + DBG(midi2, "%s complete error %d: %d/%d\n", + usb_ep->name, status, req->actual, req->length); + return; + } + + process_ump_transmit(ep); +} + +/* Start MIDI EP */ +static int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep, + struct usb_function *fn) +{ + int err; + + usb_ep_disable(usb_ep->usb_ep); + err = config_ep_by_speed(usb_ep->card->gadget, fn, usb_ep->usb_ep); + if (err) + return err; + return usb_ep_enable(usb_ep->usb_ep); +} + +/* Drop pending requests */ +static void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep) +{ + int i; + + if (!usb_ep->num_reqs) + return; + + for (i = 0; i < usb_ep->num_reqs; i++) { + if (!test_bit(i, &usb_ep->free_reqs) && usb_ep->reqs[i].req) { + usb_ep_dequeue(usb_ep->usb_ep, usb_ep->reqs[i].req); + set_bit(i, &usb_ep->free_reqs); + } + } +} + +/* Allocate requests for the given EP */ +static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep) +{ + struct f_midi2 *midi2 = usb_ep->card; + int i; + + if (!usb_ep->reqs) + return -EINVAL; + + for (i = 0; i < midi2->info.num_reqs; i++) { + if (usb_ep->reqs[i].req) + continue; + usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep, + midi2->info.req_buf_size); + if (!usb_ep->reqs[i].req) + return -ENOMEM; + usb_ep->reqs[i].req->context = &usb_ep->reqs[i]; + } + return 0; +} + +/* Free allocated requests */ +static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep) +{ + struct f_midi2 *midi2 = usb_ep->card; + int i; + + for (i = 0; i < midi2->info.num_reqs; i++) { + if (!usb_ep->reqs[i].req) + continue; + free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req); + usb_ep->reqs[i].req = NULL; + } +} + +/* Initialize EP */ +static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep, + struct f_midi2_usb_ep *usb_ep, + void *desc, int num_reqs, + void (*complete)(struct usb_ep *usb_ep, + struct usb_request *req)) +{ + int i; + + usb_ep->card = midi2; + usb_ep->ep = ep; + usb_ep->usb_ep = usb_ep_autoconfig(midi2->gadget, desc); + if (!usb_ep->usb_ep) + return -ENODEV; + usb_ep->complete = complete; + + if (num_reqs) { + usb_ep->reqs = kcalloc(num_reqs, sizeof(*usb_ep->reqs), + GFP_KERNEL); + if (!usb_ep->reqs) + return -ENOMEM; + for (i = 0; i < num_reqs; i++) { + usb_ep->reqs[i].index = i; + usb_ep->reqs[i].usb_ep = usb_ep; + set_bit(i, &usb_ep->free_reqs); + usb_ep->num_reqs++; + } + } + + return 0; +} + +/* Free EP */ +static void f_midi2_free_ep(struct f_midi2_usb_ep *usb_ep) +{ + f_midi2_drop_reqs(usb_ep); + + f_midi2_free_ep_reqs(usb_ep); + + kfree(usb_ep->reqs); + usb_ep->num_reqs = 0; + usb_ep->free_reqs = 0; + usb_ep->reqs = NULL; +} + +/* Queue requests for EP-out at start */ +static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep) +{ + int i, err; + + for (i = 0; i < usb_ep->num_reqs; i++) { + if (!test_bit(i, &usb_ep->free_reqs) || !usb_ep->reqs[i].req) + continue; + usb_ep->reqs[i].req->complete = usb_ep->complete; + err = usb_ep_queue(usb_ep->usb_ep, usb_ep->reqs[i].req, + GFP_ATOMIC); + if (!err) + clear_bit(i, &usb_ep->free_reqs); + } +} + +/* + * Gadget Function callbacks + */ + +/* gadget function set_alt callback */ +static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf, + unsigned int alt) +{ + struct f_midi2 *midi2 = func_to_midi2(fn); + struct f_midi2_ep *ep; + int i, op_mode, err; + + if (intf != midi2->midi_if || alt > 1) + return 0; + + if (alt == 0) + op_mode = MIDI_OP_MODE_MIDI1; + else if (alt == 1) + op_mode = MIDI_OP_MODE_MIDI2; + else + op_mode = MIDI_OP_MODE_UNSET; + + if (midi2->operation_mode == op_mode) + return 0; + + midi2->operation_mode = op_mode; + + if (op_mode != MIDI_OP_MODE_MIDI2) { + for (i = 0; i < midi2->num_eps; i++) { + ep = &midi2->midi2_eps[i]; + f_midi2_drop_reqs(&ep->ep_in); + f_midi2_drop_reqs(&ep->ep_out); + f_midi2_free_ep_reqs(&ep->ep_in); + f_midi2_free_ep_reqs(&ep->ep_out); + } + return 0; + } + + for (i = 0; i < midi2->num_eps; i++) { + ep = &midi2->midi2_eps[i]; + + err = f_midi2_start_ep(&ep->ep_in, fn); + if (err) + return err; + err = f_midi2_start_ep(&ep->ep_out, fn); + if (err) + return err; + + err = f_midi2_alloc_ep_reqs(&ep->ep_in); + if (err) + return err; + err = f_midi2_alloc_ep_reqs(&ep->ep_out); + if (err) + return err; + + f_midi2_queue_out_reqs(&ep->ep_out); + } + + return 0; +} + +/* gadget function get_alt callback */ +static int f_midi2_get_alt(struct usb_function *fn, unsigned int intf) +{ + struct f_midi2 *midi2 = func_to_midi2(fn); + + if (intf == midi2->midi_if && + midi2->operation_mode == MIDI_OP_MODE_MIDI2) + return 1; + return 0; +} + +/* convert UMP direction to USB MIDI 2.0 direction */ +static unsigned int ump_to_usb_dir(unsigned int ump_dir) +{ + switch (ump_dir) { + case SNDRV_UMP_DIR_INPUT: + return USB_MS_GR_TRM_BLOCK_TYPE_INPUT_ONLY; + case SNDRV_UMP_DIR_OUTPUT: + return USB_MS_GR_TRM_BLOCK_TYPE_OUTPUT_ONLY; + default: + return USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL; + } +} + +/* assign GTB descriptors (for the given request) */ +static void assign_block_descriptors(struct f_midi2 *midi2, + struct usb_request *req, + int max_len) +{ + struct usb_ms20_gr_trm_block_header_descriptor header; + struct usb_ms20_gr_trm_block_descriptor *desc; + struct f_midi2_block_info *b; + struct f_midi2_ep *ep; + int i, blk, len; + char *data; + + len = sizeof(gtb_header_desc) + sizeof(gtb_desc) * midi2->total_blocks; + if (WARN_ON(len > midi2->info.req_buf_size)) + return; + + header = gtb_header_desc; + header.wTotalLength = cpu_to_le16(len); + if (max_len < len) { + len = min_t(int, len, sizeof(header)); + memcpy(req->buf, &header, len); + req->length = len; + req->zero = len < max_len; + return; + } + + memcpy(req->buf, &header, sizeof(header)); + data = req->buf + sizeof(header); + for (i = 0; i < midi2->num_eps; i++) { + ep = &midi2->midi2_eps[i]; + for (blk = 0; blk < ep->num_blks; blk++) { + b = &ep->blks[blk].info; + desc = (struct usb_ms20_gr_trm_block_descriptor *)data; + + *desc = gtb_desc; + desc->bGrpTrmBlkID = ep->blks[blk].gtb_id; + desc->bGrpTrmBlkType = ump_to_usb_dir(b->direction); + desc->nGroupTrm = b->first_group; + desc->nNumGroupTrm = b->num_groups; + desc->iBlockItem = ep->blks[blk].string_id; + + if (ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) + desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0; + else + desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128; + + if (b->is_midi1 == 2) { + desc->wMaxInputBandwidth = cpu_to_le16(1); + desc->wMaxOutputBandwidth = cpu_to_le16(1); + } + + data += sizeof(*desc); + } + } + + req->length = len; + req->zero = len < max_len; +} + +/* gadget function setup callback: handle GTB requests */ +static int f_midi2_setup(struct usb_function *fn, + const struct usb_ctrlrequest *ctrl) +{ + struct f_midi2 *midi2 = func_to_midi2(fn); + struct usb_composite_dev *cdev = fn->config->cdev; + struct usb_request *req = cdev->req; + u16 value, length; + + if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD || + ctrl->bRequest != USB_REQ_GET_DESCRIPTOR) + return -EOPNOTSUPP; + + value = le16_to_cpu(ctrl->wValue); + length = le16_to_cpu(ctrl->wLength); + + if ((value >> 8) != USB_DT_CS_GR_TRM_BLOCK) + return -EOPNOTSUPP; + + /* handle only altset 1 */ + if ((value & 0xff) != 1) + return -EOPNOTSUPP; + + assign_block_descriptors(midi2, req, length); + return usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); +} + +/* gadget function disable callback */ +static void f_midi2_disable(struct usb_function *fn) +{ + struct f_midi2 *midi2 = func_to_midi2(fn); + + midi2->operation_mode = MIDI_OP_MODE_UNSET; +} + +/* + * ALSA UMP ops: most of them are NOPs, only trigger for write is needed + */ +static int f_midi2_ump_open(struct snd_ump_endpoint *ump, int dir) +{ + return 0; +} + +static void f_midi2_ump_close(struct snd_ump_endpoint *ump, int dir) +{ +} + +static void f_midi2_ump_trigger(struct snd_ump_endpoint *ump, int dir, int up) +{ + struct f_midi2_ep *ep = ump->private_data; + + if (up && dir == SNDRV_RAWMIDI_STREAM_OUTPUT) + process_ump_transmit(ep); +} + +static void f_midi2_ump_drain(struct snd_ump_endpoint *ump, int dir) +{ +} + +static const struct snd_ump_ops f_midi2_ump_ops = { + .open = f_midi2_ump_open, + .close = f_midi2_ump_close, + .trigger = f_midi2_ump_trigger, + .drain = f_midi2_ump_drain, +}; + +/* + * ALSA UMP instance creation / deletion + */ +static void f_midi2_free_card(struct f_midi2 *midi2) +{ + if (midi2->card) { + snd_card_free_when_closed(midi2->card); + midi2->card = NULL; + } +} + +/* use a reverse direction for the gadget host */ +static int reverse_dir(int dir) +{ + if (!dir || dir == SNDRV_UMP_DIR_BIDIRECTION) + return dir; + return (dir == SNDRV_UMP_DIR_OUTPUT) ? + SNDRV_UMP_DIR_INPUT : SNDRV_UMP_DIR_OUTPUT; +} + +static int f_midi2_create_card(struct f_midi2 *midi2) +{ + struct snd_card *card; + struct snd_ump_endpoint *ump; + struct f_midi2_ep *ep; + int i, id, blk, err; + __be32 sw; + + err = snd_card_new(&midi2->gadget->dev, -1, NULL, THIS_MODULE, 0, + &card); + if (err < 0) + return err; + midi2->card = card; + + strcpy(card->driver, "f_midi2"); + strcpy(card->shortname, "MIDI 2.0 Gadget"); + strcpy(card->longname, "MIDI 2.0 Gadget"); + + id = 0; + for (i = 0; i < midi2->num_eps; i++) { + ep = &midi2->midi2_eps[i]; + err = snd_ump_endpoint_new(card, "MIDI 2.0 Gadget", id, + 1, 1, &ump); + if (err < 0) + goto error; + id++; + + ep->ump = ump; + ump->no_process_stream = true; + ump->private_data = ep; + ump->ops = &f_midi2_ump_ops; + if (midi2->info.static_block) + ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS; + ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8; + ump->info.protocol = (ep->info.protocol & 3) << 8; + ump->info.version = 0x0101; + ump->info.family_id = ep->info.family; + ump->info.model_id = ep->info.model; + ump->info.manufacturer_id = ep->info.manufacturer & 0xffffff; + sw = cpu_to_be32(ep->info.sw_revision); + memcpy(ump->info.sw_revision, &sw, 4); + + strscpy(ump->info.name, ump_ep_name(ep), + sizeof(ump->info.name)); + strscpy(ump->info.product_id, ump_product_id(ep), + sizeof(ump->info.product_id)); + strscpy(ump->core.name, ump->info.name, sizeof(ump->core.name)); + + for (blk = 0; blk < ep->num_blks; blk++) { + const struct f_midi2_block_info *b = &ep->blks[blk].info; + struct snd_ump_block *fb; + + err = snd_ump_block_new(ump, blk, + reverse_dir(b->direction), + b->first_group, b->num_groups, + &ep->blks[blk].fb); + if (err < 0) + goto error; + fb = ep->blks[blk].fb; + fb->info.active = !!b->active; + fb->info.midi_ci_version = b->midi_ci_version; + fb->info.ui_hint = reverse_dir(b->ui_hint); + fb->info.sysex8_streams = b->sysex8_streams; + fb->info.flags |= b->is_midi1; + strscpy(fb->info.name, ump_fb_name(b), + sizeof(fb->info.name)); + } + } + + for (i = 0; i < midi2->num_eps; i++) { + err = snd_ump_attach_legacy_rawmidi(midi2->midi2_eps[i].ump, + "Legacy MIDI", id); + if (err < 0) + goto error; + id++; + } + + err = snd_card_register(card); + if (err < 0) + goto error; + + return 0; + + error: + f_midi2_free_card(midi2); + return err; +} + +/* + * Creation of USB descriptors + */ +struct f_midi2_usb_config { + struct usb_descriptor_header **list; + unsigned int size; + unsigned int alloc; +}; + +static int append_config(struct f_midi2_usb_config *config, void *d) +{ + unsigned int size; + void *buf; + + if (config->size + 2 >= config->alloc) { + size = config->size + 16; + buf = krealloc(config->list, size * sizeof(void *), GFP_KERNEL); + if (!buf) + return -ENOMEM; + config->list = buf; + config->alloc = size; + } + + config->list[config->size] = d; + config->size++; + config->list[config->size] = NULL; + return 0; +} + +static int append_configs(struct f_midi2_usb_config *config, void **d) +{ + int err; + + for (; *d; d++) { + err = append_config(config, *d); + if (err) + return err; + } + return 0; +} + +static int f_midi2_create_usb_configs(struct f_midi2 *midi2, + struct f_midi2_usb_config *config, + int speed) +{ + void **midi1_eps; + int i, err; + + switch (speed) { + default: + case USB_SPEED_HIGH: + midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(512); + midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(512); + for (i = 0; i < midi2->num_eps; i++) + midi2_midi2_ep_out_desc[i].wMaxPacketSize = + cpu_to_le16(512); + fallthrough; + case USB_SPEED_FULL: + midi1_eps = midi2_midi1_ep_descs; + break; + case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: + midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(1024); + midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(1024); + for (i = 0; i < midi2->num_eps; i++) + midi2_midi2_ep_out_desc[i].wMaxPacketSize = + cpu_to_le16(1024); + midi1_eps = midi2_midi1_ep_ss_descs; + break; + } + + err = append_configs(config, midi2_audio_descs); + if (err < 0) + return err; + err = append_configs(config, midi2_midi1_descs); + if (err < 0) + return err; + err = append_configs(config, midi1_eps); + if (err < 0) + return err; + err = append_configs(config, midi2_midi2_descs); + if (err < 0) + return err; + + for (i = 0; i < midi2->num_eps; i++) { + err = append_config(config, &midi2_midi2_ep_out_desc[i]); + if (err < 0) + return err; + if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) { + err = append_config(config, &midi2_midi2_ep_out_ss_comp_desc); + if (err < 0) + return err; + } + err = append_config(config, &midi2_midi2_ep_out_class_desc[i]); + if (err < 0) + return err; + err = append_config(config, &midi2_midi2_ep_in_desc[i]); + if (err < 0) + return err; + if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) { + err = append_config(config, &midi2_midi2_ep_in_ss_comp_desc); + if (err < 0) + return err; + } + err = append_config(config, &midi2_midi2_ep_in_class_desc[i]); + if (err < 0) + return err; + } + + return 0; +} + +static void f_midi2_free_usb_configs(struct f_midi2_usb_config *config) +{ + kfree(config->list); + memset(config, 0, sizeof(*config)); +} + +/* as we use the static descriptors for simplicity, serialize bind call */ +static DEFINE_MUTEX(f_midi2_desc_mutex); + +/* fill MIDI2 EP class-specific descriptor */ +static void fill_midi2_class_desc(struct f_midi2_ep *ep, + struct usb_ms20_endpoint_descriptor_32 *cdesc) +{ + int blk; + + cdesc->bLength = USB_DT_MS20_ENDPOINT_SIZE(ep->num_blks); + cdesc->bDescriptorType = USB_DT_CS_ENDPOINT; + cdesc->bDescriptorSubtype = USB_MS_GENERAL_2_0; + cdesc->bNumGrpTrmBlock = ep->num_blks; + for (blk = 0; blk < ep->num_blks; blk++) + cdesc->baAssoGrpTrmBlkID[blk] = ep->blks[blk].gtb_id; +} + +/* initialize MIDI2 EP-in */ +static int f_midi2_init_midi2_ep_in(struct f_midi2 *midi2, int index) +{ + struct f_midi2_ep *ep = &midi2->midi2_eps[index]; + struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_in_desc[index]; + + desc->bLength = USB_DT_ENDPOINT_SIZE; + desc->bDescriptorType = USB_DT_ENDPOINT; + desc->bEndpointAddress = USB_DIR_IN; + desc->bmAttributes = USB_ENDPOINT_XFER_INT; + desc->wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET_INT); + desc->bInterval = 1; + + fill_midi2_class_desc(ep, &midi2_midi2_ep_in_class_desc[index]); + + return f_midi2_init_ep(midi2, ep, &ep->ep_in, desc, + midi2->info.num_reqs, f_midi2_ep_in_complete); +} + +/* initialize MIDI2 EP-out */ +static int f_midi2_init_midi2_ep_out(struct f_midi2 *midi2, int index) +{ + struct f_midi2_ep *ep = &midi2->midi2_eps[index]; + struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_out_desc[index]; + + desc->bLength = USB_DT_ENDPOINT_SIZE; + desc->bDescriptorType = USB_DT_ENDPOINT; + desc->bEndpointAddress = USB_DIR_OUT; + desc->bmAttributes = USB_ENDPOINT_XFER_BULK; + + fill_midi2_class_desc(ep, &midi2_midi2_ep_out_class_desc[index]); + + return f_midi2_init_ep(midi2, ep, &ep->ep_out, desc, + midi2->info.num_reqs, f_midi2_ep_out_complete); +} + +/* gadget function bind callback */ +static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_midi2 *midi2 = func_to_midi2(f); + struct f_midi2_ep *ep; + struct f_midi2_usb_config config = {}; + struct usb_gadget_strings string_fn = { + .language = 0x0409, /* en-us */ + .strings = midi2->string_defs, + }; + struct usb_gadget_strings *strings[] = { + &string_fn, + NULL, + }; + int i, blk, status; + + midi2->gadget = cdev->gadget; + midi2->operation_mode = MIDI_OP_MODE_UNSET; + + status = f_midi2_create_card(midi2); + if (status < 0) + goto fail_register; + + /* maybe allocate device-global string ID */ + midi2->strings = usb_gstrings_attach(c->cdev, strings, + midi2->total_blocks + 1); + if (IS_ERR(midi2->strings)) { + status = PTR_ERR(midi2->strings); + goto fail_string; + } + + mutex_lock(&f_midi2_desc_mutex); + midi2_midi1_if_desc.iInterface = midi2->strings[STR_IFACE].id; + midi2_midi2_if_desc.iInterface = midi2->strings[STR_IFACE].id; + for (i = 0; i < midi2->num_eps; i++) { + ep = &midi2->midi2_eps[i]; + for (blk = 0; blk < ep->num_blks; blk++) + ep->blks[blk].string_id = + midi2->strings[gtb_to_str_id(ep->blks[blk].gtb_id)].id; + } + + /* audio interface */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + midi2_audio_if_desc.bInterfaceNumber = status; + + /* MIDI streaming */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + midi2->midi_if = status; + midi2_midi1_if_desc.bInterfaceNumber = status; + midi2_midi2_if_desc.bInterfaceNumber = status; + midi2_audio_class_desc.baInterfaceNr[0] = status; + + /* allocate instance-specific endpoints */ + status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_in, + &midi2_midi1_ep_in_desc, 0, NULL); + if (status) + goto fail; + status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_out, + &midi2_midi1_ep_out_desc, 0, NULL); + if (status) + goto fail; + + for (i = 0; i < midi2->num_eps; i++) { + status = f_midi2_init_midi2_ep_in(midi2, i); + if (status) + goto fail; + status = f_midi2_init_midi2_ep_out(midi2, i); + if (status) + goto fail; + } + + status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_FULL); + if (status < 0) + goto fail; + f->fs_descriptors = usb_copy_descriptors(config.list); + if (!f->fs_descriptors) { + status = -ENOMEM; + goto fail; + } + f_midi2_free_usb_configs(&config); + + if (gadget_is_dualspeed(midi2->gadget)) { + status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_HIGH); + if (status < 0) + goto fail; + f->hs_descriptors = usb_copy_descriptors(config.list); + if (!f->hs_descriptors) { + status = -ENOMEM; + goto fail; + } + f_midi2_free_usb_configs(&config); + } + + if (gadget_is_superspeed(midi2->gadget)) { + status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_SUPER); + if (status < 0) + goto fail; + f->ss_descriptors = usb_copy_descriptors(config.list); + if (!f->ss_descriptors) { + status = -ENOMEM; + goto fail; + } + if (gadget_is_superspeed_plus(midi2->gadget)) { + f->ssp_descriptors = usb_copy_descriptors(config.list); + if (!f->ssp_descriptors) { + status = -ENOMEM; + goto fail; + } + } + f_midi2_free_usb_configs(&config); + } + + mutex_unlock(&f_midi2_desc_mutex); + return 0; + +fail: + f_midi2_free_usb_configs(&config); + mutex_unlock(&f_midi2_desc_mutex); + usb_free_all_descriptors(f); +fail_string: + f_midi2_free_card(midi2); +fail_register: + ERROR(midi2, "%s: can't bind, err %d\n", f->name, status); + return status; +} + +/* gadget function unbind callback */ +static void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_midi2 *midi2 = func_to_midi2(f); + int i; + + f_midi2_free_card(midi2); + + f_midi2_free_ep(&midi2->midi1_ep_in); + f_midi2_free_ep(&midi2->midi1_ep_out); + for (i = 0; i < midi2->num_eps; i++) { + f_midi2_free_ep(&midi2->midi2_eps[i].ep_in); + f_midi2_free_ep(&midi2->midi2_eps[i].ep_out); + } + + usb_free_all_descriptors(f); +} + +/* create a f_midi2_block_opts instance for the given block number */ +static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts, + unsigned int blk, + struct f_midi2_block_opts **block_p) +{ + struct f_midi2_block_opts *block_opts; + + block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL); + if (!block_opts) + return -ENOMEM; + + block_opts->ep = ep_opts; + block_opts->id = blk; + + /* set up the default values */ + block_opts->info.direction = SNDRV_UMP_DIR_BIDIRECTION; + block_opts->info.first_group = 0; + block_opts->info.num_groups = 1; + block_opts->info.ui_hint = SNDRV_UMP_BLOCK_UI_HINT_BOTH; + block_opts->info.active = 1; + + ep_opts->blks[blk] = block_opts; + *block_p = block_opts; + return 0; +} + +/* create a f_midi2_ep_opts instance */ +static int f_midi2_ep_opts_create(struct f_midi2_opts *opts, + unsigned int index, + struct f_midi2_ep_opts **ep_p) +{ + struct f_midi2_ep_opts *ep_opts; + + ep_opts = kzalloc(sizeof(*ep_opts), GFP_KERNEL); + if (!ep_opts) + return -ENOMEM; + + ep_opts->opts = opts; + ep_opts->index = index; + + /* set up the default values */ + ep_opts->info.protocol = 2; + ep_opts->info.protocol_caps = 3; + + opts->eps[index] = ep_opts; + *ep_p = ep_opts; + return 0; +} + +static const struct config_item_type f_midi2_func_type = { + .ct_owner = THIS_MODULE, +}; + +static void f_midi2_free_inst(struct usb_function_instance *f) +{ + struct f_midi2_opts *opts; + + opts = container_of(f, struct f_midi2_opts, func_inst); + + /* we have only one EP and one FB */ + if (opts->eps[0]) { + kfree(opts->eps[0]->blks[0]); + kfree(opts->eps[0]); + } + kfree(opts); +} + +/* gadget alloc_inst */ +static struct usb_function_instance *f_midi2_alloc_inst(void) +{ + struct f_midi2_opts *opts; + struct f_midi2_ep_opts *ep_opts; + struct f_midi2_block_opts *block_opts; + int ret; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = f_midi2_free_inst; + opts->info.process_ump = true; + opts->info.static_block = true; + opts->info.num_reqs = 32; + opts->info.req_buf_size = 512; + + ret = f_midi2_ep_opts_create(opts, 0, &ep_opts); + if (ret) { + kfree(opts); + return ERR_PTR(ret); + } + + /* create the default block */ + ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts); + if (ret) { + kfree(ep_opts); + kfree(opts); + return ERR_PTR(ret); + } + + config_group_init_type_name(&opts->func_inst.group, "", + &f_midi2_func_type); + return &opts->func_inst; +} + +static void do_f_midi2_free(struct f_midi2 *midi2, struct f_midi2_opts *opts) +{ + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); + kfree(midi2->string_defs); + kfree(midi2); +} + +static void f_midi2_free(struct usb_function *f) +{ + do_f_midi2_free(func_to_midi2(f), + container_of(f->fi, struct f_midi2_opts, func_inst)); +} + +/* gadget alloc callback */ +static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) +{ + struct f_midi2 *midi2; + struct f_midi2_opts *opts; + int i; + + midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL); + if (!midi2) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_midi2_opts, func_inst); + mutex_lock(&opts->lock); + ++opts->refcnt; + mutex_unlock(&opts->lock); + + spin_lock_init(&midi2->queue_lock); + + midi2->func.name = "midi2_func"; + midi2->func.bind = f_midi2_bind; + midi2->func.unbind = f_midi2_unbind; + midi2->func.get_alt = f_midi2_get_alt; + midi2->func.set_alt = f_midi2_set_alt; + midi2->func.setup = f_midi2_setup; + midi2->func.disable = f_midi2_disable; + midi2->func.free_func = f_midi2_free; + + midi2->info = opts->info; + + /* fixed 1 UMP EP and 1 UMP FB as of now */ + midi2->num_eps = 1; + midi2->midi2_eps[0].info = opts->eps[0]->info; + midi2->midi2_eps[0].card = midi2; + midi2->midi2_eps[0].num_blks = 1; + midi2->midi2_eps[0].blks[0].info = opts->eps[0]->blks[0]->info; + midi2->midi2_eps[0].blks[0].gtb_id = 1; + + for (i = 0; i < midi2->num_eps; i++) + midi2->total_blocks += midi2->midi2_eps[i].num_blks; + + midi2->string_defs = kcalloc(midi2->total_blocks + 1, + sizeof(*midi2->string_defs), GFP_KERNEL); + if (!midi2->string_defs) { + do_f_midi2_free(midi2, opts); + return ERR_PTR(-ENOMEM); + } + + if (opts->info.iface_name && *opts->info.iface_name) + midi2->string_defs[0].s = opts->info.iface_name; + else + midi2->string_defs[0].s = ump_ep_name(&midi2->midi2_eps[0]); + midi2->string_defs[1].s = ump_fb_name(&midi2->midi2_eps[0].blks[0].info); + + return &midi2->func; +} + +DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/u_midi2.h b/drivers/usb/gadget/function/u_midi2.h new file mode 100644 index 000000000000..a68dc2ea035e --- /dev/null +++ b/drivers/usb/gadget/function/u_midi2.h @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Utility definitions for MIDI 2.0 function + */ + +#ifndef U_MIDI2_H +#define U_MIDI2_H + +#include +#include + +struct f_midi2_opts; +struct f_midi2_ep_opts; +struct f_midi2_block_opts; + +/* UMP Function Block info */ +struct f_midi2_block_info { + unsigned int direction; /* FB direction: 1-3 */ + unsigned int first_group; /* first UMP group: 0-15 */ + unsigned int num_groups; /* number of UMP groups: 1-16 */ + unsigned int ui_hint; /* UI-hint: 0-3 */ + unsigned int midi_ci_version; /* MIDI-CI version: 0-255 */ + unsigned int sysex8_streams; /* number of sysex8 streams: 0-255 */ + unsigned int is_midi1; /* MIDI 1.0 port: 0-2 */ + bool active; /* FB active flag: bool */ + const char *name; /* FB name */ +}; + +/* UMP Endpoint info */ +struct f_midi2_ep_info { + unsigned int protocol_caps; /* protocol capabilities: 1-3 */ + unsigned int protocol; /* default protocol: 1-2 */ + unsigned int manufacturer; /* manufacturer id: 0-0xffffff */ + unsigned int family; /* device family id: 0-0xffff */ + unsigned int model; /* device model id: 0x-0xffff */ + unsigned int sw_revision; /* software revision: 32bit */ + + const char *ep_name; /* Endpoint name */ + const char *product_id; /* Product ID */ +}; + +struct f_midi2_card_info { + bool process_ump; /* process UMP stream: bool */ + bool static_block; /* static FBs: bool */ + unsigned int req_buf_size; /* request buffer size */ + unsigned int num_reqs; /* number of requests */ + const char *iface_name; /* interface name */ +}; + +struct f_midi2_block_opts { + struct config_group group; + unsigned int id; + struct f_midi2_block_info info; + struct f_midi2_ep_opts *ep; +}; + +struct f_midi2_ep_opts { + struct config_group group; + unsigned int index; + struct f_midi2_ep_info info; + struct f_midi2_block_opts *blks[SNDRV_UMP_MAX_BLOCKS]; + struct f_midi2_opts *opts; +}; + +#define MAX_UMP_EPS 4 +#define MAX_CABLES 16 + +struct f_midi2_opts { + struct usb_function_instance func_inst; + struct mutex lock; + int refcnt; + + struct f_midi2_card_info info; + + unsigned int num_eps; + struct f_midi2_ep_opts *eps[MAX_UMP_EPS]; +}; + +#endif /* U_MIDI2_H */ -- cgit From 29ee7a4dddd5caa18d1cef000f20c6af43f762f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Jul 2023 08:22:01 +0200 Subject: usb: gadget: midi2: Add configfs support This patch adds the support of configfs to MIDI 2.0 function driver for users to allow configuring the UMP Endpoint and Function Blocks more flexibly. The configuration is in a tree form. The top-most contains some card-level configurations. UMP Endpoints are defined in subdirectories (ep.0, ep.1, etc) that contain Endpoint-specific configurations such as ep_name, etc. And, UMP Function Blocks are defined in the subdirectories (block.0, block.1, etc) under EP subdirectory. As default, the driver creates a single UMP Endpoint (ep.0) and a single Function Block (block.0) to work in a minimalistic manner. User can modify those attributes freely to fit with the demands. When multiple Function Blocks are required, user can create another directory as block.1, block.2, and so on (up to block.31). A block.* directory can be deleted dynamically, too. A caveat is that the block number has to be continuous. Similarly, when multiple UMP Endpoints are required, user can create another directory as ep.1, ep.2, up to ep.3. Also, some driver behavior can be controlled in the card top-level configs. e.g. you can pass process_ump=0 to disable the processing of UMP Stream messages. This would be equivalent with the older MIDI 2.0 spec that doesn't support UMP v1.1 features. The configfs interface checks upper- / lower-bound of input values, and more sanity checks are performed at binding. Attributes can't be changed any longer once when the instance is linked to UDC. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20230725062206.9674-3-tiwai@suse.de Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/configfs-usb-gadget-midi2 | 52 ++ drivers/usb/gadget/function/f_midi2.c | 621 ++++++++++++++++++++- 2 files changed, 650 insertions(+), 23 deletions(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-midi2 diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi2 b/Documentation/ABI/testing/configfs-usb-gadget-midi2 new file mode 100644 index 000000000000..a3a036d784c7 --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-midi2 @@ -0,0 +1,52 @@ +What: /config/usb-gadget/gadget/functions/midi2.name +Date: Jul 2023 +KernelVersion: 6.6 +Description: + The attributes: + + ============ =============================================== + process_ump Flag to process UMP Stream messages (0 or 1) + static_block Flag for static blocks (0 or 1) + iface_name MIDI interface name string + ============ =============================================== + +What: /config/usb-gadget/gadget/functions/midi2.name/ep.number +Date: Jul 2023 +KernelVersion: 6.6 +Description: + This group contains a UMP Endpoint configuration. + A new Endpoint starts from 0, and can be up to 3. + + The attributes: + + ============= =============================================== + protocol_caps MIDI protocol capabilities (1, 2 or 3 for both) + protocol Default MIDI protocol (1 or 2) + ep_name UMP Endpoint name string + product_id Product ID string + manufacturer Manufacture ID (24 bit) + family Device family ID (16 bit) + model Device model ID (16 bit) + sw_revision Software Revision (32 bit) + ============= =============================================== + +What: /config/usb-gadget/gadget/functions/midi2.name/ep.number/block.number +Date: Jul 2023 +KernelVersion: 6.6 +Description: + This group contains a UMP Function Block configuration. + A new block starts from 0, and can be up to 31. + + The attributes: + + =============== =============================================== + name Function Block name string + direction 1: input, 2: output, 3: bidirectional + first_group The first UMP Group number (0-15) + num_groups The number of groups in this FB (1-16) + ui_hint 0: unknown, 1: receiver, 2: sender, 3: both + midi_ci_verison Supported MIDI-CI version number (8 bit) + is_midi1 Legacy MIDI 1.0 device (0, 1 or 2) + sysex8_streams Max number of SysEx8 streams (8 bit) + active Active FB flag (0 or 1) + =============== =============================================== diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index 848cb3150deb..c68a6fa0d237 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -292,7 +292,7 @@ static struct usb_interface_descriptor midi2_midi2_if_desc = { .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, // to be filled .bAlternateSetting = 1, - .bNumEndpoints = 2, + .bNumEndpoints = 2, // to be filled .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, .bInterfaceProtocol = 0, @@ -1403,6 +1403,8 @@ static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f) midi2->strings[gtb_to_str_id(ep->blks[blk].gtb_id)].id; } + midi2_midi2_if_desc.bNumEndpoints = midi2->num_eps * 2; + /* audio interface */ status = usb_interface_id(c, f); if (status < 0) @@ -1510,16 +1512,274 @@ static void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); } +/* + * ConfigFS interface + */ + +/* type conversion helpers */ +static inline struct f_midi2_opts *to_f_midi2_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_midi2_opts, + func_inst.group); +} + +static inline struct f_midi2_ep_opts * +to_f_midi2_ep_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_midi2_ep_opts, + group); +} + +static inline struct f_midi2_block_opts * +to_f_midi2_block_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_midi2_block_opts, + group); +} + +/* trim the string to be usable for EP and FB name strings */ +static void make_name_string(char *s) +{ + char *p; + + p = strchr(s, '\n'); + if (p) + *p = 0; + + p = s + strlen(s); + for (; p > s && isspace(*p); p--) + *p = 0; +} + +/* configfs helpers: generic show/store for unisnged int */ +static ssize_t f_midi2_opts_uint_show(struct f_midi2_opts *opts, + u32 val, const char *format, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, format, val); + mutex_unlock(&opts->lock); + return result; +} + +static ssize_t f_midi2_opts_uint_store(struct f_midi2_opts *opts, + u32 *valp, u32 minval, u32 maxval, + const char *page, size_t len) +{ + int ret; + u32 val; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou32(page, 0, &val); + if (ret) + goto end; + if (val < minval || val > maxval) { + ret = -EINVAL; + goto end; + } + + *valp = val; + ret = len; + +end: + mutex_unlock(&opts->lock); + return ret; +} + +/* generic store for bool */ +static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts, + bool *valp, const char *page, size_t len) +{ + int ret; + bool val; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtobool(page, &val); + if (ret) + goto end; + *valp = val; + ret = len; + +end: + mutex_unlock(&opts->lock); + return ret; +} + +/* generic show/store for string */ +static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts, + const char *str, char *page) +{ + int result = 0; + + mutex_lock(&opts->lock); + if (str) + result = scnprintf(page, PAGE_SIZE, "%s\n", str); + mutex_unlock(&opts->lock); + return result; +} + +static ssize_t f_midi2_opts_str_store(struct f_midi2_opts *opts, + const char **strp, size_t maxlen, + const char *page, size_t len) +{ + char *c; + int ret; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + c = kstrndup(page, min(len, maxlen), GFP_KERNEL); + if (!c) { + ret = -ENOMEM; + goto end; + } + + kfree(*strp); + make_name_string(c); + *strp = c; + ret = len; + +end: + mutex_unlock(&opts->lock); + return ret; +} + +/* + * Definitions for UMP Block config + */ + +/* define an uint option for block */ +#define F_MIDI2_BLOCK_OPT(name, format, minval, maxval) \ +static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\ + char *page) \ +{ \ + struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \ + return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \ + format "\n", page); \ +} \ + \ +static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\ + const char *page, size_t len) \ +{ \ + struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \ + return f_midi2_opts_uint_store(opts->ep->opts, &opts->info.name,\ + minval, maxval, page, len); \ +} \ + \ +CONFIGFS_ATTR(f_midi2_block_opts_, name) + +/* define a boolean option for block */ +#define F_MIDI2_BLOCK_BOOL_OPT(name) \ +static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\ + char *page) \ +{ \ + struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \ + return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \ + "%u\n", page); \ +} \ + \ +static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\ + const char *page, size_t len) \ +{ \ + struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \ + return f_midi2_opts_bool_store(opts->ep->opts, &opts->info.name,\ + page, len); \ +} \ + \ +CONFIGFS_ATTR(f_midi2_block_opts_, name) + +F_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3); +F_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15); +F_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16); +F_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3); +F_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1); +F_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255); +F_MIDI2_BLOCK_OPT(is_midi1, "%u", 0, 2); +F_MIDI2_BLOCK_BOOL_OPT(active); + +static ssize_t f_midi2_block_opts_name_show(struct config_item *item, + char *page) +{ + struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); + + return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page); +} + +static ssize_t f_midi2_block_opts_name_store(struct config_item *item, + const char *page, size_t len) +{ + struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); + + return f_midi2_opts_str_store(opts->ep->opts, &opts->info.name, 128, + page, len); +} + +CONFIGFS_ATTR(f_midi2_block_opts_, name); + +static struct configfs_attribute *f_midi2_block_attrs[] = { + &f_midi2_block_opts_attr_direction, + &f_midi2_block_opts_attr_first_group, + &f_midi2_block_opts_attr_num_groups, + &f_midi2_block_opts_attr_ui_hint, + &f_midi2_block_opts_attr_midi_ci_version, + &f_midi2_block_opts_attr_sysex8_streams, + &f_midi2_block_opts_attr_is_midi1, + &f_midi2_block_opts_attr_active, + &f_midi2_block_opts_attr_name, + NULL, +}; + +static void f_midi2_block_opts_release(struct config_item *item) +{ + struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); + + kfree(opts->info.name); + kfree(opts); +} + +static struct configfs_item_operations f_midi2_block_item_ops = { + .release = f_midi2_block_opts_release, +}; + +static const struct config_item_type f_midi2_block_type = { + .ct_item_ops = &f_midi2_block_item_ops, + .ct_attrs = f_midi2_block_attrs, + .ct_owner = THIS_MODULE, +}; + /* create a f_midi2_block_opts instance for the given block number */ static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts, unsigned int blk, struct f_midi2_block_opts **block_p) { struct f_midi2_block_opts *block_opts; + int ret = 0; + + mutex_lock(&ep_opts->opts->lock); + if (ep_opts->opts->refcnt || ep_opts->blks[blk]) { + ret = -EBUSY; + goto out; + } block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL); - if (!block_opts) - return -ENOMEM; + if (!block_opts) { + ret = -ENOMEM; + goto out; + } block_opts->ep = ep_opts; block_opts->id = blk; @@ -1533,9 +1793,143 @@ static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts, ep_opts->blks[blk] = block_opts; *block_p = block_opts; - return 0; + + mutex_unlock(&ep_opts->opts->lock); + out: + return ret; } +/* make_group callback for a block */ +static struct config_group * +f_midi2_opts_block_make(struct config_group *group, const char *name) +{ + struct f_midi2_ep_opts *ep_opts; + struct f_midi2_block_opts *block_opts; + unsigned int blk; + int ret; + + if (strncmp(name, "block.", 6)) + return ERR_PTR(-EINVAL); + ret = kstrtouint(name + 6, 10, &blk); + if (ret) + return ERR_PTR(ret); + + ep_opts = to_f_midi2_ep_opts(&group->cg_item); + + if (blk >= SNDRV_UMP_MAX_BLOCKS) + return ERR_PTR(-EINVAL); + if (ep_opts->blks[blk]) + return ERR_PTR(-EBUSY); + ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts); + if (ret) + return ERR_PTR(ret); + + config_group_init_type_name(&block_opts->group, name, + &f_midi2_block_type); + return &block_opts->group; +} + +/* drop_item callback for a block */ +static void +f_midi2_opts_block_drop(struct config_group *group, struct config_item *item) +{ + struct f_midi2_block_opts *block_opts = to_f_midi2_block_opts(item); + + mutex_lock(&block_opts->ep->opts->lock); + block_opts->ep->blks[block_opts->id] = NULL; + mutex_unlock(&block_opts->ep->opts->lock); + config_item_put(item); +} + +/* + * Definitions for UMP Endpoint config + */ + +/* define an uint option for EP */ +#define F_MIDI2_EP_OPT(name, format, minval, maxval) \ +static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ + return f_midi2_opts_uint_show(opts->opts, opts->info.name, \ + format "\n", page); \ +} \ + \ +static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \ + const char *page, size_t len)\ +{ \ + struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ + return f_midi2_opts_uint_store(opts->opts, &opts->info.name, \ + minval, maxval, page, len); \ +} \ + \ +CONFIGFS_ATTR(f_midi2_ep_opts_, name) + +/* define a string option for EP */ +#define F_MIDI2_EP_STR_OPT(name, maxlen) \ +static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ + return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\ +} \ + \ +static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ + return f_midi2_opts_str_store(opts->opts, &opts->info.name, maxlen,\ + page, len); \ +} \ + \ +CONFIGFS_ATTR(f_midi2_ep_opts_, name) + +F_MIDI2_EP_OPT(protocol, "0x%x", 1, 2); +F_MIDI2_EP_OPT(protocol_caps, "0x%x", 1, 3); +F_MIDI2_EP_OPT(manufacturer, "0x%x", 0, 0xffffff); +F_MIDI2_EP_OPT(family, "0x%x", 0, 0xffff); +F_MIDI2_EP_OPT(model, "0x%x", 0, 0xffff); +F_MIDI2_EP_OPT(sw_revision, "0x%x", 0, 0xffffffff); +F_MIDI2_EP_STR_OPT(ep_name, 128); +F_MIDI2_EP_STR_OPT(product_id, 128); + +static struct configfs_attribute *f_midi2_ep_attrs[] = { + &f_midi2_ep_opts_attr_protocol, + &f_midi2_ep_opts_attr_protocol_caps, + &f_midi2_ep_opts_attr_ep_name, + &f_midi2_ep_opts_attr_product_id, + &f_midi2_ep_opts_attr_manufacturer, + &f_midi2_ep_opts_attr_family, + &f_midi2_ep_opts_attr_model, + &f_midi2_ep_opts_attr_sw_revision, + NULL, +}; + +static void f_midi2_ep_opts_release(struct config_item *item) +{ + struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); + + kfree(opts->info.ep_name); + kfree(opts->info.product_id); + kfree(opts); +} + +static struct configfs_item_operations f_midi2_ep_item_ops = { + .release = f_midi2_ep_opts_release, +}; + +static struct configfs_group_operations f_midi2_ep_group_ops = { + .make_group = f_midi2_opts_block_make, + .drop_item = f_midi2_opts_block_drop, +}; + +static const struct config_item_type f_midi2_ep_type = { + .ct_item_ops = &f_midi2_ep_item_ops, + .ct_group_ops = &f_midi2_ep_group_ops, + .ct_attrs = f_midi2_ep_attrs, + .ct_owner = THIS_MODULE, +}; + /* create a f_midi2_ep_opts instance */ static int f_midi2_ep_opts_create(struct f_midi2_opts *opts, unsigned int index, @@ -1559,7 +1953,119 @@ static int f_midi2_ep_opts_create(struct f_midi2_opts *opts, return 0; } +/* make_group callback for an EP */ +static struct config_group * +f_midi2_opts_ep_make(struct config_group *group, const char *name) +{ + struct f_midi2_opts *opts; + struct f_midi2_ep_opts *ep_opts; + unsigned int index; + int ret; + + if (strncmp(name, "ep.", 3)) + return ERR_PTR(-EINVAL); + ret = kstrtouint(name + 3, 10, &index); + if (ret) + return ERR_PTR(ret); + + opts = to_f_midi2_opts(&group->cg_item); + if (index >= MAX_UMP_EPS) + return ERR_PTR(-EINVAL); + if (opts->eps[index]) + return ERR_PTR(-EBUSY); + ret = f_midi2_ep_opts_create(opts, index, &ep_opts); + if (ret) + return ERR_PTR(ret); + + config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type); + return &ep_opts->group; +} + +/* drop_item callback for an EP */ +static void +f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item) +{ + struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item); + + mutex_lock(&ep_opts->opts->lock); + ep_opts->opts->eps[ep_opts->index] = NULL; + mutex_unlock(&ep_opts->opts->lock); + config_item_put(item); +} + +/* + * Definitions for card config + */ + +/* define a bool option for card */ +#define F_MIDI2_BOOL_OPT(name) \ +static ssize_t f_midi2_opts_##name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct f_midi2_opts *opts = to_f_midi2_opts(item); \ + return f_midi2_opts_uint_show(opts, opts->info.name, \ + "%u\n", page); \ +} \ + \ +static ssize_t f_midi2_opts_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct f_midi2_opts *opts = to_f_midi2_opts(item); \ + return f_midi2_opts_bool_store(opts, &opts->info.name, \ + page, len); \ +} \ + \ +CONFIGFS_ATTR(f_midi2_opts_, name) + +F_MIDI2_BOOL_OPT(process_ump); +F_MIDI2_BOOL_OPT(static_block); + +static ssize_t f_midi2_opts_iface_name_show(struct config_item *item, + char *page) +{ + struct f_midi2_opts *opts = to_f_midi2_opts(item); + + return f_midi2_opts_str_show(opts, opts->info.iface_name, page); +} + +static ssize_t f_midi2_opts_iface_name_store(struct config_item *item, + const char *page, size_t len) +{ + struct f_midi2_opts *opts = to_f_midi2_opts(item); + + return f_midi2_opts_str_store(opts, &opts->info.iface_name, 128, + page, len); +} + +CONFIGFS_ATTR(f_midi2_opts_, iface_name); + +static struct configfs_attribute *f_midi2_attrs[] = { + &f_midi2_opts_attr_process_ump, + &f_midi2_opts_attr_static_block, + &f_midi2_opts_attr_iface_name, + NULL +}; + +static void f_midi2_opts_release(struct config_item *item) +{ + struct f_midi2_opts *opts = to_f_midi2_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations f_midi2_item_ops = { + .release = f_midi2_opts_release, +}; + +static struct configfs_group_operations f_midi2_group_ops = { + .make_group = f_midi2_opts_ep_make, + .drop_item = f_midi2_opts_ep_drop, +}; + static const struct config_item_type f_midi2_func_type = { + .ct_item_ops = &f_midi2_item_ops, + .ct_group_ops = &f_midi2_group_ops, + .ct_attrs = f_midi2_attrs, .ct_owner = THIS_MODULE, }; @@ -1569,11 +2075,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f) opts = container_of(f, struct f_midi2_opts, func_inst); - /* we have only one EP and one FB */ - if (opts->eps[0]) { - kfree(opts->eps[0]->blks[0]); - kfree(opts->eps[0]); - } + kfree(opts->info.iface_name); kfree(opts); } @@ -1596,6 +2098,7 @@ static struct usb_function_instance *f_midi2_alloc_inst(void) opts->info.num_reqs = 32; opts->info.req_buf_size = 512; + /* create the default ep */ ret = f_midi2_ep_opts_create(opts, 0, &ep_opts); if (ret) { kfree(opts); @@ -1612,6 +2115,15 @@ static struct usb_function_instance *f_midi2_alloc_inst(void) config_group_init_type_name(&opts->func_inst.group, "", &f_midi2_func_type); + + config_group_init_type_name(&ep_opts->group, "ep.0", + &f_midi2_ep_type); + configfs_add_default_group(&ep_opts->group, &opts->func_inst.group); + + config_group_init_type_name(&block_opts->group, "block.0", + &f_midi2_block_type); + configfs_add_default_group(&block_opts->group, &ep_opts->group); + return &opts->func_inst; } @@ -1630,12 +2142,58 @@ static void f_midi2_free(struct usb_function *f) container_of(f->fi, struct f_midi2_opts, func_inst)); } +/* verify the parameters set up via configfs; + * return the number of EPs or a negative error + */ +static int verify_parameters(struct f_midi2_opts *opts) +{ + int i, j, num_eps, num_blks; + struct f_midi2_ep_info *ep; + struct f_midi2_block_info *bp; + + for (num_eps = 0; num_eps < MAX_UMP_EPS && opts->eps[num_eps]; + num_eps++) + ; + if (!num_eps) { + pr_err("f_midi2: No EP is defined\n"); + return -EINVAL; + } + + num_blks = 0; + for (i = 0; i < num_eps; i++) { + ep = &opts->eps[i]->info; + if (!(ep->protocol_caps & ep->protocol)) { + pr_err("f_midi2: Invalid protocol 0x%x (caps 0x%x) for EP %d\n", + ep->protocol, ep->protocol_caps, i); + return -EINVAL; + } + + for (j = 0; j < SNDRV_UMP_MAX_BLOCKS && opts->eps[i]->blks[j]; + j++, num_blks++) { + bp = &opts->eps[i]->blks[j]->info; + if (bp->first_group + bp->num_groups > SNDRV_UMP_MAX_GROUPS) { + pr_err("f_midi2: Invalid group definitions for block %d:%d\n", + i, j); + return -EINVAL; + } + } + } + if (!num_blks) { + pr_err("f_midi2: No block is defined\n"); + return -EINVAL; + } + + return num_eps; +} + /* gadget alloc callback */ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) { struct f_midi2 *midi2; struct f_midi2_opts *opts; - int i; + struct f_midi2_ep *ep; + struct f_midi2_block *bp; + int i, num_eps, blk; midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL); if (!midi2) @@ -1643,6 +2201,12 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) opts = container_of(fi, struct f_midi2_opts, func_inst); mutex_lock(&opts->lock); + num_eps = verify_parameters(opts); + if (num_eps < 0) { + mutex_unlock(&opts->lock); + kfree(midi2); + return ERR_PTR(num_eps); + } ++opts->refcnt; mutex_unlock(&opts->lock); @@ -1658,17 +2222,20 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) midi2->func.free_func = f_midi2_free; midi2->info = opts->info; + midi2->num_eps = num_eps; - /* fixed 1 UMP EP and 1 UMP FB as of now */ - midi2->num_eps = 1; - midi2->midi2_eps[0].info = opts->eps[0]->info; - midi2->midi2_eps[0].card = midi2; - midi2->midi2_eps[0].num_blks = 1; - midi2->midi2_eps[0].blks[0].info = opts->eps[0]->blks[0]->info; - midi2->midi2_eps[0].blks[0].gtb_id = 1; - - for (i = 0; i < midi2->num_eps; i++) - midi2->total_blocks += midi2->midi2_eps[i].num_blks; + for (i = 0; i < num_eps; i++) { + ep = &midi2->midi2_eps[i]; + ep->info = opts->eps[i]->info; + ep->card = midi2; + for (blk = 0; blk < SNDRV_UMP_MAX_BLOCKS && + opts->eps[i]->blks[blk]; blk++) { + bp = &ep->blks[blk]; + ep->num_blks++; + bp->info = opts->eps[i]->blks[blk]->info; + bp->gtb_id = ++midi2->total_blocks; + } + } midi2->string_defs = kcalloc(midi2->total_blocks + 1, sizeof(*midi2->string_defs), GFP_KERNEL); @@ -1678,10 +2245,18 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) } if (opts->info.iface_name && *opts->info.iface_name) - midi2->string_defs[0].s = opts->info.iface_name; + midi2->string_defs[STR_IFACE].s = opts->info.iface_name; else - midi2->string_defs[0].s = ump_ep_name(&midi2->midi2_eps[0]); - midi2->string_defs[1].s = ump_fb_name(&midi2->midi2_eps[0].blks[0].info); + midi2->string_defs[STR_IFACE].s = ump_ep_name(&midi2->midi2_eps[0]); + + for (i = 0; i < midi2->num_eps; i++) { + ep = &midi2->midi2_eps[i]; + for (blk = 0; blk < ep->num_blks; blk++) { + bp = &ep->blks[blk]; + midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s = + ump_fb_name(&bp->info); + } + } return &midi2->func; } -- cgit From 856fa444b0982746f95e801bab0e586533eb29a3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Jul 2023 08:22:02 +0200 Subject: usb: gadget: midi2: Dynamically create MIDI 1.0 altset descriptors This patch extends MIDI 2.0 function driver to deal with more MIDI1 Jacks depending on the given Block configuration. For MIDI 1.0, we take the configuration given in Function Block 0, and create MIDI Jacks and Endpoints depending on the definition there. That is, when more UMP Groups are defined in the Block 0, the corresponding MIDI1 Jacks will be created. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20230725062206.9674-4-tiwai@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_midi2.c | 228 +++++++++++++++++++++++----------- 1 file changed, 157 insertions(+), 71 deletions(-) diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index c68a6fa0d237..b15d832ff441 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -151,7 +151,7 @@ static struct usb_ms20_gr_trm_block_descriptor gtb_desc = { }; DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); -DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); +DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32); @@ -185,7 +185,7 @@ static struct usb_interface_descriptor midi2_midi1_if_desc = { .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, // to be filled .bAlternateSetting = 0, - .bNumEndpoints = 2, + .bNumEndpoints = 2, // to be filled .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, .bInterfaceProtocol = 0, @@ -200,50 +200,6 @@ static struct usb_ms_header_descriptor midi2_midi1_class_desc = { .wTotalLength = __cpu_to_le16(0x41), // to be calculated }; -/* MIDI 1.0 IN (Embedded) Jack */ -static struct usb_midi_in_jack_descriptor midi2_midi1_in_jack1_desc = { - .bLength = 0x06, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = 0x01, - .iJack = 0, -}; - -/* MIDI 1.0 IN (External) Jack */ -static struct usb_midi_in_jack_descriptor midi2_midi1_in_jack2_desc = { - .bLength = 0x06, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = 0x02, - .iJack = 0, -}; - -/* MIDI 1.0 OUT (Embedded) Jack */ -static struct usb_midi_out_jack_descriptor_1 midi2_midi1_out_jack1_desc = { - .bLength = 0x09, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = 0x03, - .bNrInputPins = 1, - .pins = { { 0x02, 0x01 } }, - .iJack = 0, -}; - -/* MIDI 1.0 OUT (External) Jack */ -static struct usb_midi_out_jack_descriptor_1 midi2_midi1_out_jack2_desc = { - .bLength = 0x09, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = 0x04, - .bNrInputPins = 1, - .pins = { { 0x01, 0x01 } }, - .iJack = 0, -}; - /* MIDI 1.0 EP OUT */ static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, @@ -257,8 +213,8 @@ static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = { .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, }; -static struct usb_ms_endpoint_descriptor_1 midi2_midi1_ep_out_class_desc = { - .bLength = 0x05, +static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_out_class_desc = { + .bLength = 0x05, // to be filled .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, .bNumEmbMIDIJack = 1, @@ -278,8 +234,8 @@ static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = { .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, }; -static struct usb_ms_endpoint_descriptor_1 midi2_midi1_ep_in_class_desc = { - .bLength = 0x05, +static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_in_class_desc = { + .bLength = 0x05, // to be filled .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, .bNumEmbMIDIJack = 1, @@ -337,25 +293,29 @@ static void *midi2_audio_descs[] = { static void *midi2_midi1_descs[] = { &midi2_midi1_if_desc, &midi2_midi1_class_desc, - &midi2_midi1_in_jack1_desc, - &midi2_midi1_in_jack2_desc, - &midi2_midi1_out_jack1_desc, - &midi2_midi1_out_jack2_desc, NULL }; -static void *midi2_midi1_ep_descs[] = { +static void *midi2_midi1_ep_out_descs[] = { &midi2_midi1_ep_out_desc, &midi2_midi1_ep_out_class_desc, + NULL +}; + +static void *midi2_midi1_ep_in_descs[] = { &midi2_midi1_ep_in_desc, &midi2_midi1_ep_in_class_desc, NULL }; -static void *midi2_midi1_ep_ss_descs[] = { +static void *midi2_midi1_ep_out_ss_descs[] = { &midi2_midi1_ep_out_desc, &midi2_midi1_ep_out_ss_comp_desc, &midi2_midi1_ep_out_class_desc, + NULL +}; + +static void *midi2_midi1_ep_in_ss_descs[] = { &midi2_midi1_ep_in_desc, &midi2_midi1_ep_in_ss_comp_desc, &midi2_midi1_ep_in_class_desc, @@ -1197,6 +1157,11 @@ struct f_midi2_usb_config { struct usb_descriptor_header **list; unsigned int size; unsigned int alloc; + + /* MIDI 1.0 jacks */ + unsigned char jack_in, jack_out, jack_id; + struct usb_midi_in_jack_descriptor jack_ins[16]; + struct usb_midi_out_jack_descriptor_1 jack_outs[16]; }; static int append_config(struct f_midi2_usb_config *config, void *d) @@ -1231,12 +1196,61 @@ static int append_configs(struct f_midi2_usb_config *config, void **d) return 0; } +static int append_midi1_in_jack(struct f_midi2 *midi2, + struct f_midi2_usb_config *config, + unsigned int type) +{ + struct usb_midi_in_jack_descriptor *jack = + &config->jack_ins[config->jack_in++]; + int id = ++config->jack_id; + int err; + + jack->bLength = 0x06; + jack->bDescriptorType = USB_DT_CS_INTERFACE; + jack->bDescriptorSubtype = USB_MS_MIDI_IN_JACK; + jack->bJackType = type; + jack->bJackID = id; + jack->iJack = midi2->strings[STR_GTB1].id; // TODO: better names? + + err = append_config(config, jack); + if (err < 0) + return err; + return id; +} + +static int append_midi1_out_jack(struct f_midi2 *midi2, + struct f_midi2_usb_config *config, + unsigned int type, unsigned int source) +{ + struct usb_midi_out_jack_descriptor_1 *jack = + &config->jack_outs[config->jack_out++]; + int id = ++config->jack_id; + int err; + + jack->bLength = 0x09; + jack->bDescriptorType = USB_DT_CS_INTERFACE; + jack->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK; + jack->bJackType = type; + jack->bJackID = id; + jack->bNrInputPins = 1; + jack->pins[0].baSourceID = source; + jack->pins[0].baSourcePin = 0x01; + jack->iJack = midi2->strings[STR_GTB1].id; // TODO: better names? + + err = append_config(config, jack); + if (err < 0) + return err; + return id; +} + static int f_midi2_create_usb_configs(struct f_midi2 *midi2, struct f_midi2_usb_config *config, int speed) { - void **midi1_eps; - int i, err; + struct f_midi2_block *blk = &midi2->midi2_eps[0].blks[0]; + void **midi1_in_eps, **midi1_out_eps; + int i, jack, total; + int err; switch (speed) { default: @@ -1248,7 +1262,8 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2, cpu_to_le16(512); fallthrough; case USB_SPEED_FULL: - midi1_eps = midi2_midi1_ep_descs; + midi1_in_eps = midi2_midi1_ep_in_descs; + midi1_out_eps = midi2_midi1_ep_out_descs; break; case USB_SPEED_SUPER: case USB_SPEED_SUPER_PLUS: @@ -1257,19 +1272,85 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2, for (i = 0; i < midi2->num_eps; i++) midi2_midi2_ep_out_desc[i].wMaxPacketSize = cpu_to_le16(1024); - midi1_eps = midi2_midi1_ep_ss_descs; + midi1_in_eps = midi2_midi1_ep_in_ss_descs; + midi1_out_eps = midi2_midi1_ep_out_ss_descs; break; } err = append_configs(config, midi2_audio_descs); if (err < 0) return err; + + switch (blk->info.direction) { + case SNDRV_UMP_DIR_INPUT: + case SNDRV_UMP_DIR_OUTPUT: + midi2_midi1_if_desc.bNumEndpoints = 1; + break; + default: + midi2_midi1_if_desc.bNumEndpoints = 2; + break; + } + err = append_configs(config, midi2_midi1_descs); if (err < 0) return err; - err = append_configs(config, midi1_eps); - if (err < 0) - return err; + + total = USB_DT_MS_HEADER_SIZE; + if (blk->info.direction != SNDRV_UMP_DIR_INPUT) { + midi2_midi1_ep_out_class_desc.bLength = + USB_DT_MS_ENDPOINT_SIZE(blk->info.num_groups); + total += midi2_midi1_ep_out_class_desc.bLength; + midi2_midi1_ep_out_class_desc.bNumEmbMIDIJack = + blk->info.num_groups; + total += blk->info.num_groups * + (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1)); + for (i = 0; i < blk->info.num_groups; i++) { + jack = append_midi1_in_jack(midi2, config, + USB_MS_EMBEDDED); + if (jack < 0) + return jack; + midi2_midi1_ep_out_class_desc.baAssocJackID[i] = jack; + jack = append_midi1_out_jack(midi2, config, + USB_MS_EXTERNAL, jack); + if (jack < 0) + return jack; + } + } + + if (blk->info.direction != SNDRV_UMP_DIR_OUTPUT) { + midi2_midi1_ep_in_class_desc.bLength = + USB_DT_MS_ENDPOINT_SIZE(blk->info.num_groups); + total += midi2_midi1_ep_in_class_desc.bLength; + midi2_midi1_ep_in_class_desc.bNumEmbMIDIJack = + blk->info.num_groups; + total += blk->info.num_groups * + (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1)); + for (i = 0; i < blk->info.num_groups; i++) { + jack = append_midi1_in_jack(midi2, config, + USB_MS_EXTERNAL); + if (jack < 0) + return jack; + jack = append_midi1_out_jack(midi2, config, + USB_MS_EMBEDDED, jack); + if (jack < 0) + return jack; + midi2_midi1_ep_in_class_desc.baAssocJackID[i] = jack; + } + } + + midi2_midi1_class_desc.wTotalLength = cpu_to_le16(total); + + if (blk->info.direction != SNDRV_UMP_DIR_INPUT) { + err = append_configs(config, midi1_out_eps); + if (err < 0) + return err; + } + if (blk->info.direction != SNDRV_UMP_DIR_OUTPUT) { + err = append_configs(config, midi1_in_eps); + if (err < 0) + return err; + } + err = append_configs(config, midi2_midi2_descs); if (err < 0) return err; @@ -1421,14 +1502,19 @@ static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f) midi2_audio_class_desc.baInterfaceNr[0] = status; /* allocate instance-specific endpoints */ - status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_in, - &midi2_midi1_ep_in_desc, 0, NULL); - if (status) - goto fail; - status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_out, - &midi2_midi1_ep_out_desc, 0, NULL); - if (status) - goto fail; + if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_OUTPUT) { + status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_in, + &midi2_midi1_ep_in_desc, 0, NULL); + if (status) + goto fail; + } + + if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_INPUT) { + status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_out, + &midi2_midi1_ep_out_desc, 0, NULL); + if (status) + goto fail; + } for (i = 0; i < midi2->num_eps; i++) { status = f_midi2_init_midi2_ep_in(midi2, i); -- cgit From d6468be779af2eaa92bb853090ad540a49ed867e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Jul 2023 08:22:03 +0200 Subject: usb: gadget: midi2: MIDI 1.0 interface (altset 0) support This patch extends MIDI 2.0 function driver to add more proper support for MIDI 1.0 interface. Before this patch, the driver only exposes the USB descriptor of a MIDI 1.0 interface in altset 0 while no actual I/O is running for it. This patch enables the actual I/O for the altset 0; the backend UMP rawmidi is translated from/to the MIDI 1.0 USB commands. For converting to USB MIDI 1.0 data protocol, a helper function is copied from the existing f_midi driver, in addition to a few other UMP Core helper functions. For the MIDI 1.0 OUT (that is, input for gadget), the incoming USB MIDI 1.0 packet is translated to UMP packets via UMP Core helper, and tossed to the attached UMP rawmidi. It's a relatively straightforward. OTOH, for MIDI 1.0 IN (i.e. output for gadget), it's a bit more complex: we need to convert a source UMP packet once to the standard MIDI 1.0 byte stream, and convert it again to USB MIDI 1.0 packets, then send them out. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20230725062206.9674-5-tiwai@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_midi2.c | 494 +++++++++++++++++++++++++++++++--- 1 file changed, 452 insertions(+), 42 deletions(-) diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index b15d832ff441..a368ac51d349 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,27 @@ struct f_midi2_block { unsigned int string_id; /* assigned string id */ }; +/* Temporary buffer for altset 0 MIDI 1.0 handling */ +struct f_midi2_midi1_port { + unsigned int pending; /* pending bytes on the input buffer */ + u8 buf[32]; /* raw MIDI 1.0 byte input */ + u8 state; /* running status */ + u8 data[2]; /* rendered USB MIDI 1.0 packet data */ +}; + +/* MIDI 1.0 message states */ +enum { + STATE_INITIAL = 0, /* pseudo state */ + STATE_1PARAM, + STATE_2PARAM_1, + STATE_2PARAM_2, + STATE_SYSEX_0, + STATE_SYSEX_1, + STATE_SYSEX_2, + STATE_REAL_TIME, + STATE_FINISHED, /* pseudo state */ +}; + /* Resources for UMP Endpoint */ struct f_midi2_ep { struct snd_ump_endpoint *ump; /* assigned UMP EP */ @@ -89,6 +111,11 @@ struct f_midi2 { struct f_midi2_usb_ep midi1_ep_in; struct f_midi2_usb_ep midi1_ep_out; + /* conversion for MIDI 1.0 EP-in */ + struct f_midi2_midi1_port midi1_port[MAX_CABLES]; + /* conversion for MIDI 1.0 EP-out */ + struct ump_cvt_to_ump midi1_ump_cvt; + int midi_if; /* USB MIDI interface number */ int operation_mode; /* current operation mode */ @@ -707,12 +734,353 @@ static void f_midi2_ep_in_complete(struct usb_ep *usb_ep, process_ump_transmit(ep); } +/* + * MIDI1 (altset 0) USB request handling + */ + +/* process one MIDI byte -- copied from f_midi.c + * + * fill the packet or request if needed + * returns true if the request became empty (queued) + */ +static bool process_midi1_byte(struct f_midi2 *midi2, u8 cable, u8 b, + struct usb_request **req_p) +{ + struct f_midi2_midi1_port *port = &midi2->midi1_port[cable]; + u8 p[4] = { cable << 4, 0, 0, 0 }; + int next_state = STATE_INITIAL; + struct usb_request *req = *req_p; + + switch (b) { + case 0xf8 ... 0xff: + /* System Real-Time Messages */ + p[0] |= 0x0f; + p[1] = b; + next_state = port->state; + port->state = STATE_REAL_TIME; + break; + + case 0xf7: + /* End of SysEx */ + switch (port->state) { + case STATE_SYSEX_0: + p[0] |= 0x05; + p[1] = 0xf7; + next_state = STATE_FINISHED; + break; + case STATE_SYSEX_1: + p[0] |= 0x06; + p[1] = port->data[0]; + p[2] = 0xf7; + next_state = STATE_FINISHED; + break; + case STATE_SYSEX_2: + p[0] |= 0x07; + p[1] = port->data[0]; + p[2] = port->data[1]; + p[3] = 0xf7; + next_state = STATE_FINISHED; + break; + default: + /* Ignore byte */ + next_state = port->state; + port->state = STATE_INITIAL; + } + break; + + case 0xf0 ... 0xf6: + /* System Common Messages */ + port->data[0] = port->data[1] = 0; + port->state = STATE_INITIAL; + switch (b) { + case 0xf0: + port->data[0] = b; + port->data[1] = 0; + next_state = STATE_SYSEX_1; + break; + case 0xf1: + case 0xf3: + port->data[0] = b; + next_state = STATE_1PARAM; + break; + case 0xf2: + port->data[0] = b; + next_state = STATE_2PARAM_1; + break; + case 0xf4: + case 0xf5: + next_state = STATE_INITIAL; + break; + case 0xf6: + p[0] |= 0x05; + p[1] = 0xf6; + next_state = STATE_FINISHED; + break; + } + break; + + case 0x80 ... 0xef: + /* + * Channel Voice Messages, Channel Mode Messages + * and Control Change Messages. + */ + port->data[0] = b; + port->data[1] = 0; + port->state = STATE_INITIAL; + if (b >= 0xc0 && b <= 0xdf) + next_state = STATE_1PARAM; + else + next_state = STATE_2PARAM_1; + break; + + case 0x00 ... 0x7f: + /* Message parameters */ + switch (port->state) { + case STATE_1PARAM: + if (port->data[0] < 0xf0) + p[0] |= port->data[0] >> 4; + else + p[0] |= 0x02; + + p[1] = port->data[0]; + p[2] = b; + /* This is to allow Running State Messages */ + next_state = STATE_1PARAM; + break; + case STATE_2PARAM_1: + port->data[1] = b; + next_state = STATE_2PARAM_2; + break; + case STATE_2PARAM_2: + if (port->data[0] < 0xf0) + p[0] |= port->data[0] >> 4; + else + p[0] |= 0x03; + + p[1] = port->data[0]; + p[2] = port->data[1]; + p[3] = b; + /* This is to allow Running State Messages */ + next_state = STATE_2PARAM_1; + break; + case STATE_SYSEX_0: + port->data[0] = b; + next_state = STATE_SYSEX_1; + break; + case STATE_SYSEX_1: + port->data[1] = b; + next_state = STATE_SYSEX_2; + break; + case STATE_SYSEX_2: + p[0] |= 0x04; + p[1] = port->data[0]; + p[2] = port->data[1]; + p[3] = b; + next_state = STATE_SYSEX_0; + break; + } + break; + } + + /* States where we have to write into the USB request */ + if (next_state == STATE_FINISHED || + port->state == STATE_SYSEX_2 || + port->state == STATE_1PARAM || + port->state == STATE_2PARAM_2 || + port->state == STATE_REAL_TIME) { + memcpy(req->buf + req->length, p, sizeof(p)); + req->length += sizeof(p); + + if (next_state == STATE_FINISHED) { + next_state = STATE_INITIAL; + port->data[0] = port->data[1] = 0; + } + + if (midi2->info.req_buf_size - req->length <= 4) { + queue_request_ep_raw(req); + *req_p = NULL; + return true; + } + } + + port->state = next_state; + return false; +} + +/* process all pending MIDI bytes in the internal buffer; + * returns true if the request gets empty + * returns false if all have been processed + */ +static bool process_midi1_pending_buf(struct f_midi2 *midi2, + struct usb_request **req_p) +{ + unsigned int cable, c; + + for (cable = 0; cable < midi2->midi2_eps[0].blks[0].info.num_groups; + cable++) { + struct f_midi2_midi1_port *port = &midi2->midi1_port[cable]; + + if (!port->pending) + continue; + for (c = 0; c < port->pending; c++) { + if (process_midi1_byte(midi2, cable, port->buf[c], + req_p)) { + port->pending -= c; + if (port->pending) + memmove(port->buf, port->buf + c, + port->pending); + return true; + } + } + port->pending = 0; + } + + return false; +} + +/* fill the MIDI bytes onto the temporary buffer + */ +static void fill_midi1_pending_buf(struct f_midi2 *midi2, u8 cable, u8 *buf, + unsigned int size) +{ + struct f_midi2_midi1_port *port = &midi2->midi1_port[cable]; + + if (port->pending + size > sizeof(port->buf)) + return; + memcpy(port->buf + port->pending, buf, size); + port->pending += size; +} + +/* try to process data given from the associated UMP stream */ +static void process_midi1_transmit(struct f_midi2 *midi2) +{ + struct f_midi2_usb_ep *usb_ep = &midi2->midi1_ep_in; + struct f_midi2_ep *ep = &midi2->midi2_eps[0]; + struct usb_request *req = NULL; + /* 12 is the largest outcome (4 MIDI1 cmds) for a single UMP packet */ + unsigned char outbuf[12]; + unsigned char group; + int len, size, cable; + u32 ump; + + if (!usb_ep->usb_ep || !usb_ep->usb_ep->enabled) + return; + + for (;;) { + if (!req) { + req = get_empty_request(usb_ep); + if (!req) + break; + } + + if (process_midi1_pending_buf(midi2, &req)) + continue; + + len = snd_ump_transmit(ep->ump, &ump, 4); + if (len <= 0) + break; + if (snd_ump_receive_ump_val(ep->ump, ump) <= 0) + continue; + size = snd_ump_convert_from_ump(ep->ump->input_buf, outbuf, + &group); + if (size <= 0) + continue; + cable = group - ep->blks[0].info.first_group; + if (cable < 0 || cable >= ep->blks[0].info.num_groups) + continue; + fill_midi1_pending_buf(midi2, cable, outbuf, size); + } + + if (req) { + if (req->length) + queue_request_ep_raw(req); + else + put_empty_request(req); + } +} + +/* complete handler for MIDI1 EP-in requests */ +static void f_midi2_midi1_ep_in_complete(struct usb_ep *usb_ep, + struct usb_request *req) +{ + struct f_midi2_req_ctx *ctx = req->context; + struct f_midi2 *midi2 = ctx->usb_ep->card; + int status = req->status; + + put_empty_request(req); + + if (status) { + DBG(midi2, "%s complete error %d: %d/%d\n", + usb_ep->name, status, req->actual, req->length); + return; + } + + process_midi1_transmit(midi2); +} + +/* complete handler for MIDI1 EP-out requests */ +static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep, + struct usb_request *req) +{ + struct f_midi2_req_ctx *ctx = req->context; + struct f_midi2 *midi2 = ctx->usb_ep->card; + struct f_midi2_ep *ep = &midi2->midi2_eps[0]; + struct ump_cvt_to_ump *cvt = &midi2->midi1_ump_cvt; + static const u8 midi1_packet_bytes[16] = { + 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 + }; + unsigned int group, bytes, c, len; + int status = req->status; + const u8 *buf = req->buf; + + if (status) { + DBG(midi2, "%s complete error %d: %d/%d\n", + usb_ep->name, status, req->actual, req->length); + goto error; + } + + len = req->actual >> 2; + for (; len; len--, buf += 4) { + group = *buf >> 4; + if (group >= ep->blks[0].info.num_groups) + continue; + group += ep->blks[0].info.first_group; + bytes = midi1_packet_bytes[*buf & 0x0f]; + for (c = 0; c < bytes; c++) { + snd_ump_convert_to_ump(cvt, group, ep->info.protocol, + buf[c + 1]); + if (cvt->ump_bytes) { + snd_ump_receive(ep->ump, cvt->ump, + cvt->ump_bytes); + cvt->ump_bytes = 0; + } + } + } + + if (midi2->operation_mode != MIDI_OP_MODE_MIDI1) + goto error; + + if (queue_request_ep_raw(req)) + goto error; + return; + + error: + put_empty_request(req); +} + +/* + * Common EP handling helpers + */ + /* Start MIDI EP */ static int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep, struct usb_function *fn) { int err; + if (!usb_ep->usb_ep) + return 0; + usb_ep_disable(usb_ep->usb_ep); err = config_ep_by_speed(usb_ep->card->gadget, fn, usb_ep->usb_ep); if (err) @@ -725,7 +1093,7 @@ static void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep) { int i; - if (!usb_ep->num_reqs) + if (!usb_ep->usb_ep || !usb_ep->num_reqs) return; for (i = 0; i < usb_ep->num_reqs; i++) { @@ -742,6 +1110,8 @@ static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep) struct f_midi2 *midi2 = usb_ep->card; int i; + if (!usb_ep->usb_ep) + return 0; if (!usb_ep->reqs) return -EINVAL; @@ -774,7 +1144,7 @@ static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep) /* Initialize EP */ static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep, struct f_midi2_usb_ep *usb_ep, - void *desc, int num_reqs, + void *desc, void (*complete)(struct usb_ep *usb_ep, struct usb_request *req)) { @@ -787,17 +1157,15 @@ static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep, return -ENODEV; usb_ep->complete = complete; - if (num_reqs) { - usb_ep->reqs = kcalloc(num_reqs, sizeof(*usb_ep->reqs), - GFP_KERNEL); - if (!usb_ep->reqs) - return -ENOMEM; - for (i = 0; i < num_reqs; i++) { - usb_ep->reqs[i].index = i; - usb_ep->reqs[i].usb_ep = usb_ep; - set_bit(i, &usb_ep->free_reqs); - usb_ep->num_reqs++; - } + usb_ep->reqs = kcalloc(midi2->info.num_reqs, sizeof(*usb_ep->reqs), + GFP_KERNEL); + if (!usb_ep->reqs) + return -ENOMEM; + for (i = 0; i < midi2->info.num_reqs; i++) { + usb_ep->reqs[i].index = i; + usb_ep->reqs[i].usb_ep = usb_ep; + set_bit(i, &usb_ep->free_reqs); + usb_ep->num_reqs++; } return 0; @@ -821,6 +1189,9 @@ static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep) { int i, err; + if (!usb_ep->usb_ep) + return; + for (i = 0; i < usb_ep->num_reqs; i++) { if (!test_bit(i, &usb_ep->free_reqs) || !usb_ep->reqs[i].req) continue; @@ -836,6 +1207,41 @@ static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep) * Gadget Function callbacks */ +/* stop both IN and OUT EPs */ +static void f_midi2_stop_eps(struct f_midi2_usb_ep *ep_in, + struct f_midi2_usb_ep *ep_out) +{ + f_midi2_drop_reqs(ep_in); + f_midi2_drop_reqs(ep_out); + f_midi2_free_ep_reqs(ep_in); + f_midi2_free_ep_reqs(ep_out); +} + +/* start/queue both IN and OUT EPs */ +static int f_midi2_start_eps(struct f_midi2_usb_ep *ep_in, + struct f_midi2_usb_ep *ep_out, + struct usb_function *fn) +{ + int err; + + err = f_midi2_start_ep(ep_in, fn); + if (err) + return err; + err = f_midi2_start_ep(ep_out, fn); + if (err) + return err; + + err = f_midi2_alloc_ep_reqs(ep_in); + if (err) + return err; + err = f_midi2_alloc_ep_reqs(ep_out); + if (err) + return err; + + f_midi2_queue_out_reqs(ep_out); + return 0; +} + /* gadget function set_alt callback */ static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf, unsigned int alt) @@ -859,35 +1265,28 @@ static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf, midi2->operation_mode = op_mode; + if (op_mode != MIDI_OP_MODE_MIDI1) + f_midi2_stop_eps(&midi2->midi1_ep_in, &midi2->midi1_ep_out); + if (op_mode != MIDI_OP_MODE_MIDI2) { for (i = 0; i < midi2->num_eps; i++) { ep = &midi2->midi2_eps[i]; - f_midi2_drop_reqs(&ep->ep_in); - f_midi2_drop_reqs(&ep->ep_out); - f_midi2_free_ep_reqs(&ep->ep_in); - f_midi2_free_ep_reqs(&ep->ep_out); + f_midi2_stop_eps(&ep->ep_in, &ep->ep_out); } - return 0; } - for (i = 0; i < midi2->num_eps; i++) { - ep = &midi2->midi2_eps[i]; - - err = f_midi2_start_ep(&ep->ep_in, fn); - if (err) - return err; - err = f_midi2_start_ep(&ep->ep_out, fn); - if (err) - return err; + if (op_mode == MIDI_OP_MODE_MIDI1) + return f_midi2_start_eps(&midi2->midi1_ep_in, + &midi2->midi1_ep_out, fn); - err = f_midi2_alloc_ep_reqs(&ep->ep_in); - if (err) - return err; - err = f_midi2_alloc_ep_reqs(&ep->ep_out); - if (err) - return err; + if (op_mode == MIDI_OP_MODE_MIDI2) { + for (i = 0; i < midi2->num_eps; i++) { + ep = &midi2->midi2_eps[i]; - f_midi2_queue_out_reqs(&ep->ep_out); + err = f_midi2_start_eps(&ep->ep_in, &ep->ep_out, fn); + if (err) + return err; + } } return 0; @@ -1026,9 +1425,18 @@ static void f_midi2_ump_close(struct snd_ump_endpoint *ump, int dir) static void f_midi2_ump_trigger(struct snd_ump_endpoint *ump, int dir, int up) { struct f_midi2_ep *ep = ump->private_data; + struct f_midi2 *midi2 = ep->card; - if (up && dir == SNDRV_RAWMIDI_STREAM_OUTPUT) - process_ump_transmit(ep); + if (up && dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { + switch (midi2->operation_mode) { + case MIDI_OP_MODE_MIDI1: + process_midi1_transmit(midi2); + break; + case MIDI_OP_MODE_MIDI2: + process_ump_transmit(ep); + break; + } + } } static void f_midi2_ump_drain(struct snd_ump_endpoint *ump, int dir) @@ -1160,8 +1568,8 @@ struct f_midi2_usb_config { /* MIDI 1.0 jacks */ unsigned char jack_in, jack_out, jack_id; - struct usb_midi_in_jack_descriptor jack_ins[16]; - struct usb_midi_out_jack_descriptor_1 jack_outs[16]; + struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES]; + struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES]; }; static int append_config(struct f_midi2_usb_config *config, void *d) @@ -1422,7 +1830,7 @@ static int f_midi2_init_midi2_ep_in(struct f_midi2 *midi2, int index) fill_midi2_class_desc(ep, &midi2_midi2_ep_in_class_desc[index]); return f_midi2_init_ep(midi2, ep, &ep->ep_in, desc, - midi2->info.num_reqs, f_midi2_ep_in_complete); + f_midi2_ep_in_complete); } /* initialize MIDI2 EP-out */ @@ -1439,7 +1847,7 @@ static int f_midi2_init_midi2_ep_out(struct f_midi2 *midi2, int index) fill_midi2_class_desc(ep, &midi2_midi2_ep_out_class_desc[index]); return f_midi2_init_ep(midi2, ep, &ep->ep_out, desc, - midi2->info.num_reqs, f_midi2_ep_out_complete); + f_midi2_ep_out_complete); } /* gadget function bind callback */ @@ -1504,14 +1912,16 @@ static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific endpoints */ if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_OUTPUT) { status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_in, - &midi2_midi1_ep_in_desc, 0, NULL); + &midi2_midi1_ep_in_desc, + f_midi2_midi1_ep_in_complete); if (status) goto fail; } if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_INPUT) { status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_out, - &midi2_midi1_ep_out_desc, 0, NULL); + &midi2_midi1_ep_out_desc, + f_midi2_midi1_ep_out_complete); if (status) goto fail; } -- cgit From 8559caa985503d057e55dd7c6362b8f98359ba2e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Jul 2023 08:22:04 +0200 Subject: usb: gadget: midi2: Add testing documentation Describing brief testing of the new MIDI 2.0 function driver. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20230725062206.9674-6-tiwai@suse.de Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/gadget-testing.rst | 141 +++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 2fca40443dc9..0f3708ae5bc8 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -27,6 +27,7 @@ provided by gadgets. 18. UVC function 19. PRINTER function 20. UAC1 function (new API) + 21. MIDI2 function 1. ACM function @@ -965,3 +966,143 @@ e.g.:: $ arecord -f dat -t wav -D hw:CARD=UAC1Gadget,DEV=0 | \ aplay -D default:CARD=OdroidU3 + + +21. MIDI2 function +================== + +The function is provided by usb_f_midi2.ko module. +It will create a virtual ALSA card containing a UMP rawmidi device +where the UMP packet is looped back. In addition, a legacy rawmidi +device is created. The UMP rawmidi is bound with ALSA sequencer +clients, too. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "midi2". +The midi2 function provides these attributes in its function directory +as the card top-level information: + + ============= ================================================= + process_ump Bool flag to process UMP Stream messages (0 or 1) + static_block Bool flag for static blocks (0 or 1) + iface_name Optional interface name string + ============= ================================================= + +The directory contains a subdirectory "ep.0", and this provides the +attributes for a UMP Endpoint (which is a pair of USB MIDI Endpoints): + + ============= ================================================= + protocol_caps MIDI protocol capabilities; + 1: MIDI 1.0, 2: MIDI 2.0, or 3: both protocols + protocol Default MIDI protocol (either 1 or 2) + ep_name UMP Endpoint name string + product_id Product ID string + manufacturer Manufacture ID number (24 bit) + family Device family ID number (16 bit) + model Device model ID number (16 bit) + sw_revision Software revision (32 bit) + ============= ================================================= + +Each Endpoint subdirectory contains a subdirectory "block.0", which +represents the Function Block for Block 0 information. +Its attributes are: + + =============== =============================================== + name Function Block name string + direction Direction of this FB + 1: input, 2: output, or 3: bidirectional + first_group The first UMP Group number (0-15) + num_groups The number of groups in this FB (1-16) + ui_hint UI-hint of this FB + 0: unknown, 1: receiver, 2: sender, 3: both + midi_ci_verison Supported MIDI-CI version number (8 bit) + is_midi1 Legacy MIDI 1.0 device (0-2) + 0: MIDI 2.0 device, + 1: MIDI 1.0 without restriction, or + 2: MIDI 1.0 with low speed + sysex8_streams Max number of SysEx8 streams (8 bit) + active Bool flag for FB activity (0 or 1) + =============== =============================================== + +If multiple Function Blocks are required, you can add more Function +Blocks by creating subdirectories "block." with the corresponding +Function Block number (1, 2, ....). The FB subdirectories can be +dynamically removed, too. Note that the Function Block numbers must be +continuous. + +Similarly, if you multiple UMP Endpoints are required, you can add +more Endpoints by creating subdirectories "ep.". The number must +be continuous. + +For emulating the old MIDI 2.0 device without UMP v1.1 support, pass 0 +to `process_ump` flag. Then the whole UMP v1.1 requests are ignored. + +Testing the MIDI2 function +-------------------------- + +On the device: run the gadget, and running:: + + $ cat /proc/asound/cards + +will show a new sound card containing a MIDI2 device. + +OTOH, on the host:: + + $ cat /proc/asound/cards + +will show a new sound card containing either MIDI1 or MIDI2 device, +depending on the USB audio driver configuration. + +On both, when ALSA sequencer is enabled on the host, you can find the +UMP MIDI client such as "MIDI 2.0 Gadget". + +As the driver simply loops back the data, there is no need for a real +device just for testing. + +For testing a MIDI input from the gadget to the host (e.g. emulating a +MIDI keyboard), you can send a MIDI stream like the following. + +On the gadget:: + + $ aconnect -o + .... + client 20: 'MIDI 2.0 Gadget' [type=kernel,card=1] + 0 'MIDI 2.0 ' + 1 'Group 1 (MIDI 2.0 Gadget I/O)' + $ aplaymidi -p 20:1 to_host.mid + +On the host:: + + $ aconnect -i + .... + client 24: 'MIDI 2.0 Gadget' [type=kernel,card=2] + 0 'MIDI 2.0 ' + 1 'Group 1 (MIDI 2.0 Gadget I/O)' + $ arecordmidi -p 24:1 from_gadget.mid + +If you have a UMP-capable application, you can use the UMP port to +send/receive the raw UMP packets, too. For example, aseqdump program +with UMP support can receive from UMP port. On the host:: + + $ aseqdump -u 2 -p 24:1 + Waiting for data. Press Ctrl+C to end. + Source Group Event Ch Data + 24:1 Group 0, Program change 0, program 0, Bank select 0:0 + 24:1 Group 0, Channel pressure 0, value 0x80000000 + +For testing a MIDI output to the gadget to the host (e.g. emulating a +MIDI synth), it'll be just other way round. + +On the gadget:: + + $ arecordmidi -p 20:1 from_host.mid + +On the host:: + + $ aplaymidi -p 24:1 to_gadget.mid + +The access to MIDI 1.0 on altset 0 on the host is supported, and it's +translated from/to UMP packets on the gadget. It's bound to only +Function Block 0. -- cgit From 1b437d2fb3c1a8c7f2a8a096c0871c8e7c8d109e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Jul 2023 08:22:05 +0200 Subject: usb: gadget: midi2: Add "Operation Mode" control Add a new ALSA control element to watch the current operation mode (MIDI 1.0 or MIDI 2.0). It's a read-only control that reflects the current value of altsetting, and 0 means unused, 1 for MIDI 1.0 (altset 0) and 2 for MIDI 2.0 (altset 1). Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20230725062206.9674-7-tiwai@suse.de Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/gadget-testing.rst | 11 +++++++++++ drivers/usb/gadget/function/f_midi2.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 0f3708ae5bc8..1fb181d61322 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -1106,3 +1106,14 @@ On the host:: The access to MIDI 1.0 on altset 0 on the host is supported, and it's translated from/to UMP packets on the gadget. It's bound to only Function Block 0. + +The current operation mode can be observed in ALSA control element +"Operation Mode" for SND_CTL_IFACE_RAWMIDI. For example:: + + $ amixer -c1 contents + numid=1,iface=RAWMIDI,name='Operation Mode' + ; type=INTEGER,access=r--v----,values=1,min=0,max=2,step=0 + : values=2 + +where 0 = unused, 1 = MIDI 1.0 (altset 0), 2 = MIDI 2.0 (altset 1). +The example above shows it's running in 2, i.e. MIDI 2.0. diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index a368ac51d349..ec9ef15abfea 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -1450,6 +1451,36 @@ static const struct snd_ump_ops f_midi2_ump_ops = { .drain = f_midi2_ump_drain, }; +/* + * "Operation Mode" control element + */ +static int f_midi2_operation_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = MIDI_OP_MODE_UNSET; + uinfo->value.integer.max = MIDI_OP_MODE_MIDI2; + return 0; +} + +static int f_midi2_operation_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct f_midi2 *midi2 = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = midi2->operation_mode; + return 0; +} + +static const struct snd_kcontrol_new operation_mode_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI, + .name = "Operation Mode", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = f_midi2_operation_mode_info, + .get = f_midi2_operation_mode_get, +}; + /* * ALSA UMP instance creation / deletion */ @@ -1547,6 +1578,10 @@ static int f_midi2_create_card(struct f_midi2 *midi2) id++; } + err = snd_ctl_add(card, snd_ctl_new1(&operation_mode_ctl, midi2)); + if (err < 0) + goto error; + err = snd_card_register(card); if (err < 0) goto error; -- cgit From a85ff0db48c372063988f2072a07bd361ce9c4ef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Jul 2023 08:22:06 +0200 Subject: usb: gadget: midi2: More flexible MIDI 1.0 configuration This patch allows users to set up MIDI 1.0 ports more flexibly. Namely, instead of the fixed mapping only from FB 0, now multiple block definitions are applied to build up the MIDI 1.0 mapping. The each block config has midi1_first_group and midi1_num_groups attributes, and those specify which Groups are used for MIDI 1.0. Those fields must be within the UMP Groups defined in the block itself. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20230725062206.9674-8-tiwai@suse.de Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/configfs-usb-gadget-midi2 | 24 ++-- Documentation/usb/gadget-testing.rst | 34 ++--- drivers/usb/gadget/function/f_midi2.c | 154 ++++++++++++++++----- drivers/usb/gadget/function/u_midi2.h | 2 + 4 files changed, 153 insertions(+), 61 deletions(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi2 b/Documentation/ABI/testing/configfs-usb-gadget-midi2 index a3a036d784c7..0eac3aaba137 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-midi2 +++ b/Documentation/ABI/testing/configfs-usb-gadget-midi2 @@ -39,14 +39,16 @@ Description: The attributes: - =============== =============================================== - name Function Block name string - direction 1: input, 2: output, 3: bidirectional - first_group The first UMP Group number (0-15) - num_groups The number of groups in this FB (1-16) - ui_hint 0: unknown, 1: receiver, 2: sender, 3: both - midi_ci_verison Supported MIDI-CI version number (8 bit) - is_midi1 Legacy MIDI 1.0 device (0, 1 or 2) - sysex8_streams Max number of SysEx8 streams (8 bit) - active Active FB flag (0 or 1) - =============== =============================================== + ================= ============================================== + name Function Block name string + direction 1: input, 2: output, 3: bidirectional + first_group The first UMP Group number (0-15) + num_groups The number of groups in this FB (1-16) + midi1_first_group The first UMP Group number for MIDI 1.0 (0-15) + midi1_num_groups The number of groups for MIDI 1.0 (0-16) + ui_hint 0: unknown, 1: receiver, 2: sender, 3: both + midi_ci_verison Supported MIDI-CI version number (8 bit) + is_midi1 Legacy MIDI 1.0 device (0, 1 or 2) + sysex8_streams Max number of SysEx8 streams (8 bit) + active Active FB flag (0 or 1) + ================= ============================================== diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 1fb181d61322..394cd226bfae 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -1009,22 +1009,24 @@ Each Endpoint subdirectory contains a subdirectory "block.0", which represents the Function Block for Block 0 information. Its attributes are: - =============== =============================================== - name Function Block name string - direction Direction of this FB - 1: input, 2: output, or 3: bidirectional - first_group The first UMP Group number (0-15) - num_groups The number of groups in this FB (1-16) - ui_hint UI-hint of this FB - 0: unknown, 1: receiver, 2: sender, 3: both - midi_ci_verison Supported MIDI-CI version number (8 bit) - is_midi1 Legacy MIDI 1.0 device (0-2) - 0: MIDI 2.0 device, - 1: MIDI 1.0 without restriction, or - 2: MIDI 1.0 with low speed - sysex8_streams Max number of SysEx8 streams (8 bit) - active Bool flag for FB activity (0 or 1) - =============== =============================================== + ================= =============================================== + name Function Block name string + direction Direction of this FB + 1: input, 2: output, or 3: bidirectional + first_group The first UMP Group number (0-15) + num_groups The number of groups in this FB (1-16) + midi1_first_group The first UMP Group number for MIDI 1.0 (0-15) + midi1_num_groups The number of groups for MIDI 1.0 (0-16) + ui_hint UI-hint of this FB + 0: unknown, 1: receiver, 2: sender, 3: both + midi_ci_verison Supported MIDI-CI version number (8 bit) + is_midi1 Legacy MIDI 1.0 device (0-2) + 0: MIDI 2.0 device, + 1: MIDI 1.0 without restriction, or + 2: MIDI 1.0 with low speed + sysex8_streams Max number of SysEx8 streams (8 bit) + active Bool flag for FB activity (0 or 1) + ================= =============================================== If multiple Function Blocks are required, you can add more Function Blocks by creating subdirectories "block." with the corresponding diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index ec9ef15abfea..f1c47753e0c1 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -84,6 +84,8 @@ struct f_midi2_ep { struct f_midi2_usb_ep ep_in; /* USB MIDI EP-in */ struct f_midi2_usb_ep ep_out; /* USB MIDI EP-out */ + + u8 in_group_to_cable[SNDRV_UMP_MAX_GROUPS]; /* map to cable; 1-based! */ }; /* indices for USB strings */ @@ -95,6 +97,13 @@ enum { /* 1-based GTB id to string id */ #define gtb_to_str_id(id) (STR_GTB1 + (id) - 1) +/* mapping from MIDI 1.0 cable to UMP group */ +struct midi1_cable_mapping { + struct f_midi2_ep *ep; + unsigned char block; + unsigned char group; +}; + /* operation mode */ enum { MIDI_OP_MODE_UNSET, /* no altset set yet */ @@ -112,10 +121,17 @@ struct f_midi2 { struct f_midi2_usb_ep midi1_ep_in; struct f_midi2_usb_ep midi1_ep_out; + /* number of MIDI 1.0 I/O cables */ + unsigned int num_midi1_in; + unsigned int num_midi1_out; + /* conversion for MIDI 1.0 EP-in */ struct f_midi2_midi1_port midi1_port[MAX_CABLES]; /* conversion for MIDI 1.0 EP-out */ struct ump_cvt_to_ump midi1_ump_cvt; + /* mapping between cables and UMP groups */ + struct midi1_cable_mapping in_cable_mapping[MAX_CABLES]; + struct midi1_cable_mapping out_cable_mapping[MAX_CABLES]; int midi_if; /* USB MIDI interface number */ int operation_mode; /* current operation mode */ @@ -917,8 +933,7 @@ static bool process_midi1_pending_buf(struct f_midi2 *midi2, { unsigned int cable, c; - for (cable = 0; cable < midi2->midi2_eps[0].blks[0].info.num_groups; - cable++) { + for (cable = 0; cable < midi2->num_midi1_in; cable++) { struct f_midi2_midi1_port *port = &midi2->midi1_port[cable]; if (!port->pending) @@ -960,8 +975,8 @@ static void process_midi1_transmit(struct f_midi2 *midi2) struct usb_request *req = NULL; /* 12 is the largest outcome (4 MIDI1 cmds) for a single UMP packet */ unsigned char outbuf[12]; - unsigned char group; - int len, size, cable; + unsigned char group, cable; + int len, size; u32 ump; if (!usb_ep->usb_ep || !usb_ep->usb_ep->enabled) @@ -986,9 +1001,10 @@ static void process_midi1_transmit(struct f_midi2 *midi2) &group); if (size <= 0) continue; - cable = group - ep->blks[0].info.first_group; - if (cable < 0 || cable >= ep->blks[0].info.num_groups) + cable = ep->in_group_to_cable[group]; + if (!cable) continue; + cable--; /* to 0-base */ fill_midi1_pending_buf(midi2, cable, outbuf, size); } @@ -1025,12 +1041,12 @@ static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep, { struct f_midi2_req_ctx *ctx = req->context; struct f_midi2 *midi2 = ctx->usb_ep->card; - struct f_midi2_ep *ep = &midi2->midi2_eps[0]; + struct f_midi2_ep *ep; struct ump_cvt_to_ump *cvt = &midi2->midi1_ump_cvt; static const u8 midi1_packet_bytes[16] = { 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 }; - unsigned int group, bytes, c, len; + unsigned int group, cable, bytes, c, len; int status = req->status; const u8 *buf = req->buf; @@ -1042,10 +1058,11 @@ static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep, len = req->actual >> 2; for (; len; len--, buf += 4) { - group = *buf >> 4; - if (group >= ep->blks[0].info.num_groups) + cable = *buf >> 4; + ep = midi2->out_cable_mapping[cable].ep; + if (!ep) continue; - group += ep->blks[0].info.first_group; + group = midi2->out_cable_mapping[cable].group; bytes = midi1_packet_bytes[*buf & 0x0f]; for (c = 0; c < bytes; c++) { snd_ump_convert_to_ump(cvt, group, ep->info.protocol, @@ -1641,6 +1658,7 @@ static int append_configs(struct f_midi2_usb_config *config, void **d) static int append_midi1_in_jack(struct f_midi2 *midi2, struct f_midi2_usb_config *config, + struct midi1_cable_mapping *map, unsigned int type) { struct usb_midi_in_jack_descriptor *jack = @@ -1653,7 +1671,9 @@ static int append_midi1_in_jack(struct f_midi2 *midi2, jack->bDescriptorSubtype = USB_MS_MIDI_IN_JACK; jack->bJackType = type; jack->bJackID = id; - jack->iJack = midi2->strings[STR_GTB1].id; // TODO: better names? + /* use the corresponding block name as jack name */ + if (map->ep) + jack->iJack = map->ep->blks[map->block].string_id; err = append_config(config, jack); if (err < 0) @@ -1663,6 +1683,7 @@ static int append_midi1_in_jack(struct f_midi2 *midi2, static int append_midi1_out_jack(struct f_midi2 *midi2, struct f_midi2_usb_config *config, + struct midi1_cable_mapping *map, unsigned int type, unsigned int source) { struct usb_midi_out_jack_descriptor_1 *jack = @@ -1678,7 +1699,9 @@ static int append_midi1_out_jack(struct f_midi2 *midi2, jack->bNrInputPins = 1; jack->pins[0].baSourceID = source; jack->pins[0].baSourcePin = 0x01; - jack->iJack = midi2->strings[STR_GTB1].id; // TODO: better names? + /* use the corresponding block name as jack name */ + if (map->ep) + jack->iJack = map->ep->blks[map->block].string_id; err = append_config(config, jack); if (err < 0) @@ -1690,7 +1713,6 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2, struct f_midi2_usb_config *config, int speed) { - struct f_midi2_block *blk = &midi2->midi2_eps[0].blks[0]; void **midi1_in_eps, **midi1_out_eps; int i, jack, total; int err; @@ -1724,56 +1746,55 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2, if (err < 0) return err; - switch (blk->info.direction) { - case SNDRV_UMP_DIR_INPUT: - case SNDRV_UMP_DIR_OUTPUT: - midi2_midi1_if_desc.bNumEndpoints = 1; - break; - default: + if (midi2->num_midi1_in && midi2->num_midi1_out) midi2_midi1_if_desc.bNumEndpoints = 2; - break; - } + else + midi2_midi1_if_desc.bNumEndpoints = 1; err = append_configs(config, midi2_midi1_descs); if (err < 0) return err; total = USB_DT_MS_HEADER_SIZE; - if (blk->info.direction != SNDRV_UMP_DIR_INPUT) { + if (midi2->num_midi1_out) { midi2_midi1_ep_out_class_desc.bLength = - USB_DT_MS_ENDPOINT_SIZE(blk->info.num_groups); + USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_out); total += midi2_midi1_ep_out_class_desc.bLength; midi2_midi1_ep_out_class_desc.bNumEmbMIDIJack = - blk->info.num_groups; - total += blk->info.num_groups * + midi2->num_midi1_out; + total += midi2->num_midi1_out * (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1)); - for (i = 0; i < blk->info.num_groups; i++) { + for (i = 0; i < midi2->num_midi1_out; i++) { jack = append_midi1_in_jack(midi2, config, + &midi2->in_cable_mapping[i], USB_MS_EMBEDDED); if (jack < 0) return jack; midi2_midi1_ep_out_class_desc.baAssocJackID[i] = jack; jack = append_midi1_out_jack(midi2, config, + &midi2->in_cable_mapping[i], USB_MS_EXTERNAL, jack); if (jack < 0) return jack; } } - if (blk->info.direction != SNDRV_UMP_DIR_OUTPUT) { + if (midi2->num_midi1_in) { midi2_midi1_ep_in_class_desc.bLength = - USB_DT_MS_ENDPOINT_SIZE(blk->info.num_groups); + USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_in); total += midi2_midi1_ep_in_class_desc.bLength; midi2_midi1_ep_in_class_desc.bNumEmbMIDIJack = - blk->info.num_groups; - total += blk->info.num_groups * + midi2->num_midi1_in; + total += midi2->num_midi1_in * (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1)); - for (i = 0; i < blk->info.num_groups; i++) { + for (i = 0; i < midi2->num_midi1_in; i++) { jack = append_midi1_in_jack(midi2, config, + &midi2->out_cable_mapping[i], USB_MS_EXTERNAL); if (jack < 0) return jack; jack = append_midi1_out_jack(midi2, config, + &midi2->out_cable_mapping[i], USB_MS_EMBEDDED, jack); if (jack < 0) return jack; @@ -1783,12 +1804,12 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2, midi2_midi1_class_desc.wTotalLength = cpu_to_le16(total); - if (blk->info.direction != SNDRV_UMP_DIR_INPUT) { + if (midi2->num_midi1_out) { err = append_configs(config, midi1_out_eps); if (err < 0) return err; } - if (blk->info.direction != SNDRV_UMP_DIR_OUTPUT) { + if (midi2->num_midi1_in) { err = append_configs(config, midi1_in_eps); if (err < 0) return err; @@ -2236,6 +2257,8 @@ CONFIGFS_ATTR(f_midi2_block_opts_, name) F_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3); F_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15); F_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16); +F_MIDI2_BLOCK_OPT(midi1_first_group, "0x%x", 0, 15); +F_MIDI2_BLOCK_OPT(midi1_num_groups, "0x%x", 0, 16); F_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3); F_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1); F_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255); @@ -2265,6 +2288,8 @@ static struct configfs_attribute *f_midi2_block_attrs[] = { &f_midi2_block_opts_attr_direction, &f_midi2_block_opts_attr_first_group, &f_midi2_block_opts_attr_num_groups, + &f_midi2_block_opts_attr_midi1_first_group, + &f_midi2_block_opts_attr_midi1_num_groups, &f_midi2_block_opts_attr_ui_hint, &f_midi2_block_opts_attr_midi_ci_version, &f_midi2_block_opts_attr_sysex8_streams, @@ -2644,6 +2669,9 @@ static struct usb_function_instance *f_midi2_alloc_inst(void) return ERR_PTR(ret); } + /* set up the default MIDI1 (that is mandatory) */ + block_opts->info.midi1_num_groups = 1; + config_group_init_type_name(&opts->func_inst.group, "", &f_midi2_func_type); @@ -2707,6 +2735,16 @@ static int verify_parameters(struct f_midi2_opts *opts) i, j); return -EINVAL; } + + if (bp->midi1_num_groups) { + if (bp->midi1_first_group < bp->first_group || + bp->midi1_first_group + bp->midi1_num_groups > + bp->first_group + bp->num_groups) { + pr_err("f_midi2: Invalid MIDI1 group definitions for block %d:%d\n", + i, j); + return -EINVAL; + } + } } } if (!num_blks) { @@ -2717,6 +2755,46 @@ static int verify_parameters(struct f_midi2_opts *opts) return num_eps; } +/* fill mapping between MIDI 1.0 cable and UMP EP/group */ +static void fill_midi1_cable_mapping(struct f_midi2 *midi2, + struct f_midi2_ep *ep, + int blk) +{ + const struct f_midi2_block_info *binfo = &ep->blks[blk].info; + struct midi1_cable_mapping *map; + int i, group; + + if (!binfo->midi1_num_groups) + return; + if (binfo->direction != SNDRV_UMP_DIR_OUTPUT) { + group = binfo->midi1_first_group; + map = midi2->in_cable_mapping + midi2->num_midi1_in; + for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) { + if (midi2->num_midi1_in >= MAX_CABLES) + break; + map->ep = ep; + map->block = blk; + map->group = group; + midi2->num_midi1_in++; + /* store 1-based cable number */ + ep->in_group_to_cable[group] = midi2->num_midi1_in; + } + } + + if (binfo->direction != SNDRV_UMP_DIR_INPUT) { + group = binfo->midi1_first_group; + map = midi2->out_cable_mapping + midi2->num_midi1_out; + for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) { + if (midi2->num_midi1_out >= MAX_CABLES) + break; + map->ep = ep; + map->block = blk; + map->group = group; + midi2->num_midi1_out++; + } + } +} + /* gadget alloc callback */ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) { @@ -2786,9 +2864,17 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) bp = &ep->blks[blk]; midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s = ump_fb_name(&bp->info); + + fill_midi1_cable_mapping(midi2, ep, blk); } } + if (!midi2->num_midi1_in && !midi2->num_midi1_out) { + pr_err("f_midi2: MIDI1 definition is missing\n"); + do_f_midi2_free(midi2, opts); + return ERR_PTR(-EINVAL); + } + return &midi2->func; } diff --git a/drivers/usb/gadget/function/u_midi2.h b/drivers/usb/gadget/function/u_midi2.h index a68dc2ea035e..4e7adb41dfb7 100644 --- a/drivers/usb/gadget/function/u_midi2.h +++ b/drivers/usb/gadget/function/u_midi2.h @@ -18,6 +18,8 @@ struct f_midi2_block_info { unsigned int direction; /* FB direction: 1-3 */ unsigned int first_group; /* first UMP group: 0-15 */ unsigned int num_groups; /* number of UMP groups: 1-16 */ + unsigned int midi1_first_group; /* first UMP group for MIDI 1.0 */ + unsigned int midi1_num_groups; /* number of UMP groups for MIDI 1.0 */ unsigned int ui_hint; /* UI-hint: 0-3 */ unsigned int midi_ci_version; /* MIDI-CI version: 0-255 */ unsigned int sysex8_streams; /* number of sysex8 streams: 0-255 */ -- cgit From 3a3be3a1ebf03241bfbfcc93c084b4a2c6a13bdf Mon Sep 17 00:00:00 2001 From: Haotien Hsu Date: Thu, 27 Jul 2023 15:49:27 +0800 Subject: usb: xhci: tegra: Add shutdown callback for Tegra XUSB If memory accesses by the Tegra XUSB controller are translated through the SMMU (System MMU), the hardware may continue accessing memory even after the SMMU translations have been disabled during the shutdown process and this can in turn cause unpredictable crashes. Fix this by adding a shutdown implementation that ensures the hardware is turned off during system reboot or shutdown. Signed-off-by: Henry Lin Signed-off-by: Haotien Hsu Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20230727074927.2428611-1-haotienh@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 51e236c1ff71..4ec149313cc8 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1912,6 +1912,15 @@ put_padctl: return err; } +static void tegra_xusb_disable(struct tegra_xusb *tegra) +{ + tegra_xusb_powergate_partitions(tegra); + tegra_xusb_powerdomain_remove(tegra->dev, tegra); + tegra_xusb_phy_disable(tegra); + tegra_xusb_clk_disable(tegra); + regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); +} + static void tegra_xusb_remove(struct platform_device *pdev) { struct tegra_xusb *tegra = platform_get_drvdata(pdev); @@ -1934,14 +1943,18 @@ static void tegra_xusb_remove(struct platform_device *pdev) pm_runtime_put(&pdev->dev); - tegra_xusb_powergate_partitions(tegra); + tegra_xusb_disable(tegra); + tegra_xusb_padctl_put(tegra->padctl); +} - tegra_xusb_powerdomain_remove(&pdev->dev, tegra); +static void tegra_xusb_shutdown(struct platform_device *pdev) +{ + struct tegra_xusb *tegra = platform_get_drvdata(pdev); - tegra_xusb_phy_disable(tegra); - tegra_xusb_clk_disable(tegra); - regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); - tegra_xusb_padctl_put(tegra->padctl); + pm_runtime_get_sync(&pdev->dev); + disable_irq(tegra->xhci_irq); + xhci_shutdown(tegra->hcd); + tegra_xusb_disable(tegra); } static bool xhci_hub_ports_suspended(struct xhci_hub *hub) @@ -2652,6 +2665,7 @@ MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); static struct platform_driver tegra_xusb_driver = { .probe = tegra_xusb_probe, .remove_new = tegra_xusb_remove, + .shutdown = tegra_xusb_shutdown, .driver = { .name = "tegra-xusb", .pm = &tegra_xusb_pm_ops, -- cgit From fbfac685eca91881b686dd6607e1ae7336a08985 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:47 +0800 Subject: usb: ehci-npcm7xx: fix typo in npcm7xx_ehci_hcd_drv_probe() Replace tab with space. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-1-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-npcm7xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c index ad79ce63afcf..0b3b3bd689cf 100644 --- a/drivers/usb/host/ehci-npcm7xx.c +++ b/drivers/usb/host/ehci-npcm7xx.c @@ -53,7 +53,7 @@ static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev) int irq; int retval; - dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n"); + dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n"); if (usb_disabled()) return -ENODEV; -- cgit From 49aa25ad8574ee4410aa3910cbf6d28500c1c2c0 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:48 +0800 Subject: usb: chipidea/core: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-2-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 51994d655b82..6e1196b53253 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1028,8 +1028,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) return -ENODEV; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(base)) return PTR_ERR(base); -- cgit From f62d9201b0427802a9443082507b6a7d4184f818 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:49 +0800 Subject: usb: ohci-at91: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Acked-by: Alan Stern Link: https://lore.kernel.org/r/20230726113816.888-3-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 3b023ea71f8d..7ed9a3bcea55 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -200,8 +200,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, return -ENOMEM; ohci_at91 = hcd_to_ohci_at91_priv(hcd); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { retval = PTR_ERR(hcd->regs); goto err; -- cgit From ad089faf79e5ab949a301d60611842254060adb6 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:50 +0800 Subject: usb: ohci-at91: Remove redundant msg at probe time platform_get_irq() directly prints error information. Signed-off-by: Yangtao Li Reviewed-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230726113816.888-4-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 7ed9a3bcea55..dc31a7fa7ca4 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -190,10 +190,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, int irq; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_dbg(dev, "hcd probe: missing irq resource\n"); + if (irq < 0) return irq; - } hcd = usb_create_hcd(driver, dev, "at91"); if (!hcd) -- cgit From 187eeae3e9c7a1c315e5077b1d35c6622d0c28a1 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:51 +0800 Subject: usb: gadget: udc: udc-xilinx: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-5-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/udc-xilinx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 0a025bf14e06..f301b09bf3f8 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -2078,8 +2078,7 @@ static int xudc_probe(struct platform_device *pdev) udc->req->usb_req.buf = buff; /* Map the registers */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->addr = devm_ioremap_resource(&pdev->dev, res); + udc->addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(udc->addr)) return PTR_ERR(udc->addr); -- cgit From 3318f9c1a87b0c4983141cd1740fc553214b9f42 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:52 +0800 Subject: usb: gadget: aspeed: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-6-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/aspeed-vhub/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index 16f2db8c4a2b..f60a019bb173 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -328,8 +328,7 @@ static int ast_vhub_probe(struct platform_device *pdev) vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1, VHUB_IRQ_DEV1_BIT); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - vhub->regs = devm_ioremap_resource(&pdev->dev, res); + vhub->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(vhub->regs)) { dev_err(&pdev->dev, "Failed to map resources\n"); return PTR_ERR(vhub->regs); -- cgit From effc991663ace2911724171d8ec3546e80bd0dac Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:53 +0800 Subject: usb: gadget/snps_udc_plat: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-7-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/snps_udc_plat.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index 0ed685db149d..35c7a149b977 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -112,8 +112,7 @@ static int udc_plat_probe(struct platform_device *pdev) spin_lock_init(&udc->lock); udc->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->virt_addr = devm_ioremap_resource(dev, res); + udc->virt_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(udc->virt_addr)) return PTR_ERR(udc->virt_addr); -- cgit From 0ea395866b2b3639baf1cb050940ed08c6814302 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:54 +0800 Subject: usb: gadget/atmel_usba_udc: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Reviewed-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230726113816.888-8-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/atmel_usba_udc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 6c0ed3fa5eb1..02b1bef5e22e 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2285,15 +2285,13 @@ static int usba_udc_probe(struct platform_device *pdev) udc->gadget = usba_gadget_template; INIT_LIST_HEAD(&udc->gadget.ep_list); - res = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); - udc->regs = devm_ioremap_resource(&pdev->dev, res); + udc->regs = devm_platform_get_and_ioremap_resource(pdev, CTRL_IOMEM_ID, &res); if (IS_ERR(udc->regs)) return PTR_ERR(udc->regs); dev_info(&pdev->dev, "MMIO registers at %pR mapped at %p\n", res, udc->regs); - res = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID); - udc->fifo = devm_ioremap_resource(&pdev->dev, res); + udc->fifo = devm_platform_get_and_ioremap_resource(pdev, FIFO_IOMEM_ID, &res); if (IS_ERR(udc->fifo)) return PTR_ERR(udc->fifo); dev_info(&pdev->dev, "FIFO at %pR mapped at %p\n", res, udc->fifo); -- cgit From 50fd16ebf2f18299373673cc5c2eeb1e8e1367ec Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:55 +0800 Subject: usb: gadget: aspeed_udc: Convert to devm_platform_ioremap_resource() Use devm_platform_ioremap_resource() to simplify code. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-9-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/aspeed_udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c index 01968e2167f9..2ef89a442f50 100644 --- a/drivers/usb/gadget/udc/aspeed_udc.c +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -1468,7 +1468,6 @@ static int ast_udc_probe(struct platform_device *pdev) enum usb_device_speed max_speed; struct device *dev = &pdev->dev; struct ast_udc_dev *udc; - struct resource *res; int rc; udc = devm_kzalloc(&pdev->dev, sizeof(struct ast_udc_dev), GFP_KERNEL); @@ -1484,8 +1483,7 @@ static int ast_udc_probe(struct platform_device *pdev) udc->gadget.name = "aspeed-udc"; udc->gadget.dev.init_name = "gadget"; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->reg = devm_ioremap_resource(&pdev->dev, res); + udc->reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->reg)) { dev_err(&pdev->dev, "Failed to map resources\n"); return PTR_ERR(udc->reg); -- cgit From 5645caa80ffd02c27ea8e5a43e7c847eadb421f5 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:56 +0800 Subject: usb: ehci-atmel: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Reviewed-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230726113816.888-10-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-atmel.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 61808c51e702..e14b66d848ee 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -122,8 +122,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev) } atmel_ehci = hcd_to_atmel_ehci_priv(hcd); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { retval = PTR_ERR(hcd->regs); goto fail_request_resource; -- cgit From 4ecb32d6e23a68f77220c2a0d7d26f72c3cac06f Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:57 +0800 Subject: usb: ehci-platform: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-11-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 83bf56c9424f..98b073185e1c 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -359,8 +359,7 @@ static int ehci_platform_probe(struct platform_device *dev) goto err_reset; } - res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); goto err_power; -- cgit From 1c13321c0c69959306e5fa626cc786299bb0157b Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:58 +0800 Subject: usb: ehci-spear: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-12-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-spear.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 1407703649be..d0e94e4c9fe2 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -91,8 +91,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) goto fail; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { retval = PTR_ERR(hcd->regs); goto err_put_hcd; -- cgit From a9ec284ba1714ad4260e976d6abd1c93e363c499 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:37:59 +0800 Subject: usb: isp1362-hcd: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-13-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 606f0a64f3b7..a52c3d858f3e 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2651,8 +2651,7 @@ static int isp1362_probe(struct platform_device *pdev) if (IS_ERR(addr_reg)) return PTR_ERR(addr_reg); - data = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data_reg = devm_ioremap_resource(&pdev->dev, data); + data_reg = devm_platform_get_and_ioremap_resource(pdev, 0, &data); if (IS_ERR(data_reg)) return PTR_ERR(data_reg); -- cgit From 54a15a7e6ad54ca2100f7eea79ff31a19a85dd51 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:00 +0800 Subject: usb: ohci-da8xx: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-14-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-da8xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 9bd6cb9af364..d9adae53466b 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -436,8 +436,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev) goto err; } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(dev, mem); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(hcd->regs)) { error = PTR_ERR(hcd->regs); goto err; -- cgit From 096019682bf9ed340bf48a350d73142b2fc645c6 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:01 +0800 Subject: usb: host: ohci-platform: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-15-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-platform.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 45a2c981319e..4a75507325dd 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -200,8 +200,7 @@ static int ohci_platform_probe(struct platform_device *dev) goto err_reset; } - res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); goto err_power; -- cgit From 888765e7e6d26925b3e97b11ac30591c5ec8e2fc Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:02 +0800 Subject: usb: ehci-sh: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-16-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sh.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 0520e762801d..575c513f7ea0 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -95,8 +95,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) goto fail_create_hcd; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { ret = PTR_ERR(hcd->regs); goto fail_request_resource; -- cgit From 31a7b792bdb15bd2fd6fd0cc3420db6ce12c9804 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:03 +0800 Subject: usb: ohci-exynos: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-17-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-exynos.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index ab31c459b32d..20e26a474591 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -149,8 +149,7 @@ static int exynos_ohci_probe(struct platform_device *pdev) if (err) goto fail_clk; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); goto fail_io; -- cgit From 438ca58898dcf5ac010188a9cf3c18387427bd84 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:04 +0800 Subject: usb: ehci-npcm7xx: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-18-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-npcm7xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c index 0b3b3bd689cf..3d3317a1a0b3 100644 --- a/drivers/usb/host/ehci-npcm7xx.c +++ b/drivers/usb/host/ehci-npcm7xx.c @@ -79,8 +79,7 @@ static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev) goto fail; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { retval = PTR_ERR(hcd->regs); goto err_put_hcd; -- cgit From 1b19426bc5931a0940c25feb2abe44ce6534964c Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:05 +0800 Subject: usb: ohci-nxp: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-19-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-nxp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index c04b2af5c766..8264c454f6bd 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -202,8 +202,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) goto fail_hcd; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { ret = PTR_ERR(hcd->regs); goto fail_resource; -- cgit From 4da14754684f1c70a6860e4ea2aaddf78c3e40c3 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:06 +0800 Subject: usb: ehci-orion: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-20-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-orion.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index a5f4e2f98346..54f74b45c4b1 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -232,8 +232,7 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) if (err) goto err; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(&pdev->dev, res); + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) { err = PTR_ERR(regs); goto err; -- cgit From 3525df13ae56ed81ffd227426273ecfa42237596 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:07 +0800 Subject: usb: ehci-fsl: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-21-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 3c776f4de4b8..5b1ce394a417 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -87,8 +87,7 @@ static int fsl_ehci_drv_probe(struct platform_device *pdev) goto err1; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { retval = PTR_ERR(hcd->regs); goto err2; -- cgit From edc81ba4accc86cbec7e1f2fc445b2f0ebd5dfe9 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:08 +0800 Subject: usb: oxu210hp-hcd: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-22-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/oxu210hp-hcd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 50c1ccabb0f5..d467472f9d3c 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -4230,8 +4230,7 @@ static int oxu_drv_probe(struct platform_device *pdev) return irq; dev_dbg(&pdev->dev, "IRQ resource %d\n", irq); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(base)) { ret = PTR_ERR(base); goto error; -- cgit From e873efdb078dfda657930ddc6023a3414b34ba78 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:09 +0800 Subject: usb: ohci-pxa27x: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-23-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-pxa27x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 7410442f720f..357d9aee38a3 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -435,8 +435,7 @@ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev) if (!hcd) return -ENOMEM; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, r); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r); if (IS_ERR(hcd->regs)) { retval = PTR_ERR(hcd->regs); goto err; -- cgit From e7ef16450031aaf21ec3915b42f2bb794dd6b07a Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:10 +0800 Subject: usb: ehci-omap: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-24-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-omap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index cb6509a735ac..b24f371a46f3 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -113,8 +113,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) if (irq < 0) return irq; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(dev, res); + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) return PTR_ERR(regs); -- cgit From eeaf04a9cfb4572dfe8faaaeadde68c91798c35c Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:11 +0800 Subject: usb: ohci-spear: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-25-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-spear.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index f4b2656407dd..993f347c5c28 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -68,8 +68,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) goto fail; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { retval = PTR_ERR(hcd->regs); goto err_put_hcd; -- cgit From d64d7f919f563686bb56aa57c3e8c3bad8f2afc1 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:12 +0800 Subject: usb: ehci-mv: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-26-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-mv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 9320cf0e5bc7..2f1fc7eb8b72 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -142,8 +142,7 @@ static int mv_ehci_probe(struct platform_device *pdev) goto err_put_hcd; } - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ehci_mv->base = devm_ioremap_resource(&pdev->dev, r); + ehci_mv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r); if (IS_ERR(ehci_mv->base)) { retval = PTR_ERR(ehci_mv->base); goto err_put_hcd; -- cgit From 7f5094fae40546b6a242d0598edeaf2ad1de828d Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:13 +0800 Subject: usb: uhci-platform: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-27-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-platform.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 71ca532fc086..3dec5dd3a0d5 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -91,8 +91,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) uhci = hcd_to_uhci(hcd); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { ret = PTR_ERR(hcd->regs); goto err_rmr; -- cgit From 23396172f20a926e6c4c16e43e3499d9ba17cf45 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:14 +0800 Subject: usb: ehci-st: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-28-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-st.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c index ee0976b815b4..2dbb0d86daaa 100644 --- a/drivers/usb/host/ehci-st.c +++ b/drivers/usb/host/ehci-st.c @@ -158,11 +158,6 @@ static int st_ehci_platform_probe(struct platform_device *dev) irq = platform_get_irq(dev, 0); if (irq < 0) return irq; - res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res_mem) { - dev_err(&dev->dev, "no memory resource provided"); - return -ENXIO; - } hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, dev_name(&dev->dev)); @@ -222,14 +217,13 @@ static int st_ehci_platform_probe(struct platform_device *dev) goto err_put_clks; } - hcd->rsrc_start = res_mem->start; - hcd->rsrc_len = resource_size(res_mem); - - hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); goto err_put_clks; } + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) -- cgit From 61baaa656408b4b3b630f8c3792eb87845fca5e5 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:15 +0800 Subject: usb: ehci-exynos: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Reviewed-by: Andi Shyti Link: https://lore.kernel.org/r/20230726113816.888-29-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-exynos.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 20f8c0ec6810..f644b131cc0b 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -173,8 +173,7 @@ static int exynos_ehci_probe(struct platform_device *pdev) if (err) goto fail_clk; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(&pdev->dev, res); + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); goto fail_io; -- cgit From ba0b3af706305e5b11fd832eecd2c4a7fce57156 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Jul 2023 19:38:16 +0800 Subject: usb: ohci-st: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yangtao Li Link: https://lore.kernel.org/r/20230726113816.888-30-frank.li@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-st.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c index 884e447a8098..214342013f7e 100644 --- a/drivers/usb/host/ohci-st.c +++ b/drivers/usb/host/ohci-st.c @@ -139,12 +139,6 @@ static int st_ohci_platform_probe(struct platform_device *dev) if (irq < 0) return irq; - res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res_mem) { - dev_err(&dev->dev, "no memory resource provided"); - return -ENXIO; - } - hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, dev_name(&dev->dev)); if (!hcd) @@ -199,14 +193,14 @@ static int st_ohci_platform_probe(struct platform_device *dev) goto err_power; } - hcd->rsrc_start = res_mem->start; - hcd->rsrc_len = resource_size(res_mem); - - hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); goto err_power; } + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) goto err_power; -- cgit From 6bb75eb9ff3acfc3e1f31c01d70e72b3b3637167 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Fri, 28 Jul 2023 16:51:20 +0800 Subject: usb: gadget: midi2: fix missing unlock in f_midi2_block_opts_create() In the error path in f_midi2_block_opts_create(), mutex_unlock() is missed, fix it by move the unlock after 'out' label. Fixes: 29ee7a4dddd5 ("usb: gadget: midi2: Add configfs support") Signed-off-by: Yang Yingliang Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230728085120.3192474-1-yangyingliang@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_midi2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index f1c47753e0c1..5a971ba600fe 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -2350,8 +2350,8 @@ static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts, ep_opts->blks[blk] = block_opts; *block_p = block_opts; - mutex_unlock(&ep_opts->opts->lock); out: + mutex_unlock(&ep_opts->opts->lock); return ret; } -- cgit From 25a1489dc4214133ae44a7385b4f951887857eef Mon Sep 17 00:00:00 2001 From: Varshini Rajendran Date: Fri, 28 Jul 2023 15:53:18 +0530 Subject: dt-bindings: usb: ehci: Add atmel at91sam9g45-ehci compatible Document at91sam9g45-ehci compatible for usb-ehci. Signed-off-by: Varshini Rajendran Acked-by: Rob Herring Link: https://lore.kernel.org/r/20230728102318.265360-1-varshini.rajendran@microchip.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/generic-ehci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index b956bb5fada7..e5c8f4e085de 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -67,6 +67,7 @@ properties: - const: generic-ehci - items: - enum: + - atmel,at91sam9g45-ehci - cavium,octeon-6335-ehci - ibm,usb-ehci-440epx - ibm,usb-ehci-460ex -- cgit From e0f75882521f1416406981299fd99777b5d02dcc Mon Sep 17 00:00:00 2001 From: Guiting Shen Date: Fri, 28 Jul 2023 20:06:48 +0800 Subject: usb: ohci-at91: Fix the unhandle interrupt when resume The ohci_hcd_at91_drv_suspend() sets ohci->rh_state to OHCI_RH_HALTED when suspend which will let the ohci_irq() skip the interrupt after resume. And nobody to handle this interrupt. According to the comment in ohci_hcd_at91_drv_suspend(), it need to reset when resume from suspend(MEM) to fix by setting "hibernated" argument of ohci_resume(). Signed-off-by: Guiting Shen Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20230728120648.5878-1-aarongt.shen@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index dc31a7fa7ca4..f691cd98a574 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -669,7 +669,13 @@ ohci_hcd_at91_drv_resume(struct device *dev) else at91_start_clock(ohci_at91); - ohci_resume(hcd, false); + /* + * According to the comment in ohci_hcd_at91_drv_suspend() + * we need to do a reset if the 48Mhz clock was stopped, + * that is, if ohci_at91->wakeup is clear. Tell ohci_resume() + * to reset in this case by setting its "hibernated" flag. + */ + ohci_resume(hcd, !ohci_at91->wakeup); return 0; } -- cgit From 4202633a3a23c14823226656cac579a75b621cdf Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Sat, 29 Jul 2023 13:30:26 +0800 Subject: phy: realtek: usb: phy-rtk-usb2 and phy-rtk-usb3 needs USB_COMMON When USB_COMMON are not enabled, phy-rtk-usb2 and phy-rtk-usb3 suffers a build error due to a missing usb_debug_root that is provided by CONFIG_USB_COMMON, so make the driver select USB_COMMON. or1k-linux-ld: drivers/phy/realtek/phy-rtk-usb2.o: in function `create_phy_debug_root': >> drivers/phy/realtek/phy-rtk-usb2.c:715: undefined reference to `usb_debug_root' >> or1k-linux-ld: drivers/phy/realtek/phy-rtk-usb2.c:715: undefined reference to `usb_debug_root' or1k-linux-ld: drivers/phy/realtek/phy-rtk-usb3.o: in function `create_phy_debug_root': >> drivers/phy/realtek/phy-rtk-usb3.c:349: undefined reference to `usb_debug_root' >> or1k-linux-ld: drivers/phy/realtek/phy-rtk-usb3.c:349: undefined reference to `usb_debug_root' Fixes: 134e6d25f6bd ("phy: realtek: usb: Add driver for the Realtek SoC USB 2.0 PHY") Fixed: adda6e82a7de ("phy: realtek: usb: Add driver for the Realtek SoC USB 3.0 PHY") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202307290733.weSlHrGp-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202307291022.3pVeTR9z-lkp@intel.com/ Signed-off-by: Stanley Chang Link: https://lore.kernel.org/r/20230729053029.6226-1-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/phy/realtek/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/phy/realtek/Kconfig b/drivers/phy/realtek/Kconfig index a5a5a71edc9c..650e20ed69af 100644 --- a/drivers/phy/realtek/Kconfig +++ b/drivers/phy/realtek/Kconfig @@ -7,6 +7,7 @@ config PHY_RTK_RTD_USB2PHY depends on USB_SUPPORT select GENERIC_PHY select USB_PHY + select USB_COMMON help Enable this to support Realtek SoC USB2 phy transceiver. The DHC (digital home center) RTD series SoCs used the Synopsys @@ -18,6 +19,7 @@ config PHY_RTK_RTD_USB3PHY depends on USB_SUPPORT select GENERIC_PHY select USB_PHY + select USB_COMMON help Enable this to support Realtek SoC USB3 phy transceiver. The DHC (digital home center) RTD series SoCs used the Synopsys -- cgit From f26069c9d7b6f83af953060cf05e425ea3cc7eeb Mon Sep 17 00:00:00 2001 From: Benjamin Bara Date: Fri, 23 Jun 2023 09:28:12 +0200 Subject: usb: misc: onboard-hub: support multiple power supplies As some of the onboard hubs require multiple power supplies, provide the environment to support them. Signed-off-by: Benjamin Bara Acked-by: Matthias Kaehlcke Link: https://lore.kernel.org/r/20230620-hx3-v7-1-f79b4b22a1bf@skidata.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/onboard_usb_hub.c | 37 +++++++++++++++++++++++++++++-------- drivers/usb/misc/onboard_usb_hub.h | 7 +++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index 20b29db90758..135e1878f64a 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -27,6 +27,17 @@ #include "onboard_usb_hub.h" +/* + * Use generic names, as the actual names might differ between hubs. If a new + * hub requires more than the currently supported supplies, add a new one here. + */ +static const char * const supply_names[] = { + "vdd", + "vdd2", +}; + +#define MAX_SUPPLIES ARRAY_SIZE(supply_names) + static void onboard_hub_attach_usb_driver(struct work_struct *work); static struct usb_device_driver onboard_hub_usbdev_driver; @@ -40,7 +51,7 @@ struct usbdev_node { }; struct onboard_hub { - struct regulator *vdd; + struct regulator_bulk_data supplies[MAX_SUPPLIES]; struct device *dev; const struct onboard_hub_pdata *pdata; struct gpio_desc *reset_gpio; @@ -55,9 +66,9 @@ static int onboard_hub_power_on(struct onboard_hub *hub) { int err; - err = regulator_enable(hub->vdd); + err = regulator_bulk_enable(hub->pdata->num_supplies, hub->supplies); if (err) { - dev_err(hub->dev, "failed to enable regulator: %d\n", err); + dev_err(hub->dev, "failed to enable supplies: %d\n", err); return err; } @@ -75,9 +86,9 @@ static int onboard_hub_power_off(struct onboard_hub *hub) gpiod_set_value_cansleep(hub->reset_gpio, 1); - err = regulator_disable(hub->vdd); + err = regulator_bulk_disable(hub->pdata->num_supplies, hub->supplies); if (err) { - dev_err(hub->dev, "failed to disable regulator: %d\n", err); + dev_err(hub->dev, "failed to disable supplies: %d\n", err); return err; } @@ -232,6 +243,7 @@ static int onboard_hub_probe(struct platform_device *pdev) const struct of_device_id *of_id; struct device *dev = &pdev->dev; struct onboard_hub *hub; + unsigned int i; int err; hub = devm_kzalloc(dev, sizeof(*hub), GFP_KERNEL); @@ -246,9 +258,18 @@ static int onboard_hub_probe(struct platform_device *pdev) if (!hub->pdata) return -EINVAL; - hub->vdd = devm_regulator_get(dev, "vdd"); - if (IS_ERR(hub->vdd)) - return PTR_ERR(hub->vdd); + if (hub->pdata->num_supplies > MAX_SUPPLIES) + return dev_err_probe(dev, -EINVAL, "max %zu supplies supported!\n", + MAX_SUPPLIES); + + for (i = 0; i < hub->pdata->num_supplies; i++) + hub->supplies[i].supply = supply_names[i]; + + err = devm_regulator_bulk_get(dev, hub->pdata->num_supplies, hub->supplies); + if (err) { + dev_err(dev, "Failed to get regulator supplies: %d\n", err); + return err; + } hub->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h index 0134af1f9e42..76edde3f5833 100644 --- a/drivers/usb/misc/onboard_usb_hub.h +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -8,30 +8,37 @@ struct onboard_hub_pdata { unsigned long reset_us; /* reset pulse width in us */ + unsigned int num_supplies; /* number of supplies */ }; static const struct onboard_hub_pdata microchip_usb424_data = { .reset_us = 1, + .num_supplies = 1, }; static const struct onboard_hub_pdata realtek_rts5411_data = { .reset_us = 0, + .num_supplies = 1, }; static const struct onboard_hub_pdata ti_tusb8041_data = { .reset_us = 3000, + .num_supplies = 1, }; static const struct onboard_hub_pdata genesys_gl850g_data = { .reset_us = 3, + .num_supplies = 1, }; static const struct onboard_hub_pdata genesys_gl852g_data = { .reset_us = 50, + .num_supplies = 1, }; static const struct onboard_hub_pdata vialab_vl817_data = { .reset_us = 10, + .num_supplies = 1, }; static const struct of_device_id onboard_hub_match[] = { -- cgit From b43cd82a1a40daaf4e1dd7098b8b63b4b8dfb094 Mon Sep 17 00:00:00 2001 From: Benjamin Bara Date: Fri, 23 Jun 2023 09:28:13 +0200 Subject: usb: misc: onboard-hub: add support for Cypress HX3 USB 3.0 family The HX3 comes in different variants (up to 4 USB 3.0 ports; multi-TT), e.g. CYUSB330x/CYUSB331x/CYUSB332x/CYUSB230x. It operates with two different power supplies: 1V2 and 3V3. Add the support for this hub, for controlling the reset pin and the power supplies. Reset time is extracted from data sheet, page 24: "The RESETN pin can be tied to VDD_IO through an external resistor and to ground (GND) through an external capacitor (minimum 5 ms time constant)." V_IH min is given at 0.7 * 3V3 (page 34), therefore use 10ms. Also add USB PIDs for the USB 2.0 and USB 3.0 root hub. Acked-by: Matthias Kaehlcke Signed-off-by: Benjamin Bara Link: https://lore.kernel.org/r/20230620-hx3-v7-2-f79b4b22a1bf@skidata.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/onboard_usb_hub.c | 3 +++ drivers/usb/misc/onboard_usb_hub.h | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index 135e1878f64a..3da1a4659c5f 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -350,6 +350,7 @@ static struct platform_driver onboard_hub_driver = { /************************** USB driver **************************/ +#define VENDOR_ID_CYPRESS 0x04b4 #define VENDOR_ID_GENESYS 0x05e3 #define VENDOR_ID_MICROCHIP 0x0424 #define VENDOR_ID_REALTEK 0x0bda @@ -428,6 +429,8 @@ static void onboard_hub_usbdev_disconnect(struct usb_device *udev) } static const struct usb_device_id onboard_hub_id_table[] = { + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 */ diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h index 76edde3f5833..4026ba64c592 100644 --- a/drivers/usb/misc/onboard_usb_hub.h +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -26,6 +26,11 @@ static const struct onboard_hub_pdata ti_tusb8041_data = { .num_supplies = 1, }; +static const struct onboard_hub_pdata cypress_hx3_data = { + .reset_us = 10000, + .num_supplies = 2, +}; + static const struct onboard_hub_pdata genesys_gl850g_data = { .reset_us = 3, .num_supplies = 1, @@ -46,6 +51,8 @@ static const struct of_device_id onboard_hub_match[] = { { .compatible = "usb424,2517", .data = µchip_usb424_data, }, { .compatible = "usb451,8140", .data = &ti_tusb8041_data, }, { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, + { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, }, + { .compatible = "usb4b4,6506", .data = &cypress_hx3_data, }, { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, -- cgit From 1eca51f58a10259f63fbc1ca77e0582581e9bd48 Mon Sep 17 00:00:00 2001 From: Benjamin Bara Date: Fri, 23 Jun 2023 09:28:14 +0200 Subject: dt-bindings: usb: Add binding for Cypress HX3 USB 3.0 family The HX3 family comes in different variants (up to 4 USB 3.0 ports; multi-TT), e.g. CYUSB330x/CYUSB331x/CYUSB332x/CYUSB230x. This initial version of the binding only describes USB related aspects of the HX3 family, it does not cover the option of connecting the controller as an i2c slave. Reviewed-by: Rob Herring Signed-off-by: Benjamin Bara Link: https://lore.kernel.org/r/20230620-hx3-v7-3-f79b4b22a1bf@skidata.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/cypress,hx3.yaml | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/cypress,hx3.yaml diff --git a/Documentation/devicetree/bindings/usb/cypress,hx3.yaml b/Documentation/devicetree/bindings/usb/cypress,hx3.yaml new file mode 100644 index 000000000000..47add0d85fb8 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/cypress,hx3.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/cypress,hx3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cypress HX3 USB 3.0 hub controller family + +maintainers: + - Benjamin Bara + +allOf: + - $ref: usb-device.yaml# + +properties: + compatible: + enum: + - usb4b4,6504 + - usb4b4,6506 + + reg: true + + reset-gpios: + items: + - description: GPIO specifier for RESETN pin. + + vdd-supply: + description: + 1V2 power supply (VDD_EFUSE, AVDD12, DVDD12). + + vdd2-supply: + description: + 3V3 power supply (AVDD33, VDD_IO). + + peer-hub: + $ref: /schemas/types.yaml#/definitions/phandle + description: + phandle to the peer hub on the controller. + +required: + - compatible + - reg + - peer-hub + - vdd-supply + - vdd2-supply + +additionalProperties: false + +examples: + - | + #include + + usb { + dr_mode = "host"; + #address-cells = <1>; + #size-cells = <0>; + + /* 2.0 hub on port 1 */ + hub_2_0: hub@1 { + compatible = "usb4b4,6504"; + reg = <1>; + peer-hub = <&hub_3_0>; + reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>; + vdd-supply = <®_1v2_usb>; + vdd2-supply = <®_3v3_usb>; + }; + + /* 3.0 hub on port 2 */ + hub_3_0: hub@2 { + compatible = "usb4b4,6506"; + reg = <2>; + peer-hub = <&hub_2_0>; + reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>; + vdd-supply = <®_1v2_usb>; + vdd2-supply = <®_3v3_usb>; + }; + }; -- cgit From 6dacc6db4628af3fdc0627dca6dd6104ec9138ed Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sun, 2 Jul 2023 15:06:08 -0400 Subject: thunderbolt: Set variable tmu_params storage class specifier to static smatch reports drivers/thunderbolt/tmu.c:30:3: warning: symbol 'tmu_params' was not declared. Should it be static? This variable is only used in its defining file so should be static. Signed-off-by: Tom Rix Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 1269f417515b..c789024d7ffe 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -19,7 +19,7 @@ static const unsigned int tmu_rates[] = { [TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI] = 16, }; -const struct { +static const struct { unsigned int freq_meas_window; unsigned int avg_const; unsigned int delta_avg_const; -- cgit From adbe9720e573d0466b506df3b848bcf4c4dd63c9 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Fri, 4 Aug 2023 18:54:30 +0800 Subject: usb: musb: Fix deferred probing When platform_get_irq_byname() fails, it may return -EPROBE_DEFER, which suggested deferred probing, it is very important to propagate it upstream. We cannot override it with other error code. Commit ce753ad1549c ("platform: finally disallow IRQ0 in platform_get_irq() and its ilk") makes sure IRQ0 is not returned. Signed-off-by: Zhu Wang Link: https://lore.kernel.org/r/20230804105430.95773-1-wangzhu9@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index ecbd3784bec3..b24adb5b399f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2610,8 +2610,8 @@ static int musb_probe(struct platform_device *pdev) int irq = platform_get_irq_byname(pdev, "mc"); void __iomem *base; - if (irq <= 0) - return -ENODEV; + if (irq < 0) + return irq; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) -- cgit From d3af2f4c0434f3dde7f921f52614af81787bbec6 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Tue, 1 Aug 2023 20:28:34 +0800 Subject: usb: typec: tcpci_mt6370: remove redundant dev_err_probe() When platform_get_irq() is called, the error message has been printed, so it need not to call dev_err_probe() to present error messages. Signed-off-by: Zhu Wang Reviewed-by: Guenter Roeck Reviewed-by: AngeloGioacchino Del Regno Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230801122834.89168-1-wangzhu9@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci_mt6370.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c index 2a079464b398..9cda1005ef01 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6370.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c @@ -147,7 +147,7 @@ static int mt6370_tcpc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return dev_err_probe(dev, irq, "Failed to get irq\n"); + return irq; /* Assign TCPCI feature and ops */ priv->tcpci_data.auto_discharge_disconnect = 1; -- cgit From 4f4bda58c5aef66493f9878463c3f28502653067 Mon Sep 17 00:00:00 2001 From: Zhu Wang Date: Fri, 4 Aug 2023 19:00:05 +0800 Subject: usb: gadget: udc: gr_udc: Fix deferred probing When platform_get_irq() fails, it may return -EPROBE_DEFER, which suggested deferred probing, it is very important to propagate it upstream. We cannot override it with other error code. Commit ce753ad1549c ("platform: finally disallow IRQ0 in platform_get_irq() and its ilk") makes sure IRQ0 is not returned. Signed-off-by: Zhu Wang Link: https://lore.kernel.org/r/20230804110005.97061-1-wangzhu9@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/gr_udc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 0c3969301a53..c6dfa7cccc11 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2136,15 +2136,15 @@ static int gr_probe(struct platform_device *pdev) return PTR_ERR(regs); dev->irq = platform_get_irq(pdev, 0); - if (dev->irq <= 0) - return -ENODEV; + if (dev->irq < 0) + return dev->irq; /* Some core configurations has separate irqs for IN and OUT events */ dev->irqi = platform_get_irq(pdev, 1); if (dev->irqi > 0) { dev->irqo = platform_get_irq(pdev, 2); - if (dev->irqo <= 0) - return -ENODEV; + if (dev->irqo < 0) + return dev->irqo; } else { dev->irqi = 0; } -- cgit From ebcf774671da8b4985f61af11fd67ef74ffd48cf Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Fri, 4 Aug 2023 17:17:12 +0800 Subject: USB: cytherm: Correct the code style issue of redundant spaces Ther are many redundant spaces, which is not consistent with the kernel code style, so remove it. Signed-off-by: Ruan Jinjie Link: https://lore.kernel.org/r/20230804091713.41503-1-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/cytherm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 3e3802aaefa3..9504178f5c89 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -307,17 +307,17 @@ static int cytherm_probe(struct usb_interface *interface, struct usb_cytherm *dev = NULL; int retval = -ENOMEM; - dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL); + dev = kzalloc(sizeof(struct usb_cytherm), GFP_KERNEL); if (!dev) goto error_mem; dev->udev = usb_get_dev(udev); - usb_set_intfdata (interface, dev); + usb_set_intfdata(interface, dev); dev->brightness = 0xFF; - dev_info (&interface->dev, + dev_info(&interface->dev, "Cypress thermometer device now attached\n"); return 0; @@ -329,10 +329,10 @@ static void cytherm_disconnect(struct usb_interface *interface) { struct usb_cytherm *dev; - dev = usb_get_intfdata (interface); + dev = usb_get_intfdata(interface); /* first remove the files, then NULL the pointer */ - usb_set_intfdata (interface, NULL); + usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); -- cgit From 98d6db05eda86ce50c84dcf29dcc9de0e9f8f140 Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Thu, 3 Aug 2023 11:28:38 +0800 Subject: usb: gadget: udc: Remove redundant initialization for udc_driver There is a warning reported by coccinelle: ./drivers/usb/gadget/udc/renesas_usbf.c:3381:3-8: No need to set .owner here. The core will do it. The module_platform_driver() will set "THIS_MODULE" to driver.owner when register a driver for platform-level devices, so it is redundant initialization to set driver.owner in udc_driver statement. Remove it to silence the warning. Signed-off-by: Li Zetao Acked-by: Herve Codina Link: https://lore.kernel.org/r/20230803032838.3045730-1-lizetao1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/renesas_usbf.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c index 3482b41d0646..657f265ac7cc 100644 --- a/drivers/usb/gadget/udc/renesas_usbf.c +++ b/drivers/usb/gadget/udc/renesas_usbf.c @@ -3378,7 +3378,6 @@ MODULE_DEVICE_TABLE(of, usbf_match); static struct platform_driver udc_driver = { .driver = { .name = "usbf_renesas", - .owner = THIS_MODULE, .of_match_table = usbf_match, }, .probe = usbf_probe, -- cgit From f2e5812fb4fb2bef665bd86dc579b292faae2029 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Wed, 2 Aug 2023 11:12:36 +0800 Subject: usb: host: Do not check for 0 return after calling platform_get_irq() It is not possible for platform_get_irq() to return 0. Use the return value from platform_get_irq(). Signed-off-by: Ruan Jinjie Reviewed-by: Justin Chen Link: https://lore.kernel.org/r/20230802031236.2272196-1-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-atmel.c | 4 ++-- drivers/usb/host/ehci-brcm.c | 4 ++-- drivers/usb/host/ehci-orion.c | 4 ++-- drivers/usb/host/ehci-sh.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index e14b66d848ee..6a6e1c510b28 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -102,8 +102,8 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev) pr_debug("Initializing Atmel-SoC USB Host Controller\n"); irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - retval = -ENODEV; + if (irq < 0) { + retval = irq; goto fail_create_hcd; } diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 0362a082abb4..77e42c739c58 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -140,8 +140,8 @@ static int ehci_brcm_probe(struct platform_device *pdev) return err; irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return irq ? irq : -EINVAL; + if (irq < 0) + return irq; /* Hook the hub control routine to work around a bug */ ehci_brcm_hc_driver.hub_control = ehci_brcm_hub_control; diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 54f74b45c4b1..6c47ab0a491d 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -218,8 +218,8 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) pr_debug("Initializing Orion-SoC USB Host Controller\n"); irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - err = -ENODEV; + if (irq < 0) { + err = irq; goto err; } diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 575c513f7ea0..d31d9506e41a 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -82,8 +82,8 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) return -ENODEV; irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - ret = -ENODEV; + if (irq < 0) { + ret = irq; goto fail_create_hcd; } -- cgit From 40f362ffa5e9ddf413825c99e9121db0ab59301f Mon Sep 17 00:00:00 2001 From: Kyle Tso Date: Tue, 1 Aug 2023 00:21:59 +0800 Subject: usb: typec: tcpm: Refactor the PPS APDO selection In current design of the PPS APDO selection, TCPM power supply only accepts the requested voltage which is inside the range of the selected PPS profile. To extend the flexibility and usability, remove the checks about the voltage range in current profile. And try to search all PPS APDOs of the Source that fit the requested voltage. Also remove some redundant checks in tcpm_pd_build_pps_request. Signed-off-by: Kyle Tso Acked-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230731162159.19483-1-kyletso@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 122 ++++++++---------------------------------- 1 file changed, 21 insertions(+), 101 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 829d75ebab42..9c496b8302b4 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -3253,23 +3253,12 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, return ret; } -#define min_pps_apdo_current(x, y) \ - min(pdo_pps_apdo_max_current(x), pdo_pps_apdo_max_current(y)) - static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) { - unsigned int i, j, max_mw = 0, max_mv = 0; - unsigned int min_src_mv, max_src_mv, src_ma, src_mw; - unsigned int min_snk_mv, max_snk_mv; - unsigned int max_op_mv; - u32 pdo, src, snk; - unsigned int src_pdo = 0, snk_pdo = 0; + unsigned int i, src_ma, max_temp_mw = 0, max_op_ma, op_mw; + unsigned int src_pdo = 0; + u32 pdo, src; - /* - * Select the source PPS APDO providing the most power while staying - * within the board's limits. We skip the first PDO as this is always - * 5V 3A. - */ for (i = 1; i < port->nr_source_caps; ++i) { pdo = port->source_caps[i]; @@ -3280,54 +3269,17 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) continue; } - min_src_mv = pdo_pps_apdo_min_voltage(pdo); - max_src_mv = pdo_pps_apdo_max_voltage(pdo); - src_ma = pdo_pps_apdo_max_current(pdo); - src_mw = (src_ma * max_src_mv) / 1000; - - /* - * Now search through the sink PDOs to find a matching - * PPS APDO. Again skip the first sink PDO as this will - * always be 5V 3A. - */ - for (j = 1; j < port->nr_snk_pdo; j++) { - pdo = port->snk_pdo[j]; - - switch (pdo_type(pdo)) { - case PDO_TYPE_APDO: - if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) { - tcpm_log(port, - "Not PPS APDO (sink), ignoring"); - continue; - } - - min_snk_mv = - pdo_pps_apdo_min_voltage(pdo); - max_snk_mv = - pdo_pps_apdo_max_voltage(pdo); - break; - default: - tcpm_log(port, - "Not APDO type (sink), ignoring"); - continue; - } + if (port->pps_data.req_out_volt > pdo_pps_apdo_max_voltage(pdo) || + port->pps_data.req_out_volt < pdo_pps_apdo_min_voltage(pdo)) + continue; - if (min_src_mv <= max_snk_mv && - max_src_mv >= min_snk_mv) { - max_op_mv = min(max_src_mv, max_snk_mv); - src_mw = (max_op_mv * src_ma) / 1000; - /* Prefer higher voltages if available */ - if ((src_mw == max_mw && - max_op_mv > max_mv) || - src_mw > max_mw) { - src_pdo = i; - snk_pdo = j; - max_mw = src_mw; - max_mv = max_op_mv; - } - } + src_ma = pdo_pps_apdo_max_current(pdo); + max_op_ma = min(src_ma, port->pps_data.req_op_curr); + op_mw = max_op_ma * port->pps_data.req_out_volt / 1000; + if (op_mw > max_temp_mw) { + src_pdo = i; + max_temp_mw = op_mw; } - break; default: tcpm_log(port, "Not APDO type (source), ignoring"); @@ -3337,16 +3289,10 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) if (src_pdo) { src = port->source_caps[src_pdo]; - snk = port->snk_pdo[snk_pdo]; - - port->pps_data.req_min_volt = max(pdo_pps_apdo_min_voltage(src), - pdo_pps_apdo_min_voltage(snk)); - port->pps_data.req_max_volt = min(pdo_pps_apdo_max_voltage(src), - pdo_pps_apdo_max_voltage(snk)); - port->pps_data.req_max_curr = min_pps_apdo_current(src, snk); - port->pps_data.req_out_volt = min(port->pps_data.req_max_volt, - max(port->pps_data.req_min_volt, - port->pps_data.req_out_volt)); + + port->pps_data.req_min_volt = pdo_pps_apdo_min_voltage(src); + port->pps_data.req_max_volt = pdo_pps_apdo_max_voltage(src); + port->pps_data.req_max_curr = pdo_pps_apdo_max_current(src); port->pps_data.req_op_curr = min(port->pps_data.req_max_curr, port->pps_data.req_op_curr); } @@ -3464,32 +3410,16 @@ static int tcpm_pd_send_request(struct tcpm_port *port) static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo) { unsigned int out_mv, op_ma, op_mw, max_mv, max_ma, flags; - enum pd_pdo_type type; unsigned int src_pdo_index; - u32 pdo; src_pdo_index = tcpm_pd_select_pps_apdo(port); if (!src_pdo_index) return -EOPNOTSUPP; - pdo = port->source_caps[src_pdo_index]; - type = pdo_type(pdo); - - switch (type) { - case PDO_TYPE_APDO: - if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) { - tcpm_log(port, "Invalid APDO selected!"); - return -EINVAL; - } - max_mv = port->pps_data.req_max_volt; - max_ma = port->pps_data.req_max_curr; - out_mv = port->pps_data.req_out_volt; - op_ma = port->pps_data.req_op_curr; - break; - default: - tcpm_log(port, "Invalid PDO selected!"); - return -EINVAL; - } + max_mv = port->pps_data.req_max_volt; + max_ma = port->pps_data.req_max_curr; + out_mv = port->pps_data.req_out_volt; + op_ma = port->pps_data.req_op_curr; flags = RDO_USB_COMM | RDO_NO_SUSPEND; @@ -5882,12 +5812,6 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt) goto port_unlock; } - if (req_out_volt < port->pps_data.min_volt || - req_out_volt > port->pps_data.max_volt) { - ret = -EINVAL; - goto port_unlock; - } - target_mw = (port->current_limit * req_out_volt) / 1000; if (target_mw < port->operating_snk_mw) { ret = -EINVAL; @@ -6440,11 +6364,7 @@ static int tcpm_psy_set_prop(struct power_supply *psy, ret = tcpm_psy_set_online(port, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - if (val->intval < port->pps_data.min_volt * 1000 || - val->intval > port->pps_data.max_volt * 1000) - ret = -EINVAL; - else - ret = tcpm_pps_set_out_volt(port, val->intval / 1000); + ret = tcpm_pps_set_out_volt(port, val->intval / 1000); break; case POWER_SUPPLY_PROP_CURRENT_NOW: if (val->intval > port->pps_data.max_curr * 1000) -- cgit From dad255a2361ae14b9d46f94bb3803b0d23f955df Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 31 Jul 2023 11:44:21 -0300 Subject: dt-bindings: usb: ci-hdrc-usb2: Add the "fsl,imx35-usb" entry The "fsl,imx35-usb" entry is missing in the supported compatible string list. Add it to the list. Signed-off-by: Fabio Estevam Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20230731144422.1532498-1-festevam@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml index 532d6464c8b3..85016dd2e187 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml @@ -34,6 +34,7 @@ properties: - fsl,imx23-usb - fsl,imx25-usb - fsl,imx28-usb + - fsl,imx35-usb - fsl,imx50-usb - fsl,imx51-usb - fsl,imx53-usb -- cgit From e2fa03ec60f6313ff7cedce7119e6fbab06a8534 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 31 Jul 2023 11:44:22 -0300 Subject: dt-bindings: usb: ci-hdrc-usb2: Fix clocks/clock-names maxItems imx35.dtsi has three USB clocks. Adjust the maxItems to fix the following schema warnings: imx35-eukrea-mbimxsd35-baseboard.dtb: usb@53ff4400: clock-names: ['ipg', 'ahb', 'per'] is too long Signed-off-by: Fabio Estevam Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20230731144422.1532498-2-festevam@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml index 85016dd2e187..d2303f9a638c 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml @@ -77,11 +77,11 @@ properties: clocks: minItems: 1 - maxItems: 2 + maxItems: 3 clock-names: minItems: 1 - maxItems: 2 + maxItems: 3 dr_mode: true -- cgit From 2569088dbaaff6401630bda517db2e1ac4d92692 Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Tue, 1 Aug 2023 15:14:52 +0800 Subject: phy: realtek: usb: add the error handler for nvmem_cell_read There are following smatch warning: drivers/phy/realtek/phy-rtk-usb2.c:901 get_phy_data_by_efuse() error: 'buf' dereferencing possible ERR_PTR() drivers/phy/realtek/phy-rtk-usb2.c:942 get_phy_data_by_efuse() error: 'buf' dereferencing possible ERR_PTR() drivers/phy/realtek/phy-rtk-usb3.c:460 get_phy_data_by_efuse() error: 'buf' dereferencing possible ERR_PTR() The nvmem_cell_read may fail to read. So, driver must handle failure cases. Fixes: 134e6d25f6bd ("phy: realtek: usb: Add driver for the Realtek SoC USB 2.0 PHY") Fixes: adda6e82a7de ("phy: realtek: usb: Add driver for the Realtek SoC USB 3.0 PHY") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-phy/e7ff2870-c30c-4d8d-a7a9-d2d6a4962eb5@kadam.mountain/ Signed-off-by: Stanley Chang Link: https://lore.kernel.org/r/20230801071509.20096-1-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/phy/realtek/phy-rtk-usb2.c | 14 ++++++++------ drivers/phy/realtek/phy-rtk-usb3.c | 7 ++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c index ed47a1ce5d9c..5e7ee060b404 100644 --- a/drivers/phy/realtek/phy-rtk-usb2.c +++ b/drivers/phy/realtek/phy-rtk-usb2.c @@ -898,9 +898,10 @@ static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy, size_t buf_size; buf = nvmem_cell_read(cell, &buf_size); - value = buf[0] & phy_cfg->dc_driving_mask; - - kfree(buf); + if (!IS_ERR(buf)) { + value = buf[0] & phy_cfg->dc_driving_mask; + kfree(buf); + } nvmem_cell_put(cell); } @@ -939,9 +940,10 @@ static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy, size_t buf_size; buf = nvmem_cell_read(cell, &buf_size); - value = buf[0] & phy_cfg->dc_disconnect_mask; - - kfree(buf); + if (!IS_ERR(buf)) { + value = buf[0] & phy_cfg->dc_disconnect_mask; + kfree(buf); + } nvmem_cell_put(cell); } diff --git a/drivers/phy/realtek/phy-rtk-usb3.c b/drivers/phy/realtek/phy-rtk-usb3.c index 6050f1ef4f6b..7881f908aade 100644 --- a/drivers/phy/realtek/phy-rtk-usb3.c +++ b/drivers/phy/realtek/phy-rtk-usb3.c @@ -457,9 +457,10 @@ static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy, size_t buf_size; buf = nvmem_cell_read(cell, &buf_size); - value = buf[0] & USB_U3_TX_LFPS_SWING_TRIM_MASK; - - kfree(buf); + if (!IS_ERR(buf)) { + value = buf[0] & USB_U3_TX_LFPS_SWING_TRIM_MASK; + kfree(buf); + } nvmem_cell_put(cell); } -- cgit From 803b1c8a0cea58cccde16eba31d285956f4c920c Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 1 Aug 2023 15:01:10 +0800 Subject: usb: typec: tcpm: not sink vbus if operational current is 0mA PD3.0 Spec 6.4.1.3.1 said: For a Sink requiring no power from the Source, the Voltage (B19-10) shall be set to 5V and the Operational Current Shall be set to 0mA. Therefore, we can keep sink path closed if the operational current of the first fixed PDO is 0mA. Signed-off-by: Xu Yang Acked-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230801070110.1653394-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 9c496b8302b4..5a7d8cc04628 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4231,7 +4231,9 @@ static void run_state_machine(struct tcpm_port *port) if (port->slow_charger_loop && (current_lim > PD_P_SNK_STDBY_MW / 5)) current_lim = PD_P_SNK_STDBY_MW / 5; tcpm_set_current_limit(port, current_lim, 5000); - tcpm_set_charge(port, true); + /* Not sink vbus if operational current is 0mA */ + tcpm_set_charge(port, !!pdo_max_current(port->snk_pdo[0])); + if (!port->pd_supported) tcpm_set_state(port, SNK_READY, 0); else @@ -4512,7 +4514,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_current_limit(port, tcpm_get_current_limit(port), 5000); - tcpm_set_charge(port, true); + /* Not sink vbus if operational current is 0mA */ + tcpm_set_charge(port, !!pdo_max_current(port->snk_pdo[0])); } if (port->ams == HARD_RESET) tcpm_ams_finish(port); -- cgit From 3024faf74de7ed7c0f6dc29bced4dbdbd2a1eade Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Fri, 4 Aug 2023 17:32:49 +0800 Subject: usb: gadget: udc: Remove unnecessary NULL values The NULL initialization of the pointers assigned by kzalloc() first is not necessary, because if the kzalloc() failed, the pointers will be assigned NULL, otherwise it works as usual. so remove it. Signed-off-by: Ruan Jinjie Link: https://lore.kernel.org/r/20230804093253.91647-2-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_udc_core.c | 2 +- drivers/usb/gadget/udc/mv_u3d_core.c | 4 ++-- drivers/usb/gadget/udc/mv_udc_core.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 5265ca418cde..ee5705d336e3 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -671,7 +671,7 @@ static int fsl_ep_disable(struct usb_ep *_ep) static struct usb_request * fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { - struct fsl_req *req = NULL; + struct fsl_req *req; req = kzalloc(sizeof *req, gfp_flags); if (!req) diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index 3473048a85f5..2a421f0ff931 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -665,7 +665,7 @@ static int mv_u3d_ep_disable(struct usb_ep *_ep) static struct usb_request * mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { - struct mv_u3d_req *req = NULL; + struct mv_u3d_req *req; req = kzalloc(sizeof *req, gfp_flags); if (!req) @@ -1779,7 +1779,7 @@ static void mv_u3d_remove(struct platform_device *dev) static int mv_u3d_probe(struct platform_device *dev) { - struct mv_u3d *u3d = NULL; + struct mv_u3d *u3d; struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev); int retval = 0; struct resource *r; diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 79db74e2040b..d888dcda2bc8 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -595,7 +595,7 @@ static int mv_ep_disable(struct usb_ep *_ep) static struct usb_request * mv_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { - struct mv_req *req = NULL; + struct mv_req *req; req = kzalloc(sizeof *req, gfp_flags); if (!req) -- cgit From f87ba66a2a1b2e1dc1c4ece51fa5db93c9cf1d43 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Fri, 4 Aug 2023 17:32:50 +0800 Subject: USB: misc: Remove unnecessary NULL values The NULL initialization of the pointers assigned by kzalloc() first is not necessary, because if the kzalloc() failed, the pointers will be assigned NULL, otherwise it works as usual. so remove it. Signed-off-by: Ruan Jinjie Link: https://lore.kernel.org/r/20230804093253.91647-3-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/cypress_cy7c63.c | 2 +- drivers/usb/misc/cytherm.c | 2 +- drivers/usb/misc/usbsevseg.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 14faec51d7a5..cecd7693b741 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -203,7 +203,7 @@ ATTRIBUTE_GROUPS(cypress); static int cypress_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct cypress *dev = NULL; + struct cypress *dev; int retval = -ENOMEM; /* allocate memory for our device state and initialize it */ diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 9504178f5c89..875016dd073c 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -304,7 +304,7 @@ static int cytherm_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); - struct usb_cytherm *dev = NULL; + struct usb_cytherm *dev; int retval = -ENOMEM; dev = kzalloc(sizeof(struct usb_cytherm), GFP_KERNEL); diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index c3114d9bd128..546deff754ba 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -305,7 +305,7 @@ static int sevseg_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); - struct usb_sevsegdev *mydev = NULL; + struct usb_sevsegdev *mydev; int rc = -ENOMEM; mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL); -- cgit From 708368fb845f668ae5817f101e61bad8bbdc2bb8 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Fri, 4 Aug 2023 17:32:51 +0800 Subject: usb: chipidea: udc: Remove an unnecessary NULL value The NULL initialization of the pointers assigned by kzalloc() first is not necessary, because if the kzalloc() failed, the pointers will be assigned NULL, otherwise it works as usual. so remove it. Signed-off-by: Ruan Jinjie Link: https://lore.kernel.org/r/20230804093253.91647-4-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index d58355427eeb..0b7bd3c643c3 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1463,7 +1463,7 @@ static int ep_disable(struct usb_ep *ep) */ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { - struct ci_hw_req *hwreq = NULL; + struct ci_hw_req *hwreq; if (ep == NULL) return NULL; -- cgit From 9de17578b912f39e818035a217efd82a98cd26a6 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Fri, 4 Aug 2023 17:32:52 +0800 Subject: usb: musb: Remove an unnecessary NULL value The NULL initialization of the pointers assigned by kzalloc() first is not necessary, because if the kzalloc() failed, the pointers will be assigned NULL, otherwise it works as usual. so remove it. Signed-off-by: Ruan Jinjie Link: https://lore.kernel.org/r/20230804093253.91647-5-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 31c44325e828..051c6da7cf6d 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1130,7 +1130,7 @@ static int musb_gadget_disable(struct usb_ep *ep) struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct musb_ep *musb_ep = to_musb_ep(ep); - struct musb_request *request = NULL; + struct musb_request *request; request = kzalloc(sizeof *request, gfp_flags); if (!request) -- cgit From b35935d66a3a25848519b9bffc1fca0b6b1004c5 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Fri, 4 Aug 2023 17:32:53 +0800 Subject: USB: usbip: Remove an unnecessary NULL value The NULL initialization of the pointers assigned by kzalloc() first is not necessary, because if the kzalloc() failed, the pointers will be assigned NULL, otherwise it works as usual. so remove it. Signed-off-by: Ruan Jinjie Link: https://lore.kernel.org/r/20230804093253.91647-6-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index 2bc428f2e261..8e42839e6060 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -489,7 +489,7 @@ static void vudc_device_unusable(struct usbip_device *ud) struct vudc_device *alloc_vudc_device(int devid) { - struct vudc_device *udc_dev = NULL; + struct vudc_device *udc_dev; udc_dev = kzalloc(sizeof(*udc_dev), GFP_KERNEL); if (!udc_dev) -- cgit From 976f82e8aa3cfe557ee22a313bf48f418695bedf Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 31 Jul 2023 11:30:45 +0200 Subject: usb: dwc3: dwc3-octeon: Convert to glue driver DWC3 as implemented in Cavium SoC is using UCTL bridge unit between I/O interconnect and USB controller. Currently there is no bond with dwc3 core code, so if anything goes wrong in UCTL setup dwc3 is left in reset, which leads to bus error while trying to read any device register. Thus any failure in UCTL initialization ends with kernel panic. To avoid this move Octeon DWC3 glue code from arch/mips and make it proper glue driver which is used instead of dwc3-of-simple. Signed-off-by: Ladislav Michl Acked-by: Thomas Bogendoerfer Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/ZMd/ReyiY7wS6DvN@lenoch Signed-off-by: Greg Kroah-Hartman --- arch/mips/cavium-octeon/Makefile | 1 - arch/mips/cavium-octeon/octeon-platform.c | 1 - arch/mips/cavium-octeon/octeon-usb.c | 551 ----------------------------- drivers/usb/dwc3/Kconfig | 10 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-octeon.c | 560 ++++++++++++++++++++++++++++++ drivers/usb/dwc3/dwc3-of-simple.c | 1 - 7 files changed, 571 insertions(+), 554 deletions(-) delete mode 100644 arch/mips/cavium-octeon/octeon-usb.c create mode 100644 drivers/usb/dwc3/dwc3-octeon.c diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile index 7c02e542959a..2a5926578841 100644 --- a/arch/mips/cavium-octeon/Makefile +++ b/arch/mips/cavium-octeon/Makefile @@ -18,4 +18,3 @@ obj-y += crypto/ obj-$(CONFIG_MTD) += flash_setup.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_OCTEON_ILM) += oct_ilm.o -obj-$(CONFIG_USB) += octeon-usb.o diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c index ce05c0dd3acd..235c77ce7b18 100644 --- a/arch/mips/cavium-octeon/octeon-platform.c +++ b/arch/mips/cavium-octeon/octeon-platform.c @@ -450,7 +450,6 @@ static const struct of_device_id octeon_ids[] __initconst = { { .compatible = "cavium,octeon-3860-bootbus", }, { .compatible = "cavium,mdio-mux", }, { .compatible = "gpio-leds", }, - { .compatible = "cavium,octeon-7130-usb-uctl", }, {}, }; diff --git a/arch/mips/cavium-octeon/octeon-usb.c b/arch/mips/cavium-octeon/octeon-usb.c deleted file mode 100644 index 2add435ad038..000000000000 --- a/arch/mips/cavium-octeon/octeon-usb.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * XHCI HCD glue for Cavium Octeon III SOCs. - * - * Copyright (C) 2010-2017 Cavium Networks - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * USB Control Register - */ -#define USBDRD_UCTL_CTL 0x00 -/* BIST fast-clear mode select. A BIST run with this bit set - * clears all entries in USBH RAMs to 0x0. - */ -# define USBDRD_UCTL_CTL_CLEAR_BIST BIT(63) -/* 1 = Start BIST and cleared by hardware */ -# define USBDRD_UCTL_CTL_START_BIST BIT(62) -/* Reference clock select for SuperSpeed and HighSpeed PLLs: - * 0x0 = Both PLLs use DLMC_REF_CLK0 for reference clock - * 0x1 = Both PLLs use DLMC_REF_CLK1 for reference clock - * 0x2 = SuperSpeed PLL uses DLMC_REF_CLK0 for reference clock & - * HighSpeed PLL uses PLL_REF_CLK for reference clck - * 0x3 = SuperSpeed PLL uses DLMC_REF_CLK1 for reference clock & - * HighSpeed PLL uses PLL_REF_CLK for reference clck - */ -# define USBDRD_UCTL_CTL_REF_CLK_SEL GENMASK(61, 60) -/* 1 = Spread-spectrum clock enable, 0 = SS clock disable */ -# define USBDRD_UCTL_CTL_SSC_EN BIT(59) -/* Spread-spectrum clock modulation range: - * 0x0 = -4980 ppm downspread - * 0x1 = -4492 ppm downspread - * 0x2 = -4003 ppm downspread - * 0x3 - 0x7 = Reserved - */ -# define USBDRD_UCTL_CTL_SSC_RANGE GENMASK(58, 56) -/* Enable non-standard oscillator frequencies: - * [55:53] = modules -1 - * [52:47] = 2's complement push amount, 0 = Feature disabled - */ -# define USBDRD_UCTL_CTL_SSC_REF_CLK_SEL GENMASK(55, 47) -/* Reference clock multiplier for non-standard frequencies: - * 0x19 = 100MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1 - * 0x28 = 125MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1 - * 0x32 = 50MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1 - * Other Values = Reserved - */ -# define USBDRD_UCTL_CTL_MPLL_MULTIPLIER GENMASK(46, 40) -/* Enable reference clock to prescaler for SuperSpeed functionality. - * Should always be set to "1" - */ -# define USBDRD_UCTL_CTL_REF_SSP_EN BIT(39) -/* Divide the reference clock by 2 before entering the - * REF_CLK_FSEL divider: - * If REF_CLK_SEL = 0x0 or 0x1, then only 0x0 is legal - * If REF_CLK_SEL = 0x2 or 0x3, then: - * 0x1 = DLMC_REF_CLK* is 125MHz - * 0x0 = DLMC_REF_CLK* is another supported frequency - */ -# define USBDRD_UCTL_CTL_REF_CLK_DIV2 BIT(38) -/* Select reference clock freqnuency for both PLL blocks: - * 0x27 = REF_CLK_SEL is 0x0 or 0x1 - * 0x07 = REF_CLK_SEL is 0x2 or 0x3 - */ -# define USBDRD_UCTL_CTL_REF_CLK_FSEL GENMASK(37, 32) -/* Controller clock enable. */ -# define USBDRD_UCTL_CTL_H_CLK_EN BIT(30) -/* Select bypass input to controller clock divider: - * 0x0 = Use divided coprocessor clock from H_CLKDIV - * 0x1 = Use clock from GPIO pins - */ -# define USBDRD_UCTL_CTL_H_CLK_BYP_SEL BIT(29) -/* Reset controller clock divider. */ -# define USBDRD_UCTL_CTL_H_CLKDIV_RST BIT(28) -/* Clock divider select: - * 0x0 = divide by 1 - * 0x1 = divide by 2 - * 0x2 = divide by 4 - * 0x3 = divide by 6 - * 0x4 = divide by 8 - * 0x5 = divide by 16 - * 0x6 = divide by 24 - * 0x7 = divide by 32 - */ -# define USBDRD_UCTL_CTL_H_CLKDIV_SEL GENMASK(26, 24) -/* USB3 port permanently attached: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_USB3_PORT_PERM_ATTACH BIT(21) -/* USB2 port permanently attached: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_USB2_PORT_PERM_ATTACH BIT(20) -/* Disable SuperSpeed PHY: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_USB3_PORT_DISABLE BIT(18) -/* Disable HighSpeed PHY: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_USB2_PORT_DISABLE BIT(16) -/* Enable PHY SuperSpeed block power: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_SS_POWER_EN BIT(14) -/* Enable PHY HighSpeed block power: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_HS_POWER_EN BIT(12) -/* Enable USB UCTL interface clock: 0xx = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_CSCLK_EN BIT(4) -/* Controller mode: 0x0 = Host, 0x1 = Device */ -# define USBDRD_UCTL_CTL_DRD_MODE BIT(3) -/* PHY reset */ -# define USBDRD_UCTL_CTL_UPHY_RST BIT(2) -/* Software reset UAHC */ -# define USBDRD_UCTL_CTL_UAHC_RST BIT(1) -/* Software resets UCTL */ -# define USBDRD_UCTL_CTL_UCTL_RST BIT(0) - -#define USBDRD_UCTL_BIST_STATUS 0x08 -#define USBDRD_UCTL_SPARE0 0x10 -#define USBDRD_UCTL_INTSTAT 0x30 -#define USBDRD_UCTL_PORT_CFG_HS(port) (0x40 + (0x20 * port)) -#define USBDRD_UCTL_PORT_CFG_SS(port) (0x48 + (0x20 * port)) -#define USBDRD_UCTL_PORT_CR_DBG_CFG(port) (0x50 + (0x20 * port)) -#define USBDRD_UCTL_PORT_CR_DBG_STATUS(port) (0x58 + (0x20 * port)) - -/* - * UCTL Configuration Register - */ -#define USBDRD_UCTL_HOST_CFG 0xe0 -/* Indicates minimum value of all received BELT values */ -# define USBDRD_UCTL_HOST_CFG_HOST_CURRENT_BELT GENMASK(59, 48) -/* HS jitter adjustment */ -# define USBDRD_UCTL_HOST_CFG_FLA GENMASK(37, 32) -/* Bus-master enable: 0x0 = Disabled (stall DMAs), 0x1 = enabled */ -# define USBDRD_UCTL_HOST_CFG_BME BIT(28) -/* Overcurrent protection enable: 0x0 = unavailable, 0x1 = available */ -# define USBDRD_UCTL_HOST_OCI_EN BIT(27) -/* Overcurrent sene selection: - * 0x0 = Overcurrent indication from off-chip is active-low - * 0x1 = Overcurrent indication from off-chip is active-high - */ -# define USBDRD_UCTL_HOST_OCI_ACTIVE_HIGH_EN BIT(26) -/* Port power control enable: 0x0 = unavailable, 0x1 = available */ -# define USBDRD_UCTL_HOST_PPC_EN BIT(25) -/* Port power control sense selection: - * 0x0 = Port power to off-chip is active-low - * 0x1 = Port power to off-chip is active-high - */ -# define USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN BIT(24) - -/* - * UCTL Shim Features Register - */ -#define USBDRD_UCTL_SHIM_CFG 0xe8 -/* Out-of-bound UAHC register access: 0 = read, 1 = write */ -# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_WRN BIT(63) -/* SRCID error log for out-of-bound UAHC register access: - * [59:58] = chipID - * [57] = Request source: 0 = core, 1 = NCB-device - * [56:51] = Core/NCB-device number, [56] always 0 for NCB devices - * [50:48] = SubID - */ -# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_OSRC GENMASK(59, 48) -/* Error log for bad UAHC DMA access: 0 = Read log, 1 = Write log */ -# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_WRN BIT(47) -/* Encoded error type for bad UAHC DMA */ -# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_TYPE GENMASK(43, 40) -/* Select the IOI read command used by DMA accesses */ -# define USBDRD_UCTL_SHIM_CFG_DMA_READ_CMD BIT(12) -/* Select endian format for DMA accesses to the L2C: - * 0x0 = Little endian - * 0x1 = Big endian - * 0x2 = Reserved - * 0x3 = Reserved - */ -# define USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK(9, 8) -/* Select endian format for IOI CSR access to UAHC: - * 0x0 = Little endian - * 0x1 = Big endian - * 0x2 = Reserved - * 0x3 = Reserved - */ -# define USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK(1, 0) - -#define USBDRD_UCTL_ECC 0xf0 -#define USBDRD_UCTL_SPARE1 0xf8 - -static DEFINE_MUTEX(dwc3_octeon_clocks_mutex); - -#ifdef CONFIG_CAVIUM_OCTEON_SOC -#include -static inline uint64_t dwc3_octeon_readq(void __iomem *addr) -{ - return cvmx_readq_csr(addr); -} - -static inline void dwc3_octeon_writeq(void __iomem *base, uint64_t val) -{ - cvmx_writeq_csr(base, val); -} - -static void dwc3_octeon_config_gpio(int index, int gpio) -{ - union cvmx_gpio_bit_cfgx gpio_bit; - - if ((OCTEON_IS_MODEL(OCTEON_CN73XX) || - OCTEON_IS_MODEL(OCTEON_CNF75XX)) - && gpio <= 31) { - gpio_bit.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(gpio)); - gpio_bit.s.tx_oe = 1; - gpio_bit.s.output_sel = (index == 0 ? 0x14 : 0x15); - cvmx_write_csr(CVMX_GPIO_BIT_CFGX(gpio), gpio_bit.u64); - } else if (gpio <= 15) { - gpio_bit.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(gpio)); - gpio_bit.s.tx_oe = 1; - gpio_bit.s.output_sel = (index == 0 ? 0x14 : 0x19); - cvmx_write_csr(CVMX_GPIO_BIT_CFGX(gpio), gpio_bit.u64); - } else { - gpio_bit.u64 = cvmx_read_csr(CVMX_GPIO_XBIT_CFGX(gpio)); - gpio_bit.s.tx_oe = 1; - gpio_bit.s.output_sel = (index == 0 ? 0x14 : 0x19); - cvmx_write_csr(CVMX_GPIO_XBIT_CFGX(gpio), gpio_bit.u64); - } -} -#else -static inline uint64_t dwc3_octeon_readq(void __iomem *addr) -{ - return 0; -} - -static inline void dwc3_octeon_writeq(void __iomem *base, uint64_t val) { } - -static inline void dwc3_octeon_config_gpio(int index, int gpio) { } -#endif - -static int dwc3_octeon_get_divider(void) -{ - static const uint8_t clk_div[] = { 1, 2, 4, 6, 8, 16, 24, 32 }; - int div = 0; - - while (div < ARRAY_SIZE(clk_div)) { - uint64_t rate = octeon_get_io_clock_rate() / clk_div[div]; - if (rate <= 300000000 && rate >= 150000000) - break; - div++; - } - - return div; -} - -static int dwc3_octeon_config_power(struct device *dev, void __iomem *base) -{ - uint32_t gpio_pwr[3]; - int gpio, len, power_active_low; - struct device_node *node = dev->of_node; - u64 val; - void __iomem *uctl_host_cfg_reg = base + USBDRD_UCTL_HOST_CFG; - - if (of_find_property(node, "power", &len) != NULL) { - if (len == 12) { - of_property_read_u32_array(node, "power", gpio_pwr, 3); - power_active_low = gpio_pwr[2] & 0x01; - gpio = gpio_pwr[1]; - } else if (len == 8) { - of_property_read_u32_array(node, "power", gpio_pwr, 2); - power_active_low = 0; - gpio = gpio_pwr[1]; - } else { - dev_err(dev, "invalid power configuration\n"); - return -EINVAL; - } - dwc3_octeon_config_gpio(((u64)base >> 24) & 1, gpio); - - /* Enable XHCI power control and set if active high or low. */ - val = dwc3_octeon_readq(uctl_host_cfg_reg); - val |= USBDRD_UCTL_HOST_PPC_EN; - if (power_active_low) - val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; - else - val |= USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; - dwc3_octeon_writeq(uctl_host_cfg_reg, val); - } else { - /* Disable XHCI power control and set if active high. */ - val = dwc3_octeon_readq(uctl_host_cfg_reg); - val &= ~USBDRD_UCTL_HOST_PPC_EN; - val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; - dwc3_octeon_writeq(uctl_host_cfg_reg, val); - dev_info(dev, "power control disabled\n"); - } - return 0; -} - -static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base) -{ - int i, div, mpll_mul, ref_clk_fsel, ref_clk_sel = 2; - u32 clock_rate; - u64 val; - void __iomem *uctl_ctl_reg = base + USBDRD_UCTL_CTL; - - if (dev->of_node) { - const char *ss_clock_type; - const char *hs_clock_type; - - i = of_property_read_u32(dev->of_node, - "refclk-frequency", &clock_rate); - if (i) { - dev_err(dev, "No UCTL \"refclk-frequency\"\n"); - return -EINVAL; - } - i = of_property_read_string(dev->of_node, - "refclk-type-ss", &ss_clock_type); - if (i) { - dev_err(dev, "No UCTL \"refclk-type-ss\"\n"); - return -EINVAL; - } - i = of_property_read_string(dev->of_node, - "refclk-type-hs", &hs_clock_type); - if (i) { - dev_err(dev, "No UCTL \"refclk-type-hs\"\n"); - return -EINVAL; - } - if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) { - if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0) - ref_clk_sel = 0; - else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) - ref_clk_sel = 2; - else - dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n", - hs_clock_type); - } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) { - if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) - ref_clk_sel = 1; - else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) - ref_clk_sel = 3; - else { - dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n", - hs_clock_type); - ref_clk_sel = 3; - } - } else - dev_warn(dev, "Invalid SS clock type %s, using dlmc_ref_clk0 instead\n", - ss_clock_type); - - if ((ref_clk_sel == 0 || ref_clk_sel == 1) && - (clock_rate != 100000000)) - dev_warn(dev, "Invalid UCTL clock rate of %u, using 100000000 instead\n", - clock_rate); - - } else { - dev_err(dev, "No USB UCTL device node\n"); - return -EINVAL; - } - - /* - * Step 1: Wait for all voltages to be stable...that surely - * happened before starting the kernel. SKIP - */ - - /* Step 2: Select GPIO for overcurrent indication, if desired. SKIP */ - - /* Step 3: Assert all resets. */ - val = dwc3_octeon_readq(uctl_ctl_reg); - val |= USBDRD_UCTL_CTL_UPHY_RST | - USBDRD_UCTL_CTL_UAHC_RST | - USBDRD_UCTL_CTL_UCTL_RST; - dwc3_octeon_writeq(uctl_ctl_reg, val); - - /* Step 4a: Reset the clock dividers. */ - val = dwc3_octeon_readq(uctl_ctl_reg); - val |= USBDRD_UCTL_CTL_H_CLKDIV_RST; - dwc3_octeon_writeq(uctl_ctl_reg, val); - - /* Step 4b: Select controller clock frequency. */ - div = dwc3_octeon_get_divider(); - val = dwc3_octeon_readq(uctl_ctl_reg); - val &= ~USBDRD_UCTL_CTL_H_CLKDIV_SEL; - val |= FIELD_PREP(USBDRD_UCTL_CTL_H_CLKDIV_SEL, div); - val |= USBDRD_UCTL_CTL_H_CLK_EN; - dwc3_octeon_writeq(uctl_ctl_reg, val); - val = dwc3_octeon_readq(uctl_ctl_reg); - if ((div != FIELD_GET(USBDRD_UCTL_CTL_H_CLKDIV_SEL, val)) || - (!(FIELD_GET(USBDRD_UCTL_CTL_H_CLK_EN, val)))) { - dev_err(dev, "dwc3 controller clock init failure.\n"); - return -EINVAL; - } - - /* Step 4c: Deassert the controller clock divider reset. */ - val &= ~USBDRD_UCTL_CTL_H_CLKDIV_RST; - dwc3_octeon_writeq(uctl_ctl_reg, val); - - /* Step 5a: Reference clock configuration. */ - val = dwc3_octeon_readq(uctl_ctl_reg); - val &= ~USBDRD_UCTL_CTL_REF_CLK_DIV2; - val &= ~USBDRD_UCTL_CTL_REF_CLK_SEL; - val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_SEL, ref_clk_sel); - - ref_clk_fsel = 0x07; - switch (clock_rate) { - default: - dev_warn(dev, "Invalid ref_clk %u, using 100000000 instead\n", - clock_rate); - fallthrough; - case 100000000: - mpll_mul = 0x19; - if (ref_clk_sel < 2) - ref_clk_fsel = 0x27; - break; - case 50000000: - mpll_mul = 0x32; - break; - case 125000000: - mpll_mul = 0x28; - break; - } - val &= ~USBDRD_UCTL_CTL_REF_CLK_FSEL; - val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_FSEL, ref_clk_fsel); - - val &= ~USBDRD_UCTL_CTL_MPLL_MULTIPLIER; - val |= FIELD_PREP(USBDRD_UCTL_CTL_MPLL_MULTIPLIER, mpll_mul); - - /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */ - val |= USBDRD_UCTL_CTL_SSC_EN; - - /* Step 5c: Enable SuperSpeed. */ - val |= USBDRD_UCTL_CTL_REF_SSP_EN; - - /* Step 5d: Configure PHYs. SKIP */ - - /* Step 6a & 6b: Power up PHYs. */ - val |= USBDRD_UCTL_CTL_HS_POWER_EN; - val |= USBDRD_UCTL_CTL_SS_POWER_EN; - dwc3_octeon_writeq(uctl_ctl_reg, val); - - /* Step 7: Wait 10 controller-clock cycles to take effect. */ - udelay(10); - - /* Step 8a: Deassert UCTL reset signal. */ - val = dwc3_octeon_readq(uctl_ctl_reg); - val &= ~USBDRD_UCTL_CTL_UCTL_RST; - dwc3_octeon_writeq(uctl_ctl_reg, val); - - /* Step 8b: Wait 10 controller-clock cycles. */ - udelay(10); - - /* Steo 8c: Setup power-power control. */ - if (dwc3_octeon_config_power(dev, base)) - return -EINVAL; - - /* Step 8d: Deassert UAHC reset signal. */ - val = dwc3_octeon_readq(uctl_ctl_reg); - val &= ~USBDRD_UCTL_CTL_UAHC_RST; - dwc3_octeon_writeq(uctl_ctl_reg, val); - - /* Step 8e: Wait 10 controller-clock cycles. */ - udelay(10); - - /* Step 9: Enable conditional coprocessor clock of UCTL. */ - val = dwc3_octeon_readq(uctl_ctl_reg); - val |= USBDRD_UCTL_CTL_CSCLK_EN; - dwc3_octeon_writeq(uctl_ctl_reg, val); - - /*Step 10: Set for host mode only. */ - val = dwc3_octeon_readq(uctl_ctl_reg); - val &= ~USBDRD_UCTL_CTL_DRD_MODE; - dwc3_octeon_writeq(uctl_ctl_reg, val); - - return 0; -} - -static void __init dwc3_octeon_set_endian_mode(void __iomem *base) -{ - u64 val; - void __iomem *uctl_shim_cfg_reg = base + USBDRD_UCTL_SHIM_CFG; - - val = dwc3_octeon_readq(uctl_shim_cfg_reg); - val &= ~USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE; - val &= ~USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE; -#ifdef __BIG_ENDIAN - val |= FIELD_PREP(USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE, 1); - val |= FIELD_PREP(USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE, 1); -#endif - dwc3_octeon_writeq(uctl_shim_cfg_reg, val); -} - -static void __init dwc3_octeon_phy_reset(void __iomem *base) -{ - u64 val; - void __iomem *uctl_ctl_reg = base + USBDRD_UCTL_CTL; - - val = dwc3_octeon_readq(uctl_ctl_reg); - val &= ~USBDRD_UCTL_CTL_UPHY_RST; - dwc3_octeon_writeq(uctl_ctl_reg, val); -} - -static int __init dwc3_octeon_device_init(void) -{ - const char compat_node_name[] = "cavium,octeon-7130-usb-uctl"; - struct platform_device *pdev; - struct device_node *node; - struct resource *res; - void __iomem *base; - - /* - * There should only be three universal controllers, "uctl" - * in the device tree. Two USB and a SATA, which we ignore. - */ - node = NULL; - do { - node = of_find_node_by_name(node, "uctl"); - if (!node) - return -ENODEV; - - if (of_device_is_compatible(node, compat_node_name)) { - pdev = of_find_device_by_node(node); - if (!pdev) - return -ENODEV; - - /* - * The code below maps in the registers necessary for - * setting up the clocks and reseting PHYs. We must - * release the resources so the dwc3 subsystem doesn't - * know the difference. - */ - base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(base)) { - put_device(&pdev->dev); - return PTR_ERR(base); - } - - mutex_lock(&dwc3_octeon_clocks_mutex); - if (dwc3_octeon_clocks_start(&pdev->dev, base) == 0) - dev_info(&pdev->dev, "clocks initialized.\n"); - dwc3_octeon_set_endian_mode(base); - dwc3_octeon_phy_reset(base); - mutex_unlock(&dwc3_octeon_clocks_mutex); - devm_iounmap(&pdev->dev, base); - devm_release_mem_region(&pdev->dev, res->start, - resource_size(res)); - put_device(&pdev->dev); - } - } while (node != NULL); - - return 0; -} -device_initcall(dwc3_octeon_device_init); - -MODULE_AUTHOR("David Daney "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("USB driver for OCTEON III SoC"); diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index be954a9abbe0..98efcbb76c88 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -168,4 +168,14 @@ config USB_DWC3_AM62 The Designware Core USB3 IP is programmed to operate in in USB 2.0 mode only. Say 'Y' or 'M' here if you have one such device + +config USB_DWC3_OCTEON + tristate "Cavium Octeon Platforms" + depends on CAVIUM_OCTEON_SOC || COMPILE_TEST + default USB_DWC3 + help + Support Cavium Octeon platforms with DesignWare Core USB3 IP. + Only the host mode is currently supported. + Say 'Y' or 'M' here if you have one such device. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 9f66bd82b639..fe1493d4bbe5 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -54,3 +54,4 @@ obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o +obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c new file mode 100644 index 000000000000..7134cdfc0fb6 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -0,0 +1,560 @@ +/* + * XHCI HCD glue for Cavium Octeon III SOCs. + * + * Copyright (C) 2010-2017 Cavium Networks + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * USB Control Register + */ +#define USBDRD_UCTL_CTL 0x00 +/* BIST fast-clear mode select. A BIST run with this bit set + * clears all entries in USBH RAMs to 0x0. + */ +# define USBDRD_UCTL_CTL_CLEAR_BIST BIT(63) +/* 1 = Start BIST and cleared by hardware */ +# define USBDRD_UCTL_CTL_START_BIST BIT(62) +/* Reference clock select for SuperSpeed and HighSpeed PLLs: + * 0x0 = Both PLLs use DLMC_REF_CLK0 for reference clock + * 0x1 = Both PLLs use DLMC_REF_CLK1 for reference clock + * 0x2 = SuperSpeed PLL uses DLMC_REF_CLK0 for reference clock & + * HighSpeed PLL uses PLL_REF_CLK for reference clck + * 0x3 = SuperSpeed PLL uses DLMC_REF_CLK1 for reference clock & + * HighSpeed PLL uses PLL_REF_CLK for reference clck + */ +# define USBDRD_UCTL_CTL_REF_CLK_SEL GENMASK(61, 60) +/* 1 = Spread-spectrum clock enable, 0 = SS clock disable */ +# define USBDRD_UCTL_CTL_SSC_EN BIT(59) +/* Spread-spectrum clock modulation range: + * 0x0 = -4980 ppm downspread + * 0x1 = -4492 ppm downspread + * 0x2 = -4003 ppm downspread + * 0x3 - 0x7 = Reserved + */ +# define USBDRD_UCTL_CTL_SSC_RANGE GENMASK(58, 56) +/* Enable non-standard oscillator frequencies: + * [55:53] = modules -1 + * [52:47] = 2's complement push amount, 0 = Feature disabled + */ +# define USBDRD_UCTL_CTL_SSC_REF_CLK_SEL GENMASK(55, 47) +/* Reference clock multiplier for non-standard frequencies: + * 0x19 = 100MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1 + * 0x28 = 125MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1 + * 0x32 = 50MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1 + * Other Values = Reserved + */ +# define USBDRD_UCTL_CTL_MPLL_MULTIPLIER GENMASK(46, 40) +/* Enable reference clock to prescaler for SuperSpeed functionality. + * Should always be set to "1" + */ +# define USBDRD_UCTL_CTL_REF_SSP_EN BIT(39) +/* Divide the reference clock by 2 before entering the + * REF_CLK_FSEL divider: + * If REF_CLK_SEL = 0x0 or 0x1, then only 0x0 is legal + * If REF_CLK_SEL = 0x2 or 0x3, then: + * 0x1 = DLMC_REF_CLK* is 125MHz + * 0x0 = DLMC_REF_CLK* is another supported frequency + */ +# define USBDRD_UCTL_CTL_REF_CLK_DIV2 BIT(38) +/* Select reference clock freqnuency for both PLL blocks: + * 0x27 = REF_CLK_SEL is 0x0 or 0x1 + * 0x07 = REF_CLK_SEL is 0x2 or 0x3 + */ +# define USBDRD_UCTL_CTL_REF_CLK_FSEL GENMASK(37, 32) +/* Controller clock enable. */ +# define USBDRD_UCTL_CTL_H_CLK_EN BIT(30) +/* Select bypass input to controller clock divider: + * 0x0 = Use divided coprocessor clock from H_CLKDIV + * 0x1 = Use clock from GPIO pins + */ +# define USBDRD_UCTL_CTL_H_CLK_BYP_SEL BIT(29) +/* Reset controller clock divider. */ +# define USBDRD_UCTL_CTL_H_CLKDIV_RST BIT(28) +/* Clock divider select: + * 0x0 = divide by 1 + * 0x1 = divide by 2 + * 0x2 = divide by 4 + * 0x3 = divide by 6 + * 0x4 = divide by 8 + * 0x5 = divide by 16 + * 0x6 = divide by 24 + * 0x7 = divide by 32 + */ +# define USBDRD_UCTL_CTL_H_CLKDIV_SEL GENMASK(26, 24) +/* USB3 port permanently attached: 0x0 = No, 0x1 = Yes */ +# define USBDRD_UCTL_CTL_USB3_PORT_PERM_ATTACH BIT(21) +/* USB2 port permanently attached: 0x0 = No, 0x1 = Yes */ +# define USBDRD_UCTL_CTL_USB2_PORT_PERM_ATTACH BIT(20) +/* Disable SuperSpeed PHY: 0x0 = No, 0x1 = Yes */ +# define USBDRD_UCTL_CTL_USB3_PORT_DISABLE BIT(18) +/* Disable HighSpeed PHY: 0x0 = No, 0x1 = Yes */ +# define USBDRD_UCTL_CTL_USB2_PORT_DISABLE BIT(16) +/* Enable PHY SuperSpeed block power: 0x0 = No, 0x1 = Yes */ +# define USBDRD_UCTL_CTL_SS_POWER_EN BIT(14) +/* Enable PHY HighSpeed block power: 0x0 = No, 0x1 = Yes */ +# define USBDRD_UCTL_CTL_HS_POWER_EN BIT(12) +/* Enable USB UCTL interface clock: 0xx = No, 0x1 = Yes */ +# define USBDRD_UCTL_CTL_CSCLK_EN BIT(4) +/* Controller mode: 0x0 = Host, 0x1 = Device */ +# define USBDRD_UCTL_CTL_DRD_MODE BIT(3) +/* PHY reset */ +# define USBDRD_UCTL_CTL_UPHY_RST BIT(2) +/* Software reset UAHC */ +# define USBDRD_UCTL_CTL_UAHC_RST BIT(1) +/* Software resets UCTL */ +# define USBDRD_UCTL_CTL_UCTL_RST BIT(0) + +#define USBDRD_UCTL_BIST_STATUS 0x08 +#define USBDRD_UCTL_SPARE0 0x10 +#define USBDRD_UCTL_INTSTAT 0x30 +#define USBDRD_UCTL_PORT_CFG_HS(port) (0x40 + (0x20 * port)) +#define USBDRD_UCTL_PORT_CFG_SS(port) (0x48 + (0x20 * port)) +#define USBDRD_UCTL_PORT_CR_DBG_CFG(port) (0x50 + (0x20 * port)) +#define USBDRD_UCTL_PORT_CR_DBG_STATUS(port) (0x58 + (0x20 * port)) + +/* + * UCTL Configuration Register + */ +#define USBDRD_UCTL_HOST_CFG 0xe0 +/* Indicates minimum value of all received BELT values */ +# define USBDRD_UCTL_HOST_CFG_HOST_CURRENT_BELT GENMASK(59, 48) +/* HS jitter adjustment */ +# define USBDRD_UCTL_HOST_CFG_FLA GENMASK(37, 32) +/* Bus-master enable: 0x0 = Disabled (stall DMAs), 0x1 = enabled */ +# define USBDRD_UCTL_HOST_CFG_BME BIT(28) +/* Overcurrent protection enable: 0x0 = unavailable, 0x1 = available */ +# define USBDRD_UCTL_HOST_OCI_EN BIT(27) +/* Overcurrent sene selection: + * 0x0 = Overcurrent indication from off-chip is active-low + * 0x1 = Overcurrent indication from off-chip is active-high + */ +# define USBDRD_UCTL_HOST_OCI_ACTIVE_HIGH_EN BIT(26) +/* Port power control enable: 0x0 = unavailable, 0x1 = available */ +# define USBDRD_UCTL_HOST_PPC_EN BIT(25) +/* Port power control sense selection: + * 0x0 = Port power to off-chip is active-low + * 0x1 = Port power to off-chip is active-high + */ +# define USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN BIT(24) + +/* + * UCTL Shim Features Register + */ +#define USBDRD_UCTL_SHIM_CFG 0xe8 +/* Out-of-bound UAHC register access: 0 = read, 1 = write */ +# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_WRN BIT(63) +/* SRCID error log for out-of-bound UAHC register access: + * [59:58] = chipID + * [57] = Request source: 0 = core, 1 = NCB-device + * [56:51] = Core/NCB-device number, [56] always 0 for NCB devices + * [50:48] = SubID + */ +# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_OSRC GENMASK(59, 48) +/* Error log for bad UAHC DMA access: 0 = Read log, 1 = Write log */ +# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_WRN BIT(47) +/* Encoded error type for bad UAHC DMA */ +# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_TYPE GENMASK(43, 40) +/* Select the IOI read command used by DMA accesses */ +# define USBDRD_UCTL_SHIM_CFG_DMA_READ_CMD BIT(12) +/* Select endian format for DMA accesses to the L2C: + * 0x0 = Little endian + * 0x1 = Big endian + * 0x2 = Reserved + * 0x3 = Reserved + */ +# define USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK(9, 8) +/* Select endian format for IOI CSR access to UAHC: + * 0x0 = Little endian + * 0x1 = Big endian + * 0x2 = Reserved + * 0x3 = Reserved + */ +# define USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK(1, 0) + +#define USBDRD_UCTL_ECC 0xf0 +#define USBDRD_UCTL_SPARE1 0xf8 + +struct dwc3_octeon { + struct device *dev; + void __iomem *base; +}; + +#ifdef CONFIG_CAVIUM_OCTEON_SOC +#include +static inline uint64_t dwc3_octeon_readq(void __iomem *addr) +{ + return cvmx_readq_csr(addr); +} + +static inline void dwc3_octeon_writeq(void __iomem *base, uint64_t val) +{ + cvmx_writeq_csr(base, val); +} + +static void dwc3_octeon_config_gpio(int index, int gpio) +{ + union cvmx_gpio_bit_cfgx gpio_bit; + + if ((OCTEON_IS_MODEL(OCTEON_CN73XX) || + OCTEON_IS_MODEL(OCTEON_CNF75XX)) + && gpio <= 31) { + gpio_bit.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(gpio)); + gpio_bit.s.tx_oe = 1; + gpio_bit.s.output_sel = (index == 0 ? 0x14 : 0x15); + cvmx_write_csr(CVMX_GPIO_BIT_CFGX(gpio), gpio_bit.u64); + } else if (gpio <= 15) { + gpio_bit.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(gpio)); + gpio_bit.s.tx_oe = 1; + gpio_bit.s.output_sel = (index == 0 ? 0x14 : 0x19); + cvmx_write_csr(CVMX_GPIO_BIT_CFGX(gpio), gpio_bit.u64); + } else { + gpio_bit.u64 = cvmx_read_csr(CVMX_GPIO_XBIT_CFGX(gpio)); + gpio_bit.s.tx_oe = 1; + gpio_bit.s.output_sel = (index == 0 ? 0x14 : 0x19); + cvmx_write_csr(CVMX_GPIO_XBIT_CFGX(gpio), gpio_bit.u64); + } +} +#else +static inline uint64_t dwc3_octeon_readq(void __iomem *addr) +{ + return 0; +} + +static inline void dwc3_octeon_writeq(void __iomem *base, uint64_t val) { } + +static inline void dwc3_octeon_config_gpio(int index, int gpio) { } + +static uint64_t octeon_get_io_clock_rate(void) +{ + return 150000000; +} +#endif + +static int dwc3_octeon_get_divider(void) +{ + static const uint8_t clk_div[] = { 1, 2, 4, 6, 8, 16, 24, 32 }; + int div = 0; + + while (div < ARRAY_SIZE(clk_div)) { + uint64_t rate = octeon_get_io_clock_rate() / clk_div[div]; + if (rate <= 300000000 && rate >= 150000000) + break; + div++; + } + + return div; +} + +static int dwc3_octeon_config_power(struct device *dev, void __iomem *base) +{ + uint32_t gpio_pwr[3]; + int gpio, len, power_active_low; + struct device_node *node = dev->of_node; + u64 val; + void __iomem *uctl_host_cfg_reg = base + USBDRD_UCTL_HOST_CFG; + + if (of_find_property(node, "power", &len) != NULL) { + if (len == 12) { + of_property_read_u32_array(node, "power", gpio_pwr, 3); + power_active_low = gpio_pwr[2] & 0x01; + gpio = gpio_pwr[1]; + } else if (len == 8) { + of_property_read_u32_array(node, "power", gpio_pwr, 2); + power_active_low = 0; + gpio = gpio_pwr[1]; + } else { + dev_err(dev, "invalid power configuration\n"); + return -EINVAL; + } + dwc3_octeon_config_gpio(((u64)base >> 24) & 1, gpio); + + /* Enable XHCI power control and set if active high or low. */ + val = dwc3_octeon_readq(uctl_host_cfg_reg); + val |= USBDRD_UCTL_HOST_PPC_EN; + if (power_active_low) + val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; + else + val |= USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; + dwc3_octeon_writeq(uctl_host_cfg_reg, val); + } else { + /* Disable XHCI power control and set if active high. */ + val = dwc3_octeon_readq(uctl_host_cfg_reg); + val &= ~USBDRD_UCTL_HOST_PPC_EN; + val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; + dwc3_octeon_writeq(uctl_host_cfg_reg, val); + dev_info(dev, "power control disabled\n"); + } + return 0; +} + +static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base) +{ + int i, div, mpll_mul, ref_clk_fsel, ref_clk_sel = 2; + u32 clock_rate; + u64 val; + void __iomem *uctl_ctl_reg = base + USBDRD_UCTL_CTL; + + if (dev->of_node) { + const char *ss_clock_type; + const char *hs_clock_type; + + i = of_property_read_u32(dev->of_node, + "refclk-frequency", &clock_rate); + if (i) { + dev_err(dev, "No UCTL \"refclk-frequency\"\n"); + return -EINVAL; + } + i = of_property_read_string(dev->of_node, + "refclk-type-ss", &ss_clock_type); + if (i) { + dev_err(dev, "No UCTL \"refclk-type-ss\"\n"); + return -EINVAL; + } + i = of_property_read_string(dev->of_node, + "refclk-type-hs", &hs_clock_type); + if (i) { + dev_err(dev, "No UCTL \"refclk-type-hs\"\n"); + return -EINVAL; + } + if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) { + if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0) + ref_clk_sel = 0; + else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) + ref_clk_sel = 2; + else + dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n", + hs_clock_type); + } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) { + if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) + ref_clk_sel = 1; + else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) + ref_clk_sel = 3; + else { + dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n", + hs_clock_type); + ref_clk_sel = 3; + } + } else + dev_warn(dev, "Invalid SS clock type %s, using dlmc_ref_clk0 instead\n", + ss_clock_type); + + if ((ref_clk_sel == 0 || ref_clk_sel == 1) && + (clock_rate != 100000000)) + dev_warn(dev, "Invalid UCTL clock rate of %u, using 100000000 instead\n", + clock_rate); + + } else { + dev_err(dev, "No USB UCTL device node\n"); + return -EINVAL; + } + + /* + * Step 1: Wait for all voltages to be stable...that surely + * happened before starting the kernel. SKIP + */ + + /* Step 2: Select GPIO for overcurrent indication, if desired. SKIP */ + + /* Step 3: Assert all resets. */ + val = dwc3_octeon_readq(uctl_ctl_reg); + val |= USBDRD_UCTL_CTL_UPHY_RST | + USBDRD_UCTL_CTL_UAHC_RST | + USBDRD_UCTL_CTL_UCTL_RST; + dwc3_octeon_writeq(uctl_ctl_reg, val); + + /* Step 4a: Reset the clock dividers. */ + val = dwc3_octeon_readq(uctl_ctl_reg); + val |= USBDRD_UCTL_CTL_H_CLKDIV_RST; + dwc3_octeon_writeq(uctl_ctl_reg, val); + + /* Step 4b: Select controller clock frequency. */ + div = dwc3_octeon_get_divider(); + val = dwc3_octeon_readq(uctl_ctl_reg); + val &= ~USBDRD_UCTL_CTL_H_CLKDIV_SEL; + val |= FIELD_PREP(USBDRD_UCTL_CTL_H_CLKDIV_SEL, div); + val |= USBDRD_UCTL_CTL_H_CLK_EN; + dwc3_octeon_writeq(uctl_ctl_reg, val); + val = dwc3_octeon_readq(uctl_ctl_reg); + if ((div != FIELD_GET(USBDRD_UCTL_CTL_H_CLKDIV_SEL, val)) || + (!(FIELD_GET(USBDRD_UCTL_CTL_H_CLK_EN, val)))) { + dev_err(dev, "dwc3 controller clock init failure.\n"); + return -EINVAL; + } + + /* Step 4c: Deassert the controller clock divider reset. */ + val &= ~USBDRD_UCTL_CTL_H_CLKDIV_RST; + dwc3_octeon_writeq(uctl_ctl_reg, val); + + /* Step 5a: Reference clock configuration. */ + val = dwc3_octeon_readq(uctl_ctl_reg); + val &= ~USBDRD_UCTL_CTL_REF_CLK_DIV2; + val &= ~USBDRD_UCTL_CTL_REF_CLK_SEL; + val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_SEL, ref_clk_sel); + + ref_clk_fsel = 0x07; + switch (clock_rate) { + default: + dev_warn(dev, "Invalid ref_clk %u, using 100000000 instead\n", + clock_rate); + fallthrough; + case 100000000: + mpll_mul = 0x19; + if (ref_clk_sel < 2) + ref_clk_fsel = 0x27; + break; + case 50000000: + mpll_mul = 0x32; + break; + case 125000000: + mpll_mul = 0x28; + break; + } + val &= ~USBDRD_UCTL_CTL_REF_CLK_FSEL; + val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_FSEL, ref_clk_fsel); + + val &= ~USBDRD_UCTL_CTL_MPLL_MULTIPLIER; + val |= FIELD_PREP(USBDRD_UCTL_CTL_MPLL_MULTIPLIER, mpll_mul); + + /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */ + val |= USBDRD_UCTL_CTL_SSC_EN; + + /* Step 5c: Enable SuperSpeed. */ + val |= USBDRD_UCTL_CTL_REF_SSP_EN; + + /* Step 5d: Configure PHYs. SKIP */ + + /* Step 6a & 6b: Power up PHYs. */ + val |= USBDRD_UCTL_CTL_HS_POWER_EN; + val |= USBDRD_UCTL_CTL_SS_POWER_EN; + dwc3_octeon_writeq(uctl_ctl_reg, val); + + /* Step 7: Wait 10 controller-clock cycles to take effect. */ + udelay(10); + + /* Step 8a: Deassert UCTL reset signal. */ + val = dwc3_octeon_readq(uctl_ctl_reg); + val &= ~USBDRD_UCTL_CTL_UCTL_RST; + dwc3_octeon_writeq(uctl_ctl_reg, val); + + /* Step 8b: Wait 10 controller-clock cycles. */ + udelay(10); + + /* Steo 8c: Setup power-power control. */ + if (dwc3_octeon_config_power(dev, base)) + return -EINVAL; + + /* Step 8d: Deassert UAHC reset signal. */ + val = dwc3_octeon_readq(uctl_ctl_reg); + val &= ~USBDRD_UCTL_CTL_UAHC_RST; + dwc3_octeon_writeq(uctl_ctl_reg, val); + + /* Step 8e: Wait 10 controller-clock cycles. */ + udelay(10); + + /* Step 9: Enable conditional coprocessor clock of UCTL. */ + val = dwc3_octeon_readq(uctl_ctl_reg); + val |= USBDRD_UCTL_CTL_CSCLK_EN; + dwc3_octeon_writeq(uctl_ctl_reg, val); + + /*Step 10: Set for host mode only. */ + val = dwc3_octeon_readq(uctl_ctl_reg); + val &= ~USBDRD_UCTL_CTL_DRD_MODE; + dwc3_octeon_writeq(uctl_ctl_reg, val); + + return 0; +} + +static void __init dwc3_octeon_set_endian_mode(void __iomem *base) +{ + u64 val; + void __iomem *uctl_shim_cfg_reg = base + USBDRD_UCTL_SHIM_CFG; + + val = dwc3_octeon_readq(uctl_shim_cfg_reg); + val &= ~USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE; + val &= ~USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE; +#ifdef __BIG_ENDIAN + val |= FIELD_PREP(USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE, 1); + val |= FIELD_PREP(USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE, 1); +#endif + dwc3_octeon_writeq(uctl_shim_cfg_reg, val); +} + +static void __init dwc3_octeon_phy_reset(void __iomem *base) +{ + u64 val; + void __iomem *uctl_ctl_reg = base + USBDRD_UCTL_CTL; + + val = dwc3_octeon_readq(uctl_ctl_reg); + val &= ~USBDRD_UCTL_CTL_UPHY_RST; + dwc3_octeon_writeq(uctl_ctl_reg, val); +} + +static int dwc3_octeon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct dwc3_octeon *octeon; + int err; + + octeon = devm_kzalloc(dev, sizeof(*octeon), GFP_KERNEL); + if (!octeon) + return -ENOMEM; + + octeon->dev = dev; + octeon->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(octeon->base)) + return PTR_ERR(octeon->base); + + err = dwc3_octeon_clocks_start(dev, octeon->base); + if (err) + return err; + + dwc3_octeon_set_endian_mode(octeon->base); + dwc3_octeon_phy_reset(octeon->base); + + platform_set_drvdata(pdev, octeon); + + return of_platform_populate(node, NULL, NULL, dev); +} + +static void dwc3_octeon_remove(struct platform_device *pdev) +{ + struct dwc3_octeon *octeon = platform_get_drvdata(pdev); + + of_platform_depopulate(octeon->dev); + platform_set_drvdata(pdev, NULL); +} + +static const struct of_device_id dwc3_octeon_of_match[] = { + { .compatible = "cavium,octeon-7130-usb-uctl" }, + { }, +}; +MODULE_DEVICE_TABLE(of, dwc3_octeon_of_match); + +static struct platform_driver dwc3_octeon_driver = { + .probe = dwc3_octeon_probe, + .remove_new = dwc3_octeon_remove, + .driver = { + .name = "dwc3-octeon", + .of_match_table = dwc3_octeon_of_match, + }, +}; +module_platform_driver(dwc3_octeon_driver); + +MODULE_ALIAS("platform:dwc3-octeon"); +MODULE_AUTHOR("David Daney "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare USB3 OCTEON III Glue Layer"); diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index 7e6ad8fe61a5..d1539fc9eabd 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -170,7 +170,6 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = { static const struct of_device_id of_dwc3_simple_match[] = { { .compatible = "rockchip,rk3399-dwc3" }, - { .compatible = "cavium,octeon-7130-usb-uctl" }, { .compatible = "sprd,sc9860-dwc3" }, { .compatible = "allwinner,sun50i-h6-dwc3" }, { .compatible = "hisilicon,hi3670-dwc3" }, -- cgit From 417840663ab858bafb3a0d6d86e73a5db506202a Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 31 Jul 2023 11:31:21 +0200 Subject: usb: dwc3: dwc3-octeon: Use _ULL bitfields defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While driver is intended to run on 64bit machines, it is compile time tested for 32bit targets as well. Here shift count overflow is reported for bits greater than 31, so use _ULL versions of BIT and GENMASK macros to silence these warnings. Signed-off-by: Ladislav Michl Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202307260537.MROrhVNM-lkp@intel.com/ Reviewed-by: Philippe Mathieu-Daudé Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/ZMd/aa2ncz6tJGNU@lenoch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-octeon.c | 78 +++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 7134cdfc0fb6..69fe50cfa719 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -24,9 +24,9 @@ /* BIST fast-clear mode select. A BIST run with this bit set * clears all entries in USBH RAMs to 0x0. */ -# define USBDRD_UCTL_CTL_CLEAR_BIST BIT(63) +# define USBDRD_UCTL_CTL_CLEAR_BIST BIT_ULL(63) /* 1 = Start BIST and cleared by hardware */ -# define USBDRD_UCTL_CTL_START_BIST BIT(62) +# define USBDRD_UCTL_CTL_START_BIST BIT_ULL(62) /* Reference clock select for SuperSpeed and HighSpeed PLLs: * 0x0 = Both PLLs use DLMC_REF_CLK0 for reference clock * 0x1 = Both PLLs use DLMC_REF_CLK1 for reference clock @@ -35,32 +35,32 @@ * 0x3 = SuperSpeed PLL uses DLMC_REF_CLK1 for reference clock & * HighSpeed PLL uses PLL_REF_CLK for reference clck */ -# define USBDRD_UCTL_CTL_REF_CLK_SEL GENMASK(61, 60) +# define USBDRD_UCTL_CTL_REF_CLK_SEL GENMASK_ULL(61, 60) /* 1 = Spread-spectrum clock enable, 0 = SS clock disable */ -# define USBDRD_UCTL_CTL_SSC_EN BIT(59) +# define USBDRD_UCTL_CTL_SSC_EN BIT_ULL(59) /* Spread-spectrum clock modulation range: * 0x0 = -4980 ppm downspread * 0x1 = -4492 ppm downspread * 0x2 = -4003 ppm downspread * 0x3 - 0x7 = Reserved */ -# define USBDRD_UCTL_CTL_SSC_RANGE GENMASK(58, 56) +# define USBDRD_UCTL_CTL_SSC_RANGE GENMASK_ULL(58, 56) /* Enable non-standard oscillator frequencies: * [55:53] = modules -1 * [52:47] = 2's complement push amount, 0 = Feature disabled */ -# define USBDRD_UCTL_CTL_SSC_REF_CLK_SEL GENMASK(55, 47) +# define USBDRD_UCTL_CTL_SSC_REF_CLK_SEL GENMASK_ULL(55, 47) /* Reference clock multiplier for non-standard frequencies: * 0x19 = 100MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1 * 0x28 = 125MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1 * 0x32 = 50MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1 * Other Values = Reserved */ -# define USBDRD_UCTL_CTL_MPLL_MULTIPLIER GENMASK(46, 40) +# define USBDRD_UCTL_CTL_MPLL_MULTIPLIER GENMASK_ULL(46, 40) /* Enable reference clock to prescaler for SuperSpeed functionality. * Should always be set to "1" */ -# define USBDRD_UCTL_CTL_REF_SSP_EN BIT(39) +# define USBDRD_UCTL_CTL_REF_SSP_EN BIT_ULL(39) /* Divide the reference clock by 2 before entering the * REF_CLK_FSEL divider: * If REF_CLK_SEL = 0x0 or 0x1, then only 0x0 is legal @@ -68,21 +68,21 @@ * 0x1 = DLMC_REF_CLK* is 125MHz * 0x0 = DLMC_REF_CLK* is another supported frequency */ -# define USBDRD_UCTL_CTL_REF_CLK_DIV2 BIT(38) +# define USBDRD_UCTL_CTL_REF_CLK_DIV2 BIT_ULL(38) /* Select reference clock freqnuency for both PLL blocks: * 0x27 = REF_CLK_SEL is 0x0 or 0x1 * 0x07 = REF_CLK_SEL is 0x2 or 0x3 */ -# define USBDRD_UCTL_CTL_REF_CLK_FSEL GENMASK(37, 32) +# define USBDRD_UCTL_CTL_REF_CLK_FSEL GENMASK_ULL(37, 32) /* Controller clock enable. */ -# define USBDRD_UCTL_CTL_H_CLK_EN BIT(30) +# define USBDRD_UCTL_CTL_H_CLK_EN BIT_ULL(30) /* Select bypass input to controller clock divider: * 0x0 = Use divided coprocessor clock from H_CLKDIV * 0x1 = Use clock from GPIO pins */ -# define USBDRD_UCTL_CTL_H_CLK_BYP_SEL BIT(29) +# define USBDRD_UCTL_CTL_H_CLK_BYP_SEL BIT_ULL(29) /* Reset controller clock divider. */ -# define USBDRD_UCTL_CTL_H_CLKDIV_RST BIT(28) +# define USBDRD_UCTL_CTL_H_CLKDIV_RST BIT_ULL(28) /* Clock divider select: * 0x0 = divide by 1 * 0x1 = divide by 2 @@ -93,29 +93,29 @@ * 0x6 = divide by 24 * 0x7 = divide by 32 */ -# define USBDRD_UCTL_CTL_H_CLKDIV_SEL GENMASK(26, 24) +# define USBDRD_UCTL_CTL_H_CLKDIV_SEL GENMASK_ULL(26, 24) /* USB3 port permanently attached: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_USB3_PORT_PERM_ATTACH BIT(21) +# define USBDRD_UCTL_CTL_USB3_PORT_PERM_ATTACH BIT_ULL(21) /* USB2 port permanently attached: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_USB2_PORT_PERM_ATTACH BIT(20) +# define USBDRD_UCTL_CTL_USB2_PORT_PERM_ATTACH BIT_ULL(20) /* Disable SuperSpeed PHY: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_USB3_PORT_DISABLE BIT(18) +# define USBDRD_UCTL_CTL_USB3_PORT_DISABLE BIT_ULL(18) /* Disable HighSpeed PHY: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_USB2_PORT_DISABLE BIT(16) +# define USBDRD_UCTL_CTL_USB2_PORT_DISABLE BIT_ULL(16) /* Enable PHY SuperSpeed block power: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_SS_POWER_EN BIT(14) +# define USBDRD_UCTL_CTL_SS_POWER_EN BIT_ULL(14) /* Enable PHY HighSpeed block power: 0x0 = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_HS_POWER_EN BIT(12) +# define USBDRD_UCTL_CTL_HS_POWER_EN BIT_ULL(12) /* Enable USB UCTL interface clock: 0xx = No, 0x1 = Yes */ -# define USBDRD_UCTL_CTL_CSCLK_EN BIT(4) +# define USBDRD_UCTL_CTL_CSCLK_EN BIT_ULL(4) /* Controller mode: 0x0 = Host, 0x1 = Device */ -# define USBDRD_UCTL_CTL_DRD_MODE BIT(3) +# define USBDRD_UCTL_CTL_DRD_MODE BIT_ULL(3) /* PHY reset */ -# define USBDRD_UCTL_CTL_UPHY_RST BIT(2) +# define USBDRD_UCTL_CTL_UPHY_RST BIT_ULL(2) /* Software reset UAHC */ -# define USBDRD_UCTL_CTL_UAHC_RST BIT(1) +# define USBDRD_UCTL_CTL_UAHC_RST BIT_ULL(1) /* Software resets UCTL */ -# define USBDRD_UCTL_CTL_UCTL_RST BIT(0) +# define USBDRD_UCTL_CTL_UCTL_RST BIT_ULL(0) #define USBDRD_UCTL_BIST_STATUS 0x08 #define USBDRD_UCTL_SPARE0 0x10 @@ -130,59 +130,59 @@ */ #define USBDRD_UCTL_HOST_CFG 0xe0 /* Indicates minimum value of all received BELT values */ -# define USBDRD_UCTL_HOST_CFG_HOST_CURRENT_BELT GENMASK(59, 48) +# define USBDRD_UCTL_HOST_CFG_HOST_CURRENT_BELT GENMASK_ULL(59, 48) /* HS jitter adjustment */ -# define USBDRD_UCTL_HOST_CFG_FLA GENMASK(37, 32) +# define USBDRD_UCTL_HOST_CFG_FLA GENMASK_ULL(37, 32) /* Bus-master enable: 0x0 = Disabled (stall DMAs), 0x1 = enabled */ -# define USBDRD_UCTL_HOST_CFG_BME BIT(28) +# define USBDRD_UCTL_HOST_CFG_BME BIT_ULL(28) /* Overcurrent protection enable: 0x0 = unavailable, 0x1 = available */ -# define USBDRD_UCTL_HOST_OCI_EN BIT(27) +# define USBDRD_UCTL_HOST_OCI_EN BIT_ULL(27) /* Overcurrent sene selection: * 0x0 = Overcurrent indication from off-chip is active-low * 0x1 = Overcurrent indication from off-chip is active-high */ -# define USBDRD_UCTL_HOST_OCI_ACTIVE_HIGH_EN BIT(26) +# define USBDRD_UCTL_HOST_OCI_ACTIVE_HIGH_EN BIT_ULL(26) /* Port power control enable: 0x0 = unavailable, 0x1 = available */ -# define USBDRD_UCTL_HOST_PPC_EN BIT(25) +# define USBDRD_UCTL_HOST_PPC_EN BIT_ULL(25) /* Port power control sense selection: * 0x0 = Port power to off-chip is active-low * 0x1 = Port power to off-chip is active-high */ -# define USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN BIT(24) +# define USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN BIT_ULL(24) /* * UCTL Shim Features Register */ #define USBDRD_UCTL_SHIM_CFG 0xe8 /* Out-of-bound UAHC register access: 0 = read, 1 = write */ -# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_WRN BIT(63) +# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_WRN BIT_ULL(63) /* SRCID error log for out-of-bound UAHC register access: * [59:58] = chipID * [57] = Request source: 0 = core, 1 = NCB-device * [56:51] = Core/NCB-device number, [56] always 0 for NCB devices * [50:48] = SubID */ -# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_OSRC GENMASK(59, 48) +# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_OSRC GENMASK_ULL(59, 48) /* Error log for bad UAHC DMA access: 0 = Read log, 1 = Write log */ -# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_WRN BIT(47) +# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_WRN BIT_ULL(47) /* Encoded error type for bad UAHC DMA */ -# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_TYPE GENMASK(43, 40) +# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_TYPE GENMASK_ULL(43, 40) /* Select the IOI read command used by DMA accesses */ -# define USBDRD_UCTL_SHIM_CFG_DMA_READ_CMD BIT(12) +# define USBDRD_UCTL_SHIM_CFG_DMA_READ_CMD BIT_ULL(12) /* Select endian format for DMA accesses to the L2C: * 0x0 = Little endian * 0x1 = Big endian * 0x2 = Reserved * 0x3 = Reserved */ -# define USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK(9, 8) +# define USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK_ULL(9, 8) /* Select endian format for IOI CSR access to UAHC: * 0x0 = Little endian * 0x1 = Big endian * 0x2 = Reserved * 0x3 = Reserved */ -# define USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK(1, 0) +# define USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK_ULL(1, 0) #define USBDRD_UCTL_ECC 0xf0 #define USBDRD_UCTL_SPARE1 0xf8 -- cgit From 23f87bcac649e91dce5a8bb8bd2908636117d6bd Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 31 Jul 2023 11:31:46 +0200 Subject: usb: dwc3: dwc3-octeon: Pass dwc3_octeon to setup functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass dwc3_octeon instead of just the base. It fits with the function names and it requires less change in the future if access to dwc3_octeon is needed. Signed-off-by: Ladislav Michl Reviewed-by: Philippe Mathieu-Daudé Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/ZMd/gt58laSlqAAT@lenoch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-octeon.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 69fe50cfa719..24e75881b5cf 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -300,12 +300,13 @@ static int dwc3_octeon_config_power(struct device *dev, void __iomem *base) return 0; } -static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base) +static int dwc3_octeon_clocks_start(struct dwc3_octeon *octeon) { int i, div, mpll_mul, ref_clk_fsel, ref_clk_sel = 2; u32 clock_rate; u64 val; - void __iomem *uctl_ctl_reg = base + USBDRD_UCTL_CTL; + struct device *dev = octeon->dev; + void __iomem *uctl_ctl_reg = octeon->base + USBDRD_UCTL_CTL; if (dev->of_node) { const char *ss_clock_type; @@ -452,8 +453,8 @@ static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base) /* Step 8b: Wait 10 controller-clock cycles. */ udelay(10); - /* Steo 8c: Setup power-power control. */ - if (dwc3_octeon_config_power(dev, base)) + /* Step 8c: Setup power control. */ + if (dwc3_octeon_config_power(dev, octeon->base)) return -EINVAL; /* Step 8d: Deassert UAHC reset signal. */ @@ -477,10 +478,10 @@ static int dwc3_octeon_clocks_start(struct device *dev, void __iomem *base) return 0; } -static void __init dwc3_octeon_set_endian_mode(void __iomem *base) +static void dwc3_octeon_set_endian_mode(struct dwc3_octeon *octeon) { u64 val; - void __iomem *uctl_shim_cfg_reg = base + USBDRD_UCTL_SHIM_CFG; + void __iomem *uctl_shim_cfg_reg = octeon->base + USBDRD_UCTL_SHIM_CFG; val = dwc3_octeon_readq(uctl_shim_cfg_reg); val &= ~USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE; @@ -492,10 +493,10 @@ static void __init dwc3_octeon_set_endian_mode(void __iomem *base) dwc3_octeon_writeq(uctl_shim_cfg_reg, val); } -static void __init dwc3_octeon_phy_reset(void __iomem *base) +static void dwc3_octeon_phy_reset(struct dwc3_octeon *octeon) { u64 val; - void __iomem *uctl_ctl_reg = base + USBDRD_UCTL_CTL; + void __iomem *uctl_ctl_reg = octeon->base + USBDRD_UCTL_CTL; val = dwc3_octeon_readq(uctl_ctl_reg); val &= ~USBDRD_UCTL_CTL_UPHY_RST; @@ -518,12 +519,12 @@ static int dwc3_octeon_probe(struct platform_device *pdev) if (IS_ERR(octeon->base)) return PTR_ERR(octeon->base); - err = dwc3_octeon_clocks_start(dev, octeon->base); + err = dwc3_octeon_clocks_start(octeon); if (err) return err; - dwc3_octeon_set_endian_mode(octeon->base); - dwc3_octeon_phy_reset(octeon->base); + dwc3_octeon_set_endian_mode(octeon); + dwc3_octeon_phy_reset(octeon); platform_set_drvdata(pdev, octeon); -- cgit From c61101631cdc0a98840a8e4f5a1e571ca94d82fc Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 31 Jul 2023 11:32:16 +0200 Subject: usb: dwc3: dwc3-octeon: Avoid half-initialized controller state Power gpio configuration is done from the middle of dwc3_octeon_clocks_start leaving hardware in half-initialized state if it fails. As that indicates dwc3_octeon_clocks_start does more than just initialize the clocks rename it appropriately and verify power gpio configuration in advance at the beginning of device probe. Signed-off-by: Ladislav Michl Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/ZMd/oMRx8ze22/kK@lenoch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-octeon.c | 90 ++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 24e75881b5cf..0dc45dda134c 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -192,6 +192,8 @@ struct dwc3_octeon { void __iomem *base; }; +#define DWC3_GPIO_POWER_NONE (-1) + #ifdef CONFIG_CAVIUM_OCTEON_SOC #include static inline uint64_t dwc3_octeon_readq(void __iomem *addr) @@ -258,55 +260,15 @@ static int dwc3_octeon_get_divider(void) return div; } -static int dwc3_octeon_config_power(struct device *dev, void __iomem *base) -{ - uint32_t gpio_pwr[3]; - int gpio, len, power_active_low; - struct device_node *node = dev->of_node; - u64 val; - void __iomem *uctl_host_cfg_reg = base + USBDRD_UCTL_HOST_CFG; - - if (of_find_property(node, "power", &len) != NULL) { - if (len == 12) { - of_property_read_u32_array(node, "power", gpio_pwr, 3); - power_active_low = gpio_pwr[2] & 0x01; - gpio = gpio_pwr[1]; - } else if (len == 8) { - of_property_read_u32_array(node, "power", gpio_pwr, 2); - power_active_low = 0; - gpio = gpio_pwr[1]; - } else { - dev_err(dev, "invalid power configuration\n"); - return -EINVAL; - } - dwc3_octeon_config_gpio(((u64)base >> 24) & 1, gpio); - - /* Enable XHCI power control and set if active high or low. */ - val = dwc3_octeon_readq(uctl_host_cfg_reg); - val |= USBDRD_UCTL_HOST_PPC_EN; - if (power_active_low) - val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; - else - val |= USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; - dwc3_octeon_writeq(uctl_host_cfg_reg, val); - } else { - /* Disable XHCI power control and set if active high. */ - val = dwc3_octeon_readq(uctl_host_cfg_reg); - val &= ~USBDRD_UCTL_HOST_PPC_EN; - val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; - dwc3_octeon_writeq(uctl_host_cfg_reg, val); - dev_info(dev, "power control disabled\n"); - } - return 0; -} - -static int dwc3_octeon_clocks_start(struct dwc3_octeon *octeon) +static int dwc3_octeon_setup(struct dwc3_octeon *octeon, + int power_gpio, int power_active_low) { int i, div, mpll_mul, ref_clk_fsel, ref_clk_sel = 2; u32 clock_rate; u64 val; struct device *dev = octeon->dev; void __iomem *uctl_ctl_reg = octeon->base + USBDRD_UCTL_CTL; + void __iomem *uctl_host_cfg_reg = octeon->base + USBDRD_UCTL_HOST_CFG; if (dev->of_node) { const char *ss_clock_type; @@ -454,8 +416,21 @@ static int dwc3_octeon_clocks_start(struct dwc3_octeon *octeon) udelay(10); /* Step 8c: Setup power control. */ - if (dwc3_octeon_config_power(dev, octeon->base)) - return -EINVAL; + val = dwc3_octeon_readq(uctl_host_cfg_reg); + val |= USBDRD_UCTL_HOST_PPC_EN; + if (power_gpio == DWC3_GPIO_POWER_NONE) { + val &= ~USBDRD_UCTL_HOST_PPC_EN; + } else { + val |= USBDRD_UCTL_HOST_PPC_EN; + dwc3_octeon_config_gpio(((__force uintptr_t)octeon->base >> 24) & 1, + power_gpio); + dev_dbg(dev, "power control is using gpio%d\n", power_gpio); + } + if (power_active_low) + val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; + else + val |= USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN; + dwc3_octeon_writeq(uctl_host_cfg_reg, val); /* Step 8d: Deassert UAHC reset signal. */ val = dwc3_octeon_readq(uctl_ctl_reg); @@ -508,7 +483,28 @@ static int dwc3_octeon_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct dwc3_octeon *octeon; - int err; + int power_active_low, power_gpio; + int err, len; + + power_gpio = DWC3_GPIO_POWER_NONE; + power_active_low = 0; + if (of_find_property(node, "power", &len)) { + u32 gpio_pwr[3]; + + switch (len) { + case 8: + of_property_read_u32_array(node, "power", gpio_pwr, 2); + break; + case 12: + of_property_read_u32_array(node, "power", gpio_pwr, 3); + power_active_low = gpio_pwr[2] & 0x01; + break; + default: + dev_err(dev, "invalid power configuration\n"); + return -EINVAL; + } + power_gpio = gpio_pwr[1]; + } octeon = devm_kzalloc(dev, sizeof(*octeon), GFP_KERNEL); if (!octeon) @@ -519,7 +515,7 @@ static int dwc3_octeon_probe(struct platform_device *pdev) if (IS_ERR(octeon->base)) return PTR_ERR(octeon->base); - err = dwc3_octeon_clocks_start(octeon); + err = dwc3_octeon_setup(octeon, power_gpio, power_active_low); if (err) return err; -- cgit From dc0092ce24dc1655a1b06edb37cddc0946bb40f9 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 31 Jul 2023 11:32:55 +0200 Subject: usb: dwc3: dwc3-octeon: Move node parsing into driver probe Parse and verify device tree node first, then setup UCTL bridge using verified values. This avoids needless allocations in case DT misconfiguration was found in the middle of setup. Signed-off-by: Ladislav Michl Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/ZMd/x3MHA4/QowMO@lenoch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-octeon.c | 135 ++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 75 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 0dc45dda134c..330bcb59cc95 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -261,69 +261,15 @@ static int dwc3_octeon_get_divider(void) } static int dwc3_octeon_setup(struct dwc3_octeon *octeon, + int ref_clk_sel, int ref_clk_fsel, int mpll_mul, int power_gpio, int power_active_low) { - int i, div, mpll_mul, ref_clk_fsel, ref_clk_sel = 2; - u32 clock_rate; u64 val; + int div; struct device *dev = octeon->dev; void __iomem *uctl_ctl_reg = octeon->base + USBDRD_UCTL_CTL; void __iomem *uctl_host_cfg_reg = octeon->base + USBDRD_UCTL_HOST_CFG; - if (dev->of_node) { - const char *ss_clock_type; - const char *hs_clock_type; - - i = of_property_read_u32(dev->of_node, - "refclk-frequency", &clock_rate); - if (i) { - dev_err(dev, "No UCTL \"refclk-frequency\"\n"); - return -EINVAL; - } - i = of_property_read_string(dev->of_node, - "refclk-type-ss", &ss_clock_type); - if (i) { - dev_err(dev, "No UCTL \"refclk-type-ss\"\n"); - return -EINVAL; - } - i = of_property_read_string(dev->of_node, - "refclk-type-hs", &hs_clock_type); - if (i) { - dev_err(dev, "No UCTL \"refclk-type-hs\"\n"); - return -EINVAL; - } - if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) { - if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0) - ref_clk_sel = 0; - else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) - ref_clk_sel = 2; - else - dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n", - hs_clock_type); - } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) { - if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) - ref_clk_sel = 1; - else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) - ref_clk_sel = 3; - else { - dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n", - hs_clock_type); - ref_clk_sel = 3; - } - } else - dev_warn(dev, "Invalid SS clock type %s, using dlmc_ref_clk0 instead\n", - ss_clock_type); - - if ((ref_clk_sel == 0 || ref_clk_sel == 1) && - (clock_rate != 100000000)) - dev_warn(dev, "Invalid UCTL clock rate of %u, using 100000000 instead\n", - clock_rate); - - } else { - dev_err(dev, "No USB UCTL device node\n"); - return -EINVAL; - } - /* * Step 1: Wait for all voltages to be stable...that surely * happened before starting the kernel. SKIP @@ -367,24 +313,6 @@ static int dwc3_octeon_setup(struct dwc3_octeon *octeon, val &= ~USBDRD_UCTL_CTL_REF_CLK_SEL; val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_SEL, ref_clk_sel); - ref_clk_fsel = 0x07; - switch (clock_rate) { - default: - dev_warn(dev, "Invalid ref_clk %u, using 100000000 instead\n", - clock_rate); - fallthrough; - case 100000000: - mpll_mul = 0x19; - if (ref_clk_sel < 2) - ref_clk_fsel = 0x27; - break; - case 50000000: - mpll_mul = 0x32; - break; - case 125000000: - mpll_mul = 0x28; - break; - } val &= ~USBDRD_UCTL_CTL_REF_CLK_FSEL; val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_FSEL, ref_clk_fsel); @@ -483,8 +411,64 @@ static int dwc3_octeon_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct dwc3_octeon *octeon; + const char *hs_clock_type, *ss_clock_type; + int ref_clk_sel, ref_clk_fsel, mpll_mul; int power_active_low, power_gpio; int err, len; + u32 clock_rate; + + if (of_property_read_u32(node, "refclk-frequency", &clock_rate)) { + dev_err(dev, "No UCTL \"refclk-frequency\"\n"); + return -EINVAL; + } + if (of_property_read_string(node, "refclk-type-ss", &ss_clock_type)) { + dev_err(dev, "No UCTL \"refclk-type-ss\"\n"); + return -EINVAL; + } + if (of_property_read_string(node, "refclk-type-hs", &hs_clock_type)) { + dev_err(dev, "No UCTL \"refclk-type-hs\"\n"); + return -EINVAL; + } + + ref_clk_sel = 2; + if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) { + if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0) + ref_clk_sel = 0; + else if (strcmp(hs_clock_type, "pll_ref_clk")) + dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n", + hs_clock_type); + } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) { + if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) { + ref_clk_sel = 1; + } else { + ref_clk_sel = 3; + if (strcmp(hs_clock_type, "pll_ref_clk")) + dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n", + hs_clock_type); + } + } else { + dev_warn(dev, "Invalid SS clock type %s, using dlmc_ref_clk0 instead\n", + ss_clock_type); + } + + ref_clk_fsel = 0x07; + switch (clock_rate) { + default: + dev_warn(dev, "Invalid ref_clk %u, using 100000000 instead\n", + clock_rate); + fallthrough; + case 100000000: + mpll_mul = 0x19; + if (ref_clk_sel < 2) + ref_clk_fsel = 0x27; + break; + case 50000000: + mpll_mul = 0x32; + break; + case 125000000: + mpll_mul = 0x28; + break; + } power_gpio = DWC3_GPIO_POWER_NONE; power_active_low = 0; @@ -515,7 +499,8 @@ static int dwc3_octeon_probe(struct platform_device *pdev) if (IS_ERR(octeon->base)) return PTR_ERR(octeon->base); - err = dwc3_octeon_setup(octeon, power_gpio, power_active_low); + err = dwc3_octeon_setup(octeon, ref_clk_sel, ref_clk_fsel, mpll_mul, + power_gpio, power_active_low); if (err) return err; -- cgit From 540264746d83b645b32e6a67367489d8e2c536f9 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 31 Jul 2023 11:33:24 +0200 Subject: usb: dwc3: dwc3-octeon: Dump control register on clock init failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It might be interesting to know control register value in case clock fails to enable. Signed-off-by: Ladislav Michl Reviewed-by: Philippe Mathieu-Daudé Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/ZMd/5OX9szEMnhQH@lenoch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-octeon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 330bcb59cc95..d578110f7afb 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -299,8 +299,8 @@ static int dwc3_octeon_setup(struct dwc3_octeon *octeon, val = dwc3_octeon_readq(uctl_ctl_reg); if ((div != FIELD_GET(USBDRD_UCTL_CTL_H_CLKDIV_SEL, val)) || (!(FIELD_GET(USBDRD_UCTL_CTL_H_CLK_EN, val)))) { - dev_err(dev, "dwc3 controller clock init failure.\n"); - return -EINVAL; + dev_err(dev, "clock init failure (UCTL_CTL=%016llx)\n", val); + return -EINVAL; } /* Step 4c: Deassert the controller clock divider reset. */ -- cgit From d9216d3ef538c32a7e413f3401cd6b606b81a708 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 31 Jul 2023 11:33:53 +0200 Subject: usb: dwc3: dwc3-octeon: Add SPDX header and copyright MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assign copyright to indicate driver rewrite is done for RACOM s.r.o. As David no longer works for Marvell (Cavium), I'm to blame for breakage. Signed-off-by: Ladislav Michl Acked-by: Thinh Nguyen Acked-by: David Daney Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/ZMeAAYx6Z3hlQBNQ@lenoch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-octeon.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index d578110f7afb..6f47262a117a 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -1,11 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * XHCI HCD glue for Cavium Octeon III SOCs. + * DWC3 glue for Cavium Octeon III SOCs. * * Copyright (C) 2010-2017 Cavium Networks - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. + * Copyright (C) 2023 RACOM s.r.o. */ #include @@ -537,6 +535,6 @@ static struct platform_driver dwc3_octeon_driver = { module_platform_driver(dwc3_octeon_driver); MODULE_ALIAS("platform:dwc3-octeon"); -MODULE_AUTHOR("David Daney "); +MODULE_AUTHOR("Ladislav Michl "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("DesignWare USB3 OCTEON III Glue Layer"); -- cgit From 408e1d965a1d5ff37d08cf7c1017516418912931 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 27 Dec 2022 09:17:48 +0200 Subject: thunderbolt: Log a warning if device links are not found The software connection manager needs the device links in order to establish the tunnels before the native protocols so log a warning if they are not found. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/acpi.c | 18 +++++++++++++----- drivers/thunderbolt/tb.c | 24 +++++++++++++++++------- drivers/thunderbolt/tb.h | 4 ++-- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/drivers/thunderbolt/acpi.c b/drivers/thunderbolt/acpi.c index 38fefd0e5268..c9b6bb46111c 100644 --- a/drivers/thunderbolt/acpi.c +++ b/drivers/thunderbolt/acpi.c @@ -12,7 +12,7 @@ #include "tb.h" static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data, - void **return_value) + void **ret) { struct acpi_device *adev = acpi_fetch_acpi_dev(handle); struct fwnode_handle *fwnode; @@ -84,6 +84,7 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data, if (link) { dev_dbg(&nhi->pdev->dev, "created link from %s\n", dev_name(&pdev->dev)); + *(bool *)ret = true; } else { dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n", dev_name(&pdev->dev)); @@ -104,22 +105,29 @@ out_put: * Goes over ACPI namespace finding tunneled ports that reference to * @nhi ACPI node. For each reference a device link is added. The link * is automatically removed by the driver core. + * + * Returns %true if at least one link was created. */ -void tb_acpi_add_links(struct tb_nhi *nhi) +bool tb_acpi_add_links(struct tb_nhi *nhi) { acpi_status status; + bool ret = false; if (!has_acpi_companion(&nhi->pdev->dev)) - return; + return false; /* * Find all devices that have usb4-host-controller interface * property that references to this NHI. */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 32, - tb_acpi_add_link, NULL, nhi, NULL); - if (ACPI_FAILURE(status)) + tb_acpi_add_link, NULL, nhi, (void **)&ret); + if (ACPI_FAILURE(status)) { dev_warn(&nhi->pdev->dev, "failed to enumerate tunneled ports\n"); + return false; + } + + return ret; } /** diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 62b26b7998fd..ffbae134589b 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -2366,12 +2366,13 @@ static const struct tb_cm_ops tb_cm_ops = { * downstream ports and the NHI so that the device core will make sure * NHI is resumed first before the rest. */ -static void tb_apple_add_links(struct tb_nhi *nhi) +static bool tb_apple_add_links(struct tb_nhi *nhi) { struct pci_dev *upstream, *pdev; + bool ret; if (!x86_apple_machine) - return; + return false; switch (nhi->pdev->device) { case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE: @@ -2380,26 +2381,27 @@ static void tb_apple_add_links(struct tb_nhi *nhi) case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: break; default: - return; + return false; } upstream = pci_upstream_bridge(nhi->pdev); while (upstream) { if (!pci_is_pcie(upstream)) - return; + return false; if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM) break; upstream = pci_upstream_bridge(upstream); } if (!upstream) - return; + return false; /* * For each hotplug downstream port, create add device link * back to NHI so that PCIe tunnels can be re-established after * sleep. */ + ret = false; for_each_pci_bridge(pdev, upstream->subordinate) { const struct device_link *link; @@ -2415,11 +2417,14 @@ static void tb_apple_add_links(struct tb_nhi *nhi) if (link) { dev_dbg(&nhi->pdev->dev, "created link from %s\n", dev_name(&pdev->dev)); + ret = true; } else { dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n", dev_name(&pdev->dev)); } } + + return ret; } struct tb *tb_probe(struct tb_nhi *nhi) @@ -2446,8 +2451,13 @@ struct tb *tb_probe(struct tb_nhi *nhi) tb_dbg(tb, "using software connection manager\n"); - tb_apple_add_links(nhi); - tb_acpi_add_links(nhi); + /* + * Device links are needed to make sure we establish tunnels + * before the PCIe/USB stack is resumed so complain here if we + * found them missing. + */ + if (!tb_apple_add_links(nhi) && !tb_acpi_add_links(nhi)) + tb_warn(tb, "device links to tunneled native ports are missing!\n"); return tb; } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 57a9b272cb94..d2a55ad2fd3e 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1333,7 +1333,7 @@ static inline bool usb4_port_device_is_offline(const struct usb4_port *usb4) void tb_check_quirks(struct tb_switch *sw); #ifdef CONFIG_ACPI -void tb_acpi_add_links(struct tb_nhi *nhi); +bool tb_acpi_add_links(struct tb_nhi *nhi); bool tb_acpi_is_native(void); bool tb_acpi_may_tunnel_usb3(void); @@ -1346,7 +1346,7 @@ void tb_acpi_exit(void); int tb_acpi_power_on_retimers(struct tb_port *port); int tb_acpi_power_off_retimers(struct tb_port *port); #else -static inline void tb_acpi_add_links(struct tb_nhi *nhi) { } +static inline bool tb_acpi_add_links(struct tb_nhi *nhi) { return false; } static inline bool tb_acpi_is_native(void) { return true; } static inline bool tb_acpi_may_tunnel_usb3(void) { return true; } -- cgit From d589fd42cf3179ca40d87a54f3850b165cf12691 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 28 Dec 2022 09:45:35 +0200 Subject: thunderbolt: Check Intel vendor ID in tb_switch_get_generation() Only Intel made Thunderbolt 1-3 devices so to avoid possible confusion check for the Intel vendor ID before deciding the device generation. While there move the USB4 check to happen first. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 75 ++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 7ea63bb31714..43171cc1cc2d 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2188,46 +2188,47 @@ struct device_type tb_switch_type = { static int tb_switch_get_generation(struct tb_switch *sw) { - switch (sw->config.device_id) { - case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE: - case PCI_DEVICE_ID_INTEL_EAGLE_RIDGE: - case PCI_DEVICE_ID_INTEL_LIGHT_PEAK: - case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_2C: - case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C: - case PCI_DEVICE_ID_INTEL_PORT_RIDGE: - case PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_BRIDGE: - case PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_BRIDGE: - return 1; - - case PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE: - case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE: - case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE: - return 2; - - case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE: - case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE: - case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE: - case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: - case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: - case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE: - case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE: - case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE: - case PCI_DEVICE_ID_INTEL_ICL_NHI0: - case PCI_DEVICE_ID_INTEL_ICL_NHI1: - return 3; + if (tb_switch_is_usb4(sw)) + return 4; - default: - if (tb_switch_is_usb4(sw)) - return 4; + if (sw->config.vendor_id == PCI_VENDOR_ID_INTEL) { + switch (sw->config.device_id) { + case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE: + case PCI_DEVICE_ID_INTEL_EAGLE_RIDGE: + case PCI_DEVICE_ID_INTEL_LIGHT_PEAK: + case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_2C: + case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C: + case PCI_DEVICE_ID_INTEL_PORT_RIDGE: + case PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_BRIDGE: + case PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_BRIDGE: + return 1; - /* - * For unknown switches assume generation to be 1 to be - * on the safe side. - */ - tb_sw_warn(sw, "unsupported switch device id %#x\n", - sw->config.device_id); - return 1; + case PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE: + case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE: + case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE: + return 2; + + case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE: + case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE: + case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE: + case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: + case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: + case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE: + case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE: + case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE: + case PCI_DEVICE_ID_INTEL_ICL_NHI0: + case PCI_DEVICE_ID_INTEL_ICL_NHI1: + return 3; + } } + + /* + * For unknown switches assume generation to be 1 to be on the + * safe side. + */ + tb_sw_warn(sw, "unsupported switch device id %#x\n", + sw->config.device_id); + return 1; } static bool tb_switch_exceeds_max_depth(const struct tb_switch *sw, int depth) -- cgit From a3f6445842e581233fbd19baad661dbba6d1fd58 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 3 Aug 2023 12:36:09 +0300 Subject: Documentation/ABI: thunderbolt: Replace 01.org in contact The mailing list on 01.org does not work anymore and the whole site redirects to intel.com Open Source page so replace the 01.org address with my email address. Signed-off-by: Mika Westerberg --- Documentation/ABI/testing/sysfs-bus-thunderbolt | 38 ++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index 76ab3e1fe374..221b6c75ed93 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -1,7 +1,7 @@ What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl Date: Jun 2018 KernelVersion: 4.17 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: Holds a comma separated list of device unique_ids that are allowed to be connected automatically during system startup (e.g boot devices). The list always contains @@ -33,7 +33,7 @@ Description: This attribute tells whether the system supports What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection Date: Mar 2019 KernelVersion: 4.21 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This attribute tells whether the system uses IOMMU for DMA protection. Value of 1 means IOMMU is used 0 means it is not (DMA protection is solely based on Thunderbolt @@ -42,7 +42,7 @@ Description: This attribute tells whether the system uses IOMMU What: /sys/bus/thunderbolt/devices/.../domainX/security Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This attribute holds current Thunderbolt security level set by the system BIOS. Possible values are: @@ -64,7 +64,7 @@ Description: This attribute holds current Thunderbolt security level What: /sys/bus/thunderbolt/devices/.../authorized Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This attribute is used to authorize Thunderbolt devices after they have been connected. If the device is not authorized, no PCIe devices are available to the system. @@ -98,7 +98,7 @@ Description: This attribute is used to authorize Thunderbolt devices What: /sys/bus/thunderbolt/devices/.../boot Date: Jun 2018 KernelVersion: 4.17 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This attribute contains 1 if Thunderbolt device was already authorized on boot and 0 otherwise. @@ -113,7 +113,7 @@ Description: This attribute contains the generation of the Thunderbolt What: /sys/bus/thunderbolt/devices/.../key Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: When a devices supports Thunderbolt secure connect it will have this attribute. Writing 32 byte hex string changes authorization to use the secure connection method instead. @@ -123,14 +123,14 @@ Description: When a devices supports Thunderbolt secure connect it will What: /sys/bus/thunderbolt/devices/.../device Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This attribute contains id of this device extracted from the device DROM. What: /sys/bus/thunderbolt/devices/.../device_name Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This attribute contains name of this device extracted from the device DROM. @@ -172,21 +172,21 @@ Description: This attribute reports number of TX lanes the device is What: /sys/bus/thunderbolt/devices/.../vendor Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This attribute contains vendor id of this device extracted from the device DROM. What: /sys/bus/thunderbolt/devices/.../vendor_name Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This attribute contains vendor name of this device extracted from the device DROM. What: /sys/bus/thunderbolt/devices/.../unique_id Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This attribute contains unique_id string of this device. This is either read from hardware registers (UUID on newer hardware) or based on UID from the device DROM. @@ -195,7 +195,7 @@ Description: This attribute contains unique_id string of this device. What: /sys/bus/thunderbolt/devices/.../nvm_version Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: If the device has upgradeable firmware the version number is available here. Format: %x.%x, major.minor. If the device is in safe mode reading the file returns @@ -204,7 +204,7 @@ Description: If the device has upgradeable firmware the version What: /sys/bus/thunderbolt/devices/.../nvm_authenticate Date: Sep 2017 KernelVersion: 4.13 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: When new NVM image is written to the non-active NVM area (through non_activeX NVMem device), the authentication procedure is started by writing to @@ -246,7 +246,7 @@ Description: For supported devices, automatically authenticate the new Thunderbo What: /sys/bus/thunderbolt/devices/./key Date: Jan 2018 KernelVersion: 4.15 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This contains name of the property directory the XDomain service exposes. This entry describes the protocol in question. Following directories are already reserved by @@ -261,35 +261,35 @@ Description: This contains name of the property directory the XDomain What: /sys/bus/thunderbolt/devices/./modalias Date: Jan 2018 KernelVersion: 4.15 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: Stores the same MODALIAS value emitted by uevent for the XDomain service. Format: tbtsvc:kSpNvNrN What: /sys/bus/thunderbolt/devices/./prtcid Date: Jan 2018 KernelVersion: 4.15 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This contains XDomain protocol identifier the XDomain service supports. What: /sys/bus/thunderbolt/devices/./prtcvers Date: Jan 2018 KernelVersion: 4.15 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This contains XDomain protocol version the XDomain service supports. What: /sys/bus/thunderbolt/devices/./prtcrevs Date: Jan 2018 KernelVersion: 4.15 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This contains XDomain software version the XDomain service supports. What: /sys/bus/thunderbolt/devices/./prtcstns Date: Jan 2018 KernelVersion: 4.15 -Contact: thunderbolt-software@lists.01.org +Contact: Mika Westerberg Description: This contains XDomain service specific settings as bitmask. Format: %x -- cgit From 3b563b901eefb47ce27a9897dea2739abe70ee5a Mon Sep 17 00:00:00 2001 From: Dan Drown Date: Sat, 5 Aug 2023 21:26:13 -0500 Subject: usb: cdc-acm: add PPS support This patch adds support for PPS to CDC devices. Changes to the DCD pin are monitored and passed to the ldisc system, which is used by pps-ldisc. Signed-off-by: Dan Drown Link: https://lore.kernel.org/r/ZM8ExV6bAvJtIA1d@vps3.drown.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 11da5fb284d0..9b34199474c4 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -324,8 +325,17 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) if (difference & USB_CDC_SERIAL_STATE_DSR) acm->iocount.dsr++; - if (difference & USB_CDC_SERIAL_STATE_DCD) + if (difference & USB_CDC_SERIAL_STATE_DCD) { + if (acm->port.tty) { + struct tty_ldisc *ld = tty_ldisc_ref(acm->port.tty); + if (ld) { + if (ld->ops->dcd_change) + ld->ops->dcd_change(acm->port.tty, newctrl & USB_CDC_SERIAL_STATE_DCD); + tty_ldisc_deref(ld); + } + } acm->iocount.dcd++; + } if (newctrl & USB_CDC_SERIAL_STATE_BREAK) { acm->iocount.brk++; tty_insert_flip_char(&acm->port, 0, TTY_BREAK); -- cgit From df0383ffad64dc09954a60873c1e202b47f08d90 Mon Sep 17 00:00:00 2001 From: Saranya Gopal Date: Mon, 7 Aug 2023 16:22:05 +0530 Subject: usb: typec: ucsi: Add debugfs for ucsi commands Add support for UCSI commands through the following debugfs: # /sys/kernel/debug/usb/ucsi/$UCSI_DEVICE/command # /sys/kernel/debug/usb/ucsi/$UCSI_DEVICE/response Eg: To execute UCSI GetCapabilities: # echo 0x6 > /sys/kernel/debug/usb/ucsi//command Then read the result, # cat /sys/kernel/debug/usb/ucsi//response 0x02000320000000020000ff0400000445 UCSI command will be written into the command file and the response for the command can be viewed under the response file. Reviewed-by: Andy Shevchenko Signed-off-by: Saranya Gopal Co-developed-by: Rajaram Regupathy Signed-off-by: Rajaram Regupathy Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230807105205.742819-1-saranya.gopal@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/Kconfig | 1 + drivers/usb/typec/ucsi/Makefile | 2 + drivers/usb/typec/ucsi/debugfs.c | 99 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.c | 15 ++++++ drivers/usb/typec/ucsi/ucsi.h | 24 ++++++++++ 5 files changed, 141 insertions(+) create mode 100644 drivers/usb/typec/ucsi/debugfs.c diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index b3bb0191987e..bdcb1764cfae 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -4,6 +4,7 @@ config TYPEC_UCSI tristate "USB Type-C Connector System Software Interface driver" depends on !CPU_BIG_ENDIAN depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH + select USB_COMMON if DEBUG_FS help USB Type-C Connector System Software Interface (UCSI) is a specification for an interface that allows the operating system to diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 77f09e136956..b4679f94696b 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -5,6 +5,8 @@ obj-$(CONFIG_TYPEC_UCSI) += typec_ucsi.o typec_ucsi-y := ucsi.o +typec_ucsi-$(CONFIG_DEBUG_FS) += debugfs.o + typec_ucsi-$(CONFIG_TRACING) += trace.o ifneq ($(CONFIG_POWER_SUPPLY),) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c new file mode 100644 index 000000000000..0c7bf88d4a7f --- /dev/null +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI debugfs interface + * + * Copyright (C) 2023 Intel Corporation + * + * Authors: Rajaram Regupathy + * Gopal Saranya + */ +#include +#include +#include +#include +#include + +#include + +#include "ucsi.h" + +static struct dentry *ucsi_debugfs_root; + +static int ucsi_cmd(void *data, u64 val) +{ + struct ucsi *ucsi = data; + int ret; + + memset(&ucsi->debugfs->response, 0, sizeof(ucsi->debugfs->response)); + ucsi->debugfs->status = 0; + + switch (UCSI_COMMAND(val)) { + case UCSI_SET_UOM: + case UCSI_SET_UOR: + case UCSI_SET_PDR: + case UCSI_CONNECTOR_RESET: + ret = ucsi_send_command(ucsi, val, NULL, 0); + break; + case UCSI_GET_CAPABILITY: + case UCSI_GET_CONNECTOR_CAPABILITY: + case UCSI_GET_ALTERNATE_MODES: + case UCSI_GET_CURRENT_CAM: + case UCSI_GET_PDOS: + case UCSI_GET_CABLE_PROPERTY: + case UCSI_GET_CONNECTOR_STATUS: + ret = ucsi_send_command(ucsi, val, + &ucsi->debugfs->response, + sizeof(ucsi->debugfs->response)); + break; + default: + ret = -EOPNOTSUPP; + } + + if (ret < 0) { + ucsi->debugfs->status = ret; + return ret; + } + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(ucsi_cmd_fops, NULL, ucsi_cmd, "0x%llx\n"); + +static int ucsi_resp_show(struct seq_file *s, void *not_used) +{ + struct ucsi *ucsi = s->private; + + if (ucsi->debugfs->status) + return ucsi->debugfs->status; + + seq_printf(s, "0x%016llx%016llx\n", ucsi->debugfs->response.high, + ucsi->debugfs->response.low); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ucsi_resp); + +void ucsi_debugfs_register(struct ucsi *ucsi) +{ + ucsi->debugfs = kzalloc(sizeof(*ucsi->debugfs), GFP_KERNEL); + if (!ucsi->debugfs) + return; + + ucsi->debugfs->dentry = debugfs_create_dir(dev_name(ucsi->dev), ucsi_debugfs_root); + debugfs_create_file("command", 0200, ucsi->debugfs->dentry, ucsi, &ucsi_cmd_fops); + debugfs_create_file("response", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_resp_fops); +} + +void ucsi_debugfs_unregister(struct ucsi *ucsi) +{ + debugfs_remove_recursive(ucsi->debugfs->dentry); + kfree(ucsi->debugfs); +} + +void ucsi_debugfs_init(void) +{ + ucsi_debugfs_root = debugfs_create_dir("ucsi", usb_debug_root); +} + +void ucsi_debugfs_exit(void) +{ + debugfs_remove(ucsi_debugfs_root); +} diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index f6901319639d..c6dfe3dff346 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1530,6 +1530,7 @@ EXPORT_SYMBOL_GPL(ucsi_create); */ void ucsi_destroy(struct ucsi *ucsi) { + ucsi_debugfs_unregister(ucsi); kfree(ucsi); } EXPORT_SYMBOL_GPL(ucsi_destroy); @@ -1552,6 +1553,7 @@ int ucsi_register(struct ucsi *ucsi) queue_delayed_work(system_long_wq, &ucsi->work, 0); + ucsi_debugfs_register(ucsi); return 0; } EXPORT_SYMBOL_GPL(ucsi_register); @@ -1611,6 +1613,19 @@ void ucsi_unregister(struct ucsi *ucsi) } EXPORT_SYMBOL_GPL(ucsi_unregister); +static int __init ucsi_module_init(void) +{ + ucsi_debugfs_init(); + return 0; +} +module_init(ucsi_module_init); + +static void __exit ucsi_module_exit(void) +{ + ucsi_debugfs_exit(); +} +module_exit(ucsi_module_exit); + MODULE_AUTHOR("Heikki Krogerus "); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("USB Type-C Connector System Software Interface driver"); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index c09af859f573..474315a72c77 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -15,6 +15,7 @@ struct ucsi; struct ucsi_altmode; +struct dentry; /* UCSI offsets (Bytes) */ #define UCSI_VERSION 0 @@ -277,6 +278,16 @@ struct ucsi_connector_status { /* -------------------------------------------------------------------------- */ +struct ucsi_debugfs_entry { + u64 command; + struct ucsi_data { + u64 low; + u64 high; + } response; + u32 status; + struct dentry *dentry; +}; + struct ucsi { u16 version; struct device *dev; @@ -286,6 +297,7 @@ struct ucsi { struct ucsi_capability cap; struct ucsi_connector *connector; + struct ucsi_debugfs_entry *debugfs; struct work_struct resume_work; struct delayed_work work; @@ -388,6 +400,18 @@ static inline void ucsi_displayport_remove_partner(struct typec_altmode *adev) { } #endif /* CONFIG_TYPEC_DP_ALTMODE */ +#ifdef CONFIG_DEBUG_FS +void ucsi_debugfs_init(void); +void ucsi_debugfs_exit(void); +void ucsi_debugfs_register(struct ucsi *ucsi); +void ucsi_debugfs_unregister(struct ucsi *ucsi); +#else +static inline void ucsi_debugfs_init(void) { } +static inline void ucsi_debugfs_exit(void) { } +static inline void ucsi_debugfs_register(struct ucsi *ucsi) { } +static inline void ucsi_debugfs_unregister(struct ucsi *ucsi) { } +#endif /* CONFIG_DEBUG_FS */ + /* * NVIDIA VirtualLink (svid 0x955) has two altmode. VirtualLink * DP mode with vdo=0x1 and NVIDIA test mode with vdo=0x3 -- cgit From d4255ac3fd15199b3bd3fa4ff1c16fdb50bda0ad Mon Sep 17 00:00:00 2001 From: Yue Haibing Date: Mon, 7 Aug 2023 22:09:28 +0800 Subject: usb: musb: Remove unused function declarations Commit 32fee1df5110 ("usb: musb: remove unused davinci support") removed these implementations but leave declaration. Signed-off-by: Yue Haibing Link: https://lore.kernel.org/r/20230807140928.35932-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/cppi_dma.h | 3 --- drivers/usb/musb/musb_dma.h | 4 ---- 2 files changed, 7 deletions(-) diff --git a/drivers/usb/musb/cppi_dma.h b/drivers/usb/musb/cppi_dma.h index 16dd1ed44bb5..3606be897cb2 100644 --- a/drivers/usb/musb/cppi_dma.h +++ b/drivers/usb/musb/cppi_dma.h @@ -121,9 +121,6 @@ struct cppi { struct list_head tx_complete; }; -/* CPPI IRQ handler */ -extern irqreturn_t cppi_interrupt(int, void *); - struct cppi41_dma_channel { struct dma_channel channel; struct cppi41_dma_controller *controller; diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index e2445ca3356d..0cd7fc468de8 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -198,10 +198,6 @@ extern struct dma_controller * tusb_dma_controller_create(struct musb *musb, void __iomem *base); extern void tusb_dma_controller_destroy(struct dma_controller *c); -extern struct dma_controller * -cppi_dma_controller_create(struct musb *musb, void __iomem *base); -extern void cppi_dma_controller_destroy(struct dma_controller *c); - extern struct dma_controller * cppi41_dma_controller_create(struct musb *musb, void __iomem *base); extern void cppi41_dma_controller_destroy(struct dma_controller *c); -- cgit From a647b414e05370b29a5140c526a3db693051273e Mon Sep 17 00:00:00 2001 From: Yue Haibing Date: Mon, 7 Aug 2023 22:11:28 +0800 Subject: USB: misc: Remove unused include file usb_u132.h Since commit 8be174835f07 ("usb: ftdi-elan: Delete driver") this include file is not used anymore, so can remove it. Signed-off-by: Yue Haibing Link: https://lore.kernel.org/r/20230807141128.39092-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb_u132.h | 97 --------------------------------------------- 1 file changed, 97 deletions(-) delete mode 100644 drivers/usb/misc/usb_u132.h diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h deleted file mode 100644 index 1584efbbd704..000000000000 --- a/drivers/usb/misc/usb_u132.h +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* -* Common Header File for the Elan Digital Systems U132 adapter -* this file should be included by both the "ftdi-u132" and -* the "u132-hcd" modules. -* -* Copyright(C) 2006 Elan Digital Systems Limited -*(http://www.elandigitalsystems.com) -* -* Author and Maintainer - Tony Olech - Elan Digital Systems -*(tony.olech@elandigitalsystems.com) -* -* The driver was written by Tony Olech(tony.olech@elandigitalsystems.com) -* based on various USB client drivers in the 2.6.15 linux kernel -* with constant reference to the 3rd Edition of Linux Device Drivers -* published by O'Reilly -* -* The U132 adapter is a USB to CardBus adapter specifically designed -* for PC cards that contain an OHCI host controller. Typical PC cards -* are the Orange Mobile 3G Option GlobeTrotter Fusion card. -* -* The U132 adapter will *NOT *work with PC cards that do not contain -* an OHCI controller. A simple way to test whether a PC card has an -* OHCI controller as an interface is to insert the PC card directly -* into a laptop(or desktop) with a CardBus slot and if "lspci" shows -* a new USB controller and "lsusb -v" shows a new OHCI Host Controller -* then there is a good chance that the U132 adapter will support the -* PC card.(you also need the specific client driver for the PC card) -* -* Please inform the Author and Maintainer about any PC cards that -* contain OHCI Host Controller and work when directly connected to -* an embedded CardBus slot but do not work when they are connected -* via an ELAN U132 adapter. -* -* The driver consists of two modules, the "ftdi-u132" module is -* a USB client driver that interfaces to the FTDI chip within -* the U132 adapter manufactured by Elan Digital Systems, and the -* "u132-hcd" module is a USB host controller driver that talks -* to the OHCI controller within CardBus card that are inserted -* in the U132 adapter. -* -* The "ftdi-u132" module should be loaded automatically by the -* hot plug system when the U132 adapter is plugged in. The module -* initialises the adapter which mostly consists of synchronising -* the FTDI chip, before continuously polling the adapter to detect -* PC card insertions. As soon as a PC card containing a recognised -* OHCI controller is seen the "ftdi-u132" module explicitly requests -* the kernel to load the "u132-hcd" module. -* -* The "ftdi-u132" module provides the interface to the inserted -* PC card and the "u132-hcd" module uses the API to send and receive -* data. The API features call-backs, so that part of the "u132-hcd" -* module code will run in the context of one of the kernel threads -* of the "ftdi-u132" module. -* -*/ -int ftdi_elan_switch_on_diagnostics(int number); -void ftdi_elan_gone_away(struct platform_device *pdev); -void start_usb_lock_device_tracing(void); -struct u132_platform_data { - u16 vendor; - u16 device; - u8 potpg; - void (*port_power) (struct device *dev, int is_on); - void (*reset) (struct device *dev); -}; -int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number, - void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, - void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, - int toggle_bits, int error_count, int condition_code, int repeat_number, - int halted, int skipped, int actual, int non_null)); -int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number, - void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, - void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, - int toggle_bits, int error_count, int condition_code, int repeat_number, - int halted, int skipped, int actual, int non_null)); -int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number, - void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, - void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, - int toggle_bits, int error_count, int condition_code, int repeat_number, - int halted, int skipped, int actual, int non_null)); -int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number, - void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, - void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, - int toggle_bits, int error_count, int condition_code, int repeat_number, - int halted, int skipped, int actual, int non_null)); -int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number, - void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, - void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, - int toggle_bits, int error_count, int condition_code, int repeat_number, - int halted, int skipped, int actual, int non_null)); -int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number, - void *endp); -int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset, - u8 width, u32 *data); -int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset, - u8 width, u32 data); -- cgit From af6248afd7653924d224b3fa8843fc93fdbb93b6 Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Sat, 5 Aug 2023 12:56:30 +0800 Subject: USB: usbip: Remove an unnecessary goto When udc_dev = NULL, it is not necessary to goto out to return, just return NULL directly. And the out goto label can be removed. Signed-off-by: Ruan Jinjie Reviewed-by: Shuah Khan Link: https://lore.kernel.org/r/20230805045631.1858638-1-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc_dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index 8e42839e6060..44b04c54c086 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -493,7 +493,7 @@ struct vudc_device *alloc_vudc_device(int devid) udc_dev = kzalloc(sizeof(*udc_dev), GFP_KERNEL); if (!udc_dev) - goto out; + return NULL; INIT_LIST_HEAD(&udc_dev->dev_entry); @@ -503,7 +503,6 @@ struct vudc_device *alloc_vudc_device(int devid) udc_dev = NULL; } -out: return udc_dev; } -- cgit From 85d07c55621676d47d873d2749b88f783cd4d5a1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 4 Aug 2023 15:10:59 -0400 Subject: USB: core: Unite old scheme and new scheme descriptor reads In preparation for reworking the usb_get_device_descriptor() routine, it is desirable to unite the two different code paths responsible for initially determining endpoint 0's maximum packet size in a newly discovered USB device. Making this determination presents a chicken-and-egg sort of problem, in that the only way to learn the maxpacket value is to get it from the device descriptor retrieved from the device, but communicating with the device to retrieve a descriptor requires us to know beforehand the ep0 maxpacket size. In practice this problem is solved in two different ways, referred to in hub.c as the "old scheme" and the "new scheme". The old scheme (which is the approach recommended by the USB-2 spec) involves asking the device to send just the first eight bytes of its device descriptor. Such a transfer uses packets containing no more than eight bytes each, and every USB device must have an ep0 maxpacket size >= 8, so this should succeed. Since the bMaxPacketSize0 field of the device descriptor lies within the first eight bytes, this is all we need. The new scheme is an imitation of the technique used in an early Windows USB implementation, giving it the happy advantage of working with a wide variety of devices (some of them at the time would not work with the old scheme, although that's probably less true now). It involves making an initial guess of the ep0 maxpacket size, asking the device to send up to 64 bytes worth of its device descriptor (which is only 18 bytes long), and then resetting the device to clear any error condition that might have resulted from the guess being wrong. The initial guess is determined by the connection speed; it should be correct in all cases other than full speed, for which the allowed values are 8, 16, 32, and 64 (in this case the initial guess is 64). The reason for this patch is that the old- and new-scheme parts of hub_port_init() use different code paths, one involving usb_get_device_descriptor() and one not, for their initial reads of the device descriptor. Since these reads have essentially the same purpose and are made under essentially the same circumstances, this is illogical. It makes more sense to have both of them use a common subroutine. This subroutine does basically what the new scheme's code did, because that approach is more general than the one used by the old scheme. It only needs to know how many bytes to transfer and whether or not it is being called for the first iteration of a retry loop (in case of certain time-out errors). There are two main differences from the former code: We initialize the bDescriptorType field of the transfer buffer to 0 before performing the transfer, to avoid possibly accessing an uninitialized value afterward. We read the device descriptor into a temporary buffer rather than storing it directly into udev->descriptor, which the old scheme implementation used to do. Since the whole point of this first read of the device descriptor is to determine the bMaxPacketSize0 value, that is what the new routine returns (or an error code). The value is stored in a local variable rather than in udev->descriptor. As a side effect, this necessitates moving a section of code that checks the bcdUSB field for SuperSpeed devices until after the full device descriptor has been retrieved. Signed-off-by: Alan Stern Cc: Oliver Neukum Link: https://lore.kernel.org/r/495cb5d4-f956-4f4a-a875-1e67e9489510@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 173 +++++++++++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 79 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index fcbad9e86328..91abcd904d04 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4741,6 +4741,67 @@ static int hub_enable_device(struct usb_device *udev) return hcd->driver->enable_device(hcd, udev); } +/* + * Get the bMaxPacketSize0 value during initialization by reading the + * device's device descriptor. Since we don't already know this value, + * the transfer is unsafe and it ignores I/O errors, only testing for + * reasonable received values. + * + * For "old scheme" initialization, size will be 8 so we read just the + * start of the device descriptor, which should work okay regardless of + * the actual bMaxPacketSize0 value. For "new scheme" initialization, + * size will be 64 (and buf will point to a sufficiently large buffer), + * which might not be kosher according to the USB spec but it's what + * Windows does and what many devices expect. + * + * Returns: bMaxPacketSize0 or a negative error code. + */ +static int get_bMaxPacketSize0(struct usb_device *udev, + struct usb_device_descriptor *buf, int size, bool first_time) +{ + int i, rc; + + /* + * Retry on all errors; some devices are flakey. + * 255 is for WUSB devices, we actually need to use + * 512 (WUSB1.0[4.8.1]). + */ + for (i = 0; i < GET_MAXPACKET0_TRIES; ++i) { + /* Start with invalid values in case the transfer fails */ + buf->bDescriptorType = buf->bMaxPacketSize0 = 0; + rc = usb_control_msg(udev, usb_rcvaddr0pipe(), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + USB_DT_DEVICE << 8, 0, + buf, size, + initial_descriptor_timeout); + switch (buf->bMaxPacketSize0) { + case 8: case 16: case 32: case 64: case 255: + if (buf->bDescriptorType == USB_DT_DEVICE) { + rc = buf->bMaxPacketSize0; + break; + } + fallthrough; + default: + if (rc >= 0) + rc = -EPROTO; + break; + } + + /* + * Some devices time out if they are powered on + * when already connected. They need a second + * reset, so return early. But only on the first + * attempt, lest we get into a time-out/reset loop. + */ + if (rc > 0 || (rc == -ETIMEDOUT && first_time && + udev->speed > USB_SPEED_FULL)) + break; + } + return rc; +} + +#define GET_DESCRIPTOR_BUFSIZE 64 + /* Reset device, (re)assign address, get device descriptor. * Device connection must be stable, no more debouncing needed. * Returns device in USB_STATE_ADDRESS, except on error. @@ -4765,6 +4826,12 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, int devnum = udev->devnum; const char *driver_name; bool do_new_scheme; + int maxp0; + struct usb_device_descriptor *buf; + + buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); + if (!buf) + return -ENOMEM; /* root hub ports have a slightly longer reset period * (from USB 2.0 spec, section 7.1.7.5) @@ -4884,9 +4951,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, } if (do_new_scheme) { - struct usb_device_descriptor *buf; - int r = 0; - retval = hub_enable_device(udev); if (retval < 0) { dev_err(&udev->dev, @@ -4895,52 +4959,8 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } -#define GET_DESCRIPTOR_BUFSIZE 64 - buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); - if (!buf) { - retval = -ENOMEM; - continue; - } - - /* Retry on all errors; some devices are flakey. - * 255 is for WUSB devices, we actually need to use - * 512 (WUSB1.0[4.8.1]). - */ - for (operations = 0; operations < GET_MAXPACKET0_TRIES; - ++operations) { - buf->bMaxPacketSize0 = 0; - r = usb_control_msg(udev, usb_rcvaddr0pipe(), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - USB_DT_DEVICE << 8, 0, - buf, GET_DESCRIPTOR_BUFSIZE, - initial_descriptor_timeout); - switch (buf->bMaxPacketSize0) { - case 8: case 16: case 32: case 64: case 255: - if (buf->bDescriptorType == - USB_DT_DEVICE) { - r = 0; - break; - } - fallthrough; - default: - if (r == 0) - r = -EPROTO; - break; - } - /* - * Some devices time out if they are powered on - * when already connected. They need a second - * reset. But only on the first attempt, - * lest we get into a time out/reset loop - */ - if (r == 0 || (r == -ETIMEDOUT && - retries == 0 && - udev->speed > USB_SPEED_FULL)) - break; - } - udev->descriptor.bMaxPacketSize0 = - buf->bMaxPacketSize0; - kfree(buf); + maxp0 = get_bMaxPacketSize0(udev, buf, + GET_DESCRIPTOR_BUFSIZE, retries == 0); retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ @@ -4951,14 +4971,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, retval = -ENODEV; goto fail; } - if (r) { - if (r != -ENODEV) + if (maxp0 < 0) { + if (maxp0 != -ENODEV) dev_err(&udev->dev, "device descriptor read/64, error %d\n", - r); - retval = -EMSGSIZE; + maxp0); + retval = maxp0; continue; } -#undef GET_DESCRIPTOR_BUFSIZE } /* @@ -5004,19 +5023,17 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, break; } - retval = usb_get_device_descriptor(udev, 8); - if (retval < 8) { + /* !do_new_scheme || wusb */ + maxp0 = get_bMaxPacketSize0(udev, buf, 8, retries == 0); + if (maxp0 < 0) { + retval = maxp0; if (retval != -ENODEV) dev_err(&udev->dev, "device descriptor read/8, error %d\n", retval); - if (retval >= 0) - retval = -EMSGSIZE; } else { u32 delay; - retval = 0; - delay = udev->parent->hub_delay; udev->hub_delay = min_t(u32, delay, USB_TP_TRANSMISSION_DELAY_MAX); @@ -5033,27 +5050,10 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (retval) goto fail; - /* - * Some superspeed devices have finished the link training process - * and attached to a superspeed hub port, but the device descriptor - * got from those devices show they aren't superspeed devices. Warm - * reset the port attached by the devices can fix them. - */ - if ((udev->speed >= USB_SPEED_SUPER) && - (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) { - dev_err(&udev->dev, "got a wrong device descriptor, " - "warm reset device\n"); - hub_port_reset(hub, port1, udev, - HUB_BH_RESET_TIME, true); - retval = -EINVAL; - goto fail; - } - - if (udev->descriptor.bMaxPacketSize0 == 0xff || - udev->speed >= USB_SPEED_SUPER) + if (maxp0 == 0xff || udev->speed >= USB_SPEED_SUPER) i = 512; else - i = udev->descriptor.bMaxPacketSize0; + i = maxp0; if (usb_endpoint_maxp(&udev->ep0.desc) != i) { if (udev->speed == USB_SPEED_LOW || !(i == 8 || i == 16 || i == 32 || i == 64)) { @@ -5079,6 +5079,20 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } + /* + * Some superspeed devices have finished the link training process + * and attached to a superspeed hub port, but the device descriptor + * got from those devices show they aren't superspeed devices. Warm + * reset the port attached by the devices can fix them. + */ + if ((udev->speed >= USB_SPEED_SUPER) && + (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) { + dev_err(&udev->dev, "got a wrong device descriptor, warm reset device\n"); + hub_port_reset(hub, port1, udev, HUB_BH_RESET_TIME, true); + retval = -EINVAL; + goto fail; + } + usb_detect_quirks(udev); if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { @@ -5101,6 +5115,7 @@ fail: hub_port_disable(hub, port1, 0); update_devnum(udev, devnum); /* for disconnect processing */ } + kfree(buf); return retval; } -- cgit From de28e469da75359a2bb8cd8778b78aa64b1be1f4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 4 Aug 2023 15:12:21 -0400 Subject: USB: core: Change usb_get_device_descriptor() API The usb_get_device_descriptor() routine reads the device descriptor from the udev device and stores it directly in udev->descriptor. This interface is error prone, because the USB subsystem expects in-memory copies of a device's descriptors to be immutable once the device has been initialized. The interface is changed so that the device descriptor is left in a kmalloc-ed buffer, not copied into the usb_device structure. A pointer to the buffer is returned to the caller, who is then responsible for kfree-ing it. The corresponding changes needed in the various callers are fairly small. Signed-off-by: Alan Stern Link: https://lore.kernel.org/r/d0111bb6-56c1-4f90-adf2-6cfe152f6561@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 10 +++++++--- drivers/usb/core/hub.c | 44 +++++++++++++++++++++++--------------------- drivers/usb/core/message.c | 29 ++++++++++++----------------- drivers/usb/core/usb.h | 4 ++-- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 8300baedafd2..6af0a31ff147 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -983,6 +983,7 @@ static int register_root_hub(struct usb_hcd *hcd) { struct device *parent_dev = hcd->self.controller; struct usb_device *usb_dev = hcd->self.root_hub; + struct usb_device_descriptor *descr; const int devnum = 1; int retval; @@ -994,13 +995,16 @@ static int register_root_hub(struct usb_hcd *hcd) mutex_lock(&usb_bus_idr_lock); usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); - retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); - if (retval != sizeof usb_dev->descriptor) { + descr = usb_get_device_descriptor(usb_dev); + if (IS_ERR(descr)) { + retval = PTR_ERR(descr); mutex_unlock(&usb_bus_idr_lock); dev_dbg (parent_dev, "can't read %s device descriptor %d\n", dev_name(&usb_dev->dev), retval); - return (retval < 0) ? retval : -EMSGSIZE; + return retval; } + usb_dev->descriptor = *descr; + kfree(descr); if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) { retval = usb_get_bos_descriptor(usb_dev); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 91abcd904d04..9279c8ccbcf2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2694,12 +2694,17 @@ int usb_authorize_device(struct usb_device *usb_dev) } if (usb_dev->wusb) { - result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor)); - if (result < 0) { + struct usb_device_descriptor *descr; + + descr = usb_get_device_descriptor(usb_dev); + if (IS_ERR(descr)) { + result = PTR_ERR(descr); dev_err(&usb_dev->dev, "can't re-read device descriptor for " "authorization: %d\n", result); goto error_device_descriptor; } + usb_dev->descriptor = *descr; + kfree(descr); } usb_dev->authorized = 1; @@ -4827,7 +4832,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, const char *driver_name; bool do_new_scheme; int maxp0; - struct usb_device_descriptor *buf; + struct usb_device_descriptor *buf, *descr; buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); if (!buf) @@ -5069,15 +5074,16 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, usb_ep0_reinit(udev); } - retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); - if (retval < (signed)sizeof(udev->descriptor)) { + descr = usb_get_device_descriptor(udev); + if (IS_ERR(descr)) { + retval = PTR_ERR(descr); if (retval != -ENODEV) dev_err(&udev->dev, "device descriptor read/all, error %d\n", retval); - if (retval >= 0) - retval = -ENOMSG; goto fail; } + udev->descriptor = *descr; + kfree(descr); /* * Some superspeed devices have finished the link training process @@ -5196,7 +5202,7 @@ hub_power_remaining(struct usb_hub *hub) static int descriptors_changed(struct usb_device *udev, - struct usb_device_descriptor *old_device_descriptor, + struct usb_device_descriptor *new_device_descriptor, struct usb_host_bos *old_bos) { int changed = 0; @@ -5207,8 +5213,8 @@ static int descriptors_changed(struct usb_device *udev, int length; char *buf; - if (memcmp(&udev->descriptor, old_device_descriptor, - sizeof(*old_device_descriptor)) != 0) + if (memcmp(&udev->descriptor, new_device_descriptor, + sizeof(*new_device_descriptor)) != 0) return 1; if ((old_bos && !udev->bos) || (!old_bos && udev->bos)) @@ -5533,9 +5539,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, { struct usb_port *port_dev = hub->ports[port1 - 1]; struct usb_device *udev = port_dev->child; - struct usb_device_descriptor descriptor; + struct usb_device_descriptor *descr; int status = -ENODEV; - int retval; dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus, portchange, portspeed(hub, portstatus)); @@ -5562,23 +5567,20 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, * changed device descriptors before resuscitating the * device. */ - descriptor = udev->descriptor; - retval = usb_get_device_descriptor(udev, - sizeof(udev->descriptor)); - if (retval < 0) { + descr = usb_get_device_descriptor(udev); + if (IS_ERR(descr)) { dev_dbg(&udev->dev, - "can't read device descriptor %d\n", - retval); + "can't read device descriptor %ld\n", + PTR_ERR(descr)); } else { - if (descriptors_changed(udev, &descriptor, + if (descriptors_changed(udev, descr, udev->bos)) { dev_dbg(&udev->dev, "device descriptor has changed\n"); - /* for disconnect() calls */ - udev->descriptor = descriptor; } else { status = 0; /* Nothing to do */ } + kfree(descr); } #ifdef CONFIG_PM } else if (udev->state == USB_STATE_SUSPENDED && diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 0d2bfc909019..077dfe48d01c 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1041,40 +1041,35 @@ char *usb_cache_string(struct usb_device *udev, int index) EXPORT_SYMBOL_GPL(usb_cache_string); /* - * usb_get_device_descriptor - (re)reads the device descriptor (usbcore) - * @dev: the device whose device descriptor is being updated - * @size: how much of the descriptor to read + * usb_get_device_descriptor - read the device descriptor + * @udev: the device whose device descriptor should be read * * Context: task context, might sleep. * - * Updates the copy of the device descriptor stored in the device structure, - * which dedicates space for this purpose. - * * Not exported, only for use by the core. If drivers really want to read * the device descriptor directly, they can call usb_get_descriptor() with * type = USB_DT_DEVICE and index = 0. * - * This call is synchronous, and may not be used in an interrupt context. - * - * Return: The number of bytes received on success, or else the status code - * returned by the underlying usb_control_msg() call. + * Returns: a pointer to a dynamically allocated usb_device_descriptor + * structure (which the caller must deallocate), or an ERR_PTR value. */ -int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) +struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev) { struct usb_device_descriptor *desc; int ret; - if (size > sizeof(*desc)) - return -EINVAL; desc = kmalloc(sizeof(*desc), GFP_NOIO); if (!desc) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + + ret = usb_get_descriptor(udev, USB_DT_DEVICE, 0, desc, sizeof(*desc)); + if (ret == sizeof(*desc)) + return desc; - ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size); if (ret >= 0) - memcpy(&dev->descriptor, desc, size); + ret = -EMSGSIZE; kfree(desc); - return ret; + return ERR_PTR(ret); } /* diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 69ca59841083..60363153fc3f 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -43,8 +43,8 @@ extern bool usb_endpoint_is_ignored(struct usb_device *udev, struct usb_endpoint_descriptor *epd); extern int usb_remove_device(struct usb_device *udev); -extern int usb_get_device_descriptor(struct usb_device *dev, - unsigned int size); +extern struct usb_device_descriptor *usb_get_device_descriptor( + struct usb_device *udev); extern int usb_set_isoch_delay(struct usb_device *dev); extern int usb_get_bos_descriptor(struct usb_device *dev); extern void usb_release_bos_descriptor(struct usb_device *dev); -- cgit From ff33299ec8bb80cdcc073ad9c506bd79bb2ed20b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 4 Aug 2023 15:14:14 -0400 Subject: USB: core: Fix race by not overwriting udev->descriptor in hub_port_init() Syzbot reported an out-of-bounds read in sysfs.c:read_descriptors(): BUG: KASAN: slab-out-of-bounds in read_descriptors+0x263/0x280 drivers/usb/core/sysfs.c:883 Read of size 8 at addr ffff88801e78b8c8 by task udevd/5011 CPU: 0 PID: 5011 Comm: udevd Not tainted 6.4.0-rc6-syzkaller-00195-g40f71e7cd3c6 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/27/2023 Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0xd9/0x150 lib/dump_stack.c:106 print_address_description.constprop.0+0x2c/0x3c0 mm/kasan/report.c:351 print_report mm/kasan/report.c:462 [inline] kasan_report+0x11c/0x130 mm/kasan/report.c:572 read_descriptors+0x263/0x280 drivers/usb/core/sysfs.c:883 ... Allocated by task 758: ... __do_kmalloc_node mm/slab_common.c:966 [inline] __kmalloc+0x5e/0x190 mm/slab_common.c:979 kmalloc include/linux/slab.h:563 [inline] kzalloc include/linux/slab.h:680 [inline] usb_get_configuration+0x1f7/0x5170 drivers/usb/core/config.c:887 usb_enumerate_device drivers/usb/core/hub.c:2407 [inline] usb_new_device+0x12b0/0x19d0 drivers/usb/core/hub.c:2545 As analyzed by Khazhy Kumykov, the cause of this bug is a race between read_descriptors() and hub_port_init(): The first routine uses a field in udev->descriptor, not expecting it to change, while the second overwrites it. Prior to commit 45bf39f8df7f ("USB: core: Don't hold device lock while reading the "descriptors" sysfs file") this race couldn't occur, because the routines were mutually exclusive thanks to the device locking. Removing that locking from read_descriptors() exposed it to the race. The best way to fix the bug is to keep hub_port_init() from changing udev->descriptor once udev has been initialized and registered. Drivers expect the descriptors stored in the kernel to be immutable; we should not undermine this expectation. In fact, this change should have been made long ago. So now hub_port_init() will take an additional argument, specifying a buffer in which to store the device descriptor it reads. (If udev has not yet been initialized, the buffer pointer will be NULL and then hub_port_init() will store the device descriptor in udev as before.) This eliminates the data race responsible for the out-of-bounds read. The changes to hub_port_init() appear more extensive than they really are, because of indentation changes resulting from an attempt to avoid writing to other parts of the usb_device structure after it has been initialized. Similar changes should be made to the code that reads the BOS descriptor, but that can be handled in a separate patch later on. This patch is sufficient to fix the bug found by syzbot. Reported-and-tested-by: syzbot+18996170f8096c6174d0@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-usb/000000000000c0ffe505fe86c9ca@google.com/#r Signed-off-by: Alan Stern Cc: Khazhy Kumykov Fixes: 45bf39f8df7f ("USB: core: Don't hold device lock while reading the "descriptors" sysfs file") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/b958b47a-9a46-4c22-a9f9-e42e42c31251@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 114 ++++++++++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 44 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 9279c8ccbcf2..878913f4b4b3 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4816,10 +4816,17 @@ static int get_bMaxPacketSize0(struct usb_device *udev, * the port lock. For a newly detected device that is not accessible * through any global pointers, it's not necessary to lock the device, * but it is still necessary to lock the port. + * + * For a newly detected device, @dev_descr must be NULL. The device + * descriptor retrieved from the device will then be stored in + * @udev->descriptor. For an already existing device, @dev_descr + * must be non-NULL. The device descriptor will be stored there, + * not in @udev->descriptor, because descriptors for registered + * devices are meant to be immutable. */ static int hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, - int retry_counter) + int retry_counter, struct usb_device_descriptor *dev_descr) { struct usb_device *hdev = hub->hdev; struct usb_hcd *hcd = bus_to_hcd(hdev->bus); @@ -4831,6 +4838,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, int devnum = udev->devnum; const char *driver_name; bool do_new_scheme; + const bool initial = !dev_descr; int maxp0; struct usb_device_descriptor *buf, *descr; @@ -4869,32 +4877,34 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, } oldspeed = udev->speed; - /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... - * it's fixed size except for full speed devices. - * For Wireless USB devices, ep0 max packet is always 512 (tho - * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. - */ - switch (udev->speed) { - case USB_SPEED_SUPER_PLUS: - case USB_SPEED_SUPER: - case USB_SPEED_WIRELESS: /* fixed at 512 */ - udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); - break; - case USB_SPEED_HIGH: /* fixed at 64 */ - udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); - break; - case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ - /* to determine the ep0 maxpacket size, try to read - * the device descriptor to get bMaxPacketSize0 and - * then correct our initial guess. + if (initial) { + /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... + * it's fixed size except for full speed devices. + * For Wireless USB devices, ep0 max packet is always 512 (tho + * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. */ - udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); - break; - case USB_SPEED_LOW: /* fixed at 8 */ - udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8); - break; - default: - goto fail; + switch (udev->speed) { + case USB_SPEED_SUPER_PLUS: + case USB_SPEED_SUPER: + case USB_SPEED_WIRELESS: /* fixed at 512 */ + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); + break; + case USB_SPEED_HIGH: /* fixed at 64 */ + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); + break; + case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ + /* to determine the ep0 maxpacket size, try to read + * the device descriptor to get bMaxPacketSize0 and + * then correct our initial guess. + */ + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); + break; + case USB_SPEED_LOW: /* fixed at 8 */ + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8); + break; + default: + goto fail; + } } if (udev->speed == USB_SPEED_WIRELESS) @@ -4917,22 +4927,24 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (udev->speed < USB_SPEED_SUPER) dev_info(&udev->dev, "%s %s USB device number %d using %s\n", - (udev->config) ? "reset" : "new", speed, + (initial ? "new" : "reset"), speed, devnum, driver_name); - /* Set up TT records, if needed */ - if (hdev->tt) { - udev->tt = hdev->tt; - udev->ttport = hdev->ttport; - } else if (udev->speed != USB_SPEED_HIGH - && hdev->speed == USB_SPEED_HIGH) { - if (!hub->tt.hub) { - dev_err(&udev->dev, "parent hub has no TT\n"); - retval = -EINVAL; - goto fail; + if (initial) { + /* Set up TT records, if needed */ + if (hdev->tt) { + udev->tt = hdev->tt; + udev->ttport = hdev->ttport; + } else if (udev->speed != USB_SPEED_HIGH + && hdev->speed == USB_SPEED_HIGH) { + if (!hub->tt.hub) { + dev_err(&udev->dev, "parent hub has no TT\n"); + retval = -EINVAL; + goto fail; + } + udev->tt = &hub->tt; + udev->ttport = port1; } - udev->tt = &hub->tt; - udev->ttport = port1; } /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? @@ -4966,6 +4978,12 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, maxp0 = get_bMaxPacketSize0(udev, buf, GET_DESCRIPTOR_BUFSIZE, retries == 0); + if (maxp0 > 0 && !initial && + maxp0 != udev->descriptor.bMaxPacketSize0) { + dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n"); + retval = -ENODEV; + goto fail; + } retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ @@ -5039,6 +5057,12 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, } else { u32 delay; + if (!initial && maxp0 != udev->descriptor.bMaxPacketSize0) { + dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n"); + retval = -ENODEV; + goto fail; + } + delay = udev->parent->hub_delay; udev->hub_delay = min_t(u32, delay, USB_TP_TRANSMISSION_DELAY_MAX); @@ -5082,7 +5106,10 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, retval); goto fail; } - udev->descriptor = *descr; + if (initial) + udev->descriptor = *descr; + else + *dev_descr = *descr; kfree(descr); /* @@ -5392,7 +5419,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, } /* reset (non-USB 3.0 devices) and get descriptor */ - status = hub_port_init(hub, udev, port1, i); + status = hub_port_init(hub, udev, port1, i, NULL); if (status < 0) goto loop; @@ -6022,7 +6049,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) struct usb_device *parent_hdev = udev->parent; struct usb_hub *parent_hub; struct usb_hcd *hcd = bus_to_hcd(udev->bus); - struct usb_device_descriptor descriptor = udev->descriptor; + struct usb_device_descriptor descriptor; struct usb_host_bos *bos; int i, j, ret = 0; int port1 = udev->portnum; @@ -6058,7 +6085,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) /* ep0 maxpacket size may change; let the HCD know about it. * Other endpoints will be handled by re-enumeration. */ usb_ep0_reinit(udev); - ret = hub_port_init(parent_hub, udev, port1, i); + ret = hub_port_init(parent_hub, udev, port1, i, &descriptor); if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV) break; } @@ -6070,7 +6097,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev) /* Device might have changed firmware (DFU or similar) */ if (descriptors_changed(udev, &descriptor, bos)) { dev_info(&udev->dev, "device firmware changed\n"); - udev->descriptor = descriptor; /* for disconnect() calls */ goto re_enumerate; } -- cgit From fb57f829beefd4b3746f1b23d51e80ed5d4bb87b Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Tue, 8 Aug 2023 11:37:50 +0200 Subject: usb: dwc3: dwc3-octeon: Verify clock divider Although valid USB clock divider will be calculated for all valid Octeon core frequencies, make code formally correct limiting divider not to be greater that 7 so it fits into H_CLKDIV_SEL field. Signed-off-by: Ladislav Michl Reported-by: Linux Kernel Functional Testing Closes: https://qa-reports.linaro.org/lkft/linux-next-master/build/next-20230808/testrun/18882876/suite/build/test/gcc-8-cavium_octeon_defconfig/log Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/ZNIM7tlBNdHFzXZG@lenoch Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-octeon.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 6f47262a117a..73bdcebf465c 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -251,11 +251,11 @@ static int dwc3_octeon_get_divider(void) while (div < ARRAY_SIZE(clk_div)) { uint64_t rate = octeon_get_io_clock_rate() / clk_div[div]; if (rate <= 300000000 && rate >= 150000000) - break; + return div; div++; } - return div; + return -EINVAL; } static int dwc3_octeon_setup(struct dwc3_octeon *octeon, @@ -289,6 +289,10 @@ static int dwc3_octeon_setup(struct dwc3_octeon *octeon, /* Step 4b: Select controller clock frequency. */ div = dwc3_octeon_get_divider(); + if (div < 0) { + dev_err(dev, "clock divider invalid\n"); + return div; + } val = dwc3_octeon_readq(uctl_ctl_reg); val &= ~USBDRD_UCTL_CTL_H_CLKDIV_SEL; val |= FIELD_PREP(USBDRD_UCTL_CTL_H_CLKDIV_SEL, div); -- cgit From dda4b60ed70bd670eefda081f70c0cb20bbeb1fa Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Wed, 9 Aug 2023 10:44:31 +0800 Subject: usb: ehci: add workaround for chipidea PORTSC.PEC bug Some NXP processor using chipidea IP has a bug when frame babble is detected. As per 4.15.1.1.1 Serial Bus Babble: A babble condition also exists if IN transaction is in progress at High-speed SOF2 point. This is called frame babble. The host controller must disable the port to which the frame babble is detected. The USB controller has disabled the port (PE cleared) and has asserted USBERRINT when frame babble is detected, but PEC is not asserted. Therefore, the SW isn't aware that port has been disabled. Then the SW keeps sending packets to this port, but all of the transfers will fail. This workaround will firstly assert PCD by SW when USBERRINT is detected and then judge whether port change has really occurred or not by polling roothub status. Because the PEC doesn't get asserted in our case, this patch will also assert it by SW when specific conditions are satisfied. Signed-off-by: Xu Yang Acked-by: Peter Chen Link: https://lore.kernel.org/r/20230809024432.535160-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 8 ++++++-- drivers/usb/host/ehci-hub.c | 10 +++++++++- drivers/usb/host/ehci.h | 10 ++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index a1930db0da1c..802bfafb1012 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -755,10 +755,14 @@ restart: /* normal [4.15.1.2] or error [4.15.1.1] completion */ if (likely ((status & (STS_INT|STS_ERR)) != 0)) { - if (likely ((status & STS_ERR) == 0)) + if (likely ((status & STS_ERR) == 0)) { INCR(ehci->stats.normal); - else + } else { + /* Force to check port status */ + if (ehci->has_ci_pec_bug) + status |= STS_PCD; INCR(ehci->stats.error); + } bh = 1; } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index efe30e3be22f..1aee392e8492 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -674,7 +674,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend) || (ehci->reset_done[i] && time_after_eq( - jiffies, ehci->reset_done[i]))) { + jiffies, ehci->reset_done[i])) + || ehci_has_ci_pec_bug(ehci, temp)) { if (i < 7) buf [0] |= 1 << (i + 1); else @@ -875,6 +876,13 @@ int ehci_hub_control( if (temp & PORT_PEC) status |= USB_PORT_STAT_C_ENABLE << 16; + if (ehci_has_ci_pec_bug(ehci, temp)) { + status |= USB_PORT_STAT_C_ENABLE << 16; + ehci_info(ehci, + "PE is cleared by HW port:%d PORTSC:%08x\n", + wIndex + 1, temp); + } + if ((temp & PORT_OCC) && (!ignore_oc && !ehci->spurious_oc)){ status |= USB_PORT_STAT_C_OVERCURRENT << 16; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index c5c7f8782549..1441e3400796 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -207,6 +207,7 @@ struct ehci_hcd { /* one per controller */ unsigned has_fsl_port_bug:1; /* FreeScale */ unsigned has_fsl_hs_errata:1; /* Freescale HS quirk */ unsigned has_fsl_susp_errata:1; /* NXP SUSP quirk */ + unsigned has_ci_pec_bug:1; /* ChipIdea PEC bug */ unsigned big_endian_mmio:1; unsigned big_endian_desc:1; unsigned big_endian_capbase:1; @@ -707,6 +708,15 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) */ #define ehci_has_fsl_susp_errata(e) ((e)->has_fsl_susp_errata) +/* + * Some Freescale/NXP processors using ChipIdea IP have a bug in which + * disabling the port (PE is cleared) does not cause PEC to be asserted + * when frame babble is detected. + */ +#define ehci_has_ci_pec_bug(e, portsc) \ + ((e)->has_ci_pec_bug && ((e)->command & CMD_PSE) \ + && !(portsc & PORT_PEC) && !(portsc & PORT_PE)) + /* * While most USB host controllers implement their registers in * little-endian format, a minority (celleb companion chip) implement -- cgit From 12e6ac69cc7e7d3367599ae26a92a0f9a18bc728 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Wed, 9 Aug 2023 10:44:32 +0800 Subject: usb: chipidea: add workaround for chipidea PEC bug Some NXP processors using ChipIdea USB IP have a bug when frame babble is detected. Issue description: In USB camera test, our controller is host in HS mode. In ISOC IN, when device sends data across the micro frame, it causes the babble in host controller. This will clear the PE bit. In spec, it also requires to set the PEC bit and then set the PCI bit. Without the PCI interrupt, the software does not know the PE is cleared. This will add a flag CI_HDRC_HAS_PORTSC_PEC_MISSED to some impacted platform datas. And the ehci host driver will assert PEC by SW when specific conditions are satisfied. Signed-off-by: Xu Yang Link: https://lore.kernel.org/r/20230809024432.535160-2-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 1 + drivers/usb/chipidea/ci_hdrc_imx.c | 4 +++- drivers/usb/chipidea/core.c | 2 ++ drivers/usb/chipidea/host.c | 1 + include/linux/usb/chipidea.h | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index d262b9df7b3d..d9bb3d3f026e 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -257,6 +257,7 @@ struct ci_hdrc { bool id_event; bool b_sess_valid_event; bool imx28_write_fix; + bool has_portsc_pec_bug; bool supports_runtime_pm; bool in_lpm; bool wakeup_int; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 772bbdade994..e28bb2f2612d 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -68,11 +68,13 @@ static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = { .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | + CI_HDRC_HAS_PORTSC_PEC_MISSED | CI_HDRC_PMQOS, }; static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = { - .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | + CI_HDRC_HAS_PORTSC_PEC_MISSED, }; static const struct of_device_id ci_hdrc_imx_dt_ids[] = { diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 6e1196b53253..7ac39a281b8c 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1044,6 +1044,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) CI_HDRC_IMX28_WRITE_FIX); ci->supports_runtime_pm = !!(ci->platdata->flags & CI_HDRC_SUPPORTS_RUNTIME_PM); + ci->has_portsc_pec_bug = !!(ci->platdata->flags & + CI_HDRC_HAS_PORTSC_PEC_MISSED); platform_set_drvdata(pdev, ci); ret = hw_device_init(ci, base); diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index ebe7400243b1..08af26b762a2 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -151,6 +151,7 @@ static int host_start(struct ci_hdrc *ci) ehci->has_hostpc = ci->hw_bank.lpm; ehci->has_tdi_phy_lpm = ci->hw_bank.lpm; ehci->imx28_write_fix = ci->imx28_write_fix; + ehci->has_ci_pec_bug = ci->has_portsc_pec_bug; priv = (struct ehci_ci_priv *)ehci->priv; priv->reg_vbus = NULL; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index ee38835ed77c..0b4f2d5faa08 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -63,6 +63,7 @@ struct ci_hdrc_platform_data { #define CI_HDRC_IMX_IS_HSIC BIT(14) #define CI_HDRC_PMQOS BIT(15) #define CI_HDRC_PHY_VBUS_CONTROL BIT(16) +#define CI_HDRC_HAS_PORTSC_PEC_MISSED BIT(17) enum usb_dr_mode dr_mode; #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 -- cgit From 1e4c574225cc5a0553115e5eb5787d1474db5b0f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 8 Aug 2023 20:44:18 -0400 Subject: USB: Remove remnants of Wireless USB and UWB Wireless USB has long been defunct, and kernel support for it was removed in 2020 by commit caa6772db4c1 ("Staging: remove wusbcore and UWB from the kernel tree."). Nevertheless, some vestiges of the old implementation still clutter up the USB subsystem and one or two other places. Let's get rid of them once and for all. The only parts still left are the user-facing APIs in include/uapi/linux/usb/ch9.h. (There are also a couple of misleading instances, such as the Sierra Wireless USB modem, which is a USB modem made by Sierra Wireless.) Signed-off-by: Alan Stern Link: https://lore.kernel.org/r/b4f2710f-a2de-4fb0-b50f-76776f3a961b@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/usb.c | 3 +- drivers/usb/core/config.c | 3 - drivers/usb/core/devices.c | 1 - drivers/usb/core/hcd.c | 40 +------- drivers/usb/core/hub.c | 155 +++++++++---------------------- drivers/usb/core/sysfs.c | 3 - drivers/usb/core/urb.c | 27 ++---- drivers/usb/core/usb.c | 11 --- drivers/usb/host/xhci-mem.c | 3 - drivers/usb/host/xhci.c | 11 +-- include/linux/usb.h | 12 --- include/linux/usb/ch9.h | 5 +- include/linux/usb/composite.h | 23 ----- include/linux/usb/hcd.h | 2 - include/uapi/linux/usb/ch11.h | 6 +- include/uapi/linux/usb/ch9.h | 5 +- 16 files changed, 62 insertions(+), 248 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 5e5c7bf51174..1584665fe3cb 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -286,8 +286,7 @@ static bool mt76u_check_sg(struct mt76_dev *dev) struct usb_device *udev = interface_to_usbdev(uintf); return (!disable_usb_sg && udev->bus->sg_tablesize > 0 && - (udev->bus->no_sg_constraint || - udev->speed == USB_SPEED_WIRELESS)); + udev->bus->no_sg_constraint); } static int diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 725b8dbcfe5f..b19e38d5fd10 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -1051,9 +1051,6 @@ int usb_get_bos_descriptor(struct usb_device *dev) } switch (cap_type) { - case USB_CAP_TYPE_WIRELESS_USB: - /* Wireless USB cap descriptor is handled by wusb */ - break; case USB_CAP_TYPE_EXT: dev->bos->ext_cap = (struct usb_ext_cap_descriptor *)buffer; diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 2c14a9636056..a247da73f34d 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -424,7 +424,6 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */ case USB_SPEED_FULL: speed = "12"; break; - case USB_SPEED_WIRELESS: /* Wireless has no real fixed speed */ case USB_SPEED_HIGH: speed = "480"; break; case USB_SPEED_SUPER: diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 6af0a31ff147..12b6dfeaf658 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -156,27 +156,6 @@ static const u8 usb3_rh_dev_descriptor[18] = { 0x01 /* __u8 bNumConfigurations; */ }; -/* usb 2.5 (wireless USB 1.0) root hub device descriptor */ -static const u8 usb25_rh_dev_descriptor[18] = { - 0x12, /* __u8 bLength; */ - USB_DT_DEVICE, /* __u8 bDescriptorType; Device */ - 0x50, 0x02, /* __le16 bcdUSB; v2.5 */ - - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */ - 0xFF, /* __u8 bMaxPacketSize0; always 0xFF (WUSB Spec 7.4.1). */ - - 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */ - 0x02, 0x00, /* __le16 idProduct; device 0x0002 */ - KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ - - 0x03, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - /* usb 2.0 root hub device descriptor */ static const u8 usb2_rh_dev_descriptor[18] = { 0x12, /* __u8 bLength; */ @@ -368,7 +347,7 @@ static const u8 ss_rh_config_descriptor[] = { }; /* authorized_default behaviour: - * -1 is authorized for all devices except wireless (old behaviour) + * -1 is authorized for all devices (leftover from wireless USB) * 0 is unauthorized for all devices * 1 is authorized for all devices * 2 is authorized for internal devices @@ -383,7 +362,7 @@ module_param(authorized_default, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(authorized_default, "Default USB device authorization: 0 is not authorized, 1 is " "authorized, 2 is authorized for internal devices, -1 is " - "authorized except for wireless USB (default, old behaviour)"); + "authorized (default, same as 1)"); /*-------------------------------------------------------------------------*/ /** @@ -578,9 +557,6 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case HCD_USB3: bufp = usb3_rh_dev_descriptor; break; - case HCD_USB25: - bufp = usb25_rh_dev_descriptor; - break; case HCD_USB2: bufp = usb2_rh_dev_descriptor; break; @@ -602,7 +578,6 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) bufp = ss_rh_config_descriptor; len = sizeof ss_rh_config_descriptor; break; - case HCD_USB25: case HCD_USB2: bufp = hs_rh_config_descriptor; len = sizeof hs_rh_config_descriptor; @@ -2848,18 +2823,14 @@ int usb_add_hcd(struct usb_hcd *hcd, hcd->dev_policy = USB_DEVICE_AUTHORIZE_NONE; break; - case USB_AUTHORIZE_ALL: - hcd->dev_policy = USB_DEVICE_AUTHORIZE_ALL; - break; - case USB_AUTHORIZE_INTERNAL: hcd->dev_policy = USB_DEVICE_AUTHORIZE_INTERNAL; break; + case USB_AUTHORIZE_ALL: case USB_AUTHORIZE_WIRED: default: - hcd->dev_policy = hcd->wireless ? - USB_DEVICE_AUTHORIZE_NONE : USB_DEVICE_AUTHORIZE_ALL; + hcd->dev_policy = USB_DEVICE_AUTHORIZE_ALL; break; } @@ -2903,9 +2874,6 @@ int usb_add_hcd(struct usb_hcd *hcd, case HCD_USB2: rhdev->speed = USB_SPEED_HIGH; break; - case HCD_USB25: - rhdev->speed = USB_SPEED_WIRELESS; - break; case HCD_USB3: rhdev->speed = USB_SPEED_SUPER; break; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 878913f4b4b3..b3c09e4c8492 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2140,22 +2140,6 @@ EXPORT_SYMBOL_GPL(usb_set_device_state); * USB-3.0 buses the address is assigned by the controller hardware * and it usually is not the same as the device number. * - * WUSB devices are simple: they have no hubs behind, so the mapping - * device <-> virtual port number becomes 1:1. Why? to simplify the - * life of the device connection logic in - * drivers/usb/wusbcore/devconnect.c. When we do the initial secret - * handshake we need to assign a temporary address in the unauthorized - * space. For simplicity we use the first virtual port number found to - * be free [drivers/usb/wusbcore/devconnect.c:wusbhc_devconnect_ack()] - * and that becomes it's address [X < 128] or its unauthorized address - * [X | 0x80]. - * - * We add 1 as an offset to the one-based USB-stack port number - * (zero-based wusb virtual port index) for two reasons: (a) dev addr - * 0 is reserved by USB for default address; (b) Linux's USB stack - * uses always #1 for the root hub of the controller. So USB stack's - * port #1, which is wusb virtual-port #0 has address #2. - * * Devices connected under xHCI are not as simple. The host controller * supports virtualization, so the hardware assigns device addresses and * the HCD must setup data structures before issuing a set address @@ -2168,19 +2152,13 @@ static void choose_devnum(struct usb_device *udev) /* be safe when more hub events are proceed in parallel */ mutex_lock(&bus->devnum_next_mutex); - if (udev->wusb) { - devnum = udev->portnum + 1; - BUG_ON(test_bit(devnum, bus->devmap.devicemap)); - } else { - /* Try to allocate the next devnum beginning at - * bus->devnum_next. */ - devnum = find_next_zero_bit(bus->devmap.devicemap, 128, - bus->devnum_next); - if (devnum >= 128) - devnum = find_next_zero_bit(bus->devmap.devicemap, - 128, 1); - bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1); - } + + /* Try to allocate the next devnum beginning at bus->devnum_next. */ + devnum = find_next_zero_bit(bus->devmap.devicemap, 128, + bus->devnum_next); + if (devnum >= 128) + devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1); + bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1); if (devnum < 128) { set_bit(devnum, bus->devmap.devicemap); udev->devnum = devnum; @@ -2198,9 +2176,7 @@ static void release_devnum(struct usb_device *udev) static void update_devnum(struct usb_device *udev, int devnum) { - /* The address for a WUSB device is managed by wusbcore. */ - if (!udev->wusb) - udev->devnum = devnum; + udev->devnum = devnum; if (!udev->devaddr) udev->devaddr = (u8)devnum; } @@ -2693,20 +2669,6 @@ int usb_authorize_device(struct usb_device *usb_dev) goto error_autoresume; } - if (usb_dev->wusb) { - struct usb_device_descriptor *descr; - - descr = usb_get_device_descriptor(usb_dev); - if (IS_ERR(descr)) { - result = PTR_ERR(descr); - dev_err(&usb_dev->dev, "can't re-read device descriptor for " - "authorization: %d\n", result); - goto error_device_descriptor; - } - usb_dev->descriptor = *descr; - kfree(descr); - } - usb_dev->authorized = 1; /* Choose and set the configuration. This registers the interfaces * with the driver core and lets interface drivers bind to them. @@ -2723,7 +2685,6 @@ int usb_authorize_device(struct usb_device *usb_dev) } dev_info(&usb_dev->dev, "authorized to connect\n"); -error_device_descriptor: usb_autosuspend_device(usb_dev); error_autoresume: out_authorized: @@ -2806,17 +2767,6 @@ out: return USB_SSP_GEN_UNKNOWN; } -/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */ -static unsigned hub_is_wusb(struct usb_hub *hub) -{ - struct usb_hcd *hcd; - if (hub->hdev->parent != NULL) /* not a root hub? */ - return 0; - hcd = bus_to_hcd(hub->hdev->bus); - return hcd->wireless; -} - - #ifdef CONFIG_USB_FEW_INIT_RETRIES #define PORT_RESET_TRIES 2 #define SET_ADDRESS_TRIES 1 @@ -2969,9 +2919,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, udev->tx_lanes = 1; udev->ssp_rate = USB_SSP_GEN_UNKNOWN; } - if (hub_is_wusb(hub)) - udev->speed = USB_SPEED_WIRELESS; - else if (udev->ssp_rate != USB_SSP_GEN_UNKNOWN) + if (udev->ssp_rate != USB_SSP_GEN_UNKNOWN) udev->speed = USB_SPEED_SUPER_PLUS; else if (hub_is_superspeed(hub->hdev)) udev->speed = USB_SPEED_SUPER; @@ -4880,13 +4828,10 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (initial) { /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... * it's fixed size except for full speed devices. - * For Wireless USB devices, ep0 max packet is always 512 (tho - * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. */ switch (udev->speed) { case USB_SPEED_SUPER_PLUS: case USB_SPEED_SUPER: - case USB_SPEED_WIRELESS: /* fixed at 512 */ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); break; case USB_SPEED_HIGH: /* fixed at 64 */ @@ -4907,10 +4852,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, } } - if (udev->speed == USB_SPEED_WIRELESS) - speed = "variable speed Wireless"; - else - speed = usb_speed_string(udev->speed); + speed = usb_speed_string(udev->speed); /* * The controller driver may be NULL if the controller device @@ -5003,50 +4945,44 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, } } + for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) { + retval = hub_set_address(udev, devnum); + if (retval >= 0) + break; + msleep(200); + } + if (retval < 0) { + if (retval != -ENODEV) + dev_err(&udev->dev, "device not accepting address %d, error %d\n", + devnum, retval); + goto fail; + } + if (udev->speed >= USB_SPEED_SUPER) { + devnum = udev->devnum; + dev_info(&udev->dev, + "%s SuperSpeed%s%s USB device number %d using %s\n", + (udev->config) ? "reset" : "new", + (udev->speed == USB_SPEED_SUPER_PLUS) ? + " Plus" : "", + (udev->ssp_rate == USB_SSP_GEN_2x2) ? + " Gen 2x2" : + (udev->ssp_rate == USB_SSP_GEN_2x1) ? + " Gen 2x1" : + (udev->ssp_rate == USB_SSP_GEN_1x2) ? + " Gen 1x2" : "", + devnum, driver_name); + } + /* - * If device is WUSB, we already assigned an - * unauthorized address in the Connect Ack sequence; - * authorization will assign the final address. + * cope with hardware quirkiness: + * - let SET_ADDRESS settle, some device hardware wants it + * - read ep0 maxpacket even for high and low speed, */ - if (udev->wusb == 0) { - for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) { - retval = hub_set_address(udev, devnum); - if (retval >= 0) - break; - msleep(200); - } - if (retval < 0) { - if (retval != -ENODEV) - dev_err(&udev->dev, "device not accepting address %d, error %d\n", - devnum, retval); - goto fail; - } - if (udev->speed >= USB_SPEED_SUPER) { - devnum = udev->devnum; - dev_info(&udev->dev, - "%s SuperSpeed%s%s USB device number %d using %s\n", - (udev->config) ? "reset" : "new", - (udev->speed == USB_SPEED_SUPER_PLUS) ? - " Plus" : "", - (udev->ssp_rate == USB_SSP_GEN_2x2) ? - " Gen 2x2" : - (udev->ssp_rate == USB_SSP_GEN_2x1) ? - " Gen 2x1" : - (udev->ssp_rate == USB_SSP_GEN_1x2) ? - " Gen 1x2" : "", - devnum, driver_name); - } + msleep(10); - /* cope with hardware quirkiness: - * - let SET_ADDRESS settle, some device hardware wants it - * - read ep0 maxpacket even for high and low speed, - */ - msleep(10); - if (do_new_scheme) - break; - } + if (do_new_scheme) + break; - /* !do_new_scheme || wusb */ maxp0 = get_bMaxPacketSize0(udev, buf, 8, retries == 0); if (maxp0 < 0) { retval = maxp0; @@ -5128,7 +5064,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, usb_detect_quirks(udev); - if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { + if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { retval = usb_get_bos_descriptor(udev); if (!retval) { udev->lpm_capable = usb_device_supports_lpm(udev); @@ -5404,7 +5340,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, usb_set_device_state(udev, USB_STATE_POWERED); udev->bus_mA = hub->mA_per_port; udev->level = hdev->level + 1; - udev->wusb = hub_is_wusb(hub); /* Devices connected to SuperSpeed hubs are USB 3.0 or later */ if (hub_is_superspeed(hub->hdev)) diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 323dc02becbe..5d21718afb05 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -161,9 +161,6 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr, case USB_SPEED_HIGH: speed = "480"; break; - case USB_SPEED_WIRELESS: - speed = "480"; - break; case USB_SPEED_SUPER: speed = "5000"; break; diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 9f3c54032556..7576920e2d5a 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -480,8 +480,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) urb->iso_frame_desc[n].status = -EXDEV; urb->iso_frame_desc[n].actual_length = 0; } - } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint && - dev->speed != USB_SPEED_WIRELESS) { + } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint) { struct scatterlist *sg; int i; @@ -540,17 +539,9 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: /* too small? */ - switch (dev->speed) { - case USB_SPEED_WIRELESS: - if ((urb->interval < 6) - && (xfertype == USB_ENDPOINT_XFER_INT)) - return -EINVAL; - fallthrough; - default: - if (urb->interval <= 0) - return -EINVAL; - break; - } + if (urb->interval <= 0) + return -EINVAL; + /* too big? */ switch (dev->speed) { case USB_SPEED_SUPER_PLUS: @@ -560,10 +551,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) return -EINVAL; max = 1 << 15; break; - case USB_SPEED_WIRELESS: - if (urb->interval > 16) - return -EINVAL; - break; case USB_SPEED_HIGH: /* units are microframes */ /* NOTE usb handles 2^15 */ if (urb->interval > (1024 * 8)) @@ -587,10 +574,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) default: return -EINVAL; } - if (dev->speed != USB_SPEED_WIRELESS) { - /* Round down to a power of 2, no more than max */ - urb->interval = min(max, 1 << ilog2(urb->interval)); - } + /* Round down to a power of 2, no more than max */ + urb->interval = min(max, 1 << ilog2(urb->interval)); } return usb_hcd_submit_urb(urb, mem_flags); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index bba87e5b7f8a..0945ff8df500 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -602,14 +602,6 @@ struct device_type usb_device_type = { #endif }; - -/* Returns 1 if @usb_bus is WUSB, 0 otherwise */ -static unsigned usb_bus_is_wusb(struct usb_bus *bus) -{ - struct usb_hcd *hcd = bus_to_hcd(bus); - return hcd->wireless; -} - static bool usb_dev_authorized(struct usb_device *dev, struct usb_hcd *hcd) { struct usb_hub *hub; @@ -749,9 +741,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, #endif dev->authorized = usb_dev_authorized(dev, usb_hcd); - if (!root_hub) - dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0; - return dev; } EXPORT_SYMBOL_GPL(usb_alloc_dev); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 19a402123de0..8714ab5bf04d 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1108,9 +1108,6 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS); max_packets = MAX_PACKET(8); break; - case USB_SPEED_WIRELESS: - xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); - return -EINVAL; default: /* Speed was set earlier, this shouldn't happen. */ return -EINVAL; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index fae994f679d4..e1b1b64a0723 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2194,7 +2194,6 @@ static unsigned int xhci_get_block_size(struct usb_device *udev) case USB_SPEED_SUPER_PLUS: return SS_BLOCK; case USB_SPEED_UNKNOWN: - case USB_SPEED_WIRELESS: default: /* Should never happen */ return 1; @@ -2555,10 +2554,7 @@ static void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, case USB_SPEED_HIGH: interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1; break; - case USB_SPEED_SUPER: - case USB_SPEED_SUPER_PLUS: - case USB_SPEED_UNKNOWN: - case USB_SPEED_WIRELESS: + default: /* Should never happen because only LS/FS/HS endpoints will get * added to the endpoint list. */ @@ -2615,10 +2611,7 @@ static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci, case USB_SPEED_HIGH: interval_bw->overhead[HS_OVERHEAD_TYPE] += 1; break; - case USB_SPEED_SUPER: - case USB_SPEED_SUPER_PLUS: - case USB_SPEED_UNKNOWN: - case USB_SPEED_WIRELESS: + default: /* Should never happen because only LS/FS/HS endpoints will get * added to the endpoint list. */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 25f8e62a30ec..a21074861f91 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -25,7 +25,6 @@ struct usb_device; struct usb_driver; -struct wusb_dev; /*-------------------------------------------------------------------------*/ @@ -425,7 +424,6 @@ struct usb_host_config { struct usb_host_bos { struct usb_bos_descriptor *desc; - /* wireless cap descriptor is handled by wusb */ struct usb_ext_cap_descriptor *ext_cap; struct usb_ss_cap_descriptor *ss_cap; struct usb_ssp_cap_descriptor *ssp_cap; @@ -612,7 +610,6 @@ struct usb3_lpm_parameters { * WUSB devices are not, until we authorize them from user space. * FIXME -- complete doc * @authenticated: Crypto authentication passed - * @wusb: device is Wireless USB * @lpm_capable: device supports LPM * @lpm_devinit_allow: Allow USB3 device initiated LPM, exit latency is in range * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM @@ -634,8 +631,6 @@ struct usb3_lpm_parameters { * @do_remote_wakeup: remote wakeup should be enabled * @reset_resume: needs reset instead of resume * @port_is_suspended: the upstream port is suspended (L2 or U3) - * @wusb_dev: if this is a Wireless USB device, link to the WUSB - * specific data for the device. * @slot_id: Slot ID assigned by xHCI * @removable: Device can be physically removed from this port * @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout. @@ -696,7 +691,6 @@ struct usb_device { unsigned have_langid:1; unsigned authorized:1; unsigned authenticated:1; - unsigned wusb:1; unsigned lpm_capable:1; unsigned lpm_devinit_allow:1; unsigned usb2_hw_lpm_capable:1; @@ -727,7 +721,6 @@ struct usb_device { unsigned reset_resume:1; unsigned port_is_suspended:1; - struct wusb_dev *wusb_dev; int slot_id; struct usb2_lpm_parameters l1_params; struct usb3_lpm_parameters u1_params; @@ -1742,11 +1735,6 @@ static inline void usb_fill_bulk_urb(struct urb *urb, * encoding of the endpoint interval, and express polling intervals in * microframes (eight per millisecond) rather than in frames (one per * millisecond). - * - * Wireless USB also uses the logarithmic encoding, but specifies it in units of - * 128us instead of 125us. For Wireless USB devices, the interval is passed - * through to the host controller, rather than being translated into microframe - * units. */ static inline void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 969e7dba6358..c93b410b314a 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -3,7 +3,7 @@ * This file holds USB constants and structures that are needed for * USB device APIs. These are used by the USB device model, which is * defined in chapter 9 of the USB 2.0 specification and in the - * Wireless USB 1.0 (spread around). Linux has several APIs in C that + * Wireless USB 1.0 spec (now defunct). Linux has several APIs in C that * need these: * * - the host side Linux-USB kernel driver API; @@ -14,9 +14,6 @@ * act either as a USB host or as a USB device. That means the host and * device side APIs benefit from working well together. * - * There's also "Wireless USB", using low power short range radios for - * peripheral interconnection but otherwise building on the USB framework. - * * Note all descriptors are declared '__attribute__((packed))' so that: * * [a] they never get padded, either internally (USB spec writers diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 07531c4f4350..6014340ba980 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -450,29 +450,6 @@ static inline struct usb_composite_driver *to_cdriver( * * One of these devices is allocated and initialized before the * associated device driver's bind() is called. - * - * OPEN ISSUE: it appears that some WUSB devices will need to be - * built by combining a normal (wired) gadget with a wireless one. - * This revision of the gadget framework should probably try to make - * sure doing that won't hurt too much. - * - * One notion for how to handle Wireless USB devices involves: - * - * (a) a second gadget here, discovery mechanism TBD, but likely - * needing separate "register/unregister WUSB gadget" calls; - * (b) updates to usb_gadget to include flags "is it wireless", - * "is it wired", plus (presumably in a wrapper structure) - * bandgroup and PHY info; - * (c) presumably a wireless_ep wrapping a usb_ep, and reporting - * wireless-specific parameters like maxburst and maxsequence; - * (d) configurations that are specific to wireless links; - * (e) function drivers that understand wireless configs and will - * support wireless for (additional) function instances; - * (f) a function to support association setup (like CBAF), not - * necessarily requiring a wireless adapter; - * (g) composite device setup that can create one or more wireless - * configs, including appropriate association setup support; - * (h) more, TBD. */ struct usb_composite_dev { struct usb_gadget *gadget; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 4e9623e8492b..61d4f0b793dc 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -154,7 +154,6 @@ struct usb_hcd { /* The next flag is a stopgap, to be removed when all the HCDs * support the new root-hub polling mechanism. */ unsigned uses_new_polling:1; - unsigned wireless:1; /* Wireless USB HCD */ unsigned has_tt:1; /* Integrated TT in root hub */ unsigned amd_resume_bug:1; /* AMD remote wakeup quirk */ unsigned can_do_streams:1; /* HC supports streams */ @@ -249,7 +248,6 @@ struct hc_driver { #define HCD_SHARED 0x0004 /* Two (or more) usb_hcds share HW */ #define HCD_USB11 0x0010 /* USB 1.1 */ #define HCD_USB2 0x0020 /* USB 2.0 */ -#define HCD_USB25 0x0030 /* Wireless USB 1.0 (USB 2.5)*/ #define HCD_USB3 0x0040 /* USB 3.0 */ #define HCD_USB31 0x0050 /* USB 3.1 */ #define HCD_USB32 0x0060 /* USB 3.2 */ diff --git a/include/uapi/linux/usb/ch11.h b/include/uapi/linux/usb/ch11.h index fb0cd24c392c..ce4c83f2e66a 100644 --- a/include/uapi/linux/usb/ch11.h +++ b/include/uapi/linux/usb/ch11.h @@ -15,10 +15,8 @@ /* This is arbitrary. * From USB 2.0 spec Table 11-13, offset 7, a hub can * have up to 255 ports. The most yet reported is 10. - * - * Current Wireless USB host hardware (Intel i1480 for example) allows - * up to 22 devices to connect. Upcoming hardware might raise that - * limit. Because the arrays need to add a bit for hub status data, we + * Upcoming hardware might raise that limit. + * Because the arrays need to add a bit for hub status data, we * use 31, so plus one evens out to four bytes. */ #define USB_MAXCHILDREN 31 diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 62d318377379..8a147abfc680 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -3,7 +3,7 @@ * This file holds USB constants and structures that are needed for * USB device APIs. These are used by the USB device model, which is * defined in chapter 9 of the USB 2.0 specification and in the - * Wireless USB 1.0 (spread around). Linux has several APIs in C that + * Wireless USB 1.0 spec (now defunct). Linux has several APIs in C that * need these: * * - the master/host side Linux-USB kernel driver API; @@ -14,9 +14,6 @@ * act either as a USB master/host or as a USB slave/device. That means * the master and slave side APIs benefit from working well together. * - * There's also "Wireless USB", using low power short range radios for - * peripheral interconnection but otherwise building on the USB framework. - * * Note all descriptors are declared '__attribute__((packed))' so that: * * [a] they never get padded, either internally (USB spec writers -- cgit From f176638af476c6d46257cc3303f5c7cf47d5967d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 8 Aug 2023 20:44:35 -0400 Subject: USB: Remove Wireless USB and UWB documentation Support for Wireless USB and Ultra WideBand was removed in 2020 by commit caa6772db4c1 ("Staging: remove wusbcore and UWB from the kernel tree."). But the documentation files were left behind. Let's get rid of that out-of-date documentation. Signed-off-by: Alan Stern Link: https://lore.kernel.org/r/015d4310-bcd3-4ba4-9a0e-3664f281a9be@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- CREDITS | 11 -- Documentation/ABI/testing/sysfs-bus-umc | 28 ---- Documentation/ABI/testing/sysfs-bus-usb | 34 ----- Documentation/ABI/testing/sysfs-class-uwb_rc | 156 --------------------- .../ABI/testing/sysfs-class-uwb_rc-wusbhc | 57 -------- Documentation/ABI/testing/sysfs-wusb_cbaf | 101 ------------- Documentation/admin-guide/kernel-parameters.txt | 2 +- Documentation/driver-api/usb/usb.rst | 3 +- Documentation/usb/authorization.rst | 9 +- 9 files changed, 5 insertions(+), 396 deletions(-) delete mode 100644 Documentation/ABI/testing/sysfs-bus-umc delete mode 100644 Documentation/ABI/testing/sysfs-class-uwb_rc delete mode 100644 Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc delete mode 100644 Documentation/ABI/testing/sysfs-wusb_cbaf diff --git a/CREDITS b/CREDITS index 8b4882024635..f33a33fd2371 100644 --- a/CREDITS +++ b/CREDITS @@ -666,11 +666,6 @@ S: Tamsui town, Taipei county, S: Taiwan 251 S: Republic of China -N: Reinette Chatre -E: reinette.chatre@intel.com -D: WiMedia Link Protocol implementation -D: UWB stack bits and pieces - N: Michael Elizabeth Chastain E: mec@shout.net D: Configure, Menuconfig, xconfig @@ -3023,12 +3018,6 @@ S: Demonstratsii 8-382 S: Tula 300000 S: Russia -N: Inaky Perez-Gonzalez -E: inaky.perez-gonzalez@intel.com -D: UWB stack, HWA-RC driver and HWA-HC drivers -D: Wireless USB additions to the USB stack -D: WiMedia Link Protocol bits and pieces - N: Gordon Peters E: GordPeters@smarttech.com D: Isochronous receive for IEEE 1394 driver (OHCI module). diff --git a/Documentation/ABI/testing/sysfs-bus-umc b/Documentation/ABI/testing/sysfs-bus-umc deleted file mode 100644 index 948fec412446..000000000000 --- a/Documentation/ABI/testing/sysfs-bus-umc +++ /dev/null @@ -1,28 +0,0 @@ -What: /sys/bus/umc/ -Date: July 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - The Wireless Host Controller Interface (WHCI) - specification describes a PCI-based device with - multiple capabilities; the UWB Multi-interface - Controller (UMC). - - The umc bus presents each of the individual - capabilties as a device. - -What: /sys/bus/umc/devices/.../capability_id -Date: July 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - The ID of this capability, with 0 being the radio - controller capability. - -What: /sys/bus/umc/devices/.../version -Date: July 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - The specification version this capability's hardware - interface complies with. diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index be663258b9b7..a44bfe020061 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -28,40 +28,6 @@ Description: drivers, non-authorized one are not. By default, wired USB devices are authorized. - Certified Wireless USB devices are not authorized - initially and should be (by writing 1) after the - device has been authenticated. - -What: /sys/bus/usb/device/.../wusb_cdid -Date: July 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - For Certified Wireless USB devices only. - - A devices's CDID, as 16 space-separated hex octets. - -What: /sys/bus/usb/device/.../wusb_ck -Date: July 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - For Certified Wireless USB devices only. - - Write the device's connection key (CK) to start the - authentication of the device. The CK is 16 - space-separated hex octets. - -What: /sys/bus/usb/device/.../wusb_disconnect -Date: July 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - For Certified Wireless USB devices only. - - Write a 1 to force the device to disconnect - (equivalent to unplugging a wired USB device). - What: /sys/bus/usb/drivers/.../new_id Date: October 2011 Contact: linux-usb@vger.kernel.org diff --git a/Documentation/ABI/testing/sysfs-class-uwb_rc b/Documentation/ABI/testing/sysfs-class-uwb_rc deleted file mode 100644 index a7ea169dc4eb..000000000000 --- a/Documentation/ABI/testing/sysfs-class-uwb_rc +++ /dev/null @@ -1,156 +0,0 @@ -What: /sys/class/uwb_rc -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - Interfaces for WiMedia Ultra Wideband Common Radio - Platform (UWB) radio controllers. - - Familiarity with the ECMA-368 'High Rate Ultra - Wideband MAC and PHY Specification' is assumed. - -What: /sys/class/uwb_rc/beacon_timeout_ms -Date: July 2008 -KernelVersion: 2.6.27 -Description: - If no beacons are received from a device for at least - this time, the device will be considered to have gone - and it will be removed. The default is 3 superframes - (~197 ms) as required by the specification. - -What: /sys/class/uwb_rc/uwb/ -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - An individual UWB radio controller. - -What: /sys/class/uwb_rc/uwb/beacon -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - Write: - - - - to force a specific channel to be used when beaconing, - or, if is -1, to prohibit beaconing. If - is 0, then the default channel selection - algorithm will be used. Valid channels depends on the - radio controller's supported band groups. - - Reading returns the currently active channel, or -1 if - the radio controller is not beaconing. - -What: /sys/class/uwb_rc/uwb/ASIE -Date: August 2014 -KernelVersion: 3.18 -Contact: linux-usb@vger.kernel.org -Description: - - The application-specific information element (ASIE) - included in this device's beacon, in space separated - hex octets. - - Reading returns the current ASIE. Writing replaces - the current ASIE with the one written. - -What: /sys/class/uwb_rc/uwb/scan -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - Write: - - [] - - to start (or stop) scanning on a channel. is one of: - - == ======================================= - 0 scan - 1 scan outside BP - 2 scan while inactive - 3 scanning disabled - 4 scan (with start time of ) - == ======================================= - -What: /sys/class/uwb_rc/uwb/mac_address -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - The EUI-48, in colon-separated hex octets, for this - radio controller. A write will change the radio - controller's EUI-48 but only do so while the device is - not beaconing or scanning. - -What: /sys/class/uwb_rc/uwb/wusbhc -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - A symlink to the device (if any) of the WUSB Host - Controller PAL using this radio controller. - -What: /sys/class/uwb_rc/uwb// -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - A neighbour UWB device that has either been detected - as part of a scan or is a member of the radio - controllers beacon group. - -What: /sys/class/uwb_rc/uwb//BPST -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - The time (using the radio controllers internal 1 ms - interval superframe timer) of the last beacon from - this device was received. - -What: /sys/class/uwb_rc/uwb//DevAddr -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - The current DevAddr of this device in colon separated - hex octets. - -What: /sys/class/uwb_rc/uwb//EUI_48 -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - - The EUI-48 of this device in colon separated hex - octets. - -What: /sys/class/uwb_rc/uwb//IEs -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - The latest IEs included in this device's beacon, in - space separated hex octets with one IE per line. - -What: /sys/class/uwb_rc/uwb//LQE -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - Link Quality Estimate - the Signal to Noise Ratio - (SNR) of all packets received from this device in dB. - This gives an estimate on a suitable PHY rate. Refer - to [ECMA-368] section 13.3 for more details. - -What: /sys/class/uwb_rc/uwb//RSSI -Date: July 2008 -KernelVersion: 2.6.27 -Contact: linux-usb@vger.kernel.org -Description: - Received Signal Strength Indication - the strength of - the received signal in dB. LQE is a more useful - measure of the radio link quality. diff --git a/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc b/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc deleted file mode 100644 index 55eb55cac92e..000000000000 --- a/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc +++ /dev/null @@ -1,57 +0,0 @@ -What: /sys/class/uwb_rc/uwb/wusbhc/wusb_chid -Date: July 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - Write the CHID (16 space-separated hex octets) for this host controller. - This starts the host controller, allowing it to accept connection from - WUSB devices. - - Set an all zero CHID to stop the host controller. - -What: /sys/class/uwb_rc/uwb/wusbhc/wusb_trust_timeout -Date: July 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - Devices that haven't sent a WUSB packet to the host - within 'wusb_trust_timeout' ms are considered to have - disconnected and are removed. The default value of - 4000 ms is the value required by the WUSB - specification. - - Since this relates to security (specifically, the - lifetime of PTKs and GTKs) it should not be changed - from the default. - -What: /sys/class/uwb_rc/uwb/wusbhc/wusb_phy_rate -Date: August 2009 -KernelVersion: 2.6.32 -Contact: David Vrabel -Description: - The maximum PHY rate to use for all connected devices. - This is only of limited use for testing and - development as the hardware's automatic rate - adaptation is better then this simple control. - - Refer to [ECMA-368] section 10.3.1.1 for the value to - use. - -What: /sys/class/uwb_rc/uwb/wusbhc/wusb_dnts -Date: June 2013 -KernelVersion: 3.11 -Contact: Thomas Pugliese -Description: - The device notification time slot (DNTS) count and inverval in - milliseconds that the WUSB host should use. This controls how - often the devices will have the opportunity to send - notifications to the host. - -What: /sys/class/uwb_rc/uwb/wusbhc/wusb_retry_count -Date: June 2013 -KernelVersion: 3.11 -Contact: Thomas Pugliese -Description: - The number of retries that the WUSB host should attempt - before reporting an error for a bus transaction. The range of - valid values is [0..15], where 0 indicates infinite retries. diff --git a/Documentation/ABI/testing/sysfs-wusb_cbaf b/Documentation/ABI/testing/sysfs-wusb_cbaf deleted file mode 100644 index 2969d3694ec0..000000000000 --- a/Documentation/ABI/testing/sysfs-wusb_cbaf +++ /dev/null @@ -1,101 +0,0 @@ -What: /sys/bus/usb/drivers/wusb_cbaf/.../wusb_* -Date: August 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - Various files for managing Cable Based Association of - (wireless) USB devices. - - The sequence of operations should be: - - 1. Device is plugged in. - - 2. The connection manager (CM) sees a device with CBA capability. - (the wusb_chid etc. files in /sys/devices/blah/OURDEVICE). - - 3. The CM writes the host name, supported band groups, - and the CHID (host ID) into the wusb_host_name, - wusb_host_band_groups and wusb_chid files. These - get sent to the device and the CDID (if any) for - this host is requested. - - 4. The CM can verify that the device's supported band - groups (wusb_device_band_groups) are compatible - with the host. - - 5. The CM reads the wusb_cdid file. - - 6. The CM looks it up its database. - - - If it has a matching CHID,CDID entry, the device - has been authorized before and nothing further - needs to be done. - - - If the CDID is zero (or the CM doesn't find a - matching CDID in its database), the device is - assumed to be not known. The CM may associate - the host with device by: writing a randomly - generated CDID to wusb_cdid and then a random CK - to wusb_ck (this uploads the new CC to the - device). - - CMD may choose to prompt the user before - associating with a new device. - - 7. Device is unplugged. - - References: - [WUSB-AM] - Association Models Supplement to the - Certified Wireless Universal Serial Bus - Specification, version 1.0. - -What: /sys/bus/usb/drivers/wusb_cbaf/.../wusb_chid -Date: August 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - The CHID of the host formatted as 16 space-separated - hex octets. - - Writes fetches device's supported band groups and the - the CDID for any existing association with this host. - -What: /sys/bus/usb/drivers/wusb_cbaf/.../wusb_host_name -Date: August 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - A friendly name for the host as a UTF-8 encoded string. - -What: /sys/bus/usb/drivers/wusb_cbaf/.../wusb_host_band_groups -Date: August 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - The band groups supported by the host, in the format - defined in [WUSB-AM]. - -What: /sys/bus/usb/drivers/wusb_cbaf/.../wusb_device_band_groups -Date: August 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - The band groups supported by the device, in the format - defined in [WUSB-AM]. - -What: /sys/bus/usb/drivers/wusb_cbaf/.../wusb_cdid -Date: August 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - The device's CDID formatted as 16 space-separated hex - octets. - -What: /sys/bus/usb/drivers/wusb_cbaf/.../wusb_ck -Date: August 2008 -KernelVersion: 2.6.27 -Contact: David Vrabel -Description: - Write 16 space-separated random, hex octets to - associate with the device. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index a1457995fd41..29fbb38ca759 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6607,7 +6607,7 @@ usbcore.authorized_default= [USB] Default USB device authorization: - (default -1 = authorized except for wireless USB, + (default -1 = authorized (same as 1), 0 = not authorized, 1 = authorized, 2 = authorized if device connected to internal port) diff --git a/Documentation/driver-api/usb/usb.rst b/Documentation/driver-api/usb/usb.rst index 0fcd75ee5897..fb41768696ec 100644 --- a/Documentation/driver-api/usb/usb.rst +++ b/Documentation/driver-api/usb/usb.rst @@ -777,8 +777,7 @@ Speed may be: ======= ====================================================== 1.5 Mbit/s for low speed USB 12 Mbit/s for full speed USB - 480 Mbit/s for high speed USB (added for USB 2.0); - also used for Wireless USB, which has no fixed speed + 480 Mbit/s for high speed USB (added for USB 2.0) 5000 Mbit/s for SuperSpeed USB (added for USB 3.0) ======= ====================================================== diff --git a/Documentation/usb/authorization.rst b/Documentation/usb/authorization.rst index 9e53909d04c2..150a14970e95 100644 --- a/Documentation/usb/authorization.rst +++ b/Documentation/usb/authorization.rst @@ -33,12 +33,9 @@ Remove the lock down:: $ echo 1 > /sys/bus/usb/devices/usbX/authorized_default -By default, Wired USB devices are authorized by default to -connect. Wireless USB hosts deauthorize by default all new connected -devices (this is so because we need to do an authentication phase -before authorizing). Writing "2" to the authorized_default attribute -causes kernel to only authorize by default devices connected to internal -USB ports. +By default, all USB devices are authorized. Writing "2" to the +authorized_default attribute causes the kernel to authorize by default +only devices connected to internal USB ports. Example system lockdown (lame) -- cgit From bb8dc3df68a9f3d11400482d01ce7d241093ef7a Mon Sep 17 00:00:00 2001 From: Ruan Jinjie Date: Wed, 9 Aug 2023 16:48:49 +0800 Subject: usb: gadget/snps_udc_plat: Remove redundant of_match_ptr() The driver depends on CONFIG_OF, so it is not necessary to use of_match_ptr() here. Remove of_match_ptr() and CONFIG_OF. Signed-off-by: Ruan Jinjie Link: https://lore.kernel.org/r/20230809084849.2410477-1-ruanjinjie@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/snps_udc_plat.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index 35c7a149b977..547af2ed9e5e 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -300,7 +300,6 @@ static const struct dev_pm_ops udc_plat_pm_ops = { }; #endif -#if defined(CONFIG_OF) static const struct of_device_id of_udc_match[] = { { .compatible = "brcm,ns2-udc", }, { .compatible = "brcm,cygnus-udc", }, @@ -308,14 +307,13 @@ static const struct of_device_id of_udc_match[] = { { } }; MODULE_DEVICE_TABLE(of, of_udc_match); -#endif static struct platform_driver udc_plat_driver = { .probe = udc_plat_probe, .remove_new = udc_plat_remove, .driver = { .name = "snps-udc-plat", - .of_match_table = of_match_ptr(of_udc_match), + .of_match_table = of_udc_match, #ifdef CONFIG_PM_SLEEP .pm = &udc_plat_pm_ops, #endif -- cgit From 3ddde5aa43cf302aa8ac6d4d5fa6166a09bb0a00 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 9 Aug 2023 16:53:48 +0800 Subject: USB: ohci-sm501: remove unnecessary check of mem The resource is checked in probe function, so there is no need do this check in remove function. Signed-off-by: Yang Yingliang Acked-by: Alan Stern Link: https://lore.kernel.org/r/20230809085348.2761782-1-yangyingliang@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-sm501.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 0468eeb4fcfd..4b39e9d6f33a 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -195,8 +195,7 @@ static void ohci_hcd_sm501_drv_remove(struct platform_device *pdev) release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (mem) - release_mem_region(mem->start, resource_size(mem)); + release_mem_region(mem->start, resource_size(mem)); /* mask interrupts and disable power */ -- cgit From c272dabf2d43c3523af1a40be3127e7a1f84540a Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Wed, 9 Aug 2023 14:53:27 +0800 Subject: usb: host: ehci-sched: try to turn on io watchdog as long as periodic_count > 0 If initially isoc_count = 0, periodic_count > 0 and the io watchdog is not started (e.g. just timed out), then the io watchdog may not run after submitting isoc urbs and enable_periodic(). The isoc urbs may not complete forever if the controller had already stopped periodic schedule. This will try to call turn_on_io_watchdog() for each enable_periodic() to ensure the io watchdog functions properly. Signed-off-by: Xu Yang Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20230809065327.952368-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index bd542b6fc46b..7e834587e7de 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -490,13 +490,14 @@ static int tt_no_collision( static void enable_periodic(struct ehci_hcd *ehci) { if (ehci->periodic_count++) - return; + goto out; /* Stop waiting to turn off the periodic schedule */ ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC); /* Don't start the schedule until PSS is 0 */ ehci_poll_PSS(ehci); +out: turn_on_io_watchdog(ehci); } -- cgit From 5198c0eeb8ff98ee673a2420ba96d93c477c6ef4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 11 Aug 2023 11:47:15 -0400 Subject: USB: core: Fix unused variable warning in usb_alloc_dev() The kernel test robot reported that a recent commit caused a "variable set but not used" warning. As a result of that commit, the variable no longer serves any purpose; it should be removed. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202308092350.HR4PVHUt-lkp@intel.com/ Signed-off-by: Alan Stern Fixes: 1e4c574225cc ("USB: Remove remnants of Wireless USB and UWB") Link: https://lore.kernel.org/r/7223cc66-f006-42ae-9f30-a6c546bf97a7@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 0945ff8df500..2a938cf47ccd 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -645,7 +645,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, { struct usb_device *dev; struct usb_hcd *usb_hcd = bus_to_hcd(bus); - unsigned root_hub = 0; unsigned raw_port = port1; dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -695,7 +694,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, dev->dev.parent = bus->controller; device_set_of_node_from_dev(&dev->dev, bus->sysdev); dev_set_name(&dev->dev, "usb%d", bus->busnum); - root_hub = 1; } else { /* match any labeling on the hubs; it's one-based */ if (parent->devpath[0] == '0') { -- cgit From 59cf445754566984fd55af19ba7146c76e6627bc Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 11 Aug 2023 13:38:46 -0400 Subject: USB: core: Fix oversight in SuperSpeed initialization Commit 85d07c556216 ("USB: core: Unite old scheme and new scheme descriptor reads") altered the way USB devices are enumerated following detection, and in the process it messed up the initialization of SuperSpeed (or faster) devices: [ 31.650759] usb 2-1: new SuperSpeed Plus Gen 2x1 USB device number 2 using xhci_hcd [ 31.663107] usb 2-1: device descriptor read/8, error -71 [ 31.952697] usb 2-1: new SuperSpeed Plus Gen 2x1 USB device number 3 using xhci_hcd [ 31.965122] usb 2-1: device descriptor read/8, error -71 [ 32.080991] usb usb2-port1: attempt power cycle ... The problem was caused by the commit forgetting that in SuperSpeed or faster devices, the device descriptor uses a logarithmic encoding of the bMaxPacketSize0 value. (For some reason I thought the 255 case in the switch statement was meant for these devices, but it isn't -- it was meant for Wireless USB and is no longer needed.) We can fix the oversight by testing for buf->bMaxPacketSize0 = 9 (meaning 512, the actual maxpacket size for ep0 on all SuperSpeed devices) and straightening out the logic that checks and adjusts our initial guesses of the maxpacket value. Reported-and-tested-by: Thinh Nguyen Closes: https://lore.kernel.org/linux-usb/20230810002257.nadxmfmrobkaxgnz@synopsys.com/ Signed-off-by: Alan Stern Fixes: 85d07c556216 ("USB: core: Unite old scheme and new scheme descriptor reads") Link: https://lore.kernel.org/r/8809e6c5-59d5-4d2d-ac8f-6d106658ad73@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b3c09e4c8492..3c54b218301c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4728,7 +4728,7 @@ static int get_bMaxPacketSize0(struct usb_device *udev, buf, size, initial_descriptor_timeout); switch (buf->bMaxPacketSize0) { - case 8: case 16: case 32: case 64: case 255: + case 8: case 16: case 32: case 64: case 9: if (buf->bDescriptorType == USB_DT_DEVICE) { rc = buf->bMaxPacketSize0; break; @@ -5015,23 +5015,35 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (retval) goto fail; - if (maxp0 == 0xff || udev->speed >= USB_SPEED_SUPER) - i = 512; - else - i = maxp0; - if (usb_endpoint_maxp(&udev->ep0.desc) != i) { - if (udev->speed == USB_SPEED_LOW || - !(i == 8 || i == 16 || i == 32 || i == 64)) { - dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i); - retval = -EMSGSIZE; - goto fail; - } + /* + * Check the ep0 maxpacket guess and correct it if necessary. + * maxp0 is the value stored in the device descriptor; + * i is the value it encodes (logarithmic for SuperSpeed or greater). + */ + i = maxp0; + if (udev->speed >= USB_SPEED_SUPER) { + if (maxp0 <= 16) + i = 1 << maxp0; + else + i = 0; /* Invalid */ + } + if (usb_endpoint_maxp(&udev->ep0.desc) == i) { + ; /* Initial ep0 maxpacket guess is right */ + } else if ((udev->speed == USB_SPEED_FULL || + udev->speed == USB_SPEED_HIGH) && + (i == 8 || i == 16 || i == 32 || i == 64)) { + /* Initial guess is wrong; use the descriptor's value */ if (udev->speed == USB_SPEED_FULL) dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i); else dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i); udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i); usb_ep0_reinit(udev); + } else { + /* Initial guess is wrong and descriptor's value is invalid */ + dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", maxp0); + retval = -EMSGSIZE; + goto fail; } descr = usb_get_device_descriptor(udev); -- cgit From 159a98afc88e88f588077afe818081d67f50a5e0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 11 Aug 2023 13:44:38 -0400 Subject: USB: gadget: core: Add missing kerneldoc for vbus_work Add a missing kerneldoc description of the vbus_work field in struct usb_udc. Signed-off-by: Alan Stern Fixes: 50966da807c8 ("usb: gadget: udc: core: Offload usb_udc_vbus_handler processing") Link: https://lore.kernel.org/r/1e5e7cda-b2c8-4917-9952-4354f365ede0@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index cd58f2a4e7f3..3bd0dec0cf6f 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -40,6 +40,7 @@ static const struct bus_type gadget_bus_type; * @allow_connect: Indicates whether UDC is allowed to be pulled up. * Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or * unbound. + * @vbus_work: work routine to handle VBUS status change notifications. * @connect_lock: protects udc->started, gadget->connect, * gadget->allow_connect and gadget->deactivate. The routines * usb_gadget_connect_locked(), usb_gadget_disconnect_locked(), -- cgit From 55c3e571d2a0aabef4f1354604443f1c415d2e85 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 11 Aug 2023 13:47:04 -0400 Subject: USB: gadget: f_mass_storage: Fix unused variable warning Fix a "variable set but not used" warning in f_mass_storage.c. rc is used if verbose debugging is enabled but not otherwise. Signed-off-by: Alan Stern Fixes: d5e2b67aae79 ("USB: g_mass_storage: template f_mass_storage.c file created") Link: https://lore.kernel.org/r/cfed16c7-aa46-494b-ba84-b0e0dc99be3a@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_mass_storage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index da07e45ae6df..722a3ab2b337 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -927,7 +927,7 @@ static void invalidate_sub(struct fsg_lun *curlun) { struct file *filp = curlun->filp; struct inode *inode = file_inode(filp); - unsigned long rc; + unsigned long __maybe_unused rc; rc = invalidate_mapping_pages(inode->i_mapping, 0, -1); VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc); -- cgit From 8d4ff1351801bd646c9fed7aedb9705250f2c87b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 18 Aug 2023 12:29:11 +0200 Subject: dt-bindings: usb: samsung,exynos-dwc3: fix order of clocks on Exynos5433 The Exynos5433 DTSI had always different order of DWC USB3 controller clocks than the binding. The order in the binding was introduced in the commit 949ea75b7ba4 ("dt-bindings: usb: samsung,exynos-dwc3: convert to dtschema") converting to DT schema. The Linux driver does not care about order and was always getting clocks by name. Therefore assume the DTS is the preferred order and correct the binding. Fixes: 949ea75b7ba4 ("dt-bindings: usb: samsung,exynos-dwc3: convert to dtschema") Cc: Sam Protsenko Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Reviewed-by: Sam Protsenko Link: https://lore.kernel.org/r/20230818102911.18388-1-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml index 42ceaf13cd5d..e61badb61b35 100644 --- a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml @@ -82,8 +82,8 @@ allOf: items: - const: aclk - const: susp_clk - - const: pipe_pclk - const: phyclk + - const: pipe_pclk - if: properties: -- cgit From 26f4f8358d89c0d9972a30abdb3f3a425ef49e38 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 16 Aug 2023 15:11:23 -0500 Subject: dt-bindings: usb: samsung,exynos-dwc3: Fix Exynos5433 compatible The correct compatible for Exynos5433 is "samsung,exynos5433-dwusb3". Fix the typo in its usage. Signed-off-by: Sam Protsenko Fixes: 949ea75b7ba4 ("dt-bindings: usb: samsung,exynos-dwc3: convert to dtschema") Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230816201123.3530-1-semen.protsenko@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml index e61badb61b35..deeed2bca2cd 100644 --- a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml @@ -72,7 +72,7 @@ allOf: properties: compatible: contains: - const: samsung,exynos54333-dwusb3 + const: samsung,exynos5433-dwusb3 then: properties: clocks: -- cgit From 1fa206bb764f37d2ab4bf671e483153ef0659b34 Mon Sep 17 00:00:00 2001 From: Luke Lu Date: Wed, 9 Aug 2023 21:29:11 +0000 Subject: usb: dwc3: meson-g12a: do post init to fix broken usb after resumption Device connected to usb otg port of GXL-based boards can not be recognised after resumption, doesn't recover even if disconnect and reconnect the device. dmesg shows it disconnects during resumption. [ 41.492911] usb 1-2: USB disconnect, device number 3 [ 41.499346] usb 1-2: unregistering device [ 41.511939] usb 1-2: unregistering interface 1-2:1.0 Calling usb_post_init() will fix this issue, and it's tested and verified on libretech's aml-s905x-cc board. Cc: stable@vger.kernel.org # v5.8+ Fixes: c99993376f72 ("usb: dwc3: Add Amlogic G12A DWC3 glue") Signed-off-by: Luke Lu Acked-by: Neil Armstrong Link: https://lore.kernel.org/r/20230809212911.18903-1-luke.lu@libre.computer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-meson-g12a.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index e99c7489dba0..2c07c038b584 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -926,6 +926,12 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) return ret; } + if (priv->drvdata->usb_post_init) { + ret = priv->drvdata->usb_post_init(priv); + if (ret) + return ret; + } + return 0; } -- cgit From 20deab8bfc936063385fdce19287f1f630cb2f4b Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Tue, 15 Aug 2023 15:46:48 +0800 Subject: usb: core: Use module_led_trigger macro to simplify the code Use the module_led_trigger macro to simplify the code, which is the same as declaring with module_init() and module_exit(). Signed-off-by: Li Zetao Link: https://lore.kernel.org/r/20230815074648.1015175-1-lizetao1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/ledtrig-usbport.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c index ba371a24ff78..85c999f71ad7 100644 --- a/drivers/usb/core/ledtrig-usbport.c +++ b/drivers/usb/core/ledtrig-usbport.c @@ -350,18 +350,7 @@ static struct led_trigger usbport_led_trigger = { .deactivate = usbport_trig_deactivate, }; -static int __init usbport_trig_init(void) -{ - return led_trigger_register(&usbport_led_trigger); -} - -static void __exit usbport_trig_exit(void) -{ - led_trigger_unregister(&usbport_led_trigger); -} - -module_init(usbport_trig_init); -module_exit(usbport_trig_exit); +module_led_trigger(usbport_led_trigger); MODULE_AUTHOR("Rafał Miłecki "); MODULE_DESCRIPTION("USB port trigger"); -- cgit From f23643306430f86e2f413ee2b986e0773e79da31 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Mon, 14 Aug 2023 18:05:59 +0000 Subject: usb: typec: bus: verify partner exists in typec_altmode_attention Some usb hubs will negotiate DisplayPort Alt mode with the device but will then negotiate a data role swap after entering the alt mode. The data role swap causes the device to unregister all alt modes, however the usb hub will still send Attention messages even after failing to reregister the Alt Mode. type_altmode_attention currently does not verify whether or not a device's altmode partner exists, which results in a NULL pointer error when dereferencing the typec_altmode and typec_altmode_ops belonging to the altmode partner. Verify the presence of a device's altmode partner before sending the Attention message to the Alt Mode driver. Fixes: 8a37d87d72f0 ("usb: typec: Bus type for alternate modes") Cc: stable@vger.kernel.org Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230814180559.923475-1-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/bus.c | 12 ++++++++++-- drivers/usb/typec/tcpm/tcpm.c | 3 ++- include/linux/usb/typec_altmode.h | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index fe5b9a2e61f5..e95ec7e382bb 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -183,12 +183,20 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit); * * Notifies the partner of @adev about Attention command. */ -void typec_altmode_attention(struct typec_altmode *adev, u32 vdo) +int typec_altmode_attention(struct typec_altmode *adev, u32 vdo) { - struct typec_altmode *pdev = &to_altmode(adev)->partner->adev; + struct altmode *partner = to_altmode(adev)->partner; + struct typec_altmode *pdev; + + if (!partner) + return -ENODEV; + + pdev = &partner->adev; if (pdev->ops && pdev->ops->attention) pdev->ops->attention(pdev, vdo); + + return 0; } EXPORT_SYMBOL_GPL(typec_altmode_attention); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 5639b9a1e0bf..ab7e3d7249ab 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1877,7 +1877,8 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, } break; case ADEV_ATTENTION: - typec_altmode_attention(adev, p[1]); + if (typec_altmode_attention(adev, p[1])) + tcpm_log(port, "typec_altmode_attention no port partner altmode"); break; } } diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 350d49012659..28aeef8f9e7b 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -67,7 +67,7 @@ struct typec_altmode_ops { int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo); int typec_altmode_exit(struct typec_altmode *altmode); -void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo); +int typec_altmode_attention(struct typec_altmode *altmode, u32 vdo); int typec_altmode_vdm(struct typec_altmode *altmode, const u32 header, const u32 *vdo, int count); int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf, -- cgit From 23e60c8daf5ec2ab1b731310761b668745fcf6ed Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 16 Aug 2023 14:25:02 -0300 Subject: usb: typec: tcpci: clear the fault status bit According the "USB Type-C Port Controller Interface Specification v2.0" the TCPC sets the fault status register bit-7 (AllRegistersResetToDefault) once the registers have been reset to their default values. This triggers an alert(-irq) on PTN5110 devices albeit we do mask the fault-irq, which may cause a kernel hang. Fix this generically by writing a one to the corresponding bit-7. Cc: stable@vger.kernel.org Fixes: 74e656d6b055 ("staging: typec: Type-C Port Controller Interface driver (tcpci)") Reported-by: "Angus Ainslie (Purism)" Closes: https://lore.kernel.org/all/20190508002749.14816-2-angus@akkea.ca/ Reported-by: Christian Bach Closes: https://lore.kernel.org/regressions/ZR0P278MB07737E5F1D48632897D51AC3EB329@ZR0P278MB0773.CHEP278.PROD.OUTLOOK.COM/t/ Signed-off-by: Marco Felsch Signed-off-by: Fabio Estevam Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230816172502.1155079-1-festevam@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci.c | 4 ++++ include/linux/usb/tcpci.h | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index fc708c289a73..0ee3e6e29bb1 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -602,6 +602,10 @@ static int tcpci_init(struct tcpc_dev *tcpc) if (time_after(jiffies, timeout)) return -ETIMEDOUT; + ret = tcpci_write16(tcpci, TCPC_FAULT_STATUS, TCPC_FAULT_STATUS_ALL_REG_RST_TO_DEFAULT); + if (ret < 0) + return ret; + /* Handle vendor init */ if (tcpci->data->init) { ret = tcpci->data->init(tcpci, tcpci->data); diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 85e95a3251d3..83376473ac76 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -103,6 +103,7 @@ #define TCPC_POWER_STATUS_SINKING_VBUS BIT(0) #define TCPC_FAULT_STATUS 0x1f +#define TCPC_FAULT_STATUS_ALL_REG_RST_TO_DEFAULT BIT(7) #define TCPC_ALERT_EXTENDED 0x21 -- cgit From f72ae60881ff685004d7de7152517607fcd9968f Mon Sep 17 00:00:00 2001 From: Dan Drown Date: Wed, 16 Aug 2023 20:09:43 -0500 Subject: usb: cdc-acm: move ldisc dcd notification outside of acm's read lock dcd_change notification call moved outside of the acm->read_lock to protect any future tty ldisc that calls wait_serial_change() Signed-off-by: Dan Drown Acked-by: Oliver Neukum Link: https://lore.kernel.org/r/ZN1zV/zjPgpGlHXo@vps3.drown.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 9b34199474c4..dfb28c7c3069 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -319,23 +319,24 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) } difference = acm->ctrlin ^ newctrl; + + if ((difference & USB_CDC_SERIAL_STATE_DCD) && acm->port.tty) { + struct tty_ldisc *ld = tty_ldisc_ref(acm->port.tty); + if (ld) { + if (ld->ops->dcd_change) + ld->ops->dcd_change(acm->port.tty, newctrl & USB_CDC_SERIAL_STATE_DCD); + tty_ldisc_deref(ld); + } + } + spin_lock_irqsave(&acm->read_lock, flags); acm->ctrlin = newctrl; acm->oldcount = acm->iocount; if (difference & USB_CDC_SERIAL_STATE_DSR) acm->iocount.dsr++; - if (difference & USB_CDC_SERIAL_STATE_DCD) { - if (acm->port.tty) { - struct tty_ldisc *ld = tty_ldisc_ref(acm->port.tty); - if (ld) { - if (ld->ops->dcd_change) - ld->ops->dcd_change(acm->port.tty, newctrl & USB_CDC_SERIAL_STATE_DCD); - tty_ldisc_deref(ld); - } - } + if (difference & USB_CDC_SERIAL_STATE_DCD) acm->iocount.dcd++; - } if (newctrl & USB_CDC_SERIAL_STATE_BREAK) { acm->iocount.brk++; tty_insert_flip_char(&acm->port, 0, TTY_BREAK); -- cgit From 8d1de0ebce71feb0961458dee7695f1f25767a92 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 10 Aug 2023 21:47:10 +0800 Subject: usb: dwc3: remove unnecessary platform_set_drvdata() Remove unnecessary platform_set_drvdata(..., NULL) in ->remove(), the driver_data will be cleared in device_unbind_cleanup() after calling ->remove() in driver call code. Signed-off-by: Yang Yingliang Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230810134710.114356-1-yangyingliang@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-am62.c | 2 -- drivers/usb/dwc3/dwc3-imx8mp.c | 1 - drivers/usb/dwc3/dwc3-keystone.c | 2 -- drivers/usb/dwc3/dwc3-octeon.c | 1 - 4 files changed, 6 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index 94dcbc443cf2..90a587bc29b7 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -292,8 +292,6 @@ static void dwc3_ti_remove(struct platform_device *pdev) clk_disable_unprepare(am62->usb2_refclk); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - - platform_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 4285bde58d2e..a1e15f2fffdb 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -280,7 +280,6 @@ static void dwc3_imx8mp_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_put_noidle(dev); - platform_set_drvdata(pdev, NULL); } static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 4155e8d5a559..8899348b6276 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -197,8 +197,6 @@ static void kdwc3_remove(struct platform_device *pdev) phy_power_off(kdwc->usb3_phy); phy_exit(kdwc->usb3_phy); phy_pm_runtime_put_sync(kdwc->usb3_phy); - - platform_set_drvdata(pdev, NULL); } static const struct of_device_id kdwc3_of_match[] = { diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 73bdcebf465c..ff01f2c17452 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -519,7 +519,6 @@ static void dwc3_octeon_remove(struct platform_device *pdev) struct dwc3_octeon *octeon = platform_get_drvdata(pdev); of_platform_depopulate(octeon->dev); - platform_set_drvdata(pdev, NULL); } static const struct of_device_id dwc3_octeon_of_match[] = { -- cgit From ae257611573cde279d31be3961a59e255f567fb0 Mon Sep 17 00:00:00 2001 From: Yue Haibing Date: Fri, 18 Aug 2023 20:40:25 +0800 Subject: usb: gadget: function: Remove unused declarations These declarations are not implemented anymore, remove them. Signed-off-by: Yue Haibing Link: https://lore.kernel.org/r/20230818124025.51576-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_phonet.h | 1 - drivers/usb/gadget/function/u_serial.h | 4 ---- drivers/usb/gadget/function/uvc.h | 2 -- 3 files changed, 7 deletions(-) diff --git a/drivers/usb/gadget/function/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h index c53233b37192..ff62ca22c40d 100644 --- a/drivers/usb/gadget/function/u_phonet.h +++ b/drivers/usb/gadget/function/u_phonet.h @@ -20,7 +20,6 @@ struct f_phonet_opts { struct net_device *gphonet_setup_default(void); void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g); int gphonet_register_netdev(struct net_device *net); -int phonet_bind_config(struct usb_configuration *c, struct net_device *dev); void gphonet_cleanup(struct net_device *dev); #endif /* __U_PHONET_H */ diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h index 102a7323a1fd..901d99310bc4 100644 --- a/drivers/usb/gadget/function/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h @@ -71,8 +71,4 @@ void gserial_disconnect(struct gserial *); void gserial_suspend(struct gserial *p); void gserial_resume(struct gserial *p); -/* functions are bound to configurations by a config or gadget driver */ -int gser_bind_config(struct usb_configuration *c, u8 port_num); -int obex_bind_config(struct usb_configuration *c, u8 port_num); - #endif /* __U_SERIAL_H */ diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 100475b1363e..6751de8b63ad 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -178,8 +178,6 @@ struct uvc_file_handle { */ extern void uvc_function_setup_continue(struct uvc_device *uvc); -extern void uvc_endpoint_stream(struct uvc_device *dev); - extern void uvc_function_connect(struct uvc_device *uvc); extern void uvc_function_disconnect(struct uvc_device *uvc); -- cgit From 0bd9e0219bf0764149eb83826c774162c09e74de Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 17 Aug 2023 18:08:23 +0300 Subject: usb: typec: altmodes/displayport: add support for embedded DP cases In the embedded cases, the DisplayPort connector is handled by the TCPM itself. It was proposed to add the "displayport" OF property to the DT bindings, but it was rejected in favour of properly describing the electrical signal path using of_graph. Fallback to the controller fwnode for HPD notifications to support such usecases without requiring additional DT properties. Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230817150824.14371-2-dmitry.baryshkov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/displayport.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index cdf8261e22db..426c88a516e5 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -594,7 +594,10 @@ int dp_altmode_probe(struct typec_altmode *alt) alt->ops = &dp_altmode_ops; fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */ - dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0); + if (fwnode_property_present(fwnode, "displayport")) + dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0); + else + dp->connector_fwnode = fwnode_handle_get(fwnode); /* embedded DP */ if (IS_ERR(dp->connector_fwnode)) dp->connector_fwnode = NULL; -- cgit From 4b3cd783808bb327d931bbb1324d6c367443b721 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 17 Aug 2023 18:08:24 +0300 Subject: usb: typec: qcom-pmic-typec: register drm_bridge The current approach to handling DP on bridge-enabled platforms requires a chain of DP bridges up to the USB-C connector. Register a last DRM bridge for such chain. Acked-by: Bryan O'Donoghue Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230817150824.14371-3-dmitry.baryshkov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/Kconfig | 1 + drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c | 37 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig index 5d393f520fc2..0b2993fef564 100644 --- a/drivers/usb/typec/tcpm/Kconfig +++ b/drivers/usb/typec/tcpm/Kconfig @@ -79,6 +79,7 @@ config TYPEC_WCOVE config TYPEC_QCOM_PMIC tristate "Qualcomm PMIC USB Type-C Port Controller Manager driver" depends on ARCH_QCOM || COMPILE_TEST + depends on DRM || DRM=n help A Type-C port and Power Delivery driver which aggregates two discrete pieces of silicon in the PM8150b PMIC block: the diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c index af44ee4e6e86..581199d37b49 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -17,6 +17,9 @@ #include #include #include + +#include + #include "qcom_pmic_typec_pdphy.h" #include "qcom_pmic_typec_port.h" @@ -33,6 +36,7 @@ struct pmic_typec { struct pmic_typec_port *pmic_typec_port; bool vbus_enabled; struct mutex lock; /* VBUS state serialization */ + struct drm_bridge bridge; }; #define tcpc_to_tcpm(_tcpc_) container_of(_tcpc_, struct pmic_typec, tcpc) @@ -146,6 +150,35 @@ static int qcom_pmic_typec_init(struct tcpc_dev *tcpc) return 0; } +#if IS_ENABLED(CONFIG_DRM) +static int qcom_pmic_typec_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL; +} + +static const struct drm_bridge_funcs qcom_pmic_typec_bridge_funcs = { + .attach = qcom_pmic_typec_attach, +}; + +static int qcom_pmic_typec_init_drm(struct pmic_typec *tcpm) +{ + tcpm->bridge.funcs = &qcom_pmic_typec_bridge_funcs; +#ifdef CONFIG_OF + tcpm->bridge.of_node = of_get_child_by_name(tcpm->dev->of_node, "connector"); +#endif + tcpm->bridge.ops = DRM_BRIDGE_OP_HPD; + tcpm->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; + + return devm_drm_bridge_add(tcpm->dev, &tcpm->bridge); +} +#else +static int qcom_pmic_typec_init_drm(struct pmic_typec *tcpm) +{ + return 0; +} +#endif + static int qcom_pmic_typec_probe(struct platform_device *pdev) { struct pmic_typec *tcpm; @@ -208,6 +241,10 @@ static int qcom_pmic_typec_probe(struct platform_device *pdev) mutex_init(&tcpm->lock); platform_set_drvdata(pdev, tcpm); + ret = qcom_pmic_typec_init_drm(tcpm); + if (ret) + return ret; + tcpm->tcpc.fwnode = device_get_named_child_node(tcpm->dev, "connector"); if (!tcpm->tcpc.fwnode) return -EINVAL; -- cgit From 09867af36969067d89c96ab1452656977f30e22a Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Sun, 20 Aug 2023 20:13:01 +0100 Subject: USB: cdc-acm: support flushing write buffers (TCOFLUSH) If the serial device never reads data written to it (because it is "output only") then the write buffers will still be waiting for the URB to complete on close(), which will hang for 30s until the closing_wait timeout expires. This can happen with the ESP32-H2/ESP32-C6 USB serial interface. Changing the port closing_wait timeout is a privileged operation but flushing the output buffer is not a privileged operation. Implement the flush_buffer tty operation to cancel in-progress writes so that tcflush(fd, TCOFLUSH) can be used to unblock the serial port before close. Signed-off-by: Simon Arlott Link: https://lore.kernel.org/r/555fbc4c-043b-8932-fb9b-a208d61ffbe4@0882a8b5-c6c3-11e9-b005-00805fc181fe.uuid.home.arpa Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index dfb28c7c3069..a1933a86aee0 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -864,6 +864,19 @@ static unsigned int acm_tty_write_room(struct tty_struct *tty) return acm_wb_is_avail(acm) ? acm->writesize : 0; } +static void acm_tty_flush_buffer(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + unsigned long flags; + int i; + + spin_lock_irqsave(&acm->write_lock, flags); + for (i = 0; i < ACM_NW; i++) + if (acm->wb[i].use) + usb_unlink_urb(acm->wb[i].urb); + spin_unlock_irqrestore(&acm->write_lock, flags); +} + static unsigned int acm_tty_chars_in_buffer(struct tty_struct *tty) { struct acm *acm = tty->driver_data; @@ -2027,6 +2040,7 @@ static const struct tty_operations acm_ops = { .hangup = acm_tty_hangup, .write = acm_tty_write, .write_room = acm_tty_write_room, + .flush_buffer = acm_tty_flush_buffer, .ioctl = acm_tty_ioctl, .throttle = acm_tty_throttle, .unthrottle = acm_tty_unthrottle, -- cgit From 2ccbe85456b367d24d53f6ff9f5b2cafd0b54877 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 22 Aug 2023 13:24:55 +0200 Subject: USB: dwc2: hande irq on dead controller correctly If the controller is dead, the honest answer to the question whether it has caused an irq is: unknown As the purpose of the irq return is to trigger switching off an IRQ, the correct response if you cannot determine if your device has caused the interrupt is IRQ_HANDLED Signed-off-by: Oliver Neukum Link: https://lore.kernel.org/r/20230822112455.18957-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/hcd_intr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index c9740caa5974..0144ca8350c3 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -2203,11 +2203,13 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg) irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg) { u32 gintsts, dbg_gintsts; - irqreturn_t retval = IRQ_NONE; + irqreturn_t retval = IRQ_HANDLED; if (!dwc2_is_controller_alive(hsotg)) { dev_warn(hsotg->dev, "Controller is dead\n"); return retval; + } else { + retval = IRQ_NONE; } spin_lock(&hsotg->lock); -- cgit From 7f93e683bc0667a6b5e0da4b49fa07938a9ccad4 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Tue, 22 Aug 2023 12:01:59 +0530 Subject: usb: gadget: udc-xilinx: fix restricted __le16 degrades to integer warning usb_ctrlrequest members wValue and wIndex are of type __le16, so to fix this warnings we are using le16_to_cpu() macros. Reported-by: kernel test robot Closes: https://lore.kernel.org/all/202209020044.CX2PfZzM-lkp@intel.com/ Signed-off-by: Piyush Mehta Link: https://lore.kernel.org/r/20230822063201.16929-2-piyush.mehta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/udc-xilinx.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index f301b09bf3f8..e5029dd3becd 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -1615,13 +1615,13 @@ static void xudc_getstatus(struct xusb_udc *udc) case USB_RECIP_INTERFACE: break; case USB_RECIP_ENDPOINT: - epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; + epnum = le16_to_cpu(udc->setup.wIndex) & USB_ENDPOINT_NUMBER_MASK; if (epnum >= XUSB_MAX_ENDPOINTS) goto stall; target_ep = &udc->ep[epnum]; epcfgreg = udc->read_fn(udc->addr + target_ep->offset); halt = epcfgreg & XUSB_EP_CFG_STALL_MASK; - if (udc->setup.wIndex & USB_DIR_IN) { + if (le16_to_cpu(udc->setup.wIndex) & USB_DIR_IN) { if (!target_ep->is_in) goto stall; } else { @@ -1664,7 +1664,7 @@ static void xudc_set_clear_feature(struct xusb_udc *udc) switch (udc->setup.bRequestType) { case USB_RECIP_DEVICE: - switch (udc->setup.wValue) { + switch (le16_to_cpu(udc->setup.wValue)) { case USB_DEVICE_TEST_MODE: /* * The Test Mode will be executed @@ -1684,13 +1684,15 @@ static void xudc_set_clear_feature(struct xusb_udc *udc) break; case USB_RECIP_ENDPOINT: if (!udc->setup.wValue) { - endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; + endpoint = le16_to_cpu(udc->setup.wIndex) & + USB_ENDPOINT_NUMBER_MASK; if (endpoint >= XUSB_MAX_ENDPOINTS) { xudc_ep0_stall(udc); return; } target_ep = &udc->ep[endpoint]; - outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK; + outinbit = le16_to_cpu(udc->setup.wIndex) & + USB_ENDPOINT_DIR_MASK; outinbit = outinbit >> 7; /* Make sure direction matches.*/ @@ -1867,7 +1869,7 @@ static void xudc_ep0_in(struct xusb_udc *udc) u16 count = 0; u16 length; u8 *ep0rambase; - u8 test_mode = udc->setup.wIndex >> 8; + u8 test_mode = le16_to_cpu(udc->setup.wIndex) >> 8; req = list_first_entry(&ep0->queue, struct xusb_req, queue); bytes_to_tx = req->usb_req.length - req->usb_req.actual; @@ -1878,12 +1880,12 @@ static void xudc_ep0_in(struct xusb_udc *udc) case USB_REQ_SET_ADDRESS: /* Set the address of the device.*/ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, - udc->setup.wValue); + le16_to_cpu(udc->setup.wValue)); break; case USB_REQ_SET_FEATURE: if (udc->setup.bRequestType == USB_RECIP_DEVICE) { - if (udc->setup.wValue == + if (le16_to_cpu(udc->setup.wValue) == USB_DEVICE_TEST_MODE) udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, -- cgit From 0411fa8a5f655ebc0753e718fdfe68bc66a756f0 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Tue, 22 Aug 2023 12:02:00 +0530 Subject: usb: gadget: udc-xilinx: fix cast from restricted __le16 warning usb_ctrlrequest member wValue, wLength and wIndex are of type __le16, conversion macro cpu_to_le16() input argument is __u16, so properly typecasted to fix the cast from restricted __le16 warning. Reported-by: kernel test robot Closes: https://lore.kernel.org/all/202209020044.CX2PfZzM-lkp@intel.com/ Signed-off-by: Piyush Mehta Link: https://lore.kernel.org/r/20230822063201.16929-3-piyush.mehta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/udc-xilinx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index e5029dd3becd..eb06b4234267 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -1755,9 +1755,9 @@ static void xudc_handle_setup(struct xusb_udc *udc) memcpy(&setup, ep0rambase, 8); udc->setup = setup; - udc->setup.wValue = cpu_to_le16(setup.wValue); - udc->setup.wIndex = cpu_to_le16(setup.wIndex); - udc->setup.wLength = cpu_to_le16(setup.wLength); + udc->setup.wValue = cpu_to_le16((u16 __force)setup.wValue); + udc->setup.wIndex = cpu_to_le16((u16 __force)setup.wIndex); + udc->setup.wLength = cpu_to_le16((u16 __force)setup.wLength); /* Clear previous requests */ xudc_nuke(ep0, -ECONNRESET); -- cgit From 52ecf812de2548ea0704e67f99cea1d0f3b8b173 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Tue, 22 Aug 2023 12:02:01 +0530 Subject: usb: gadget: udc-xilinx: fix incorrect type in assignment warning The bitwise attribute is used by the sparse utility to make sure the variable is converted to the local processor type before other (unsafe) operations are performed on the variable. Fix the below sparse warnings type casted with __le16: warning: incorrect type in assignment (different base types) expected unsigned short [usertype] got restricted __le16 [usertype] Reported-by: kernel test robot Closes: https://lore.kernel.org/all/202209020044.CX2PfZzM-lkp@intel.com/ Signed-off-by: Piyush Mehta Link: https://lore.kernel.org/r/20230822063201.16929-4-piyush.mehta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/udc-xilinx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index eb06b4234267..56b8286a8009 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -1636,7 +1636,7 @@ static void xudc_getstatus(struct xusb_udc *udc) } req->usb_req.length = 2; - *(u16 *)req->usb_req.buf = cpu_to_le16(status); + *(__le16 *)req->usb_req.buf = cpu_to_le16(status); ret = __xudc_ep0_queue(ep0, req); if (ret == 0) return; -- cgit From 592d7a4663d2f23eda360048e7a35149cc3aa8d5 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Fri, 18 Aug 2023 22:17:26 -0500 Subject: usb: dwc3: exynos: Add support for Exynos850 variant Add Exynos850 compatible string and associated driver data. Only two clocks are needed for this SoC: - bus_early: bus clock needed for registers access - ref: USB 2.0 DRD reference clock (50 MHz) Signed-off-by: Sam Protsenko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230819031731.22618-4-semen.protsenko@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-exynos.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index f882dd647340..5d365ca51771 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -163,6 +163,12 @@ static const struct dwc3_exynos_driverdata exynos7_drvdata = { .suspend_clk_idx = 1, }; +static const struct dwc3_exynos_driverdata exynos850_drvdata = { + .clk_names = { "bus_early", "ref" }, + .num_clks = 2, + .suspend_clk_idx = -1, +}; + static const struct of_device_id exynos_dwc3_match[] = { { .compatible = "samsung,exynos5250-dwusb3", @@ -173,6 +179,9 @@ static const struct of_device_id exynos_dwc3_match[] = { }, { .compatible = "samsung,exynos7-dwusb3", .data = &exynos7_drvdata, + }, { + .compatible = "samsung,exynos850-dwusb3", + .data = &exynos850_drvdata, }, { } }; -- cgit From 0c2dfb3ea6e92e8179efb44652442f4b87557ed6 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Fri, 18 Aug 2023 22:17:24 -0500 Subject: dt-bindings: usb: samsung,exynos-dwc3: Add Exynos850 support Exynos850 has dwc3 compatible USB controller, so it can reuse existing dwc3 glue layer. Document a new compatible for Exynos850 and its clocks. Signed-off-by: Sam Protsenko Link: https://lore.kernel.org/r/20230819031731.22618-2-semen.protsenko@linaro.org Reviewed-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/samsung,exynos-dwc3.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml index deeed2bca2cd..1ade99e85ba8 100644 --- a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml @@ -15,6 +15,7 @@ properties: - samsung,exynos5250-dwusb3 - samsung,exynos5433-dwusb3 - samsung,exynos7-dwusb3 + - samsung,exynos850-dwusb3 '#address-cells': const: 1 @@ -101,6 +102,21 @@ allOf: - const: usbdrd30_susp_clk - const: usbdrd30_axius_clk + - if: + properties: + compatible: + contains: + const: samsung,exynos850-dwusb3 + then: + properties: + clocks: + minItems: 2 + maxItems: 2 + clock-names: + items: + - const: bus_early + - const: ref + additionalProperties: false examples: -- cgit From 98102ae1549e3af33359ec3a8e57adafa57b1b01 Mon Sep 17 00:00:00 2001 From: Linyu Yuan Date: Thu, 3 Aug 2023 17:10:47 +0800 Subject: usb: gadget: use working speed to calcaulate network bitrate and qlen Take ecm_bitrate() as example, it will be called after gadget device link speed negotiation, consider code if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER), if a gadget device link speed is USB_SPEED_SUPER, gadget_is_superspeed(g) must be true, or not it is a wrong configuration of gadget max support speed. Remove gadget_is_superspeed(g) checking should be safe, and remove other similar operation in ncm, rndis, u_ether. Signed-off-by: Linyu Yuan Link: https://lore.kernel.org/r/20230803091053.9714-2-quic_linyyuan@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ecm.c | 4 ++-- drivers/usb/gadget/function/f_ncm.c | 6 +++--- drivers/usb/gadget/function/f_rndis.c | 6 +++--- drivers/usb/gadget/function/u_ether.c | 5 ++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index c6e63ad77a40..cbe05da94bde 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -68,9 +68,9 @@ static inline struct f_ecm *func_to_ecm(struct usb_function *f) /* peak (theoretical) bulk transfer rate in bits-per-second */ static inline unsigned ecm_bitrate(struct usb_gadget *g) { - if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + if (g->speed == USB_SPEED_SUPER) return 13 * 1024 * 8 * 1000 * 8; - else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + else if (g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else return 19 * 64 * 1 * 1000 * 8; diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 424bb3b666db..e6dac5510540 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -85,11 +85,11 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g) { if (!g) return 0; - else if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS) + else if (g->speed >= USB_SPEED_SUPER_PLUS) return 4250000000U; - else if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + else if (g->speed == USB_SPEED_SUPER) return 3750000000U; - else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + else if (g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else return 19 * 64 * 1 * 1000 * 8; diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index ee95e8f5f9d4..eff5d7cbce00 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -87,11 +87,11 @@ static inline struct f_rndis *func_to_rndis(struct usb_function *f) /* peak (theoretical) bulk transfer rate in bits-per-second */ static unsigned int bitrate(struct usb_gadget *g) { - if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS) + if (g->speed >= USB_SPEED_SUPER_PLUS) return 4250000000U; - if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + if (g->speed == USB_SPEED_SUPER) return 3750000000U; - else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + else if (g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else return 19 * 64 * 1 * 1000 * 8; diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index a366abb45623..4bb0553da658 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -93,11 +93,10 @@ struct eth_dev { #define DEFAULT_QLEN 2 /* double buffering by default */ -/* for dual-speed hardware, use deeper queues at high/super speed */ +/* use deeper queues at high/super speed */ static inline int qlen(struct usb_gadget *gadget, unsigned qmult) { - if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH || - gadget->speed >= USB_SPEED_SUPER)) + if (gadget->speed == USB_SPEED_HIGH || gadget->speed >= USB_SPEED_SUPER) return qmult * DEFAULT_QLEN; else return DEFAULT_QLEN; -- cgit From 8165763f82bd87d742b91ffef2874e7c8d1f6d2b Mon Sep 17 00:00:00 2001 From: Linyu Yuan Date: Thu, 3 Aug 2023 17:10:48 +0800 Subject: usb: gadget: add a inline function gether_bitrate() In function ecm_bitrate(), it is not support report bit rate for super speed plus mode, but it can use same bit rate value defined in ncm and rndis. Add a common inline function gether_bitrate() which report different for all possible speeds, it can be used by ecm, ncm and rndis, also remove old function from them. Signed-off-by: Linyu Yuan Link: https://lore.kernel.org/r/20230803091053.9714-3-quic_linyyuan@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ecm.c | 15 ++------------- drivers/usb/gadget/function/f_ncm.c | 19 ++----------------- drivers/usb/gadget/function/f_rndis.c | 15 +-------------- drivers/usb/gadget/function/u_ether.h | 13 +++++++++++++ 4 files changed, 18 insertions(+), 44 deletions(-) diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index cbe05da94bde..7e943b562348 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -65,17 +65,6 @@ static inline struct f_ecm *func_to_ecm(struct usb_function *f) return container_of(f, struct f_ecm, port.func); } -/* peak (theoretical) bulk transfer rate in bits-per-second */ -static inline unsigned ecm_bitrate(struct usb_gadget *g) -{ - if (g->speed == USB_SPEED_SUPER) - return 13 * 1024 * 8 * 1000 * 8; - else if (g->speed == USB_SPEED_HIGH) - return 13 * 512 * 8 * 1000 * 8; - else - return 19 * 64 * 1 * 1000 * 8; -} - /*-------------------------------------------------------------------------*/ /* @@ -411,10 +400,10 @@ static void ecm_do_notify(struct f_ecm *ecm) /* SPEED_CHANGE data is up/down speeds in bits/sec */ data = req->buf + sizeof *event; - data[0] = cpu_to_le32(ecm_bitrate(cdev->gadget)); + data[0] = cpu_to_le32(gether_bitrate(cdev->gadget)); data[1] = data[0]; - DBG(cdev, "notify speed %d\n", ecm_bitrate(cdev->gadget)); + DBG(cdev, "notify speed %d\n", gether_bitrate(cdev->gadget)); ecm->notify_state = ECM_NOTIFY_NONE; break; } diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index e6dac5510540..0feadf686a31 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -80,21 +80,6 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f) return container_of(f, struct f_ncm, port.func); } -/* peak (theoretical) bulk transfer rate in bits-per-second */ -static inline unsigned ncm_bitrate(struct usb_gadget *g) -{ - if (!g) - return 0; - else if (g->speed >= USB_SPEED_SUPER_PLUS) - return 4250000000U; - else if (g->speed == USB_SPEED_SUPER) - return 3750000000U; - else if (g->speed == USB_SPEED_HIGH) - return 13 * 512 * 8 * 1000 * 8; - else - return 19 * 64 * 1 * 1000 * 8; -} - /*-------------------------------------------------------------------------*/ /* @@ -576,10 +561,10 @@ static void ncm_do_notify(struct f_ncm *ncm) /* SPEED_CHANGE data is up/down speeds in bits/sec */ data = req->buf + sizeof *event; - data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget)); + data[0] = cpu_to_le32(gether_bitrate(cdev->gadget)); data[1] = data[0]; - DBG(cdev, "notify speed %u\n", ncm_bitrate(cdev->gadget)); + DBG(cdev, "notify speed %u\n", gether_bitrate(cdev->gadget)); ncm->notify_state = NCM_NOTIFY_CONNECT; break; } diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index eff5d7cbce00..ed1c3eb91d3b 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -84,19 +84,6 @@ static inline struct f_rndis *func_to_rndis(struct usb_function *f) return container_of(f, struct f_rndis, port.func); } -/* peak (theoretical) bulk transfer rate in bits-per-second */ -static unsigned int bitrate(struct usb_gadget *g) -{ - if (g->speed >= USB_SPEED_SUPER_PLUS) - return 4250000000U; - if (g->speed == USB_SPEED_SUPER) - return 3750000000U; - else if (g->speed == USB_SPEED_HIGH) - return 13 * 512 * 8 * 1000 * 8; - else - return 19 * 64 * 1 * 1000 * 8; -} - /*-------------------------------------------------------------------------*/ /* @@ -640,7 +627,7 @@ static void rndis_open(struct gether *geth) DBG(cdev, "%s\n", __func__); rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, - bitrate(cdev->gadget) / 100); + gether_bitrate(cdev->gadget) / 100); rndis_signal_connect(rndis->params); } diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h index 851ee10d6e63..34be220cef77 100644 --- a/drivers/usb/gadget/function/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -279,4 +279,17 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) return true; } +/* peak (theoretical) bulk transfer rate in bits-per-second */ +static inline unsigned int gether_bitrate(struct usb_gadget *g) +{ + if (g->speed >= USB_SPEED_SUPER_PLUS) + return 4250000000U; + if (g->speed == USB_SPEED_SUPER) + return 3750000000U; + else if (g->speed == USB_SPEED_HIGH) + return 13 * 512 * 8 * 1000 * 8; + else + return 19 * 64 * 1 * 1000 * 8; +} + #endif /* __U_ETHER_H */ -- cgit From 3c5b006f3ee800b4bd9ed37b3a8f271b8560126e Mon Sep 17 00:00:00 2001 From: Linyu Yuan Date: Thu, 3 Aug 2023 17:10:49 +0800 Subject: usb: gadget: f_uvc: change endpoint allocation in uvc_function_bind() when call uvc_function_bind(), gadget still have no connection speed, just follow other gadget function, use fs endpoint descriptor to allocate a video endpoint, remove gadget_is_{super|dual}speed() API call. Signed-off-by: Linyu Yuan Link: https://lore.kernel.org/r/20230803091053.9714-4-quic_linyyuan@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uvc.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 5e919fb65833..c8e149f8315f 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -719,21 +719,13 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) } uvc->enable_interrupt_ep = opts->enable_interrupt_ep; - if (gadget_is_superspeed(c->cdev->gadget)) - ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep, - &uvc_ss_streaming_comp); - else if (gadget_is_dualspeed(cdev->gadget)) - ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep); - else - ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); - + ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); if (!ep) { uvcg_info(f, "Unable to allocate streaming EP\n"); goto error; } uvc->video.ep = ep; - uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; -- cgit From 46decc82ffd54212cc2c600031daec6e835a6503 Mon Sep 17 00:00:00 2001 From: Linyu Yuan Date: Thu, 3 Aug 2023 17:10:50 +0800 Subject: usb: gadget: unconditionally allocate hs/ss descriptor in bind operation Take f_midi_bind() for example, when composite layer call it, it will allocate hs descriptor by calling gadget_is_dualspeed() API to check gadget max support speed capability, but most other gadget function didn't do like this. To follow other function drivers, it is safe to remove the check which mean support all possible link speed by default in function driver. Similar change apply to midi2 and uvc. Also in midi and midi2, as there is no descriptor difference between super speed and super speed plus, follow other gadget function drivers, do not allocate descriptor for super speed plus, composite layer will handle it properly. Signed-off-by: Linyu Yuan Link: https://lore.kernel.org/r/20230803091053.9714-5-quic_linyyuan@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_midi.c | 56 ++++++++++++++--------------------- drivers/usb/gadget/function/f_midi2.c | 44 ++++++++++----------------- drivers/usb/gadget/function/f_uvc.c | 26 ++++++++-------- 3 files changed, 51 insertions(+), 75 deletions(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index fddf539008a9..2d02f25f9597 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1023,40 +1023,30 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) if (!f->fs_descriptors) goto fail_f_midi; - if (gadget_is_dualspeed(c->cdev->gadget)) { - bulk_in_desc.wMaxPacketSize = cpu_to_le16(512); - bulk_out_desc.wMaxPacketSize = cpu_to_le16(512); - f->hs_descriptors = usb_copy_descriptors(midi_function); - if (!f->hs_descriptors) - goto fail_f_midi; - } + bulk_in_desc.wMaxPacketSize = cpu_to_le16(512); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(512); + f->hs_descriptors = usb_copy_descriptors(midi_function); + if (!f->hs_descriptors) + goto fail_f_midi; - if (gadget_is_superspeed(c->cdev->gadget)) { - bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024); - bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024); - i = endpoint_descriptor_index; - midi_function[i++] = (struct usb_descriptor_header *) - &bulk_out_desc; - midi_function[i++] = (struct usb_descriptor_header *) - &bulk_out_ss_comp_desc; - midi_function[i++] = (struct usb_descriptor_header *) - &ms_out_desc; - midi_function[i++] = (struct usb_descriptor_header *) - &bulk_in_desc; - midi_function[i++] = (struct usb_descriptor_header *) - &bulk_in_ss_comp_desc; - midi_function[i++] = (struct usb_descriptor_header *) - &ms_in_desc; - f->ss_descriptors = usb_copy_descriptors(midi_function); - if (!f->ss_descriptors) - goto fail_f_midi; - - if (gadget_is_superspeed_plus(c->cdev->gadget)) { - f->ssp_descriptors = usb_copy_descriptors(midi_function); - if (!f->ssp_descriptors) - goto fail_f_midi; - } - } + bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024); + i = endpoint_descriptor_index; + midi_function[i++] = (struct usb_descriptor_header *) + &bulk_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &bulk_out_ss_comp_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &ms_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &bulk_in_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &bulk_in_ss_comp_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &ms_in_desc; + f->ss_descriptors = usb_copy_descriptors(midi_function); + if (!f->ss_descriptors) + goto fail_f_midi; kfree(midi_function); diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index 5a971ba600fe..ec8cd7c7bbfc 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -1731,7 +1731,6 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2, midi1_out_eps = midi2_midi1_ep_out_descs; break; case USB_SPEED_SUPER: - case USB_SPEED_SUPER_PLUS: midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(1024); midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(1024); for (i = 0; i < midi2->num_eps; i++) @@ -2001,36 +2000,25 @@ static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f) } f_midi2_free_usb_configs(&config); - if (gadget_is_dualspeed(midi2->gadget)) { - status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_HIGH); - if (status < 0) - goto fail; - f->hs_descriptors = usb_copy_descriptors(config.list); - if (!f->hs_descriptors) { - status = -ENOMEM; - goto fail; - } - f_midi2_free_usb_configs(&config); + status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_HIGH); + if (status < 0) + goto fail; + f->hs_descriptors = usb_copy_descriptors(config.list); + if (!f->hs_descriptors) { + status = -ENOMEM; + goto fail; } + f_midi2_free_usb_configs(&config); - if (gadget_is_superspeed(midi2->gadget)) { - status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_SUPER); - if (status < 0) - goto fail; - f->ss_descriptors = usb_copy_descriptors(config.list); - if (!f->ss_descriptors) { - status = -ENOMEM; - goto fail; - } - if (gadget_is_superspeed_plus(midi2->gadget)) { - f->ssp_descriptors = usb_copy_descriptors(config.list); - if (!f->ssp_descriptors) { - status = -ENOMEM; - goto fail; - } - } - f_midi2_free_usb_configs(&config); + status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_SUPER); + if (status < 0) + goto fail; + f->ss_descriptors = usb_copy_descriptors(config.list); + if (!f->ss_descriptors) { + status = -ENOMEM; + goto fail; } + f_midi2_free_usb_configs(&config); mutex_unlock(&f_midi2_desc_mutex); return 0; diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index c8e149f8315f..faa398109431 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -780,21 +780,19 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) f->fs_descriptors = NULL; goto error; } - if (gadget_is_dualspeed(cdev->gadget)) { - f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); - if (IS_ERR(f->hs_descriptors)) { - ret = PTR_ERR(f->hs_descriptors); - f->hs_descriptors = NULL; - goto error; - } + + f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); + if (IS_ERR(f->hs_descriptors)) { + ret = PTR_ERR(f->hs_descriptors); + f->hs_descriptors = NULL; + goto error; } - if (gadget_is_superspeed(c->cdev->gadget)) { - f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); - if (IS_ERR(f->ss_descriptors)) { - ret = PTR_ERR(f->ss_descriptors); - f->ss_descriptors = NULL; - goto error; - } + + f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); + if (IS_ERR(f->ss_descriptors)) { + ret = PTR_ERR(f->ss_descriptors); + f->ss_descriptors = NULL; + goto error; } /* Preallocate control endpoint request. */ -- cgit From 4dfdd90b85f811fc2cbae0d5f87a8d3e9bee9b17 Mon Sep 17 00:00:00 2001 From: Linyu Yuan Date: Thu, 3 Aug 2023 17:10:51 +0800 Subject: usb: gadget: config: remove max speed check in usb_assign_descriptors() usb_assign_descriptors() usally called inside function bind operation, and gadget still have no working connection speed, let's support all speed at this point, it may possible allocate extra memory to store descriptors, but it is small and acceptable. Remove gadget_is_{*}speed() API checking to allow support all speed. Signed-off-by: Linyu Yuan Link: https://lore.kernel.org/r/20230803091053.9714-6-quic_linyyuan@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/config.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index 05507606b2b4..b1f625245713 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -162,8 +162,6 @@ int usb_assign_descriptors(struct usb_function *f, struct usb_descriptor_header **ss, struct usb_descriptor_header **ssp) { - struct usb_gadget *g = f->config->cdev->gadget; - /* super-speed-plus descriptor falls back to super-speed one, * if such a descriptor was provided, thus avoiding a NULL * pointer dereference if a 5gbps capable gadget is used with @@ -177,17 +175,17 @@ int usb_assign_descriptors(struct usb_function *f, if (!f->fs_descriptors) goto err; } - if (hs && gadget_is_dualspeed(g)) { + if (hs) { f->hs_descriptors = usb_copy_descriptors(hs); if (!f->hs_descriptors) goto err; } - if (ss && gadget_is_superspeed(g)) { + if (ss) { f->ss_descriptors = usb_copy_descriptors(ss); if (!f->ss_descriptors) goto err; } - if (ssp && gadget_is_superspeed_plus(g)) { + if (ssp) { f->ssp_descriptors = usb_copy_descriptors(ssp); if (!f->ssp_descriptors) goto err; -- cgit From dc2e6960a0a951c26c9644913db830f7a69bda89 Mon Sep 17 00:00:00 2001 From: Linyu Yuan Date: Thu, 3 Aug 2023 17:10:52 +0800 Subject: usb: gadget: composite: cleanup function config_ep_by_speed_and_alt() When call this function, gadget already have working speed, if it is USB_SPEED_SUPER_PLUS, in theroy gadget_is_superspeed_plus() checking should be true, so there is no need to call it. it is same for other working speed. Remove all gadget_is_{*}speed_plus() API call to clean it up. Signed-off-by: Linyu Yuan Link: https://lore.kernel.org/r/20230803091053.9714-7-quic_linyyuan@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index dd9b90481b4c..0ace45b66a31 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -170,33 +170,27 @@ int config_ep_by_speed_and_alt(struct usb_gadget *g, /* select desired speed */ switch (g->speed) { case USB_SPEED_SUPER_PLUS: - if (gadget_is_superspeed_plus(g)) { - if (f->ssp_descriptors) { - speed_desc = f->ssp_descriptors; - want_comp_desc = 1; - break; - } - incomplete_desc = true; + if (f->ssp_descriptors) { + speed_desc = f->ssp_descriptors; + want_comp_desc = 1; + break; } + incomplete_desc = true; fallthrough; case USB_SPEED_SUPER: - if (gadget_is_superspeed(g)) { - if (f->ss_descriptors) { - speed_desc = f->ss_descriptors; - want_comp_desc = 1; - break; - } - incomplete_desc = true; + if (f->ss_descriptors) { + speed_desc = f->ss_descriptors; + want_comp_desc = 1; + break; } + incomplete_desc = true; fallthrough; case USB_SPEED_HIGH: - if (gadget_is_dualspeed(g)) { - if (f->hs_descriptors) { - speed_desc = f->hs_descriptors; - break; - } - incomplete_desc = true; + if (f->hs_descriptors) { + speed_desc = f->hs_descriptors; + break; } + incomplete_desc = true; fallthrough; default: speed_desc = f->fs_descriptors; -- cgit From 333ab99eab3c6aa7941b898ced3cd754abd20b63 Mon Sep 17 00:00:00 2001 From: Linyu Yuan Date: Thu, 3 Aug 2023 17:10:53 +0800 Subject: usb: gadget: remove max support speed info in bind operation Take ecm_bind() for example, it call gadget_is_{*}speed() API to show gadget max support speed, it is not much help, remove the API usage here is safe. Similar change apply to acm,eem,loopback,ncm,obex,rndis,serial, sourcesink,subset functions. Signed-off-by: Linyu Yuan Link: https://lore.kernel.org/r/20230803091053.9714-8-quic_linyyuan@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_acm.c | 4 +--- drivers/usb/gadget/function/f_ecm.c | 4 +--- drivers/usb/gadget/function/f_eem.c | 4 +--- drivers/usb/gadget/function/f_loopback.c | 4 +--- drivers/usb/gadget/function/f_ncm.c | 4 +--- drivers/usb/gadget/function/f_obex.c | 3 +-- drivers/usb/gadget/function/f_rndis.c | 4 +--- drivers/usb/gadget/function/f_serial.c | 4 +--- drivers/usb/gadget/function/f_sourcesink.c | 4 +--- drivers/usb/gadget/function/f_subset.c | 4 +--- 10 files changed, 10 insertions(+), 29 deletions(-) diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index cb523f118f04..f616059c5e1e 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -691,10 +691,8 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) goto fail; dev_dbg(&cdev->gadget->dev, - "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", + "acm ttyGS%d: IN/%s OUT/%s NOTIFY/%s\n", acm->port_num, - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", acm->port.in->name, acm->port.out->name, acm->notify->name); return 0; diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 7e943b562348..f55f60639e42 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -788,9 +788,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm->port.open = ecm_open; ecm->port.close = ecm_close; - DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n", - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n", ecm->port.in_ep->name, ecm->port.out_ep->name, ecm->notify->name); return 0; diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c index 5d38f29bda72..3b445bd88498 100644 --- a/drivers/usb/gadget/function/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -311,9 +311,7 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto fail; - DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n", - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n", eem->port.in_ep->name, eem->port.out_ep->name); return 0; diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index ae41f556eb75..17ac6ace0cff 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -211,9 +211,7 @@ autoconf_fail: if (ret) return ret; - DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", - (gadget_is_superspeed(c->cdev->gadget) ? "super" : - (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), + DBG(cdev, "%s: IN/%s, OUT/%s\n", f->name, loop->in_ep->name, loop->out_ep->name); return 0; } diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 0feadf686a31..feccf4c8cc4f 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1529,9 +1529,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); ncm->task_timer.function = ncm_tx_timeout; - DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n", - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n", ncm->port.in_ep->name, ncm->port.out_ep->name, ncm->notify->name); return 0; diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index ab26d84ed95e..dcb093210305 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -365,9 +365,8 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto fail; - dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n", + dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: IN/%s OUT/%s\n", obex->port_num, - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", obex->port.in->name, obex->port.out->name); return 0; diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index ed1c3eb91d3b..b47f99d17ee9 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -798,9 +798,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) * until we're activated via set_alt(). */ - DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + DBG(cdev, "RNDIS: IN/%s OUT/%s NOTIFY/%s\n", rndis->port.in_ep->name, rndis->port.out_ep->name, rndis->notify->name); return 0; diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index a9480b9e312e..65c50092aea2 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -236,10 +236,8 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f) gser_ss_function, gser_ss_function); if (status) goto fail; - dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", + dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: IN/%s OUT/%s\n", gser->port_num, - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", gser->port.in->name, gser->port.out->name); return 0; diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index 6803cd60cc6d..2edbd9b510d6 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -436,9 +436,7 @@ no_iso: if (ret) return ret; - DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n", - (gadget_is_superspeed(c->cdev->gadget) ? "super" : - (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), + DBG(cdev, "%s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n", f->name, ss->in_ep->name, ss->out_ep->name, ss->iso_in_ep ? ss->iso_in_ep->name : "", ss->iso_out_ep ? ss->iso_out_ep->name : ""); diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 51c1cae162d9..8ae9689ef2a0 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -367,9 +367,7 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) * until we're activated via set_alt(). */ - DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n", - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + DBG(cdev, "CDC Subset: IN/%s OUT/%s\n", geth->port.in_ep->name, geth->port.out_ep->name); return 0; -- cgit From e16d5f1447e0fae6cbcd1d430b20f87b65f25307 Mon Sep 17 00:00:00 2001 From: Yinbo Zhu Date: Tue, 15 Aug 2023 14:58:33 +0800 Subject: usb: dwc2: add pci_device_id driver_data parse support The dwc2 driver has everything we need to run in PCI mode except for pci_device_id driver_data parse. With that to set Loongson dwc2 element and added identified as PCI_VENDOR_ID_LOONGSON and PCI_DEVICE_ID_LOONGSON_DWC2 in dwc2_pci_ids, the Loongson dwc2 controller will work. Signed-off-by: Yinbo Zhu Link: https://lore.kernel.org/r/20230815065833.3375-1-zhuyinbo@loongson.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/core.h | 1 + drivers/usb/dwc2/params.c | 39 ++++++++++++++++++++++++++++++++++++++- drivers/usb/dwc2/pci.c | 14 +------------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 0bb4c0c845bf..c92a1da46a01 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1330,6 +1330,7 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev); /* The device ID match table */ extern const struct of_device_id dwc2_of_match_table[]; extern const struct acpi_device_id dwc2_acpi_match[]; +extern const struct pci_device_id dwc2_pci_ids[]; int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg); int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg); diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 4c7c3dd15f9b..93f52e371cdd 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -7,9 +7,14 @@ #include #include #include +#include +#include #include "core.h" +#define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0 +#define PCI_DEVICE_ID_LOONGSON_DWC2 0x7a04 + static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg) { struct dwc2_core_params *p = &hsotg->params; @@ -55,6 +60,14 @@ static void dwc2_set_jz4775_params(struct dwc2_hsotg *hsotg) !device_property_read_bool(hsotg->dev, "disable-over-current"); } +static void dwc2_set_loongson_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->phy_utmi_width = 8; + p->power_down = DWC2_POWER_DOWN_PARAM_PARTIAL; +} + static void dwc2_set_x1600_params(struct dwc2_hsotg *hsotg) { struct dwc2_core_params *p = &hsotg->params; @@ -302,6 +315,23 @@ const struct acpi_device_id dwc2_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, dwc2_acpi_match); +const struct pci_device_id dwc2_pci_ids[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_PRODUCT_ID_HAPS_HSOTG), + }, + { + PCI_DEVICE(PCI_VENDOR_ID_STMICRO, + PCI_DEVICE_ID_STMICRO_USB_OTG), + }, + { + PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DWC2), + .driver_data = (unsigned long)dwc2_set_loongson_params, + }, + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, dwc2_pci_ids); +EXPORT_SYMBOL_GPL(dwc2_pci_ids); + static void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg) { switch (hsotg->hw_params.op_mode) { @@ -948,13 +978,20 @@ int dwc2_init_params(struct dwc2_hsotg *hsotg) if (match && match->data) { set_params = match->data; set_params(hsotg); - } else { + } else if (!match) { const struct acpi_device_id *amatch; + const struct pci_device_id *pmatch = NULL; amatch = acpi_match_device(dwc2_acpi_match, hsotg->dev); if (amatch && amatch->driver_data) { set_params = (set_params_cb)amatch->driver_data; set_params(hsotg); + } else if (!amatch) + pmatch = pci_match_id(dwc2_pci_ids, to_pci_dev(hsotg->dev->parent)); + + if (pmatch && pmatch->driver_data) { + set_params = (set_params_cb)pmatch->driver_data; + set_params(hsotg); } } diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c index b7306ed8be4c..f3a1e4232a31 100644 --- a/drivers/usb/dwc2/pci.c +++ b/drivers/usb/dwc2/pci.c @@ -24,7 +24,7 @@ #include #include -#define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0 +#include "core.h" static const char dwc2_driver_name[] = "dwc2-pci"; @@ -122,18 +122,6 @@ err: return ret; } -static const struct pci_device_id dwc2_pci_ids[] = { - { - PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_PRODUCT_ID_HAPS_HSOTG), - }, - { - PCI_DEVICE(PCI_VENDOR_ID_STMICRO, - PCI_DEVICE_ID_STMICRO_USB_OTG), - }, - { /* end: all zeroes */ } -}; -MODULE_DEVICE_TABLE(pci, dwc2_pci_ids); - static struct pci_driver dwc2_pci_driver = { .name = dwc2_driver_name, .id_table = dwc2_pci_ids, -- cgit From 873854c02364ebb991fc06f7148c14dfb5419e1b Mon Sep 17 00:00:00 2001 From: Martin Kohn Date: Thu, 27 Jul 2023 22:23:00 +0000 Subject: USB: serial: option: add Quectel EM05G variant (0x030e) Add Quectel EM05G with product ID 0x030e. Interface 4 is used for qmi. T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=2c7c ProdID=030e Rev= 3.18 S: Manufacturer=Quectel S: Product=Quectel EM05-G C:* #Ifs= 5 Cfg#= 1 Atr=a0 MxPwr=500mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan E: Ad=89(I) Atr=03(Int.) MxPS= 8 Ivl=32ms E: Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Signed-off-by: Martin Kohn Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 288a96a74266..2b0d77c7ee43 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -258,6 +258,7 @@ static void option_instat_callback(struct urb *urb); #define QUECTEL_PRODUCT_EM05G 0x030a #define QUECTEL_PRODUCT_EM060K 0x030b #define QUECTEL_PRODUCT_EM05G_CS 0x030c +#define QUECTEL_PRODUCT_EM05GV2 0x030e #define QUECTEL_PRODUCT_EM05CN_SG 0x0310 #define QUECTEL_PRODUCT_EM05G_SG 0x0311 #define QUECTEL_PRODUCT_EM05CN 0x0312 @@ -1186,6 +1187,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(6) | ZLP }, { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G, 0xff), .driver_info = RSVD(6) | ZLP }, + { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05GV2, 0xff), + .driver_info = RSVD(4) | ZLP }, { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_CS, 0xff), .driver_info = RSVD(6) | ZLP }, { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_GR, 0xff), -- cgit From 4d9488b294e1f8353bbcadc4c7172a7f7490199b Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Wed, 23 Aug 2023 15:57:51 +0800 Subject: USB: serial: option: add FOXCONN T99W368/T99W373 product The difference of T99W368 and T99W373 is the chip solution. T99W368 is designed based on Qualcomm SDX65 and T99W373 is SDX62. Test evidence as below: T: Bus=01 Lev=02 Prnt=05 Port=00 Cnt=01 Dev#= 7 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e0f0 Rev=05.04 S: Manufacturer=FII S: Product=OLYMPIC USB WWAN Adapter S: SerialNumber=78ada8c4 C: #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=500mA I: If#=0x0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim I: If#=0x1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I: If#=0x2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option I: If#=0x3 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) I: If#=0x4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option I: If#=0x5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option T: Bus=01 Lev=02 Prnt=05 Port=00 Cnt=01 Dev#= 8 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e0ee Rev=05.04 S: Manufacturer=FII S: Product=OLYMPIC USB WWAN Adapter S: SerialNumber=78ada8d5 C: #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=500mA I: If#=0x0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim I: If#=0x1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I: If#=0x2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option I: If#=0x3 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) I: If#=0x4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option I: If#=0x5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option Both of them share the same port configuration: 0&1: MBIM, 2: Modem, 3:GNSS, 4:NMEA, 5:Diag GNSS port don't use serial driver. Signed-off-by: Slark Xiao Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 2b0d77c7ee43..7e638cf3d4da 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2229,6 +2229,10 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0db, 0xff), /* Foxconn T99W265 MBIM */ .driver_info = RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0ee, 0xff), /* Foxconn T99W368 MBIM */ + .driver_info = RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0f0, 0xff), /* Foxconn T99W373 MBIM */ + .driver_info = RSVD(3) }, { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */ .driver_info = RSVD(4) | RSVD(5) | RSVD(6) }, { USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */ -- cgit From c97cd0b4b54eb42aed7f6c3c295a2d137f6d2416 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Mon, 31 Jul 2023 16:59:23 +0000 Subject: usb: typec: tcpm: set initial svdm version based on pd revision When sending Discover Identity messages to a Port Partner that uses Power Delivery v2 and SVDM v1, we currently send PD v2 messages with SVDM v2.0, expecting the port partner to respond with its highest supported SVDM version as stated in Section 6.4.4.2.3 in the Power Delivery v3 specification. However, sending SVDM v2 to some Power Delivery v2 port partners results in a NAK whereas sending SVDM v1 does not. NAK messages can be handled by the initiator (PD v3 section 6.4.4.2.5.1), and one solution could be to resend Discover Identity on a lower SVDM version if possible. But, Section 6.4.4.3 of PD v2 states that "A NAK response Should be taken as an indication not to retry that particular Command." Instead, we can set the SVDM version to the maximum one supported by the negotiated PD revision. When operating in PD v2, this obeys Section 6.4.4.2.3, which states the SVDM field "Shall be set to zero to indicate Version 1.0." In PD v3, the SVDM field "Shall be set to 01b to indicate Version 2.0." Fixes: c34e85fa69b9 ("usb: typec: tcpm: Send DISCOVER_IDENTITY from dedicated work") Cc: stable@vger.kernel.org Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230731165926.1815338-1-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index ab7e3d7249ab..df728a066c58 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -3859,6 +3859,29 @@ static enum typec_cc_status tcpm_pwr_opmode_to_rp(enum typec_pwr_opmode opmode) } } +static void tcpm_set_initial_svdm_version(struct tcpm_port *port) +{ + switch (port->negotiated_rev) { + case PD_REV30: + break; + /* + * 6.4.4.2.3 Structured VDM Version + * 2.0 states "At this time, there is only one version (1.0) defined. + * This field Shall be set to zero to indicate Version 1.0." + * 3.0 states "This field Shall be set to 01b to indicate Version 2.0." + * To ensure that we follow the Power Delivery revision we are currently + * operating on, downgrade the SVDM version to the highest one supported + * by the Power Delivery revision. + */ + case PD_REV20: + typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0); + break; + default: + typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0); + break; + } +} + static void run_state_machine(struct tcpm_port *port) { int ret; @@ -4096,10 +4119,12 @@ static void run_state_machine(struct tcpm_port *port) * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using * port->explicit_contract to decide whether to send the command. */ - if (port->explicit_contract) + if (port->explicit_contract) { + tcpm_set_initial_svdm_version(port); mod_send_discover_delayed_work(port, 0); - else + } else { port->send_discover = false; + } /* * 6.3.5 @@ -4388,10 +4413,12 @@ static void run_state_machine(struct tcpm_port *port) * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using * port->explicit_contract. */ - if (port->explicit_contract) + if (port->explicit_contract) { + tcpm_set_initial_svdm_version(port); mod_send_discover_delayed_work(port, 0); - else + } else { port->send_discover = false; + } power_supply_changed(port->psy); break; -- cgit From 2d6d80127006ae3da26b1f21a65eccf957f2d1e5 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Sun, 20 Aug 2023 23:15:18 +0800 Subject: usb: typec: tcpm: reset counter when enter into unattached state after try role The try_src_count and try_snk_count may still be 1 after enter into unattached state. This may be caused by below case: - SNK_TRY->SNK_TRY_WAIT->SRC_TRYWAIT->SNK_UNATTACHED - SRC_TRY->SRC_TRY_WAIT->SNK_TRYWAIT->SNK_UNATTACHED The port->attached is not true at the end and tcpm_reset_port() will not be called. This will reset counter into for these cases, otherwise the tcpm won't try role when new cable attached. Signed-off-by: Xu Yang Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230820151518.1403006-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index df728a066c58..3051b8a8db81 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -3720,6 +3720,9 @@ static void tcpm_detach(struct tcpm_port *port) if (tcpm_port_is_disconnected(port)) port->hard_reset_count = 0; + port->try_src_count = 0; + port->try_snk_count = 0; + if (!port->attached) return; -- cgit From 78e0ea4277546debf7e96797ac3b768539cc44f6 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 20 Aug 2023 04:44:48 +0000 Subject: tcpm: Avoid soft reset when partner does not support get_status When partner does not support get_status message, tcpm right now responds with soft reset message. This causes PD renegotiation to happen and resets PPS link. Avoid soft resetting the link when partner does not support get_status message to mitigate PPS resets. [ 208.926752] Setting voltage/current limit 9500 mV 2450 mA [ 208.930407] set_auto_vbus_discharge_threshold mode:3 pps_active:y vbus:9500 ret:0 [ 208.930418] state change SNK_TRANSITION_SINK -> SNK_READY [rev3 POWER_NEGOTIATION] [ 208.930455] AMS POWER_NEGOTIATION finished // ALERT message from the Source [ 213.948442] PD RX, header: 0x19a6 [1] [ 213.948451] state change SNK_READY -> GET_STATUS_SEND [rev3 GETTING_SOURCE_SINK_STATUS] [ 213.948457] PD TX, header: 0x492 [ 213.950402] PD TX complete, status: 0 [ 213.950427] pending state change GET_STATUS_SEND -> GET_STATUS_SEND_TIMEOUT @ 60 ms [rev3 GETTING_SOURCE_SINK_STATUS] // NOT_SUPPORTED from the Source [ 213.959954] PD RX, header: 0xbb0 [1] // sink sends SOFT_RESET [ 213.959958] state change GET_STATUS_SEND -> SNK_SOFT_RESET [rev3 GETTING_SOURCE_SINK_STATUS] [ 213.959962] AMS GETTING_SOURCE_SINK_STATUS finished [ 213.959964] AMS SOFT_RESET_AMS start [ 213.959966] state change SNK_SOFT_RESET -> AMS_START [rev3 SOFT_RESET_AMS] [ 213.959969] state change AMS_START -> SOFT_RESET_SEND [rev3 SOFT_RESET_AMS] Cc: stable@vger.kernel.org Fixes: 8dea75e11380 ("usb: typec: tcpm: Protocol Error handling") Signed-off-by: Badhri Jagan Sridharan Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230820044449.1005889-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 3051b8a8db81..d962f67c95ae 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2754,6 +2754,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, port->sink_cap_done = true; tcpm_set_state(port, ready_state(port), 0); break; + /* + * Some port partners do not support GET_STATUS, avoid soft reset the link to + * prevent redundant power re-negotiation + */ + case GET_STATUS_SEND: + tcpm_set_state(port, ready_state(port), 0); + break; case SRC_READY: case SNK_READY: if (port->vdm_state > VDM_STATE_READY) { -- cgit From aad6ad1b780aea3928437ffd23cc8b3e42b7ac8a Mon Sep 17 00:00:00 2001 From: Utkarsh Patel Date: Mon, 17 Jul 2023 19:47:02 -0700 Subject: platform/chrome: cros_ec_typec: Configure Retimer cable type Connector class driver only configure cable type active or passive. Configure if the cable type is retimer or redriver with this change. This detail will be provided as a part of cable discover mode VDO. Signed-off-by: Utkarsh Patel Acked-by: Prashant Malani Link: https://lore.kernel.org/r/20230718024703.1013367-2-utkarsh.h.patel@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/platform/chrome/cros_ec_typec.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 25f9767c28e8..d0b4d3fc40ed 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -406,6 +406,27 @@ static int cros_typec_usb_safe_state(struct cros_typec_port *port) return ret; } +/** + * cros_typec_get_cable_vdo() - Get Cable VDO of the connected cable + * @port: Type-C port data + * @svid: Standard or Vendor ID to match + * + * Returns the Cable VDO if match is found and returns 0 if match is not found. + */ +static int cros_typec_get_cable_vdo(struct cros_typec_port *port, u16 svid) +{ + struct list_head *head = &port->plug_mode_list; + struct cros_typec_altmode_node *node; + u32 ret = 0; + + list_for_each_entry(node, head, list) { + if (node->amode->svid == svid) + return node->amode->vdo; + } + + return ret; +} + /* * Spoof the VDOs that were likely communicated by the partner for TBT alt * mode. @@ -432,6 +453,9 @@ static int cros_typec_enable_tbt(struct cros_typec_data *typec, /* Cable Discover Mode VDO */ data.cable_mode = TBT_MODE; + + data.cable_mode |= cros_typec_get_cable_vdo(port, USB_TYPEC_TBT_SID); + data.cable_mode |= TBT_SET_CABLE_SPEED(pd_ctrl->cable_speed); if (pd_ctrl->control_flags & USB_PD_CTRL_OPTICAL_CABLE) @@ -522,8 +546,10 @@ static int cros_typec_enable_usb4(struct cros_typec_data *typec, /* Cable Type */ if (pd_ctrl->control_flags & USB_PD_CTRL_OPTICAL_CABLE) data.eudo |= EUDO_CABLE_TYPE_OPTICAL << EUDO_CABLE_TYPE_SHIFT; - else if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_CABLE) + else if (cros_typec_get_cable_vdo(port, USB_TYPEC_TBT_SID) & TBT_CABLE_RETIMER) data.eudo |= EUDO_CABLE_TYPE_RE_TIMER << EUDO_CABLE_TYPE_SHIFT; + else if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_CABLE) + data.eudo |= EUDO_CABLE_TYPE_RE_DRIVER << EUDO_CABLE_TYPE_SHIFT; data.active_link_training = !!(pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_LINK_UNIDIR); -- cgit