From 655b8af57d31e07340b023a807e43dadf81a0818 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 11 Nov 2023 20:15:43 +0000 Subject: [PATCH 001/169] thunderbolt: Remove duplicated re-assignment of pointer 'out' The pointer 'out' is initialized and then a few statements later being re-assigned the same value. The second re-assignment is redundant and can be removed. Signed-off-by: Colin Ian King Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 7534cd3a81f4..8d45cb4f04ab 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -1313,8 +1313,6 @@ static void tb_dp_dump(struct tb_tunnel *tunnel) "DP IN maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n", rate, lanes, tb_dp_bandwidth(rate, lanes)); - out = tunnel->dst_port; - if (tb_port_read(out, &dp_cap, TB_CFG_PORT, out->cap_adap + DP_LOCAL_CAP, 1)) return; From f9cdf40ed66b7abc4fc03c5b47725583e5dc1072 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 11 Nov 2023 20:26:56 +0000 Subject: [PATCH 002/169] USB: misc: iowarrior: remove redundant assignment to variable io_res The variable io_res is being assigned a value that is never read, it is either being re-assigned a new value that is read later or it's not used depending on the cases in the following switch statement. The assignment is redundant and can be removed. Cleans up clang scan build warning: drivers/usb/misc/iowarrior.c:504:2: warning: Value stored to 'io_res' is never read [deadcode.DeadStores] Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20231111202656.339103-1-colin.i.king@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/iowarrior.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 1e3df27bab58..6d28467ce352 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -501,7 +501,6 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, dev->minor, cmd, arg); retval = 0; - io_res = 0; switch (cmd) { case IOW_WRITE: if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 || From b9a24821c7f7f8f8fbc9fc8539de96b60422d817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 3 Nov 2023 18:14:29 +0100 Subject: [PATCH 003/169] USB: usbip: vudc: 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() will be 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 Link: https://lore.kernel.org/r/20231103171428.3636570-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc.h | 2 +- drivers/usb/usbip/vudc_dev.c | 3 +-- drivers/usb/usbip/vudc_main.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h index 1bd4bc005829..faf61c9c6a98 100644 --- a/drivers/usb/usbip/vudc.h +++ b/drivers/usb/usbip/vudc.h @@ -173,6 +173,6 @@ struct vudc_device *alloc_vudc_device(int devid); void put_vudc_device(struct vudc_device *udc_dev); int vudc_probe(struct platform_device *pdev); -int vudc_remove(struct platform_device *pdev); +void vudc_remove(struct platform_device *pdev); #endif /* __USBIP_VUDC_H */ diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index 44b04c54c086..f11535020e35 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -628,12 +628,11 @@ out: return ret; } -int vudc_remove(struct platform_device *pdev) +void vudc_remove(struct platform_device *pdev) { struct vudc *udc = platform_get_drvdata(pdev); usb_del_gadget_udc(&udc->gadget); cleanup_vudc_hw(udc); kfree(udc); - return 0; } diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c index 993e721cb840..8bee553e4894 100644 --- a/drivers/usb/usbip/vudc_main.c +++ b/drivers/usb/usbip/vudc_main.c @@ -19,7 +19,7 @@ MODULE_PARM_DESC(num, "number of emulated controllers"); static struct platform_driver vudc_driver = { .probe = vudc_probe, - .remove = vudc_remove, + .remove_new = vudc_remove, .driver = { .name = GADGET_NAME, .dev_groups = vudc_groups, From cbd1b152519a581bfe14fb6b14ea6e57cca426c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 20 Nov 2023 22:58:32 +0100 Subject: [PATCH 004/169] usb: gadget: at91_udc: 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() will be renamed to .remove(). In the error path emit an error message replacing the (less useful) message by the core. Apart from the improved error message there is no change in behaviour. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20231120215830.71071-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/at91_udc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 30ea4a9d5301..e3bf17a98b38 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1924,7 +1924,7 @@ err_unprepare_fclk: return retval; } -static int at91udc_remove(struct platform_device *pdev) +static void at91udc_remove(struct platform_device *pdev) { struct at91_udc *udc = platform_get_drvdata(pdev); unsigned long flags; @@ -1932,8 +1932,11 @@ static int at91udc_remove(struct platform_device *pdev) DBG("remove\n"); usb_del_gadget_udc(&udc->gadget); - if (udc->driver) - return -EBUSY; + if (udc->driver) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } spin_lock_irqsave(&udc->lock, flags); pullup(udc, 0); @@ -1943,8 +1946,6 @@ static int at91udc_remove(struct platform_device *pdev) remove_debug_file(udc); clk_unprepare(udc->fclk); clk_unprepare(udc->iclk); - - return 0; } #ifdef CONFIG_PM @@ -2001,7 +2002,7 @@ static int at91udc_resume(struct platform_device *pdev) static struct platform_driver at91_udc_driver = { .probe = at91udc_probe, - .remove = at91udc_remove, + .remove_new = at91udc_remove, .shutdown = at91udc_shutdown, .suspend = at91udc_suspend, .resume = at91udc_resume, From 103081ef40b6738d3052a41f419705517a0f9436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 20 Nov 2023 22:58:33 +0100 Subject: [PATCH 005/169] usb: gadget: fsl_udc: 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() will be renamed to .remove(). In the error path emit an error message replacing the (less useful) message by the core. Apart from the improved error message there is no change in behaviour. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20231120215830.71071-3-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_udc_core.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 2693a10eb0c7..535b6e79a198 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2532,15 +2532,18 @@ err_kfree: /* Driver removal function * Free resources and finish pending transactions */ -static int fsl_udc_remove(struct platform_device *pdev) +static void fsl_udc_remove(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); DECLARE_COMPLETION_ONSTACK(done); - if (!udc_controller) - return -ENODEV; + if (!udc_controller) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } udc_controller->done = &done; usb_del_gadget_udc(&udc_controller->gadget); @@ -2568,8 +2571,6 @@ static int fsl_udc_remove(struct platform_device *pdev) */ if (pdata->exit) pdata->exit(pdev); - - return 0; } /*----------------------------------------------------------------- @@ -2667,7 +2668,7 @@ static const struct platform_device_id fsl_udc_devtype[] = { MODULE_DEVICE_TABLE(platform, fsl_udc_devtype); static struct platform_driver udc_driver = { .probe = fsl_udc_probe, - .remove = fsl_udc_remove, + .remove_new = fsl_udc_remove, .id_table = fsl_udc_devtype, /* these suspend and resume are not usb suspend and resume */ .suspend = fsl_udc_suspend, From c45b52f71c4b3b826c18d7a5c7a40c139e8312fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 20 Nov 2023 22:58:34 +0100 Subject: [PATCH 006/169] usb: gadget: gr_udc: 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() will be renamed to .remove(). In the error path emit an error message replacing the (less useful) message by the core. Apart from the improved error message there is no change in behaviour. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20231120215830.71071-4-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/gr_udc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index c6dfa7cccc11..fb901be5dac1 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2089,15 +2089,18 @@ static void gr_ep_remove(struct gr_udc *dev, int num, int is_in) ep->tailbuf, ep->tailbuf_paddr); } -static int gr_remove(struct platform_device *pdev) +static void gr_remove(struct platform_device *pdev) { struct gr_udc *dev = platform_get_drvdata(pdev); int i; if (dev->added) usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */ - if (dev->driver) - return -EBUSY; + if (dev->driver) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } gr_dfs_delete(dev); dma_pool_destroy(dev->desc_pool); @@ -2110,8 +2113,6 @@ static int gr_remove(struct platform_device *pdev) gr_ep_remove(dev, i, 0); for (i = 0; i < dev->nepi; i++) gr_ep_remove(dev, i, 1); - - return 0; } static int gr_request_irq(struct gr_udc *dev, int irq) { @@ -2248,7 +2249,7 @@ static struct platform_driver gr_driver = { .of_match_table = gr_match, }, .probe = gr_probe, - .remove = gr_remove, + .remove_new = gr_remove, }; module_platform_driver(gr_driver); From 5d888fee4adefbc24bad8eb1091238090ac35e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 20 Nov 2023 22:58:35 +0100 Subject: [PATCH 007/169] usb: gadget: lpc32xx_udc: 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() will be renamed to .remove(). In the error path emit an error message replacing the (less useful) message by the core. Apart from the improved error message there is no change in behaviour. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20231120215830.71071-5-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/lpc32xx_udc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index a917cc9a32ab..d5f29f8fe481 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3174,13 +3174,16 @@ i2c_fail: return retval; } -static int lpc32xx_udc_remove(struct platform_device *pdev) +static void lpc32xx_udc_remove(struct platform_device *pdev) { struct lpc32xx_udc *udc = platform_get_drvdata(pdev); usb_del_gadget_udc(&udc->gadget); - if (udc->driver) - return -EBUSY; + if (udc->driver) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } udc_clk_set(udc, 1); udc_disable(udc); @@ -3194,8 +3197,6 @@ static int lpc32xx_udc_remove(struct platform_device *pdev) udc->udca_v_base, udc->udca_p_base); clk_disable_unprepare(udc->usb_slv_clk); - - return 0; } #ifdef CONFIG_PM @@ -3255,7 +3256,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match); static struct platform_driver lpc32xx_udc_driver = { .probe = lpc32xx_udc_probe, - .remove = lpc32xx_udc_remove, + .remove_new = lpc32xx_udc_remove, .shutdown = lpc32xx_udc_shutdown, .suspend = lpc32xx_udc_suspend, .resume = lpc32xx_udc_resume, From 725d1f1e338bfa0c133e38989d795484534fb9ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 20 Nov 2023 22:58:36 +0100 Subject: [PATCH 008/169] usb: gadget: pxa25x_udc: 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() will be renamed to .remove(). In the error path emit an error message replacing the (less useful) message by the core. Apart from the improved error message there is no change in behaviour. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20231120215830.71071-6-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/pxa25x_udc.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index df0551ecc810..1ac26cb49ecf 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -2397,12 +2397,15 @@ static void pxa25x_udc_shutdown(struct platform_device *_dev) pullup_off(); } -static int pxa25x_udc_remove(struct platform_device *pdev) +static void pxa25x_udc_remove(struct platform_device *pdev) { struct pxa25x_udc *dev = platform_get_drvdata(pdev); - if (dev->driver) - return -EBUSY; + if (dev->driver) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } usb_del_gadget_udc(&dev->gadget); dev->pullup = 0; @@ -2414,7 +2417,6 @@ static int pxa25x_udc_remove(struct platform_device *pdev) dev->transceiver = NULL; the_controller = NULL; - return 0; } /*-------------------------------------------------------------------------*/ @@ -2472,7 +2474,7 @@ static int pxa25x_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .shutdown = pxa25x_udc_shutdown, .probe = pxa25x_udc_probe, - .remove = pxa25x_udc_remove, + .remove_new = pxa25x_udc_remove, .suspend = pxa25x_udc_suspend, .resume = pxa25x_udc_resume, .driver = { From 2d1803ada2e021b9333b8a784c55fe828ab2b985 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 16 Nov 2023 11:14:52 -0800 Subject: [PATCH 009/169] usb: gadget: f_midi: Replace strlcpy() with strscpy() strlcpy() reads the entire source buffer first. This read may exceed the destination size limit. This is both inefficient and can lead to linear read overflows if a source string is not NUL-terminated[1]. Additionally, it returns the size of the source string, not the resulting size of the destination string. In an effort to remove strlcpy() completely[2], replace strlcpy() here with strscpy(). In the unlikely (impossible?) case where opts->id was larger than PAGE_SIZE, this will now correctly report truncation errors. Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strlcpy [1] Link: https://github.com/KSPP/linux/issues/89 [2] Cc: Greg Kroah-Hartman Cc: Will McVicker Cc: "Gustavo A. R. Silva" Cc: Peter Chen Cc: Davidlohr Bueso Cc: Zhang Qilong Cc: Linyu Yuan Cc: John Keeping Cc: Azeem Shaikh Cc: linux-usb@vger.kernel.org Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20231116191452.work.902-kees@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_midi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 5335845d697b..20c6fbd94f32 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1177,11 +1177,11 @@ F_MIDI_OPT(out_ports, true, MAX_PORTS); static ssize_t f_midi_opts_id_show(struct config_item *item, char *page) { struct f_midi_opts *opts = to_f_midi_opts(item); - int result; + ssize_t result; mutex_lock(&opts->lock); if (opts->id) { - result = strlcpy(page, opts->id, PAGE_SIZE); + result = strscpy(page, opts->id, PAGE_SIZE); } else { page[0] = 0; result = 0; From d990e227741692079872a8336d961ef4ab9e8aaf Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 15 Nov 2023 21:14:07 +0000 Subject: [PATCH 010/169] dt-bindings: usb: renesas,usbhs: Document RZ/Five SoC The USBHS IP block on the RZ/Five SoC is identical to one found on the RZ/G2UL SoC. "renesas,usbhs-r9a07g043" compatible string will be used on the RZ/Five SoC so to make this clear and to keep this file consistent, update the comment to include RZ/Five SoC. No driver changes are required as generic compatible string "renesas,rza2-usbhs" will be used as a fallback on RZ/Five SoC. Signed-off-by: Lad Prabhakar Acked-by: Conor Dooley Reviewed-by: Geert Uytterhoeven Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20231115211407.32067-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/renesas,usbhs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml index bad55dfb2fa0..40ada78f2328 100644 --- a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml +++ b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml @@ -19,7 +19,7 @@ properties: - items: - enum: - renesas,usbhs-r7s9210 # RZ/A2 - - renesas,usbhs-r9a07g043 # RZ/G2UL + - renesas,usbhs-r9a07g043 # RZ/G2UL and RZ/Five - renesas,usbhs-r9a07g044 # RZ/G2{L,LC} - renesas,usbhs-r9a07g054 # RZ/V2L - const: renesas,rza2-usbhs From 6437760accfbaf1160342adeaea1a73dcd278799 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Thu, 2 Nov 2023 07:51:13 +0000 Subject: [PATCH 011/169] usb: misc: eud: Add IRQ check for platform_get_irq() The function eud_probe() should check the return value of platform_get_irq() for errors so as to not pass a negative value to the devm_request_threaded_irq(). Signed-off-by: Chen Ni Reviewed-by: Caleb Connolly Link: https://lore.kernel.org/r/20231102075113.1043358-1-nichen@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/qcom_eud.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c index 7f371ea1248c..26e9b8749d8a 100644 --- a/drivers/usb/misc/qcom_eud.c +++ b/drivers/usb/misc/qcom_eud.c @@ -205,6 +205,9 @@ static int eud_probe(struct platform_device *pdev) return PTR_ERR(chip->mode_mgr); chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) + return chip->irq; + ret = devm_request_threaded_irq(&pdev->dev, chip->irq, handle_eud_irq, handle_eud_irq_thread, IRQF_ONESHOT, NULL, chip); if (ret) From a776452debdcad6d6e6f8f89fb26496038c86074 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Thu, 2 Nov 2023 12:36:03 +0530 Subject: [PATCH 012/169] usb: chipidea: udc: Add revision check of 2.20[CI_REVISION_22] Issue: Adding a dTD to a Primed Endpoint May Not Get Recognized with revision 2.20a. There is an issue with the add dTD tripwire semaphore (ATDTW bit in USBCMD register) that can cause the controller to ignore a dTD that is added to a primed endpoint. When this happens, the software can read the tripwire bit and the status bit at '1' even though the endpoint is unprimed. This issue observed with the Windows host machine. Workaround: The software must implement a periodic cycle, and check for each dTD pending on execution (Active = 1), if the endpoint is primed. It can do this by reading the corresponding bits in the ENDPTPRIME and ENDPTSTAT registers. If these bits are read at 0, the software needs to re-prime the endpoint by writing 1 to the corresponding bit in the ENDPTPRIME register. Added conditional revision check of 2.20[CI_REVISION_22] along with 2.40. Signed-off-by: Piyush Mehta Acked-by: Peter Chen Link: https://lore.kernel.org/r/20231102070603.777313-1-piyush.mehta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 0b7bd3c643c3..2d7f616270c1 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -688,7 +688,8 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) if ((TD_STATUS_ACTIVE & tmptoken) != 0) { int n = hw_ep_bit(hwep->num, hwep->dir); - if (ci->rev == CI_REVISION_24) + if (ci->rev == CI_REVISION_24 || + ci->rev == CI_REVISION_22) if (!hw_read(ci, OP_ENDPTSTAT, BIT(n))) reprime_dtd(ci, hwep, node); hwreq->req.status = -EALREADY; From 7836be3b100cfc6db3db7ddeb985254bfe1775e4 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Fri, 27 Oct 2023 14:39:19 -0400 Subject: [PATCH 013/169] usb: cdns3: skip set TRB_IOC when usb_request: no_interrupt is true No completion irq is needed if no_interrupt is true. Needn't set TRB_IOC at this case. Check usb_request: no_interrupt and set/skip TRB_IOC in cdns3_ep_run_transfer(). Signed-off-by: Frank Li Acked-by: Peter Chen Link: https://lore.kernel.org/r/20231027183919.664271-1-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 11a5b3437c32..15463b7cddd2 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -1126,6 +1126,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, u16 total_tdl = 0; struct scatterlist *s = NULL; bool sg_supported = !!(request->num_mapped_sgs); + u32 ioc = request->no_interrupt ? 0 : TRB_IOC; if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) num_trb = priv_ep->interval; @@ -1235,11 +1236,11 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, control |= pcs; if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) { - control |= TRB_IOC | TRB_ISP; + control |= ioc | TRB_ISP; } else { /* for last element in TD or in SG list */ if (sg_iter == (num_trb - 1) && sg_iter != 0) - control |= pcs | TRB_IOC | TRB_ISP; + control |= pcs | ioc | TRB_ISP; } if (sg_iter) @@ -1270,7 +1271,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, priv_req->num_of_trb = num_trb; if (sg_iter == 1) - trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP); + trb->control |= cpu_to_le32(ioc | TRB_ISP); if (priv_dev->dev_ver < DEV_VER_V2 && (priv_ep->flags & EP_TDLCHK_EN)) { From 68a1317412e19cdbc0cbbb9474013adfa1aa986f Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Mon, 30 Oct 2023 23:31:58 +0100 Subject: [PATCH 014/169] usb: raw-gadget: update documentation A recent patch added reporting of more event types to Raw Gadget. Update the documentation to reflect that this feature has been implemented. Signed-off-by: Andrey Konovalov Link: https://lore.kernel.org/r/20231030223158.36636-1-andrey.konovalov@linux.dev Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/raw-gadget.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/usb/raw-gadget.rst b/Documentation/usb/raw-gadget.rst index 818a1648b387..59b2132b584d 100644 --- a/Documentation/usb/raw-gadget.rst +++ b/Documentation/usb/raw-gadget.rst @@ -81,9 +81,6 @@ feature must be kept in the implementation. Potential future improvements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Report more events (suspend, resume, etc.) through - ``USB_RAW_IOCTL_EVENT_FETCH``. - - Support ``O_NONBLOCK`` I/O. This would be another mode of operation, where Raw Gadget would not wait until the completion of each USB request. From 5389b5d74efb6da22b01cdd91105bffc5f71dbdc Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 30 Oct 2023 10:41:59 +0100 Subject: [PATCH 015/169] dt-bindings: usb: qcom,dwc3: document the SM8560 SuperSpeed DWC3 USB controller Document the SuperSpeed DWC3 USB controller on the SM8650 Platform. Signed-off-by: Neil Armstrong Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20231030-topic-sm8650-upstream-bindings-dwc3-v2-1-60c0824fb835@linaro.org 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 e889158ca205..ccce57e4290d 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -46,6 +46,7 @@ properties: - qcom,sm8350-dwc3 - qcom,sm8450-dwc3 - qcom,sm8550-dwc3 + - qcom,sm8650-dwc3 - const: qcom,dwc3 reg: @@ -318,6 +319,7 @@ allOf: - qcom,sm8250-dwc3 - qcom,sm8450-dwc3 - qcom,sm8550-dwc3 + - qcom,sm8650-dwc3 then: properties: clocks: @@ -377,6 +379,7 @@ allOf: - qcom,sm8350-dwc3 - qcom,sm8450-dwc3 - qcom,sm8550-dwc3 + - qcom,sm8650-dwc3 then: properties: interrupts: From 9768af12edb3b23cfa1f7c05e019115b73f9490c Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Fri, 27 Oct 2023 14:29:55 +0200 Subject: [PATCH 016/169] usb: phy: generic: add suspend support for regulator Disable the vcc regulator on suspend and enable it on resume. Signed-off-by: Stefan Eichenberger Signed-off-by: Francesco Dolcini Link: https://lore.kernel.org/r/20231027122955.22123-1-francesco@dolcini.it Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-generic.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 770081b828a4..9ab50f26db60 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -46,15 +46,21 @@ EXPORT_SYMBOL_GPL(usb_phy_generic_unregister); static int nop_set_suspend(struct usb_phy *x, int suspend) { struct usb_phy_generic *nop = dev_get_drvdata(x->dev); + int ret = 0; - if (!IS_ERR(nop->clk)) { - if (suspend) + if (suspend) { + if (!IS_ERR(nop->clk)) clk_disable_unprepare(nop->clk); - else + if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev)) + ret = regulator_disable(nop->vcc); + } else { + if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev)) + ret = regulator_enable(nop->vcc); + if (!IS_ERR(nop->clk)) clk_prepare_enable(nop->clk); } - return 0; + return ret; } static void nop_reset(struct usb_phy_generic *nop) From e0cc05d52ad310cced029449bcda0f9fc847097c Mon Sep 17 00:00:00 2001 From: Guan-Yu Lin Date: Thu, 16 Nov 2023 16:32:16 +0800 Subject: [PATCH 017/169] usb: typec: tcpm: skip checking port->send_discover in PD3.0 The original Collison Avoidance mechanism, port->send_discover, avoids the conflict when port partners start AMS almost the same time. However, this mechanism is replaced by SINK_TX_OK and SINK_TX_NG. Skip the check in PD3.0 to avoid the deadlock when source is requesting DR_SWAP where sink is requesting DISCOVER_IDENTITY. Signed-off-by: Guan-Yu Lin Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20231116083221.1201892-1-guanyulin@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 058d5b853b57..ff3c171a3a75 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2847,7 +2847,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); } else { - if (port->send_discover) { + if (port->send_discover && port->negotiated_rev < PD_REV30) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -2863,7 +2863,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); } else { - if (port->send_discover) { + if (port->send_discover && port->negotiated_rev < PD_REV30) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -2872,7 +2872,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, } break; case PD_CTRL_VCONN_SWAP: - if (port->send_discover) { + if (port->send_discover && port->negotiated_rev < PD_REV30) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } From d1dbd6987ee10f44a06c46bcfade69cdbb56a5cd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Nov 2023 15:19:53 +0100 Subject: [PATCH 018/169] dt-bindings: usb: qcom,dwc3: adjust number of interrupts on SM6125 Qualcomm SM6125 DWC3 USB controller comes with two interrupts (verified with downstream/vendor code of Trinket DTSI from Xiaomi Laurel device). Move the qcom,sm6125-dwc3 to appropriate place in allOf:if:then blocks constraining interrupts. Signed-off-by: Krzysztof Kozlowski Acked-by: Conor Dooley Reviewed-by: Marijn Suijten Link: https://lore.kernel.org/r/20231111141953.51841-1-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/qcom,dwc3.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index ccce57e4290d..f6339e8e92d9 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -372,7 +372,6 @@ allOf: - qcom,sdx65-dwc3 - qcom,sdx75-dwc3 - qcom,sm4250-dwc3 - - qcom,sm6125-dwc3 - qcom,sm6350-dwc3 - qcom,sm8150-dwc3 - qcom,sm8250-dwc3 @@ -406,6 +405,7 @@ allOf: - qcom,msm8996-dwc3 - qcom,msm8998-dwc3 - qcom,sm6115-dwc3 + - qcom,sm6125-dwc3 then: properties: interrupts: From a769154c7cac037914ba375ae88aae55b2c853e0 Mon Sep 17 00:00:00 2001 From: Hardik Gajjar Date: Fri, 27 Oct 2023 17:20:28 +0200 Subject: [PATCH 019/169] usb: xhci: Add timeout argument in address_device USB HCD callback - The HCD address_device callback now accepts a user-defined timeout value in milliseconds, providing better control over command execution times. - The default timeout value for the address_device command has been set to 5000 ms, aligning with the USB 3.2 specification. However, this timeout can be adjusted as needed. - The xhci_setup_device function has been updated to accept the timeout value, allowing it to specify the maximum wait time for the command operation to complete. - The hub driver has also been updated to accommodate the newly added timeout parameter during the SET_ADDRESS request. Signed-off-by: Hardik Gajjar Reviewed-by: Mathias Nyman Link: https://lore.kernel.org/r/20231027152029.104363-1-hgajjar@de.adit-jv.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- drivers/usb/host/xhci-mem.c | 2 ++ drivers/usb/host/xhci-ring.c | 11 ++++++----- drivers/usb/host/xhci.c | 23 ++++++++++++++++------- drivers/usb/host/xhci.h | 9 +++++++-- include/linux/usb/hcd.h | 5 +++-- 6 files changed, 35 insertions(+), 17 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b4584a0cd484..049058264367 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4662,7 +4662,7 @@ static int hub_set_address(struct usb_device *udev, int devnum) if (udev->state != USB_STATE_DEFAULT) return -EINVAL; if (hcd->driver->address_device) - retval = hcd->driver->address_device(hcd, udev); + retval = hcd->driver->address_device(hcd, udev, USB_CTRL_SET_TIMEOUT); else retval = usb_control_msg(udev, usb_sndaddr0pipe(), USB_REQ_SET_ADDRESS, 0, devnum, 0, diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 62116586848b..6faa854152ef 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1739,6 +1739,8 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, } command->status = 0; + /* set default timeout to 5000 ms */ + command->timeout_ms = XHCI_CMD_DEFAULT_TIMEOUT; INIT_LIST_HEAD(&command->cmd_list); return command; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f3b5e6345858..2c1d614b3b0f 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -366,9 +366,10 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) readl(&xhci->dba->doorbell[0]); } -static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay) +static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci) { - return mod_delayed_work(system_wq, &xhci->cmd_timer, delay); + return mod_delayed_work(system_wq, &xhci->cmd_timer, + msecs_to_jiffies(xhci->current_cmd->timeout_ms)); } static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci) @@ -412,7 +413,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) && !(xhci->xhc_state & XHCI_STATE_DYING)) { xhci->current_cmd = cur_cmd; - xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci); xhci_ring_cmd_db(xhci); } } @@ -1787,7 +1788,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, if (!list_is_singular(&xhci->cmd_list)) { xhci->current_cmd = list_first_entry(&cmd->cmd_list, struct xhci_command, cmd_list); - xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci); } else if (xhci->current_cmd == cmd) { xhci->current_cmd = NULL; } @@ -4287,7 +4288,7 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, /* if there are no other commands queued we start the timeout timer */ if (list_empty(&xhci->cmd_list)) { xhci->current_cmd = cmd; - xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci); } list_add_tail(&cmd->cmd_list, &xhci->cmd_list); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 884b0898d9c9..cb885ddc2032 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4029,12 +4029,18 @@ disable_slot: return 0; } -/* - * Issue an Address Device command and optionally send a corresponding - * SetAddress request to the device. +/** + * xhci_setup_device - issues an Address Device command to assign a unique + * USB bus address. + * @hcd: USB host controller data structure. + * @udev: USB dev structure representing the connected device. + * @setup: Enum specifying setup mode: address only or with context. + * @timeout_ms: Max wait time (ms) for the command operation to complete. + * + * Return: 0 if successful; otherwise, negative error code. */ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, - enum xhci_setup_dev setup) + enum xhci_setup_dev setup, unsigned int timeout_ms) { const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address"; unsigned long flags; @@ -4091,6 +4097,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, } command->in_ctx = virt_dev->in_ctx; + command->timeout_ms = timeout_ms; slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); @@ -4217,14 +4224,16 @@ out: return ret; } -static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) +static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev, + unsigned int timeout_ms) { - return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS); + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS, timeout_ms); } static int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev) { - return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY); + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY, + XHCI_CMD_DEFAULT_TIMEOUT); } /* diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3ea5c092bba7..62b610f00754 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -791,6 +791,8 @@ struct xhci_command { struct completion *completion; union xhci_trb *command_trb; struct list_head cmd_list; + /* xHCI command response timeout in milliseconds */ + unsigned int timeout_ms; }; /* drop context bitmasks */ @@ -1550,8 +1552,11 @@ struct xhci_td { unsigned int num_trbs; }; -/* xHCI command default timeout value */ -#define XHCI_CMD_DEFAULT_TIMEOUT (5 * HZ) +/* + * xHCI command default timeout value in milliseconds. + * USB 3.2 spec, section 9.2.6.1 + */ +#define XHCI_CMD_DEFAULT_TIMEOUT 5000 /* command descriptor */ struct xhci_cd { diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 00724b4f6e12..cd77fc6095a1 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -372,8 +372,9 @@ struct hc_driver { * or bandwidth constraints. */ void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); - /* Returns the hardware-chosen device address */ - int (*address_device)(struct usb_hcd *, struct usb_device *udev); + /* Set the hardware-chosen device address */ + int (*address_device)(struct usb_hcd *, struct usb_device *udev, + unsigned int timeout_ms); /* prepares the hardware to send commands to the device */ int (*enable_device)(struct usb_hcd *, struct usb_device *udev); /* Notifies the HCD after a hub descriptor is fetched. From 5a1ccf0c72cf917ff3ccc131d1bb8d19338ffe52 Mon Sep 17 00:00:00 2001 From: Hardik Gajjar Date: Fri, 27 Oct 2023 17:20:29 +0200 Subject: [PATCH 020/169] usb: new quirk to reduce the SET_ADDRESS request timeout This patch introduces a new USB quirk, USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT, which modifies the timeout value for the SET_ADDRESS request. The standard timeout for USB request/command is 5000 ms, as recommended in the USB 3.2 specification (section 9.2.6.1). However, certain scenarios, such as connecting devices through an APTIV hub, can lead to timeout errors when the device enumerates as full speed initially and later switches to high speed during chirp negotiation. In such cases, USB analyzer logs reveal that the bus suspends for 5 seconds due to incorrect chirp parsing and resumes only after two consecutive timeout errors trigger a hub driver reset. Packet(54) Dir(?) Full Speed J(997.100 us) Idle( 2.850 us) _______| Time Stamp(28 . 105 910 682) _______|_____________________________________________________________Ch0 Packet(55) Dir(?) Full Speed J(997.118 us) Idle( 2.850 us) _______| Time Stamp(28 . 106 910 632) _______|_____________________________________________________________Ch0 Packet(56) Dir(?) Full Speed J(399.650 us) Idle(222.582 us) _______| Time Stamp(28 . 107 910 600) _______|_____________________________________________________________Ch0 Packet(57) Dir Chirp J( 23.955 ms) Idle(115.169 ms) _______| Time Stamp(28 . 108 532 832) _______|_____________________________________________________________Ch0 Packet(58) Dir(?) Full Speed J (Suspend)( 5.347 sec) Idle( 5.366 us) _______| Time Stamp(28 . 247 657 600) _______|_____________________________________________________________Ch0 This 5-second delay in device enumeration is undesirable, particularly in automotive applications where quick enumeration is crucial (ideally within 3 seconds). The newly introduced quirks provide the flexibility to align with a 3-second time limit, as required in specific contexts like automotive applications. By reducing the SET_ADDRESS request timeout to 500 ms, the system can respond more swiftly to errors, initiate rapid recovery, and ensure efficient device enumeration. This change is vital for scenarios where rapid smartphone enumeration and screen projection are essential. To use the quirk, please write "vendor_id:product_id:p" to /sys/bus/usb/drivers/hub/module/parameter/quirks For example, echo "0x2c48:0x0132:p" > /sys/bus/usb/drivers/hub/module/parameters/quirks" Signed-off-by: Hardik Gajjar Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20231027152029.104363-2-hgajjar@de.adit-jv.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/kernel-parameters.txt | 3 +++ drivers/usb/core/hub.c | 15 +++++++++++++-- drivers/usb/core/quirks.c | 7 +++++++ include/linux/usb/quirks.h | 3 +++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 65731b060e3f..0a6a4b7f7a3b 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6908,6 +6908,9 @@ pause after every control message); o = USB_QUIRK_HUB_SLOW_RESET (Hub needs extra delay after resetting its port); + p = USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT + (Reduce timeout of the SET_ADDRESS + request from 5000 ms to 500 ms); Example: quirks=0781:5580:bk,0a5c:5834:gij usbhid.mousepoll= diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 049058264367..756fbd7cac43 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -54,6 +54,12 @@ #define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */ #define USB_PING_RESPONSE_TIME 400 /* ns */ +/* + * The SET_ADDRESS request timeout will be 500 ms when + * USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT quirk flag is set. + */ +#define USB_SHORT_SET_ADDRESS_REQ_TIMEOUT 500 /* ms */ + /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ @@ -4649,7 +4655,12 @@ EXPORT_SYMBOL_GPL(usb_ep0_reinit); static int hub_set_address(struct usb_device *udev, int devnum) { int retval; + unsigned int timeout_ms = USB_CTRL_SET_TIMEOUT; struct usb_hcd *hcd = bus_to_hcd(udev->bus); + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); + + if (hub->hdev->quirks & USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT) + timeout_ms = USB_SHORT_SET_ADDRESS_REQ_TIMEOUT; /* * The host controller will choose the device address, @@ -4662,11 +4673,11 @@ static int hub_set_address(struct usb_device *udev, int devnum) if (udev->state != USB_STATE_DEFAULT) return -EINVAL; if (hcd->driver->address_device) - retval = hcd->driver->address_device(hcd, udev, USB_CTRL_SET_TIMEOUT); + retval = hcd->driver->address_device(hcd, udev, timeout_ms); else retval = usb_control_msg(udev, usb_sndaddr0pipe(), USB_REQ_SET_ADDRESS, 0, devnum, 0, - NULL, 0, USB_CTRL_SET_TIMEOUT); + NULL, 0, timeout_ms); if (retval == 0) { update_devnum(udev, devnum); /* Device now using proper address. */ diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 15e9bd180a1d..b4783574b8e6 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -138,6 +138,9 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp) case 'o': flags |= USB_QUIRK_HUB_SLOW_RESET; break; + case 'p': + flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT; + break; /* Ignore unrecognized flag characters */ } } @@ -527,6 +530,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM }, + /* APTIV AUTOMOTIVE HUB */ + { USB_DEVICE(0x2c48, 0x0132), .driver_info = + USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT }, + /* DJI CineSSD */ { USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM }, diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index eeb7c2157c72..59409c1fc3de 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -72,4 +72,7 @@ /* device has endpoints that should be ignored */ #define USB_QUIRK_ENDPOINT_IGNORE BIT(15) +/* short SET_ADDRESS request timeout */ +#define USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT BIT(16) + #endif /* __LINUX_USB_QUIRKS_H */ From 991544dc579b636e69defa3eec486fd6f6191e59 Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Wed, 8 Nov 2023 16:41:01 -0800 Subject: [PATCH 021/169] usb: gadget: uvc: prevent use of disabled endpoint Currently the set_alt callback immediately disables the endpoint and queues the v4l2 streamoff event. However, as the streamoff event is processed asynchronously, it is possible that the video_pump thread attempts to queue requests to an already disabled endpoint. This change moves disabling usb endpoint to the end of streamoff event callback. As the endpoint's state can no longer be used, video_pump is now guarded by uvc->state as well. To be consistent with the actual streaming state, uvc->state is now toggled between CONNECTED and STREAMING from the v4l2 event callback only. Link: https://lore.kernel.org/20230615171558.GK741@pendragon.ideasonboard.com/ Link: https://lore.kernel.org/20230531085544.253363-1-dan.scally@ideasonboard.com/ Reviewed-by: Daniel Scally Reviewed-by: Michael Grzeschik Tested-by: Michael Grzeschik Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20231109004104.3467968-1-arakesh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uvc.c | 11 +++++------ drivers/usb/gadget/function/f_uvc.h | 2 +- drivers/usb/gadget/function/uvc.h | 2 +- drivers/usb/gadget/function/uvc_v4l2.c | 20 +++++++++++++++++--- drivers/usb/gadget/function/uvc_video.c | 3 ++- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 786379f1b7b7..77999ed53d33 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -263,10 +263,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) return 0; } -void uvc_function_setup_continue(struct uvc_device *uvc) +void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep) { struct usb_composite_dev *cdev = uvc->func.config->cdev; + if (disable_ep && uvc->video.ep) + usb_ep_disable(uvc->video.ep); + usb_composite_setup_continue(cdev); } @@ -337,15 +340,11 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_STREAMING) return 0; - if (uvc->video.ep) - usb_ep_disable(uvc->video.ep); - memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMOFF; v4l2_event_queue(&uvc->vdev, &v4l2_event); - uvc->state = UVC_STATE_CONNECTED; - return 0; + return USB_GADGET_DELAYED_STATUS; case 1: if (uvc->state != UVC_STATE_CONNECTED) diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index 1db972d4beeb..083aef0c65c6 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -11,7 +11,7 @@ struct uvc_device; -void uvc_function_setup_continue(struct uvc_device *uvc); +void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep); void uvc_function_connect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 6751de8b63ad..989bc6b4e93d 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -177,7 +177,7 @@ struct uvc_file_handle { * Functions */ -extern void uvc_function_setup_continue(struct uvc_device *uvc); +extern void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep); extern void uvc_function_connect(struct uvc_device *uvc); extern void uvc_function_disconnect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 3f0a9795c0d4..7cb8d027ff0c 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -451,7 +451,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * Complete the alternate setting selection setup phase now that * userspace is ready to provide video frames. */ - uvc_function_setup_continue(uvc); + uvc_function_setup_continue(uvc, 0); uvc->state = UVC_STATE_STREAMING; return 0; @@ -463,11 +463,18 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_video *video = &uvc->video; + int ret = 0; if (type != video->queue.queue.type) return -EINVAL; - return uvcg_video_enable(video, 0); + uvc->state = UVC_STATE_CONNECTED; + ret = uvcg_video_enable(video, 0); + if (ret < 0) + return ret; + + uvc_function_setup_continue(uvc, 1); + return 0; } static int @@ -500,6 +507,14 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, static void uvc_v4l2_disable(struct uvc_device *uvc) { uvc_function_disconnect(uvc); + /* + * Drop uvc->state to CONNECTED if it was streaming before. + * This ensures that the usb_requests are no longer queued + * to the controller. + */ + if (uvc->state == UVC_STATE_STREAMING) + uvc->state = UVC_STATE_CONNECTED; + uvcg_video_enable(&uvc->video, 0); uvcg_free_buffers(&uvc->video.queue); uvc->func_connected = false; @@ -647,4 +662,3 @@ const struct v4l2_file_operations uvc_v4l2_fops = { .get_unmapped_area = uvcg_v4l2_get_unmapped_area, #endif }; - diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 91af3b1ef0d4..c334802ac0a4 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -384,13 +384,14 @@ static void uvcg_video_pump(struct work_struct *work) struct uvc_video_queue *queue = &video->queue; /* video->max_payload_size is only set when using bulk transfer */ bool is_bulk = video->max_payload_size; + struct uvc_device *uvc = video->uvc; struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; bool buf_done; int ret; - while (video->ep->enabled) { + while (uvc->state == UVC_STATE_STREAMING && video->ep->enabled) { /* * Retrieve the first available USB request, protected by the * request lock. From aeb686a98a9e9743c4c0338957e59643a2708146 Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Wed, 8 Nov 2023 16:41:02 -0800 Subject: [PATCH 022/169] usb: gadget: uvc: Allocate uvc_requests one at a time Currently, the uvc gadget driver allocates all uvc_requests as one array and deallocates them all when the video stream stops. This includes de-allocating all the usb_requests associated with those uvc_requests. This can lead to use-after-free issues if any of those de-allocated usb_requests were still owned by the usb controller. This patch is 1 of 2 patches addressing the use-after-free issue. Instead of bulk allocating all uvc_requests as an array, this patch allocates uvc_requests one at a time, which should allows for similar granularity when deallocating the uvc_requests. This patch has no functional changes other than allocating each uvc_request separately, and similarly freeing each of them separately. Link: https://lore.kernel.org/7cd81649-2795-45b6-8c10-b7df1055020d@google.com Reviewed-by: Daniel Scally Reviewed-by: Michael Grzeschik Suggested-by: Michael Grzeschik Tested-by: Michael Grzeschik Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20231109004104.3467968-2-arakesh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc.h | 3 +- drivers/usb/gadget/function/uvc_video.c | 90 ++++++++++++++----------- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 989bc6b4e93d..993694da0bbc 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -81,6 +81,7 @@ struct uvc_request { struct sg_table sgt; u8 header[UVCG_REQUEST_HEADER_LEN]; struct uvc_buffer *last_buf; + struct list_head list; }; struct uvc_video { @@ -102,7 +103,7 @@ struct uvc_video { /* Requests */ unsigned int req_size; - struct uvc_request *ureq; + struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ struct list_head req_free; spinlock_t req_lock; diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index c334802ac0a4..1619f9664748 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -227,6 +227,24 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, * Request handling */ +static void +uvc_video_free_request(struct uvc_request *ureq, struct usb_ep *ep) +{ + sg_free_table(&ureq->sgt); + if (ureq->req && ep) { + usb_ep_free_request(ep, ureq->req); + ureq->req = NULL; + } + + kfree(ureq->req_buffer); + ureq->req_buffer = NULL; + + if (!list_empty(&ureq->list)) + list_del_init(&ureq->list); + + kfree(ureq); +} + static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) { int ret; @@ -293,27 +311,12 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) static int uvc_video_free_requests(struct uvc_video *video) { - unsigned int i; + struct uvc_request *ureq, *temp; - if (video->ureq) { - for (i = 0; i < video->uvc_num_requests; ++i) { - sg_free_table(&video->ureq[i].sgt); - - if (video->ureq[i].req) { - usb_ep_free_request(video->ep, video->ureq[i].req); - video->ureq[i].req = NULL; - } - - if (video->ureq[i].req_buffer) { - kfree(video->ureq[i].req_buffer); - video->ureq[i].req_buffer = NULL; - } - } - - kfree(video->ureq); - video->ureq = NULL; - } + list_for_each_entry_safe(ureq, temp, &video->ureqs, list) + uvc_video_free_request(ureq, video->ep); + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); video->req_size = 0; return 0; @@ -322,6 +325,7 @@ uvc_video_free_requests(struct uvc_video *video) static int uvc_video_alloc_requests(struct uvc_video *video) { + struct uvc_request *ureq; unsigned int req_size; unsigned int i; int ret = -ENOMEM; @@ -332,29 +336,33 @@ uvc_video_alloc_requests(struct uvc_video *video) * max_t(unsigned int, video->ep->maxburst, 1) * (video->ep->mult); - video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL); - if (video->ureq == NULL) - return -ENOMEM; - - for (i = 0; i < video->uvc_num_requests; ++i) { - video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL); - if (video->ureq[i].req_buffer == NULL) + for (i = 0; i < video->uvc_num_requests; i++) { + ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL); + if (ureq == NULL) goto error; - video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL); - if (video->ureq[i].req == NULL) + INIT_LIST_HEAD(&ureq->list); + + list_add_tail(&ureq->list, &video->ureqs); + + ureq->req_buffer = kmalloc(req_size, GFP_KERNEL); + if (ureq->req_buffer == NULL) goto error; - video->ureq[i].req->buf = video->ureq[i].req_buffer; - video->ureq[i].req->length = 0; - video->ureq[i].req->complete = uvc_video_complete; - video->ureq[i].req->context = &video->ureq[i]; - video->ureq[i].video = video; - video->ureq[i].last_buf = NULL; + ureq->req = usb_ep_alloc_request(video->ep, GFP_KERNEL); + if (ureq->req == NULL) + goto error; - list_add_tail(&video->ureq[i].req->list, &video->req_free); + ureq->req->buf = ureq->req_buffer; + ureq->req->length = 0; + ureq->req->complete = uvc_video_complete; + ureq->req->context = ureq; + ureq->video = video; + ureq->last_buf = NULL; + + list_add_tail(&ureq->req->list, &video->req_free); /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ - sg_alloc_table(&video->ureq[i].sgt, + sg_alloc_table(&ureq->sgt, DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN, PAGE_SIZE) + 2, GFP_KERNEL); } @@ -489,8 +497,8 @@ static void uvcg_video_pump(struct work_struct *work) */ int uvcg_video_enable(struct uvc_video *video, int enable) { - unsigned int i; int ret; + struct uvc_request *ureq; if (video->ep == NULL) { uvcg_info(&video->uvc->func, @@ -502,9 +510,10 @@ int uvcg_video_enable(struct uvc_video *video, int enable) cancel_work_sync(&video->pump); uvcg_queue_cancel(&video->queue, 0); - for (i = 0; i < video->uvc_num_requests; ++i) - if (video->ureq && video->ureq[i].req) - usb_ep_dequeue(video->ep, video->ureq[i].req); + list_for_each_entry(ureq, &video->ureqs, list) { + if (ureq->req) + usb_ep_dequeue(video->ep, ureq->req); + } uvc_video_free_requests(video); uvcg_queue_enable(&video->queue, 0); @@ -536,6 +545,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable) */ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) { + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); INIT_WORK(&video->pump, uvcg_video_pump); From 2079b60bda3257146a4e8ed7525513865f7e6b3e Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Wed, 8 Nov 2023 16:41:03 -0800 Subject: [PATCH 023/169] usb: gadget: uvc: move video disable logic to its own function This patch refactors the video disable logic in uvcg_video_enable into its own separate function 'uvcg_video_disable'. This function is now used anywhere uvcg_video_enable(video, 0) was used. Reviewed-by: Daniel Scally Suggested-by: Michael Grzeschik Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20231109004104.3467968-3-arakesh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc_v4l2.c | 6 ++-- drivers/usb/gadget/function/uvc_video.c | 46 ++++++++++++++++--------- drivers/usb/gadget/function/uvc_video.h | 3 +- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 7cb8d027ff0c..904dd283cbf7 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -443,7 +443,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) return -EINVAL; /* Enable UVC video. */ - ret = uvcg_video_enable(video, 1); + ret = uvcg_video_enable(video); if (ret < 0) return ret; @@ -469,7 +469,7 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) return -EINVAL; uvc->state = UVC_STATE_CONNECTED; - ret = uvcg_video_enable(video, 0); + ret = uvcg_video_disable(video); if (ret < 0) return ret; @@ -515,7 +515,7 @@ static void uvc_v4l2_disable(struct uvc_device *uvc) if (uvc->state == UVC_STATE_STREAMING) uvc->state = UVC_STATE_CONNECTED; - uvcg_video_enable(&uvc->video, 0); + uvcg_video_disable(&uvc->video); uvcg_free_buffers(&uvc->video.queue); uvc->func_connected = false; wake_up_interruptible(&uvc->func_connected_queue); diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 1619f9664748..c3e8c48f46a9 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -493,12 +493,38 @@ static void uvcg_video_pump(struct work_struct *work) } /* - * Enable or disable the video stream. + * Disable the video stream */ -int uvcg_video_enable(struct uvc_video *video, int enable) +int +uvcg_video_disable(struct uvc_video *video) +{ + struct uvc_request *ureq; + + if (video->ep == NULL) { + uvcg_info(&video->uvc->func, + "Video disable failed, device is uninitialized.\n"); + return -ENODEV; + } + + cancel_work_sync(&video->pump); + uvcg_queue_cancel(&video->queue, 0); + + list_for_each_entry(ureq, &video->ureqs, list) { + if (ureq->req) + usb_ep_dequeue(video->ep, ureq->req); + } + + uvc_video_free_requests(video); + uvcg_queue_enable(&video->queue, 0); + return 0; +} + +/* + * Enable the video stream. + */ +int uvcg_video_enable(struct uvc_video *video) { int ret; - struct uvc_request *ureq; if (video->ep == NULL) { uvcg_info(&video->uvc->func, @@ -506,20 +532,6 @@ int uvcg_video_enable(struct uvc_video *video, int enable) return -ENODEV; } - if (!enable) { - cancel_work_sync(&video->pump); - uvcg_queue_cancel(&video->queue, 0); - - list_for_each_entry(ureq, &video->ureqs, list) { - if (ureq->req) - usb_ep_dequeue(video->ep, ureq->req); - } - - uvc_video_free_requests(video); - uvcg_queue_enable(&video->queue, 0); - return 0; - } - if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0) return ret; diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h index 03adeefa343b..8ef6259741f1 100644 --- a/drivers/usb/gadget/function/uvc_video.h +++ b/drivers/usb/gadget/function/uvc_video.h @@ -14,7 +14,8 @@ struct uvc_video; -int uvcg_video_enable(struct uvc_video *video, int enable); +int uvcg_video_enable(struct uvc_video *video); +int uvcg_video_disable(struct uvc_video *video); int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc); From da324ffce34c521b239f319d4051260444a3eb4a Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Wed, 8 Nov 2023 16:41:04 -0800 Subject: [PATCH 024/169] usb: gadget: uvc: Fix use-after-free for inflight usb_requests Currently, the uvc gadget driver allocates all uvc_requests as one array and deallocates them all when the video stream stops. This includes de-allocating all the usb_requests associated with those uvc_requests. This can lead to use-after-free issues if any of those de-allocated usb_requests were still owned by the usb controller. This is patch 2 of 2 in fixing the use-after-free issue. It adds a new flag to uvc_video to track when frames and requests should be flowing. When disabling the video stream, the flag is tripped and, instead of de-allocating all uvc_requests and usb_requests, the gadget driver only de-allocates those usb_requests that are currently owned by it (as present in req_free). Other usb_requests are left untouched until their completion handler is called which takes care of freeing the usb_request and its corresponding uvc_request. Now that uvc_video does not depends on uvc->state, this patch removes unnecessary upates to uvc->state that were made to accommodate uvc_video logic. This should ensure that uvc gadget driver never accidentally de-allocates a usb_request that it doesn't own. Link: https://lore.kernel.org/7cd81649-2795-45b6-8c10-b7df1055020d@google.com Reviewed-by: Daniel Scally Reviewed-by: Michael Grzeschik Suggested-by: Michael Grzeschik Tested-by: Michael Grzeschik Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20231109004104.3467968-4-arakesh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc.h | 1 + drivers/usb/gadget/function/uvc_v4l2.c | 10 +- drivers/usb/gadget/function/uvc_video.c | 130 ++++++++++++++++++++---- 3 files changed, 112 insertions(+), 29 deletions(-) diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 993694da0bbc..be0d012aa244 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -102,6 +102,7 @@ struct uvc_video { unsigned int uvc_num_requests; /* Requests */ + bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ struct list_head req_free; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 904dd283cbf7..c7e5fa4f29e0 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -468,11 +468,11 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) if (type != video->queue.queue.type) return -EINVAL; - uvc->state = UVC_STATE_CONNECTED; ret = uvcg_video_disable(video); if (ret < 0) return ret; + uvc->state = UVC_STATE_CONNECTED; uvc_function_setup_continue(uvc, 1); return 0; } @@ -507,14 +507,6 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, static void uvc_v4l2_disable(struct uvc_device *uvc) { uvc_function_disconnect(uvc); - /* - * Drop uvc->state to CONNECTED if it was streaming before. - * This ensures that the usb_requests are no longer queued - * to the controller. - */ - if (uvc->state == UVC_STATE_STREAMING) - uvc->state = UVC_STATE_CONNECTED; - uvcg_video_disable(&uvc->video); uvcg_free_buffers(&uvc->video.queue); uvc->func_connected = false; diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index c3e8c48f46a9..164bdeb7f2a9 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -227,6 +227,10 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, * Request handling */ +/* + * Callers must take care to hold req_lock when this function may be called + * from multiple threads. For example, when frames are streaming to the host. + */ static void uvc_video_free_request(struct uvc_request *ureq, struct usb_ep *ep) { @@ -271,9 +275,26 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) struct uvc_request *ureq = req->context; struct uvc_video *video = ureq->video; struct uvc_video_queue *queue = &video->queue; - struct uvc_device *uvc = video->uvc; + struct uvc_buffer *last_buf; unsigned long flags; + spin_lock_irqsave(&video->req_lock, flags); + if (!video->is_enabled) { + /* + * When is_enabled is false, uvcg_video_disable() ensures + * that in-flight uvc_buffers are returned, so we can + * safely call free_request without worrying about + * last_buf. + */ + uvc_video_free_request(ureq, ep); + spin_unlock_irqrestore(&video->req_lock, flags); + return; + } + + last_buf = ureq->last_buf; + ureq->last_buf = NULL; + spin_unlock_irqrestore(&video->req_lock, flags); + switch (req->status) { case 0: break; @@ -295,17 +316,26 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) uvcg_queue_cancel(queue, 0); } - if (ureq->last_buf) { - uvcg_complete_buffer(&video->queue, ureq->last_buf); - ureq->last_buf = NULL; + if (last_buf) { + spin_lock_irqsave(&queue->irqlock, flags); + uvcg_complete_buffer(queue, last_buf); + spin_unlock_irqrestore(&queue->irqlock, flags); } spin_lock_irqsave(&video->req_lock, flags); - list_add_tail(&req->list, &video->req_free); - spin_unlock_irqrestore(&video->req_lock, flags); - - if (uvc->state == UVC_STATE_STREAMING) + /* + * Video stream might have been disabled while we were + * processing the current usb_request. So make sure + * we're still streaming before queueing the usb_request + * back to req_free + */ + if (video->is_enabled) { + list_add_tail(&req->list, &video->req_free); queue_work(video->async_wq, &video->pump); + } else { + uvc_video_free_request(ureq, ep); + } + spin_unlock_irqrestore(&video->req_lock, flags); } static int @@ -392,20 +422,22 @@ static void uvcg_video_pump(struct work_struct *work) struct uvc_video_queue *queue = &video->queue; /* video->max_payload_size is only set when using bulk transfer */ bool is_bulk = video->max_payload_size; - struct uvc_device *uvc = video->uvc; struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; bool buf_done; int ret; - while (uvc->state == UVC_STATE_STREAMING && video->ep->enabled) { + while (true) { + if (!video->ep->enabled) + return; + /* - * Retrieve the first available USB request, protected by the - * request lock. + * Check is_enabled and retrieve the first available USB + * request, protected by the request lock. */ spin_lock_irqsave(&video->req_lock, flags); - if (list_empty(&video->req_free)) { + if (!video->is_enabled || list_empty(&video->req_free)) { spin_unlock_irqrestore(&video->req_lock, flags); return; } @@ -487,9 +519,11 @@ static void uvcg_video_pump(struct work_struct *work) return; spin_lock_irqsave(&video->req_lock, flags); - list_add_tail(&req->list, &video->req_free); + if (video->is_enabled) + list_add_tail(&req->list, &video->req_free); + else + uvc_video_free_request(req->context, video->ep); spin_unlock_irqrestore(&video->req_lock, flags); - return; } /* @@ -498,7 +532,11 @@ static void uvcg_video_pump(struct work_struct *work) int uvcg_video_disable(struct uvc_video *video) { - struct uvc_request *ureq; + unsigned long flags; + struct list_head inflight_bufs; + struct usb_request *req, *temp; + struct uvc_buffer *buf, *btemp; + struct uvc_request *ureq, *utemp; if (video->ep == NULL) { uvcg_info(&video->uvc->func, @@ -506,15 +544,58 @@ uvcg_video_disable(struct uvc_video *video) return -ENODEV; } + INIT_LIST_HEAD(&inflight_bufs); + spin_lock_irqsave(&video->req_lock, flags); + video->is_enabled = false; + + /* + * Remove any in-flight buffers from the uvc_requests + * because we want to return them before cancelling the + * queue. This ensures that we aren't stuck waiting for + * all complete callbacks to come through before disabling + * vb2 queue. + */ + list_for_each_entry(ureq, &video->ureqs, list) { + if (ureq->last_buf) { + list_add_tail(&ureq->last_buf->queue, &inflight_bufs); + ureq->last_buf = NULL; + } + } + spin_unlock_irqrestore(&video->req_lock, flags); + cancel_work_sync(&video->pump); uvcg_queue_cancel(&video->queue, 0); - list_for_each_entry(ureq, &video->ureqs, list) { - if (ureq->req) - usb_ep_dequeue(video->ep, ureq->req); + spin_lock_irqsave(&video->req_lock, flags); + /* + * Remove all uvc_requests from ureqs with list_del_init + * This lets uvc_video_free_request correctly identify + * if the uvc_request is attached to a list or not when freeing + * memory. + */ + list_for_each_entry_safe(ureq, utemp, &video->ureqs, list) + list_del_init(&ureq->list); + + list_for_each_entry_safe(req, temp, &video->req_free, list) { + list_del(&req->list); + uvc_video_free_request(req->context, video->ep); } - uvc_video_free_requests(video); + INIT_LIST_HEAD(&video->ureqs); + INIT_LIST_HEAD(&video->req_free); + video->req_size = 0; + spin_unlock_irqrestore(&video->req_lock, flags); + + /* + * Return all the video buffers before disabling the queue. + */ + spin_lock_irqsave(&video->queue.irqlock, flags); + list_for_each_entry_safe(buf, btemp, &inflight_bufs, queue) { + list_del(&buf->queue); + uvcg_complete_buffer(&video->queue, buf); + } + spin_unlock_irqrestore(&video->queue.irqlock, flags); + uvcg_queue_enable(&video->queue, 0); return 0; } @@ -532,6 +613,14 @@ int uvcg_video_enable(struct uvc_video *video) return -ENODEV; } + /* + * Safe to access request related fields without req_lock because + * this is the only thread currently active, and no other + * request handling thread will become active until this function + * returns. + */ + video->is_enabled = true; + if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0) return ret; @@ -557,6 +646,7 @@ int uvcg_video_enable(struct uvc_video *video) */ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) { + video->is_enabled = false; INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); From 6acba0345b68772830582ca1ca369a2f45631275 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 20 Nov 2023 06:20:25 +0000 Subject: [PATCH 025/169] usb:gadget:uvc Do not use worker thread to pump isoc usb requests When we use an async work queue to perform the function of pumping usb requests to the usb controller, it is possible that amongst other factors, thread scheduling affects at what cadence we're able to pump requests. This could mean isoc usb requests miss their uframes - resulting in video stream flickers on the host device. To avoid this, we make the async_wq thread only produce isoc usb_requests with uvc buffers encoded into them. The process of queueing to the endpoint is done by the uvc_video_complete() handler. In case no usb_requests are ready with encoded information, we just queue a zero length request to the endpoint from the complete handler. For bulk endpoints the async_wq thread still queues usb requests to the endpoint. Signed-off-by: Michael Grzeschik Signed-off-by: Jayant Chowdhary Suggested-by: Avichal Rakesh Suggested-by: Alan Stern Link: https://lore.kernel.org/r/20231120062026.3759463-1-jchowdhary@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc.h | 8 + drivers/usb/gadget/function/uvc_video.c | 202 ++++++++++++++++++------ 2 files changed, 165 insertions(+), 45 deletions(-) diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index be0d012aa244..cb35687b11e7 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -105,7 +105,15 @@ struct uvc_video { bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ + + /* USB requests that the video pump thread can encode into */ struct list_head req_free; + + /* + * USB requests video pump thread has already encoded into. These are + * ready to be queued to the endpoint. + */ + struct list_head req_ready; spinlock_t req_lock; unsigned int req_int_count; diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 164bdeb7f2a9..98ba524c27f5 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -269,6 +269,101 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) return ret; } +/* This function must be called with video->req_lock held. */ +static int uvcg_video_usb_req_queue(struct uvc_video *video, + struct usb_request *req, bool queue_to_ep) +{ + bool is_bulk = video->max_payload_size; + struct list_head *list = NULL; + + if (!video->is_enabled) { + uvc_video_free_request(req->context, video->ep); + return -ENODEV; + } + if (queue_to_ep) { + struct uvc_request *ureq = req->context; + /* + * With USB3 handling more requests at a higher speed, we can't + * afford to generate an interrupt for every request. Decide to + * interrupt: + * + * - When no more requests are available in the free queue, as + * this may be our last chance to refill the endpoint's + * request queue. + * + * - When this is request is the last request for the video + * buffer, as we want to start sending the next video buffer + * ASAP in case it doesn't get started already in the next + * iteration of this loop. + * + * - Four times over the length of the requests queue (as + * indicated by video->uvc_num_requests), as a trade-off + * between latency and interrupt load. + */ + if (list_empty(&video->req_free) || ureq->last_buf || + !(video->req_int_count % + DIV_ROUND_UP(video->uvc_num_requests, 4))) { + video->req_int_count = 0; + req->no_interrupt = 0; + } else { + req->no_interrupt = 1; + } + video->req_int_count++; + return uvcg_video_ep_queue(video, req); + } + /* + * If we're not queuing to the ep, for isoc we're queuing + * to the req_ready list, otherwise req_free. + */ + list = is_bulk ? &video->req_free : &video->req_ready; + list_add_tail(&req->list, list); + return 0; +} + +/* + * Must only be called from uvcg_video_enable - since after that we only want to + * queue requests to the endpoint from the uvc_video_complete complete handler. + * This function is needed in order to 'kick start' the flow of requests from + * gadget driver to the usb controller. + */ +static void uvc_video_ep_queue_initial_requests(struct uvc_video *video) +{ + struct usb_request *req = NULL; + unsigned long flags = 0; + unsigned int count = 0; + int ret = 0; + + /* + * We only queue half of the free list since we still want to have + * some free usb_requests in the free list for the video_pump async_wq + * thread to encode uvc buffers into. Otherwise we could get into a + * situation where the free list does not have any usb requests to + * encode into - we always end up queueing 0 length requests to the + * end point. + */ + unsigned int half_list_size = video->uvc_num_requests / 2; + + spin_lock_irqsave(&video->req_lock, flags); + /* + * Take these requests off the free list and queue them all to the + * endpoint. Since we queue 0 length requests with the req_lock held, + * there isn't any 'data' race involved here with the complete handler. + */ + while (count < half_list_size) { + req = list_first_entry(&video->req_free, struct usb_request, + list); + list_del(&req->list); + req->length = 0; + ret = uvcg_video_ep_queue(video, req); + if (ret < 0) { + uvcg_queue_cancel(&video->queue, 0); + break; + } + count++; + } + spin_unlock_irqrestore(&video->req_lock, flags); +} + static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { @@ -277,6 +372,8 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) struct uvc_video_queue *queue = &video->queue; struct uvc_buffer *last_buf; unsigned long flags; + bool is_bulk = video->max_payload_size; + int ret = 0; spin_lock_irqsave(&video->req_lock, flags); if (!video->is_enabled) { @@ -330,8 +427,45 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) * back to req_free */ if (video->is_enabled) { - list_add_tail(&req->list, &video->req_free); - queue_work(video->async_wq, &video->pump); + /* + * Here we check whether any request is available in the ready + * list. If it is, queue it to the ep and add the current + * usb_request to the req_free list - for video_pump to fill in. + * Otherwise, just use the current usb_request to queue a 0 + * length request to the ep. Since we always add to the req_free + * list if we dequeue from the ready list, there will never + * be a situation where the req_free list is completely out of + * requests and cannot recover. + */ + struct usb_request *to_queue = req; + + to_queue->length = 0; + if (!list_empty(&video->req_ready)) { + to_queue = list_first_entry(&video->req_ready, + struct usb_request, list); + list_del(&to_queue->list); + list_add_tail(&req->list, &video->req_free); + /* + * Queue work to the wq as well since it is possible that a + * buffer may not have been completely encoded with the set of + * in-flight usb requests for whih the complete callbacks are + * firing. + * In that case, if we do not queue work to the worker thread, + * the buffer will never be marked as complete - and therefore + * not be returned to userpsace. As a result, + * dequeue -> queue -> dequeue flow of uvc buffers will not + * happen. + */ + queue_work(video->async_wq, &video->pump); + } + /* + * Queue to the endpoint. The actual queueing to ep will + * only happen on one thread - the async_wq for bulk endpoints + * and this thread for isoc endpoints. + */ + ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk); + if (ret < 0) + uvcg_queue_cancel(queue, 0); } else { uvc_video_free_request(ureq, ep); } @@ -348,6 +482,7 @@ uvc_video_free_requests(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); + INIT_LIST_HEAD(&video->req_ready); video->req_size = 0; return 0; } @@ -425,8 +560,7 @@ static void uvcg_video_pump(struct work_struct *work) struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; - bool buf_done; - int ret; + int ret = 0; while (true) { if (!video->ep->enabled) @@ -455,15 +589,6 @@ static void uvcg_video_pump(struct work_struct *work) if (buf != NULL) { video->encode(req, video, buf); - buf_done = buf->state == UVC_BUF_STATE_DONE; - } else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) { - /* - * No video buffer available; the queue is still connected and - * we're transferring over ISOC. Queue a 0 length request to - * prevent missed ISOC transfers. - */ - req->length = 0; - buf_done = false; } else { /* * Either the queue has been disconnected or no video buffer @@ -474,45 +599,25 @@ static void uvcg_video_pump(struct work_struct *work) break; } - /* - * With USB3 handling more requests at a higher speed, we can't - * afford to generate an interrupt for every request. Decide to - * interrupt: - * - * - When no more requests are available in the free queue, as - * this may be our last chance to refill the endpoint's - * request queue. - * - * - When this is request is the last request for the video - * buffer, as we want to start sending the next video buffer - * ASAP in case it doesn't get started already in the next - * iteration of this loop. - * - * - Four times over the length of the requests queue (as - * indicated by video->uvc_num_requests), as a trade-off - * between latency and interrupt load. - */ - if (list_empty(&video->req_free) || buf_done || - !(video->req_int_count % - DIV_ROUND_UP(video->uvc_num_requests, 4))) { - video->req_int_count = 0; - req->no_interrupt = 0; - } else { - req->no_interrupt = 1; - } - - /* Queue the USB request */ - ret = uvcg_video_ep_queue(video, req); spin_unlock_irqrestore(&queue->irqlock, flags); + spin_lock_irqsave(&video->req_lock, flags); + /* For bulk end points we queue from the worker thread + * since we would preferably not want to wait on requests + * to be ready, in the uvcg_video_complete() handler. + * For isoc endpoints we add the request to the ready list + * and only queue it to the endpoint from the complete handler. + */ + ret = uvcg_video_usb_req_queue(video, req, is_bulk); + spin_unlock_irqrestore(&video->req_lock, flags); + if (ret < 0) { uvcg_queue_cancel(queue, 0); break; } - /* Endpoint now owns the request */ + /* The request is owned by the endpoint / ready list. */ req = NULL; - video->req_int_count++; } if (!req) @@ -581,8 +686,14 @@ uvcg_video_disable(struct uvc_video *video) uvc_video_free_request(req->context, video->ep); } + list_for_each_entry_safe(req, temp, &video->req_ready, list) { + list_del(&req->list); + uvc_video_free_request(req->context, video->ep); + } + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); + INIT_LIST_HEAD(&video->req_ready); video->req_size = 0; spin_unlock_irqrestore(&video->req_lock, flags); @@ -636,7 +747,7 @@ int uvcg_video_enable(struct uvc_video *video) video->req_int_count = 0; - queue_work(video->async_wq, &video->pump); + uvc_video_ep_queue_initial_requests(video); return ret; } @@ -649,6 +760,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) video->is_enabled = false; INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); + INIT_LIST_HEAD(&video->req_ready); spin_lock_init(&video->req_lock); INIT_WORK(&video->pump, uvcg_video_pump); From ea3ba10f2961f08155bb66c0b1b8088652ca5f28 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Mon, 27 Nov 2023 12:22:24 +0100 Subject: [PATCH 026/169] usb: misc: onboard_usb_hub: Print symbolic error names Instead of printing the decimal error codes, let's use the more human-readable symbolic error names provided by the %pe printk format specifier. Signed-off-by: Frieder Schrempf Link: https://lore.kernel.org/r/20231127112234.109073-1-frieder@fris.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/onboard_usb_hub.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index 2b45404e9732..6d122772a970 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -68,7 +69,7 @@ static int onboard_hub_power_on(struct onboard_hub *hub) err = regulator_bulk_enable(hub->pdata->num_supplies, hub->supplies); if (err) { - dev_err(hub->dev, "failed to enable supplies: %d\n", err); + dev_err(hub->dev, "failed to enable supplies: %pe\n", ERR_PTR(err)); return err; } @@ -88,7 +89,7 @@ static int onboard_hub_power_off(struct onboard_hub *hub) err = regulator_bulk_disable(hub->pdata->num_supplies, hub->supplies); if (err) { - dev_err(hub->dev, "failed to disable supplies: %d\n", err); + dev_err(hub->dev, "failed to disable supplies: %pe\n", ERR_PTR(err)); return err; } @@ -235,7 +236,7 @@ static void onboard_hub_attach_usb_driver(struct work_struct *work) err = driver_attach(&onboard_hub_usbdev_driver.drvwrap.driver); if (err) - pr_err("Failed to attach USB driver: %d\n", err); + pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err)); } static int onboard_hub_probe(struct platform_device *pdev) @@ -262,7 +263,7 @@ static int onboard_hub_probe(struct platform_device *pdev) 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); + dev_err(dev, "Failed to get regulator supplies: %pe\n", ERR_PTR(err)); return err; } From 65e62b8a955adebee8634804d8f72599213c2774 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Mon, 27 Nov 2023 12:22:25 +0100 Subject: [PATCH 027/169] usb: misc: onboard_usb_hub: Add support for clock input Most onboard USB hubs have a dedicated crystal oscillator but on some boards the clock signal for the hub is provided by the SoC. In order to support this, we add the possibility of specifying a clock in the devicetree that gets enabled/disabled when the hub is powered up/down. Signed-off-by: Frieder Schrempf Link: https://lore.kernel.org/r/20231127112234.109073-2-frieder@fris.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/onboard_usb_hub.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index 6d122772a970..3b209104f648 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -5,6 +5,7 @@ * Copyright (c) 2022, Google LLC */ +#include #include #include #include @@ -61,12 +62,19 @@ struct onboard_hub { bool going_away; struct list_head udev_list; struct mutex lock; + struct clk *clk; }; static int onboard_hub_power_on(struct onboard_hub *hub) { int err; + err = clk_prepare_enable(hub->clk); + if (err) { + dev_err(hub->dev, "failed to enable clock: %pe\n", ERR_PTR(err)); + return err; + } + err = regulator_bulk_enable(hub->pdata->num_supplies, hub->supplies); if (err) { dev_err(hub->dev, "failed to enable supplies: %pe\n", ERR_PTR(err)); @@ -93,6 +101,8 @@ static int onboard_hub_power_off(struct onboard_hub *hub) return err; } + clk_disable_unprepare(hub->clk); + hub->is_powered_on = false; return 0; @@ -267,6 +277,10 @@ static int onboard_hub_probe(struct platform_device *pdev) return err; } + hub->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(hub->clk)) + return dev_err_probe(dev, PTR_ERR(hub->clk), "failed to get clock\n"); + hub->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(hub->reset_gpio)) From 24af68a0ed53629bdde7b53ef8c2be72580d293b Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Mon, 27 Nov 2023 12:22:26 +0100 Subject: [PATCH 028/169] usb: misc: onboard_usb_hub: Add support for Cypress CY7C6563x The Cypress CY7C6563x is a 2/4-port USB 2.0 hub. Add support for this hub in the driver in order to bring up reset, supply or clock dependencies. There is no reset pulse width given in the datasheet so we expect a minimal value of 1us to be enough. This hasn't been tested though due to lack of hardware which has the reset connected to a GPIO. Signed-off-by: Frieder Schrempf Link: https://lore.kernel.org/r/20231127112234.109073-3-frieder@fris.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/onboard_usb_hub.c | 1 + drivers/usb/misc/onboard_usb_hub.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index 3b209104f648..c09fb6bd7d7d 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -441,6 +441,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_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_CYPRESS, 0x6570) }, /* CY7C6563x 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 292110e64a1d..f360d5cf8d8a 100644 --- a/drivers/usb/misc/onboard_usb_hub.h +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -36,6 +36,11 @@ static const struct onboard_hub_pdata cypress_hx3_data = { .num_supplies = 2, }; +static const struct onboard_hub_pdata cypress_hx2vl_data = { + .reset_us = 1, + .num_supplies = 1, +}; + static const struct onboard_hub_pdata genesys_gl850g_data = { .reset_us = 3, .num_supplies = 1, @@ -61,6 +66,7 @@ static const struct of_device_id onboard_hub_match[] = { { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, }, { .compatible = "usb4b4,6506", .data = &cypress_hx3_data, }, + { .compatible = "usb4b4,6570", .data = &cypress_hx2vl_data, }, { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, From 35a1743f4598e09524ae39651bc42733ec9749de Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:29 +0200 Subject: [PATCH 029/169] xhci: dbc: Drop duplicate checks for dma_free_coherent() dma_free_coherent() is NULL-aware, not necessary to check for the parameter twice. Drop duplicate conditionals in the caller. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index b40d9238d447..9e9ce3711813 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -28,7 +28,7 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring) if (!ring) return; - if (ring->first_seg && ring->first_seg->trbs) { + if (ring->first_seg) { dma_free_coherent(dev, TRB_SEGMENT_SIZE, ring->first_seg->trbs, ring->first_seg->dma); @@ -394,9 +394,8 @@ static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring, static void dbc_erst_free(struct device *dev, struct xhci_erst *erst) { - if (erst->entries) - dma_free_coherent(dev, sizeof(struct xhci_erst_entry), - erst->entries, erst->erst_dma_addr); + dma_free_coherent(dev, sizeof(struct xhci_erst_entry), erst->entries, + erst->erst_dma_addr); erst->entries = NULL; } @@ -543,11 +542,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc) xhci_dbc_eps_exit(dbc); - if (dbc->string) { - dma_free_coherent(dbc->dev, dbc->string_size, - dbc->string, dbc->string_dma); - dbc->string = NULL; - } + dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma); + dbc->string = NULL; dbc_free_ctx(dbc->dev, dbc->ctx); dbc->ctx = NULL; From 601fbf65b2a0af126268da22500c24e8616ade5a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:30 +0200 Subject: [PATCH 030/169] xhci: dbc: Convert to use sysfs_streq() It's standard approach to parse values from user space by using sysfs_streq(). Make driver use it. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 9e9ce3711813..f505b79afe53 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -957,9 +957,9 @@ static ssize_t dbc_store(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - if (!strncmp(buf, "enable", 6)) + if (sysfs_streq(buf, "enable")) xhci_dbc_start(dbc); - else if (!strncmp(buf, "disable", 7)) + else if (sysfs_streq(buf, "disable")) xhci_dbc_stop(dbc); else return -EINVAL; From a230f1a74866802b9a87ce7aa8dbd81d22831ab3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:31 +0200 Subject: [PATCH 031/169] xhci: dbc: Use sysfs_emit() to instead of scnprintf() Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 44 +++++++++++++--------------------- drivers/usb/host/xhci-dbgcap.h | 1 + 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index f505b79afe53..df14e336370d 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -910,41 +910,29 @@ static void xhci_dbc_handle_events(struct work_struct *work) mod_delayed_work(system_wq, &dbc->event_work, 1); } +static const char * const dbc_state_strings[DS_MAX] = { + [DS_DISABLED] = "disabled", + [DS_INITIALIZED] = "initialized", + [DS_ENABLED] = "enabled", + [DS_CONNECTED] = "connected", + [DS_CONFIGURED] = "configured", + [DS_STALLED] = "stalled", +}; + static ssize_t dbc_show(struct device *dev, struct device_attribute *attr, char *buf) { - const char *p; struct xhci_dbc *dbc; struct xhci_hcd *xhci; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - switch (dbc->state) { - case DS_DISABLED: - p = "disabled"; - break; - case DS_INITIALIZED: - p = "initialized"; - break; - case DS_ENABLED: - p = "enabled"; - break; - case DS_CONNECTED: - p = "connected"; - break; - case DS_CONFIGURED: - p = "configured"; - break; - case DS_STALLED: - p = "stalled"; - break; - default: - p = "unknown"; - } + if (dbc->state >= ARRAY_SIZE(dbc_state_strings)) + return sysfs_emit(buf, "unknown\n"); - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]); } static ssize_t dbc_store(struct device *dev, @@ -977,7 +965,7 @@ static ssize_t dbc_idVendor_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%04x\n", dbc->idVendor); + return sysfs_emit(buf, "%04x\n", dbc->idVendor); } static ssize_t dbc_idVendor_store(struct device *dev, @@ -1017,7 +1005,7 @@ static ssize_t dbc_idProduct_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%04x\n", dbc->idProduct); + return sysfs_emit(buf, "%04x\n", dbc->idProduct); } static ssize_t dbc_idProduct_store(struct device *dev, @@ -1056,7 +1044,7 @@ static ssize_t dbc_bcdDevice_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%04x\n", dbc->bcdDevice); + return sysfs_emit(buf, "%04x\n", dbc->bcdDevice); } static ssize_t dbc_bcdDevice_store(struct device *dev, @@ -1096,7 +1084,7 @@ static ssize_t dbc_bInterfaceProtocol_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%02x\n", dbc->bInterfaceProtocol); + return sysfs_emit(buf, "%02x\n", dbc->bInterfaceProtocol); } static ssize_t dbc_bInterfaceProtocol_store(struct device *dev, diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index 51a7ab3ba0ca..e39e3ae1677a 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -82,6 +82,7 @@ enum dbc_state { DS_CONNECTED, DS_CONFIGURED, DS_STALLED, + DS_MAX }; struct dbc_ep { From e3be8fb7d012d1e9a1d1b753ca50ff057bbb12b2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:32 +0200 Subject: [PATCH 032/169] xhci: dbc: Use ATTRIBUTE_GROUPS() Embrace ATTRIBUTE_GROUPS() to avoid boiler plate code. This should not introduce any functional changes. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index df14e336370d..660e3ee31dc6 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -1123,7 +1123,7 @@ static DEVICE_ATTR_RW(dbc_idProduct); static DEVICE_ATTR_RW(dbc_bcdDevice); static DEVICE_ATTR_RW(dbc_bInterfaceProtocol); -static struct attribute *dbc_dev_attributes[] = { +static struct attribute *dbc_dev_attrs[] = { &dev_attr_dbc.attr, &dev_attr_dbc_idVendor.attr, &dev_attr_dbc_idProduct.attr, @@ -1131,10 +1131,7 @@ static struct attribute *dbc_dev_attributes[] = { &dev_attr_dbc_bInterfaceProtocol.attr, NULL }; - -static const struct attribute_group dbc_dev_attrib_grp = { - .attrs = dbc_dev_attributes, -}; +ATTRIBUTE_GROUPS(dbc_dev); struct xhci_dbc * xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *driver) @@ -1160,7 +1157,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver * INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events); spin_lock_init(&dbc->lock); - ret = sysfs_create_group(&dev->kobj, &dbc_dev_attrib_grp); + ret = sysfs_create_groups(&dev->kobj, dbc_dev_groups); if (ret) goto err; @@ -1179,7 +1176,7 @@ void xhci_dbc_remove(struct xhci_dbc *dbc) xhci_dbc_stop(dbc); /* remove sysfs files */ - sysfs_remove_group(&dbc->dev->kobj, &dbc_dev_attrib_grp); + sysfs_remove_groups(&dbc->dev->kobj, dbc_dev_groups); kfree(dbc); } From 24352d170b5fcab1d7724dd20257e0b73cbdb453 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:33 +0200 Subject: [PATCH 033/169] xhci: dbc: Check for errors first in xhci_dbc_stop() The usual pattern is to check for errors and then continue if none. Apply that pattern to xhci_dbc_stop() code. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-6-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 660e3ee31dc6..6b9f4b839270 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -646,11 +646,11 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) spin_lock_irqsave(&dbc->lock, flags); ret = xhci_do_dbc_stop(dbc); spin_unlock_irqrestore(&dbc->lock, flags); + if (ret) + return; - if (!ret) { - xhci_dbc_mem_cleanup(dbc); - pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ - } + xhci_dbc_mem_cleanup(dbc); + pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ } static void From 89cd6362e6ad4aa8663c41ac960f223a761385b4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:34 +0200 Subject: [PATCH 034/169] xhci: dbc: Don't shadow error codes in store() functions kstrtox() along with regmap API can return different error codes based on the circumstances. Don't shadow them when returning to the caller. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-7-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 6b9f4b839270..c211c69e8041 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -977,9 +977,11 @@ static ssize_t dbc_idVendor_store(struct device *dev, void __iomem *ptr; u16 value; u32 dev_info; + int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; @@ -1017,9 +1019,11 @@ static ssize_t dbc_idProduct_store(struct device *dev, void __iomem *ptr; u32 dev_info; u16 value; + int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; @@ -1056,9 +1060,11 @@ static ssize_t dbc_bcdDevice_store(struct device *dev, void __iomem *ptr; u32 dev_info; u16 value; + int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; @@ -1098,9 +1104,13 @@ static ssize_t dbc_bInterfaceProtocol_store(struct device *dev, u8 value; int ret; - /* bInterfaceProtocol is 8 bit, but xhci only supports values 0 and 1 */ + /* bInterfaceProtocol is 8 bit, but... */ ret = kstrtou8(buf, 0, &value); - if (ret || value > 1) + if (ret) + return ret; + + /* ...xhci only supports values 0 and 1 */ + if (value > 1) return -EINVAL; xhci = hcd_to_xhci(dev_get_drvdata(dev)); From b28718717b10befac6f43eb5a1e075ee4fb4ef02 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:35 +0200 Subject: [PATCH 035/169] xhci: dbc: Replace custom return value with proper Linux error code Replace the custom return value with proper Linux error code. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-8-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index c211c69e8041..779a564ad698 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -593,7 +593,7 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc) static int xhci_do_dbc_stop(struct xhci_dbc *dbc) { if (dbc->state == DS_DISABLED) - return -1; + return -EINVAL; writel(0, &dbc->regs->control); dbc->state = DS_DISABLED; From 84637512e09c51929e36647a151d26cd1efa1b31 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:36 +0200 Subject: [PATCH 036/169] xhci: dbc: Use sizeof_field() where it makes sense Instead of doing custom calculations, use sizeof_field() macro. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-9-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 779a564ad698..0c9fd61e9c5b 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -374,7 +374,7 @@ static void xhci_dbc_eps_init(struct xhci_dbc *dbc) static void xhci_dbc_eps_exit(struct xhci_dbc *dbc) { - memset(dbc->eps, 0, sizeof(struct dbc_ep) * ARRAY_SIZE(dbc->eps)); + memset(dbc->eps, 0, sizeof_field(struct xhci_dbc, eps)); } static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring, From 311902d4cc4c13c0f1b4e79ee7eb9e04642f3d0b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:37 +0200 Subject: [PATCH 037/169] xhci: dbc: Use sizeof(*pointer) instead of sizeof(type) It is preferred to use sizeof(*pointer) instead of sizeof(type). The type of the variable can change and one needs not change the former (unlike the latter). No functional change intended. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-10-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 0c9fd61e9c5b..73494d55b0be 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -380,7 +380,7 @@ static void xhci_dbc_eps_exit(struct xhci_dbc *dbc) static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring, struct xhci_erst *erst, gfp_t flags) { - erst->entries = dma_alloc_coherent(dev, sizeof(struct xhci_erst_entry), + erst->entries = dma_alloc_coherent(dev, sizeof(*erst->entries), &erst->erst_dma_addr, flags); if (!erst->entries) return -ENOMEM; @@ -394,7 +394,7 @@ static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring, static void dbc_erst_free(struct device *dev, struct xhci_erst *erst) { - dma_free_coherent(dev, sizeof(struct xhci_erst_entry), erst->entries, + dma_free_coherent(dev, sizeof(*erst->entries), erst->entries, erst->erst_dma_addr); erst->entries = NULL; } @@ -494,7 +494,7 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags) goto ctx_fail; /* Allocate the string table: */ - dbc->string_size = sizeof(struct dbc_str_descs); + dbc->string_size = sizeof(*dbc->string); dbc->string = dma_alloc_coherent(dev, dbc->string_size, &dbc->string_dma, flags); if (!dbc->string) From cdcaa870c7be7e421de5c33257d1b3cc074dc802 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Dec 2023 17:06:38 +0200 Subject: [PATCH 038/169] xhci: dbc: Add missing headers Don't inherit headers "by chances" from asm/bug.h, asm/io.h, etc... Include the needed headers explicitly. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-11-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 73494d55b0be..d82935d31126 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -6,9 +6,24 @@ * * Author: Lu Baolu */ +#include +#include #include -#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include #include "xhci.h" #include "xhci-trace.h" From 5080ef2d373ada6fba2e7b86a56a93c8da909a46 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Fri, 1 Dec 2023 17:06:39 +0200 Subject: [PATCH 039/169] xhci: check if legacy irq is available before using it as fallback Move the error check "No MSI-X/MSI found and no IRQ in BIOS" inside 'goto legacy'. It is better to check if the IRQ interrupt is available, before trying to add a handler. Additionally the aforementioned error message is much more clear. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-12-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 95ed9404f6f8..7f2b1312e943 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -228,12 +228,12 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) return 0; } +legacy_irq: if (!pdev->irq) { xhci_err(xhci, "No msi-x/msi found and no IRQ in BIOS\n"); return -EINVAL; } - legacy_irq: if (!strlen(hcd->irq_descr)) snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", hcd->driver->description, hcd->self.busnum); From f977f4c9301c8a67c367946a08c4ce940b42c299 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Fri, 1 Dec 2023 17:06:40 +0200 Subject: [PATCH 040/169] xhci: add handler for only one interrupt line Current xHCI driver only supports one "interrupter", meaning we will only use one MSI/MSI-X interrupt line. Thus, add handler only to the first interrupt line. Signed-off-by: Niklas Neronin Co-developed-by: Mathias Nyman Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-13-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 7f2b1312e943..59bbae69f97c 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -95,10 +95,9 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) if (hcd->msix_enabled) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - int i; - for (i = 0; i < xhci->msix_count; i++) - synchronize_irq(pci_irq_vector(pdev, i)); + /* for now, the driver only supports one primary interrupter */ + synchronize_irq(pci_irq_vector(pdev, 0)); } } @@ -112,15 +111,7 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) if (hcd->irq > 0) return; - if (hcd->msix_enabled) { - int i; - - for (i = 0; i < xhci->msix_count; i++) - free_irq(pci_irq_vector(pdev, i), xhci_to_hcd(xhci)); - } else { - free_irq(pci_irq_vector(pdev, 0), xhci_to_hcd(xhci)); - } - + free_irq(pci_irq_vector(pdev, 0), xhci_to_hcd(xhci)); pci_free_irq_vectors(pdev); hcd->msix_enabled = 0; } @@ -159,9 +150,9 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) */ static int xhci_setup_msix(struct xhci_hcd *xhci) { - int i, ret; struct usb_hcd *hcd = xhci_to_hcd(xhci); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + int ret; /* * calculate number of msi-x vectors supported. @@ -181,22 +172,16 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) return ret; } - for (i = 0; i < xhci->msix_count; i++) { - ret = request_irq(pci_irq_vector(pdev, i), xhci_msi_irq, 0, - "xhci_hcd", xhci_to_hcd(xhci)); - if (ret) - goto disable_msix; + ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd", + xhci_to_hcd(xhci)); + if (ret) { + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt"); + pci_free_irq_vectors(pdev); + return ret; } hcd->msix_enabled = 1; return ret; - -disable_msix: - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt"); - while (--i >= 0) - free_irq(pci_irq_vector(pdev, i), xhci_to_hcd(xhci)); - pci_free_irq_vectors(pdev); - return ret; } static int xhci_try_enable_msi(struct usb_hcd *hcd) From 74554e9c227679a6fc5eb4d968d31c2ca4df853e Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Fri, 1 Dec 2023 17:06:41 +0200 Subject: [PATCH 041/169] xhci: refactor static MSI-X function The current way the xhci driver sets up MSI/MSI-X interrupts is overly complex and messy. The whole MSI/MSI-X setup can be done in one simple function. Start refactoring this by incorporating 'xhci_setup_msix()' into 'xhci_try_enable_msi()'. 'xhci_setup_msix()' is a static function which is only called by 'xhci_try_enable_msi()'. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-14-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 74 ++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 59bbae69f97c..370943c04881 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -145,45 +145,6 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) return ret; } -/* - * Set up MSI-X - */ -static int xhci_setup_msix(struct xhci_hcd *xhci) -{ - struct usb_hcd *hcd = xhci_to_hcd(xhci); - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - int ret; - - /* - * calculate number of msi-x vectors supported. - * - HCS_MAX_INTRS: the max number of interrupts the host can handle, - * with max number of interrupters based on the xhci HCSPARAMS1. - * - num_online_cpus: maximum msi-x vectors per CPUs core. - * Add additional 1 vector to ensure always available interrupt. - */ - xhci->msix_count = min(num_online_cpus() + 1, - HCS_MAX_INTRS(xhci->hcs_params1)); - - ret = pci_alloc_irq_vectors(pdev, xhci->msix_count, xhci->msix_count, - PCI_IRQ_MSIX); - if (ret < 0) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Failed to enable MSI-X"); - return ret; - } - - ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd", - xhci_to_hcd(xhci)); - if (ret) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt"); - pci_free_irq_vectors(pdev); - return ret; - } - - hcd->msix_enabled = 1; - return ret; -} - static int xhci_try_enable_msi(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -203,11 +164,38 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) free_irq(hcd->irq, hcd); hcd->irq = 0; - ret = xhci_setup_msix(xhci); - if (ret) - /* fall back to msi*/ - ret = xhci_setup_msi(xhci); + /* + * calculate number of MSI-X vectors supported. + * - HCS_MAX_INTRS: the max number of interrupts the host can handle, + * with max number of interrupters based on the xhci HCSPARAMS1. + * - num_online_cpus: maximum MSI-X vectors per CPUs core. + * Add additional 1 vector to ensure always available interrupt. + */ + xhci->msix_count = min(num_online_cpus() + 1, + HCS_MAX_INTRS(xhci->hcs_params1)); + ret = pci_alloc_irq_vectors(pdev, xhci->msix_count, xhci->msix_count, + PCI_IRQ_MSIX); + if (ret < 0) { + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Failed to enable MSI-X"); + goto setup_msi; + } + + ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd", + xhci_to_hcd(xhci)); + if (ret) { + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt"); + pci_free_irq_vectors(pdev); + goto setup_msi; + } + + hcd->msi_enabled = 1; + hcd->msix_enabled = 1; + return 0; + +setup_msi: + /* fall back to MSI */ + ret = xhci_setup_msi(xhci); if (!ret) { hcd->msi_enabled = 1; return 0; From a795f708b2844054a7d12aae72397bf51d0f87b3 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Fri, 1 Dec 2023 17:06:42 +0200 Subject: [PATCH 042/169] xhci: refactor static MSI function The current way the xhci driver sets up MSI interrupts is overly complex and messy. The whole MSI setup can be done in one simple function. Continue refactoring MSI/MSI-X setup by incorporating 'xhci_setup_msi()' into 'xhci_try_enable_msi()'. Now all interrupt enabling is contained in one function, which should make it easier to rework. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-15-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 49 ++++++++++++------------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 370943c04881..dbec0a315566 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -116,35 +116,6 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) hcd->msix_enabled = 0; } -/* - * Set up MSI - */ -static int xhci_setup_msi(struct xhci_hcd *xhci) -{ - int ret; - /* - * TODO:Check with MSI Soc for sysdev - */ - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - - ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); - if (ret < 0) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "failed to allocate MSI entry"); - return ret; - } - - ret = request_irq(pdev->irq, xhci_msi_irq, - 0, "xhci_hcd", xhci_to_hcd(xhci)); - if (ret) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "disable MSI interrupt"); - pci_free_irq_vectors(pdev); - } - - return ret; -} - static int xhci_try_enable_msi(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -194,13 +165,23 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) return 0; setup_msi: - /* fall back to MSI */ - ret = xhci_setup_msi(xhci); - if (!ret) { - hcd->msi_enabled = 1; - return 0; + /* TODO: Check with MSI Soc for sysdev */ + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (ret < 0) { + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "failed to allocate MSI entry"); + goto legacy_irq; } + ret = request_irq(pdev->irq, xhci_msi_irq, 0, "xhci_hcd", xhci_to_hcd(xhci)); + if (ret) { + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI interrupt"); + pci_free_irq_vectors(pdev); + goto legacy_irq; + } + + hcd->msi_enabled = 1; + return 0; + legacy_irq: if (!pdev->irq) { xhci_err(xhci, "No msi-x/msi found and no IRQ in BIOS\n"); From dfbf4441f2d31d21e0a98c5fcff6dae8b57b79dd Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Fri, 1 Dec 2023 17:06:43 +0200 Subject: [PATCH 043/169] xhci: change 'msix_count' to encompass MSI or MSI-X vectors Instead of variable 'msix_count' containing the number of MSI-X vectors, now it can contains MSI or MSI-X vector amount. Because both interrupt methods allow several vectors. Thus, 'msix_count' is renamed to 'nvecs'. Additionally, instead of storing the maximum possible vector amount, now it stores the amount of successfully allocated vectors, or negative integer on allocation failure. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-16-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 14 +++++++------- drivers/usb/host/xhci.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index dbec0a315566..2307164a1e81 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -142,12 +142,12 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) * - num_online_cpus: maximum MSI-X vectors per CPUs core. * Add additional 1 vector to ensure always available interrupt. */ - xhci->msix_count = min(num_online_cpus() + 1, - HCS_MAX_INTRS(xhci->hcs_params1)); + xhci->nvecs = min(num_online_cpus() + 1, + HCS_MAX_INTRS(xhci->hcs_params1)); - ret = pci_alloc_irq_vectors(pdev, xhci->msix_count, xhci->msix_count, - PCI_IRQ_MSIX); - if (ret < 0) { + xhci->nvecs = pci_alloc_irq_vectors(pdev, xhci->nvecs, xhci->nvecs, + PCI_IRQ_MSIX); + if (xhci->nvecs < 0) { xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Failed to enable MSI-X"); goto setup_msi; } @@ -166,8 +166,8 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) setup_msi: /* TODO: Check with MSI Soc for sysdev */ - ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); - if (ret < 0) { + xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (xhci->nvecs < 0) { xhci_dbg_trace(xhci, trace_xhci_dbg_init, "failed to allocate MSI entry"); goto legacy_irq; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 62b610f00754..90e6b6ef7bd2 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1765,8 +1765,8 @@ struct xhci_hcd { int page_size; /* Valid values are 12 to 20, inclusive */ int page_shift; - /* msi-x vectors */ - int msix_count; + /* MSI-X/MSI vectors */ + int nvecs; /* optional clocks */ struct clk *clk; struct clk *reg_clk; From 9831960df237855ea3333e08c26cc9405a7ed440 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Fri, 1 Dec 2023 17:06:44 +0200 Subject: [PATCH 044/169] xhci: rework 'xhci_try_enable_msi()' MSI and MSI-X setup code Simplify 'xhci_try_enable_msi()' and reduce unnecessary function calls. xHCI driver first tries to allocate 'num_online_cpu()' number of MSI-X vectors, if that fails it falls back to a single MSI vector. There is no good reason for this, we currently only support a primary interrupter. However, we are still interested in knowing if there are more vectors available, which will be utilized once we get secondary interrupter support. Call 'pci_alloc_irq_vectors()' once (with MSI-X and MSI flag), instead of separately for MSI-X and MSI. And accept any number of MSI-X or MSI vectors between 1 and 'num_online_cpu()'. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-17-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 44 +++++++++++++------------------------ 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 2307164a1e81..398f81b0500b 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -116,13 +116,13 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) hcd->msix_enabled = 0; } +/* Try enabling MSI-X with MSI and legacy IRQ as fallback */ static int xhci_try_enable_msi(struct usb_hcd *hcd) { + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev; int ret; - pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); /* * Some Fresco Logic host controllers advertise MSI, but fail to * generate interrupts. Don't even try to enable MSI. @@ -145,42 +145,28 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) xhci->nvecs = min(num_online_cpus() + 1, HCS_MAX_INTRS(xhci->hcs_params1)); - xhci->nvecs = pci_alloc_irq_vectors(pdev, xhci->nvecs, xhci->nvecs, - PCI_IRQ_MSIX); + /* TODO: Check with MSI Soc for sysdev */ + xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, xhci->nvecs, + PCI_IRQ_MSIX | PCI_IRQ_MSI); if (xhci->nvecs < 0) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Failed to enable MSI-X"); - goto setup_msi; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "failed to allocate IRQ vectors"); + goto legacy_irq; } ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd", xhci_to_hcd(xhci)); - if (ret) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt"); - pci_free_irq_vectors(pdev); - goto setup_msi; - } + if (ret) + goto free_irq_vectors; hcd->msi_enabled = 1; - hcd->msix_enabled = 1; + hcd->msix_enabled = pdev->msix_enabled; return 0; -setup_msi: - /* TODO: Check with MSI Soc for sysdev */ - xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); - if (xhci->nvecs < 0) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "failed to allocate MSI entry"); - goto legacy_irq; - } - - ret = request_irq(pdev->irq, xhci_msi_irq, 0, "xhci_hcd", xhci_to_hcd(xhci)); - if (ret) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI interrupt"); - pci_free_irq_vectors(pdev); - goto legacy_irq; - } - - hcd->msi_enabled = 1; - return 0; +free_irq_vectors: + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable %s interrupt", + pdev->msix_enabled ? "MSI-X" : "MSI"); + pci_free_irq_vectors(pdev); legacy_irq: if (!pdev->irq) { From 36b24ebf9a04f19f1fe47ec0b50e5ca1d30dd1bc Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Fri, 1 Dec 2023 17:06:45 +0200 Subject: [PATCH 045/169] xhci: minor coding style cleanup in 'xhci_try_enable_msi()' Remove extra spaces/indentation and add spaces where required. This commit does not change any functionality. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-18-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 398f81b0500b..dfeca3cbac8b 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -178,12 +178,10 @@ legacy_irq: snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", hcd->driver->description, hcd->self.busnum); - /* fall back to legacy interrupt*/ - ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, - hcd->irq_descr, hcd); + /* fall back to legacy interrupt */ + ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd); if (ret) { - xhci_err(xhci, "request interrupt %d failed\n", - pdev->irq); + xhci_err(xhci, "request interrupt %d failed\n", pdev->irq); return ret; } hcd->irq = pdev->irq; From e34900f46cd69b2859b9894275415134a0988d9d Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 1 Dec 2023 17:06:46 +0200 Subject: [PATCH 046/169] xhci: Reconfigure endpoint 0 max packet size only during endpoint reset The max packet size for full speed control endpoint 0 may vary. It is defined in the device descriptor, which is read using the same endpoint. Usb core sets a temporary max packet size value until the real value is read. xhci driver needs to reconfigure the endpoint context seen by the controller if the max packet size changes. It makes more sense to do this reconfiguration in xhci_endpoint_reset() instead of urb enqueue as usb core will call endpoint reset during enumeration if the max packet values differ. Max packet size adjustment for endpoint 0 can only happen once per device enumeration. Previously the max packet size was checked during every urb enqueue. This is an additional check for every enqueued urb, and also turned out to have locking issues as urbs may be queued in any context while xhci max packet size reconfiguration requires memory allocation, locking, and sleeping. Tested with a full speed device using both old and new scheme enumeration and an intentionally incorrect preliminary max packet size value. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-19-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 85 ++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index cb885ddc2032..71a117db21a7 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1438,10 +1438,8 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, * descriptor. If the usb_device's max packet size changes after that point, * we need to issue an evaluate context command and wait on it. */ -static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, - unsigned int ep_index, struct urb *urb, gfp_t mem_flags) +static int xhci_check_ep0_maxpacket(struct xhci_hcd *xhci, struct xhci_virt_device *vdev) { - struct xhci_container_ctx *out_ctx; struct xhci_input_control_ctx *ctrl_ctx; struct xhci_ep_ctx *ep_ctx; struct xhci_command *command; @@ -1449,11 +1447,15 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, int hw_max_packet_size; int ret = 0; - out_ctx = xhci->devs[slot_id]->out_ctx; - ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); + ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, 0); hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); - max_packet_size = usb_endpoint_maxp(&urb->dev->ep0.desc); - if (hw_max_packet_size != max_packet_size) { + max_packet_size = usb_endpoint_maxp(&vdev->udev->ep0.desc); + + if (hw_max_packet_size == max_packet_size) + return 0; + + switch (max_packet_size) { + case 8: case 16: case 32: case 64: case 9: xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, "Max Packet Size for ep 0 changed."); xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, @@ -1465,28 +1467,22 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, "Issuing evaluate context command."); - /* Set up the input context flags for the command */ - /* FIXME: This won't work if a non-default control endpoint - * changes max packet sizes. - */ - - command = xhci_alloc_command(xhci, true, mem_flags); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) return -ENOMEM; - command->in_ctx = xhci->devs[slot_id]->in_ctx; + command->in_ctx = vdev->in_ctx; ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx); if (!ctrl_ctx) { xhci_warn(xhci, "%s: Could not get input context, bad type.\n", __func__); ret = -ENOMEM; - goto command_cleanup; + break; } /* Set up the modified control endpoint 0 */ - xhci_endpoint_copy(xhci, xhci->devs[slot_id]->in_ctx, - xhci->devs[slot_id]->out_ctx, ep_index); + xhci_endpoint_copy(xhci, vdev->in_ctx, vdev->out_ctx, 0); - ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, ep_index); + ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, 0); ep_ctx->ep_info &= cpu_to_le32(~EP_STATE_MASK);/* must clear */ ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); @@ -1494,17 +1490,20 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG); ctrl_ctx->drop_flags = 0; - ret = xhci_configure_endpoint(xhci, urb->dev, command, - true, false); - - /* Clean up the input context for later use by bandwidth - * functions. - */ + ret = xhci_configure_endpoint(xhci, vdev->udev, command, + true, false); + /* Clean up the input context for later use by bandwidth functions */ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG); -command_cleanup: - kfree(command->completion); - kfree(command); + break; + default: + dev_dbg(&vdev->udev->dev, "incorrect max packet size %d for ep0\n", + max_packet_size); + return -EINVAL; } + + kfree(command->completion); + kfree(command); + return ret; } @@ -1561,21 +1560,6 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag trace_xhci_urb_enqueue(urb); - if (usb_endpoint_xfer_control(&urb->ep->desc)) { - /* Check to see if the max packet size for the default control - * endpoint changed during FS device enumeration - */ - if (urb->dev->speed == USB_SPEED_FULL) { - ret = xhci_check_maxpacket(xhci, slot_id, - ep_index, urb, mem_flags); - if (ret < 0) { - xhci_urb_free_priv(urb_priv); - urb->hcpriv = NULL; - return ret; - } - } - } - spin_lock_irqsave(&xhci->lock, flags); if (xhci->xhc_state & XHCI_STATE_DYING) { @@ -3104,6 +3088,21 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, int err; xhci = hcd_to_xhci(hcd); + ep_index = xhci_get_endpoint_index(&host_ep->desc); + + /* + * Usb core assumes a max packet value for ep0 on FS devices until the + * real value is read from the descriptor. Core resets Ep0 if values + * mismatch. Reconfigure the xhci ep0 endpoint context here in that case + */ + if (usb_endpoint_xfer_control(&host_ep->desc) && ep_index == 0) { + udev = container_of(host_ep, struct usb_device, ep0); + if (udev->speed == USB_SPEED_FULL) + xhci_check_ep0_maxpacket(xhci, xhci->devs[udev->slot_id]); + /* Nothing else should be done here for ep0 during ep reset */ + return; + } + if (!host_ep->hcpriv) return; udev = (struct usb_device *) host_ep->hcpriv; @@ -3116,7 +3115,7 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, */ if (!udev->slot_id || !vdev) return; - ep_index = xhci_get_endpoint_index(&host_ep->desc); + ep = &vdev->eps[ep_index]; /* Bail out if toggle is already being cleared by a endpoint reset */ From e2e2aacf042f52854c92775b7800ba668e0bdfe4 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 1 Dec 2023 17:06:47 +0200 Subject: [PATCH 047/169] xhci: fix possible null pointer deref during xhci urb enqueue There is a short gap between urb being submitted and actually added to the endpoint queue (linked). If the device is disconnected during this time then usb core is not yet aware of the pending urb, and device may be freed just before xhci_urq_enqueue() continues, dereferencing the freed device. Freeing the device is protected by the xhci spinlock, so make sure we take and keep the lock while checking that device exists, dereference it, and add the urb to the queue. Remove the unnecessary URB check, usb core checks it before calling xhci_urb_enqueue() Suggested-by: Kuen-Han Tsai Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231201150647.1307406-20-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 71a117db21a7..08fbabff23a0 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1521,24 +1521,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag struct urb_priv *urb_priv; int num_tds; - if (!urb) - return -EINVAL; - ret = xhci_check_args(hcd, urb->dev, urb->ep, - true, true, __func__); - if (ret <= 0) - return ret ? ret : -EINVAL; - - slot_id = urb->dev->slot_id; ep_index = xhci_get_endpoint_index(&urb->ep->desc); - ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; - - if (!HCD_HW_ACCESSIBLE(hcd)) - return -ESHUTDOWN; - - if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { - xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); - return -ENODEV; - } if (usb_endpoint_xfer_isoc(&urb->ep->desc)) num_tds = urb->number_of_packets; @@ -1562,12 +1545,35 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag spin_lock_irqsave(&xhci->lock, flags); + ret = xhci_check_args(hcd, urb->dev, urb->ep, + true, true, __func__); + if (ret <= 0) { + ret = ret ? ret : -EINVAL; + goto free_priv; + } + + slot_id = urb->dev->slot_id; + + if (!HCD_HW_ACCESSIBLE(hcd)) { + ret = -ESHUTDOWN; + goto free_priv; + } + + if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { + xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); + ret = -ENODEV; + goto free_priv; + } + if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for non-responsive xHCI host.\n", urb->ep->desc.bEndpointAddress, urb); ret = -ESHUTDOWN; goto free_priv; } + + ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; + if (*ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) { xhci_warn(xhci, "WARN: Can't enqueue URB, ep in streams transition state %x\n", *ep_state); From 36d8aef52d0562b5b1dc2e54d0fad1dee07182c2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 30 Nov 2023 10:54:35 +0000 Subject: [PATCH 048/169] usb: atm: Remove snprintf() from sysfs call-backs and replace with sysfs_emit() Since snprintf() has the documented, but still rather strange trait of returning the length of the data that *would have been* written to the array if space were available, rather than the arguably more useful length of data *actually* written, it is usually considered wise to use something else instead in order to avoid confusion. In the case of sysfs call-backs, new wrappers exist that do just that. This patch replaces the 2 uses of snprintf() found in the sysfs .show() call-backs with the new sysfs_emit() helpers. Whist we're at it, let's replace the sprintf()s as well. For no other reason than consistency. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Matthieu CASTET Cc: Stanislaw Gruszka Cc: Greg Kroah-Hartman Cc: Damien Bergamini Cc: linux-usb@vger.kernel.org Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231130105459.3208986-2-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 5812f7ea7f90..8d5580d8d20a 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2252,7 +2252,7 @@ static ssize_t stat_status_show(struct device *dev, struct device_attribute *att sc = dev_to_uea(dev); if (!sc) goto out; - ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state); + ret = sysfs_emit(buf, "%08x\n", sc->stats.phy.state); out: mutex_unlock(&uea_mutex); return ret; @@ -2318,19 +2318,19 @@ static ssize_t stat_human_status_show(struct device *dev, switch (modem_state) { case 0: - ret = sprintf(buf, "Modem is booting\n"); + ret = sysfs_emit(buf, "Modem is booting\n"); break; case 1: - ret = sprintf(buf, "Modem is initializing\n"); + ret = sysfs_emit(buf, "Modem is initializing\n"); break; case 2: - ret = sprintf(buf, "Modem is operational\n"); + ret = sysfs_emit(buf, "Modem is operational\n"); break; case 3: - ret = sprintf(buf, "Modem synchronization failed\n"); + ret = sysfs_emit(buf, "Modem synchronization failed\n"); break; default: - ret = sprintf(buf, "Modem state is unknown\n"); + ret = sysfs_emit(buf, "Modem state is unknown\n"); break; } out: @@ -2364,7 +2364,7 @@ static ssize_t stat_delin_show(struct device *dev, struct device_attribute *attr delin = "LOSS"; } - ret = sprintf(buf, "%s\n", delin); + ret = sysfs_emit(buf, "%s\n", delin); out: mutex_unlock(&uea_mutex); return ret; @@ -2384,7 +2384,7 @@ static ssize_t stat_##name##_show(struct device *dev, \ sc = dev_to_uea(dev); \ if (!sc) \ goto out; \ - ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.name); \ + ret = sysfs_emit(buf, "%08x\n", sc->stats.phy.name); \ if (reset) \ sc->stats.phy.name = 0; \ out: \ From b385ef088c7aab20a2c0dc20d390d69a6620f0f3 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 30 Nov 2023 10:54:36 +0000 Subject: [PATCH 049/169] usb: cdnsp: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. The uses in this file all seem to assume that data *has been* written! Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Pawel Laszczak Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231130105459.3208986-3-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdnsp-debug.h | 354 ++++++++++++++++---------------- 1 file changed, 177 insertions(+), 177 deletions(-) diff --git a/drivers/usb/cdns3/cdnsp-debug.h b/drivers/usb/cdns3/cdnsp-debug.h index ad617b7455b9..cd138acdcce1 100644 --- a/drivers/usb/cdns3/cdnsp-debug.h +++ b/drivers/usb/cdns3/cdnsp-debug.h @@ -187,202 +187,202 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0, switch (type) { case TRB_LINK: - ret = snprintf(str, size, - "LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c", - field1, field0, GET_INTR_TARGET(field2), - cdnsp_trb_type_string(type), - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CHAIN ? 'C' : 'c', - field3 & TRB_TC ? 'T' : 't', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c", + field1, field0, GET_INTR_TARGET(field2), + cdnsp_trb_type_string(type), + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_TC ? 'T' : 't', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_TRANSFER: case TRB_COMPLETION: case TRB_PORT_STATUS: case TRB_HC_EVENT: - ret = snprintf(str, size, - "ep%d%s(%d) type '%s' TRB %08x%08x status '%s'" - " len %ld slot %ld flags %c:%c", - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), - cdnsp_trb_type_string(type), field1, field0, - cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)), - EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3), - field3 & EVENT_DATA ? 'E' : 'e', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "ep%d%s(%d) type '%s' TRB %08x%08x status '%s'" + " len %ld slot %ld flags %c:%c", + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), + cdnsp_trb_type_string(type), field1, field0, + cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)), + EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3), + field3 & EVENT_DATA ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_MFINDEX_WRAP: - ret = snprintf(str, size, "%s: flags %c", - cdnsp_trb_type_string(type), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, "%s: flags %c", + cdnsp_trb_type_string(type), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_SETUP: - ret = snprintf(str, size, - "type '%s' bRequestType %02x bRequest %02x " - "wValue %02x%02x wIndex %02x%02x wLength %d " - "length %ld TD size %ld intr %ld Setup ID %ld " - "flags %c:%c:%c", - cdnsp_trb_type_string(type), - field0 & 0xff, - (field0 & 0xff00) >> 8, - (field0 & 0xff000000) >> 24, - (field0 & 0xff0000) >> 16, - (field1 & 0xff00) >> 8, - field1 & 0xff, - (field1 & 0xff000000) >> 16 | - (field1 & 0xff0000) >> 16, - TRB_LEN(field2), GET_TD_SIZE(field2), - GET_INTR_TARGET(field2), - TRB_SETUPID_TO_TYPE(field3), - field3 & TRB_IDT ? 'D' : 'd', - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "type '%s' bRequestType %02x bRequest %02x " + "wValue %02x%02x wIndex %02x%02x wLength %d " + "length %ld TD size %ld intr %ld Setup ID %ld " + "flags %c:%c:%c", + cdnsp_trb_type_string(type), + field0 & 0xff, + (field0 & 0xff00) >> 8, + (field0 & 0xff000000) >> 24, + (field0 & 0xff0000) >> 16, + (field1 & 0xff00) >> 8, + field1 & 0xff, + (field1 & 0xff000000) >> 16 | + (field1 & 0xff0000) >> 16, + TRB_LEN(field2), GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + TRB_SETUPID_TO_TYPE(field3), + field3 & TRB_IDT ? 'D' : 'd', + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_DATA: - ret = snprintf(str, size, - "type '%s' Buffer %08x%08x length %ld TD size %ld " - "intr %ld flags %c:%c:%c:%c:%c:%c:%c", - cdnsp_trb_type_string(type), - field1, field0, TRB_LEN(field2), - GET_TD_SIZE(field2), - GET_INTR_TARGET(field2), - field3 & TRB_IDT ? 'D' : 'i', - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CHAIN ? 'C' : 'c', - field3 & TRB_NO_SNOOP ? 'S' : 's', - field3 & TRB_ISP ? 'I' : 'i', - field3 & TRB_ENT ? 'E' : 'e', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "type '%s' Buffer %08x%08x length %ld TD size %ld " + "intr %ld flags %c:%c:%c:%c:%c:%c:%c", + cdnsp_trb_type_string(type), + field1, field0, TRB_LEN(field2), + GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + field3 & TRB_IDT ? 'D' : 'i', + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_NO_SNOOP ? 'S' : 's', + field3 & TRB_ISP ? 'I' : 'i', + field3 & TRB_ENT ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_STATUS: - ret = snprintf(str, size, - "Buffer %08x%08x length %ld TD size %ld intr" - "%ld type '%s' flags %c:%c:%c:%c", - field1, field0, TRB_LEN(field2), - GET_TD_SIZE(field2), - GET_INTR_TARGET(field2), - cdnsp_trb_type_string(type), - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CHAIN ? 'C' : 'c', - field3 & TRB_ENT ? 'E' : 'e', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "Buffer %08x%08x length %ld TD size %ld intr" + "%ld type '%s' flags %c:%c:%c:%c", + field1, field0, TRB_LEN(field2), + GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + cdnsp_trb_type_string(type), + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_ENT ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_NORMAL: case TRB_ISOC: case TRB_EVENT_DATA: case TRB_TR_NOOP: - ret = snprintf(str, size, - "type '%s' Buffer %08x%08x length %ld " - "TD size %ld intr %ld " - "flags %c:%c:%c:%c:%c:%c:%c:%c:%c", - cdnsp_trb_type_string(type), - field1, field0, TRB_LEN(field2), - GET_TD_SIZE(field2), - GET_INTR_TARGET(field2), - field3 & TRB_BEI ? 'B' : 'b', - field3 & TRB_IDT ? 'T' : 't', - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CHAIN ? 'C' : 'c', - field3 & TRB_NO_SNOOP ? 'S' : 's', - field3 & TRB_ISP ? 'I' : 'i', - field3 & TRB_ENT ? 'E' : 'e', - field3 & TRB_CYCLE ? 'C' : 'c', - !(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v'); + ret = scnprintf(str, size, + "type '%s' Buffer %08x%08x length %ld " + "TD size %ld intr %ld " + "flags %c:%c:%c:%c:%c:%c:%c:%c:%c", + cdnsp_trb_type_string(type), + field1, field0, TRB_LEN(field2), + GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + field3 & TRB_BEI ? 'B' : 'b', + field3 & TRB_IDT ? 'T' : 't', + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_NO_SNOOP ? 'S' : 's', + field3 & TRB_ISP ? 'I' : 'i', + field3 & TRB_ENT ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c', + !(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v'); break; case TRB_CMD_NOOP: case TRB_ENABLE_SLOT: - ret = snprintf(str, size, "%s: flags %c", - cdnsp_trb_type_string(type), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, "%s: flags %c", + cdnsp_trb_type_string(type), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_DISABLE_SLOT: - ret = snprintf(str, size, "%s: slot %ld flags %c", - cdnsp_trb_type_string(type), - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, "%s: slot %ld flags %c", + cdnsp_trb_type_string(type), + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_ADDR_DEV: - ret = snprintf(str, size, - "%s: ctx %08x%08x slot %ld flags %c:%c", - cdnsp_trb_type_string(type), field1, field0, - TRB_TO_SLOT_ID(field3), - field3 & TRB_BSR ? 'B' : 'b', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ctx %08x%08x slot %ld flags %c:%c", + cdnsp_trb_type_string(type), field1, field0, + TRB_TO_SLOT_ID(field3), + field3 & TRB_BSR ? 'B' : 'b', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_CONFIG_EP: - ret = snprintf(str, size, - "%s: ctx %08x%08x slot %ld flags %c:%c", - cdnsp_trb_type_string(type), field1, field0, - TRB_TO_SLOT_ID(field3), - field3 & TRB_DC ? 'D' : 'd', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ctx %08x%08x slot %ld flags %c:%c", + cdnsp_trb_type_string(type), field1, field0, + TRB_TO_SLOT_ID(field3), + field3 & TRB_DC ? 'D' : 'd', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_EVAL_CONTEXT: - ret = snprintf(str, size, - "%s: ctx %08x%08x slot %ld flags %c", - cdnsp_trb_type_string(type), field1, field0, - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ctx %08x%08x slot %ld flags %c", + cdnsp_trb_type_string(type), field1, field0, + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_RESET_EP: case TRB_HALT_ENDPOINT: - ret = snprintf(str, size, - "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", - cdnsp_trb_type_string(type), - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), field1, field0, - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", + cdnsp_trb_type_string(type), + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), field1, field0, + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_STOP_RING: - ret = snprintf(str, size, - "%s: ep%d%s(%d) slot %ld sp %d flags %c", - cdnsp_trb_type_string(type), - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), - TRB_TO_SLOT_ID(field3), - TRB_TO_SUSPEND_PORT(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ep%d%s(%d) slot %ld sp %d flags %c", + cdnsp_trb_type_string(type), + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), + TRB_TO_SLOT_ID(field3), + TRB_TO_SUSPEND_PORT(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_SET_DEQ: - ret = snprintf(str, size, - "%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c", - cdnsp_trb_type_string(type), - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), field1, field0, - TRB_TO_STREAM_ID(field2), - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c", + cdnsp_trb_type_string(type), + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), field1, field0, + TRB_TO_STREAM_ID(field2), + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_RESET_DEV: - ret = snprintf(str, size, "%s: slot %ld flags %c", - cdnsp_trb_type_string(type), - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, "%s: slot %ld flags %c", + cdnsp_trb_type_string(type), + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_ENDPOINT_NRDY: temp = TRB_TO_HOST_STREAM(field2); - ret = snprintf(str, size, - "%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c", - cdnsp_trb_type_string(type), - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), temp, - temp == STREAM_PRIME_ACK ? "(PRIME)" : "", - temp == STREAM_REJECTED ? "(REJECTED)" : "", - TRB_TO_DEV_STREAM(field0), - field3 & TRB_STAT ? 'S' : 's', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c", + cdnsp_trb_type_string(type), + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), temp, + temp == STREAM_PRIME_ACK ? "(PRIME)" : "", + temp == STREAM_REJECTED ? "(REJECTED)" : "", + TRB_TO_DEV_STREAM(field0), + field3 & TRB_STAT ? 'S' : 's', + field3 & TRB_CYCLE ? 'C' : 'c'); break; default: - ret = snprintf(str, size, - "type '%s' -> raw %08x %08x %08x %08x", - cdnsp_trb_type_string(type), - field0, field1, field2, field3); + ret = scnprintf(str, size, + "type '%s' -> raw %08x %08x %08x %08x", + cdnsp_trb_type_string(type), + field0, field1, field2, field3); } - if (ret >= size) - pr_info("CDNSP: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNSP: buffer may be truncated.\n"); return str; } @@ -465,32 +465,32 @@ static inline const char *cdnsp_decode_portsc(char *str, size_t size, { int ret; - ret = snprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ", - portsc & PORT_POWER ? "Powered" : "Powered-off", - portsc & PORT_CONNECT ? "Connected" : "Not-connected", - portsc & PORT_PED ? "Enabled" : "Disabled", - cdnsp_portsc_link_state_string(portsc), - DEV_PORT_SPEED(portsc)); + ret = scnprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ", + portsc & PORT_POWER ? "Powered" : "Powered-off", + portsc & PORT_CONNECT ? "Connected" : "Not-connected", + portsc & PORT_PED ? "Enabled" : "Disabled", + cdnsp_portsc_link_state_string(portsc), + DEV_PORT_SPEED(portsc)); if (portsc & PORT_RESET) - ret += snprintf(str + ret, size - ret, "In-Reset "); + ret += scnprintf(str + ret, size - ret, "In-Reset "); - ret += snprintf(str + ret, size - ret, "Change: "); + ret += scnprintf(str + ret, size - ret, "Change: "); if (portsc & PORT_CSC) - ret += snprintf(str + ret, size - ret, "CSC "); + ret += scnprintf(str + ret, size - ret, "CSC "); if (portsc & PORT_WRC) - ret += snprintf(str + ret, size - ret, "WRC "); + ret += scnprintf(str + ret, size - ret, "WRC "); if (portsc & PORT_RC) - ret += snprintf(str + ret, size - ret, "PRC "); + ret += scnprintf(str + ret, size - ret, "PRC "); if (portsc & PORT_PLC) - ret += snprintf(str + ret, size - ret, "PLC "); + ret += scnprintf(str + ret, size - ret, "PLC "); if (portsc & PORT_CEC) - ret += snprintf(str + ret, size - ret, "CEC "); - ret += snprintf(str + ret, size - ret, "Wake: "); + ret += scnprintf(str + ret, size - ret, "CEC "); + ret += scnprintf(str + ret, size - ret, "Wake: "); if (portsc & PORT_WKCONN_E) - ret += snprintf(str + ret, size - ret, "WCE "); + ret += scnprintf(str + ret, size - ret, "WCE "); if (portsc & PORT_WKDISC_E) - ret += snprintf(str + ret, size - ret, "WDE "); + ret += scnprintf(str + ret, size - ret, "WDE "); return str; } @@ -562,20 +562,20 @@ static inline const char *cdnsp_decode_ep_context(char *str, size_t size, avg = EP_AVG_TRB_LENGTH(tx_info); - ret = snprintf(str, size, "State %s mult %d max P. Streams %d %s", - cdnsp_ep_state_string(ep_state), mult, - max_pstr, lsa ? "LSA " : ""); + ret = scnprintf(str, size, "State %s mult %d max P. Streams %d %s", + cdnsp_ep_state_string(ep_state), mult, + max_pstr, lsa ? "LSA " : ""); - ret += snprintf(str + ret, size - ret, - "interval %d us max ESIT payload %d CErr %d ", - (1 << interval) * 125, esit, cerr); + ret += scnprintf(str + ret, size - ret, + "interval %d us max ESIT payload %d CErr %d ", + (1 << interval) * 125, esit, cerr); - ret += snprintf(str + ret, size - ret, - "Type %s %sburst %d maxp %d deq %016llx ", - cdnsp_ep_type_string(ep_type), hid ? "HID" : "", - burst, maxp, deq); + ret += scnprintf(str + ret, size - ret, + "Type %s %sburst %d maxp %d deq %016llx ", + cdnsp_ep_type_string(ep_type), hid ? "HID" : "", + burst, maxp, deq); - ret += snprintf(str + ret, size - ret, "avg trb len %d", avg); + ret += scnprintf(str + ret, size - ret, "avg trb len %d", avg); return str; } From 7d7f794482b74e39d8c0cd830333eb40fc0234d4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 30 Nov 2023 10:54:37 +0000 Subject: [PATCH 050/169] usb: fotg210-hcd: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. The uses in this file both seem to assume that data *has been* written! Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Linus Walleij Cc: Greg Kroah-Hartman Cc: Yuan-Hsin Chen Cc: Feng-Hsin Chiang Cc: Po-Yu Chuang Cc: linux-usb@vger.kernel.org Signed-off-by: Lee Jones Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20231130105459.3208986-4-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/fotg210/fotg210-hcd.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 929106c16b29..b2f8b53cc8ef 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -404,9 +404,9 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, else if (td->hw_alt_next != list_end) mark = '/'; } - temp = snprintf(next, size, - "\n\t%p%c%s len=%d %08x urb %p", - td, mark, ({ char *tmp; + temp = scnprintf(next, size, + "\n\t%p%c%s len=%d %08x urb %p", + td, mark, ({ char *tmp; switch ((scratch>>8)&0x03) { case 0: tmp = "out"; @@ -424,17 +424,13 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, (scratch >> 16) & 0x7fff, scratch, td->urb); - if (size < temp) - temp = size; size -= temp; next += temp; if (temp == size) goto done; } - temp = snprintf(next, size, "\n"); - if (size < temp) - temp = size; + temp = scnprintf(next, size, "\n"); size -= temp; next += temp; From 38168e2de320b9c12132eb72cf1f1d3d411fe38c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 30 Nov 2023 10:54:38 +0000 Subject: [PATCH 051/169] usb: gadget: Remove snprintf() from sysfs call-backs and replace with sysfs_emit() Since snprintf() has the documented, but still rather strange trait of returning the length of the data that *would have been* written to the array if space were available, rather than the arguably more useful length of data *actually* written, it is usually considered wise to use something else instead in order to avoid confusion. In the case of sysfs call-backs, new wrappers exist that do just that. This patch replaces just one use of snprintf() found in the sysfs .show() call-back with the new sysfs_emit() helper. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231130105459.3208986-5-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/configfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 4c639e9ddedc..b7d2a1313a68 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -812,7 +812,7 @@ static ssize_t gadget_string_s_show(struct config_item *item, char *page) struct gadget_string *string = to_gadget_string(item); int ret; - ret = snprintf(page, sizeof(string->string), "%s\n", string->string); + ret = sysfs_emit(page, "%s\n", string->string); return ret; } From dadc0f0f7afc0273ff680b504ed830d0c307dea3 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 30 Nov 2023 10:54:39 +0000 Subject: [PATCH 052/169] usb: gadget: f_tcm: Remove snprintf() from sysfs call-backs and replace with sysfs_emit() Since snprintf() has the documented, but still rather strange trait of returning the length of the data that *would have been* written to the array if space were available, rather than the arguably more useful length of data *actually* written, it is usually considered wise to use something else instead in order to avoid confusion. In the case of sysfs call-backs, new wrappers exist that do just that. This patch replaces just one use of snprintf() found in the sysfs .show() call-back with the new sysfs_emit() helper. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Greg Kroah-Hartman Cc: "Martin K. Petersen" Cc: Dmitry Bogdanov Cc: linux-usb@vger.kernel.org Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231130105459.3208986-6-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_tcm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index ff33f31bcdf6..37befd6db001 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -1504,8 +1504,8 @@ static ssize_t tcm_usbg_tpg_nexus_show(struct config_item *item, char *page) ret = -ENODEV; goto out; } - ret = snprintf(page, PAGE_SIZE, "%s\n", - tv_nexus->tvn_se_sess->se_node_acl->initiatorname); + ret = sysfs_emit(page, "%s\n", + tv_nexus->tvn_se_sess->se_node_acl->initiatorname); out: mutex_unlock(&tpg->tpg_mutex); return ret; From c2d95fcff0f01fa00d9683dddeeea6732b74c779 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 1 Dec 2023 10:29:50 -0800 Subject: [PATCH 053/169] usb: core: Don't force USB generic_subclass drivers to define probe() There's no real reason that subclassed USB drivers _need_ to define probe() since they might want to subclass for some other reason. Make it optional to define probe() if we're a generic_subclass. Signed-off-by: Douglas Anderson Reviewed-by: Grant Grundler Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20231201102946.v2.1.I7ea0dd55ee2acdb48b0e6d28c1a704ab2c29206f@changeid Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index f58a0299fb3b..1dc0c0413043 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -290,7 +290,10 @@ static int usb_probe_device(struct device *dev) * specialised device drivers prior to setting the * use_generic_driver bit. */ - error = udriver->probe(udev); + if (udriver->probe) + error = udriver->probe(udev); + else if (!udriver->generic_subclass) + error = -EINVAL; if (error == -ENODEV && udriver != &usb_generic_driver && (udriver->id_table || udriver->match)) { udev->use_generic_driver = 1; From a87b8e3be926af0fc3b9b1af42b1127bd1ff077c Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 1 Dec 2023 10:29:51 -0800 Subject: [PATCH 054/169] usb: core: Allow subclassed USB drivers to override usb_choose_configuration() For some USB devices we might want to do something different for usb_choose_configuration(). One example here is the r8152 driver where we want to end up using the vendor driver with the preferred interface. The r8152 driver tried to make things work by implementing a USB generic_subclass driver and then overriding the normal config selection after it happened. This is less than ideal and also caused breakage if someone deauthorized and re-authorized the USB device because the USB core ended up going back to it's default logic for choosing the best config. I made an attempt to fix this [1] but it was a bit ugly. Let's do this better and allow USB generic_subclass drivers to override usb_choose_configuration(). [1] https://lore.kernel.org/r/20231130154337.1.Ie00e07f07f87149c9ce0b27ae4e26991d307e14b@changeid Suggested-by: Alan Stern Signed-off-by: Douglas Anderson Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20231201102946.v2.2.Iade5fa31997f1a0ca3e1dec0591633b02471df12@changeid Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 7 +++++++ include/linux/usb.h | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 740342a2812a..dcb897158228 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -59,10 +59,17 @@ int usb_choose_configuration(struct usb_device *udev) int num_configs; int insufficient_power = 0; struct usb_host_config *c, *best; + struct usb_device_driver *udriver = to_usb_device_driver(udev->dev.driver); if (usb_device_is_owned(udev)) return 0; + if (udriver->choose_configuration) { + i = udriver->choose_configuration(udev); + if (i >= 0) + return i; + } + best = NULL; c = udev->config; num_configs = udev->descriptor.bNumConfigurations; diff --git a/include/linux/usb.h b/include/linux/usb.h index 8c61643acd49..618e5a0b1a22 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1264,6 +1264,9 @@ struct usb_driver { * module is being unloaded. * @suspend: Called when the device is going to be suspended by the system. * @resume: Called when the device is being resumed by the system. + * @choose_configuration: If non-NULL, called instead of the default + * usb_choose_configuration(). If this returns an error then we'll go + * on to call the normal usb_choose_configuration(). * @dev_groups: Attributes attached to the device that will be created once it * is bound to the driver. * @drvwrap: Driver-model core structure wrapper. @@ -1287,6 +1290,9 @@ struct usb_device_driver { int (*suspend) (struct usb_device *udev, pm_message_t message); int (*resume) (struct usb_device *udev, pm_message_t message); + + int (*choose_configuration) (struct usb_device *udev); + const struct attribute_group **dev_groups; struct usbdrv_wrap drvwrap; const struct usb_device_id *id_table; From aa4f2b3e418e8673e55145de8b8016a7a9920306 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 1 Dec 2023 10:29:52 -0800 Subject: [PATCH 055/169] r8152: Choose our USB config with choose_configuration() rather than probe() If you deauthorize the r8152 device (by writing 0 to the "authorized" field in sysfs) and then reauthorize it (by writing a 1) then it no longer works. This is because when you do the above we lose the special configuration that we set in rtl8152_cfgselector_probe(). Deauthorizing causes the config to be set to -1 and then reauthorizing runs the default logic for choosing the best config. I made an attempt to fix it so that the config is kept across deauthorizing / reauthorizing [1] but it was a bit ugly. Let's instead use the new USB core feature to override choose_configuration(). This patch relies upon the patches ("usb: core: Don't force USB generic_subclass drivers to define probe()") and ("usb: core: Allow subclassed USB drivers to override usb_choose_configuration()") [1] https://lore.kernel.org/r/20231130154337.1.Ie00e07f07f87149c9ce0b27ae4e26991d307e14b@changeid Fixes: ec51fbd1b8a2 ("r8152: add USB device driver for config selection") Suggested-by: Alan Stern Signed-off-by: Douglas Anderson Reviewed-by: Grant Grundler Link: https://lore.kernel.org/r/20231201102946.v2.3.Ie00e07f07f87149c9ce0b27ae4e26991d307e14b@changeid Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/r8152.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 2c5c1e91ded6..0da723d11326 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -10053,7 +10053,7 @@ static struct usb_driver rtl8152_driver = { .disable_hub_initiated_lpm = 1, }; -static int rtl8152_cfgselector_probe(struct usb_device *udev) +static int rtl8152_cfgselector_choose_configuration(struct usb_device *udev) { struct usb_host_config *c; int i, num_configs; @@ -10080,19 +10080,13 @@ static int rtl8152_cfgselector_probe(struct usb_device *udev) if (i == num_configs) return -ENODEV; - if (usb_set_configuration(udev, c->desc.bConfigurationValue)) { - dev_err(&udev->dev, "Failed to set configuration %d\n", - c->desc.bConfigurationValue); - return -ENODEV; - } - - return 0; + return c->desc.bConfigurationValue; } static struct usb_device_driver rtl8152_cfgselector_driver = { - .name = MODULENAME "-cfgselector", - .probe = rtl8152_cfgselector_probe, - .id_table = rtl8152_table, + .name = MODULENAME "-cfgselector", + .choose_configuration = rtl8152_cfgselector_choose_configuration, + .id_table = rtl8152_table, .generic_subclass = 1, .supports_autosuspend = 1, }; From 6a4d4a27f986ac19338da8f18d73eed80ce28fca Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 23 Nov 2023 16:37:01 +0100 Subject: [PATCH 056/169] usb: typec: tps6598x: add reset gpio support The TPS6598x PD controller provides an active-high hardware reset input that reinitializes all device settings. If it is not grounded by design, the driver must be able to de-assert it in order to initialize the device. The PD controller is not ready for registration right after the reset de-assertion and a delay must be introduced in that case. According to TI, the delay can reach up to 1000 ms [1], which is in line with the experimental results obtained with a TPS65987D. Add a GPIO descriptor for the reset signal and basic reset management for initialization and suspend/resume. [1] https://e2e.ti.com/support/power-management-group/power-management/ f/power-management-forum/1269856/tps65987d-tps65987d-reset-de-assert- to-normal-operation/4809389#4809389 Signed-off-by: Javier Carrasco Reviewed-by: Bryan O'Donoghue Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230912-topic-tps6598x_reset-v3-1-0c2873070a77@wolfvision.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 196535ad996d..806ef68273ca 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,9 @@ #define TPS_PBMC_RC 0 /* Return code */ #define TPS_PBMC_DPCS 2 /* device patch complete status */ +/* reset de-assertion to ready for operation */ +#define TPS_SETUP_MS 1000 + enum { TPS_PORTINFO_SINK, TPS_PORTINFO_SINK_ACCESSORY, @@ -119,6 +123,7 @@ struct tps6598x { struct mutex lock; /* device lock */ u8 i2c_protocol:1; + struct gpio_desc *reset; struct typec_port *port; struct typec_partner *partner; struct usb_pd_identity partner_identity; @@ -1191,6 +1196,13 @@ static int tps6598x_probe(struct i2c_client *client) mutex_init(&tps->lock); tps->dev = &client->dev; + tps->reset = devm_gpiod_get_optional(tps->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(tps->reset)) + return dev_err_probe(tps->dev, PTR_ERR(tps->reset), + "failed to get reset GPIO\n"); + if (tps->reset) + msleep(TPS_SETUP_MS); + tps->regmap = devm_regmap_init_i2c(client, &tps6598x_regmap_config); if (IS_ERR(tps->regmap)) return PTR_ERR(tps->regmap); @@ -1348,6 +1360,9 @@ static void tps6598x_remove(struct i2c_client *client) /* Reset PD controller to remove any applied patch */ if (device_is_compatible(tps->dev, "ti,tps25750")) tps6598x_exec_cmd_tmo(tps, "GAID", 0, NULL, 0, NULL, 2000, 0); + + if (tps->reset) + gpiod_set_value_cansleep(tps->reset, 1); } static int __maybe_unused tps6598x_suspend(struct device *dev) @@ -1358,6 +1373,8 @@ static int __maybe_unused tps6598x_suspend(struct device *dev) if (tps->wakeup) { disable_irq(client->irq); enable_irq_wake(client->irq); + } else if (tps->reset) { + gpiod_set_value_cansleep(tps->reset, 1); } if (!client->irq) @@ -1385,6 +1402,9 @@ static int __maybe_unused tps6598x_resume(struct device *dev) if (tps->wakeup) { disable_irq_wake(client->irq); enable_irq(client->irq); + } else if (tps->reset) { + gpiod_set_value_cansleep(tps->reset, 0); + msleep(TPS_SETUP_MS); } if (!client->irq) From 9f802703fde29011aa9003cb1c5a418623a944ca Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 23 Nov 2023 16:37:02 +0100 Subject: [PATCH 057/169] dt-bindings: usb: tps6598x: add reset-gpios property The TPS6598x device family provides a high-level reset pin. It can be either grounded or used to reinitialize all device settings. Document the reset GPIO as an optional property and add it to the existing example. Signed-off-by: Javier Carrasco Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230912-topic-tps6598x_reset-v3-2-0c2873070a77@wolfvision.net Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/ti,tps6598x.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml b/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml index 323d664ae06a..1745e28b3110 100644 --- a/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml +++ b/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml @@ -38,6 +38,10 @@ properties: - const: main - const: patch-address + reset-gpios: + description: GPIO used for the HRESET pin. + maxItems: 1 + wakeup-source: true interrupts: @@ -90,6 +94,7 @@ additionalProperties: false examples: - | + #include #include i2c { #address-cells = <1>; @@ -106,6 +111,7 @@ examples: pinctrl-names = "default"; pinctrl-0 = <&typec_pins>; + reset-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; typec_con: connector { compatible = "usb-c-connector"; From db9e54709895241dda23f9347f619afb15291353 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Tue, 21 Nov 2023 20:38:47 +0000 Subject: [PATCH 058/169] usb: typec: tcpm: add tcpm_port_error_recovery symbol Add tcpm_port_error_recovery symbol and corresponding event that runs in tcpm_pd_event handler to set the port to the ERROR_RECOVERY state. tcpci drivers can use the symbol to reset the port when tcpc faults affect port functionality. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20231121203845.170234-5-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 14 ++++++++++++++ include/linux/usb/tcpm.h | 1 + 2 files changed, 15 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 50cbc52386b3..ff67553b6932 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -251,6 +251,7 @@ enum frs_typec_current { #define TCPM_FRS_EVENT BIT(3) #define TCPM_SOURCING_VBUS BIT(4) #define TCPM_PORT_CLEAN BIT(5) +#define TCPM_PORT_ERROR BIT(6) #define LOG_BUFFER_ENTRIES 1024 #define LOG_BUFFER_ENTRY_SIZE 128 @@ -5487,6 +5488,10 @@ static void tcpm_pd_event_handler(struct kthread_work *work) tcpm_set_state(port, tcpm_default_state(port), 0); } } + if (events & TCPM_PORT_ERROR) { + tcpm_log(port, "port triggering error recovery"); + tcpm_set_state(port, ERROR_RECOVERY, 0); + } spin_lock(&port->pd_event_lock); } @@ -5554,6 +5559,15 @@ bool tcpm_port_is_toggling(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_port_is_toggling); +void tcpm_port_error_recovery(struct tcpm_port *port) +{ + spin_lock(&port->pd_event_lock); + port->pd_events |= TCPM_PORT_ERROR; + spin_unlock(&port->pd_event_lock); + kthread_queue_work(port->wq, &port->event_work); +} +EXPORT_SYMBOL_GPL(tcpm_port_error_recovery); + static void tcpm_enable_frs_work(struct kthread_work *work) { struct tcpm_port *port = container_of(work, struct tcpm_port, enable_frs); diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index ab7ca872950b..65fac5e1f317 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -173,5 +173,6 @@ void tcpm_pd_hard_reset(struct tcpm_port *port); void tcpm_tcpc_reset(struct tcpm_port *port); void tcpm_port_clean(struct tcpm_port *port); bool tcpm_port_is_toggling(struct tcpm_port *port); +void tcpm_port_error_recovery(struct tcpm_port *port); #endif /* __LINUX_USB_TCPM_H */ From 5e4c8814a431d21bfaf20b464134f40f2f81e152 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Tue, 21 Nov 2023 20:38:48 +0000 Subject: [PATCH 059/169] usb: typec: tcpci: add vconn over current fault handling to maxim_core Add TCPC_FAULT_STATUS_VCONN_OC constant and corresponding mask definition. Maxim TCPC is capable of detecting VConn over current faults, so add fault to alert mask. When a Vconn over current fault is triggered, put the port in an error recovery state via tcpm_port_error_recovery. Signed-off-by: RD Babiera Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20231121203845.170234-6-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci_maxim_core.c | 20 +++++++++++++++++++- include/linux/usb/tcpci.h | 5 ++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index 9454b12a073c..7fb966fd639b 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -92,11 +92,16 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip) return; } + /* Vconn Over Current Protection */ + ret = max_tcpci_write8(chip, TCPC_FAULT_STATUS_MASK, TCPC_FAULT_STATUS_MASK_VCONN_OC); + if (ret < 0) + return; + alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_FAILED | TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_RX_STATUS | TCPC_ALERT_CC_STATUS | TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS | /* Enable Extended alert for detecting Fast Role Swap Signal */ - TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS; + TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS | TCPC_ALERT_FAULT; ret = max_tcpci_write16(chip, TCPC_ALERT_MASK, alert_mask); if (ret < 0) { @@ -295,6 +300,19 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status) } } + if (status & TCPC_ALERT_FAULT) { + ret = max_tcpci_read8(chip, TCPC_FAULT_STATUS, ®_status); + if (ret < 0) + return ret; + + ret = max_tcpci_write8(chip, TCPC_FAULT_STATUS, reg_status); + if (ret < 0) + return ret; + + if (reg_status & TCPC_FAULT_STATUS_VCONN_OC) + tcpm_port_error_recovery(chip->port); + } + if (status & TCPC_ALERT_EXTND) { ret = max_tcpci_read8(chip, TCPC_ALERT_EXTENDED, ®_status); if (ret < 0) diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 83376473ac76..467e8045e9f8 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -36,7 +36,9 @@ #define TCPC_ALERT_MASK 0x12 #define TCPC_POWER_STATUS_MASK 0x14 -#define TCPC_FAULT_STATUS_MASK 0x15 + +#define TCPC_FAULT_STATUS_MASK 0x15 +#define TCPC_FAULT_STATUS_MASK_VCONN_OC BIT(1) #define TCPC_EXTENDED_STATUS_MASK 0x16 #define TCPC_EXTENDED_STATUS_MASK_VSAFE0V BIT(0) @@ -104,6 +106,7 @@ #define TCPC_FAULT_STATUS 0x1f #define TCPC_FAULT_STATUS_ALL_REG_RST_TO_DEFAULT BIT(7) +#define TCPC_FAULT_STATUS_VCONN_OC BIT(1) #define TCPC_ALERT_EXTENDED 0x21 From 6666ea93d2c422ebeb8039d11e642552da682070 Mon Sep 17 00:00:00 2001 From: Hardik Gajjar Date: Tue, 5 Dec 2023 19:18:28 +0100 Subject: [PATCH 060/169] usb: hub: Replace hardcoded quirk value with BIT() macro This patch replaces the hardcoded quirk value in the macro with BIT(). Signed-off-by: Hardik Gajjar Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20231205181829.127353-1-hgajjar@de.adit-jv.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5a60e3c7d8ef..7b6bfffbdc55 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -47,8 +47,8 @@ #define USB_VENDOR_TEXAS_INSTRUMENTS 0x0451 #define USB_PRODUCT_TUSB8041_USB3 0x8140 #define USB_PRODUCT_TUSB8041_USB2 0x8142 -#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 -#define HUB_QUIRK_DISABLE_AUTOSUSPEND 0x02 +#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND BIT(0) +#define HUB_QUIRK_DISABLE_AUTOSUSPEND BIT(1) #define USB_TP_TRANSMISSION_DELAY 40 /* ns */ #define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */ From 855d75cf8311fee156fabb5639bb53757ca83dd4 Mon Sep 17 00:00:00 2001 From: Hardik Gajjar Date: Tue, 5 Dec 2023 19:18:29 +0100 Subject: [PATCH 061/169] usb: hub: Add quirk to decrease IN-ep poll interval for Microchip USB491x hub There is a potential delay in notifying Linux USB drivers of downstream USB bus activity when connecting a high-speed or superSpeed device via the Microchip USB491x hub. This delay is due to the fixed bInterval value of 12 in the silicon of the Microchip USB491x hub. Microchip requested to ignore the device descriptor and decrease that value to 9 as it was too late to modify that in silicon. This patch speeds up the USB enummeration process that helps to pass Apple Carplay certifications and improve the User experience when utilizing the USB device via Microchip Multihost USB491x Hub. A new hub quirk HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL speeds up the notification process for Microchip USB491x hub by limiting the maximum bInterval value to 9. Signed-off-by: Hardik Gajjar Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20231205181829.127353-2-hgajjar@de.adit-jv.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7b6bfffbdc55..ffd7c99e24a3 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -47,12 +47,18 @@ #define USB_VENDOR_TEXAS_INSTRUMENTS 0x0451 #define USB_PRODUCT_TUSB8041_USB3 0x8140 #define USB_PRODUCT_TUSB8041_USB2 0x8142 +#define USB_VENDOR_MICROCHIP 0x0424 +#define USB_PRODUCT_USB4913 0x4913 +#define USB_PRODUCT_USB4914 0x4914 +#define USB_PRODUCT_USB4915 0x4915 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND BIT(0) #define HUB_QUIRK_DISABLE_AUTOSUSPEND BIT(1) +#define HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL BIT(2) #define USB_TP_TRANSMISSION_DELAY 40 /* ns */ #define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */ #define USB_PING_RESPONSE_TIME 400 /* ns */ +#define USB_REDUCE_FRAME_INTR_BINTERVAL 9 /* * The SET_ADDRESS request timeout will be 500 ms when @@ -1910,6 +1916,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_autopm_get_interface_no_resume(intf); } + if ((id->driver_info & HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL) && + desc->endpoint[0].desc.bInterval > USB_REDUCE_FRAME_INTR_BINTERVAL) { + desc->endpoint[0].desc.bInterval = + USB_REDUCE_FRAME_INTR_BINTERVAL; + /* Tell the HCD about the interrupt ep's new bInterval */ + usb_set_interface(hdev, 0, 0); + } + if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) { onboard_hub_create_pdevs(hdev, &hub->onboard_hub_devs); @@ -5906,6 +5920,21 @@ static const struct usb_device_id hub_id_table[] = { .idVendor = USB_VENDOR_TEXAS_INSTRUMENTS, .idProduct = USB_PRODUCT_TUSB8041_USB3, .driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND}, + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_PRODUCT, + .idVendor = USB_VENDOR_MICROCHIP, + .idProduct = USB_PRODUCT_USB4913, + .driver_info = HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL}, + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_PRODUCT, + .idVendor = USB_VENDOR_MICROCHIP, + .idProduct = USB_PRODUCT_USB4914, + .driver_info = HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL}, + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_PRODUCT, + .idVendor = USB_VENDOR_MICROCHIP, + .idProduct = USB_PRODUCT_USB4915, + .driver_info = HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL}, { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, .bDeviceClass = USB_CLASS_HUB}, { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, From b3f8e6ae30bd7ade3a96c9beb2a17167d55492a5 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Tue, 5 Dec 2023 21:05:29 +0100 Subject: [PATCH 062/169] dt-bindings: usb: xhci: Add support for BCM2711 The xHCI IP on the BCM2711 SoC is compatible to "brcm,xhci-brcm-v2", but also requires a power domain. So introduce a new compatible and the specific constraints. Since the key allOf can only occur once, merge the reference below. Signed-off-by: Stefan Wahren Reviewed-by: Conor Dooley Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20231205200531.8232-2-wahrenst@gmx.net Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/generic-xhci.yaml | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/generic-xhci.yaml b/Documentation/devicetree/bindings/usb/generic-xhci.yaml index 594ebb3ee432..6ceafa4af292 100644 --- a/Documentation/devicetree/bindings/usb/generic-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-xhci.yaml @@ -9,9 +9,6 @@ title: USB xHCI Controller maintainers: - Mathias Nyman -allOf: - - $ref: usb-xhci.yaml# - properties: compatible: oneOf: @@ -25,6 +22,11 @@ properties: - marvell,armada-380-xhci - marvell,armada-8k-xhci - const: generic-xhci + - description: Broadcom SoCs with power domains + items: + - enum: + - brcm,bcm2711-xhci + - const: brcm,xhci-brcm-v2 - description: Broadcom STB SoCs with xHCI enum: - brcm,xhci-brcm-v2 @@ -49,6 +51,9 @@ properties: - const: core - const: reg + power-domains: + maxItems: 1 + unevaluatedProperties: false required: @@ -56,6 +61,20 @@ required: - reg - interrupts +allOf: + - $ref: usb-xhci.yaml# + - if: + properties: + compatible: + contains: + const: brcm,bcm2711-xhci + then: + required: + - power-domains + else: + properties: + power-domains: false + examples: - | usb@f0931000 { From 95e71986fc1b9a2e1de30bca8e7a5fab12817cdd Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Tue, 5 Dec 2023 21:05:30 +0100 Subject: [PATCH 063/169] usb: xhci: xhci-plat: Add support for BCM2711 With the introduction of a BCM2711 specific compatible, this also needs to be added to the xHCI driver. Signed-off-by: Stefan Wahren Tested-by: Florian Fainelli Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20231205200531.8232-3-wahrenst@gmx.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 732cdeb73920..e46175c38570 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -130,6 +130,9 @@ static const struct of_device_id usb_xhci_of_match[] = { }, { .compatible = "brcm,xhci-brcm-v2", .data = &xhci_plat_brcm, + }, { + .compatible = "brcm,bcm2711-xhci", + .data = &xhci_plat_brcm, }, { .compatible = "brcm,bcm7445-xhci", .data = &xhci_plat_brcm, From 522c35e08b53f157ad3e51848caa861b258001e4 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Tue, 5 Dec 2023 21:05:31 +0100 Subject: [PATCH 064/169] ARM: dts: bcm2711: Add BCM2711 xHCI support The BCM2711 SoC also has a mostly generic xHCI. The USB port is currently only usable on the Compute Module 4 (e.g. via IO board). Because DWC2 and xHCI share the same PHY, we let the bootloader enable it on demand. Signed-off-by: Stefan Wahren Tested-by: Florian Fainelli Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20231205200531.8232-4-wahrenst@gmx.net Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi | 5 +++++ arch/arm/boot/dts/broadcom/bcm2711.dtsi | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi b/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi index 98817a6675b9..d233a191c139 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "bcm2835-rpi.dtsi" +#include #include / { @@ -76,3 +77,7 @@ &vchiq { interrupts = ; }; + +&xhci { + power-domains = <&power RPI_POWER_DOMAIN_USB>; +}; diff --git a/arch/arm/boot/dts/broadcom/bcm2711.dtsi b/arch/arm/boot/dts/broadcom/bcm2711.dtsi index 4a379a14966d..22c7f1561344 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi @@ -604,6 +604,20 @@ }; }; + xhci: usb@7e9c0000 { + compatible = "brcm,bcm2711-xhci", "brcm,xhci-brcm-v2"; + reg = <0x0 0x7e9c0000 0x100000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; + /* DWC2 and this IP block share the same USB PHY, + * enabling both at the same time results in lockups. + * So keep this node disabled and let the bootloader + * decide which interface should be enabled. + */ + status = "disabled"; + }; + v3d: gpu@7ec00000 { compatible = "brcm,2711-v3d"; reg = <0x0 0x7ec00000 0x4000>, From 5cc623a4edaf383eea39546104084b089f3035ca Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 6 Dec 2023 10:13:17 -0800 Subject: [PATCH 065/169] usb: cdns3: starfive: don't misuse /** comment Use a common C comment "/*" instead of "/**" to prevent a warning from scripts/kernel-doc. cdns3-starfive.c:23: warning: expecting prototype for cdns3(). Prototype was for USB_STRAP_HOST() instead Signed-off-by: Randy Dunlap Cc: Minda Chen Cc: Peter Chen Cc: Pawel Laszczak Cc: Roger Quadros Cc: Greg Kroah-Hartman Cc: Acked-by: Peter Chen Link: https://lore.kernel.org/r/20231206181317.27515-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-starfive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c index a7265b86e427..c04d196abd87 100644 --- a/drivers/usb/cdns3/cdns3-starfive.c +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller * * Copyright (C) 2023 StarFive Technology Co., Ltd. From 51920207674e9e3475a91d2091583889792df99a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 6 Dec 2023 10:13:35 -0800 Subject: [PATCH 066/169] usb: fotg210-udc: fix function kernel-doc comments Correct kernel-doc comments to prevent warnings from scripts/kernel-doc. fotg210-udc.c:1103: warning: Function parameter or member 'g' not described in 'fotg210_vbus_session' fotg210-udc.c:1103: warning: Excess function parameter '_gadget' description in 'fotg210_vbus_session' fotg210-udc.c:1103: warning: No description found for return value of 'fotg210_vbus_session' fotg210-udc.c:1129: warning: No description found for return value of 'fotg210_phy_event' Signed-off-by: Randy Dunlap Cc: Linus Walleij Cc: Greg Kroah-Hartman Cc: Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20231206181335.27540-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/fotg210/fotg210-udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/fotg210/fotg210-udc.c b/drivers/usb/fotg210/fotg210-udc.c index f7ea84070554..0bae12e34f9a 100644 --- a/drivers/usb/fotg210/fotg210-udc.c +++ b/drivers/usb/fotg210/fotg210-udc.c @@ -1094,10 +1094,10 @@ static int fotg210_udc_stop(struct usb_gadget *g) /** * fotg210_vbus_session - Called by external transceiver to enable/disable udc - * @_gadget: usb gadget + * @g: usb gadget * @is_active: 0 if should disable UDC VBUS, 1 if should enable * - * Returns 0 + * Returns: %0 */ static int fotg210_vbus_session(struct usb_gadget *g, int is_active) { @@ -1122,7 +1122,7 @@ static const struct usb_gadget_ops fotg210_gadget_ops = { * * Called by the USB Phy when a cable connect or disconnect is sensed. * - * Returns NOTIFY_OK or NOTIFY_DONE + * Returns: NOTIFY_OK or NOTIFY_DONE */ static int fotg210_phy_event(struct notifier_block *nb, unsigned long action, void *data) From 79fff9379e6c928d376e5d8f7f63894ebff67865 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 7 Nov 2023 13:06:25 +0200 Subject: [PATCH 067/169] thunderbolt: Unwind TMU configuration if tb_switch_set_tmu_mode_params() fails Dan reported that the kernel test robot found an issue with the TMU code namely in tb_switch_tmu_change_mode() where we should actually go back to the previous mode in case of failure instead of just returning back the error. Fix this by unwinding the configuration as we do with the other error paths in this function. Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202311030814.AXtCk7PO-lkp@intel.com/ 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 11f2aec2a5d3..9a259c72e5a7 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -894,7 +894,7 @@ static int tb_switch_tmu_change_mode(struct tb_switch *sw) ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.mode_request); if (ret) - return ret; + goto out; /* Program the new mode and the downstream router lane adapter */ switch (sw->tmu.mode_request) { From 36b6ad6ad0350554e611a8cb754ccd40857416a8 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 15 Nov 2023 15:01:36 +0200 Subject: [PATCH 068/169] thunderbolt: Handle lane bonding of Gen 4 XDomain links properly Gen 4 links come up as bonded already so we are not supposed to initiate lane bonding on them. However, we should still update the port structures accordingly. Split these into their own functions to make it easier to follow. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 2 -- drivers/thunderbolt/xdomain.c | 49 ++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 5acdeb766860..6ff3e9624301 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -503,8 +503,6 @@ static void tb_port_unconfigure_xdomain(struct tb_port *port) usb4_port_unconfigure_xdomain(port); else tb_lc_unconfigure_xdomain(port); - - tb_port_enable(port->dual_link_port); } static void tb_scan_xdomain(struct tb_port *port) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 9803f0bbf20d..0a885ee5788d 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1895,6 +1895,50 @@ struct device_type tb_xdomain_type = { }; EXPORT_SYMBOL_GPL(tb_xdomain_type); +static void tb_xdomain_link_init(struct tb_xdomain *xd, struct tb_port *down) +{ + if (!down->dual_link_port) + return; + + /* + * Gen 4 links come up already as bonded so only update the port + * structures here. + */ + if (tb_port_get_link_generation(down) >= 4) { + down->bonded = true; + down->dual_link_port->bonded = true; + } else { + xd->bonding_possible = true; + } +} + +static void tb_xdomain_link_exit(struct tb_xdomain *xd) +{ + struct tb_port *down = tb_xdomain_downstream_port(xd); + + if (!down->dual_link_port) + return; + + if (tb_port_get_link_generation(down) >= 4) { + down->bonded = false; + down->dual_link_port->bonded = false; + } else if (xd->link_width > TB_LINK_WIDTH_SINGLE) { + /* + * Just return port structures back to way they were and + * update credits. No need to update userspace because + * the XDomain is removed soon anyway. + */ + tb_port_lane_bonding_disable(down); + tb_port_update_credits(down); + } else if (down->dual_link_port) { + /* + * Re-enable the lane 1 adapter we disabled at the end + * of tb_xdomain_get_properties(). + */ + tb_port_enable(down->dual_link_port); + } +} + /** * tb_xdomain_alloc() - Allocate new XDomain object * @tb: Domain where the XDomain belongs @@ -1945,7 +1989,8 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, goto err_free_local_uuid; } else { xd->needs_uuid = true; - xd->bonding_possible = !!down->dual_link_port; + + tb_xdomain_link_init(xd, down); } device_initialize(&xd->dev); @@ -2014,6 +2059,8 @@ void tb_xdomain_remove(struct tb_xdomain *xd) device_for_each_child_reverse(&xd->dev, xd, unregister_service); + tb_xdomain_link_exit(xd); + /* * Undo runtime PM here explicitly because it is possible that * the XDomain was never added to the bus and thus device_del() From 30c6759b232bf42297a9ee6c60debf9082005d41 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Sun, 5 Nov 2023 17:52:19 +0200 Subject: [PATCH 069/169] thunderbolt: Move width_name() helper to tb.h We are going to use it in subsequent patches, so make it available outside of switch.c. Also, change the name to tb_width_name() to follow the naming conventions. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 20 ++------------------ drivers/thunderbolt/tb.h | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 1e15ffa79295..7873b173a351 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -941,22 +941,6 @@ int tb_port_get_link_generation(struct tb_port *port) } } -static const char *width_name(enum tb_link_width width) -{ - switch (width) { - case TB_LINK_WIDTH_SINGLE: - return "symmetric, single lane"; - case TB_LINK_WIDTH_DUAL: - return "symmetric, dual lanes"; - case TB_LINK_WIDTH_ASYM_TX: - return "asymmetric, 3 transmitters, 1 receiver"; - case TB_LINK_WIDTH_ASYM_RX: - return "asymmetric, 3 receivers, 1 transmitter"; - default: - return "unknown"; - } -} - /** * tb_port_get_link_width() - Get current link width * @port: Port to check (USB4 or CIO) @@ -2769,7 +2753,7 @@ static void tb_switch_link_init(struct tb_switch *sw) return; tb_sw_dbg(sw, "current link speed %u.0 Gb/s\n", sw->link_speed); - tb_sw_dbg(sw, "current link width %s\n", width_name(sw->link_width)); + tb_sw_dbg(sw, "current link width %s\n", tb_width_name(sw->link_width)); bonded = sw->link_width >= TB_LINK_WIDTH_DUAL; @@ -3029,7 +3013,7 @@ int tb_switch_set_link_width(struct tb_switch *sw, enum tb_link_width width) tb_switch_update_link_attributes(sw); - tb_sw_dbg(sw, "link width set to %s\n", width_name(width)); + tb_sw_dbg(sw, "link width set to %s\n", tb_width_name(width)); return ret; } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index e299e53473ae..1760c21e5b12 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -568,6 +568,22 @@ static inline struct tb_port *tb_port_at(u64 route, struct tb_switch *sw) return &sw->ports[port]; } +static inline const char *tb_width_name(enum tb_link_width width) +{ + switch (width) { + case TB_LINK_WIDTH_SINGLE: + return "symmetric, single lane"; + case TB_LINK_WIDTH_DUAL: + return "symmetric, dual lanes"; + case TB_LINK_WIDTH_ASYM_TX: + return "asymmetric, 3 transmitters, 1 receiver"; + case TB_LINK_WIDTH_ASYM_RX: + return "asymmetric, 3 receivers, 1 transmitter"; + default: + return "unknown"; + } +} + /** * tb_port_has_remote() - Does the port have switch connected downstream * @port: Port to check From ea20adddd6c154e9e107c47fbf1b4697e68180df Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Tue, 7 Nov 2023 13:00:53 +0200 Subject: [PATCH 070/169] thunderbolt: Log XDomain link speed and width In the same way we do for routers. This is useful for debugging purposes. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 0a885ee5788d..9495742913d5 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1462,6 +1462,11 @@ static int tb_xdomain_get_properties(struct tb_xdomain *xd) tb_port_disable(port->dual_link_port); } + dev_dbg(&xd->dev, "current link speed %u.0 Gb/s\n", + xd->link_speed); + dev_dbg(&xd->dev, "current link width %s\n", + tb_width_name(xd->link_width)); + if (device_add(&xd->dev)) { dev_err(&xd->dev, "failed to add XDomain device\n"); return -ENODEV; From 3c052ec6f4f866163573445a02d60b629805a6e7 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 15 Nov 2023 12:09:53 +0200 Subject: [PATCH 071/169] thunderbolt: Transition link to asymmetric only when both sides support it We can transition Gen 4 link to asymmetric only when both sides of the link support it in the required direction. For this reason make sure that the downstream adapter also supports asymmetric link before starting the transition. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 6ff3e9624301..f81e7cf53d69 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1094,8 +1094,9 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, clx = tb_disable_clx(sw); tb_for_each_upstream_port_on_path(src_port, dst_port, up) { + struct tb_port *down = tb_switch_downstream_port(up->sw); + enum tb_link_width width_up, width_down; int consumed_up, consumed_down; - enum tb_link_width width; ret = tb_consumed_dp_bandwidth(tb, src_port, dst_port, up, &consumed_up, &consumed_down); @@ -1116,7 +1117,8 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, if (consumed_down + requested_down < asym_threshold) continue; - width = TB_LINK_WIDTH_ASYM_RX; + width_up = TB_LINK_WIDTH_ASYM_RX; + width_down = TB_LINK_WIDTH_ASYM_TX; } else { /* Upstream, the opposite of above */ if (consumed_down + requested_down >= TB_ASYM_MIN) { @@ -1126,13 +1128,15 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, if (consumed_up + requested_up < asym_threshold) continue; - width = TB_LINK_WIDTH_ASYM_TX; + width_up = TB_LINK_WIDTH_ASYM_TX; + width_down = TB_LINK_WIDTH_ASYM_RX; } - if (up->sw->link_width == width) + if (up->sw->link_width == width_up) continue; - if (!tb_port_width_supported(up, width)) + if (!tb_port_width_supported(up, width_up) || + !tb_port_width_supported(down, width_down)) continue; tb_sw_dbg(up->sw, "configuring asymmetric link\n"); @@ -1141,7 +1145,7 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, * Here requested + consumed > threshold so we need to * transtion the link into asymmetric now. */ - ret = tb_switch_set_link_width(up->sw, width); + ret = tb_switch_set_link_width(up->sw, width_up); if (ret) { tb_sw_warn(up->sw, "failed to set link width\n"); break; From 09dc766bd60b7bc6a2742aaf2410e434f458893c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 17 Nov 2023 12:58:39 +0200 Subject: [PATCH 072/169] thunderbolt: Disable CL states only when actually needed If there is not going to be an actual transition to asymmetric or symmetric, there is no point to disable and re-enable CL states either. So instead disable them only when we know that an actual transition is going to take place. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index f81e7cf53d69..cf1a20a33b9a 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1075,15 +1075,14 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, struct tb_port *dst_port, int requested_up, int requested_down) { + bool clx = false, clx_disabled = false, downstream; struct tb_switch *sw; - bool clx, downstream; struct tb_port *up; int ret = 0; if (!asym_threshold) return 0; - /* Disable CL states before doing any transitions */ downstream = tb_port_path_direction_downstream(src_port, dst_port); /* Pick up router deepest in the hierarchy */ if (downstream) @@ -1091,8 +1090,6 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, else sw = src_port->sw; - clx = tb_disable_clx(sw); - tb_for_each_upstream_port_on_path(src_port, dst_port, up) { struct tb_port *down = tb_switch_downstream_port(up->sw); enum tb_link_width width_up, width_down; @@ -1139,6 +1136,16 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, !tb_port_width_supported(down, width_down)) continue; + /* + * Disable CL states before doing any transitions. We + * delayed it until now that we know there is a real + * transition taking place. + */ + if (!clx_disabled) { + clx = tb_disable_clx(sw); + clx_disabled = true; + } + tb_sw_dbg(up->sw, "configuring asymmetric link\n"); /* @@ -1175,15 +1182,14 @@ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port, struct tb_port *dst_port, int requested_up, int requested_down) { + bool clx = false, clx_disabled = false, downstream; struct tb_switch *sw; - bool clx, downstream; struct tb_port *up; int ret = 0; if (!asym_threshold) return 0; - /* Disable CL states before doing any transitions */ downstream = tb_port_path_direction_downstream(src_port, dst_port); /* Pick up router deepest in the hierarchy */ if (downstream) @@ -1191,8 +1197,6 @@ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port, else sw = src_port->sw; - clx = tb_disable_clx(sw); - tb_for_each_upstream_port_on_path(src_port, dst_port, up) { int consumed_up, consumed_down; @@ -1225,6 +1229,12 @@ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port, if (up->sw->link_width == TB_LINK_WIDTH_DUAL) continue; + /* Disable CL states before doing any transitions */ + if (!clx_disabled) { + clx = tb_disable_clx(sw); + clx_disabled = true; + } + tb_sw_dbg(up->sw, "configuring symmetric link\n"); ret = tb_switch_set_link_width(up->sw, TB_LINK_WIDTH_DUAL); From 97e0a21ce835603b81a5c11de5bf45820194db10 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 23 Nov 2023 13:39:21 +0200 Subject: [PATCH 073/169] thunderbolt: Use tb_dp_read_cap() to read DP_COMMON_CAP as well There is no point doing this separately as the register layout is the same. For this reason rename tb_dp_read_dprx() to tb_dp_wait_dprx() and call tb_dp_read_cap() instead. While there add debug log if the DPRX capability read times out. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 8d45cb4f04ab..6297c85dea06 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -1067,8 +1067,7 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, return 0; } -static int tb_dp_read_dprx(struct tb_tunnel *tunnel, u32 *rate, u32 *lanes, - int timeout_msec) +static int tb_dp_wait_dprx(struct tb_tunnel *tunnel, int timeout_msec) { ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec); struct tb_port *in = tunnel->src_port; @@ -1087,15 +1086,13 @@ static int tb_dp_read_dprx(struct tb_tunnel *tunnel, u32 *rate, u32 *lanes, return ret; if (val & DP_COMMON_CAP_DPRX_DONE) { - *rate = tb_dp_cap_get_rate(val); - *lanes = tb_dp_cap_get_lanes(val); - tb_tunnel_dbg(tunnel, "DPRX read done\n"); return 0; } usleep_range(100, 150); } while (ktime_before(ktime_get(), timeout)); + tb_tunnel_dbg(tunnel, "DPRX read timeout\n"); return -ETIMEDOUT; } @@ -1110,6 +1107,7 @@ static int tb_dp_read_cap(struct tb_tunnel *tunnel, unsigned int cap, u32 *rate, switch (cap) { case DP_LOCAL_CAP: case DP_REMOTE_CAP: + case DP_COMMON_CAP: break; default: @@ -1182,7 +1180,7 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, * reduced one). Otherwise return the remote (possibly * reduced) caps. */ - ret = tb_dp_read_dprx(tunnel, &rate, &lanes, 150); + ret = tb_dp_wait_dprx(tunnel, 150); if (ret) { if (ret == -ETIMEDOUT) ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP, @@ -1190,6 +1188,9 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, if (ret) return ret; } + ret = tb_dp_read_cap(tunnel, DP_COMMON_CAP, &rate, &lanes); + if (ret) + return ret; } else if (sw->generation >= 2) { ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP, &rate, &lanes); if (ret) From 0b663ec9fe07e5c46ede45da75951eb11fc2f215 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 15 Nov 2023 12:09:58 +0200 Subject: [PATCH 074/169] thunderbolt: Improve logging when DisplayPort resource is added due to hotplug To allow us differentiate how DisplayPort resource is added to the DisplayPort resources list make the debug log to append "hotplug" when this was due to an actual hotplug. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index cf1a20a33b9a..bd211e97ee4b 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1903,7 +1903,7 @@ static void tb_dp_resource_available(struct tb *tb, struct tb_port *port) return; } - tb_port_dbg(port, "DP %s resource available\n", + tb_port_dbg(port, "DP %s resource available after hotplug\n", tb_port_is_dpin(port) ? "IN" : "OUT"); list_add_tail(&port->list, &tcm->dp_resources); From 54967f4177d3d8d6231eeedee669c2512bcbf172 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 15 Nov 2023 12:09:56 +0200 Subject: [PATCH 075/169] thunderbolt: Make PCIe tunnel setup and teardown follow CM guide The USB4 Connection Manager guide suggests that the PCIe paths are enabled from the upstream adapter to the downstream adapter and vice versa on disable so make the driver follows this sequence. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 6297c85dea06..b0c3c8373775 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -199,14 +199,21 @@ static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate) return res; } - res = tb_pci_port_enable(tunnel->src_port, activate); + if (activate) + res = tb_pci_port_enable(tunnel->dst_port, activate); + else + res = tb_pci_port_enable(tunnel->src_port, activate); if (res) return res; - if (tb_port_is_pcie_up(tunnel->dst_port)) { - res = tb_pci_port_enable(tunnel->dst_port, activate); + + if (activate) { + res = tb_pci_port_enable(tunnel->src_port, activate); if (res) return res; + } else { + /* Downstream router could be unplugged */ + tb_pci_port_enable(tunnel->dst_port, activate); } return activate ? 0 : tb_pci_set_ext_encapsulation(tunnel, activate); From 2b3a6239d286d6b74295ccf21905ba6533bcf3a9 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 15 Nov 2023 12:09:57 +0200 Subject: [PATCH 076/169] thunderbolt: Disable PCIe extended encapsulation upon teardown properly In case of PCIe tunnel teardown (including if caused by router unplug), PCIe extended encapsulation bit should be cleared in downstream and upstream routers accordingly. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index b0c3c8373775..6fffb2c82d3d 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -173,16 +173,28 @@ static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable) int ret; /* Only supported of both routers are at least USB4 v2 */ - if (tb_port_get_link_generation(port) < 4) + if ((usb4_switch_version(tunnel->src_port->sw) < 2) || + (usb4_switch_version(tunnel->dst_port->sw) < 2)) + return 0; + + if (enable && tb_port_get_link_generation(port) < 4) return 0; ret = usb4_pci_port_set_ext_encapsulation(tunnel->src_port, enable); if (ret) return ret; + /* + * Downstream router could be unplugged so disable of encapsulation + * in upstream router is still possible. + */ ret = usb4_pci_port_set_ext_encapsulation(tunnel->dst_port, enable); - if (ret) - return ret; + if (ret) { + if (enable) + return ret; + if (ret != -ENODEV) + return ret; + } tb_tunnel_dbg(tunnel, "extended encapsulation %s\n", str_enabled_disabled(enable)); From 2cd3da4e37453019e21a486d9de3144f46b4fdf7 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 20 May 2022 13:47:11 +0300 Subject: [PATCH 077/169] thunderbolt: Add support for Intel Lunar Lake Intel Lunar Lake has similar integrated Thunderbolt/USB4 controller as Intel Meteor Lake with some small differences in the host router (it has 3 DP IN adapters for instance). Add the Intel Lunar Lake PCI IDs to the driver list of supported devices. Tested-by: Pengfei Xu Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 4 ++++ drivers/thunderbolt/nhi.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 4b7bec74e89f..fb4f46e51753 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1517,6 +1517,10 @@ static struct pci_device_id nhi_ids[] = { .driver_data = (kernel_ulong_t)&icl_nhi_ops }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI1), .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI) }, diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 0f029ce75882..7a07c7c1a9c2 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -90,6 +90,8 @@ extern const struct tb_nhi_ops icl_nhi_ops; #define PCI_DEVICE_ID_INTEL_TGL_H_NHI1 0x9a21 #define PCI_DEVICE_ID_INTEL_RPL_NHI0 0xa73e #define PCI_DEVICE_ID_INTEL_RPL_NHI1 0xa76d +#define PCI_DEVICE_ID_INTEL_LNL_NHI0 0xa833 +#define PCI_DEVICE_ID_INTEL_LNL_NHI1 0xa834 #define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 From 53b5ff83d89349778bc61e67237f72c5dc6536c2 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Tue, 21 Nov 2023 23:51:18 +0530 Subject: [PATCH 078/169] usb: dwc3: xilinx: improve error handling for PM APIs Improve error handling for PM APIs in the dwc3_xlnx_probe function by introducing devm_pm_runtime_enable and error label. Removed unnecessary API pm_runtime_disable call in dwc3_xlnx_remove. Signed-off-by: Piyush Mehta Signed-off-by: Radhey Shyam Pandey Link: https://lore.kernel.org/r/1700590878-124335-1-git-send-email-radhey.shyam.pandey@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-xilinx.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index 5b7e92f476de..6095f4dee6ce 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -293,11 +293,15 @@ static int dwc3_xlnx_probe(struct platform_device *pdev) goto err_clk_put; pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_suspend_ignore_children(dev, false); - pm_runtime_get_sync(dev); + ret = devm_pm_runtime_enable(dev); + if (ret < 0) + goto err_pm_set_suspended; - return 0; + pm_suspend_ignore_children(dev, false); + return pm_runtime_resume_and_get(dev); + +err_pm_set_suspended: + pm_runtime_set_suspended(dev); err_clk_put: clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); @@ -315,7 +319,6 @@ static void dwc3_xlnx_remove(struct platform_device *pdev) clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); priv_data->num_clocks = 0; - pm_runtime_disable(dev); pm_runtime_put_noidle(dev); pm_runtime_set_suspended(dev); } From 66aad7d8d3ec5a3a8ec2023841bcec2ded5f65c9 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 7 Dec 2023 14:26:30 +0100 Subject: [PATCH 079/169] usb: cdc-acm: return correct error code on unsupported break In ACM support for sending breaks to devices is optional. If a device says that it doenot support sending breaks, the host must respect that. Given the number of optional features providing tty operations for each combination is not practical and errors need to be returned dynamically if unsupported features are requested. In case a device does not support break, we want the tty layer to treat that like it treats drivers that statically cannot support sending a break. It ignores the inability and does nothing. This patch uses EOPNOTSUPP to indicate that. Signed-off-by: Oliver Neukum Fixes: 9e98966c7bb94 ("tty: rework break handling") Link: https://lore.kernel.org/r/20231207132639.18250-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 3 +++ drivers/usb/class/cdc-acm.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 06414e43e0b5..96617f9af819 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2489,6 +2489,9 @@ static int send_break(struct tty_struct *tty, unsigned int duration) if (!retval) { msleep_interruptible(duration); retval = tty->ops->break_ctl(tty, 0); + } else if (retval == -EOPNOTSUPP) { + /* some drivers can tell only dynamically */ + retval = 0; } tty_write_unlock(tty); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index a1f4e1ead97f..0e7439dba8fe 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -916,6 +916,9 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state) struct acm *acm = tty->driver_data; int retval; + if (!(acm->ctrl_caps & USB_CDC_CAP_BRK)) + return -EOPNOTSUPP; + retval = acm_send_break(acm, state ? 0xffff : 0); if (retval < 0) dev_dbg(&acm->control->dev, From ecb43ef59498628a9df79afb47e67dadf777e3cd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 7 Dec 2023 09:42:17 -0300 Subject: [PATCH 080/169] dt-bindings: usb: genesys,gl850g: Document 'peer-hub' The 'peer-hub' is a valid property for the hub. Document it to fix the following dt-schema warning: imx8mp-debix-som-a-bmb-08.dtb: hub@1: 'peer-hub' does not match any of the regexes: 'pinctrl-[0-9]+' from schema $id: http://devicetree.org/schemas/usb/genesys,gl850g.yaml# Signed-off-by: Fabio Estevam Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20231207124217.2530457-1-festevam@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/genesys,gl850g.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml index ee08b9c3721f..37cf5249e526 100644 --- a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml +++ b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml @@ -29,6 +29,11 @@ properties: description: the regulator that provides 3.3V core power to the hub. + peer-hub: + $ref: /schemas/types.yaml#/definitions/phandle + description: + phandle to the peer hub on the controller. + required: - compatible - reg From 2ddc97a71a27b6f940f1ed0f1e70a4e8e62b77e5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 7 Dec 2023 14:29:12 -0300 Subject: [PATCH 081/169] dt-bindings: usb: nxp,ptn5110: Fix typos in the title Fix the misspelled "Controller" and "Type-C" words. Signed-off-by: Fabio Estevam Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20231207172912.2658226-1-festevam@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/nxp,ptn5110.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/nxp,ptn5110.yaml b/Documentation/devicetree/bindings/usb/nxp,ptn5110.yaml index 28eb25ecba74..eaedb4cc6b6c 100644 --- a/Documentation/devicetree/bindings/usb/nxp,ptn5110.yaml +++ b/Documentation/devicetree/bindings/usb/nxp,ptn5110.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/usb/nxp,ptn5110.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: NXP PTN5110 Typec Port Cotroller +title: NXP PTN5110 Type-C Port Controller maintainers: - Li Jun From e4e5f9e3bff79951cef7fd6912049b1cecf8a72d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 10 Dec 2023 18:43:56 +0100 Subject: [PATCH 082/169] usb: chipidea: Remove usage of the deprecated ida_simple_xx() API ida_alloc() and ida_free() should be preferred to the deprecated ida_simple_get() and ida_simple_remove(). This is less verbose. Signed-off-by: Christophe JAILLET Acked-by: Peter Chen Link: https://lore.kernel.org/r/8bf382976c0ba0986c0dbe93427266273f0776ef.1702230217.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 7ac39a281b8c..0af9e68035fb 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -862,7 +862,7 @@ struct platform_device *ci_hdrc_add_device(struct device *dev, if (ret) return ERR_PTR(ret); - id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&ci_ida, GFP_KERNEL); if (id < 0) return ERR_PTR(id); @@ -892,7 +892,7 @@ struct platform_device *ci_hdrc_add_device(struct device *dev, err: platform_device_put(pdev); put_id: - ida_simple_remove(&ci_ida, id); + ida_free(&ci_ida, id); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(ci_hdrc_add_device); @@ -901,7 +901,7 @@ void ci_hdrc_remove_device(struct platform_device *pdev) { int id = pdev->id; platform_device_unregister(pdev); - ida_simple_remove(&ci_ida, id); + ida_free(&ci_ida, id); } EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); From 7516f86aa5ec9f3f2ae1c5c12b56eef76c7b1a15 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 10 Dec 2023 18:36:15 +0100 Subject: [PATCH 083/169] usb: typec: Remove usage of the deprecated ida_simple_xx() API ida_alloc() and ida_free() should be preferred to the deprecated ida_simple_get() and ida_simple_remove(). This is less verbose. Signed-off-by: Christophe JAILLET Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/c7b99c4f52649ce6405779fbf9170edc5633fdbb.1702229697.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 8 ++++---- drivers/usb/typec/pd.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 16a670828dde..5fe01bf795b9 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -476,7 +476,7 @@ static int altmode_id_get(struct device *dev) else ids = &to_typec_port(dev)->mode_ids; - return ida_simple_get(ids, 0, 0, GFP_KERNEL); + return ida_alloc(ids, GFP_KERNEL); } static void altmode_id_remove(struct device *dev, int id) @@ -490,7 +490,7 @@ static void altmode_id_remove(struct device *dev, int id) else ids = &to_typec_port(dev)->mode_ids; - ida_simple_remove(ids, id); + ida_free(ids, id); } static void typec_altmode_release(struct device *dev) @@ -1798,7 +1798,7 @@ static void typec_release(struct device *dev) { struct typec_port *port = to_typec_port(dev); - ida_simple_remove(&typec_index_ida, port->id); + ida_free(&typec_index_ida, port->id); ida_destroy(&port->mode_ids); typec_switch_put(port->sw); typec_mux_put(port->mux); @@ -2297,7 +2297,7 @@ struct typec_port *typec_register_port(struct device *parent, if (!port) return ERR_PTR(-ENOMEM); - id = ida_simple_get(&typec_index_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&typec_index_ida, GFP_KERNEL); if (id < 0) { kfree(port); return ERR_PTR(id); diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c index 85d015cdbe1f..7f3d61f220f2 100644 --- a/drivers/usb/typec/pd.c +++ b/drivers/usb/typec/pd.c @@ -571,7 +571,7 @@ static void pd_release(struct device *dev) { struct usb_power_delivery *pd = to_usb_power_delivery(dev); - ida_simple_remove(&pd_ida, pd->id); + ida_free(&pd_ida, pd->id); kfree(pd); } @@ -616,7 +616,7 @@ usb_power_delivery_register(struct device *parent, struct usb_power_delivery_des if (!pd) return ERR_PTR(-ENOMEM); - ret = ida_simple_get(&pd_ida, 0, 0, GFP_KERNEL); + ret = ida_alloc(&pd_ida, GFP_KERNEL); if (ret < 0) { kfree(pd); return ERR_PTR(ret); From d1756ac67e7f8d4c15ba4dd0c132e593b0a56de6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 4 Dec 2023 04:03:00 +0200 Subject: [PATCH 084/169] dt-bindings: connector: usb: add altmodes description Add description of the USB-C AltModes supported on the particular USB-C connector. This is required for devices like Qualcomm Robotics RB5, which have no other way to express alternative modes supported by the hardware platform. Reviewed-by: Rob Herring Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20231204020303.2287338-2-dmitry.baryshkov@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/connector/usb-connector.yaml | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index 7c8a3e8430d3..28660b931d0c 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -171,6 +171,32 @@ properties: offer the power, Capability Mismatch is set. Required for power sink and power dual role. + altmodes: + type: object + description: List of Alternative Modes supported by the schematics on the + particular device. This is only necessary if there are no other means to + discover supported alternative modes (e.g. through the UCSI firmware + interface). + + additionalProperties: false + + patternProperties: + "^(displayport)$": + type: object + description: + A single USB-C Alternative Mode as supported by the USB-C connector logic. + + additionalProperties: false + + properties: + svid: + $ref: /schemas/types.yaml#/definitions/uint16 + description: Unique value assigned by USB-IF to the Vendor / AltMode. + enum: [ 0xff01 ] + vdo: + $ref: /schemas/types.yaml#/definitions/uint32 + description: VDO returned by Discover Modes USB PD command. + port: $ref: /schemas/graph.yaml#/properties/port description: OF graph bindings modeling a data bus to the connector, e.g. @@ -289,6 +315,13 @@ examples: compatible = "usb-c-connector"; label = "USB-C"; + altmodes { + displayport { + svid = /bits/ 16 <0xff01>; + vdo = <0x00001c46>; + }; + }; + ports { #address-cells = <1>; #size-cells = <0>; From 0dbda971860c702c33df173b627f9d74b3152b75 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 4 Dec 2023 04:03:01 +0200 Subject: [PATCH 085/169] usb: typec: change altmode SVID to u16 entry As stated in the changelog for the commit 7b458a4c5d73 ("usb: typec: Add typec_port_register_altmodes()"), the code should be adjusted according to the AltMode bindings. As the SVID is 16 bits wide (according to the USB PD Spec), use fwnode_property_read_u16() to read it. Acked-by: Hans de Goede Reviewed-by: Heikki Krogerus Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20231204020303.2287338-3-dmitry.baryshkov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/chtwc_int33fe.c | 2 +- drivers/usb/typec/class.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/intel/chtwc_int33fe.c b/drivers/platform/x86/intel/chtwc_int33fe.c index 848baecc1bb0..93f75ba1dafd 100644 --- a/drivers/platform/x86/intel/chtwc_int33fe.c +++ b/drivers/platform/x86/intel/chtwc_int33fe.c @@ -136,7 +136,7 @@ static const struct software_node altmodes_node = { }; static const struct property_entry dp_altmode_properties[] = { - PROPERTY_ENTRY_U32("svid", 0xff01), + PROPERTY_ENTRY_U16("svid", 0xff01), PROPERTY_ENTRY_U32("vdo", 0x0c0086), { } }; diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 5fe01bf795b9..aeae8009b9e3 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -2231,7 +2231,8 @@ void typec_port_register_altmodes(struct typec_port *port, struct typec_altmode_desc desc; struct typec_altmode *alt; size_t index = 0; - u32 svid, vdo; + u16 svid; + u32 vdo; int ret; altmodes_node = device_get_named_child_node(&port->dev, "altmodes"); @@ -2239,7 +2240,7 @@ void typec_port_register_altmodes(struct typec_port *port, return; /* No altmodes specified */ fwnode_for_each_child_node(altmodes_node, child) { - ret = fwnode_property_read_u32(child, "svid", &svid); + ret = fwnode_property_read_u16(child, "svid", &svid); if (ret) { dev_err(&port->dev, "Error reading svid for altmode %s\n", fwnode_get_name(child)); From 70e6163d17dd501ef27680eeb80d78b2cf823c5e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 4 Dec 2023 04:03:02 +0200 Subject: [PATCH 086/169] arm64: dts: qcom: qrb5165-rb5: use u16 for DP altmode svid Follow the bindings and use 16-bit value for AltMode SVID instead of using the full u32. Fixes: b3dea914127e ("arm64: dts: qcom: qrb5165-rb5: enable DP altmode") Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20231204020303.2287338-4-dmitry.baryshkov@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts index c8cd40a462a3..88b37ceb13ed 100644 --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts @@ -1425,7 +1425,7 @@ altmodes { displayport { - svid = <0xff01>; + svid = /bits/ 16 <0xff01>; vdo = <0x00001c46>; }; }; From 7d530f4cc0632056d9f8f207245aa3d91a25d168 Mon Sep 17 00:00:00 2001 From: Kyle Tso Date: Tue, 5 Dec 2023 15:47:46 +0800 Subject: [PATCH 087/169] usb: typec: tcpm: Query Source partner for FRS capability only if it is DRP Source-only port partner will always respond NOT_SUPPORTED to GET_SINK_CAP. Avoid this redundant AMS by bailing out querying the FRS capability if the Source port partner is not DRP. Signed-off-by: Kyle Tso Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20231205074747.1821297-1-kyletso@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index ff67553b6932..8372f98de757 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4402,7 +4402,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_current_limit(port, tcpm_get_current_limit(port), 5000); tcpm_swap_complete(port, 0); tcpm_typec_connect(port); - mod_enable_frs_delayed_work(port, 0); + if (port->pd_capable && port->source_caps[0] & PDO_FIXED_DUAL_ROLE) + mod_enable_frs_delayed_work(port, 0); tcpm_pps_complete(port, port->pps_status); if (port->ams != NONE_AMS) From 44995e6f07028f798efd0c3c11a1efc78330f600 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 11 Dec 2023 07:32:41 -0800 Subject: [PATCH 088/169] usb: core: Fix crash w/ usb_choose_configuration() if no driver It's possible that usb_choose_configuration() can get called when a USB device has no driver. In this case the recent commit a87b8e3be926 ("usb: core: Allow subclassed USB drivers to override usb_choose_configuration()") can cause a crash since it dereferenced the driver structure without checking for NULL. Let's add a check. A USB device with no driver is an anomaly, so make usb_choose_configuration() return immediately if there is no driver. This was seen in the real world when usbguard got ahold of a r8152 device at the wrong time. It can also be simulated via this on a computer with one r8152-based USB Ethernet adapter: cd /sys/bus/usb/drivers/r8152-cfgselector to_unbind="$(ls -d *-*)" real_dir="$(readlink -f "${to_unbind}")" echo "${to_unbind}" > unbind cd "${real_dir}" echo 0 > authorized echo 1 > authorized Fixes: a87b8e3be926 ("usb: core: Allow subclassed USB drivers to override usb_choose_configuration()") Reviewed-by: Alan Stern Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20231211073237.v3.1.If27eb3bf7812f91ab83810f232292f032f4203e0@changeid Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index dcb897158228..b134bff5c3fe 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -59,7 +59,16 @@ int usb_choose_configuration(struct usb_device *udev) int num_configs; int insufficient_power = 0; struct usb_host_config *c, *best; - struct usb_device_driver *udriver = to_usb_device_driver(udev->dev.driver); + struct usb_device_driver *udriver; + + /* + * If a USB device (not an interface) doesn't have a driver then the + * kernel has no business trying to select or install a configuration + * for it. + */ + if (!udev->dev.driver) + return -1; + udriver = to_usb_device_driver(udev->dev.driver); if (usb_device_is_owned(udev)) return 0; From de12c5384307d2fa2a735b7d21cf7603e1bcb5c9 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 12 Dec 2023 09:54:28 +0100 Subject: [PATCH 089/169] dt-bindings: usb: Document WCD939x USB SubSystem Altmode/Analog Audio Switch Document the Qualcomm WCD9390/WCD9395 USB SubSystem Altmode/Analog Audio Switch which is a separate USB SubSystem for Altmode/Analog Audio Switch accessible over an I2C interface. Since Audio Headphone and Microphone data path between the Codec and the USB-C Mux subsystems are external to the IC, it requires a second port to handle USB-C altmode & orientation switching for Audio Accessory Mode to the Codec SubSystem. Signed-off-by: Neil Armstrong Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20231212-topic-sm8650-upstream-wcd939x-usbss-v2-1-38961fea5867@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/usb/qcom,wcd939x-usbss.yaml | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml diff --git a/Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml b/Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml new file mode 100644 index 000000000000..7ddfd3313a18 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/qcom,wcd939x-usbss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCD9380/WCD9385 USB SubSystem Altmode/Analog Audio Switch + +maintainers: + - Neil Armstrong + +description: + Qualcomm WCD9390/WCD9395 is a standalone Hi-Fi audio codec IC with a + functionally separate USB SubSystem for Altmode/Analog Audio Switch + accessible over an I2C interface. + The Audio Headphone and Microphone data path between the Codec and the + USB-C Mux subsystems are external to the IC, thus requiring DT port-endpoint + graph description to handle USB-C altmode & orientation switching for Audio + Accessory Mode. + +properties: + compatible: + oneOf: + - const: qcom,wcd9390-usbss + - items: + - const: qcom,wcd9395-usbss + - const: qcom,wcd9390-usbss + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + vdd-supply: + description: USBSS VDD power supply + + mode-switch: + description: Flag the port as possible handle of altmode switching + type: boolean + + orientation-switch: + description: Flag the port as possible handler of orientation switching + type: boolean + + ports: + $ref: /schemas/graph.yaml#/properties/ports + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: + A port node to link the WCD939x USB SubSystem to a TypeC controller for the + purpose of handling altmode muxing and orientation switching. + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + A port node to link the WCD939x USB SubSystem to the Codec SubSystem for the + purpose of handling USB-C Audio Accessory Mode muxing and orientation switching. + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + typec-mux@42 { + compatible = "qcom,wcd9390-usbss"; + reg = <0x42>; + + vdd-supply = <&vreg_bob>; + + mode-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + wcd9390_usbss_sbu: endpoint { + remote-endpoint = <&typec_sbu>; + }; + }; + port@1 { + reg = <1>; + wcd9390_usbss_codec: endpoint { + remote-endpoint = <&wcd9390_codec_usbss>; + }; + }; + }; + }; + }; +... From 36d586c0570e075c18bcc3b4ec4de11fcdbf5dd1 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 12 Dec 2023 09:54:29 +0100 Subject: [PATCH 090/169] usb: typec: mux: add Qualcomm WCD939X USB SubSystem Altmode Mux driver Qualcomm WCD9390/WCD9395 is a standalone Hi-Fi audio codec IC with a functionally separate USB SubSystem for Altmode/Analog Audio Switch accessible over an I2C interface. It provides switching USB-C USB2.0 lines between USB and Audio Headphones speaker lines, and the USB-C SBU lines between DisplayPort AUX and Audio Headphones Microphone/Ground. The Audio Headphone and Microphone data path between the Codec and the USB-C Mux subsystems are external to the IC, thus requiring DT port-endpoint graph description to handle USB-C altmode & orientation switching for Audio Accessory Mode. Signed-off-by: Neil Armstrong Link: https://lore.kernel.org/r/20231212-topic-sm8650-upstream-wcd939x-usbss-v2-2-38961fea5867@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/Kconfig | 10 + drivers/usb/typec/mux/Makefile | 1 + drivers/usb/typec/mux/wcd939x-usbss.c | 779 ++++++++++++++++++++++++++ 3 files changed, 790 insertions(+) create mode 100644 drivers/usb/typec/mux/wcd939x-usbss.c diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index 816b9bd08355..71968179c1cb 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -56,4 +56,14 @@ config TYPEC_MUX_PTN36502 Say Y or M if your system has a NXP PTN36502 Type-C redriver chip found on some devices with a Type-C port. +config TYPEC_MUX_WCD939X_USBSS + tristate "Qualcomm WCD939x USBSS Analog Audio Switch driver" + depends on I2C + select REGMAP_I2C + help + Driver for the Qualcomm WCD939x Audio Codec USBSS domain which + provides support for muxing analog audio and sideband signals on a + common USB Type-C connector. + If compiled as a module, the module will be named wcd939x-usbss. + endmenu diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index 9d6a5557b0bd..57dc9ac6f8dc 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o obj-$(CONFIG_TYPEC_MUX_PTN36502) += ptn36502.o +obj-$(CONFIG_TYPEC_MUX_WCD939X_USBSS) += wcd939x-usbss.o diff --git a/drivers/usb/typec/mux/wcd939x-usbss.c b/drivers/usb/typec/mux/wcd939x-usbss.c new file mode 100644 index 000000000000..d46c353dfaf2 --- /dev/null +++ b/drivers/usb/typec/mux/wcd939x-usbss.c @@ -0,0 +1,779 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (C) 2023 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WCD_USBSS_PMP_OUT1 0x2 + +#define WCD_USBSS_DP_DN_MISC1 0x20 + +#define WCD_USBSS_DP_DN_MISC1_DP_PCOMP_2X_DYN_BST_ON_EN BIT(3) +#define WCD_USBSS_DP_DN_MISC1_DN_PCOMP_2X_DYN_BST_ON_EN BIT(0) + +#define WCD_USBSS_MG1_EN 0x24 + +#define WCD_USBSS_MG1_EN_CT_SNS_EN BIT(1) + +#define WCD_USBSS_MG1_BIAS 0x25 + +#define WCD_USBSS_MG1_BIAS_PCOMP_DYN_BST_EN BIT(3) + +#define WCD_USBSS_MG1_MISC 0x27 + +#define WCD_USBSS_MG1_MISC_PCOMP_2X_DYN_BST_ON_EN BIT(5) + +#define WCD_USBSS_MG2_EN 0x28 + +#define WCD_USBSS_MG2_EN_CT_SNS_EN BIT(1) + +#define WCD_USBSS_MG2_BIAS 0x29 + +#define WCD_USBSS_MG2_BIAS_PCOMP_DYN_BST_EN BIT(3) + +#define WCD_USBSS_MG2_MISC 0x30 + +#define WCD_USBSS_MG2_MISC_PCOMP_2X_DYN_BST_ON_EN BIT(5) + +#define WCD_USBSS_DISP_AUXP_THRESH 0x80 + +#define WCD_USBSS_DISP_AUXP_THRESH_DISP_AUXP_OVPON_CM GENMASK(7, 5) + +#define WCD_USBSS_DISP_AUXP_CTL 0x81 + +#define WCD_USBSS_DISP_AUXP_CTL_LK_CANCEL_TRK_COEFF GENMASK(2, 0) + +#define WCD_USBSS_CPLDO_CTL2 0xa1 + +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE 0x403 + +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DEVICE_ENABLE BIT(7) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXP_TO_MGX_SWITCHES BIT(6) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXM_TO_MGX_SWITCHES BIT(5) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DNL_SWITCHES BIT(4) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DPR_SWITCHES BIT(3) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_SENSE_SWITCHES BIT(2) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_MIC_SWITCHES BIT(1) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_AGND_SWITCHES BIT(0) + +#define WCD_USBSS_SWITCH_SELECT0 0x404 + +#define WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES BIT(7) /* 1-> MG2 */ +#define WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES BIT(6) /* 1-> MG2 */ +#define WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES GENMASK(5, 4) +#define WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES GENMASK(3, 2) +#define WCD_USBSS_SWITCH_SELECT0_SENSE_SWITCHES BIT(1) /* 1-> SBU2 */ +#define WCD_USBSS_SWITCH_SELECT0_MIC_SWITCHES BIT(0) /* 1-> MG2 */ + +#define WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_L 0 +#define WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_DN 1 +#define WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_DN2 2 + +#define WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_R 0 +#define WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_DP 1 +#define WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_DR2 2 + +#define WCD_USBSS_SWITCH_SELECT1 0x405 + +#define WCD_USBSS_SWITCH_SELECT1_AGND_SWITCHES BIT(0) /* 1-> MG2 */ + +#define WCD_USBSS_DELAY_R_SW 0x40d +#define WCD_USBSS_DELAY_MIC_SW 0x40e +#define WCD_USBSS_DELAY_SENSE_SW 0x40f +#define WCD_USBSS_DELAY_GND_SW 0x410 +#define WCD_USBSS_DELAY_L_SW 0x411 + +#define WCD_USBSS_FUNCTION_ENABLE 0x413 + +#define WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT GENMASK(1, 0) + +#define WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT_MANUAL 1 +#define WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT_AUDIO_FSM 2 + +#define WCD_USBSS_EQUALIZER1 0x415 + +#define WCD_USBSS_EQUALIZER1_EQ_EN BIT(7) +#define WCD_USBSS_EQUALIZER1_BW_SETTINGS GENMASK(6, 3) + +#define WCD_USBSS_USB_SS_CNTL 0x419 + +#define WCD_USBSS_USB_SS_CNTL_STANDBY_STATE BIT(4) +#define WCD_USBSS_USB_SS_CNTL_RCO_EN BIT(3) +#define WCD_USBSS_USB_SS_CNTL_USB_SS_MODE GENMASK(2, 0) + +#define WCD_USBSS_USB_SS_CNTL_USB_SS_MODE_AATC 2 +#define WCD_USBSS_USB_SS_CNTL_USB_SS_MODE_USB 5 + +#define WCD_USBSS_AUDIO_FSM_START 0x433 + +#define WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG BIT(0) + +#define WCD_USBSS_RATIO_SPKR_REXT_L_LSB 0x461 +#define WCD_USBSS_RATIO_SPKR_REXT_L_MSB 0x462 +#define WCD_USBSS_RATIO_SPKR_REXT_R_LSB 0x463 +#define WCD_USBSS_RATIO_SPKR_REXT_R_MSB 0x464 +#define WCD_USBSS_AUD_COEF_L_K0_0 0x475 +#define WCD_USBSS_AUD_COEF_L_K0_1 0x476 +#define WCD_USBSS_AUD_COEF_L_K0_2 0x477 +#define WCD_USBSS_AUD_COEF_L_K1_0 0x478 +#define WCD_USBSS_AUD_COEF_L_K1_1 0x479 +#define WCD_USBSS_AUD_COEF_L_K2_0 0x47a +#define WCD_USBSS_AUD_COEF_L_K2_1 0x47b +#define WCD_USBSS_AUD_COEF_L_K3_0 0x47c +#define WCD_USBSS_AUD_COEF_L_K3_1 0x47d +#define WCD_USBSS_AUD_COEF_L_K4_0 0x47e +#define WCD_USBSS_AUD_COEF_L_K4_1 0x47f +#define WCD_USBSS_AUD_COEF_L_K5_0 0x480 +#define WCD_USBSS_AUD_COEF_L_K5_1 0x481 +#define WCD_USBSS_AUD_COEF_R_K0_0 0x482 +#define WCD_USBSS_AUD_COEF_R_K0_1 0x483 +#define WCD_USBSS_AUD_COEF_R_K0_2 0x484 +#define WCD_USBSS_AUD_COEF_R_K1_0 0x485 +#define WCD_USBSS_AUD_COEF_R_K1_1 0x486 +#define WCD_USBSS_AUD_COEF_R_K2_0 0x487 +#define WCD_USBSS_AUD_COEF_R_K2_1 0x488 +#define WCD_USBSS_AUD_COEF_R_K3_0 0x489 +#define WCD_USBSS_AUD_COEF_R_K3_1 0x48a +#define WCD_USBSS_AUD_COEF_R_K4_0 0x48b +#define WCD_USBSS_AUD_COEF_R_K4_1 0x48c +#define WCD_USBSS_AUD_COEF_R_K5_0 0x48d +#define WCD_USBSS_AUD_COEF_R_K5_1 0x48e +#define WCD_USBSS_GND_COEF_L_K0_0 0x48f +#define WCD_USBSS_GND_COEF_L_K0_1 0x490 +#define WCD_USBSS_GND_COEF_L_K0_2 0x491 +#define WCD_USBSS_GND_COEF_L_K1_0 0x492 +#define WCD_USBSS_GND_COEF_L_K1_1 0x493 +#define WCD_USBSS_GND_COEF_L_K2_0 0x494 +#define WCD_USBSS_GND_COEF_L_K2_1 0x495 +#define WCD_USBSS_GND_COEF_L_K3_0 0x496 +#define WCD_USBSS_GND_COEF_L_K3_1 0x497 +#define WCD_USBSS_GND_COEF_L_K4_0 0x498 +#define WCD_USBSS_GND_COEF_L_K4_1 0x499 +#define WCD_USBSS_GND_COEF_L_K5_0 0x49a +#define WCD_USBSS_GND_COEF_L_K5_1 0x49b +#define WCD_USBSS_GND_COEF_R_K0_0 0x49c +#define WCD_USBSS_GND_COEF_R_K0_1 0x49d +#define WCD_USBSS_GND_COEF_R_K0_2 0x49e +#define WCD_USBSS_GND_COEF_R_K1_0 0x49f +#define WCD_USBSS_GND_COEF_R_K1_1 0x4a0 +#define WCD_USBSS_GND_COEF_R_K2_0 0x4a1 +#define WCD_USBSS_GND_COEF_R_K2_1 0x4a2 +#define WCD_USBSS_GND_COEF_R_K3_0 0x4a3 +#define WCD_USBSS_GND_COEF_R_K3_1 0x4a4 +#define WCD_USBSS_GND_COEF_R_K4_0 0x4a5 +#define WCD_USBSS_GND_COEF_R_K4_1 0x4a6 +#define WCD_USBSS_GND_COEF_R_K5_0 0x4a7 +#define WCD_USBSS_GND_COEF_R_K5_1 0x4a8 + +#define WCD_USBSS_MAX_REGISTER 0x4c1 + +struct wcd939x_usbss { + struct i2c_client *client; + struct gpio_desc *reset_gpio; + struct regulator *vdd_supply; + + /* used to serialize concurrent change requests */ + struct mutex lock; + + struct typec_switch_dev *sw; + struct typec_mux_dev *mux; + + struct regmap *regmap; + + struct typec_mux *codec; + struct typec_switch *codec_switch; + + enum typec_orientation orientation; + unsigned long mode; + unsigned int svid; +}; + +static const struct regmap_range_cfg wcd939x_usbss_ranges[] = { + { + .range_min = 0, + .range_max = WCD_USBSS_MAX_REGISTER, + .selector_reg = 0x0, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + }, +}; + +static const struct regmap_config wcd939x_usbss_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = WCD_USBSS_MAX_REGISTER, + .ranges = wcd939x_usbss_ranges, + .num_ranges = ARRAY_SIZE(wcd939x_usbss_ranges), +}; + +/* Linearlizer coefficients for 32ohm load */ +static const struct { + unsigned int offset; + unsigned int mask; + unsigned int value; +} wcd939x_usbss_coeff_init[] = { + { WCD_USBSS_AUD_COEF_L_K5_0, GENMASK(7, 0), 0x39 }, + { WCD_USBSS_AUD_COEF_R_K5_0, GENMASK(7, 0), 0x39 }, + { WCD_USBSS_GND_COEF_L_K2_0, GENMASK(7, 0), 0xe8 }, + { WCD_USBSS_GND_COEF_L_K4_0, GENMASK(7, 0), 0x73 }, + { WCD_USBSS_GND_COEF_R_K2_0, GENMASK(7, 0), 0xe8 }, + { WCD_USBSS_GND_COEF_R_K4_0, GENMASK(7, 0), 0x73 }, + { WCD_USBSS_RATIO_SPKR_REXT_L_LSB, GENMASK(7, 0), 0x00 }, + { WCD_USBSS_RATIO_SPKR_REXT_L_MSB, GENMASK(6, 0), 0x04 }, + { WCD_USBSS_RATIO_SPKR_REXT_R_LSB, GENMASK(7, 0), 0x00 }, + { WCD_USBSS_RATIO_SPKR_REXT_R_MSB, GENMASK(6, 0), 0x04 }, +}; + +static int wcd939x_usbss_set(struct wcd939x_usbss *usbss) +{ + bool reverse = (usbss->orientation == TYPEC_ORIENTATION_REVERSE); + bool enable_audio = false; + bool enable_usb = false; + bool enable_dp = false; + int ret; + + /* USB Mode */ + if (usbss->mode < TYPEC_STATE_MODAL || + (!usbss->svid && (usbss->mode == TYPEC_MODE_USB2 || + usbss->mode == TYPEC_MODE_USB3))) { + enable_usb = true; + } else if (usbss->svid) { + switch (usbss->mode) { + /* DP Only */ + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + enable_dp = true; + break; + + /* DP + USB */ + case TYPEC_DP_STATE_D: + case TYPEC_DP_STATE_F: + enable_usb = true; + enable_dp = true; + break; + + default: + return -EOPNOTSUPP; + } + } else if (usbss->mode == TYPEC_MODE_AUDIO) { + enable_audio = true; + } else { + return -EOPNOTSUPP; + } + + /* Disable all switches */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXP_TO_MGX_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXM_TO_MGX_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DPR_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DNL_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_SENSE_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_MIC_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_AGND_SWITCHES); + if (ret) + return ret; + + /* Clear switches */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_SENSE_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_MIC_SWITCHES); + if (ret) + return ret; + + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT1, + WCD_USBSS_SWITCH_SELECT1_AGND_SWITCHES); + if (ret) + return ret; + + /* Enable OVP_MG1_BIAS PCOMP_DYN_BST_EN */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_MG1_BIAS, + WCD_USBSS_MG1_BIAS_PCOMP_DYN_BST_EN); + if (ret) + return ret; + + /* Enable OVP_MG2_BIAS PCOMP_DYN_BST_EN */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_MG2_BIAS, + WCD_USBSS_MG2_BIAS_PCOMP_DYN_BST_EN); + if (ret) + return ret; + + /* Disable Equalizer in safe mode */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_EQUALIZER1, + WCD_USBSS_EQUALIZER1_EQ_EN); + if (ret) + return ret; + + /* Start FSM with all disabled, force write */ + ret = regmap_write_bits(usbss->regmap, WCD_USBSS_AUDIO_FSM_START, + WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG, + WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG); + + /* 35us to allow the SBU switch to turn off */ + usleep_range(35, 1000); + + /* Setup Audio Accessory mux/switch */ + if (enable_audio) { + int i; + + /* + * AATC switch configuration: + * "Normal": + * - R: DNR + * - L: DNL + * - Sense: GSBU2 + * - Mic: MG1 + * - AGND: MG2 + * "Swapped": + * - R: DNR + * - L: DNL + * - Sense: GSBU1 + * - Mic: MG2 + * - AGND: MG1 + * Swapped information is given by the codec MBHC logic + */ + + /* Set AATC mode */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_USB_SS_MODE, + FIELD_PREP(WCD_USBSS_USB_SS_CNTL_USB_SS_MODE, + WCD_USBSS_USB_SS_CNTL_USB_SS_MODE_AATC)); + if (ret) + return ret; + + /* Select L for DNL_SWITCHES and R for DPR_SWITCHES */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES, + FIELD_PREP(WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_L) | + FIELD_PREP(WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_R)); + if (ret) + return ret; + + if (reverse) + /* Select MG2 for MIC, SBU1 for Sense */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_MIC_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_MIC_SWITCHES); + else + /* Select MG1 for MIC, SBU2 for Sense */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_SENSE_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_SENSE_SWITCHES); + if (ret) + return ret; + + if (reverse) + /* Disable OVP_MG1_BIAS PCOMP_DYN_BST_EN */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_MG1_BIAS, + WCD_USBSS_MG1_BIAS_PCOMP_DYN_BST_EN); + else + /* Disable OVP_MG2_BIAS PCOMP_DYN_BST_EN */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_MG2_BIAS, + WCD_USBSS_MG2_BIAS_PCOMP_DYN_BST_EN); + if (ret) + return ret; + + /* Enable SENSE, MIC switches */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_SENSE_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_MIC_SWITCHES); + if (ret) + return ret; + + if (reverse) + /* Select MG1 for AGND_SWITCHES */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT1, + WCD_USBSS_SWITCH_SELECT1_AGND_SWITCHES); + else + /* Select MG2 for AGND_SWITCHES */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT1, + WCD_USBSS_SWITCH_SELECT1_AGND_SWITCHES); + if (ret) + return ret; + + /* Enable AGND switches */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_AGND_SWITCHES); + if (ret) + return ret; + + /* Enable DPR, DNL switches */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DNL_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DPR_SWITCHES); + if (ret) + return ret; + + /* Setup FSM delays */ + ret = regmap_write(usbss->regmap, WCD_USBSS_DELAY_L_SW, 0x02); + if (ret) + return ret; + + ret = regmap_write(usbss->regmap, WCD_USBSS_DELAY_R_SW, 0x02); + if (ret) + return ret; + + ret = regmap_write(usbss->regmap, WCD_USBSS_DELAY_MIC_SW, 0x01); + if (ret) + return ret; + + /* Start FSM, force write */ + ret = regmap_write_bits(usbss->regmap, WCD_USBSS_AUDIO_FSM_START, + WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG, + WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG); + if (ret) + return ret; + + /* Default Linearlizer coefficients */ + for (i = 0; i < ARRAY_SIZE(wcd939x_usbss_coeff_init); ++i) + regmap_update_bits(usbss->regmap, + wcd939x_usbss_coeff_init[i].offset, + wcd939x_usbss_coeff_init[i].mask, + wcd939x_usbss_coeff_init[i].value); + + return 0; + } + + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_USB_SS_MODE, + FIELD_PREP(WCD_USBSS_USB_SS_CNTL_USB_SS_MODE, + WCD_USBSS_USB_SS_CNTL_USB_SS_MODE_USB)); + if (ret) + return ret; + + /* Enable USB muxes */ + if (enable_usb) { + /* Do not enable Equalizer in safe mode */ + if (usbss->mode != TYPEC_STATE_SAFE) { + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_EQUALIZER1, + WCD_USBSS_EQUALIZER1_EQ_EN); + if (ret) + return ret; + } + + /* Select DN for DNL_SWITCHES and DP for DPR_SWITCHES */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES, + FIELD_PREP(WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_DN) | + FIELD_PREP(WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_DP)); + if (ret) + return ret; + + /* Enable DNL_SWITCHES and DPR_SWITCHES */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DPR_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DNL_SWITCHES); + if (ret) + return ret; + } + + /* Enable DP AUX muxes */ + if (enable_dp) { + /* Update Leakage Canceller Coefficient for AUXP pins */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_DISP_AUXP_CTL, + WCD_USBSS_DISP_AUXP_CTL_LK_CANCEL_TRK_COEFF, + FIELD_PREP(WCD_USBSS_DISP_AUXP_CTL_LK_CANCEL_TRK_COEFF, + 5)); + if (ret) + return ret; + + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_DISP_AUXP_THRESH, + WCD_USBSS_DISP_AUXP_THRESH_DISP_AUXP_OVPON_CM); + if (ret) + return ret; + + if (reverse) + /* Select MG2 for AUXP and MG1 for AUXM */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES); + else + /* Select MG1 for AUXP and MG2 for AUXM */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES); + if (ret) + return ret; + + /* Enable DP_AUXP_TO_MGX and DP_AUXM_TO_MGX switches */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXP_TO_MGX_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXM_TO_MGX_SWITCHES); + + /* 15us to allow the SBU switch to turn on again */ + usleep_range(15, 1000); + } + + return 0; +} + +static int wcd939x_usbss_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct wcd939x_usbss *usbss = typec_switch_get_drvdata(sw); + int ret = 0; + + mutex_lock(&usbss->lock); + + if (usbss->orientation != orientation) { + usbss->orientation = orientation; + + ret = wcd939x_usbss_set(usbss); + } + + mutex_unlock(&usbss->lock); + + if (ret) + return ret; + + /* Report orientation to codec after switch has been done */ + return typec_switch_set(usbss->codec_switch, orientation); +} + +static int wcd939x_usbss_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct wcd939x_usbss *usbss = typec_mux_get_drvdata(mux); + int ret = 0; + + mutex_lock(&usbss->lock); + + if (usbss->mode != state->mode) { + usbss->mode = state->mode; + + if (state->alt) + usbss->svid = state->alt->svid; + else + usbss->svid = 0; // No SVID + + ret = wcd939x_usbss_set(usbss); + } + + mutex_unlock(&usbss->lock); + + if (ret) + return ret; + + /* Report event to codec after switch has been done */ + return typec_mux_set(usbss->codec, state); +} + +static int wcd939x_usbss_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct typec_switch_desc sw_desc = { }; + struct typec_mux_desc mux_desc = { }; + struct wcd939x_usbss *usbss; + int ret; + + usbss = devm_kzalloc(dev, sizeof(*usbss), GFP_KERNEL); + if (!usbss) + return -ENOMEM; + + usbss->client = client; + mutex_init(&usbss->lock); + + usbss->regmap = devm_regmap_init_i2c(client, &wcd939x_usbss_regmap_config); + if (IS_ERR(usbss->regmap)) + return dev_err_probe(dev, PTR_ERR(usbss->regmap), "failed to initialize regmap\n"); + + usbss->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(usbss->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(usbss->reset_gpio), + "unable to acquire reset gpio\n"); + + usbss->vdd_supply = devm_regulator_get_optional(dev, "vdd"); + if (IS_ERR(usbss->vdd_supply)) + return PTR_ERR(usbss->vdd_supply); + + /* Get Codec's MUX & Switch devices */ + usbss->codec = fwnode_typec_mux_get(dev->fwnode); + if (IS_ERR(usbss->codec)) + return dev_err_probe(dev, PTR_ERR(usbss->codec), + "failed to acquire codec mode-switch\n"); + + usbss->codec_switch = fwnode_typec_switch_get(dev->fwnode); + if (IS_ERR(usbss->codec_switch)) { + ret = dev_err_probe(dev, PTR_ERR(usbss->codec_switch), + "failed to acquire codec orientation-switch\n"); + goto err_mux_put; + } + + usbss->mode = TYPEC_STATE_SAFE; + usbss->orientation = TYPEC_ORIENTATION_NONE; + + gpiod_set_value(usbss->reset_gpio, 1); + + ret = regulator_enable(usbss->vdd_supply); + if (ret) { + dev_err(dev, "Failed to enable vdd: %d\n", ret); + goto err_mux_switch; + } + + msleep(20); + + gpiod_set_value(usbss->reset_gpio, 0); + + msleep(20); + + /* Disable standby */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_STANDBY_STATE); + if (ret) + goto err_regulator_disable; + + /* Set manual mode by default */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_FUNCTION_ENABLE, + WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT, + FIELD_PREP(WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT, + WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT_MANUAL)); + if (ret) + goto err_regulator_disable; + + /* Enable dynamic boosting for DP and DN */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_DP_DN_MISC1, + WCD_USBSS_DP_DN_MISC1_DP_PCOMP_2X_DYN_BST_ON_EN | + WCD_USBSS_DP_DN_MISC1_DN_PCOMP_2X_DYN_BST_ON_EN); + if (ret) + goto err_regulator_disable; + + /* Enable dynamic boosting for MG1 OVP */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_MG1_MISC, + WCD_USBSS_MG1_MISC_PCOMP_2X_DYN_BST_ON_EN); + if (ret) + goto err_regulator_disable; + + /* Enable dynamic boosting for MG2 OVP */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_MG2_MISC, + WCD_USBSS_MG2_MISC_PCOMP_2X_DYN_BST_ON_EN); + if (ret) + goto err_regulator_disable; + + /* Write 0xFF to WCD_USBSS_CPLDO_CTL2 */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_CPLDO_CTL2, 0xff); + if (ret) + goto err_regulator_disable; + + /* Set RCO_EN: WCD_USBSS_USB_SS_CNTL Bit<3> --> 0x0 --> 0x1 */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_RCO_EN); + if (ret) + goto err_regulator_disable; + + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_RCO_EN); + if (ret) + goto err_regulator_disable; + + /* Disable all switches but enable the mux */ + ret = regmap_write(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DEVICE_ENABLE); + if (ret) + goto err_regulator_disable; + + /* Setup in SAFE mode */ + ret = wcd939x_usbss_set(usbss); + if (ret) + goto err_regulator_disable; + + sw_desc.drvdata = usbss; + sw_desc.fwnode = dev_fwnode(dev); + sw_desc.set = wcd939x_usbss_switch_set; + + usbss->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(usbss->sw)) { + ret = dev_err_probe(dev, PTR_ERR(usbss->sw), "failed to register typec switch\n"); + goto err_regulator_disable; + } + + mux_desc.drvdata = usbss; + mux_desc.fwnode = dev_fwnode(dev); + mux_desc.set = wcd939x_usbss_mux_set; + + usbss->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(usbss->mux)) { + ret = dev_err_probe(dev, PTR_ERR(usbss->mux), "failed to register typec mux\n"); + goto err_switch_unregister; + } + + i2c_set_clientdata(client, usbss); + + return 0; + +err_switch_unregister: + typec_switch_unregister(usbss->sw); + +err_regulator_disable: + regulator_disable(usbss->vdd_supply); + +err_mux_switch: + typec_switch_put(usbss->codec_switch); + +err_mux_put: + typec_mux_put(usbss->codec); + + return ret; +} + +static void wcd939x_usbss_remove(struct i2c_client *client) +{ + struct wcd939x_usbss *usbss = i2c_get_clientdata(client); + + typec_mux_unregister(usbss->mux); + typec_switch_unregister(usbss->sw); + + regulator_disable(usbss->vdd_supply); + + typec_switch_put(usbss->codec_switch); + typec_mux_put(usbss->codec); +} + +static const struct i2c_device_id wcd939x_usbss_table[] = { + { "wcd9390-usbss" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wcd939x_usbss_table); + +static const struct of_device_id wcd939x_usbss_of_table[] = { + { .compatible = "qcom,wcd9390-usbss" }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd939x_usbss_of_table); + +static struct i2c_driver wcd939x_usbss_driver = { + .driver = { + .name = "wcd939x-usbss", + .of_match_table = wcd939x_usbss_of_table, + }, + .probe = wcd939x_usbss_probe, + .remove = wcd939x_usbss_remove, + .id_table = wcd939x_usbss_table, +}; +module_i2c_driver(wcd939x_usbss_driver); + +MODULE_DESCRIPTION("Qualcomm WCD939x USBSS driver"); +MODULE_LICENSE("GPL"); From b8fb6db6cb04e3c35d661d0f6cf6f8dc7444ce0c Mon Sep 17 00:00:00 2001 From: Perr Zhang Date: Wed, 13 Dec 2023 19:21:06 +0800 Subject: [PATCH 091/169] usb: f_uac1: adds support for SS and SSP Patch adds support of SS and SSP speed. Tested with rockchip rk3399 dwc3 Signed-off-by: Perr Zhang Link: https://lore.kernel.org/r/20231213112106.605260-1-strongbox8@zoho.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uac1.c | 77 +++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 6f0e1d803dc2..78634ea7863a 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -292,6 +292,77 @@ static struct usb_descriptor_header *f_audio_desc[] = { NULL, }; +/* Standard ISO OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor ss_as_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + .bInterval = 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_as_out_ep_desc_comp = { + .bLength = sizeof(ss_as_out_ep_desc_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + /* wBytesPerInterval = DYNAMIC */ +}; + +/* Standard ISO OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor ss_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + .bInterval = 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_as_in_ep_desc_comp = { + .bLength = sizeof(ss_as_in_ep_desc_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + /* wBytesPerInterval = DYNAMIC */ +}; + +static struct usb_descriptor_header *f_audio_ss_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&io_out_ot_desc, + (struct usb_descriptor_header *)&io_in_it_desc, + (struct usb_descriptor_header *)&usb_in_ot_desc, + + (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_out_header_desc, + + (struct usb_descriptor_header *)&as_out_type_i_desc, + + //(struct usb_descriptor_header *)&as_out_ep_desc, + (struct usb_descriptor_header *)&ss_as_out_ep_desc, + (struct usb_descriptor_header *)&ss_as_out_ep_desc_comp, + (struct usb_descriptor_header *)&as_iso_out_desc, + + (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_in_header_desc, + + (struct usb_descriptor_header *)&as_in_type_i_desc, + + //(struct usb_descriptor_header *)&as_in_ep_desc, + (struct usb_descriptor_header *)&ss_as_in_ep_desc, + (struct usb_descriptor_header *)&ss_as_in_ep_desc_comp, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + enum { STR_AC_IF, STR_USB_OUT_IT, @@ -1352,6 +1423,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); if (!ep) goto err_free_fu; + ss_as_out_ep_desc.bEndpointAddress = as_out_ep_desc.bEndpointAddress; audio->out_ep = ep; audio->out_ep->desc = &as_out_ep_desc; } @@ -1360,6 +1432,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc); if (!ep) goto err_free_fu; + ss_as_in_ep_desc.bEndpointAddress = as_in_ep_desc.bEndpointAddress; audio->in_ep = ep; audio->in_ep->desc = &as_in_ep_desc; } @@ -1367,8 +1440,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) setup_descriptor(audio_opts); /* copy descriptors, and track endpoint copies */ - status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL, - NULL); + status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, f_audio_ss_desc, + f_audio_ss_desc); if (status) goto err_free_fu; From 0466e7e693efe6647f23532529a9c41a1fa5f4ac Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:30 +0000 Subject: [PATCH 092/169] usb: gadget: configfs: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-2-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/configfs.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index b7d2a1313a68..ce3cfa1f36f5 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -606,10 +606,11 @@ static struct config_group *function_make( char *instance_name; int ret; - ret = snprintf(buf, MAX_NAME_LEN, "%s", name); - if (ret >= MAX_NAME_LEN) + if (strlen(name) >= MAX_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); + scnprintf(buf, MAX_NAME_LEN, "%s", name); + func_name = buf; instance_name = strchr(func_name, '.'); if (!instance_name) { @@ -701,10 +702,12 @@ static struct config_group *config_desc_make( int ret; gi = container_of(group, struct gadget_info, configs_group); - ret = snprintf(buf, MAX_NAME_LEN, "%s", name); - if (ret >= MAX_NAME_LEN) + + if (strlen(name) >= MAX_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); + scnprintf(buf, MAX_NAME_LEN, "%s", name); + num_str = strchr(buf, '.'); if (!num_str) { pr_err("Unable to locate . in name.bConfigurationValue\n"); From c1a371866db9c44ab3004b5fc06df066a1b96262 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:31 +0000 Subject: [PATCH 093/169] usb: gadget: f_uac1: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Ruslan Bilovol Cc: Julian Scheel Cc: Bryan Wu Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-3-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uac1.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 78634ea7863a..7de74a3dd392 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -1634,7 +1634,7 @@ static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ int result; \ \ mutex_lock(&opts->lock); \ - result = snprintf(page, sizeof(opts->name), "%s", opts->name); \ + result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \ mutex_unlock(&opts->lock); \ \ return result; \ @@ -1652,7 +1652,7 @@ static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \ goto end; \ } \ \ - ret = snprintf(opts->name, min(sizeof(opts->name), len), \ + ret = scnprintf(opts->name, min(sizeof(opts->name), len), \ "%s", page); \ \ end: \ @@ -1758,7 +1758,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void) opts->req_number = UAC1_DEF_REQ_NUM; - snprintf(opts->function_name, sizeof(opts->function_name), "AC Interface"); + scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface"); return &opts->func_inst; } From 60034e0aedf507888c4a880f57011bb7f5d7700c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:32 +0000 Subject: [PATCH 094/169] usb: gadget: f_uac2: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: James Gruber Cc: Yadwinder Singh Cc: Jaswinder Singh Cc: Ruslan Bilovol Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-4-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uac2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index f9a0f07a7476..383f6854cfec 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -2045,7 +2045,7 @@ static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ int result; \ \ mutex_lock(&opts->lock); \ - result = snprintf(page, sizeof(opts->name), "%s", opts->name); \ + result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \ mutex_unlock(&opts->lock); \ \ return result; \ @@ -2063,7 +2063,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ goto end; \ } \ \ - ret = snprintf(opts->name, min(sizeof(opts->name), len), \ + ret = scnprintf(opts->name, min(sizeof(opts->name), len), \ "%s", page); \ \ end: \ @@ -2187,7 +2187,7 @@ static struct usb_function_instance *afunc_alloc_inst(void) opts->req_number = UAC2_DEF_REQ_NUM; opts->fb_max = FBACK_FAST_MAX; - snprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink"); + scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink"); opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE; opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE; From 0d12c1cca7883620b1888ce4b892a9ed27bf3682 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:33 +0000 Subject: [PATCH 095/169] usb: gadget: uvc: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Laurent Pinchart Cc: Daniel Scally Cc: Andrzej Pietrasiewicz Signed-off-by: Lee Jones Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20231213164246.1021885-5-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc_configfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 9bf0e985acfa..7e704b2bcfd1 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -3414,7 +3414,7 @@ static ssize_t f_uvc_opts_string_##cname##_show(struct config_item *item,\ int result; \ \ mutex_lock(&opts->lock); \ - result = snprintf(page, sizeof(opts->aname), "%s", opts->aname);\ + result = scnprintf(page, sizeof(opts->aname), "%s", opts->aname);\ mutex_unlock(&opts->lock); \ \ return result; \ From d32dcb0659bcb3b68878a985280024ec92d0db5b Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:34 +0000 Subject: [PATCH 096/169] usb: gadget: udc: atmel: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Cristian Birsan Cc: Nicolas Ferre Cc: Alexandre Belloni Cc: Claudiu Beznea Cc: Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-6-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/atmel_usba_udc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 02b1bef5e22e..b76885d78e8a 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -94,7 +94,7 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf, inode_lock(file_inode(file)); list_for_each_entry_safe(req, tmp_req, queue, queue) { - len = snprintf(tmpbuf, sizeof(tmpbuf), + len = scnprintf(tmpbuf, sizeof(tmpbuf), "%8p %08x %c%c%c %5d %c%c%c\n", req->req.buf, req->req.length, req->req.no_interrupt ? 'i' : 'I', @@ -104,7 +104,6 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf, req->submitted ? 'F' : 'f', req->using_dma ? 'D' : 'd', req->last_transaction ? 'L' : 'l'); - len = min(len, sizeof(tmpbuf)); if (len > nbytes) break; From 01dc7f7c29be8b4fa853eb300f54065d981b31a9 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:35 +0000 Subject: [PATCH 097/169] usb: cdns2: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Pawel Laszczak Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-7-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/cdns2/cdns2-debug.h | 138 ++++++++++----------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h index be9ae0d28a40..f5f330004190 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h +++ b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h @@ -16,34 +16,34 @@ static inline const char *cdns2_decode_usb_irq(char *str, size_t size, { int ret; - ret = snprintf(str, size, "usbirq: 0x%02x - ", usb_irq); + ret = scnprintf(str, size, "usbirq: 0x%02x - ", usb_irq); if (usb_irq & USBIRQ_SOF) - ret += snprintf(str + ret, size - ret, "SOF "); + ret += scnprintf(str + ret, size - ret, "SOF "); if (usb_irq & USBIRQ_SUTOK) - ret += snprintf(str + ret, size - ret, "SUTOK "); + ret += scnprintf(str + ret, size - ret, "SUTOK "); if (usb_irq & USBIRQ_SUDAV) - ret += snprintf(str + ret, size - ret, "SETUP "); + ret += scnprintf(str + ret, size - ret, "SETUP "); if (usb_irq & USBIRQ_SUSPEND) - ret += snprintf(str + ret, size - ret, "Suspend "); + ret += scnprintf(str + ret, size - ret, "Suspend "); if (usb_irq & USBIRQ_URESET) - ret += snprintf(str + ret, size - ret, "Reset "); + ret += scnprintf(str + ret, size - ret, "Reset "); if (usb_irq & USBIRQ_HSPEED) - ret += snprintf(str + ret, size - ret, "HS "); + ret += scnprintf(str + ret, size - ret, "HS "); if (usb_irq & USBIRQ_LPM) - ret += snprintf(str + ret, size - ret, "LPM "); + ret += scnprintf(str + ret, size - ret, "LPM "); - ret += snprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq); + ret += scnprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq); if (ext_irq & EXTIRQ_WAKEUP) - ret += snprintf(str + ret, size - ret, "Wakeup "); + ret += scnprintf(str + ret, size - ret, "Wakeup "); if (ext_irq & EXTIRQ_VBUSFAULT_FALL) - ret += snprintf(str + ret, size - ret, "VBUS_FALL "); + ret += scnprintf(str + ret, size - ret, "VBUS_FALL "); if (ext_irq & EXTIRQ_VBUSFAULT_RISE) - ret += snprintf(str + ret, size - ret, "VBUS_RISE "); + ret += scnprintf(str + ret, size - ret, "VBUS_RISE "); - if (ret >= size) - pr_info("CDNS2: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNS2: buffer may be truncated.\n"); return str; } @@ -54,28 +54,28 @@ static inline const char *cdns2_decode_dma_irq(char *str, size_t size, { int ret; - ret = snprintf(str, size, "ISTS: %08x, %s: %08x ", - ep_ists, ep_name, ep_sts); + ret = scnprintf(str, size, "ISTS: %08x, %s: %08x ", + ep_ists, ep_name, ep_sts); if (ep_sts & DMA_EP_STS_IOC) - ret += snprintf(str + ret, size - ret, "IOC "); + ret += scnprintf(str + ret, size - ret, "IOC "); if (ep_sts & DMA_EP_STS_ISP) - ret += snprintf(str + ret, size - ret, "ISP "); + ret += scnprintf(str + ret, size - ret, "ISP "); if (ep_sts & DMA_EP_STS_DESCMIS) - ret += snprintf(str + ret, size - ret, "DESCMIS "); + ret += scnprintf(str + ret, size - ret, "DESCMIS "); if (ep_sts & DMA_EP_STS_TRBERR) - ret += snprintf(str + ret, size - ret, "TRBERR "); + ret += scnprintf(str + ret, size - ret, "TRBERR "); if (ep_sts & DMA_EP_STS_OUTSMM) - ret += snprintf(str + ret, size - ret, "OUTSMM "); + ret += scnprintf(str + ret, size - ret, "OUTSMM "); if (ep_sts & DMA_EP_STS_ISOERR) - ret += snprintf(str + ret, size - ret, "ISOERR "); + ret += scnprintf(str + ret, size - ret, "ISOERR "); if (ep_sts & DMA_EP_STS_DBUSY) - ret += snprintf(str + ret, size - ret, "DBUSY "); + ret += scnprintf(str + ret, size - ret, "DBUSY "); if (DMA_EP_STS_CCS(ep_sts)) - ret += snprintf(str + ret, size - ret, "CCS "); + ret += scnprintf(str + ret, size - ret, "CCS "); - if (ret >= size) - pr_info("CDNS2: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNS2: buffer may be truncated.\n"); return str; } @@ -105,43 +105,43 @@ static inline const char *cdns2_raw_ring(struct cdns2_endpoint *pep, int ret; int i; - ret = snprintf(str, size, "\n\t\tTR for %s:", pep->name); + ret = scnprintf(str, size, "\n\t\tTR for %s:", pep->name); trb = &trbs[ring->dequeue]; dma = cdns2_trb_virt_to_dma(pep, trb); - ret += snprintf(str + ret, size - ret, - "\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n", - ring->dequeue, trb, &dma); + ret += scnprintf(str + ret, size - ret, + "\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n", + ring->dequeue, trb, &dma); trb = &trbs[ring->enqueue]; dma = cdns2_trb_virt_to_dma(pep, trb); - ret += snprintf(str + ret, size - ret, - "\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n", - ring->enqueue, trb, &dma); + ret += scnprintf(str + ret, size - ret, + "\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n", + ring->enqueue, trb, &dma); - ret += snprintf(str + ret, size - ret, - "\t\tfree trbs: %d, CCS=%d, PCS=%d\n", - ring->free_trbs, ring->ccs, ring->pcs); + ret += scnprintf(str + ret, size - ret, + "\t\tfree trbs: %d, CCS=%d, PCS=%d\n", + ring->free_trbs, ring->ccs, ring->pcs); if (TRBS_PER_SEGMENT > 40) { - ret += snprintf(str + ret, size - ret, - "\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT); + ret += scnprintf(str + ret, size - ret, + "\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT); return str; } dma = ring->dma; for (i = 0; i < TRBS_PER_SEGMENT; ++i) { trb = &trbs[i]; - ret += snprintf(str + ret, size - ret, - "\t\t@%pad %08x %08x %08x\n", &dma, - le32_to_cpu(trb->buffer), - le32_to_cpu(trb->length), - le32_to_cpu(trb->control)); + ret += scnprintf(str + ret, size - ret, + "\t\t@%pad %08x %08x %08x\n", &dma, + le32_to_cpu(trb->buffer), + le32_to_cpu(trb->length), + le32_to_cpu(trb->control)); dma += sizeof(*trb); } - if (ret >= size) - pr_info("CDNS2: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNS2: buffer may be truncated.\n"); return str; } @@ -166,36 +166,36 @@ static inline const char *cdns2_decode_trb(char *str, size_t size, u32 flags, switch (type) { case TRB_LINK: - ret = snprintf(str, size, - "LINK %08x type '%s' flags %c:%c:%c%c:%c", - buffer, cdns2_trb_type_string(type), - flags & TRB_CYCLE ? 'C' : 'c', - flags & TRB_TOGGLE ? 'T' : 't', - flags & TRB_CHAIN ? 'C' : 'c', - flags & TRB_CHAIN ? 'H' : 'h', - flags & TRB_IOC ? 'I' : 'i'); + ret = scnprintf(str, size, + "LINK %08x type '%s' flags %c:%c:%c%c:%c", + buffer, cdns2_trb_type_string(type), + flags & TRB_CYCLE ? 'C' : 'c', + flags & TRB_TOGGLE ? 'T' : 't', + flags & TRB_CHAIN ? 'C' : 'c', + flags & TRB_CHAIN ? 'H' : 'h', + flags & TRB_IOC ? 'I' : 'i'); break; case TRB_NORMAL: - ret = snprintf(str, size, - "type: '%s', Buffer: %08x, length: %ld, burst len: %ld, " - "flags %c:%c:%c%c:%c", - cdns2_trb_type_string(type), - buffer, TRB_LEN(length), - TRB_FIELD_TO_BURST(length), - flags & TRB_CYCLE ? 'C' : 'c', - flags & TRB_ISP ? 'I' : 'i', - flags & TRB_CHAIN ? 'C' : 'c', - flags & TRB_CHAIN ? 'H' : 'h', - flags & TRB_IOC ? 'I' : 'i'); + ret = scnprintf(str, size, + "type: '%s', Buffer: %08x, length: %ld, burst len: %ld, " + "flags %c:%c:%c%c:%c", + cdns2_trb_type_string(type), + buffer, TRB_LEN(length), + TRB_FIELD_TO_BURST(length), + flags & TRB_CYCLE ? 'C' : 'c', + flags & TRB_ISP ? 'I' : 'i', + flags & TRB_CHAIN ? 'C' : 'c', + flags & TRB_CHAIN ? 'H' : 'h', + flags & TRB_IOC ? 'I' : 'i'); break; default: - ret = snprintf(str, size, "type '%s' -> raw %08x %08x %08x", - cdns2_trb_type_string(type), - buffer, length, flags); + ret = scnprintf(str, size, "type '%s' -> raw %08x %08x %08x", + cdns2_trb_type_string(type), + buffer, length, flags); } - if (ret >= size) - pr_info("CDNS2: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNS2: buffer may be truncated.\n"); return str; } From a6eef67cdb84e06112fc29176d6c6061d3ea8d79 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:36 +0000 Subject: [PATCH 098/169] usb: host: max3421-hcd: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Cristian Birsan Cc: Nicolas Ferre Cc: Alexandre Belloni Cc: Claudiu Beznea Cc: Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-8-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/max3421-hcd.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index d152d72de126..9fe4f48b1898 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1158,12 +1158,12 @@ dump_eps(struct usb_hcd *hcd) end = dp + sizeof(ubuf); *dp = '\0'; list_for_each_entry(urb, &ep->urb_list, urb_list) { - ret = snprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb, - usb_pipetype(urb->pipe), - usb_urb_dir_in(urb) ? "IN" : "OUT", - urb->actual_length, - urb->transfer_buffer_length); - if (ret < 0 || ret >= end - dp) + ret = scnprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb, + usb_pipetype(urb->pipe), + usb_urb_dir_in(urb) ? "IN" : "OUT", + urb->actual_length, + urb->transfer_buffer_length); + if (ret == end - dp - 1) break; /* error or buffer full */ dp += ret; } @@ -1255,9 +1255,9 @@ max3421_handle_irqs(struct usb_hcd *hcd) end = sbuf + sizeof(sbuf); *dp = '\0'; for (i = 0; i < 16; ++i) { - int ret = snprintf(dp, end - dp, " %lu", - max3421_hcd->err_stat[i]); - if (ret < 0 || ret >= end - dp) + int ret = scnprintf(dp, end - dp, " %lu", + max3421_hcd->err_stat[i]); + if (ret == end - dp - 1) break; /* error or buffer full */ dp += ret; } From 86b20af11e84c26ae3fde4dcc4f490948e3f8035 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:37 +0000 Subject: [PATCH 099/169] usb: yurex: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Whilst we're at it, let's define some magic numbers to increase readability and ease of maintenance. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Tomoki Sekiyama Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-9-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/yurex.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index c640f98d20c5..5a13cddace0e 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -34,6 +34,8 @@ #define YUREX_BUF_SIZE 8 #define YUREX_WRITE_TIMEOUT (HZ*2) +#define MAX_S64_STRLEN 20 /* {-}922337203685477580{7,8} */ + /* table of devices that work with this driver */ static struct usb_device_id yurex_table[] = { { USB_DEVICE(YUREX_VENDOR_ID, YUREX_PRODUCT_ID) }, @@ -401,7 +403,7 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, { struct usb_yurex *dev; int len = 0; - char in_buffer[20]; + char in_buffer[MAX_S64_STRLEN]; unsigned long flags; dev = file->private_data; @@ -412,14 +414,14 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, return -ENODEV; } + if (WARN_ON_ONCE(dev->bbu > S64_MAX || dev->bbu < S64_MIN)) + return -EIO; + spin_lock_irqsave(&dev->lock, flags); - len = snprintf(in_buffer, 20, "%lld\n", dev->bbu); + scnprintf(in_buffer, MAX_S64_STRLEN, "%lld\n", dev->bbu); spin_unlock_irqrestore(&dev->lock, flags); mutex_unlock(&dev->io_mutex); - if (WARN_ON_ONCE(len >= sizeof(in_buffer))) - return -EIO; - return simple_read_from_buffer(buffer, count, ppos, in_buffer, len); } From 79632569619f4d57ff745398dba98e09105b5108 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:38 +0000 Subject: [PATCH 100/169] usb: mon_stat: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-10-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_stat.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c index 98ab0cc473d6..3c23805ab1a4 100644 --- a/drivers/usb/mon/mon_stat.c +++ b/drivers/usb/mon/mon_stat.c @@ -35,9 +35,9 @@ static int mon_stat_open(struct inode *inode, struct file *file) mbus = inode->i_private; - sp->slen = snprintf(sp->str, STAT_BUF_SIZE, - "nreaders %d events %u text_lost %u\n", - mbus->nreaders, mbus->cnt_events, mbus->cnt_text_lost); + sp->slen = scnprintf(sp->str, STAT_BUF_SIZE, + "nreaders %d events %u text_lost %u\n", + mbus->nreaders, mbus->cnt_events, mbus->cnt_text_lost); file->private_data = sp; return 0; From 9d4e3d15d7bf3439a6f3fb4aafc4f92ea2c5c5fd Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:39 +0000 Subject: [PATCH 101/169] usb: mon_text: Replace snprintf() with the safer scnprintf() variant There is a general misunderstanding amongst engineers that {v}snprintf() returns the length of the data *actually* encoded into the destination array. However, as per the C99 standard {v}snprintf() really returns the length of the data that *would have been* written if there were enough space for it. This misunderstanding has led to buffer-overruns in the past. It's generally considered safer to use the {v}scnprintf() variants in their place (or even sprintf() in simple cases). So let's do that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-11-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_text.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 39cb14164652..2fe9b95bac1d 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -352,7 +352,7 @@ static int mon_text_open(struct inode *inode, struct file *file) rp->r.rnf_error = mon_text_error; rp->r.rnf_complete = mon_text_complete; - snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp); + scnprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp); rp->e_slab = kmem_cache_create(rp->slab_name, sizeof(struct mon_event_text), sizeof(long), 0, mon_text_ctor); @@ -700,46 +700,28 @@ static const struct file_operations mon_fops_text_u = { int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) { - enum { NAMESZ = 10 }; + enum { NAMESZ = 12 }; char name[NAMESZ]; int busnum = ubus? ubus->busnum: 0; - int rc; if (mon_dir == NULL) return 0; if (ubus != NULL) { - rc = snprintf(name, NAMESZ, "%dt", busnum); - if (rc <= 0 || rc >= NAMESZ) - goto err_print_t; + scnprintf(name, NAMESZ, "%dt", busnum); mbus->dent_t = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t); } - rc = snprintf(name, NAMESZ, "%du", busnum); - if (rc <= 0 || rc >= NAMESZ) - goto err_print_u; + scnprintf(name, NAMESZ, "%du", busnum); mbus->dent_u = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u); - rc = snprintf(name, NAMESZ, "%ds", busnum); - if (rc <= 0 || rc >= NAMESZ) - goto err_print_s; + scnprintf(name, NAMESZ, "%ds", busnum); mbus->dent_s = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat); return 1; - -err_print_s: - debugfs_remove(mbus->dent_u); - mbus->dent_u = NULL; -err_print_u: - if (ubus != NULL) { - debugfs_remove(mbus->dent_t); - mbus->dent_t = NULL; - } -err_print_t: - return 0; } void mon_text_del(struct mon_bus *mbus) From e5892ea81515366588e8fb00a5a3cdca687355dc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:40 +0000 Subject: [PATCH 102/169] usb: phy: twl6030: Remove snprintf() from sysfs call-backs and replace with sysfs_emit() Since snprintf() has the documented, but still rather strange trait of returning the length of the data that *would have been* written to the array if space were available, rather than the arguably more useful length of data *actually* written, it is usually considered wise to use something else instead in order to avoid confusion. In the case of sysfs call-backs, new wrappers exist that do just that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Hema HK Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20231213164246.1021885-12-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-twl6030-usb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index c3ce6b1054f1..da09cff55abc 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -179,16 +179,16 @@ static ssize_t vbus_show(struct device *dev, switch (twl->linkstat) { case MUSB_VBUS_VALID: - ret = snprintf(buf, PAGE_SIZE, "vbus\n"); + ret = sysfs_emit(buf, "vbus\n"); break; case MUSB_ID_GROUND: - ret = snprintf(buf, PAGE_SIZE, "id\n"); + ret = sysfs_emit(buf, "id\n"); break; case MUSB_VBUS_OFF: - ret = snprintf(buf, PAGE_SIZE, "none\n"); + ret = sysfs_emit(buf, "none\n"); break; default: - ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); + ret = sysfs_emit(buf, "UNKNOWN\n"); } spin_unlock_irqrestore(&twl->lock, flags); From 3e42084a1c4790364c28b5cafd5c66fc25396a64 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Dec 2023 16:42:41 +0000 Subject: [PATCH 103/169] usb: storage: Remove snprintf() from sysfs call-backs and replace with sysfs_emit() Since snprintf() has the documented, but still rather strange trait of returning the length of the data that *would have been* written to the array if space were available, rather than the arguably more useful length of data *actually* written, it is usually considered wise to use something else instead in order to avoid confusion. In the case of sysfs call-backs, new wrappers exist that do just that. Link: https://lwn.net/Articles/69419/ Link: https://github.com/KSPP/linux/issues/105 Cc: Alan Stern Cc: Signed-off-by: Lee Jones Acked-by: Alan Stern Link: https://lore.kernel.org/r/20231213164246.1021885-13-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/sierra_ms.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c index 0774ba22fb66..177fa6cd143a 100644 --- a/drivers/usb/storage/sierra_ms.c +++ b/drivers/usb/storage/sierra_ms.c @@ -98,26 +98,26 @@ static ssize_t truinst_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev = interface_to_usbdev(intf); int result; if (swi_tru_install == TRU_FORCE_MS) { - result = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n"); + result = sysfs_emit(buf, "Forced Mass Storage\n"); } else { swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL); if (!swocInfo) { - snprintf(buf, PAGE_SIZE, "Error\n"); + sysfs_emit(buf, "Error\n"); return -ENOMEM; } result = sierra_get_swoc_info(udev, swocInfo); if (result < 0) { dev_dbg(dev, "SWIMS: failed SWoC query\n"); kfree(swocInfo); - snprintf(buf, PAGE_SIZE, "Error\n"); + sysfs_emit(buf, "Error\n"); return -EIO; } debug_swoc(dev, swocInfo); - result = snprintf(buf, PAGE_SIZE, - "REV=%02d SKU=%04X VER=%04X\n", - swocInfo->rev, - swocInfo->LinuxSKU, - swocInfo->LinuxVer); + result = sysfs_emit(buf, + "REV=%02d SKU=%04X VER=%04X\n", + swocInfo->rev, + swocInfo->LinuxSKU, + swocInfo->LinuxVer); kfree(swocInfo); } return result; From 61fbf20312bdd1394a9cac67ed8f706e205511af Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Thu, 14 Dec 2023 12:04:15 +0300 Subject: [PATCH 104/169] usb: gadget: f_fs: fix fortify warning When compiling with gcc version 14.0.0 20231206 (experimental) and CONFIG_FORTIFY_SOURCE=y, I've noticed the following warning: ... In function 'fortify_memcpy_chk', inlined from '__ffs_func_bind_do_os_desc' at drivers/usb/gadget/function/f_fs.c:2934:3: ./include/linux/fortify-string.h:588:25: warning: call to '__read_overflow2_field' declared with attribute warning: detected read beyond size of field (2nd parameter); maybe use struct_group()? [-Wattribute-warning] 588 | __read_overflow2_field(q_size_field, size); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This call to 'memcpy()' is interpreted as an attempt to copy both 'CompatibleID' and 'SubCompatibleID' of 'struct usb_ext_compat_desc' from an address of the first one, which causes an overread warning. Since we actually want to copy both of them at once, use the convenient 'struct_group()' and 'sizeof_field()' here. Signed-off-by: Dmitry Antipov Link: https://lore.kernel.org/r/20231214090428.27292-1-dmantipov@yandex.ru Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 5 ++--- include/uapi/linux/usb/functionfs.h | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index efe3e3b85769..dafedc33928d 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2931,9 +2931,8 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type, t = &func->function.os_desc_table[desc->bFirstInterfaceNumber]; t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber]; - memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID, - ARRAY_SIZE(desc->CompatibleID) + - ARRAY_SIZE(desc->SubCompatibleID)); + memcpy(t->os_desc->ext_compat_id, &desc->IDs, + sizeof_field(struct usb_ext_compat_desc, IDs)); length = sizeof(*desc); } break; diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index d77ee6b65328..078098e73fd3 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -73,8 +73,10 @@ struct usb_os_desc_header { struct usb_ext_compat_desc { __u8 bFirstInterfaceNumber; __u8 Reserved1; - __u8 CompatibleID[8]; - __u8 SubCompatibleID[8]; + __struct_group(/* no tag */, IDs, /* no attrs */, + __u8 CompatibleID[8]; + __u8 SubCompatibleID[8]; + ); __u8 Reserved2[6]; }; From 776630be36935be3a51e5ecfa7fc7614c4d4e46e Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Thu, 14 Dec 2023 15:40:11 +0100 Subject: [PATCH 105/169] usb: cdns3: Use dev_err_probe Create an error message or upon deferral add a description for sysfs. Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20231214144011.1987586-1-alexander.stein@ew.tq-group.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-plat.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 2c1aca84f226..3ef8e3c872a3 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -87,16 +87,20 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral"); if (cdns->dev_irq < 0) - return cdns->dev_irq; + return dev_err_probe(dev, cdns->dev_irq, + "Failed to get peripheral IRQ\n"); regs = devm_platform_ioremap_resource_byname(pdev, "dev"); if (IS_ERR(regs)) - return PTR_ERR(regs); + return dev_err_probe(dev, PTR_ERR(regs), + "Failed to get dev base\n"); + cdns->dev_regs = regs; cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); if (cdns->otg_irq < 0) - return cdns->otg_irq; + return dev_err_probe(dev, cdns->otg_irq, + "Failed to get otg IRQ\n"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg"); if (!res) { @@ -119,7 +123,8 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); if (IS_ERR(cdns->usb2_phy)) - return PTR_ERR(cdns->usb2_phy); + return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), + "Failed to get cdn3,usb2-phy\n"); ret = phy_init(cdns->usb2_phy); if (ret) @@ -127,7 +132,8 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); if (IS_ERR(cdns->usb3_phy)) - return PTR_ERR(cdns->usb3_phy); + return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy), + "Failed to get cdn3,usb3-phy\n"); ret = phy_init(cdns->usb3_phy); if (ret) From 4d2f8c859146672ce84f49648121c70ea84e246c Mon Sep 17 00:00:00 2001 From: Ghanshyam Agrawal Date: Fri, 15 Dec 2023 14:09:30 +0530 Subject: [PATCH 106/169] usb: typec: fixed a typo Fixed one typo. Signed-off-by: Ghanshyam Agrawal Link: https://lore.kernel.org/r/20231215083930.566164-1-ghanshyam1898@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/pd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c index 7f3d61f220f2..b9cca2be76fc 100644 --- a/drivers/usb/typec/pd.c +++ b/drivers/usb/typec/pd.c @@ -468,7 +468,7 @@ static struct device_type pd_capabilities_type = { /** * usb_power_delivery_register_capabilities - Register a set of capabilities. * @pd: The USB PD instance that the capabilities belong to. - * @desc: Description of the Capablities Message. + * @desc: Description of the Capabilities Message. * * This function registers a Capabilities Message described in @desc. The * capabilities will have their own sub-directory under @pd in sysfs. From c51ffe929f3b34999ce19f413d3950ea72e1e5ad Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Thu, 14 Dec 2023 19:06:52 +0200 Subject: [PATCH 107/169] dt-bindings: usb: qcom,dwc3: Add X1E80100 binding Add X1E80100 to the DT schema. Signed-off-by: Abel Vesa Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20231214-x1e80100-usb-v1-1-c22be5c0109e@linaro.org 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 3ec62027f663..473c4bfaf8a2 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -47,6 +47,7 @@ properties: - qcom,sm8450-dwc3 - qcom,sm8550-dwc3 - qcom,sm8650-dwc3 + - qcom,x1e80100-dwc3 - const: qcom,dwc3 reg: @@ -264,6 +265,7 @@ allOf: contains: enum: - qcom,sc8280xp-dwc3 + - qcom,x1e80100-dwc3 then: properties: clocks: @@ -459,6 +461,7 @@ allOf: contains: enum: - qcom,sc8280xp-dwc3 + - qcom,x1e80100-dwc3 then: properties: interrupts: From f1fd91a0924b6bff91ca1287461fb8e3b3b61d92 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 15 Dec 2023 14:16:14 +0100 Subject: [PATCH 108/169] usb: gadget: webcam: Make g_webcam loadable again commit 588b9e85609b ("usb: gadget: uvc: add v4l2 enumeration api calls") has rendered the precomposed (aka legacy) webcam gadget unloadable. uvc_alloc() since then has depended on certain config groups being available in configfs tree related to the UVC function. However, legacy gadgets do not create anything in configfs, so uvc_alloc() must fail with -ENOENT no matter what. This patch mimics the required configfs hierarchy to satisfy the code which inspects formats and frames found in uvcg_streaming_header. This has been tested with guvcview on the host side, using vivid as a source of video stream on the device side and using the userspace program found at https://gitlab.freedesktop.org/camera/uvc-gadget.git. Signed-off-by: Andrzej Pietrasiewicz Fixes: 588b9e85609b ("usb: gadget: uvc: add v4l2 enumeration api calls") Link: https://lore.kernel.org/r/20231215131614.29132-1-andrzej.p@collabora.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uvc.c | 39 ++-- drivers/usb/gadget/function/u_uvc.h | 6 + drivers/usb/gadget/legacy/webcam.c | 333 +++++++++++++++++++++------- 3 files changed, 281 insertions(+), 97 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 77999ed53d33..da5f28e471b0 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -959,7 +959,8 @@ static void uvc_free(struct usb_function *f) struct uvc_device *uvc = to_uvc(f); struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts, func_inst); - config_item_put(&uvc->header->item); + if (!opts->header) + config_item_put(&uvc->header->item); --opts->refcnt; kfree(uvc); } @@ -1051,25 +1052,29 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) uvc->desc.hs_streaming = opts->hs_streaming; uvc->desc.ss_streaming = opts->ss_streaming; - streaming = config_group_find_item(&opts->func_inst.group, "streaming"); - if (!streaming) - goto err_config; + if (opts->header) { + uvc->header = opts->header; + } else { + streaming = config_group_find_item(&opts->func_inst.group, "streaming"); + if (!streaming) + goto err_config; - header = config_group_find_item(to_config_group(streaming), "header"); - config_item_put(streaming); - if (!header) - goto err_config; + header = config_group_find_item(to_config_group(streaming), "header"); + config_item_put(streaming); + if (!header) + goto err_config; - h = config_group_find_item(to_config_group(header), "h"); - config_item_put(header); - if (!h) - goto err_config; + h = config_group_find_item(to_config_group(header), "h"); + config_item_put(header); + if (!h) + goto err_config; - uvc->header = to_uvcg_streaming_header(h); - if (!uvc->header->linked) { - mutex_unlock(&opts->lock); - kfree(uvc); - return ERR_PTR(-EBUSY); + uvc->header = to_uvcg_streaming_header(h); + if (!uvc->header->linked) { + mutex_unlock(&opts->lock); + kfree(uvc); + return ERR_PTR(-EBUSY); + } } uvc->desc.extension_units = &opts->extension_units; diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 1ce58f61253c..3ac392cbb779 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -98,6 +98,12 @@ struct f_uvc_opts { */ struct mutex lock; int refcnt; + + /* + * Only for legacy gadget. Shall be NULL for configfs-composed gadgets, + * which is guaranteed by alloc_inst implementation of f_uvc doing kzalloc. + */ + struct uvcg_streaming_header *header; }; #endif /* U_UVC_H */ diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c index c06dd1af7a0c..c395438d3978 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -12,6 +12,7 @@ #include #include "u_uvc.h" +#include "uvc_configfs.h" USB_GADGET_COMPOSITE_OPTIONS(); @@ -84,8 +85,6 @@ static struct usb_device_descriptor webcam_device_descriptor = { .bNumConfigurations = 0, /* dynamic */ }; -DECLARE_UVC_HEADER_DESCRIPTOR(1); - static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { .bLength = UVC_DT_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, @@ -158,72 +157,179 @@ static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = { .bmaControls[1][0] = 4, }; -static const struct uvc_format_uncompressed uvc_format_yuv = { - .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, - .bFormatIndex = 1, - .bNumFrameDescriptors = 2, - .guidFormat = - { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}, - .bBitsPerPixel = 16, - .bDefaultFrameIndex = 1, - .bAspectRatioX = 0, - .bAspectRatioY = 0, - .bmInterlaceFlags = 0, - .bCopyProtect = 0, +static const struct uvcg_color_matching uvcg_color_matching = { + .desc = { + .bLength = UVC_DT_COLOR_MATCHING_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_COLORFORMAT, + .bColorPrimaries = 1, + .bTransferCharacteristics = 1, + .bMatrixCoefficients = 4, + }, +}; + +static struct uvcg_uncompressed uvcg_format_yuv = { + .fmt = { + .type = UVCG_UNCOMPRESSED, + /* add to .frames and fill .num_frames at runtime */ + .color_matching = (struct uvcg_color_matching *)&uvcg_color_matching, + }, + .desc = { + .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, + .bFormatIndex = 1, + .bNumFrameDescriptors = 2, + .guidFormat = { + 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }, + .bBitsPerPixel = 16, + .bDefaultFrameIndex = 1, + .bAspectRatioX = 0, + .bAspectRatioY = 0, + .bmInterlaceFlags = 0, + .bCopyProtect = 0, + }, +}; + +static struct uvcg_format_ptr uvcg_format_ptr_yuv = { + .fmt = &uvcg_format_yuv.fmt, }; DECLARE_UVC_FRAME_UNCOMPRESSED(1); DECLARE_UVC_FRAME_UNCOMPRESSED(3); +#define UVCG_WIDTH_360P 640 +#define UVCG_HEIGHT_360P 360 +#define UVCG_MIN_BITRATE_360P 18432000 +#define UVCG_MAX_BITRATE_360P 55296000 +#define UVCG_MAX_VIDEO_FB_SZ_360P 460800 +#define UVCG_FRM_INTERV_0_360P 666666 +#define UVCG_FRM_INTERV_1_360P 1000000 +#define UVCG_FRM_INTERV_2_360P 5000000 +#define UVCG_DEFAULT_FRM_INTERV_360P UVCG_FRM_INTERV_0_360P + static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = { .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, .bFrameIndex = 1, .bmCapabilities = 0, - .wWidth = cpu_to_le16(640), - .wHeight = cpu_to_le16(360), - .dwMinBitRate = cpu_to_le32(18432000), - .dwMaxBitRate = cpu_to_le32(55296000), - .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), - .dwDefaultFrameInterval = cpu_to_le32(666666), + .wWidth = cpu_to_le16(UVCG_WIDTH_360P), + .wHeight = cpu_to_le16(UVCG_HEIGHT_360P), + .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P), + .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P), + .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P), + .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P), .bFrameIntervalType = 3, - .dwFrameInterval[0] = cpu_to_le32(666666), - .dwFrameInterval[1] = cpu_to_le32(1000000), - .dwFrameInterval[2] = cpu_to_le32(5000000), + .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P), + .dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P), + .dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P), }; +static u32 uvcg_frame_yuv_360p_dw_frame_interval[] = { + [0] = UVCG_FRM_INTERV_0_360P, + [1] = UVCG_FRM_INTERV_1_360P, + [2] = UVCG_FRM_INTERV_2_360P, +}; + +static const struct uvcg_frame uvcg_frame_yuv_360p = { + .fmt_type = UVCG_UNCOMPRESSED, + .frame = { + .b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), + .b_descriptor_type = USB_DT_CS_INTERFACE, + .b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED, + .b_frame_index = 1, + .bm_capabilities = 0, + .w_width = UVCG_WIDTH_360P, + .w_height = UVCG_HEIGHT_360P, + .dw_min_bit_rate = UVCG_MIN_BITRATE_360P, + .dw_max_bit_rate = UVCG_MAX_BITRATE_360P, + .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P, + .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P, + .b_frame_interval_type = 3, + }, + .dw_frame_interval = uvcg_frame_yuv_360p_dw_frame_interval, +}; + +static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_360p = { + .frm = (struct uvcg_frame *)&uvcg_frame_yuv_360p, +}; +#define UVCG_WIDTH_720P 1280 +#define UVCG_HEIGHT_720P 720 +#define UVCG_MIN_BITRATE_720P 29491200 +#define UVCG_MAX_BITRATE_720P 29491200 +#define UVCG_MAX_VIDEO_FB_SZ_720P 1843200 +#define UVCG_FRM_INTERV_0_720P 5000000 +#define UVCG_DEFAULT_FRM_INTERV_720P UVCG_FRM_INTERV_0_720P + static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, .bFrameIndex = 2, .bmCapabilities = 0, - .wWidth = cpu_to_le16(1280), - .wHeight = cpu_to_le16(720), - .dwMinBitRate = cpu_to_le32(29491200), - .dwMaxBitRate = cpu_to_le32(29491200), - .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), - .dwDefaultFrameInterval = cpu_to_le32(5000000), + .wWidth = cpu_to_le16(UVCG_WIDTH_720P), + .wHeight = cpu_to_le16(UVCG_HEIGHT_720P), + .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P), + .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P), + .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P), + .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P), .bFrameIntervalType = 1, - .dwFrameInterval[0] = cpu_to_le32(5000000), + .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P), }; -static const struct uvc_format_mjpeg uvc_format_mjpg = { - .bLength = UVC_DT_FORMAT_MJPEG_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, - .bFormatIndex = 2, - .bNumFrameDescriptors = 2, - .bmFlags = 0, - .bDefaultFrameIndex = 1, - .bAspectRatioX = 0, - .bAspectRatioY = 0, - .bmInterlaceFlags = 0, - .bCopyProtect = 0, +static u32 uvcg_frame_yuv_720p_dw_frame_interval[] = { + [0] = UVCG_FRM_INTERV_0_720P, +}; + +static const struct uvcg_frame uvcg_frame_yuv_720p = { + .fmt_type = UVCG_UNCOMPRESSED, + .frame = { + .b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), + .b_descriptor_type = USB_DT_CS_INTERFACE, + .b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED, + .b_frame_index = 2, + .bm_capabilities = 0, + .w_width = UVCG_WIDTH_720P, + .w_height = UVCG_HEIGHT_720P, + .dw_min_bit_rate = UVCG_MIN_BITRATE_720P, + .dw_max_bit_rate = UVCG_MAX_BITRATE_720P, + .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P, + .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P, + .b_frame_interval_type = 1, + }, + .dw_frame_interval = uvcg_frame_yuv_720p_dw_frame_interval, +}; + +static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_720p = { + .frm = (struct uvcg_frame *)&uvcg_frame_yuv_720p, +}; + +static struct uvcg_mjpeg uvcg_format_mjpeg = { + .fmt = { + .type = UVCG_MJPEG, + /* add to .frames and fill .num_frames at runtime */ + .color_matching = (struct uvcg_color_matching *)&uvcg_color_matching, + }, + .desc = { + .bLength = UVC_DT_FORMAT_MJPEG_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, + .bFormatIndex = 2, + .bNumFrameDescriptors = 2, + .bmFlags = 0, + .bDefaultFrameIndex = 1, + .bAspectRatioX = 0, + .bAspectRatioY = 0, + .bmInterlaceFlags = 0, + .bCopyProtect = 0, + }, +}; + +static struct uvcg_format_ptr uvcg_format_ptr_mjpeg = { + .fmt = &uvcg_format_mjpeg.fmt, }; DECLARE_UVC_FRAME_MJPEG(1); @@ -235,16 +341,45 @@ static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = { .bDescriptorSubType = UVC_VS_FRAME_MJPEG, .bFrameIndex = 1, .bmCapabilities = 0, - .wWidth = cpu_to_le16(640), - .wHeight = cpu_to_le16(360), - .dwMinBitRate = cpu_to_le32(18432000), - .dwMaxBitRate = cpu_to_le32(55296000), - .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), - .dwDefaultFrameInterval = cpu_to_le32(666666), + .wWidth = cpu_to_le16(UVCG_WIDTH_360P), + .wHeight = cpu_to_le16(UVCG_HEIGHT_360P), + .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P), + .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P), + .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P), + .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P), .bFrameIntervalType = 3, - .dwFrameInterval[0] = cpu_to_le32(666666), - .dwFrameInterval[1] = cpu_to_le32(1000000), - .dwFrameInterval[2] = cpu_to_le32(5000000), + .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P), + .dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P), + .dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P), +}; + +static u32 uvcg_frame_mjpeg_360p_dw_frame_interval[] = { + [0] = UVCG_FRM_INTERV_0_360P, + [1] = UVCG_FRM_INTERV_1_360P, + [2] = UVCG_FRM_INTERV_2_360P, +}; + +static const struct uvcg_frame uvcg_frame_mjpeg_360p = { + .fmt_type = UVCG_MJPEG, + .frame = { + .b_length = UVC_DT_FRAME_MJPEG_SIZE(3), + .b_descriptor_type = USB_DT_CS_INTERFACE, + .b_descriptor_subtype = UVC_VS_FRAME_MJPEG, + .b_frame_index = 1, + .bm_capabilities = 0, + .w_width = UVCG_WIDTH_360P, + .w_height = UVCG_HEIGHT_360P, + .dw_min_bit_rate = UVCG_MIN_BITRATE_360P, + .dw_max_bit_rate = UVCG_MAX_BITRATE_360P, + .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P, + .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P, + .b_frame_interval_type = 3, + }, + .dw_frame_interval = uvcg_frame_mjpeg_360p_dw_frame_interval, +}; + +static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_360p = { + .frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_360p, }; static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { @@ -253,23 +388,44 @@ static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { .bDescriptorSubType = UVC_VS_FRAME_MJPEG, .bFrameIndex = 2, .bmCapabilities = 0, - .wWidth = cpu_to_le16(1280), - .wHeight = cpu_to_le16(720), - .dwMinBitRate = cpu_to_le32(29491200), - .dwMaxBitRate = cpu_to_le32(29491200), - .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), - .dwDefaultFrameInterval = cpu_to_le32(5000000), + .wWidth = cpu_to_le16(UVCG_WIDTH_720P), + .wHeight = cpu_to_le16(UVCG_HEIGHT_720P), + .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P), + .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P), + .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P), + .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P), .bFrameIntervalType = 1, - .dwFrameInterval[0] = cpu_to_le32(5000000), + .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P), }; -static const struct uvc_color_matching_descriptor uvc_color_matching = { - .bLength = UVC_DT_COLOR_MATCHING_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_VS_COLORFORMAT, - .bColorPrimaries = 1, - .bTransferCharacteristics = 1, - .bMatrixCoefficients = 4, +static u32 uvcg_frame_mjpeg_720p_dw_frame_interval[] = { + [0] = UVCG_FRM_INTERV_0_720P, +}; + +static const struct uvcg_frame uvcg_frame_mjpeg_720p = { + .fmt_type = UVCG_MJPEG, + .frame = { + .b_length = UVC_DT_FRAME_MJPEG_SIZE(1), + .b_descriptor_type = USB_DT_CS_INTERFACE, + .b_descriptor_subtype = UVC_VS_FRAME_MJPEG, + .b_frame_index = 2, + .bm_capabilities = 0, + .w_width = UVCG_WIDTH_720P, + .w_height = UVCG_HEIGHT_720P, + .dw_min_bit_rate = UVCG_MIN_BITRATE_720P, + .dw_max_bit_rate = UVCG_MAX_BITRATE_720P, + .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P, + .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P, + .b_frame_interval_type = 1, + }, + .dw_frame_interval = uvcg_frame_mjpeg_720p_dw_frame_interval, +}; + +static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_720p = { + .frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_720p, +}; + +static struct uvcg_streaming_header uvcg_streaming_header = { }; static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = { @@ -290,40 +446,40 @@ static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = { static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = { (const struct uvc_descriptor_header *) &uvc_input_header, - (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc, (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, - (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, + (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, NULL, }; static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { (const struct uvc_descriptor_header *) &uvc_input_header, - (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc, (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, - (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, + (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, NULL, }; static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { (const struct uvc_descriptor_header *) &uvc_input_header, - (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc, (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, - (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, + (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, NULL, }; @@ -387,6 +543,23 @@ webcam_bind(struct usb_composite_dev *cdev) uvc_opts->hs_streaming = uvc_hs_streaming_cls; uvc_opts->ss_streaming = uvc_ss_streaming_cls; + INIT_LIST_HEAD(&uvcg_format_yuv.fmt.frames); + list_add_tail(&uvcg_frame_ptr_yuv_360p.entry, &uvcg_format_yuv.fmt.frames); + list_add_tail(&uvcg_frame_ptr_yuv_720p.entry, &uvcg_format_yuv.fmt.frames); + uvcg_format_yuv.fmt.num_frames = 2; + + INIT_LIST_HEAD(&uvcg_format_mjpeg.fmt.frames); + list_add_tail(&uvcg_frame_ptr_mjpeg_360p.entry, &uvcg_format_mjpeg.fmt.frames); + list_add_tail(&uvcg_frame_ptr_mjpeg_720p.entry, &uvcg_format_mjpeg.fmt.frames); + uvcg_format_mjpeg.fmt.num_frames = 2; + + INIT_LIST_HEAD(&uvcg_streaming_header.formats); + list_add_tail(&uvcg_format_ptr_yuv.entry, &uvcg_streaming_header.formats); + list_add_tail(&uvcg_format_ptr_mjpeg.entry, &uvcg_streaming_header.formats); + uvcg_streaming_header.num_fmt = 2; + + uvc_opts->header = &uvcg_streaming_header; + /* Allocate string descriptor numbers ... note that string contents * can be overridden by the composite_dev glue. */ From c084af69a8f425b8cb56479ff0ea11e48aba0c62 Mon Sep 17 00:00:00 2001 From: Himanshu Bhavani Date: Fri, 15 Dec 2023 20:04:57 +0530 Subject: [PATCH 109/169] usb: dwc3: imx8mp: Fix smatch warning dwc3_imx8mp_pm_resume() warn: 'dwc3_imx->suspend_clk' from clk_prepare_enable() not released Signed-off-by: Himanshu Bhavani Link: https://lore.kernel.org/r/20231215143458.158810-1-himanshu.bhavani@siliconsignals.io Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-imx8mp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index a1e15f2fffdb..8ee448068503 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -363,8 +363,10 @@ static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev) } ret = clk_prepare_enable(dwc3_imx->hsio_clk); - if (ret) + if (ret) { + clk_disable_unprepare(dwc3_imx->suspend_clk); return ret; + } ret = dwc3_imx8mp_resume(dwc3_imx, PMSG_RESUME); From 80602b6b5a23b210224a5b44d9e5b5c1dc193632 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 15 Dec 2023 14:57:07 +0200 Subject: [PATCH 110/169] xhci: Fix null pointer dereference during S4 resume when resetting ep0 During device enumeration usb core resets endpoint 0 if the max packet size value differs from the one read from the device descriptor. usb core will additionally reset endpoint 0 during S4 resume, before re-enumerating the device, if the device has a reset-resume flag set. In this case the xhci device representation vdev may be lost due to xHC restore error and re-initialization during S4 resume. Make sure slot_id and vdev are valid before trying to re-configure max packet size during endpoint 0 reset. max packet size will be re-configured later during re-enumeration. This fixes commit e34900f46cd6 ("xhci: Reconfigure endpoint 0 max packet size only during endpoint reset") which is currently in usb-next, on its way to 6.8 Fixes: e34900f46cd6 ("xhci: Reconfigure endpoint 0 max packet size only during endpoint reset") Tested-by: Wendy Wang Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231215125707.1732989-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 08fbabff23a0..7d5b94905b9c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3077,6 +3077,9 @@ done: * of an endpoint that isn't in the halted state this function will issue a * configure endpoint command with the Drop and Add bits set for the target * endpoint. Refer to the additional note in xhci spcification section 4.6.8. + * + * vdev may be lost due to xHC restore error and re-initialization during S3/S4 + * resume. A new vdev will be allocated later by xhci_discover_or_reset_device() */ static void xhci_endpoint_reset(struct usb_hcd *hcd, @@ -3102,9 +3105,17 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, * mismatch. Reconfigure the xhci ep0 endpoint context here in that case */ if (usb_endpoint_xfer_control(&host_ep->desc) && ep_index == 0) { + udev = container_of(host_ep, struct usb_device, ep0); - if (udev->speed == USB_SPEED_FULL) - xhci_check_ep0_maxpacket(xhci, xhci->devs[udev->slot_id]); + if (udev->speed != USB_SPEED_FULL || !udev->slot_id) + return; + + vdev = xhci->devs[udev->slot_id]; + if (!vdev || vdev->udev != udev) + return; + + xhci_check_ep0_maxpacket(xhci, vdev); + /* Nothing else should be done here for ep0 during ep reset */ return; } @@ -3114,11 +3125,6 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, udev = (struct usb_device *) host_ep->hcpriv; vdev = xhci->devs[udev->slot_id]; - /* - * vdev may be lost due to xHC restore error and re-initialization - * during S3/S4 resume. A new vdev will be allocated later by - * xhci_discover_or_reset_device() - */ if (!udev->slot_id || !vdev) return; From 76cd718a9ffda837d4b4f1558c6e6ea08a9a7c59 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 15 Dec 2023 19:30:04 +0200 Subject: [PATCH 111/169] dt-bindings: connector: usb: add accessory mode description Add description of the USB-C Accessory Modes supported on the particular USB-C connector. This is required for devices like Qualcomm SM8150-HDK, which have no other way to express accessory modes supported by the hardware platform. Reviewed-by: Rob Herring Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20231215173005.313422-2-dmitry.baryshkov@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/connector/usb-connector.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index 28660b931d0c..f5966b3a2d9a 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -171,6 +171,18 @@ properties: offer the power, Capability Mismatch is set. Required for power sink and power dual role. + accessory-mode-audio: + type: boolean + description: Whether the device supports Audio Adapter Accessory Mode. This + is only necessary if there are no other means to discover supported + alternative modes (e.g. through the UCSI firmware interface). + + accessory-mode-debug: + type: boolean + description: Whether the device supports Debug Accessory Mode. This + is only necessary if there are no other means to discover supported + alternative modes (e.g. through the UCSI firmware interface). + altmodes: type: object description: List of Alternative Modes supported by the schematics on the From e9158c7e55339737847cebbfa397c668713f1a15 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 15 Dec 2023 19:30:05 +0200 Subject: [PATCH 112/169] usb: typec: tcpm: Parse Accessory Mode information Some of the boards supported by the TCPM drivers can support USB-C Accessory Modes (Analog Audio, Debug). Parse information about supported modes from the device tree. Reviewed-by: Heikki Krogerus Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20231215173005.313422-3-dmitry.baryshkov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 8372f98de757..cf70f1cf2f61 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -6144,6 +6144,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, { const char *opmode_str; int ret; + int mode; u32 mw, frs_current; if (!fwnode) @@ -6162,6 +6163,14 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, if (ret < 0) return ret; + mode = 0; + + if (fwnode_property_read_bool(fwnode, "accessory-mode-audio")) + port->typec_caps.accessory[mode++] = TYPEC_ACCESSORY_AUDIO; + + if (fwnode_property_read_bool(fwnode, "accessory-mode-debug")) + port->typec_caps.accessory[mode++] = TYPEC_ACCESSORY_DEBUG; + port->port_type = port->typec_caps.type; port->pd_supported = !fwnode_property_read_bool(fwnode, "pd-disable"); From 8be0c877fb3b671dac0cf56d1f1f9e65f9a9fb81 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 19 Dec 2023 16:43:05 +0100 Subject: [PATCH 113/169] thunderbolt: make tb_bus_type const Now that the driver core can properly handle constant struct bus_type, move the tb_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Andreas Noever Cc: Michael Jamet Cc: Yehezkel Bernat Cc: Acked-by: Mika Westerberg Link: https://lore.kernel.org/r/2023121904-utopia-broadcast-06d1@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/domain.c | 2 +- include/linux/thunderbolt.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index ec7b5f65804e..9fb1a64f3300 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -307,7 +307,7 @@ static const struct attribute_group *domain_attr_groups[] = { NULL, }; -struct bus_type tb_bus_type = { +const struct bus_type tb_bus_type = { .name = "thunderbolt", .match = tb_service_match, .probe = tb_service_probe, diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 6151c210d987..2c835e5c41f6 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -86,7 +86,7 @@ struct tb { unsigned long privdata[]; }; -extern struct bus_type tb_bus_type; +extern const struct bus_type tb_bus_type; extern struct device_type tb_service_type; extern struct device_type tb_xdomain_type; From ba2a2a86de04e67bb1d7f8251894eb11eed062e9 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Mon, 4 Dec 2023 15:14:58 +0200 Subject: [PATCH 114/169] thunderbolt: Keep link as asymmetric if preferred by hardware In case of the link is brought up as asymmetric (due to hardware preference), we honor that and don't transition it to symmetric, unless a router with symmetric link got plugged below, in the topology (and a bandwidth allows transition to symmetric). Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 13 +++++++++++++ drivers/thunderbolt/tb.c | 28 +++++++++++++++++++++------- drivers/thunderbolt/tb.h | 2 ++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 7873b173a351..e5a622b9acc3 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2773,6 +2773,19 @@ static void tb_switch_link_init(struct tb_switch *sw) if (down->dual_link_port) down->dual_link_port->bonded = bonded; tb_port_update_credits(down); + + if (tb_port_get_link_generation(up) < 4) + return; + + /* + * Set the Gen 4 preferred link width. This is what the router + * prefers when the link is brought up. If the router does not + * support asymmetric link configuration, this also will be set + * to TB_LINK_WIDTH_DUAL. + */ + sw->preferred_link_width = sw->link_width; + tb_sw_dbg(sw, "preferred link width %s\n", + tb_width_name(sw->preferred_link_width)); } /** diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index bd211e97ee4b..825cc2f4d8ef 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1173,14 +1173,15 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, * @dst_port: Destination adapter * @requested_up: New lower bandwidth request upstream (Mb/s) * @requested_down: New lower bandwidth request downstream (Mb/s) + * @keep_asym: Keep asymmetric link if preferred * * Goes over each link from @src_port to @dst_port and tries to * transition the link to symmetric if the currently consumed bandwidth - * allows. + * allows and link asymmetric preference is ignored (if @keep_asym is %false). */ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port, struct tb_port *dst_port, int requested_up, - int requested_down) + int requested_down, bool keep_asym) { bool clx = false, clx_disabled = false, downstream; struct tb_switch *sw; @@ -1229,6 +1230,19 @@ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port, if (up->sw->link_width == TB_LINK_WIDTH_DUAL) continue; + /* + * Here consumed < threshold so we can transition the + * link to symmetric. + * + * However, if the router prefers asymmetric link we + * honor that (unless @keep_asym is %false). + */ + if (keep_asym && + up->sw->preferred_link_width > TB_LINK_WIDTH_DUAL) { + tb_sw_dbg(up->sw, "keeping preferred asymmetric link\n"); + continue; + } + /* Disable CL states before doing any transitions */ if (!clx_disabled) { clx = tb_disable_clx(sw); @@ -1282,7 +1296,7 @@ static void tb_configure_link(struct tb_port *down, struct tb_port *up, struct tb_port *host_port; host_port = tb_port_at(tb_route(sw), tb->root_switch); - tb_configure_sym(tb, host_port, up, 0, 0); + tb_configure_sym(tb, host_port, up, 0, 0, false); } /* Set the link configured */ @@ -1467,7 +1481,7 @@ static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel) * If bandwidth on a link is < asym_threshold * transition the link to symmetric. */ - tb_configure_sym(tb, src_port, dst_port, 0, 0); + tb_configure_sym(tb, src_port, dst_port, 0, 0, true); /* Now we can allow the domain to runtime suspend again */ pm_runtime_mark_last_busy(&dst_port->sw->dev); pm_runtime_put_autosuspend(&dst_port->sw->dev); @@ -2289,7 +2303,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up, * If bandwidth on a link is < asym_threshold transition * the link to symmetric. */ - tb_configure_sym(tb, in, out, *requested_up, *requested_down); + tb_configure_sym(tb, in, out, *requested_up, *requested_down, true); /* * If requested bandwidth is less or equal than what is * currently allocated to that tunnel we simply change @@ -2332,7 +2346,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up, ret = tb_configure_asym(tb, in, out, *requested_up, *requested_down); if (ret) { - tb_configure_sym(tb, in, out, 0, 0); + tb_configure_sym(tb, in, out, 0, 0, true); return ret; } @@ -2340,7 +2354,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up, requested_down); if (ret) { tb_tunnel_warn(tunnel, "failed to allocate bandwidth\n"); - tb_configure_sym(tb, in, out, 0, 0); + tb_configure_sym(tb, in, out, 0, 0, true); } } else { ret = -ENOBUFS; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 1760c21e5b12..997c5a536905 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -125,6 +125,7 @@ struct tb_switch_tmu { * @device_name: Name of the device (or %NULL if not known) * @link_speed: Speed of the link in Gb/s * @link_width: Width of the upstream facing link + * @preferred_link_width: Router preferred link width (only set for Gen 4 links) * @link_usb4: Upstream link is USB4 * @generation: Switch Thunderbolt generation * @cap_plug_events: Offset to the plug events capability (%0 if not found) @@ -178,6 +179,7 @@ struct tb_switch { const char *device_name; unsigned int link_speed; enum tb_link_width link_width; + enum tb_link_width preferred_link_width; bool link_usb4; unsigned int generation; int cap_plug_events; From 04b99eac389adc6485f7913d83ec9ed68bbc8326 Mon Sep 17 00:00:00 2001 From: Werner Sembach Date: Wed, 20 Dec 2023 16:09:56 +0100 Subject: [PATCH 115/169] thunderbolt: Reduce retry timeout to speed up boot for some devices This is a followup to "thunderbolt: Workaround an IOMMU fault on certain systems with Intel Maple Ridge". It seems like the timeout can be reduced to 250ms. This reduces the overall delay caused by the retires to ~1s. This is about the time other things being initialized in parallel need anyway*, so like this the effective boot time is no longer compromised. *I only had a single device available for my measurements: A Clevo X170KM-G desktop replacement notebook. Signed-off-by: Werner Sembach Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index d8b9c734abd3..56790d50f9e3 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -1020,7 +1020,7 @@ icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, memset(&reply, 0, sizeof(reply)); ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), - 1, 10, 2000); + 1, 10, 250); if (ret) return ret; From 1760bfa7d7ca490cf8a61fe50ddeb1769cadd89e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 22 Dec 2023 21:06:36 -0800 Subject: [PATCH 116/169] usb: linux/usb.h: fix Excess kernel-doc description warning Remove the @removable: line to prevent the kernel-doc warning: include/linux/usb.h:732: warning: Excess struct member 'removable' description in 'usb_device' Signed-off-by: Randy Dunlap Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Link: https://lore.kernel.org/r/20231223050636.14022-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/usb.h b/include/linux/usb.h index 618e5a0b1a22..07556341ba2b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -632,7 +632,6 @@ struct usb3_lpm_parameters { * @reset_resume: needs reset instead of resume * @port_is_suspended: the upstream port is suspended (L2 or U3) * @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. * @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout. * @u2_params: exit latencies for USB3 U2 LPM state, and hub-initiated timeout. From 9c6b789e954fae73c548f39332bcc56bdf0d4373 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 2 Jan 2024 11:11:41 +0200 Subject: [PATCH 117/169] Revert "usb: typec: class: fix typec_altmode_put_partner to put plugs" This reverts commit b17b7fe6dd5c6ff74b38b0758ca799cdbb79e26e. That commit messed up the reference counting, so it needs to be rethought. Fixes: b17b7fe6dd5c ("usb: typec: class: fix typec_altmode_put_partner to put plugs") Cc: Cc: RD Babiera Reported-by: Chris Bainbridge Closes: https://lore.kernel.org/lkml/CAP-bSRb3SXpgo_BEdqZB-p1K5625fMegRZ17ZkPE1J8ZYgEHDg@mail.gmail.com/ Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240102091142.2136472-1-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index aeae8009b9e3..4d11f2b536fa 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -267,7 +267,7 @@ static void typec_altmode_put_partner(struct altmode *altmode) if (!partner) return; - adev = &altmode->adev; + adev = &partner->adev; if (is_typec_plug(adev->dev.parent)) { struct typec_plug *plug = to_typec_plug(adev->dev.parent); @@ -497,8 +497,7 @@ static void typec_altmode_release(struct device *dev) { struct altmode *alt = to_altmode(to_typec_altmode(dev)); - if (!is_typec_port(dev->parent)) - typec_altmode_put_partner(alt); + typec_altmode_put_partner(alt); altmode_id_remove(alt->adev.dev.parent, alt->id); kfree(alt); From ca2dc35e555e7043de585f4e46123d8fbd2b5a21 Mon Sep 17 00:00:00 2001 From: William Wu Date: Tue, 26 Dec 2023 15:19:59 +0800 Subject: [PATCH 118/169] usb: dwc2: Disable clock gating feature on Rockchip SoCs The DWC2 IP on the Rockchip SoCs doesn't support clock gating. When a clock gating is enabled, system hangs. Signed-off-by: William Wu Acked-by: Minas Harutyunyan Link: https://lore.kernel.org/r/1703575199-23638-1-git-send-email-william.wu@rock-chips.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/params.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index fb03162ae9b7..eb677c3cfd0b 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -130,6 +130,7 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) p->lpm_clock_gating = false; p->besl = false; p->hird_threshold_en = false; + p->no_clock_gating = true; } static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg) From 7059fbebcb00554c3f31e5b5d93ef6d2d96dc7b4 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 22 Dec 2023 22:11:27 +0000 Subject: [PATCH 119/169] Revert "usb: dwc3: Soft reset phy on probe for host" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 8bea147dfdf823eaa8d3baeccc7aeb041b41944b. The phy soft reset GUSB2PHYCFG.PHYSOFTRST only applies to UTMI phy, not ULPI. This fix is incomplete. Cc: Fixes: 8bea147dfdf8 ("usb: dwc3: Soft reset phy on probe for host") Reported-by: Köry Maincent Closes: https://lore.kernel.org/linux-usb/20231205151959.5236c231@kmaincent-XPS-13-7390 Signed-off-by: Thinh Nguyen Link: https://lore.kernel.org/r/29a26593a60eba727de872a3e580a674807b3339.1703282469.git.Thinh.Nguyen@synopsys.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 39 +-------------------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index b101dbf8c5dc..832c41fec4f7 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -279,46 +279,9 @@ int dwc3_core_soft_reset(struct dwc3 *dwc) * XHCI driver will reset the host block. If dwc3 was configured for * host-only mode or current role is host, then we can return early. */ - if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) + if (dwc->dr_mode == USB_DR_MODE_HOST || dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) return 0; - /* - * If the dr_mode is host and the dwc->current_dr_role is not the - * corresponding DWC3_GCTL_PRTCAP_HOST, then the dwc3_core_init_mode - * isn't executed yet. Ensure the phy is ready before the controller - * updates the GCTL.PRTCAPDIR or other settings by soft-resetting - * the phy. - * - * Note: GUSB3PIPECTL[n] and GUSB2PHYCFG[n] are port settings where n - * is port index. If this is a multiport host, then we need to reset - * all active ports. - */ - if (dwc->dr_mode == USB_DR_MODE_HOST) { - u32 usb3_port; - u32 usb2_port; - - usb3_port = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - usb3_port |= DWC3_GUSB3PIPECTL_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port); - - usb2_port = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - usb2_port |= DWC3_GUSB2PHYCFG_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port); - - /* Small delay for phy reset assertion */ - usleep_range(1000, 2000); - - usb3_port &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port); - - usb2_port &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port); - - /* Wait for clock synchronization */ - msleep(50); - return 0; - } - reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg |= DWC3_DCTL_CSFTRST; reg &= ~DWC3_DCTL_RUN_STOP; From afe28cd686aeb77e8d9140d50fb1cf06a7ecb731 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 22 Dec 2023 22:11:33 +0000 Subject: [PATCH 120/169] Revert "usb: dwc3: don't reset device side if dwc3 was configured as host-only" This reverts commit e835c0a4e23c38531dcee5ef77e8d1cf462658c7. Don't omit soft-reset. During initialization, the driver may need to perform a soft reset to ensure the phy is ready when the controller updates the GCTL.PRTCAPDIR or other settings by issuing phy soft-reset. Many platforms often have access to DCTL register for soft-reset despite being host-only. If there are actual reported issues from the platforms that don't expose DCTL registers, then we will need to revisit (perhaps to teach dwc3 to perform xhci's soft-reset USBCMD.HCRST). Cc: Fixes: e835c0a4e23c ("usb: dwc3: don't reset device side if dwc3 was configured as host-only") Signed-off-by: Thinh Nguyen Link: https://lore.kernel.org/r/7668ab11a48f260820825274976eb41fec7f54d1.1703282469.git.Thinh.Nguyen@synopsys.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 832c41fec4f7..f50b5575d588 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -277,9 +277,9 @@ int dwc3_core_soft_reset(struct dwc3 *dwc) /* * We're resetting only the device side because, if we're in host mode, * XHCI driver will reset the host block. If dwc3 was configured for - * host-only mode or current role is host, then we can return early. + * host-only mode, then we can return early. */ - if (dwc->dr_mode == USB_DR_MODE_HOST || dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) return 0; reg = dwc3_readl(dwc->regs, DWC3_DCTL); From 961410c9e8534d3c6f347c04cacf50bc5c002a3f Mon Sep 17 00:00:00 2001 From: liyouhong Date: Thu, 21 Dec 2023 10:34:25 +0800 Subject: [PATCH 121/169] drivers/usb/gadget/udc: Fix spelling typo in comments(reqest->request) Fix spelling typo in comments. Reported-by: k2ci Signed-off-by: liyouhong Link: https://lore.kernel.org/r/20231221023425.1316397-1-liyouhong@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_udc_core.c | 2 +- drivers/usb/gadget/udc/mv_udc_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 535b6e79a198..e8042c158f6d 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1360,7 +1360,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, udc->ep0_dir = USB_DIR_IN; /* Borrow the per device status_req */ req = udc->status_req; - /* Fill in the reqest structure */ + /* Fill in the request structure */ *((u16 *) req->req.buf) = cpu_to_le16(tmp); req->ep = ep; diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index d888dcda2bc8..78308b64955d 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -1451,7 +1451,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) req = udc->status_req; - /* fill in the reqest structure */ + /* fill in the request structure */ if (empty == false) { *((u16 *) req->req.buf) = cpu_to_le16(status); req->req.length = 2; From d49f90822015ad9c8837717e6b5967c8748626df Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 14 Dec 2023 17:29:09 +0100 Subject: [PATCH 122/169] usb: typec: tipd: add init and reset functions to tipd_data The current implementation includes a number of special cases for the tps25750. Nevertheless, init and reset functions can be generalized by adding function pointers to the tipd_data structure in order to offer that functionality to other parts without additional conditional clauses. Some functionality like the cold reset request (GAID) is shared by the tps25750 and the tps6598x, so they can use the same reset function. Signed-off-by: Javier Carrasco Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20231207-tps6598x_update-v2-1-f3cfcde6d890@wolfvision.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 45 +++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 806ef68273ca..f0c4cd571a37 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -115,6 +115,8 @@ struct tipd_data { void (*trace_power_status)(u16 status); void (*trace_status)(u32 status); int (*apply_patch)(struct tps6598x *tps); + int (*init)(struct tps6598x *tps); + int (*reset)(struct tps6598x *tps); }; struct tps6598x { @@ -1106,6 +1108,11 @@ wait_for_app: return 0; }; +static int cd321x_init(struct tps6598x *tps) +{ + return 0; +} + static int tps25750_init(struct tps6598x *tps) { int ret; @@ -1124,6 +1131,21 @@ static int tps25750_init(struct tps6598x *tps) return 0; } +static int tps6598x_init(struct tps6598x *tps) +{ + return 0; +} + +static int cd321x_reset(struct tps6598x *tps) +{ + return 0; +} + +static int tps6598x_reset(struct tps6598x *tps) +{ + return tps6598x_exec_cmd_tmo(tps, "GAID", 0, NULL, 0, NULL, 2000, 0); +} + static int tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) { @@ -1187,7 +1209,6 @@ static int tps6598x_probe(struct i2c_client *client) u32 vid; int ret; u64 mask1; - bool is_tps25750; tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); if (!tps) @@ -1207,8 +1228,7 @@ static int tps6598x_probe(struct i2c_client *client) if (IS_ERR(tps->regmap)) return PTR_ERR(tps->regmap); - is_tps25750 = device_is_compatible(tps->dev, "ti,tps25750"); - if (!is_tps25750) { + if (!device_is_compatible(tps->dev, "ti,tps25750")) { ret = tps6598x_read32(tps, TPS_REG_VID, &vid); if (ret < 0 || !vid) return -ENODEV; @@ -1251,8 +1271,8 @@ static int tps6598x_probe(struct i2c_client *client) if (ret < 0) return ret; - if (is_tps25750 && ret == TPS_MODE_PTCH) { - ret = tps25750_init(tps); + if (ret == TPS_MODE_PTCH) { + ret = tps->data->init(tps); if (ret) return ret; } @@ -1340,8 +1360,8 @@ err_clear_mask: tps6598x_write64(tps, TPS_REG_INT_MASK1, 0); err_reset_controller: /* Reset PD controller to remove any applied patch */ - if (is_tps25750) - tps6598x_exec_cmd_tmo(tps, "GAID", 0, NULL, 0, NULL, 2000, 0); + tps->data->reset(tps); + return ret; } @@ -1358,8 +1378,7 @@ static void tps6598x_remove(struct i2c_client *client) usb_role_switch_put(tps->role_sw); /* Reset PD controller to remove any applied patch */ - if (device_is_compatible(tps->dev, "ti,tps25750")) - tps6598x_exec_cmd_tmo(tps, "GAID", 0, NULL, 0, NULL, 2000, 0); + tps->data->reset(tps); if (tps->reset) gpiod_set_value_cansleep(tps->reset, 1); @@ -1393,7 +1412,7 @@ static int __maybe_unused tps6598x_resume(struct device *dev) if (ret < 0) return ret; - if (device_is_compatible(tps->dev, "ti,tps25750") && ret == TPS_MODE_PTCH) { + if (ret == TPS_MODE_PTCH) { ret = tps25750_init(tps); if (ret) return ret; @@ -1423,6 +1442,8 @@ static const struct tipd_data cd321x_data = { .register_port = tps6598x_register_port, .trace_power_status = trace_tps6598x_power_status, .trace_status = trace_tps6598x_status, + .init = cd321x_init, + .reset = cd321x_reset, }; static const struct tipd_data tps6598x_data = { @@ -1430,6 +1451,8 @@ static const struct tipd_data tps6598x_data = { .register_port = tps6598x_register_port, .trace_power_status = trace_tps6598x_power_status, .trace_status = trace_tps6598x_status, + .init = tps6598x_init, + .reset = tps6598x_reset, }; static const struct tipd_data tps25750_data = { @@ -1438,6 +1461,8 @@ static const struct tipd_data tps25750_data = { .trace_power_status = trace_tps25750_power_status, .trace_status = trace_tps25750_status, .apply_patch = tps25750_apply_patch, + .init = tps25750_init, + .reset = tps6598x_reset, }; static const struct of_device_id tps6598x_of_match[] = { From 798531b85f08c1dd128b88acd1f98c6a6b30dffd Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 14 Dec 2023 17:29:10 +0100 Subject: [PATCH 123/169] usb: typec: tipd: add function to request firmware The firmware request process is device agnostic and can be used for other parts. Signed-off-by: Javier Carrasco Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20231207-tps6598x_update-v2-2-f3cfcde6d890@wolfvision.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index f0c4cd571a37..83e5eeecdf5c 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -873,6 +873,30 @@ tps6598x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) return 0; } +static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw) +{ + const char *firmware_name; + int ret; + + ret = device_property_read_string(tps->dev, "firmware-name", + &firmware_name); + if (ret) + return ret; + + ret = request_firmware(fw, firmware_name, tps->dev); + if (ret) { + dev_err(tps->dev, "failed to retrieve \"%s\"\n", firmware_name); + return ret; + } + + if ((*fw)->size == 0) { + release_firmware(*fw); + ret = -EINVAL; + } + + return ret; +} + static int tps25750_write_firmware(struct tps6598x *tps, u8 bpms_addr, const u8 *data, size_t len) @@ -961,16 +985,9 @@ static int tps25750_start_patch_burst_mode(struct tps6598x *tps) if (ret) return ret; - ret = request_firmware(&fw, firmware_name, tps->dev); - if (ret) { - dev_err(tps->dev, "failed to retrieve \"%s\"\n", firmware_name); + ret = tps_request_firmware(tps, &fw); + if (ret) return ret; - } - - if (fw->size == 0) { - ret = -EINVAL; - goto release_fw; - } ret = of_property_match_string(np, "reg-names", "patch-address"); if (ret < 0) { From e79ead88eeb8f3e945355b537bfaca7532dfee10 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 14 Dec 2023 17:29:11 +0100 Subject: [PATCH 124/169] usb: typec: tipd: declare in_data in as const in exec_cmd functions The input data passed to execute commands with tps6598x_exec_cmd() is not supposed to be modified by the function. Moreover, this data is passed to tps6598x_exec_cmd_tmo() and finally to tps6598x_block_write(), which expects a const pointer. The current implementation does not produce any bugs, but it discards const qualifiers from the pointers passed as arguments. This leads to compile issues if 'discarded-qualifiers' is active and a const pointer is passed to the function, which is the case if data from a firmware structure is passed to execute update commands. Adding the const modifier to in_data prevents such issues and provides code consistency. Signed-off-by: Javier Carrasco Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20231207-tps6598x_update-v2-3-f3cfcde6d890@wolfvision.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 83e5eeecdf5c..7f4bbc0629b0 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -330,7 +330,7 @@ static void tps6598x_disconnect(struct tps6598x *tps, u32 status) } static int tps6598x_exec_cmd_tmo(struct tps6598x *tps, const char *cmd, - size_t in_len, u8 *in_data, + size_t in_len, const u8 *in_data, size_t out_len, u8 *out_data, u32 cmd_timeout_ms, u32 res_delay_ms) { @@ -396,7 +396,7 @@ static int tps6598x_exec_cmd_tmo(struct tps6598x *tps, const char *cmd, } static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, - size_t in_len, u8 *in_data, + size_t in_len, const u8 *in_data, size_t out_len, u8 *out_data) { return tps6598x_exec_cmd_tmo(tps, cmd, in_len, in_data, From 4c3ea81aa8e11400f24e5541bf46c2cadb4202e9 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 14 Dec 2023 17:29:12 +0100 Subject: [PATCH 125/169] usb: typec: tipd: add patch update support for tps6598x The TPS6598x PD controller supports firmware updates that can be loaded either from an external flash memory or a host using the device's I2C host interface. This patch implements the second approach, which is especially relevant if no flash memory is available. In order to make patch bundle updates, a series of tasks (special commands) must be sent to the device as it is documented in the TPS65987DDH and TPS65988DH Host Interface Technical Reference Manual[1], section 4.11 (Patch Bundle Update Tasks). The update sequence is as follows: 1. PTCs - Start Patch Load Sequence: the proposed approach includes device and application configuration data. 2. PTCd - Patch Download: 64-byte data chunks must be sent until the end of the firmware file is reached (the last chunk may be shorter). 3. PTCc - Patch Data Transfer Complete: ends the patch loading sequence. After this sequence and if no errors occurred, the device will change its mode to 'APP' after SETUP_MS milliseconds, and then it will be ready for normal operation. [1] https://www.ti.com/lit/ug/slvubh2b/slvubh2b.pdf?ts=1697623299919&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FTPS65987D Signed-off-by: Javier Carrasco Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20231207-tps6598x_update-v2-4-f3cfcde6d890@wolfvision.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 68 ++++++++++++++++++++++++++++++- drivers/usb/typec/tipd/tps6598x.h | 18 ++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 7f4bbc0629b0..a956eb976906 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1125,6 +1125,71 @@ wait_for_app: return 0; }; +static int tps6598x_apply_patch(struct tps6598x *tps) +{ + u8 in = TPS_PTCS_CONTENT_DEV | TPS_PTCS_CONTENT_APP; + u8 out[TPS_MAX_LEN] = {0}; + size_t in_len = sizeof(in); + size_t copied_bytes = 0; + size_t bytes_left; + const struct firmware *fw; + const char *firmware_name; + int ret; + + ret = device_property_read_string(tps->dev, "firmware-name", + &firmware_name); + if (ret) + return ret; + + ret = tps_request_firmware(tps, &fw); + if (ret) + return ret; + + ret = tps6598x_exec_cmd(tps, "PTCs", in_len, &in, + TPS_PTCS_OUT_BYTES, out); + if (ret || out[TPS_PTCS_STATUS] == TPS_PTCS_STATUS_FAIL) { + if (!ret) + ret = -EBUSY; + dev_err(tps->dev, "Update start failed (%d)\n", ret); + goto release_fw; + } + + bytes_left = fw->size; + while (bytes_left) { + if (bytes_left < TPS_MAX_LEN) + in_len = bytes_left; + else + in_len = TPS_MAX_LEN; + ret = tps6598x_exec_cmd(tps, "PTCd", in_len, + fw->data + copied_bytes, + TPS_PTCD_OUT_BYTES, out); + if (ret || out[TPS_PTCD_TRANSFER_STATUS] || + out[TPS_PTCD_LOADING_STATE] == TPS_PTCD_LOAD_ERR) { + if (!ret) + ret = -EBUSY; + dev_err(tps->dev, "Patch download failed (%d)\n", ret); + goto release_fw; + } + copied_bytes += in_len; + bytes_left -= in_len; + } + + ret = tps6598x_exec_cmd(tps, "PTCc", 0, NULL, TPS_PTCC_OUT_BYTES, out); + if (ret || out[TPS_PTCC_DEV] || out[TPS_PTCC_APP]) { + if (!ret) + ret = -EBUSY; + dev_err(tps->dev, "Update completion failed (%d)\n", ret); + goto release_fw; + } + msleep(TPS_SETUP_MS); + dev_info(tps->dev, "Firmware update succeeded\n"); + +release_fw: + release_firmware(fw); + + return ret; +}; + static int cd321x_init(struct tps6598x *tps) { return 0; @@ -1150,7 +1215,7 @@ static int tps25750_init(struct tps6598x *tps) static int tps6598x_init(struct tps6598x *tps) { - return 0; + return tps->data->apply_patch(tps); } static int cd321x_reset(struct tps6598x *tps) @@ -1468,6 +1533,7 @@ static const struct tipd_data tps6598x_data = { .register_port = tps6598x_register_port, .trace_power_status = trace_tps6598x_power_status, .trace_status = trace_tps6598x_status, + .apply_patch = tps6598x_apply_patch, .init = tps6598x_init, .reset = tps6598x_reset, }; diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index 01609bf509e4..89b24519463a 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -235,4 +235,22 @@ /* SLEEP CONF REG */ #define TPS_SLEEP_CONF_SLEEP_MODE_ALLOWED BIT(0) +/* Start Patch Download Sequence */ +#define TPS_PTCS_CONTENT_APP BIT(0) +#define TPS_PTCS_CONTENT_DEV BIT(1) +#define TPS_PTCS_OUT_BYTES 4 +#define TPS_PTCS_STATUS 1 + +#define TPS_PTCS_STATUS_FAIL 0x80 +/* Patch Download */ +#define TPS_PTCD_OUT_BYTES 10 +#define TPS_PTCD_TRANSFER_STATUS 1 +#define TPS_PTCD_LOADING_STATE 2 + +#define TPS_PTCD_LOAD_ERR 0x09 +/* Patch Download Complete */ +#define TPS_PTCC_OUT_BYTES 4 +#define TPS_PTCC_DEV 2 +#define TPS_PTCC_APP 3 + #endif /* __TPS6598X_H__ */ From e7d3b9f28654dbfce7e09f8028210489adaf6a33 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Mon, 18 Dec 2023 22:36:35 -0800 Subject: [PATCH 126/169] usb: yurex: Fix inconsistent locking bug in yurex_read() Unlock before returning on the error path. Fixes: 86b20af11e84 ("usb: yurex: Replace snprintf() with the safer scnprintf() variant") Reported-by: Dan Carpenter Reported-by: kernel test robot Closes: https://lore.kernel.org/r/202312170252.3udgrIcP-lkp@intel.com/ Signed-off-by: Harshit Mogalapalli Link: https://lore.kernel.org/r/20231219063639.450994-1-harshit.m.mogalapalli@oracle.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/yurex.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 5a13cddace0e..9a0649d23693 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -414,8 +414,10 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, return -ENODEV; } - if (WARN_ON_ONCE(dev->bbu > S64_MAX || dev->bbu < S64_MIN)) + if (WARN_ON_ONCE(dev->bbu > S64_MAX || dev->bbu < S64_MIN)) { + mutex_unlock(&dev->io_mutex); return -EIO; + } spin_lock_irqsave(&dev->lock, flags); scnprintf(in_buffer, MAX_S64_STRLEN, "%lld\n", dev->bbu); From 730e12fbec53ab59dd807d981a204258a4cfb29a Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Wed, 6 Dec 2023 12:18:14 -0800 Subject: [PATCH 127/169] usb: dwc3: gadget: Handle EP0 request dequeuing properly Current EP0 dequeue path will share the same as other EPs. However, there are some special considerations that need to be made for EP0 transfers: - EP0 transfers never transition into the started_list - EP0 only has one active request at a time In case there is a vendor specific control message for a function over USB FFS, then there is no guarantee on the timeline which the DATA/STATUS stage is responded to. While this occurs, any attempt to end transfers on non-control EPs will end up having the DWC3_EP_DELAY_STOP flag set, and defer issuing of the end transfer command. If the USB FFS application decides to timeout the control transfer, or if USB FFS AIO path exits, the USB FFS driver will issue a call to usb_ep_dequeue() for the ep0 request. In case of the AIO exit path, the AIO FS blocks until all pending USB requests utilizing the AIO path is completed. However, since the dequeue of ep0 req does not happen properly, all non-control EPs with the DWC3_EP_DELAY_STOP flag set will not be handled, and the AIO exit path will be stuck waiting for the USB FFS data endpoints to receive a completion callback. Fix is to utilize dwc3_ep0_reset_state() in the dequeue API to ensure EP0 is brought back to the SETUP state, and ensures that any deferred end transfer commands are handled. This also will end any active transfers on EP0, compared to the previous implementation which directly called giveback only. Fixes: fcd2def66392 ("usb: dwc3: gadget: Refactor dwc3_gadget_ep_dequeue") Cc: stable Signed-off-by: Wesley Cheng Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20231206201814.32664-1-quic_wcheng@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 858fe4c299b7..88d8d589f014 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2103,7 +2103,17 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, list_for_each_entry(r, &dep->pending_list, list) { if (r == req) { - dwc3_gadget_giveback(dep, req, -ECONNRESET); + /* + * Explicitly check for EP0/1 as dequeue for those + * EPs need to be handled differently. Control EP + * only deals with one USB req, and giveback will + * occur during dwc3_ep0_stall_and_restart(). EP0 + * requests are never added to started_list. + */ + if (dep->number > 1) + dwc3_gadget_giveback(dep, req, -ECONNRESET); + else + dwc3_ep0_reset_state(dwc); goto out; } } From 738ec5ab7acca7ee5856e36a5a6a0c41201fe88f Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Mon, 18 Dec 2023 15:47:30 +0800 Subject: [PATCH 128/169] usb: ueagle-atm: Use wait_event_freezable_timeout() in uea_wait() A freezable kernel thread can enter frozen state during freezing by either calling try_to_freeze() or using wait_event_freezable() and its variants. So for the following snippet of code in a kernel thread loop: wait_event_interruptible_timeout(); try_to_freeze(); We can change it to a simple wait_event_freezable_timeout() and then eliminate a function call. Signed-off-by: Kevin Hao Link: https://lore.kernel.org/r/20231218074730.1898699-1-haokexin@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 8d5580d8d20a..16703815be0c 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -546,7 +546,7 @@ MODULE_PARM_DESC(annex, #define uea_wait(sc, cond, timeo) \ ({ \ - int _r = wait_event_interruptible_timeout(sc->sync_q, \ + int _r = wait_event_freezable_timeout(sc->sync_q, \ (cond) || kthread_should_stop(), timeo); \ if (kthread_should_stop()) \ _r = -ENODEV; \ @@ -1896,7 +1896,6 @@ static int uea_kthread(void *data) ret = sc->stat(sc); if (ret != -EAGAIN) uea_wait(sc, 0, msecs_to_jiffies(1000)); - try_to_freeze(); } uea_leaves(INS_TO_USBDEV(sc)); return ret; From e9d40b215e38480fd94c66b06d79045717a59e9c Mon Sep 17 00:00:00 2001 From: Uttkarsh Aggarwal Date: Fri, 22 Dec 2023 15:17:04 +0530 Subject: [PATCH 129/169] usb: dwc: ep0: Update request status in dwc3_ep0_stall_restart Current implementation blocks the running operations when Plug-out and Plug-In is performed continuously, process gets stuck in dwc3_thread_interrupt(). Code Flow: CPU1 ->Gadget_start ->dwc3_interrupt ->dwc3_thread_interrupt ->dwc3_process_event_buf ->dwc3_process_event_entry ->dwc3_endpoint_interrupt ->dwc3_ep0_interrupt ->dwc3_ep0_inspect_setup ->dwc3_ep0_stall_and_restart By this time if pending_list is not empty, it will get the next request on the given list and calls dwc3_gadget_giveback which will unmap request and call its complete() callback to notify upper layers that it has completed. Currently dwc3_gadget_giveback status is set to -ECONNRESET, whereas it should be -ESHUTDOWN based on condition if not dwc->connected is true. Cc: Fixes: d742220b3577 ("usb: dwc3: ep0: giveback requests on stall_and_restart") Signed-off-by: Uttkarsh Aggarwal Link: https://lore.kernel.org/r/20231222094704.20276-1-quic_uaggarwa@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/ep0.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index b94243237293..6ae8a36f21cf 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -238,7 +238,10 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) struct dwc3_request *req; req = next_request(&dep->pending_list); - dwc3_gadget_giveback(dep, req, -ECONNRESET); + if (!dwc->connected) + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + else + dwc3_gadget_giveback(dep, req, -ECONNRESET); } dwc->eps[0]->trb_enqueue = 0; From 91736d0619eb4083f33ae737b9d9763fc6b196ed Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Tue, 19 Dec 2023 09:45:59 +0530 Subject: [PATCH 130/169] usb: dwc3: core: set force_gen1 bit in USB31 devices if max speed is SS Currently for dwc3_usb31 controller, if maximum_speed is limited to super-speed in DT, then device mode is limited to SS, but host mode still works in SSP. The documentation for max-speed property is as follows: "Tells USB controllers we want to work up to a certain speed. Incase this isn't passed via DT, USB controllers should default to their maximum HW capability." It doesn't specify that the property is only for device mode. There are cases where we need to limit the host's maximum speed to SuperSpeed only. Use this property for host mode to contrain host's speed to SuperSpeed. Signed-off-by: Krishna Kurapati Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20231219041559.15789-1-quic_kriskura@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 12 ++++++++++++ drivers/usb/dwc3/core.h | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f50b5575d588..4447ef2cbc0f 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1330,6 +1330,18 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_config_threshold(dwc); + /* + * Modify this for all supported Super Speed ports when + * multiport support is added. + */ + if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && + (DWC3_IP_IS(DWC31)) && + dwc->maximum_speed == USB_SPEED_SUPER) { + reg = dwc3_readl(dwc->regs, DWC3_LLUCTL); + reg |= DWC3_LLUCTL_FORCE_GEN1; + dwc3_writel(dwc->regs, DWC3_LLUCTL, reg); + } + return 0; err_power_off_phy: diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index efe6caf4d0e8..e120611a5174 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -172,6 +172,8 @@ #define DWC3_OEVTEN 0xcc0C #define DWC3_OSTS 0xcc10 +#define DWC3_LLUCTL 0xd024 + /* Bit fields */ /* Global SoC Bus Configuration INCRx Register 0 */ @@ -657,6 +659,9 @@ #define DWC3_OSTS_VBUSVLD BIT(1) #define DWC3_OSTS_CONIDSTS BIT(0) +/* Force Gen1 speed on Gen2 link */ +#define DWC3_LLUCTL_FORCE_GEN1 BIT(10) + /* Structures */ struct dwc3_trb; From 6d6887c42e946f43bed2e64571a40c8476a1e4a9 Mon Sep 17 00:00:00 2001 From: Yinbo Zhu Date: Thu, 28 Dec 2023 15:11:13 +0800 Subject: [PATCH 131/169] usb: xhci-plat: fix usb disconnect issue after s4 The xhci retaining bogus hardware states cause usb disconnect devices connected before hibernation(s4) and refer to the commit 'f3d478858be ("usb: ohci-platform: fix usb disconnect issue after s4")' which set flag "hibernated" as true when resume-from-hibernation and that the drivers will reset the hardware to get rid of any existing state and make sure resume from hibernation re-enumerates everything for xhci. Signed-off-by: Yinbo Zhu Link: https://lore.kernel.org/r/20231228071113.1719-1-zhuyinbo@loongson.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index e46175c38570..f04fde19f551 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -436,7 +436,7 @@ void xhci_plat_remove(struct platform_device *dev) } EXPORT_SYMBOL_GPL(xhci_plat_remove); -static int __maybe_unused xhci_plat_suspend(struct device *dev) +static int xhci_plat_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -464,7 +464,7 @@ static int __maybe_unused xhci_plat_suspend(struct device *dev) return 0; } -static int __maybe_unused xhci_plat_resume(struct device *dev) +static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -486,7 +486,7 @@ static int __maybe_unused xhci_plat_resume(struct device *dev) if (ret) goto disable_clks; - ret = xhci_resume(xhci, PMSG_RESUME); + ret = xhci_resume(xhci, pmsg); if (ret) goto disable_clks; @@ -505,6 +505,16 @@ disable_clks: return ret; } +static int xhci_plat_resume(struct device *dev) +{ + return xhci_plat_resume_common(dev, PMSG_RESUME); +} + +static int xhci_plat_restore(struct device *dev) +{ + return xhci_plat_resume_common(dev, PMSG_RESTORE); +} + static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -527,7 +537,12 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) } const struct dev_pm_ops xhci_plat_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume) + .suspend = pm_sleep_ptr(xhci_plat_suspend), + .resume = pm_sleep_ptr(xhci_plat_resume), + .freeze = pm_sleep_ptr(xhci_plat_suspend), + .thaw = pm_sleep_ptr(xhci_plat_resume), + .poweroff = pm_sleep_ptr(xhci_plat_suspend), + .restore = pm_sleep_ptr(xhci_plat_restore), SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume, From 501b15207138e3cf85c8413fbbb746f6ee21b0a1 Mon Sep 17 00:00:00 2001 From: Kyle Tso Date: Sat, 16 Dec 2023 18:46:29 +0800 Subject: [PATCH 132/169] dt-bindings: connector: Add child nodes for multiple PD capabilities Commit 662a60102c12 ("usb: typec: Separate USB Power Delivery from USB Type-C") allows userspace to configure the PD of a port by selecting different set of predefined PD capabilities. Define the PD capability sets in DT for better configurability in device modules. Define an optional child node "capabilities" to contain multiple USB Power Delivery capabilities. Define child nodes with pattern (e.g. caps-0, caps-1) under "capabilities". Each node contains PDO data of a selectable Power Delivery capability. Also define common properties for source-pdos, sink-pdos, and op-sink-microwatt that can be referenced. Signed-off-by: Kyle Tso Link: https://lore.kernel.org/r/20231205030114.1349089-2-kyletso@google.com Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20231216104630.2720818-2-kyletso@google.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/connector/usb-connector.yaml | 81 ++++++++++++------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index f5966b3a2d9a..fb216ce68bb3 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -66,7 +66,6 @@ properties: Particularly, if use an output GPIO to control a VBUS regulator, should model it as a regulator. See bindings/regulator/fixed-regulator.yaml - # The following are optional properties for "usb-c-connector". power-role: description: Determines the power role that the Type C connector will support. "dual" refers to Dual Role Port (DRP). @@ -119,30 +118,6 @@ properties: # The following are optional properties for "usb-c-connector" with power # delivery support. - source-pdos: - description: An array of u32 with each entry providing supported power - source data object(PDO), the detailed bit definitions of PDO can be found - in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.2 - Source_Capabilities Message, the order of each entry(PDO) should follow - the PD spec chapter 6.4.1. Required for power source and power dual role. - User can specify the source PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() - defined in dt-bindings/usb/pd.h. - minItems: 1 - maxItems: 7 - $ref: /schemas/types.yaml#/definitions/uint32-array - - sink-pdos: - description: An array of u32 with each entry providing supported power sink - data object(PDO), the detailed bit definitions of PDO can be found in - "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.3 - Sink Capabilities Message, the order of each entry(PDO) should follow the - PD spec chapter 6.4.1. Required for power sink and power dual role. User - can specify the sink PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() defined - in dt-bindings/usb/pd.h. - minItems: 1 - maxItems: 7 - $ref: /schemas/types.yaml#/definitions/uint32-array - sink-vdos: description: An array of u32 with each entry, a Vendor Defined Message Object (VDO), providing additional information corresponding to the product, the detailed bit @@ -166,11 +141,6 @@ properties: maxItems: 6 $ref: /schemas/types.yaml#/definitions/uint32-array - op-sink-microwatt: - description: Sink required operating power in microwatt, if source can't - offer the power, Capability Mismatch is set. Required for power sink and - power dual role. - accessory-mode-audio: type: boolean description: Whether the device supports Audio Adapter Accessory Mode. This @@ -269,6 +239,20 @@ properties: SNK_READY for non-pd link. type: boolean + capabilities: + description: A child node to contain all the selectable USB Power Delivery capabilities. + type: object + + patternProperties: + "^caps-[0-9]+$": + description: Child nodes under "capabilities" node. Each node contains a selectable USB + Power Delivery capability. + type: object + $ref: "#/$defs/capabilities" + unevaluatedProperties: false + + additionalProperties: false + dependencies: sink-vdos-v1: [ sink-vdos ] sink-vdos: [ sink-vdos-v1 ] @@ -276,7 +260,42 @@ dependencies: required: - compatible +$defs: + capabilities: + type: object + + properties: + source-pdos: + description: An array of u32 with each entry providing supported power + source data object(PDO), the detailed bit definitions of PDO can be found + in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.2 + Source_Capabilities Message, the order of each entry(PDO) should follow + the PD spec chapter 6.4.1. Required for power source and power dual role. + User can specify the source PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() + defined in dt-bindings/usb/pd.h. + minItems: 1 + maxItems: 7 + $ref: /schemas/types.yaml#/definitions/uint32-array + + sink-pdos: + description: An array of u32 with each entry providing supported power sink + data object(PDO), the detailed bit definitions of PDO can be found in + "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.3 + Sink Capabilities Message, the order of each entry(PDO) should follow the + PD spec chapter 6.4.1. Required for power sink and power dual role. User + can specify the sink PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() defined + in dt-bindings/usb/pd.h. + minItems: 1 + maxItems: 7 + $ref: /schemas/types.yaml#/definitions/uint32-array + + op-sink-microwatt: + description: Sink required operating power in microwatt, if source can't + offer the power, Capability Mismatch is set. Required for power sink and + power dual role. + allOf: + - $ref: "#/$defs/capabilities" - if: properties: compatible: @@ -305,7 +324,7 @@ anyOf: - typec-power-opmode - new-source-frs-typec-current -additionalProperties: false +unevaluatedProperties: false examples: # Micro-USB connector with HS lines routed via controller (MUIC). From cd099cde4ed264403b434d8344994f97ac2a4349 Mon Sep 17 00:00:00 2001 From: Kyle Tso Date: Sat, 16 Dec 2023 18:46:30 +0800 Subject: [PATCH 133/169] usb: typec: tcpm: Support multiple capabilities Refactor tcpm_fw_get_caps to support the multiple pd capabilities got from fwnode. For backward compatibility, the original single capability is still applicable. The fetched data is stored in the newly defined structure "pd_data" and there is an array "pd_list" to store the pointers to them. A dedicated array "pds" is used to store the handles of the registered usb_power_delivery instances. Also implement the .pd_get and .pd_set ops which are introduced in commit a7cff92f0635 ("usb: typec: USB Power Delivery helpers for ports and partners"). Once the .pd_set is called, the current capability will be updated and state machine will re-negotiate the power contract if possible. Signed-off-by: Kyle Tso Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20231216104630.2720818-3-kyletso@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 385 ++++++++++++++++++++++++++-------- 1 file changed, 297 insertions(+), 88 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index cf70f1cf2f61..5945e3a2b0f7 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -297,6 +297,15 @@ struct pd_pps_data { bool active; }; +struct pd_data { + struct usb_power_delivery *pd; + struct usb_power_delivery_capabilities *source_cap; + struct usb_power_delivery_capabilities_desc source_desc; + struct usb_power_delivery_capabilities *sink_cap; + struct usb_power_delivery_capabilities_desc sink_desc; + unsigned int operating_snk_mw; +}; + struct tcpm_port { struct device *dev; @@ -398,12 +407,14 @@ struct tcpm_port { unsigned int rx_msgid; /* USB PD objects */ - struct usb_power_delivery *pd; + struct usb_power_delivery **pds; + struct pd_data **pd_list; struct usb_power_delivery_capabilities *port_source_caps; struct usb_power_delivery_capabilities *port_sink_caps; struct usb_power_delivery *partner_pd; struct usb_power_delivery_capabilities *partner_source_caps; struct usb_power_delivery_capabilities *partner_sink_caps; + struct usb_power_delivery *selected_pd; /* Partner capabilities/requests */ u32 sink_request; @@ -413,6 +424,7 @@ struct tcpm_port { unsigned int nr_sink_caps; /* Local capabilities */ + unsigned int pd_count; u32 src_pdo[PDO_MAX_OBJECTS]; unsigned int nr_src_pdo; u32 snk_pdo[PDO_MAX_OBJECTS]; @@ -6060,12 +6072,114 @@ port_unlock: return 0; } +static struct pd_data *tcpm_find_pd_data(struct tcpm_port *port, struct usb_power_delivery *pd) +{ + int i; + + for (i = 0; port->pd_list[i]; i++) { + if (port->pd_list[i]->pd == pd) + return port->pd_list[i]; + } + + return ERR_PTR(-ENODATA); +} + +static struct usb_power_delivery **tcpm_pd_get(struct typec_port *p) +{ + struct tcpm_port *port = typec_get_drvdata(p); + + return port->pds; +} + +static int tcpm_pd_set(struct typec_port *p, struct usb_power_delivery *pd) +{ + struct tcpm_port *port = typec_get_drvdata(p); + struct pd_data *data; + int i, ret = 0; + + mutex_lock(&port->lock); + + if (port->selected_pd == pd) + goto unlock; + + data = tcpm_find_pd_data(port, pd); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + goto unlock; + } + + if (data->sink_desc.pdo[0]) { + for (i = 0; i < PDO_MAX_OBJECTS && data->sink_desc.pdo[i]; i++) + port->snk_pdo[i] = data->sink_desc.pdo[i]; + port->nr_snk_pdo = i + 1; + port->operating_snk_mw = data->operating_snk_mw; + } + + if (data->source_desc.pdo[0]) { + for (i = 0; i < PDO_MAX_OBJECTS && data->source_desc.pdo[i]; i++) + port->snk_pdo[i] = data->source_desc.pdo[i]; + port->nr_src_pdo = i + 1; + } + + switch (port->state) { + case SRC_UNATTACHED: + case SRC_ATTACH_WAIT: + case SRC_TRYWAIT: + tcpm_set_cc(port, tcpm_rp_cc(port)); + break; + case SRC_SEND_CAPABILITIES: + case SRC_SEND_CAPABILITIES_TIMEOUT: + case SRC_NEGOTIATE_CAPABILITIES: + case SRC_READY: + case SRC_WAIT_NEW_CAPABILITIES: + port->caps_count = 0; + port->upcoming_state = SRC_SEND_CAPABILITIES; + ret = tcpm_ams_start(port, POWER_NEGOTIATION); + if (ret == -EAGAIN) { + port->upcoming_state = INVALID_STATE; + goto unlock; + } + break; + case SNK_NEGOTIATE_CAPABILITIES: + case SNK_NEGOTIATE_PPS_CAPABILITIES: + case SNK_READY: + case SNK_TRANSITION_SINK: + case SNK_TRANSITION_SINK_VBUS: + if (port->pps_data.active) + port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; + else if (port->pd_capable) + port->upcoming_state = SNK_NEGOTIATE_CAPABILITIES; + else + break; + + port->update_sink_caps = true; + + ret = tcpm_ams_start(port, POWER_NEGOTIATION); + if (ret == -EAGAIN) { + port->upcoming_state = INVALID_STATE; + goto unlock; + } + break; + default: + break; + } + + port->port_source_caps = data->source_cap; + port->port_sink_caps = data->sink_cap; + port->selected_pd = pd; +unlock: + mutex_unlock(&port->lock); + return ret; +} + static const struct typec_operations tcpm_ops = { .try_role = tcpm_try_role, .dr_set = tcpm_dr_set, .pr_set = tcpm_pr_set, .vconn_set = tcpm_vconn_set, - .port_type_set = tcpm_port_type_set + .port_type_set = tcpm_port_type_set, + .pd_get = tcpm_pd_get, + .pd_set = tcpm_pd_set }; void tcpm_tcpc_reset(struct tcpm_port *port) @@ -6079,58 +6193,63 @@ EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); static void tcpm_port_unregister_pd(struct tcpm_port *port) { - usb_power_delivery_unregister_capabilities(port->port_sink_caps); + int i; + port->port_sink_caps = NULL; - usb_power_delivery_unregister_capabilities(port->port_source_caps); port->port_source_caps = NULL; - usb_power_delivery_unregister(port->pd); - port->pd = NULL; + for (i = 0; i < port->pd_count; i++) { + usb_power_delivery_unregister_capabilities(port->pd_list[i]->sink_cap); + kfree(port->pd_list[i]->sink_cap); + usb_power_delivery_unregister_capabilities(port->pd_list[i]->source_cap); + kfree(port->pd_list[i]->source_cap); + devm_kfree(port->dev, port->pd_list[i]); + port->pd_list[i] = NULL; + usb_power_delivery_unregister(port->pds[i]); + port->pds[i] = NULL; + } } static int tcpm_port_register_pd(struct tcpm_port *port) { struct usb_power_delivery_desc desc = { port->typec_caps.pd_revision }; - struct usb_power_delivery_capabilities_desc caps = { }; struct usb_power_delivery_capabilities *cap; - int ret; + int ret, i; if (!port->nr_src_pdo && !port->nr_snk_pdo) return 0; - port->pd = usb_power_delivery_register(port->dev, &desc); - if (IS_ERR(port->pd)) { - ret = PTR_ERR(port->pd); - goto err_unregister; - } - - if (port->nr_src_pdo) { - memcpy_and_pad(caps.pdo, sizeof(caps.pdo), port->src_pdo, - port->nr_src_pdo * sizeof(u32), 0); - caps.role = TYPEC_SOURCE; - - cap = usb_power_delivery_register_capabilities(port->pd, &caps); - if (IS_ERR(cap)) { - ret = PTR_ERR(cap); + for (i = 0; i < port->pd_count; i++) { + port->pds[i] = usb_power_delivery_register(port->dev, &desc); + if (IS_ERR(port->pds[i])) { + ret = PTR_ERR(port->pds[i]); goto err_unregister; } + port->pd_list[i]->pd = port->pds[i]; - port->port_source_caps = cap; - } - - if (port->nr_snk_pdo) { - memcpy_and_pad(caps.pdo, sizeof(caps.pdo), port->snk_pdo, - port->nr_snk_pdo * sizeof(u32), 0); - caps.role = TYPEC_SINK; - - cap = usb_power_delivery_register_capabilities(port->pd, &caps); - if (IS_ERR(cap)) { - ret = PTR_ERR(cap); - goto err_unregister; + if (port->pd_list[i]->source_desc.pdo[0]) { + cap = usb_power_delivery_register_capabilities(port->pds[i], + &port->pd_list[i]->source_desc); + if (IS_ERR(cap)) { + ret = PTR_ERR(cap); + goto err_unregister; + } + port->pd_list[i]->source_cap = cap; } - port->port_sink_caps = cap; + if (port->pd_list[i]->sink_desc.pdo[0]) { + cap = usb_power_delivery_register_capabilities(port->pds[i], + &port->pd_list[i]->sink_desc); + if (IS_ERR(cap)) { + ret = PTR_ERR(cap); + goto err_unregister; + } + port->pd_list[i]->sink_cap = cap; + } } + port->port_source_caps = port->pd_list[0]->source_cap; + port->port_sink_caps = port->pd_list[0]->sink_cap; + port->selected_pd = port->pds[0]; return 0; err_unregister: @@ -6139,13 +6258,15 @@ err_unregister: return ret; } -static int tcpm_fw_get_caps(struct tcpm_port *port, - struct fwnode_handle *fwnode) +static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { + struct fwnode_handle *capabilities, *child, *caps = NULL; + unsigned int nr_src_pdo, nr_snk_pdo; const char *opmode_str; - int ret; + u32 *src_pdo, *snk_pdo; + u32 uw, frs_current; + int ret = 0, i; int mode; - u32 mw, frs_current; if (!fwnode) return -EINVAL; @@ -6173,28 +6294,10 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, port->port_type = port->typec_caps.type; port->pd_supported = !fwnode_property_read_bool(fwnode, "pd-disable"); - port->slow_charger_loop = fwnode_property_read_bool(fwnode, "slow-charger-loop"); - if (port->port_type == TYPEC_PORT_SNK) - goto sink; + port->self_powered = fwnode_property_read_bool(fwnode, "self-powered"); - /* Get Source PDOs for the PD port or Source Rp value for the non-PD port */ - if (port->pd_supported) { - ret = fwnode_property_count_u32(fwnode, "source-pdos"); - if (ret == 0) - return -EINVAL; - else if (ret < 0) - return ret; - - port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS); - ret = fwnode_property_read_u32_array(fwnode, "source-pdos", - port->src_pdo, port->nr_src_pdo); - if (ret) - return ret; - ret = tcpm_validate_caps(port, port->src_pdo, port->nr_src_pdo); - if (ret) - return ret; - } else { + if (!port->pd_supported) { ret = fwnode_property_read_string(fwnode, "typec-power-opmode", &opmode_str); if (ret) return ret; @@ -6202,45 +6305,150 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, if (ret < 0) return ret; port->src_rp = tcpm_pwr_opmode_to_rp(ret); + return 0; } - if (port->port_type == TYPEC_PORT_SRC) - return 0; - -sink: - port->self_powered = fwnode_property_read_bool(fwnode, "self-powered"); - - if (!port->pd_supported) - return 0; - - /* Get sink pdos */ - ret = fwnode_property_count_u32(fwnode, "sink-pdos"); - if (ret <= 0) - return -EINVAL; - - port->nr_snk_pdo = min(ret, PDO_MAX_OBJECTS); - ret = fwnode_property_read_u32_array(fwnode, "sink-pdos", - port->snk_pdo, port->nr_snk_pdo); - if ((ret < 0) || tcpm_validate_caps(port, port->snk_pdo, - port->nr_snk_pdo)) - return -EINVAL; - - if (fwnode_property_read_u32(fwnode, "op-sink-microwatt", &mw) < 0) - return -EINVAL; - port->operating_snk_mw = mw / 1000; + /* The following code are applicable to pd-capable ports, i.e. pd_supported is true. */ /* FRS can only be supported by DRP ports */ if (port->port_type == TYPEC_PORT_DRP) { ret = fwnode_property_read_u32(fwnode, "new-source-frs-typec-current", &frs_current); - if (ret >= 0 && frs_current <= FRS_5V_3A) + if (!ret && frs_current <= FRS_5V_3A) port->new_source_frs_current = frs_current; + + if (ret) + ret = 0; } + /* For the backward compatibility, "capabilities" node is optional. */ + capabilities = fwnode_get_named_child_node(fwnode, "capabilities"); + if (!capabilities) { + port->pd_count = 1; + } else { + fwnode_for_each_child_node(capabilities, child) + port->pd_count++; + + if (!port->pd_count) { + ret = -ENODATA; + goto put_capabilities; + } + } + + port->pds = devm_kcalloc(port->dev, port->pd_count, sizeof(struct usb_power_delivery *), + GFP_KERNEL); + if (!port->pds) { + ret = -ENOMEM; + goto put_capabilities; + } + + port->pd_list = devm_kcalloc(port->dev, port->pd_count, sizeof(struct pd_data *), + GFP_KERNEL); + if (!port->pd_list) { + ret = -ENOMEM; + goto put_capabilities; + } + + for (i = 0; i < port->pd_count; i++) { + port->pd_list[i] = devm_kzalloc(port->dev, sizeof(struct pd_data), GFP_KERNEL); + if (!port->pd_list[i]) { + ret = -ENOMEM; + goto put_capabilities; + } + + src_pdo = port->pd_list[i]->source_desc.pdo; + port->pd_list[i]->source_desc.role = TYPEC_SOURCE; + snk_pdo = port->pd_list[i]->sink_desc.pdo; + port->pd_list[i]->sink_desc.role = TYPEC_SINK; + + /* If "capabilities" is NULL, fall back to single pd cap population. */ + if (!capabilities) + caps = fwnode; + else + caps = fwnode_get_next_child_node(capabilities, caps); + + if (port->port_type != TYPEC_PORT_SNK) { + ret = fwnode_property_count_u32(caps, "source-pdos"); + if (ret == 0) { + ret = -EINVAL; + goto put_caps; + } + if (ret < 0) + goto put_caps; + + nr_src_pdo = min(ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(caps, "source-pdos", src_pdo, + nr_src_pdo); + if (ret) + goto put_caps; + + ret = tcpm_validate_caps(port, src_pdo, nr_src_pdo); + if (ret) + goto put_caps; + + if (i == 0) { + port->nr_src_pdo = nr_src_pdo; + memcpy_and_pad(port->src_pdo, sizeof(u32) * PDO_MAX_OBJECTS, + port->pd_list[0]->source_desc.pdo, + sizeof(u32) * nr_src_pdo, + 0); + } + } + + if (port->port_type != TYPEC_PORT_SRC) { + ret = fwnode_property_count_u32(caps, "sink-pdos"); + if (ret == 0) { + ret = -EINVAL; + goto put_caps; + } + + if (ret < 0) + goto put_caps; + + nr_snk_pdo = min(ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(caps, "sink-pdos", snk_pdo, + nr_snk_pdo); + if (ret) + goto put_caps; + + ret = tcpm_validate_caps(port, snk_pdo, nr_snk_pdo); + if (ret) + goto put_caps; + + if (fwnode_property_read_u32(caps, "op-sink-microwatt", &uw) < 0) { + ret = -EINVAL; + goto put_caps; + } + + port->pd_list[i]->operating_snk_mw = uw / 1000; + + if (i == 0) { + port->nr_snk_pdo = nr_snk_pdo; + memcpy_and_pad(port->snk_pdo, sizeof(u32) * PDO_MAX_OBJECTS, + port->pd_list[0]->sink_desc.pdo, + sizeof(u32) * nr_snk_pdo, + 0); + port->operating_snk_mw = port->pd_list[0]->operating_snk_mw; + } + } + } + +put_caps: + if (caps != fwnode) + fwnode_handle_put(caps); +put_capabilities: + fwnode_handle_put(capabilities); + return ret; +} + +static int tcpm_fw_get_snk_vdos(struct tcpm_port *port, struct fwnode_handle *fwnode) +{ + int ret; + /* sink-vdos is optional */ ret = fwnode_property_count_u32(fwnode, "sink-vdos"); if (ret < 0) - ret = 0; + return 0; port->nr_snk_vdo = min(ret, VDO_MAX_OBJECTS); if (port->nr_snk_vdo) { @@ -6606,12 +6814,14 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) tcpm_debugfs_init(port); err = tcpm_fw_get_caps(port, tcpc->fwnode); + if (err < 0) + goto out_destroy_wq; + err = tcpm_fw_get_snk_vdos(port, tcpc->fwnode); if (err < 0) goto out_destroy_wq; port->try_role = port->typec_caps.prefer_role; - port->typec_caps.fwnode = tcpc->fwnode; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ port->typec_caps.svdm_version = SVDM_VER_2_0; @@ -6620,7 +6830,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.orientation_aware = 1; port->partner_desc.identity = &port->partner_ident; - port->port_type = port->typec_caps.type; port->role_sw = usb_role_switch_get(port->dev); if (!port->role_sw) @@ -6639,7 +6848,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) if (err) goto out_role_sw_put; - port->typec_caps.pd = port->pd; + port->typec_caps.pd = port->pds[0]; port->typec_port = typec_register_port(port->dev, &port->typec_caps); if (IS_ERR(port->typec_port)) { From 398aa9a7e77cf23c2a6f882ddd3dcd96f21771dc Mon Sep 17 00:00:00 2001 From: Manan Aurora Date: Tue, 31 Oct 2023 03:46:41 +0000 Subject: [PATCH 134/169] usb: dwc3: Support EBC feature of DWC_usb31 Support configuration and use of bulk endpoints in the so-called EBC mode described in the DBC_usb31 databook (appendix E) Added a bit fifo_mode to usb_ep to indicate to the UDC driver that a specific endpoint is to operate in the EBC (or equivalent) mode when enabled Added macros for bits 15 and 14 of DEPCFG parameter 1 to indicate EBC mode and write back behaviour. These bits will be set to 1 when configuring an EBC endpoint as described in the programming guide Signed-off-by: Manan Aurora Link: https://lore.kernel.org/r/20231031034641.660606-1-maurora@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/gadget.c | 6 ++++++ drivers/usb/dwc3/gadget.h | 2 ++ include/linux/usb/gadget.h | 1 + 4 files changed, 10 insertions(+) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e120611a5174..e3eea965e57b 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -376,6 +376,7 @@ /* Global HWPARAMS4 Register */ #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) #define DWC3_MAX_HIBER_SCRATCHBUFS 15 +#define DWC3_EXT_BUFF_CONTROL BIT(21) /* Global HWPARAMS6 Register */ #define DWC3_GHWPARAMS6_BCSUPPORT BIT(14) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 88d8d589f014..c15e965ea95a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -673,6 +673,12 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(bInterval_m1); } + if (dep->endpoint.fifo_mode) { + if (!(dwc->hwparams.hwparams4 & DWC3_EXT_BUFF_CONTROL)) + return -EINVAL; + params.param1 |= DWC3_DEPCFG_EBC_HWO_NOWB | DWC3_DEPCFG_USE_EBC; + } + return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms); } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 55a56cf67d73..fd7a4e94397e 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -26,6 +26,8 @@ struct dwc3; #define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10) #define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11) #define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13) +#define DWC3_DEPCFG_EBC_HWO_NOWB BIT(14) +#define DWC3_DEPCFG_USE_EBC BIT(15) #define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16) #define DWC3_DEPCFG_STREAM_CAPABLE BIT(24) #define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 6532beb587b1..a771ccc038ac 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -236,6 +236,7 @@ struct usb_ep { unsigned max_streams:16; unsigned mult:2; unsigned maxburst:5; + unsigned fifo_mode:1; u8 address; const struct usb_endpoint_descriptor *desc; const struct usb_ss_ep_comp_descriptor *comp_desc; From 68c26fe58182f5af56bfa577d1cc0c949740baab Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Wed, 6 Dec 2023 14:59:39 +0800 Subject: [PATCH 135/169] usb: dwc3: set pm runtime active before resume common For device mode, if PM runtime autosuspend feature enabled, the runtime power status of dwc3 may be suspended when run dwc3_resume(), and dwc3 gadget would not be configured in dwc3_gadget_run_stop(). It would cause gadget connected failed if USB cable has been plugged before PM resume. So move forward pm_runtime_set_active() to fix it. Signed-off-by: Frank Wang Link: https://lore.kernel.org/r/20231206065939.16958-1-frank.wang@rock-chips.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 4447ef2cbc0f..3e55838c0001 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -2315,12 +2315,15 @@ static int dwc3_resume(struct device *dev) pinctrl_pm_select_default_state(dev); - ret = dwc3_resume_common(dwc, PMSG_RESUME); - if (ret) - return ret; - pm_runtime_disable(dev); pm_runtime_set_active(dev); + + ret = dwc3_resume_common(dwc, PMSG_RESUME); + if (ret) { + pm_runtime_set_suspended(dev); + return ret; + } + pm_runtime_enable(dev); return 0; From 1900daeefd3ebde61199649143085a2dfdebf55c Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Thu, 21 Dec 2023 21:02:16 +0530 Subject: [PATCH 136/169] usb: gadget: ncm: Add support to update wMaxSegmentSize via configfs The max segment size is currently limited to the ethernet frame length of the kernel which happens to be 1514 at this point in time. However the NCM specification limits it to 64K for sixtenn bit NTB's. For peer to peer connections, increasing the segment size gives better throughput. Add support to configure this value before configfs symlink is created. Also since the NTB Out/In buffer sizes are fixed at 16384 bytes, limit the segment size to an upper cap of 8000 to allow at least a minimum of 2 MTU sized datagrams to be aggregated. Set the default MTU size for the ncm interface during function bind before network interface is registered allowing MTU to be set in parity with wMaxSegmentSize. Update gadget documentation describing the new configfs property. Signed-off-by: Krishna Kurapati Link: https://lore.kernel.org/r/20231221153216.18657-1-quic_kriskura@quicinc.com Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/gadget-testing.rst | 20 ++++---- drivers/usb/gadget/function/f_ncm.c | 69 ++++++++++++++++++++++++++-- drivers/usb/gadget/function/u_ncm.h | 2 + 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 29072c166d23..8cd62c466d20 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -448,15 +448,17 @@ Function-specific configfs interface The function name to use when creating the function directory is "ncm". The NCM function provides these attributes in its function directory: - =============== ================================================== - ifname network device interface name associated with this - function instance - qmult queue length multiplier for high and super speed - host_addr MAC address of host's end of this - Ethernet over USB link - dev_addr MAC address of device's end of this - Ethernet over USB link - =============== ================================================== + =============== ================================================== + ifname network device interface name associated with this + function instance + qmult queue length multiplier for high and super speed + host_addr MAC address of host's end of this + Ethernet over USB link + dev_addr MAC address of device's end of this + Ethernet over USB link + max_segment_size Segment size required for P2P connections. This + will set MTU to (max_segment_size - 14 bytes) + =============== ================================================== and after creating the functions/ncm. they contain default values: qmult is 5, dev_addr and host_addr are randomly selected. diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index cc0ed29a4adc..a1575a0ca568 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -103,6 +103,16 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f) /* Delay for the transmit to wait before sending an unfilled NTB frame. */ #define TX_TIMEOUT_NSECS 300000 +/* + * Although max mtu as dictated by u_ether is 15412 bytes, setting + * max_segment_sizeto 15426 would not be efficient. If user chooses segment + * size to be (>= 8192), then we can't aggregate more than one buffer in each + * NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep + * maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow + * at least 2 packets to be aggregated reducing wastage of NTB buffer space + */ +#define MAX_DATAGRAM_SIZE 8000 + #define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \ USB_CDC_NCM_NTB32_SUPPORTED) @@ -179,7 +189,6 @@ static struct usb_cdc_ether_desc ecm_desc = { /* this descriptor actually adds value, surprise! */ /* .iMACAddress = DYNAMIC */ .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */ - .wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN), .wNumberMCFilters = cpu_to_le16(0), .bNumberPowerFilters = 0, }; @@ -1166,11 +1175,15 @@ static int ncm_unwrap_ntb(struct gether *port, struct sk_buff *skb2; int ret = -EINVAL; unsigned ntb_max = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize); - unsigned frame_max = le16_to_cpu(ecm_desc.wMaxSegmentSize); + unsigned frame_max; const struct ndp_parser_opts *opts = ncm->parser_opts; unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; int dgram_counter; int to_process = skb->len; + struct f_ncm_opts *ncm_opts; + + ncm_opts = container_of(port->func.fi, struct f_ncm_opts, func_inst); + frame_max = ncm_opts->max_segment_size; parse_ntb: tmp = (__le16 *)ntb_ptr; @@ -1430,8 +1443,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) mutex_lock(&ncm_opts->lock); gether_set_gadget(ncm_opts->net, cdev->gadget); - if (!ncm_opts->bound) + if (!ncm_opts->bound) { + ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN); status = gether_register_netdev(ncm_opts->net); + } mutex_unlock(&ncm_opts->lock); if (status) @@ -1474,6 +1489,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ncm_data_intf.bInterfaceNumber = status; ncm_union_desc.bSlaveInterface0 = status; + ecm_desc.wMaxSegmentSize = ncm_opts->max_segment_size; + status = -ENODEV; /* allocate instance-specific endpoints */ @@ -1576,11 +1593,56 @@ USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm); /* f_ncm_opts_ifname */ USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm); +static ssize_t ncm_opts_max_segment_size_show(struct config_item *item, + char *page) +{ + struct f_ncm_opts *opts = to_f_ncm_opts(item); + u16 segment_size; + + mutex_lock(&opts->lock); + segment_size = opts->max_segment_size; + mutex_unlock(&opts->lock); + + return sysfs_emit(page, "%u\n", segment_size); +} + +static ssize_t ncm_opts_max_segment_size_store(struct config_item *item, + const char *page, size_t len) +{ + struct f_ncm_opts *opts = to_f_ncm_opts(item); + u16 segment_size; + int ret; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto out; + } + + ret = kstrtou16(page, 0, &segment_size); + if (ret) + goto out; + + if (segment_size > MAX_DATAGRAM_SIZE) { + ret = -EINVAL; + goto out; + } + + opts->max_segment_size = segment_size; + ret = len; +out: + mutex_unlock(&opts->lock); + return ret; +} + +CONFIGFS_ATTR(ncm_opts_, max_segment_size); + static struct configfs_attribute *ncm_attrs[] = { &ncm_opts_attr_dev_addr, &ncm_opts_attr_host_addr, &ncm_opts_attr_qmult, &ncm_opts_attr_ifname, + &ncm_opts_attr_max_segment_size, NULL, }; @@ -1623,6 +1685,7 @@ static struct usb_function_instance *ncm_alloc_inst(void) kfree(opts); return ERR_CAST(net); } + opts->max_segment_size = cpu_to_le16(ETH_FRAME_LEN); INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); descs[0] = &opts->ncm_os_desc; diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index 5408854d8407..49ec095cdb4b 100644 --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -31,6 +31,8 @@ struct f_ncm_opts { */ struct mutex lock; int refcnt; + + u16 max_segment_size; }; #endif /* U_NCM_H */ From 1b8be5ecff26201bafb0a554c74e91571299fb94 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sun, 24 Dec 2023 10:38:13 -0500 Subject: [PATCH 137/169] usb: cdns3: fix uvc failure work since sg support enabled When IP version >= DEV_VER_V2, gadget:sg_supported is true. So uvc gadget function driver will use sg to equeue data, first is 8bytes header, the second is 1016bytes data. cdns3_prepare_trb: ep2in: trb 0000000000ac755f, dma buf: 0xbf455000, size: 8, burst: 128 ctrl: 0x00000415 (C=1, T=0, ISP, CHAIN, Normal) cdns3_prepare_trb: ep2in: trb 00000000a574e693, dma buf: 0xc0200fe0, size: 1016, burst: 128 ctrl: 0x00000405 (C=1, T=0, ISP, Normal) But cdns3_ep_run_transfer() can't correctly handle this case, which only support one TRB for ISO transfer. The controller requires duplicate the TD for each SOF if priv_ep->interval is not 1. DMA will read data from DDR to internal FIFO when get SOF. Send data to bus when receive IN token. DMA always refill FIFO when get SOF regardless host send IN token or not. If host send IN token later, some frames data will be lost. Fixed it by below major steps: 1. Calculate numembers of TRB base on sg_nums and priv_ep->interval. 2. Remove CHAIN flags for each end TRB of TD when duplicate TD. 3. The controller requires LINK TRB must be first TRB of TD. When check there are not enough TRBs lefts, just fill LINK TRB for left TRBs. .... CHAIN_TRB DATA_TRB, CHAIN_TRB DATA_TRB, LINK_TRB ... LINK_TRB ^End of TRB List Cc: Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20231224153816.1664687-2-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 53 ++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 15463b7cddd2..22a31ffa6942 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -1119,6 +1119,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, dma_addr_t trb_dma; u32 togle_pcs = 1; int sg_iter = 0; + int num_trb_req; int num_trb; int address; u32 control; @@ -1128,15 +1129,13 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, bool sg_supported = !!(request->num_mapped_sgs); u32 ioc = request->no_interrupt ? 0 : TRB_IOC; - if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) - num_trb = priv_ep->interval; - else - num_trb = sg_supported ? request->num_mapped_sgs : 1; + num_trb_req = sg_supported ? request->num_mapped_sgs : 1; - if (num_trb > priv_ep->free_trbs) { - priv_ep->flags |= EP_RING_FULL; - return -ENOBUFS; - } + /* ISO transfer require each SOF have a TD, each TD include some TRBs */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) + num_trb = priv_ep->interval * num_trb_req; + else + num_trb = num_trb_req; priv_req = to_cdns3_request(request); address = priv_ep->endpoint.desc->bEndpointAddress; @@ -1185,14 +1184,31 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, link_trb->control = cpu_to_le32(((priv_ep->pcs) ? TRB_CYCLE : 0) | TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit); + + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) { + /* + * ISO require LINK TRB must be first one of TD. + * Fill LINK TRBs for left trb space to simply software process logic. + */ + while (priv_ep->enqueue) { + *trb = *link_trb; + trace_cdns3_prepare_trb(priv_ep, trb); + + cdns3_ep_inc_enq(priv_ep); + trb = priv_ep->trb_pool + priv_ep->enqueue; + priv_req->trb = trb; + } + } + } + + if (num_trb > priv_ep->free_trbs) { + priv_ep->flags |= EP_RING_FULL; + return -ENOBUFS; } if (priv_dev->dev_ver <= DEV_VER_V2) togle_pcs = cdns3_wa1_update_guard(priv_ep, trb); - if (sg_supported) - s = request->sg; - /* set incorrect Cycle Bit for first trb*/ control = priv_ep->pcs ? 0 : TRB_CYCLE; trb->length = 0; @@ -1210,6 +1226,9 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, do { u32 length; + if (!(sg_iter % num_trb_req) && sg_supported) + s = request->sg; + /* fill TRB */ control |= TRB_TYPE(TRB_NORMAL); if (sg_supported) { @@ -1251,7 +1270,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, if (sg_supported) { trb->control |= cpu_to_le32(TRB_ISP); /* Don't set chain bit for last TRB */ - if (sg_iter < num_trb - 1) + if ((sg_iter % num_trb_req) < num_trb_req - 1) trb->control |= cpu_to_le32(TRB_CHAIN); s = sg_next(s); @@ -1509,6 +1528,12 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, /* The TRB was changed as link TRB, and the request was handled at ep_dequeue */ while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) { + + /* ISO ep_traddr may stop at LINK TRB */ + if (priv_ep->dequeue == cdns3_get_dma_pos(priv_dev, priv_ep) && + priv_ep->type == USB_ENDPOINT_XFER_ISOC) + break; + trace_cdns3_complete_trb(priv_ep, trb); cdns3_ep_inc_deq(priv_ep); trb = priv_ep->trb_pool + priv_ep->dequeue; @@ -1541,6 +1566,10 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, } if (request_handled) { + /* TRBs are duplicated by priv_ep->interval time for ISO IN */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && priv_ep->dir) + request->actual /= priv_ep->interval; + cdns3_gadget_giveback(priv_ep, priv_req, 0); request_handled = false; transfer_end = false; From 92f02efa1d86d7dcaef7f38a5fe3396c4e88a93c Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sun, 24 Dec 2023 10:38:14 -0500 Subject: [PATCH 138/169] usb: cdns3: fix iso transfer error when mult is not zero ISO basic transfer is ITP(SOF) Package_0 Package_1 ... Package_n CDNS3 DMA start dma transfer from memmory to internal FIFO when get SOF, controller will transfer data to usb bus from internal FIFO when get IN token. According USB spec defination: Maximum number of packets = (bMaxBurst + 1) * (Mult + 1) Internal memory should be the same as (bMaxBurst + 1) * (Mult + 1). DMA don't fetch data advance when ISO transfer, so only reserve (bMaxBurst + 1) * (Mult + 1) internal memory for ISO transfer. Need save Mult and bMaxBurst information and set it into EP_CFG register, otherwise only 1 package is sent by controller, other package will be lost. Cc: Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20231224153816.1664687-3-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 59 +++++++++++++++++++------------- drivers/usb/cdns3/cdns3-gadget.h | 3 ++ 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 22a31ffa6942..4c6893af22dd 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -2065,11 +2065,10 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC); struct cdns3_device *priv_dev = priv_ep->cdns3_dev; u32 bEndpointAddress = priv_ep->num | priv_ep->dir; - u32 max_packet_size = 0; - u8 maxburst = 0; + u32 max_packet_size = priv_ep->wMaxPacketSize; + u8 maxburst = priv_ep->bMaxBurst; u32 ep_cfg = 0; u8 buffering; - u8 mult = 0; int ret; buffering = priv_dev->ep_buf_size - 1; @@ -2091,8 +2090,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) break; default: ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC); - mult = priv_dev->ep_iso_burst - 1; - buffering = mult + 1; + buffering = (priv_ep->bMaxBurst + 1) * (priv_ep->mult + 1) - 1; } switch (priv_dev->gadget.speed) { @@ -2103,17 +2101,8 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) max_packet_size = is_iso_ep ? 1024 : 512; break; case USB_SPEED_SUPER: - /* It's limitation that driver assumes in driver. */ - mult = 0; - max_packet_size = 1024; - if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) { - maxburst = priv_dev->ep_iso_burst - 1; - buffering = (mult + 1) * - (maxburst + 1); - - if (priv_ep->interval > 1) - buffering++; - } else { + if (priv_ep->type != USB_ENDPOINT_XFER_ISOC) { + max_packet_size = 1024; maxburst = priv_dev->ep_buf_size - 1; } break; @@ -2142,7 +2131,6 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) if (priv_dev->dev_ver < DEV_VER_V2) priv_ep->trb_burst_size = 16; - mult = min_t(u8, mult, EP_CFG_MULT_MAX); buffering = min_t(u8, buffering, EP_CFG_BUFFERING_MAX); maxburst = min_t(u8, maxburst, EP_CFG_MAXBURST_MAX); @@ -2176,7 +2164,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) } ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) | - EP_CFG_MULT(mult) | + EP_CFG_MULT(priv_ep->mult) | /* must match EP setting */ EP_CFG_BUFFERING(buffering) | EP_CFG_MAXBURST(maxburst); @@ -2266,6 +2254,13 @@ usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget, priv_ep->type = usb_endpoint_type(desc); priv_ep->flags |= EP_CLAIMED; priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0; + priv_ep->wMaxPacketSize = usb_endpoint_maxp(desc); + priv_ep->mult = USB_EP_MAXP_MULT(priv_ep->wMaxPacketSize); + priv_ep->wMaxPacketSize &= USB_ENDPOINT_MAXP_MASK; + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && comp_desc) { + priv_ep->mult = USB_SS_MULT(comp_desc->bmAttributes) - 1; + priv_ep->bMaxBurst = comp_desc->bMaxBurst; + } spin_unlock_irqrestore(&priv_dev->lock, flags); return &priv_ep->endpoint; @@ -3049,22 +3044,40 @@ static int cdns3_gadget_check_config(struct usb_gadget *gadget) struct cdns3_endpoint *priv_ep; struct usb_ep *ep; int n_in = 0; + int iso = 0; + int out = 1; int total; + int n; list_for_each_entry(ep, &gadget->ep_list, ep_list) { priv_ep = ep_to_cdns3_ep(ep); - if ((priv_ep->flags & EP_CLAIMED) && (ep->address & USB_DIR_IN)) - n_in++; + if (!(priv_ep->flags & EP_CLAIMED)) + continue; + + n = (priv_ep->mult + 1) * (priv_ep->bMaxBurst + 1); + if (ep->address & USB_DIR_IN) { + /* + * ISO transfer: DMA start move data when get ISO, only transfer + * data as min(TD size, iso). No benefit for allocate bigger + * internal memory than 'iso'. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) + iso += n; + else + n_in++; + } else { + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) + out = max_t(int, out, n); + } } /* 2KB are reserved for EP0, 1KB for out*/ - total = 2 + n_in + 1; + total = 2 + n_in + out + iso; if (total > priv_dev->onchip_buffers) return -ENOMEM; - priv_dev->ep_buf_size = priv_dev->ep_iso_burst = - (priv_dev->onchip_buffers - 2) / (n_in + 1); + priv_dev->ep_buf_size = (priv_dev->onchip_buffers - 2 - iso) / (n_in + out); return 0; } diff --git a/drivers/usb/cdns3/cdns3-gadget.h b/drivers/usb/cdns3/cdns3-gadget.h index fbe4a8e3aa89..086a7bb83897 100644 --- a/drivers/usb/cdns3/cdns3-gadget.h +++ b/drivers/usb/cdns3/cdns3-gadget.h @@ -1168,6 +1168,9 @@ struct cdns3_endpoint { u8 dir; u8 num; u8 type; + u8 mult; + u8 bMaxBurst; + u16 wMaxPacketSize; int interval; int free_trbs; From 40c304109e866a7dc123661a5c8ca72f6b5e14e0 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sun, 24 Dec 2023 10:38:15 -0500 Subject: [PATCH 139/169] usb: cdns3: Fix uvc fail when DMA cross 4k boundery since sg enabled Supposed DMA cross 4k bounder problem should be fixed at DEV_VER_V2, but still met problem when do ISO transfer if sg enabled. Data pattern likes below when sg enabled, package size is 1k and mult is 2 [UVC Header(8B) ] [data(3k - 8)] ... The received data at offset 0xd000 will get 0xc000 data, len 0x70. Error happen position as below pattern: 0xd000: wrong 0xe000: wrong 0xf000: correct 0x10000: wrong 0x11000: wrong 0x12000: correct ... To avoid DMA cross 4k bounder at ISO transfer, reduce burst len according to start DMA address's alignment. Cc: Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20231224153816.1664687-4-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 4c6893af22dd..aeca902ab6cc 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -1120,6 +1120,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, u32 togle_pcs = 1; int sg_iter = 0; int num_trb_req; + int trb_burst; int num_trb; int address; u32 control; @@ -1243,7 +1244,36 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, total_tdl += DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); - trb->length |= cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) | + trb_burst = priv_ep->trb_burst_size; + + /* + * Supposed DMA cross 4k bounder problem should be fixed at DEV_VER_V2, but still + * met problem when do ISO transfer if sg enabled. + * + * Data pattern likes below when sg enabled, package size is 1k and mult is 2 + * [UVC Header(8B) ] [data(3k - 8)] ... + * + * The received data at offset 0xd000 will get 0xc000 data, len 0x70. Error happen + * as below pattern: + * 0xd000: wrong + * 0xe000: wrong + * 0xf000: correct + * 0x10000: wrong + * 0x11000: wrong + * 0x12000: correct + * ... + * + * But it is still unclear about why error have not happen below 0xd000, it should + * cross 4k bounder. But anyway, the below code can fix this problem. + * + * To avoid DMA cross 4k bounder at ISO transfer, reduce burst len according to 16. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && priv_dev->dev_ver <= DEV_VER_V2) + if (ALIGN_DOWN(trb->buffer, SZ_4K) != + ALIGN_DOWN(trb->buffer + length, SZ_4K)) + trb_burst = 16; + + trb->length |= cpu_to_le32(TRB_BURST_LEN(trb_burst) | TRB_LEN(length)); pcs = priv_ep->pcs ? TRB_CYCLE : 0; From 895ee5aefb7e24203de5dffae7ce9a02d78fa3d1 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sun, 24 Dec 2023 10:38:16 -0500 Subject: [PATCH 140/169] Revert "usb: gadget: f_uvc: change endpoint allocation in uvc_function_bind()" This reverts commit 3c5b006f3ee800b4bd9ed37b3a8f271b8560126e. gadget_is_{super|dual}speed() API check UDC controller capitblity. It should pass down highest speed endpoint descriptor to UDC controller. So UDC controller driver can reserve enough resource at check_config(), especially mult and maxburst. So UDC driver (such as cdns3) can know need at least (mult + 1) * (maxburst + 1) * wMaxPacketSize internal memory for this uvc functions. Cc: Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20231224153816.1664687-5-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uvc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index da5f28e471b0..929666805bd2 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -721,13 +721,29 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) } uvc->enable_interrupt_ep = opts->enable_interrupt_ep; - ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); + /* + * gadget_is_{super|dual}speed() API check UDC controller capitblity. It should pass down + * highest speed endpoint descriptor to UDC controller. So UDC controller driver can reserve + * enough resource at check_config(), especially mult and maxburst. So UDC driver (such as + * cdns3) can know need at least (mult + 1) * (maxburst + 1) * wMaxPacketSize internal + * memory for this uvc functions. This is the only straightforward method to resolve the UDC + * resource allocation issue in the current gadget framework. + */ + 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); + 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; From 5dbe9ac28355fd8f4a7c4732e1e2b7db4fb9f405 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Thu, 28 Dec 2023 19:07:51 +0800 Subject: [PATCH 141/169] usb: chipidea: ci_hdrc_imx: add wakeup clock and keep it always on Some platform using ChipIdea IP may keep 32KHz wakeup clock always on without usb driver intervention. And some may need driver to handle this clock. For now only i.MX93 needs this wakeup clock. This patch will get wakeup clock and keep it always on to make controller work properly. Signed-off-by: Xu Yang Acked-by: Peter Chen Tested-by: Stefan Wahren Link: https://lore.kernel.org/r/20231228110753.1755756-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index e28bb2f2612d..ae9a6a17ec6e 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -96,6 +96,7 @@ struct ci_hdrc_imx_data { struct usb_phy *phy; struct platform_device *ci_pdev; struct clk *clk; + struct clk *clk_wakeup; struct imx_usbmisc_data *usbmisc_data; bool supports_runtime_pm; bool override_phy_control; @@ -199,7 +200,7 @@ static int imx_get_clks(struct device *dev) data->clk_ipg = devm_clk_get(dev, "ipg"); if (IS_ERR(data->clk_ipg)) { - /* If the platform only needs one clocks */ + /* If the platform only needs one primary clock */ data->clk = devm_clk_get(dev, NULL); if (IS_ERR(data->clk)) { ret = PTR_ERR(data->clk); @@ -208,6 +209,13 @@ static int imx_get_clks(struct device *dev) PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); return ret; } + /* Get wakeup clock. Not all of the platforms need to + * handle this clock. So make it optional. + */ + data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup_clk"); + if (IS_ERR(data->clk_wakeup)) + ret = dev_err_probe(dev, PTR_ERR(data->clk_wakeup), + "Failed to get wakeup clk\n"); return ret; } @@ -423,6 +431,10 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (ret) goto disable_hsic_regulator; + ret = clk_prepare_enable(data->clk_wakeup); + if (ret) + goto err_wakeup_clk; + data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0); if (IS_ERR(data->phy)) { ret = PTR_ERR(data->phy); @@ -504,6 +516,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) disable_device: ci_hdrc_remove_device(data->ci_pdev); err_clk: + clk_disable_unprepare(data->clk_wakeup); +err_wakeup_clk: imx_disable_unprepare_clks(dev); disable_hsic_regulator: if (data->hsic_pad_regulator) @@ -530,6 +544,7 @@ static void ci_hdrc_imx_remove(struct platform_device *pdev) usb_phy_shutdown(data->phy); if (data->ci_pdev) { imx_disable_unprepare_clks(&pdev->dev); + clk_disable_unprepare(data->clk_wakeup); if (data->plat_data->flags & CI_HDRC_PMQOS) cpu_latency_qos_remove_request(&data->pm_qos_req); if (data->hsic_pad_regulator) From 128d849074d05545becf86e713715ce7676fc074 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Thu, 28 Dec 2023 19:07:52 +0800 Subject: [PATCH 142/169] usb: chipidea: wait controller resume finished for wakeup irq After the chipidea driver introduce extcon for id and vbus, it's able to wakeup from another irq source, in case the system with extcon ID cable, wakeup from usb ID cable and device removal, the usb device disconnect irq may come firstly before the extcon notifier while system resume, so we will get 2 "wakeup" irq, one for usb device disconnect; and one for extcon ID cable change(real wakeup event), current driver treat them as 2 successive wakeup irq so can't handle it correctly, then finally the usb irq can't be enabled. This patch adds a check to bypass further usb events before controller resume finished to fix it. Fixes: 1f874edcb731 ("usb: chipidea: add runtime power management support") cc: Acked-by: Peter Chen Signed-off-by: Xu Yang Signed-off-by: Li Jun Link: https://lore.kernel.org/r/20231228110753.1755756-2-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 0af9e68035fb..41014f93cfdf 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -523,6 +523,13 @@ static irqreturn_t ci_irq_handler(int irq, void *data) u32 otgsc = 0; if (ci->in_lpm) { + /* + * If we already have a wakeup irq pending there, + * let's just return to wait resume finished firstly. + */ + if (ci->wakeup_int) + return IRQ_HANDLED; + disable_irq_nosync(irq); ci->wakeup_int = true; pm_runtime_get(ci->dev); From ff2b89de471da942a4d853443688113a44fd35ed Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Thu, 28 Dec 2023 19:07:53 +0800 Subject: [PATCH 143/169] usb: phy: mxs: remove CONFIG_USB_OTG condition for mxs_phy_is_otg_host() When CONFIG_USB_OTG is not set, mxs_phy_is_otg_host() will always return false. This behaviour is wrong. Since phy.last_event will always be set for either host or device mode. Therefore, CONFIG_USB_OTG condition can be removed. Fixes: 5eda42aebb76 ("usb: phy: mxs: fix getting wrong state with mxs_phy_is_otg_host()") cc: Acked-by: Peter Chen Signed-off-by: Xu Yang Link: https://lore.kernel.org/r/20231228110753.1755756-3-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-mxs-usb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index acd46b72899e..920a32cd094d 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -388,8 +388,7 @@ 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) { - return IS_ENABLED(CONFIG_USB_OTG) && - mxs_phy->phy.last_event == USB_EVENT_ID; + return mxs_phy->phy.last_event == USB_EVENT_ID; } static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) From 3c7af52c7616c3aa6dacd2336ec748d4a65df8f4 Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Wed, 3 Jan 2024 13:49:46 -0800 Subject: [PATCH 144/169] usb: dwc3: gadget: Queue PM runtime idle on disconnect event There is a scenario where DWC3 runtime suspend is blocked due to the dwc->connected flag still being true while PM usage_count is zero after DWC3 giveback is completed and the USB gadget session is being terminated. This leads to a case where nothing schedules a PM runtime idle for the device. The exact condition is seen with the following sequence: 1. USB bus reset is issued by the host 2. Shortly after, or concurrently, a USB PD DR SWAP request is received (sink->source) 3. USB bus reset event handler runs and issues dwc3_stop_active_transfers(), and pending transfer are stopped 4. DWC3 usage_count decremented to 0, and runtime idle occurs while dwc->connected == true, returns -EBUSY 5. DWC3 disconnect event seen, dwc->connected set to false due to DR swap handling 6. No runtime idle after this point Address this by issuing an asynchronous PM runtime idle call after the disconnect event is completed, as it modifies the dwc->connected flag, which is what blocks the initial runtime idle. Fixes: fc8bb91bc83e ("usb: dwc3: implement runtime PM") Cc: Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20240103214946.2596-1-quic_wcheng@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c15e965ea95a..019368f8e9c4 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3989,6 +3989,13 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED); dwc3_ep0_reset_state(dwc); + + /* + * Request PM idle to address condition where usage count is + * already decremented to zero, but waiting for the disconnect + * interrupt to set dwc->connected to FALSE. + */ + pm_request_idle(dwc->dev); } static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) From 76c945730cdffb572c7767073cc6515fd3f646b4 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Mon, 18 Dec 2023 11:45:33 -0500 Subject: [PATCH 145/169] usb: gadget: u_ether: Re-attach netif device to mirror detachment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 6.7-rc1, there was a netif_device_detach call added to the gether_disconnect function. This clears the __LINK_STATE_PRESENT bit of the netif device and suppresses pings (ICMP messages) and TCP connection requests from the connected host. If userspace temporarily disconnects the gadget, such as by temporarily removing configuration in the gadget configfs interface, network activity should continue to be processed when the gadget is re-connected. Mirror the netif_device_detach call with a netif_device_attach call in gether_connect to fix re-connecting gadgets. Link: https://gitlab.com/postmarketOS/pmaports/-/tree/6002e51b7090aeeb42947e0ca7ec22278d7227d0/main/postmarketos-base-ui/rootfs-usr-lib-NetworkManager-dispatcher.d-50-tethering.sh Cc: stable Fixes: f49449fbc21e ("usb: gadget: u_ether: Replace netif_stop_queue with netif_device_detach") Signed-off-by: Richard Acayan Tested-by: Luca Weiss Tested-by: Duje Mihanović Link: https://lore.kernel.org/r/20231218164532.411125-2-mailingradian@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_ether.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 9d1c40c152d8..3c5a6f6ac341 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1163,6 +1163,8 @@ struct net_device *gether_connect(struct gether *link) if (netif_running(dev->net)) eth_start(dev, GFP_ATOMIC); + netif_device_attach(dev->net); + /* on error, disable any endpoints */ } else { (void) usb_ep_disable(link->out_ep); From 53c6d854be4e93fa21b80bd40e3a666c3961e82c Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Wed, 27 Dec 2023 14:49:50 +0530 Subject: [PATCH 146/169] dt-bindings: usb: dwc3: Clean up hs_phy_irq in binding The high speed related interrupts present on QC targets are as follows: 1. dp/dm irq's These IRQ's directly reflect changes on the DP/DM pads of the SoC. These are used as wakeup interrupts only on SoCs with non-QUSB2 targets with exception of SDM670/SDM845/SM6350. 2. qusb2_phy irq SoCs with QUSB2 PHY do not have separate DP/DM IRQs and expose only a single IRQ whose behavior can be modified by the QUSB2PHY_INTR_CTRL register. The required DPSE/DMSE configuration is done in QUSB2PHY_INTR_CTRL register of phy address space. 3. hs_phy_irq This is completely different from the above two and is present on all targets with exception of a few IPQ ones. The interrupt is not enabled by default and its functionality is mutually exclusive of qusb2_phy on QUSB targets and DP/DM on femto phy targets. The DTs of several QUSB2 PHY based SoCs incorrectly define "hs_phy_irq" when they should have been "qusb2_phy_irq". On Femto phy targets, the "hs_phy_irq" mentioned is either the actual "hs_phy_irq" or "pwr_event", neither of which would never be triggered directly are non-functional currently. The implementation tries to clean up this issue by addressing the discrepencies involved and fixing the hs_phy_irq's in respective DT's. Classify SoC's into four groups based on whether qusb2_phy interrupt or {dp/dm}_hs_phy_irq is used for wakeup in high speed and whether the SoCs have hs_phy_irq present in them or not. The ss_phy_irq is optional interrupt because there are mutliple SoC's which either support only High Speed or there are multiple controllers within same Soc and the secondary controller is High Speed only capable. This breaks ABI on targets running older kernels, but since the interrupt definitions are given wrong on many targets and to establish proper rules for usage of DWC3 interrupts on Qualcomm platforms, DT binding update is necessary. The bindings put pwr_event as the first interrupt and ss_phy as the last. Since all SoCs have the pwr_event (HS) interrupt, but not all controllers have the SS PHY interrupt, this would prevent, to some extent, expressing that the SS PHY is optional by keeping it last in the binding schema and making sure that minItems = maxItems - 1. No new targets have been added to schema. Only the existing ones have been re-ordered. Signed-off-by: Krishna Kurapati Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20231227091951.685-2-quic_kriskura@quicinc.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/qcom,dwc3.yaml | 141 ++++++++---------- 1 file changed, 61 insertions(+), 80 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index 473c4bfaf8a2..8242e22fde36 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -99,12 +99,29 @@ properties: - const: apps-usb interrupts: - minItems: 1 - maxItems: 4 + description: | + Different types of interrupts are used based on HS PHY used on target: + - pwr_event: Used for wakeup based on other power events. + - hs_phY_irq: Apart from DP/DM/QUSB2 PHY interrupts, there is + hs_phy_irq which is not triggered by default and its + functionality is mutually exclusive to that of + {dp/dm}_hs_phy_irq and qusb2_phy_irq. + - qusb2_phy: SoCs with QUSB2 PHY do not have separate DP/DM IRQs and + expose only a single IRQ whose behavior can be modified + by the QUSB2PHY_INTR_CTRL register. The required DPSE/ + DMSE configuration is done in QUSB2PHY_INTR_CTRL register + of PHY address space. + - {dp/dm}_hs_phy_irq: These IRQ's directly reflect changes on the DP/ + DM pads of the SoC. These are used for wakeup + only on SoCs with non-QUSB2 targets with + exception of SDM670/SDM845/SM6350. + - ss_phy_irq: Used for remote wakeup in Super Speed mode of operation. + minItems: 2 + maxItems: 5 interrupt-names: - minItems: 1 - maxItems: 4 + minItems: 2 + maxItems: 5 qcom,select-utmi-as-pipe-clk: description: @@ -361,98 +378,42 @@ allOf: compatible: contains: enum: - - qcom,ipq4019-dwc3 + - qcom,ipq5018-dwc3 - qcom,ipq6018-dwc3 - - qcom,ipq8064-dwc3 - qcom,ipq8074-dwc3 - - qcom,msm8994-dwc3 - - qcom,qcs404-dwc3 - - qcom,sc7180-dwc3 - - qcom,sdm670-dwc3 - - qcom,sdm845-dwc3 - - qcom,sdx55-dwc3 - - qcom,sdx65-dwc3 - - qcom,sdx75-dwc3 - - qcom,sm4250-dwc3 - - qcom,sm6350-dwc3 - - qcom,sm8150-dwc3 - - qcom,sm8250-dwc3 - - qcom,sm8350-dwc3 - - qcom,sm8450-dwc3 - - qcom,sm8550-dwc3 - - qcom,sm8650-dwc3 + - qcom,msm8953-dwc3 + - qcom,msm8998-dwc3 then: properties: interrupts: - items: - - description: The interrupt that is asserted - when a wakeup event is received on USB2 bus. - - description: The interrupt that is asserted - when a wakeup event is received on USB3 bus. - - description: Wakeup event on DM line. - - description: Wakeup event on DP line. + minItems: 2 + maxItems: 3 interrupt-names: items: - - const: hs_phy_irq + - const: pwr_event + - const: qusb2_phy - const: ss_phy_irq - - const: dm_hs_phy_irq - - const: dp_hs_phy_irq - if: properties: compatible: contains: enum: - - qcom,msm8953-dwc3 - qcom,msm8996-dwc3 - - qcom,msm8998-dwc3 + - qcom,qcs404-dwc3 + - qcom,sdm660-dwc3 - qcom,sm6115-dwc3 - qcom,sm6125-dwc3 - then: - properties: - interrupts: - maxItems: 2 - interrupt-names: - items: - - const: hs_phy_irq - - const: ss_phy_irq - - - if: - properties: - compatible: - contains: - enum: - - qcom,ipq5018-dwc3 - - qcom,ipq5332-dwc3 - - qcom,sdm660-dwc3 - then: - properties: - interrupts: - minItems: 1 - maxItems: 2 - interrupt-names: - minItems: 1 - items: - - const: hs_phy_irq - - const: ss_phy_irq - - - if: - properties: - compatible: - contains: - enum: - - qcom,sc7280-dwc3 then: properties: interrupts: minItems: 3 maxItems: 4 interrupt-names: - minItems: 3 items: + - const: pwr_event + - const: qusb2_phy - const: hs_phy_irq - - const: dp_hs_phy_irq - - const: dm_hs_phy_irq - const: ss_phy_irq - if: @@ -460,7 +421,7 @@ allOf: compatible: contains: enum: - - qcom,sc8280xp-dwc3 + - qcom,ipq5332-dwc3 - qcom,x1e80100-dwc3 then: properties: @@ -478,16 +439,35 @@ allOf: compatible: contains: enum: + - qcom,ipq4019-dwc3 + - qcom,ipq8064-dwc3 + - qcom,msm8994-dwc3 - qcom,sa8775p-dwc3 + - qcom,sc7180-dwc3 + - qcom,sc7280-dwc3 + - qcom,sc8280xp-dwc3 + - qcom,sdm670-dwc3 + - qcom,sdm845-dwc3 + - qcom,sdx55-dwc3 + - qcom,sdx65-dwc3 + - qcom,sdx75-dwc3 + - qcom,sm4250-dwc3 + - qcom,sm6350-dwc3 + - qcom,sm8150-dwc3 + - qcom,sm8250-dwc3 + - qcom,sm8350-dwc3 + - qcom,sm8450-dwc3 + - qcom,sm8550-dwc3 + - qcom,sm8650-dwc3 then: properties: interrupts: - minItems: 3 - maxItems: 4 + minItems: 4 + maxItems: 5 interrupt-names: - minItems: 3 items: - const: pwr_event + - const: hs_phy_irq - const: dp_hs_phy_irq - const: dm_hs_phy_irq - const: ss_phy_irq @@ -525,12 +505,13 @@ examples: <&gcc GCC_USB30_PRIM_MASTER_CLK>; assigned-clock-rates = <19200000>, <150000000>; - interrupts = , - , + interrupts = , + , + , , - ; - interrupt-names = "hs_phy_irq", "ss_phy_irq", - "dm_hs_phy_irq", "dp_hs_phy_irq"; + ; + interrupt-names = "pwr_event", "hs_phy_irq", + "dp_hs_phy_irq", "dm_hs_phy_irq", "ss_phy_irq"; power-domains = <&gcc USB30_PRIM_GDSC>; From aefdcd89d72bd92e68fe0a8182ed98b4a6b91efa Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Wed, 27 Dec 2023 14:49:51 +0530 Subject: [PATCH 147/169] usb: dwc3: qcom: Rename hs_phy_irq to qusb2_phy_irq For wakeup to work, driver needs to enable interrupts that depict what is happening on the DP/DM lines. On QUSB targets, this is identified by qusb2_phy whereas on SoCs using Femto PHY, separate {dp,dm}_hs_phy_irq's are used instead. The implementation incorrectly names qusb2_phy interrupts as "hs_phy_irq". Clean this up so that driver would be using only qusb2/(dp & dm) for wakeup purposes. For devices running older kernels, this won't break any functionality because the interrupt configurations in QUSB2 PHY based SoCs is done by configuring QUSB2PHY_INTR_CTRL register in PHY address space and it was never armed properly right from the start. Signed-off-by: Krishna Kurapati Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20231227091951.685-3-quic_kriskura@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-qcom.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index fdf6d5d3c2ad..dbd6a5b2b289 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -57,7 +57,7 @@ struct dwc3_acpi_pdata { u32 qscratch_base_offset; u32 qscratch_base_size; u32 dwc3_core_base_size; - int hs_phy_irq_index; + int qusb2_phy_irq_index; int dp_hs_phy_irq_index; int dm_hs_phy_irq_index; int ss_phy_irq_index; @@ -73,7 +73,7 @@ struct dwc3_qcom { int num_clocks; struct reset_control *resets; - int hs_phy_irq; + int qusb2_phy_irq; int dp_hs_phy_irq; int dm_hs_phy_irq; int ss_phy_irq; @@ -372,7 +372,7 @@ static void dwc3_qcom_disable_wakeup_irq(int irq) static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) { - dwc3_qcom_disable_wakeup_irq(qcom->hs_phy_irq); + dwc3_qcom_disable_wakeup_irq(qcom->qusb2_phy_irq); if (qcom->usb2_speed == USB_SPEED_LOW) { dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq); @@ -389,7 +389,7 @@ static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom) { - dwc3_qcom_enable_wakeup_irq(qcom->hs_phy_irq, 0); + dwc3_qcom_enable_wakeup_irq(qcom->qusb2_phy_irq, 0); /* * Configure DP/DM line interrupts based on the USB2 device attached to @@ -542,19 +542,19 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) int irq; int ret; - irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq", - pdata ? pdata->hs_phy_irq_index : -1); + irq = dwc3_qcom_get_irq(pdev, "qusb2_phy", + pdata ? pdata->qusb2_phy_irq_index : -1); if (irq > 0) { /* Keep wakeup interrupts disabled until suspend */ ret = devm_request_threaded_irq(qcom->dev, irq, NULL, qcom_dwc3_resume_irq, IRQF_ONESHOT | IRQF_NO_AUTOEN, - "qcom_dwc3 HS", qcom); + "qcom_dwc3 QUSB2", qcom); if (ret) { - dev_err(qcom->dev, "hs_phy_irq failed: %d\n", ret); + dev_err(qcom->dev, "qusb2_phy_irq failed: %d\n", ret); return ret; } - qcom->hs_phy_irq = irq; + qcom->qusb2_phy_irq = irq; } irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq", @@ -1058,7 +1058,7 @@ static const struct dwc3_acpi_pdata sdm845_acpi_pdata = { .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, .qscratch_base_size = SDM845_QSCRATCH_SIZE, .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, - .hs_phy_irq_index = 1, + .qusb2_phy_irq_index = 1, .dp_hs_phy_irq_index = 4, .dm_hs_phy_irq_index = 3, .ss_phy_irq_index = 2 @@ -1068,7 +1068,7 @@ static const struct dwc3_acpi_pdata sdm845_acpi_urs_pdata = { .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, .qscratch_base_size = SDM845_QSCRATCH_SIZE, .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, - .hs_phy_irq_index = 1, + .qusb2_phy_irq_index = 1, .dp_hs_phy_irq_index = 4, .dm_hs_phy_irq_index = 3, .ss_phy_irq_index = 2, From 7db25e95589e7a8871de2c5dad88eba5f432e2dd Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 3 Jan 2024 21:15:31 +0100 Subject: [PATCH 148/169] dt-bindings: usb: qcom,dwc3: Fix SDM660 clock description SDM660 was abusingly referencing one of the internal bus clocks, that were recently dropped from Linux (because the original implementation did not make much sense), circumventing the interconnect framework. Drop it. Signed-off-by: Konrad Dybcio Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230721-topic-rpm_clk_cleanup-v3-1-a66e698932e3@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/qcom,dwc3.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index 8242e22fde36..63d150b216c5 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -308,8 +308,8 @@ allOf: then: properties: clocks: - minItems: 5 - maxItems: 6 + minItems: 4 + maxItems: 5 clock-names: oneOf: - items: @@ -318,13 +318,11 @@ allOf: - const: iface - const: sleep - const: mock_utmi - - const: bus - items: - const: cfg_noc - const: core - const: sleep - const: mock_utmi - - const: bus - if: properties: From 51b6148026f043cd3ec222dbe5efa82103a3e3c9 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 3 Jan 2024 21:15:32 +0100 Subject: [PATCH 149/169] arm64: dts: qcom: msm8916: Drop RPM bus clocks Some nodes are abusingly referencing some of the internal bus clocks, that were recently removed in Linux (because the original implementation did not make much sense), managing them as if they were the only devices on an NoC bus. These clocks are now handled from within the icc framework and are no longer registered from within the CCF. Remove them. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230721-topic-rpm_clk_cleanup-v3-2-a66e698932e3@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/msm8916.dtsi | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 4f799b536a92..f7e00276b15a 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -539,9 +539,6 @@ compatible = "qcom,msm8916-bimc"; reg = <0x00400000 0x62000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_BIMC_CLK>, - <&rpmcc RPM_SMD_BIMC_A_CLK>; }; tsens: thermal-sensor@4a9000 { @@ -574,18 +571,12 @@ compatible = "qcom,msm8916-pcnoc"; reg = <0x00500000 0x11000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_PCNOC_CLK>, - <&rpmcc RPM_SMD_PCNOC_A_CLK>; }; snoc: interconnect@580000 { compatible = "qcom,msm8916-snoc"; reg = <0x00580000 0x14000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_SNOC_CLK>, - <&rpmcc RPM_SMD_SNOC_A_CLK>; }; stm: stm@802000 { From 19f837ae4e4ac4ebdec861038bffff9781100a9a Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 3 Jan 2024 21:15:33 +0100 Subject: [PATCH 150/169] arm64: dts: qcom: msm8996: Drop RPM bus clocks Some nodes are abusingly referencing some of the internal bus clocks, that were recently removed in Linux (because the original implementation did not make much sense), managing them as if they were the only devices on an NoC bus. These clocks are now handled from within the icc framework and are no longer registered from within the CCF. Remove them. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230721-topic-rpm_clk_cleanup-v3-3-a66e698932e3@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/msm8996.dtsi | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index 6ba9da9e6a8b..587e9b50a256 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -799,9 +799,6 @@ compatible = "qcom,msm8996-bimc"; reg = <0x00408000 0x5a000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_BIMC_CLK>, - <&rpmcc RPM_SMD_BIMC_A_CLK>; }; tsens0: thermal-sensor@4a9000 { @@ -852,18 +849,12 @@ compatible = "qcom,msm8996-cnoc"; reg = <0x00500000 0x1000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_CNOC_CLK>, - <&rpmcc RPM_SMD_CNOC_A_CLK>; }; snoc: interconnect@524000 { compatible = "qcom,msm8996-snoc"; reg = <0x00524000 0x1c000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_SNOC_CLK>, - <&rpmcc RPM_SMD_SNOC_A_CLK>; }; a0noc: interconnect@543000 { @@ -883,19 +874,14 @@ compatible = "qcom,msm8996-a1noc"; reg = <0x00562000 0x5000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_AGGR1_NOC_CLK>, - <&rpmcc RPM_SMD_AGGR1_NOC_A_CLK>; }; a2noc: interconnect@583000 { compatible = "qcom,msm8996-a2noc"; reg = <0x00583000 0x7000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a", "aggre2_ufs_axi", "ufs_axi"; - clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>, - <&rpmcc RPM_SMD_AGGR2_NOC_A_CLK>, - <&gcc GCC_AGGRE2_UFS_AXI_CLK>, + clock-names = "aggre2_ufs_axi", "ufs_axi"; + clocks = <&gcc GCC_AGGRE2_UFS_AXI_CLK>, <&gcc GCC_UFS_AXI_CLK>; }; @@ -903,19 +889,14 @@ compatible = "qcom,msm8996-mnoc"; reg = <0x005a4000 0x1c000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a", "iface"; - clocks = <&rpmcc RPM_SMD_MMAXI_CLK>, - <&rpmcc RPM_SMD_MMAXI_A_CLK>, - <&mmcc AHB_CLK_SRC>; + clock-names = "iface"; + clocks = <&mmcc AHB_CLK_SRC>; }; pnoc: interconnect@5c0000 { compatible = "qcom,msm8996-pnoc"; reg = <0x005c0000 0x3000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_PCNOC_CLK>, - <&rpmcc RPM_SMD_PCNOC_A_CLK>; }; tcsr_mutex: hwlock@740000 { From 437afcefdbec8972598f6a029a7ed4cb8f52dfd0 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 3 Jan 2024 21:15:34 +0100 Subject: [PATCH 151/169] arm64: dts: qcom: qcs404: Drop RPM bus clocks Some nodes are abusingly referencing some of the internal bus clocks, that were recently removed in Linux (because the original implementation did not make much sense), managing them as if they were the only devices on an NoC bus. These clocks are now handled from within the icc framework and are no longer registered from within the CCF. Remove them. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230721-topic-rpm_clk_cleanup-v3-4-a66e698932e3@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/qcs404.dtsi | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/qcs404.dtsi b/arch/arm64/boot/dts/qcom/qcs404.dtsi index 2721f32dfb71..317a0df30b83 100644 --- a/arch/arm64/boot/dts/qcom/qcs404.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs404.dtsi @@ -558,9 +558,6 @@ reg = <0x00400000 0x80000>; compatible = "qcom,qcs404-bimc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_BIMC_CLK>, - <&rpmcc RPM_SMD_BIMC_A_CLK>; }; tsens: thermal-sensor@4a9000 { @@ -601,18 +598,12 @@ reg = <0x00500000 0x15080>; compatible = "qcom,qcs404-pcnoc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_PNOC_CLK>, - <&rpmcc RPM_SMD_PNOC_A_CLK>; }; snoc: interconnect@580000 { reg = <0x00580000 0x23080>; compatible = "qcom,qcs404-snoc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_SNOC_CLK>, - <&rpmcc RPM_SMD_SNOC_A_CLK>; }; remoteproc_cdsp: remoteproc@b00000 { From 283730be6bbb7ec6a45ba6b9c7db5feeaad93f63 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 3 Jan 2024 21:15:35 +0100 Subject: [PATCH 152/169] arm64: dts: qcom: sdm630: Drop RPM bus clocks Some nodes are abusingly referencing some of the internal bus clocks, that were recently removed in Linux (because the original implementation did not make much sense), managing them as if they were the only devices on an NoC bus. These clocks are now handled from within the icc framework and are no longer registered from within the CCF. Remove them. Reviewed-by: Marijn Suijten Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230721-topic-rpm_clk_cleanup-v3-5-a66e698932e3@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 53 +++++++--------------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 775700f78e0f..513fe5e76b68 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -606,9 +606,6 @@ compatible = "qcom,sdm660-bimc"; reg = <0x01008000 0x78000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_BIMC_CLK>, - <&rpmcc RPM_SMD_BIMC_A_CLK>; }; restart@10ac000 { @@ -620,28 +617,17 @@ compatible = "qcom,sdm660-cnoc"; reg = <0x01500000 0x10000>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_CNOC_CLK>, - <&rpmcc RPM_SMD_CNOC_A_CLK>; }; snoc: interconnect@1626000 { compatible = "qcom,sdm660-snoc"; reg = <0x01626000 0x7090>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_SNOC_CLK>, - <&rpmcc RPM_SMD_SNOC_A_CLK>; }; anoc2_smmu: iommu@16c0000 { compatible = "qcom,sdm630-smmu-v2", "qcom,smmu-v2"; reg = <0x016c0000 0x40000>; - - assigned-clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>; - assigned-clock-rates = <1000>; - clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>; - clock-names = "bus"; #global-interrupts = <2>; #iommu-cells = <1>; @@ -686,16 +672,12 @@ compatible = "qcom,sdm660-a2noc"; reg = <0x01704000 0xc100>; #interconnect-cells = <1>; - clock-names = "bus", - "bus_a", - "ipa", + clock-names = "ipa", "ufs_axi", "aggre2_ufs_axi", "aggre2_usb3_axi", "cfg_noc_usb2_axi"; - clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>, - <&rpmcc RPM_SMD_AGGR2_NOC_A_CLK>, - <&rpmcc RPM_SMD_IPA_CLK>, + clocks = <&rpmcc RPM_SMD_IPA_CLK>, <&gcc GCC_UFS_AXI_CLK>, <&gcc GCC_AGGRE2_UFS_AXI_CLK>, <&gcc GCC_AGGRE2_USB3_AXI_CLK>, @@ -706,10 +688,8 @@ compatible = "qcom,sdm660-mnoc"; reg = <0x01745000 0xa010>; #interconnect-cells = <1>; - clock-names = "bus", "bus_a", "iface"; - clocks = <&rpmcc RPM_SMD_MMSSNOC_AXI_CLK>, - <&rpmcc RPM_SMD_MMSSNOC_AXI_CLK_A>, - <&mmcc AHB_CLK_SRC>; + clock-names = "iface"; + clocks = <&mmcc AHB_CLK_SRC>; }; tsens: thermal-sensor@10ae000 { @@ -1186,7 +1166,9 @@ clocks = <&gcc GCC_GPU_CFG_AHB_CLK>, <&gcc GCC_BIMC_GFX_CLK>, <&gcc GCC_GPU_BIMC_GFX_CLK>; - clock-names = "iface", "mem", "mem_iface"; + clock-names = "iface", + "mem", + "mem_iface"; #global-interrupts = <2>; #iommu-cells = <1>; @@ -1288,20 +1270,16 @@ <&gcc GCC_USB30_MASTER_CLK>, <&gcc GCC_AGGRE2_USB3_AXI_CLK>, <&gcc GCC_USB30_SLEEP_CLK>, - <&gcc GCC_USB30_MOCK_UTMI_CLK>, - <&rpmcc RPM_SMD_AGGR2_NOC_CLK>; + <&gcc GCC_USB30_MOCK_UTMI_CLK>; clock-names = "cfg_noc", "core", "iface", "sleep", - "mock_utmi", - "bus"; + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_MASTER_CLK>, - <&rpmcc RPM_SMD_AGGR2_NOC_CLK>; - assigned-clock-rates = <19200000>, <120000000>, - <19200000>; + <&gcc GCC_USB30_MASTER_CLK>; + assigned-clock-rates = <19200000>, <120000000>; interrupts = , ; @@ -2204,10 +2182,9 @@ clocks = <&mmcc MNOC_AHB_CLK>, <&mmcc BIMC_SMMU_AHB_CLK>, - <&rpmcc RPM_SMD_MMSSNOC_AXI_CLK>, <&mmcc BIMC_SMMU_AXI_CLK>; clock-names = "iface-mm", "iface-smmu", - "bus-mm", "bus-smmu"; + "bus-smmu"; #global-interrupts = <2>; #iommu-cells = <1>; @@ -2324,12 +2301,6 @@ compatible = "qcom,sdm660-gnoc"; reg = <0x17900000 0xe000>; #interconnect-cells = <1>; - /* - * This one apparently features no clocks, - * so let's not mess with the driver needlessly - */ - clock-names = "bus", "bus_a"; - clocks = <&xo_board>, <&xo_board>; }; apcs_glb: mailbox@17911000 { From 09ee216d0b9f63f44fa59651a9e7403c8fd05ea8 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 3 Jan 2024 21:15:36 +0100 Subject: [PATCH 153/169] arm64: dts: qcom: msm8939: Drop RPM bus clocks Some nodes are abusingly referencing some of the internal bus clocks, that were recently removed in Linux (because the original implementation did not make much sense), managing them as if they were the only devices on an NoC bus. These clocks are now handled from within the icc framework and are no longer registered from within the CCF. Remove them. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230721-topic-rpm_clk_cleanup-v3-6-a66e698932e3@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/msm8939.dtsi | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8939.dtsi b/arch/arm64/boot/dts/qcom/msm8939.dtsi index 324b5d26db40..b2e1f2f6c620 100644 --- a/arch/arm64/boot/dts/qcom/msm8939.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8939.dtsi @@ -601,9 +601,6 @@ bimc: interconnect@400000 { compatible = "qcom,msm8939-bimc"; reg = <0x00400000 0x62000>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_BIMC_CLK>, - <&rpmcc RPM_SMD_BIMC_A_CLK>; #interconnect-cells = <1>; }; @@ -647,25 +644,16 @@ pcnoc: interconnect@500000 { compatible = "qcom,msm8939-pcnoc"; reg = <0x00500000 0x11000>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_PCNOC_CLK>, - <&rpmcc RPM_SMD_PCNOC_A_CLK>; #interconnect-cells = <1>; }; snoc: interconnect@580000 { compatible = "qcom,msm8939-snoc"; reg = <0x00580000 0x14080>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_SNOC_CLK>, - <&rpmcc RPM_SMD_SNOC_A_CLK>; #interconnect-cells = <1>; snoc_mm: interconnect-snoc { compatible = "qcom,msm8939-snoc-mm"; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_SYSMMNOC_CLK>, - <&rpmcc RPM_SMD_SYSMMNOC_A_CLK>; #interconnect-cells = <1>; }; }; From 8ee8587fdcac12c8c73d1e8797b2eb9abe5cdf18 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 3 Jan 2024 21:15:37 +0100 Subject: [PATCH 154/169] arm64: dts: qcom: msm8998: Remove AGGRE2 clock from SLPI The AGGRE2 clock is a clock for the entire AGGRE2 bus, managed from within the interconnect driver. Attaching it to SLPI was a total hack. Get rid of it. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230721-topic-rpm_clk_cleanup-v3-7-a66e698932e3@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index b485bf925ce6..dd676d1b038d 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -1615,9 +1615,8 @@ px-supply = <&vreg_lvs2a_1p8>; - clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>, - <&rpmcc RPM_SMD_AGGR2_NOC_CLK>; - clock-names = "xo", "aggre2"; + clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>; + clock-names = "xo"; memory-region = <&slpi_mem>; From 30d2641df650e38aa5f8687a666bac036bc204dd Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 3 Jan 2024 21:15:38 +0100 Subject: [PATCH 155/169] arm64: dts: qcom: msm8996: Remove AGGRE2 clock from SLPI The AGGRE2 clock is a clock for the entire AGGRE2 bus, managed from within the interconnect driver. Attaching it to SLPI was a total hack. Get rid of it. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230721-topic-rpm_clk_cleanup-v3-8-a66e698932e3@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/msm8996.dtsi | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index 587e9b50a256..b292cace93a6 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -2434,9 +2434,8 @@ "handover", "stop-ack"; - clocks = <&xo_board>, - <&rpmcc RPM_SMD_AGGR2_NOC_CLK>; - clock-names = "xo", "aggre2"; + clocks = <&xo_board>; + clock-names = "xo"; memory-region = <&slpi_mem>; From 3eee5b4085e223339cfcadadc4aab6199c1f09ec Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 3 Jan 2024 21:15:39 +0100 Subject: [PATCH 156/169] arm64: dts: qcom: msm8996: Remove PNoC clock from MSS The PNoC clock is a clock for the entire PNoC bus, managed from within the interconnect driver. Attaching it to MSS was a total hack. Get rid of it and take the liberty to make the clock-names entries more readable. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230721-topic-rpm_clk_cleanup-v3-9-a66e698932e3@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/msm8996.dtsi | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index b292cace93a6..65cb3913b619 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -2480,10 +2480,15 @@ <&gcc GCC_MSS_GPLL0_DIV_CLK>, <&gcc GCC_MSS_SNOC_AXI_CLK>, <&gcc GCC_MSS_MNOC_BIMC_AXI_CLK>, - <&rpmcc RPM_SMD_PCNOC_CLK>, <&rpmcc RPM_SMD_QDSS_CLK>; - clock-names = "iface", "bus", "mem", "xo", "gpll0_mss", - "snoc_axi", "mnoc_axi", "pnoc", "qdss"; + clock-names = "iface", + "bus", + "mem", + "xo", + "gpll0_mss", + "snoc_axi", + "mnoc_axi", + "qdss"; resets = <&gcc GCC_MSS_RESTART>; reset-names = "mss_restart"; From 223b4ef5a3b75c4500facf07f99a925232eaf113 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 4 Jan 2024 14:16:38 +0800 Subject: [PATCH 157/169] dt-bindings: usb: mtk-xhci: add a property for Gen1 isoc-in transfer issue For Gen1 isoc-in endpoint on controller before about SSUSB IPM v1.6.0, it still send out unexpected ACK after receiving a short packet in burst transfer, this will cause an exception on connected device, specially for a 4k camera. Add a quirk property "rx-fifo-depth" to work around this hardware issue, prefer to use 3k bytes; The side-effect is that it may cause performance drop about 10%, including bulk transfer. Acked-by: Krzysztof Kozlowski Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/20240104061640.7335-1-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/mediatek,mtk-xhci.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml index e9644e333d78..924fd3d748a8 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml @@ -124,6 +124,17 @@ properties: defined in the xHCI spec on MTK's controller. default: 5000 + rx-fifo-depth: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + It is a quirk used to work around Gen1 isoc-in endpoint transfer issue + that still send out unexpected ACK after device finishes the burst + transfer with a short packet and cause an exception, specially on a 4K + camera device, it happens on controller before about IPM v1.6.0; + the side-effect is that it may cause performance drop about 10%, + including bulk transfer, prefer to use 3k here. The size is in bytes. + enum: [1024, 2048, 3072, 4096] + # the following properties are only used for case 1 wakeup-source: description: enable USB remote wakeup, see power/wakeup-source.txt From 017dbfc05c31284150819890b4cc86a699cbdb71 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 4 Jan 2024 14:16:39 +0800 Subject: [PATCH 158/169] usb: xhci-mtk: fix a short packet issue of gen1 isoc-in transfer For Gen1 isoc-in transfer, host still send out unexpected ACK after device finish the burst with a short packet, this will cause an exception on the connected device, such as, a usb 4k camera. It can be fixed by setting rxfifo depth less than 4k bytes, prefer to use 3k here, the side-effect is that may cause performance drop about 10%, including bulk transfer. Fixes: 926d60ae64a6 ("usb: xhci-mtk: modify the SOF/ITP interval for mt8195") Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/20240104061640.7335-2-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mtk.c | 40 +++++++++++++++++++++++++++++++++++-- drivers/usb/host/xhci-mtk.h | 2 ++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index bbdf1b0b7be1..3252e3d2d79c 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -7,6 +7,7 @@ * Chunfeng Yun */ +#include #include #include #include @@ -73,6 +74,9 @@ #define FRMCNT_LEV1_RANG (0x12b << 8) #define FRMCNT_LEV1_RANG_MASK GENMASK(19, 8) +#define HSCH_CFG1 0x960 +#define SCH3_RXFIFO_DEPTH_MASK GENMASK(21, 20) + #define SS_GEN2_EOF_CFG 0x990 #define SSG2EOF_OFFSET 0x3c @@ -114,6 +118,8 @@ #define SSC_IP_SLEEP_EN BIT(4) #define SSC_SPM_INT_EN BIT(1) +#define SCH_FIFO_TO_KB(x) ((x) >> 10) + enum ssusb_uwk_vers { SSUSB_UWK_V1 = 1, SSUSB_UWK_V2, @@ -165,6 +171,35 @@ static void xhci_mtk_set_frame_interval(struct xhci_hcd_mtk *mtk) writel(value, hcd->regs + SS_GEN2_EOF_CFG); } +/* + * workaround: usb3.2 gen1 isoc rx hw issue + * host send out unexpected ACK afer device fininsh a burst transfer with + * a short packet. + */ +static void xhci_mtk_rxfifo_depth_set(struct xhci_hcd_mtk *mtk) +{ + struct usb_hcd *hcd = mtk->hcd; + u32 value; + + if (!mtk->rxfifo_depth) + return; + + value = readl(hcd->regs + HSCH_CFG1); + value &= ~SCH3_RXFIFO_DEPTH_MASK; + value |= FIELD_PREP(SCH3_RXFIFO_DEPTH_MASK, + SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1); + writel(value, hcd->regs + HSCH_CFG1); +} + +static void xhci_mtk_init_quirk(struct xhci_hcd_mtk *mtk) +{ + /* workaround only for mt8195 */ + xhci_mtk_set_frame_interval(mtk); + + /* workaround for SoCs using SSUSB about before IPM v1.6.0 */ + xhci_mtk_rxfifo_depth_set(mtk); +} + static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk) { struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs; @@ -448,8 +483,7 @@ static int xhci_mtk_setup(struct usb_hcd *hcd) if (ret) return ret; - /* workaround only for mt8195 */ - xhci_mtk_set_frame_interval(mtk); + xhci_mtk_init_quirk(mtk); } ret = xhci_gen_setup(hcd, xhci_mtk_quirks); @@ -527,6 +561,8 @@ static int xhci_mtk_probe(struct platform_device *pdev) of_property_read_u32(node, "mediatek,u2p-dis-msk", &mtk->u2p_dis_msk); + of_property_read_u32(node, "rx-fifo-depth", &mtk->rxfifo_depth); + ret = usb_wakeup_of_property_parse(mtk, node); if (ret) { dev_err(dev, "failed to parse uwk property\n"); diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 39f7ae7d3087..f5e2bd66bb1b 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -171,6 +171,8 @@ struct xhci_hcd_mtk { struct regmap *uwk; u32 uwk_reg_base; u32 uwk_vers; + /* quirk */ + u32 rxfifo_depth; }; static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd) From 33d4137d57997476404bc968fcc5b2ed5639a22f Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 4 Jan 2024 14:16:40 +0800 Subject: [PATCH 159/169] arm64: dts: mediatek: mt8195: Add 'rx-fifo-depth' for cherry Add the quirk property "rx-fifo-depth" to work around Gen1 isoc-in transfer issue which send out unexpected ACK even after device already finished the burst transfer with a short patcket, specially for a 4K camera device. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/20240104061640.7335-3-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi b/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi index 5a7cab489ff3..d416fd3f2917 100644 --- a/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi @@ -1183,6 +1183,7 @@ &xhci0 { status = "okay"; + rx-fifo-depth = <3072>; vusb33-supply = <&mt6359_vusb_ldo_reg>; vbus-supply = <&usb_vbus>; }; @@ -1190,6 +1191,7 @@ &xhci1 { status = "okay"; + rx-fifo-depth = <3072>; vusb33-supply = <&mt6359_vusb_ldo_reg>; vbus-supply = <&usb_vbus>; }; From 49a78b05d5ca1e23fd737747a8757b8bdc319b30 Mon Sep 17 00:00:00 2001 From: Yajun Deng Date: Thu, 4 Jan 2024 11:28:22 +0800 Subject: [PATCH 160/169] USB: core: Use device_driver directly in struct usb_driver and usb_device_driver There is usbdrv_wrap in struct usb_driver and usb_device_driver, it contains device_driver and for_devices. for_devices is used to distinguish between device drivers and interface drivers. Like the is_usb_device(), it tests the type of the device. We can test that if the probe of device_driver is equal to usb_probe_device in is_usb_device_driver(), and then the struct usbdrv_wrap is no longer needed. Clean up struct usbdrv_wrap, use device_driver directly in struct usb_driver and usb_device_driver. This makes the code cleaner. Signed-off-by: Yajun Deng Acked-by: Alan Stern Link: https://lore.kernel.org/r/20240104032822.1896596-1-yajun.deng@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 6 +- drivers/net/can/usb/peak_usb/pcan_usb_core.c | 2 +- .../broadcom/brcm80211/brcmfmac/usb.c | 2 +- drivers/net/wireless/marvell/mwifiex/usb.c | 2 +- drivers/usb/core/driver.c | 59 ++++++++++--------- drivers/usb/core/usb.c | 2 +- drivers/usb/core/usb.h | 8 +-- drivers/usb/misc/onboard_usb_hub.c | 2 +- drivers/usb/serial/bus.c | 2 +- drivers/usb/serial/usb-serial.c | 2 +- drivers/usb/storage/uas.c | 2 +- drivers/usb/usbip/stub_main.c | 8 +-- include/linux/usb.h | 24 +++----- 13 files changed, 53 insertions(+), 68 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b8e9de887b5d..02b3b1825403 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4790,10 +4790,8 @@ static struct usb_driver btusb_driver = { .disable_hub_initiated_lpm = 1, #ifdef CONFIG_DEV_COREDUMP - .drvwrap = { - .driver = { - .coredump = btusb_coredump, - }, + .driver = { + .coredump = btusb_coredump, }, #endif }; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index 24ad9f593a77..1efa39e134f4 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -1143,7 +1143,7 @@ static void __exit peak_usb_exit(void) int err; /* last chance do send any synchronous commands here */ - err = driver_for_each_device(&peak_usb_driver.drvwrap.driver, NULL, + err = driver_for_each_device(&peak_usb_driver.driver, NULL, NULL, peak_usb_do_device_exit); if (err) pr_err("%s: failed to stop all can devices (err %d)\n", diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 2178675ae1a4..0ccf735316c2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1581,7 +1581,7 @@ static int brcmf_usb_reset_device(struct device *dev, void *notused) void brcmf_usb_exit(void) { - struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver; + struct device_driver *drv = &brcmf_usbdrvr.driver; int ret; brcmf_dbg(USB, "Enter\n"); diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index d3ab9572e711..515e6db410f2 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -687,7 +687,7 @@ static struct usb_driver mwifiex_usb_driver = { .suspend = mwifiex_usb_suspend, .resume = mwifiex_usb_resume, .soft_unbind = 1, - .drvwrap.driver = { + .driver = { .coredump = mwifiex_usb_coredump, }, }; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 1dc0c0413043..e01b1913d02b 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -189,13 +189,13 @@ static int usb_create_newid_files(struct usb_driver *usb_drv) goto exit; if (usb_drv->probe != NULL) { - error = driver_create_file(&usb_drv->drvwrap.driver, + error = driver_create_file(&usb_drv->driver, &driver_attr_new_id); if (error == 0) { - error = driver_create_file(&usb_drv->drvwrap.driver, + error = driver_create_file(&usb_drv->driver, &driver_attr_remove_id); if (error) - driver_remove_file(&usb_drv->drvwrap.driver, + driver_remove_file(&usb_drv->driver, &driver_attr_new_id); } } @@ -209,9 +209,9 @@ static void usb_remove_newid_files(struct usb_driver *usb_drv) return; if (usb_drv->probe != NULL) { - driver_remove_file(&usb_drv->drvwrap.driver, + driver_remove_file(&usb_drv->driver, &driver_attr_remove_id); - driver_remove_file(&usb_drv->drvwrap.driver, + driver_remove_file(&usb_drv->driver, &driver_attr_new_id); } } @@ -552,7 +552,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (!iface->authorized) return -ENODEV; - dev->driver = &driver->drvwrap.driver; + dev->driver = &driver->driver; usb_set_intfdata(iface, data); iface->needs_binding = 0; @@ -615,7 +615,7 @@ void usb_driver_release_interface(struct usb_driver *driver, struct device *dev = &iface->dev; /* this should never happen, don't release something that's not ours */ - if (!dev->driver || dev->driver != &driver->drvwrap.driver) + if (!dev->driver || dev->driver != &driver->driver) return; /* don't release from within disconnect() */ @@ -950,7 +950,7 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data) int ret; /* Don't reprobe if current driver isn't usb_generic_driver */ - if (dev->driver != &usb_generic_driver.drvwrap.driver) + if (dev->driver != &usb_generic_driver.driver) return 0; udev = to_usb_device(dev); @@ -964,6 +964,11 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data) return 0; } +bool is_usb_device_driver(const struct device_driver *drv) +{ + return drv->probe == usb_probe_device; +} + /** * usb_register_device_driver - register a USB device (not interface) driver * @new_udriver: USB operations for the device driver @@ -983,15 +988,14 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver, if (usb_disabled()) return -ENODEV; - new_udriver->drvwrap.for_devices = 1; - new_udriver->drvwrap.driver.name = new_udriver->name; - new_udriver->drvwrap.driver.bus = &usb_bus_type; - new_udriver->drvwrap.driver.probe = usb_probe_device; - new_udriver->drvwrap.driver.remove = usb_unbind_device; - new_udriver->drvwrap.driver.owner = owner; - new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups; + new_udriver->driver.name = new_udriver->name; + new_udriver->driver.bus = &usb_bus_type; + new_udriver->driver.probe = usb_probe_device; + new_udriver->driver.remove = usb_unbind_device; + new_udriver->driver.owner = owner; + new_udriver->driver.dev_groups = new_udriver->dev_groups; - retval = driver_register(&new_udriver->drvwrap.driver); + retval = driver_register(&new_udriver->driver); if (!retval) { pr_info("%s: registered new device driver %s\n", @@ -1023,7 +1027,7 @@ void usb_deregister_device_driver(struct usb_device_driver *udriver) pr_info("%s: deregistering device driver %s\n", usbcore_name, udriver->name); - driver_unregister(&udriver->drvwrap.driver); + driver_unregister(&udriver->driver); } EXPORT_SYMBOL_GPL(usb_deregister_device_driver); @@ -1051,18 +1055,17 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, if (usb_disabled()) return -ENODEV; - new_driver->drvwrap.for_devices = 0; - new_driver->drvwrap.driver.name = new_driver->name; - new_driver->drvwrap.driver.bus = &usb_bus_type; - new_driver->drvwrap.driver.probe = usb_probe_interface; - new_driver->drvwrap.driver.remove = usb_unbind_interface; - new_driver->drvwrap.driver.owner = owner; - new_driver->drvwrap.driver.mod_name = mod_name; - new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups; + new_driver->driver.name = new_driver->name; + new_driver->driver.bus = &usb_bus_type; + new_driver->driver.probe = usb_probe_interface; + new_driver->driver.remove = usb_unbind_interface; + new_driver->driver.owner = owner; + new_driver->driver.mod_name = mod_name; + new_driver->driver.dev_groups = new_driver->dev_groups; spin_lock_init(&new_driver->dynids.lock); INIT_LIST_HEAD(&new_driver->dynids.list); - retval = driver_register(&new_driver->drvwrap.driver); + retval = driver_register(&new_driver->driver); if (retval) goto out; @@ -1077,7 +1080,7 @@ out: return retval; out_newid: - driver_unregister(&new_driver->drvwrap.driver); + driver_unregister(&new_driver->driver); pr_err("%s: error %d registering interface driver %s\n", usbcore_name, retval, new_driver->name); @@ -1102,7 +1105,7 @@ void usb_deregister(struct usb_driver *driver) usbcore_name, driver->name); usb_remove_newid_files(driver); - driver_unregister(&driver->drvwrap.driver); + driver_unregister(&driver->driver); usb_free_dynids(driver); } EXPORT_SYMBOL_GPL(usb_deregister); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 2a938cf47ccd..dc8d9228a5e7 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -431,7 +431,7 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) struct device *dev; argb.minor = minor; - argb.drv = &drv->drvwrap.driver; + argb.drv = &drv->driver; dev = bus_find_device(&usb_bus_type, NULL, &argb, __find_interface); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 60363153fc3f..bfecb50773b6 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -175,13 +175,7 @@ static inline int is_root_hub(struct usb_device *udev) return (udev->parent == NULL); } -/* Do the same for device drivers and interface drivers. */ - -static inline int is_usb_device_driver(struct device_driver *drv) -{ - return container_of(drv, struct usbdrv_wrap, driver)-> - for_devices; -} +extern bool is_usb_device_driver(const struct device_driver *drv); /* for labeling diagnostics */ extern const char *usbcore_name; diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index c09fb6bd7d7d..0dd2b032c90b 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -244,7 +244,7 @@ static void onboard_hub_attach_usb_driver(struct work_struct *work) { int err; - err = driver_attach(&onboard_hub_usbdev_driver.drvwrap.driver); + err = driver_attach(&onboard_hub_usbdev_driver.driver); if (err) pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err)); } diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 3eb8dc3a1a8f..6c812d01b37d 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -113,7 +113,7 @@ static ssize_t new_id_store(struct device_driver *driver, if (retval >= 0 && usb_drv->usb_driver != NULL) retval = usb_store_new_id(&usb_drv->usb_driver->dynids, usb_drv->usb_driver->id_table, - &usb_drv->usb_driver->drvwrap.driver, + &usb_drv->usb_driver->driver, buf, count); return retval; } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 17b09f03ef84..f1e91eb7f8a4 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1521,7 +1521,7 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[] /* Now set udriver's id_table and look for matches */ udriver->id_table = id_table; - rc = driver_attach(&udriver->drvwrap.driver); + rc = driver_attach(&udriver->driver); return 0; err_deregister_drivers: diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 696bb0b23599..9707f53cfda9 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1246,7 +1246,7 @@ static struct usb_driver uas_driver = { .suspend = uas_suspend, .resume = uas_resume, .reset_resume = uas_reset_resume, - .drvwrap.driver.shutdown = uas_shutdown, + .driver.shutdown = uas_shutdown, .id_table = uas_usb_ids, }; diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c index 0a6624d37929..79110a69d697 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -377,14 +377,14 @@ static int __init usbip_host_init(void) goto err_usb_register; } - ret = driver_create_file(&stub_driver.drvwrap.driver, + ret = driver_create_file(&stub_driver.driver, &driver_attr_match_busid); if (ret) { pr_err("driver_create_file failed\n"); goto err_create_file; } - ret = driver_create_file(&stub_driver.drvwrap.driver, + ret = driver_create_file(&stub_driver.driver, &driver_attr_rebind); if (ret) { pr_err("driver_create_file failed\n"); @@ -402,10 +402,10 @@ err_usb_register: static void __exit usbip_host_exit(void) { - driver_remove_file(&stub_driver.drvwrap.driver, + driver_remove_file(&stub_driver.driver, &driver_attr_match_busid); - driver_remove_file(&stub_driver.drvwrap.driver, + driver_remove_file(&stub_driver.driver, &driver_attr_rebind); /* diff --git a/include/linux/usb.h b/include/linux/usb.h index 07556341ba2b..9e52179872a5 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1143,16 +1143,6 @@ extern ssize_t usb_store_new_id(struct usb_dynids *dynids, extern ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf); -/** - * struct usbdrv_wrap - wrapper for driver-model structure - * @driver: The driver-model core driver structure. - * @for_devices: Non-zero for device drivers, 0 for interface drivers. - */ -struct usbdrv_wrap { - struct device_driver driver; - int for_devices; -}; - /** * struct usb_driver - identifies USB interface driver to usbcore * @name: The driver name should be unique among USB drivers, @@ -1193,7 +1183,7 @@ struct usbdrv_wrap { * is bound to the driver. * @dynids: used internally to hold the list of dynamically added device * ids for this driver. - * @drvwrap: Driver-model core structure wrapper. + * @driver: The driver-model core driver structure. * @no_dynamic_id: if set to 1, the USB core will not allow dynamic ids to be * added to this driver by preventing the sysfs file from being created. * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend @@ -1241,13 +1231,13 @@ struct usb_driver { const struct attribute_group **dev_groups; struct usb_dynids dynids; - struct usbdrv_wrap drvwrap; + struct device_driver driver; unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; unsigned int disable_hub_initiated_lpm:1; unsigned int soft_unbind:1; }; -#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver) +#define to_usb_driver(d) container_of(d, struct usb_driver, driver) /** * struct usb_device_driver - identifies USB device driver to usbcore @@ -1268,7 +1258,7 @@ struct usb_driver { * on to call the normal usb_choose_configuration(). * @dev_groups: Attributes attached to the device that will be created once it * is bound to the driver. - * @drvwrap: Driver-model core structure wrapper. + * @driver: The driver-model core driver structure. * @id_table: used with @match() to select better matching driver at * probe() time. * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend @@ -1277,7 +1267,7 @@ struct usb_driver { * resume and suspend functions will be called in addition to the driver's * own, so this part of the setup does not need to be replicated. * - * USB drivers must provide all the fields listed above except drvwrap, + * USB drivers must provide all the fields listed above except driver, * match, and id_table. */ struct usb_device_driver { @@ -1293,13 +1283,13 @@ struct usb_device_driver { int (*choose_configuration) (struct usb_device *udev); const struct attribute_group **dev_groups; - struct usbdrv_wrap drvwrap; + struct device_driver driver; const struct usb_device_id *id_table; unsigned int supports_autosuspend:1; unsigned int generic_subclass:1; }; #define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \ - drvwrap.driver) + driver) /** * struct usb_class_driver - identifies a USB driver that wants to use the USB major number From c99b38c412343053e9af187e595793c8805bb9b8 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 2 Jan 2024 13:45:09 -0800 Subject: [PATCH 161/169] xhci: add support to allocate several interrupters Modify the XHCI drivers to accommodate for handling multiple event rings in case there are multiple interrupters. Add the required APIs so clients are able to allocate/request for an interrupter ring, and pass this information back to the client driver. This allows for users to handle the resource accordingly, such as passing the event ring base address to an audio DSP. There is no actual support for multiple MSI/MSI-X vectors. [export xhci_initialize_ring_info() -wcheng] Signed-off-by: Mathias Nyman Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20240102214549.22498-2-quic_wcheng@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-mem.c | 108 ++++++++++++++++++++++++++++---- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.c | 51 +++++++++------ drivers/usb/host/xhci.h | 6 +- 5 files changed, 137 insertions(+), 32 deletions(-) diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 6d142cd61bd6..f8ba15e7c225 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -693,7 +693,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) "command-ring", xhci->debugfs_root); - xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->interrupters[0]->event_ring, "event-ring", xhci->debugfs_root); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 6faa854152ef..4460fa7e9fab 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -323,6 +323,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, */ ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1; } +EXPORT_SYMBOL_GPL(xhci_initialize_ring_info); /* Allocate segments and link them for a ring */ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, @@ -1855,6 +1856,31 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) kfree(ir); } +void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + unsigned int intr_num; + + /* interrupter 0 is primary interrupter, don't touch it */ + if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters) + xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n"); + + /* fixme, should we check xhci->interrupter[intr_num] == ir */ + /* fixme locking */ + + spin_lock_irq(&xhci->lock); + + intr_num = ir->intr_num; + + xhci_remove_interrupter(xhci, ir); + xhci->interrupters[intr_num] = NULL; + + spin_unlock_irq(&xhci->lock); + + xhci_free_interrupter(xhci, ir); +} +EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; @@ -1862,10 +1888,14 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_remove_interrupter(xhci, xhci->interrupter); - xhci_free_interrupter(xhci, xhci->interrupter); - xhci->interrupter = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); + for (i = 0; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i]) { + xhci_remove_interrupter(xhci, xhci->interrupters[i]); + xhci_free_interrupter(xhci, xhci->interrupters[i]); + xhci->interrupters[i] = NULL; + } + } + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed interrupters"); if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); @@ -1935,6 +1965,7 @@ no_bw: for (i = 0; i < xhci->num_port_caps; i++) kfree(xhci->port_caps[i].psi); kfree(xhci->port_caps); + kfree(xhci->interrupters); xhci->num_port_caps = 0; xhci->usb2_rhub.ports = NULL; @@ -1943,6 +1974,7 @@ no_bw: xhci->rh_bw = NULL; xhci->ext_caps = NULL; xhci->port_caps = NULL; + xhci->interrupters = NULL; xhci->page_size = 0; xhci->page_shift = 0; @@ -2248,18 +2280,20 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) } static struct xhci_interrupter * -xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags) +xhci_alloc_interrupter(struct xhci_hcd *xhci, int segs, gfp_t flags) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct xhci_interrupter *ir; - unsigned int num_segs; + unsigned int num_segs = segs; int ret; ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); if (!ir) return NULL; - num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2), + /* number of ring segments should be greater than 0 */ + if (segs <= 0) + num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2), ERST_MAX_SEGS); ir->event_ring = xhci_ring_alloc(xhci, num_segs, 1, TYPE_EVENT, 0, @@ -2294,6 +2328,13 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, return -EINVAL; } + if (xhci->interrupters[intr_num]) { + xhci_warn(xhci, "Interrupter %d\n already set up", intr_num); + return -EINVAL; + } + + xhci->interrupters[intr_num] = ir; + ir->intr_num = intr_num; ir->ir_set = &xhci->run_regs->ir_set[intr_num]; /* set ERST count with the number of entries in the segment table */ @@ -2313,10 +2354,52 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, return 0; } +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir; + unsigned int i; + int err = -ENOSPC; + + if (!xhci->interrupters || xhci->max_interrupters <= 1) + return NULL; + + ir = xhci_alloc_interrupter(xhci, num_seg, GFP_KERNEL); + if (!ir) + return NULL; + + spin_lock_irq(&xhci->lock); + + /* Find available secondary interrupter, interrupter 0 is reserved for primary */ + for (i = 1; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i] == NULL) { + err = xhci_add_interrupter(xhci, ir, i); + break; + } + } + + spin_unlock_irq(&xhci->lock); + + if (err) { + xhci_warn(xhci, "Failed to add secondary interrupter, max interrupters %d\n", + xhci->max_interrupters); + xhci_free_interrupter(xhci, ir); + return NULL; + } + + xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n", + i, xhci->max_interrupters); + + return ir; +} +EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { - dma_addr_t dma; + struct xhci_interrupter *ir; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + dma_addr_t dma; unsigned int val, val2; u64 val_64; u32 page_size, temp; @@ -2440,11 +2523,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* Allocate and set up primary interrupter 0 with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); - xhci->interrupter = xhci_alloc_interrupter(xhci, flags); - if (!xhci->interrupter) + xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters), + flags, dev_to_node(dev)); + + ir = xhci_alloc_interrupter(xhci, 0, flags); + if (!ir) goto fail; - if (xhci_add_interrupter(xhci, xhci->interrupter, 0)) + if (xhci_add_interrupter(xhci, ir, 0)) goto fail; xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 2c1d614b3b0f..33806ae966f9 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3061,7 +3061,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) writel(status, &xhci->op_regs->status); /* This is the handler of the primary interrupter */ - ir = xhci->interrupter; + ir = xhci->interrupters[0]; if (!hcd->msi_enabled) { u32 irq_pending; irq_pending = readl(&ir->ir_set->irq_pending); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7d5b94905b9c..c057c42c36f4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -480,7 +480,7 @@ static int xhci_init(struct usb_hcd *hcd) static int xhci_run_finished(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; unsigned long flags; u32 temp; @@ -532,7 +532,7 @@ int xhci_run(struct usb_hcd *hcd) u64 temp_64; int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. */ @@ -596,7 +596,7 @@ void xhci_stop(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; mutex_lock(&xhci->mutex); @@ -692,36 +692,51 @@ EXPORT_SYMBOL_GPL(xhci_shutdown); #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir; + unsigned int i; xhci->s3.command = readl(&xhci->op_regs->command); xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); - if (!ir) - return; + /* save both primary and all secondary interrupters */ + /* fixme, shold we lock to prevent race with remove secondary interrupter? */ + for (i = 0; i < xhci->max_interrupters; i++) { + ir = xhci->interrupters[i]; + if (!ir) + continue; - ir->s3_erst_size = readl(&ir->ir_set->erst_size); - ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); - ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); - ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); - ir->s3_irq_control = readl(&ir->ir_set->irq_control); + ir->s3_erst_size = readl(&ir->ir_set->erst_size); + ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); + ir->s3_irq_control = readl(&ir->ir_set->irq_control); + } } static void xhci_restore_registers(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir; + unsigned int i; writel(xhci->s3.command, &xhci->op_regs->command); writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); - writel(ir->s3_erst_size, &ir->ir_set->erst_size); - xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); - xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); - writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); - writel(ir->s3_irq_control, &ir->ir_set->irq_control); + + /* FIXME should we lock to protect against freeing of interrupters */ + for (i = 0; i < xhci->max_interrupters; i++) { + ir = xhci->interrupters[i]; + if (!ir) + continue; + + writel(ir->s3_erst_size, &ir->ir_set->erst_size); + xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); + xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); + writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); + writel(ir->s3_irq_control, &ir->ir_set->irq_control); + } } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) @@ -1084,7 +1099,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - xhci_disable_interrupter(xhci->interrupter); + xhci_disable_interrupter(xhci->interrupters[0]); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 90e6b6ef7bd2..a5c72a634e6a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1774,7 +1774,7 @@ struct xhci_hcd { struct reset_control *reset; /* data structures */ struct xhci_device_context_array *dcbaa; - struct xhci_interrupter *interrupter; + struct xhci_interrupter **interrupters; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; #define CMD_RING_STATE_RUNNING (1 << 0) @@ -2085,6 +2085,10 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type, gfp_t flags); void xhci_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg); +void xhci_remove_secondary_interrupter(struct usb_hcd + *hcd, struct xhci_interrupter *ir); /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); From b1541a80414d13b1da9b528b09e68a4a518ae8fe Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Tue, 2 Jan 2024 13:45:22 -0800 Subject: [PATCH 162/169] dt-bindings: usb: xhci: Add num-hc-interrupters definition Add the definition for how many interrupts the XHCI host controller should allocate. XHCI can potentially support up to 1024 interrupters, which implementations may want to limit. Reviewed-by: Rob Herring Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20240102214549.22498-15-quic_wcheng@quicinc.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-xhci.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.yaml b/Documentation/devicetree/bindings/usb/usb-xhci.yaml index 180a261c3e8f..4238ae896ef6 100644 --- a/Documentation/devicetree/bindings/usb/usb-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/usb-xhci.yaml @@ -29,6 +29,12 @@ properties: description: Interrupt moderation interval default: 5000 + num-hc-interrupters: + description: Maximum number of interrupters to allocate + $ref: /schemas/types.yaml#/definitions/uint16 + minimum: 1 + maximum: 1024 + additionalProperties: true examples: From 79c58ab241bea76c197d09091eb85b753d5d4416 Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Tue, 2 Jan 2024 13:45:23 -0800 Subject: [PATCH 163/169] dt-bindings: usb: dwc3: Limit num-hc-interrupters definition Ensure that the number of XHCI secondary interrupters defined for a DWC3 based implementation is limited to 8. XHCI in general can potentially support up to 1024 interrupters. Reviewed-by: Rob Herring Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20240102214549.22498-16-quic_wcheng@quicinc.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/snps,dwc3.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml index ee5af4b381b1..203a1eb66691 100644 --- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml @@ -432,6 +432,10 @@ properties: items: enum: [1, 4, 8, 16, 32, 64, 128, 256] + num-hc-interrupters: + maximum: 8 + default: 1 + port: $ref: /schemas/graph.yaml#/properties/port description: From 5962ded777d689cd8bf04454273e32228d7fb71f Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Wed, 3 Jan 2024 18:17:55 +0000 Subject: [PATCH 164/169] usb: typec: class: fix typec_altmode_put_partner to put plugs When typec_altmode_put_partner is called by a plug altmode upon release, the port altmode the plug belongs to will not remove its reference to the plug. The check to see if the altmode being released is a plug evaluates against the released altmode's partner instead of the calling altmode, so change adev in typec_altmode_put_partner to properly refer to the altmode being released. Because typec_altmode_set_partner calls get_device() on the port altmode, add partner_adev that points to the port altmode in typec_put_partner to call put_device() on. typec_altmode_set_partner is not called for port altmodes, so add a check in typec_altmode_release to prevent typec_altmode_put_partner() calls on port altmode release. Fixes: 8a37d87d72f0 ("usb: typec: Bus type for alternate modes") Cc: Co-developed-by: Christian A. Ehrhardt Signed-off-by: Christian A. Ehrhardt Signed-off-by: RD Babiera Tested-by: Christian A. Ehrhardt Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240103181754.2492492-2-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 4d11f2b536fa..015aa9253353 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -263,11 +263,13 @@ static void typec_altmode_put_partner(struct altmode *altmode) { struct altmode *partner = altmode->partner; struct typec_altmode *adev; + struct typec_altmode *partner_adev; if (!partner) return; - adev = &partner->adev; + adev = &altmode->adev; + partner_adev = &partner->adev; if (is_typec_plug(adev->dev.parent)) { struct typec_plug *plug = to_typec_plug(adev->dev.parent); @@ -276,7 +278,7 @@ static void typec_altmode_put_partner(struct altmode *altmode) } else { partner->partner = NULL; } - put_device(&adev->dev); + put_device(&partner_adev->dev); } /** @@ -497,7 +499,8 @@ static void typec_altmode_release(struct device *dev) { struct altmode *alt = to_altmode(to_typec_altmode(dev)); - typec_altmode_put_partner(alt); + if (!is_typec_port(dev->parent)) + typec_altmode_put_partner(alt); altmode_id_remove(alt->adev.dev.parent, alt->id); kfree(alt); From fe814b5b0f3042f1a583734497e726ee53783cc1 Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Thu, 4 Jan 2024 13:50:08 -0800 Subject: [PATCH 165/169] usb: gadget: uvc: Fix use are free during STREAMOFF There is a path that may lead to freed memory being referenced, causing kernel panics. The kernel panic has the following stack trace: Workqueue: uvcgadget uvcg_video_pump.c51fb85fece46625450f86adbf92c56c.cfi_jt pstate: 60c00085 (nZCv daIf +PAN +UAO -TCO BTYPE=--) pc : __list_del_entry_valid+0xc0/0xd4 lr : __list_del_entry_valid+0xc0/0xd4 Call trace: __list_del_entry_valid+0xc0/0xd4 uvc_video_free_request+0x60/0x98 uvcg_video_pump+0x1cc/0x204 process_one_work+0x21c/0x4b8 worker_thread+0x29c/0x574 kthread+0x158/0x1b0 ret_from_fork+0x10/0x30 The root cause is that uvcg_video_usb_req_queue frees the uvc_request if is_enabled is false and returns an error status. video_pump also frees the associated request if uvcg_video_usb_req_queue returns an error status, leading to double free and accessing garbage memory. To fix the issue, this patch removes freeing logic from uvcg_video_usb_req_queue, and lets the callers to the function handle queueing errors as they see fit. Fixes: 6acba0345b68 ("usb:gadget:uvc Do not use worker thread to pump isoc usb requests") Tested-by: Avichal Rakesh Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20240104215009.2252452-1-arakesh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc_video.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 98ba524c27f5..7f18dc471be3 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -276,10 +276,9 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video, bool is_bulk = video->max_payload_size; struct list_head *list = NULL; - if (!video->is_enabled) { - uvc_video_free_request(req->context, video->ep); + if (!video->is_enabled) return -ENODEV; - } + if (queue_to_ep) { struct uvc_request *ureq = req->context; /* @@ -464,8 +463,15 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) * and this thread for isoc endpoints. */ ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk); - if (ret < 0) + if (ret < 0) { + /* + * Endpoint error, but the stream is still enabled. + * Put request back in req_free for it to be cleaned + * up later. + */ uvcg_queue_cancel(queue, 0); + list_add_tail(&to_queue->list, &video->req_free); + } } else { uvc_video_free_request(ureq, ep); } From 9866dc4314c6c858e451933f965d64532aec00a9 Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Thu, 4 Jan 2024 13:50:09 -0800 Subject: [PATCH 166/169] usb: gadget: uvc: Remove nested locking When handling error status from uvcg_video_usb_req_queue, uvc_video_complete currently calls uvcg_queue_cancel with video->req_lock held. uvcg_queue_cancel internally locks queue->irqlock, which nests queue->irqlock inside video->req_lock. This isn't a functional bug at the moment, but does open up possibilities for ABBA deadlocks in the future. This patch fixes the accidental nesting by dropping video->req_lock before calling uvcg_queue_cancel. Fixes: 6acba0345b68 ("usb:gadget:uvc Do not use worker thread to pump isoc usb requests") Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20240104215009.2252452-2-arakesh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc_video.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 7f18dc471be3..dd3241fc6939 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -469,13 +469,15 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) * Put request back in req_free for it to be cleaned * up later. */ - uvcg_queue_cancel(queue, 0); list_add_tail(&to_queue->list, &video->req_free); } } else { uvc_video_free_request(ureq, ep); + ret = 0; } spin_unlock_irqrestore(&video->req_lock, flags); + if (ret < 0) + uvcg_queue_cancel(queue, 0); } static int From 2dd23cc4d0e6aa55cf9fb3b05f2f4165b01de81c Mon Sep 17 00:00:00 2001 From: Gui-Dong Han <2045gemini@gmail.com> Date: Fri, 5 Jan 2024 13:24:12 +0800 Subject: [PATCH 167/169] usb: mon: Fix atomicity violation in mon_bin_vma_fault In mon_bin_vma_fault(): offset = vmf->pgoff << PAGE_SHIFT; if (offset >= rp->b_size) return VM_FAULT_SIGBUS; chunk_idx = offset / CHUNK_SIZE; pageptr = rp->b_vec[chunk_idx].pg; The code is executed without holding any lock. In mon_bin_vma_close(): spin_lock_irqsave(&rp->b_lock, flags); rp->mmap_active--; spin_unlock_irqrestore(&rp->b_lock, flags); In mon_bin_ioctl(): spin_lock_irqsave(&rp->b_lock, flags); if (rp->mmap_active) { ... } else { ... kfree(rp->b_vec); rp->b_vec = vec; rp->b_size = size; ... } spin_unlock_irqrestore(&rp->b_lock, flags); Concurrent execution of mon_bin_vma_fault() with mon_bin_vma_close() and mon_bin_ioctl() could lead to atomicity violations. mon_bin_vma_fault() accesses rp->b_size and rp->b_vec without locking, risking array out-of-bounds access or use-after-free bugs due to possible modifications in mon_bin_ioctl(). This possible bug is found by an experimental static analysis tool developed by our team, BassCheck[1]. This tool analyzes the locking APIs to extract function pairs that can be concurrently executed, and then analyzes the instructions in the paired functions to identify possible concurrency bugs including data races and atomicity violations. The above possible bug is reported when our tool analyzes the source code of Linux 6.2. To address this issue, it is proposed to add a spin lock pair in mon_bin_vma_fault() to ensure atomicity. With this patch applied, our tool never reports the possible bug, with the kernel configuration allyesconfig for x86_64. Due to the lack of associated hardware, we cannot test the patch in runtime testing, and just verify it according to the code logic. [1] https://sites.google.com/view/basscheck/ Fixes: 19e6317d24c2 ("usb: mon: Fix a deadlock in usbmon between ...") Cc: Signed-off-by: Gui-Dong Han <2045gemini@gmail.com> Link: https://lore.kernel.org/r/20240105052412.9377-1-2045gemini@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 9ca9305243fe..4e30de4db1c0 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1250,14 +1250,19 @@ static vm_fault_t mon_bin_vma_fault(struct vm_fault *vmf) struct mon_reader_bin *rp = vmf->vma->vm_private_data; unsigned long offset, chunk_idx; struct page *pageptr; + unsigned long flags; + spin_lock_irqsave(&rp->b_lock, flags); offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= rp->b_size) + if (offset >= rp->b_size) { + spin_unlock_irqrestore(&rp->b_lock, flags); return VM_FAULT_SIGBUS; + } chunk_idx = offset / CHUNK_SIZE; pageptr = rp->b_vec[chunk_idx].pg; get_page(pageptr); vmf->page = pageptr; + spin_unlock_irqrestore(&rp->b_lock, flags); return 0; } From 1154e4304174e0ab4a90d5bae3fd5548aa343472 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Fri, 5 Jan 2024 14:36:54 +0530 Subject: [PATCH 168/169] usb: typec: tipd: Separate reset for TPS6598x Some platforms like SK-AM62, SK-AM62A cannot boot up to prompt if TPS6598x is cold-reset during unconditionally on probe failures by sending "GAID" sequence. The probe can fail initially because USB0 remote-endpoint may not be probed yet, which defines the usb-role-switch property. Fixes: d49f90822015 ("usb: typec: tipd: add init and reset functions to tipd_data") Closes: https://lore.kernel.org/linux-usb/vmngazj6si7xxss7txenezkcukqje2glhvvs7ipdcx3vjiqvlk@ohmmhhhlryws/ Signed-off-by: Jai Luthra Reviewed-by: Roger Quadros Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20240105-next-tps-fix-v1-1-158cabaec168@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index a956eb976906..8ba2aa05db51 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1223,11 +1223,16 @@ static int cd321x_reset(struct tps6598x *tps) return 0; } -static int tps6598x_reset(struct tps6598x *tps) +static int tps25750_reset(struct tps6598x *tps) { return tps6598x_exec_cmd_tmo(tps, "GAID", 0, NULL, 0, NULL, 2000, 0); } +static int tps6598x_reset(struct tps6598x *tps) +{ + return 0; +} + static int tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) { @@ -1545,7 +1550,7 @@ static const struct tipd_data tps25750_data = { .trace_status = trace_tps25750_status, .apply_patch = tps25750_apply_patch, .init = tps25750_init, - .reset = tps6598x_reset, + .reset = tps25750_reset, }; static const struct of_device_id tps6598x_of_match[] = { From 933bb7b878ddd0f8c094db45551a7daddf806e00 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 4 Jan 2024 18:07:12 +0100 Subject: [PATCH 169/169] usb: typec: tipd: fix use of device-specific init function The current implementation supports device-pecific callbacks for the init function with a function pointer. The patch that introduced this feature did not update one call to the tps25750 init function to turn it into a call with the new pointer in the resume function. Fixes: d49f90822015 ("usb: typec: tipd: add init and reset functions to tipd_data") Signed-off-by: Javier Carrasco Reviewed-by: Heikki Krogerus Suggested-by: Roger Quadros Link: https://lore.kernel.org/r/20240104-dev_spec_init-v1-1-1a57e7fd8cc8@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 8ba2aa05db51..0717cfcd9f8c 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1500,7 +1500,7 @@ static int __maybe_unused tps6598x_resume(struct device *dev) return ret; if (ret == TPS_MODE_PTCH) { - ret = tps25750_init(tps); + ret = tps->data->init(tps); if (ret) return ret; }