From 3055c92c7d4024d1fb540d0946e82c97235c8479 Mon Sep 17 00:00:00 2001 From: Matthias Maennich Date: Wed, 2 Oct 2019 12:03:12 +0100 Subject: usb-storage: SCSI glue: use dev_err instead of printk Follow common practice and retire printk(KERN_ERR ...) in favor of dev_err(). Cc: Alan Stern Cc: Greg Kroah-Hartman Cc: usb-storage@lists.one-eyed-alien.net Signed-off-by: Matthias Maennich Link: https://lore.kernel.org/r/20191002110312.75749-1-maennich@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 6737fab94959..4c0c247e4101 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -379,8 +379,8 @@ static int queuecommand_lck(struct scsi_cmnd *srb, /* check for state-transition errors */ if (us->srb != NULL) { - printk(KERN_ERR "usb-storage: Error in %s: us->srb = %p\n", - __func__, us->srb); + dev_err(&us->pusb_intf->dev, + "Error in %s: us->srb = %p\n", __func__, us->srb); return SCSI_MLQUEUE_HOST_BUSY; } -- cgit From 237b668c1c5da7e22c296ceb9daf772fbb5731f3 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:50:22 +0800 Subject: usb: gadget: s3c-hsudc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904095022.24528-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/s3c-hsudc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c index 858993c73442..21252fbc0319 100644 --- a/drivers/usb/gadget/udc/s3c-hsudc.c +++ b/drivers/usb/gadget/udc/s3c-hsudc.c @@ -1263,7 +1263,6 @@ static const struct usb_gadget_ops s3c_hsudc_gadget_ops = { static int s3c_hsudc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct s3c_hsudc *hsudc; struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev); int ret, i; @@ -1290,9 +1289,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev) goto err_supplies; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - hsudc->regs = devm_ioremap_resource(&pdev->dev, res); + hsudc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hsudc->regs)) { ret = PTR_ERR(hsudc->regs); goto err_res; -- cgit From 601acc7899566f597c2f1980c3cc751a35dc4281 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:48:36 +0800 Subject: usb: gadget: renesas_usb3: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904094836.18532-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/renesas_usb3.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index e098f16c01cb..922a19be1135 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2732,7 +2732,6 @@ static struct usb_role_switch_desc renesas_usb3_role_switch_desc = { static int renesas_usb3_probe(struct platform_device *pdev) { struct renesas_usb3 *usb3; - struct resource *res; int irq, ret; const struct renesas_usb3_priv *priv; const struct soc_device_attribute *attr; @@ -2751,8 +2750,7 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (!usb3) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - usb3->reg = devm_ioremap_resource(&pdev->dev, res); + usb3->reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(usb3->reg)) return PTR_ERR(usb3->reg); -- cgit From 3c60e959fa3543491f829ae52a4d70b58dea6f06 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:47:38 +0800 Subject: usb: gadget: r8a66597-udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904094738.7860-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/r8a66597-udc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 11e25a3f4f1f..582a16165ea9 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1838,7 +1838,7 @@ static int r8a66597_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; char clk_name[8]; - struct resource *res, *ires; + struct resource *ires; int irq; void __iomem *reg = NULL; struct r8a66597 *r8a66597 = NULL; @@ -1846,8 +1846,7 @@ static int r8a66597_probe(struct platform_device *pdev) int i; unsigned long irq_trigger; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg = devm_ioremap_resource(&pdev->dev, res); + reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(reg)) return PTR_ERR(reg); -- cgit From 9fd7a05e9776400f05fe07f81d3d71dc231d4fc3 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:45:57 +0800 Subject: usb: gadget: pxa27x_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904094557.22884-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/pxa27x_udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 014233252299..5f107f277f30 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -2356,7 +2356,6 @@ MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids); */ static int pxa_udc_probe(struct platform_device *pdev) { - struct resource *regs; struct pxa_udc *udc = &memory; int retval = 0, gpio; struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev); @@ -2378,8 +2377,7 @@ static int pxa_udc_probe(struct platform_device *pdev) udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->regs = devm_ioremap_resource(&pdev->dev, regs); + udc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->regs)) return PTR_ERR(udc->regs); udc->irq = platform_get_irq(pdev, 0); -- cgit From 80d59826d2d00d4e5751d26c4d743a9021d09b0f Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:42:22 +0800 Subject: usb: gadget: pxa25x_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904094222.23128-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/pxa25x_udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index d4be53559f2e..cfafdd92c2a8 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -2321,7 +2321,6 @@ static int pxa25x_udc_probe(struct platform_device *pdev) struct pxa25x_udc *dev = &memory; int retval, irq; u32 chiprev; - struct resource *res; pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); @@ -2367,8 +2366,7 @@ static int pxa25x_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->regs = devm_ioremap_resource(&pdev->dev, res); + dev->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dev->regs)) return PTR_ERR(dev->regs); -- cgit From ec035f91297d81506384c18563101a2c21cd8177 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:40:33 +0800 Subject: usb: gadget: gr_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904094033.19652-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/gr_udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 7a0e9a58c2d8..c63383221b5a 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2118,7 +2118,6 @@ static int gr_request_irq(struct gr_udc *dev, int irq) static int gr_probe(struct platform_device *pdev) { struct gr_udc *dev; - struct resource *res; struct gr_regs __iomem *regs; int retval; u32 status; @@ -2128,8 +2127,7 @@ static int gr_probe(struct platform_device *pdev) return -ENOMEM; dev->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(dev->dev, res); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); -- cgit From 893a66d342981f8b67cdbf476f0b81f7015cda49 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:33:35 +0800 Subject: usb: bdc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904093335.22860-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/bdc/bdc_core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index cc4a16e253ac..02a3a774670b 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -480,7 +480,6 @@ static void bdc_phy_exit(struct bdc *bdc) static int bdc_probe(struct platform_device *pdev) { struct bdc *bdc; - struct resource *res; int ret = -ENOMEM; int irq; u32 temp; @@ -508,8 +507,7 @@ static int bdc_probe(struct platform_device *pdev) bdc->clk = clk; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - bdc->regs = devm_ioremap_resource(dev, res); + bdc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(bdc->regs)) { dev_err(dev, "ioremap error\n"); return -ENOMEM; -- cgit From 3aec68e3e020b6c7b6e5bac41605a1fee59097e6 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:32:27 +0800 Subject: usb: gadget: bcm63xx_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904093227.23304-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/bcm63xx_udc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index 97b16463f3ef..7fcf4a8eaeb6 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2282,7 +2282,6 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct bcm63xx_usbd_platform_data *pd = dev_get_platdata(dev); struct bcm63xx_udc *udc; - struct resource *res; int rc = -ENOMEM, i, irq; udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL); @@ -2298,13 +2297,11 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->usbd_regs = devm_ioremap_resource(dev, res); + udc->usbd_regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->usbd_regs)) return PTR_ERR(udc->usbd_regs); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - udc->iudma_regs = devm_ioremap_resource(dev, res); + udc->iudma_regs = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(udc->iudma_regs)) return PTR_ERR(udc->iudma_regs); -- cgit From 55419932d2ace7af2ce76eb6bec11d36e8e27f2f Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:10:04 +0800 Subject: usb: host: xhci-tegra: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904091004.3808-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 2ff7c911fbd0..742960adde94 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -970,7 +970,7 @@ static int tegra_xusb_powerdomain_init(struct device *dev, static int tegra_xusb_probe(struct platform_device *pdev) { struct tegra_xusb_mbox_msg msg; - struct resource *res, *regs; + struct resource *regs; struct tegra_xusb *tegra; struct xhci_hcd *xhci; unsigned int i, j, k; @@ -992,14 +992,12 @@ static int tegra_xusb_probe(struct platform_device *pdev) if (IS_ERR(tegra->regs)) return PTR_ERR(tegra->regs); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - tegra->fpci_base = devm_ioremap_resource(&pdev->dev, res); + tegra->fpci_base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(tegra->fpci_base)) return PTR_ERR(tegra->fpci_base); if (tegra->soc->has_ipfs) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res); + tegra->ipfs_base = devm_platform_ioremap_resource(pdev, 2); if (IS_ERR(tegra->ipfs_base)) return PTR_ERR(tegra->ipfs_base); } -- cgit From 9cbe0c60c7919d98c6ea51fd91a6ba8c7b09eed6 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:05:49 +0800 Subject: usb: isp1362-hcd: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904090549.24456-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 96f8daa11f25..4a3a2852523f 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2627,7 +2627,7 @@ static int isp1362_probe(struct platform_device *pdev) { struct usb_hcd *hcd; struct isp1362_hcd *isp1362_hcd; - struct resource *addr, *data, *irq_res; + struct resource *data, *irq_res; void __iomem *addr_reg; void __iomem *data_reg; int irq; @@ -2651,8 +2651,7 @@ static int isp1362_probe(struct platform_device *pdev) irq = irq_res->start; - addr = platform_get_resource(pdev, IORESOURCE_MEM, 1); - addr_reg = devm_ioremap_resource(&pdev->dev, addr); + addr_reg = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(addr_reg)) return PTR_ERR(addr_reg); -- cgit From e719ffbf872ecf4d9b56314fb25ebe019895abe7 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:02:39 +0800 Subject: usb: gadget: at91_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904090239.23920-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/at91_udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 194ffb1ed462..1b2b548c59a0 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1808,7 +1808,6 @@ static int at91udc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct at91_udc *udc; int retval; - struct resource *res; struct at91_ep *ep; int i; @@ -1839,8 +1838,7 @@ static int at91udc_probe(struct platform_device *pdev) ep->is_pingpong = 1; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->udp_baseaddr = devm_ioremap_resource(dev, res); + udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->udp_baseaddr)) return PTR_ERR(udc->udp_baseaddr); -- cgit From d706a95bb48a2c03c42ae15e28c1ca245f3362ab Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 16:50:45 +0800 Subject: usb: renesas_usbhs: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904085045.24204-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 4c3de777ef6c..18d5d1a0593e 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -590,7 +590,7 @@ static int usbhs_probe(struct platform_device *pdev) { const struct renesas_usbhs_platform_info *info; struct usbhs_priv *priv; - struct resource *res, *irq_res; + struct resource *irq_res; struct device *dev = &pdev->dev; int ret, gpio; u32 tmp; @@ -619,8 +619,7 @@ static int usbhs_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); -- cgit From 33b4332f928256744ec59aa8fb30989f878bd4db Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 16:48:27 +0800 Subject: usb: phy: mxs: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904084827.24108-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-mxs-usb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 70b8c8248caf..67b39dc62b37 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -710,7 +710,6 @@ static enum usb_charger_type mxs_phy_charger_detect(struct usb_phy *phy) static int mxs_phy_probe(struct platform_device *pdev) { - struct resource *res; void __iomem *base; struct clk *clk; struct mxs_phy *mxs_phy; @@ -723,8 +722,7 @@ static int mxs_phy_probe(struct platform_device *pdev) if (!of_id) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); -- cgit From 89fa75acc40fd053115ce24d68c9a75be96af3e2 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 16:45:58 +0800 Subject: usb: phy: keystone: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904084558.24484-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-keystone.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index 19871266312d..110e6e9ad621 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -66,15 +66,13 @@ static int keystone_usbphy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct keystone_usbphy *k_phy; - struct resource *res; int ret; k_phy = devm_kzalloc(dev, sizeof(*k_phy), GFP_KERNEL); if (!k_phy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - k_phy->phy_ctrl = devm_ioremap_resource(dev, res); + k_phy->phy_ctrl = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(k_phy->phy_ctrl)) return PTR_ERR(k_phy->phy_ctrl); -- cgit From f68341d194c35f4e8577d6f4ac90765f58e3f05f Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 16:44:27 +0800 Subject: USB: musb: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20190904084427.18532-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index bd63450af76a..15cca912c53e 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2431,14 +2431,12 @@ static int musb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int irq = platform_get_irq_byname(pdev, "mc"); - struct resource *iomem; void __iomem *base; if (irq <= 0) return -ENODEV; - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, iomem); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); -- cgit From 8f9b6228b2fcc58508774cfdfdcc4e3ac35ff7c8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 11 Sep 2019 11:07:45 +0100 Subject: usb: host: u132-hcd: fix spelling mistake "overcurren" -> "overcurrent" There is a spelling mistake in a module parameter description. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190911100745.30711-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/u132-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 4efee34f154f..e9209e3e6248 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -71,7 +71,7 @@ INT_MODULE_PARM(testing, 0); /* Some boards misreport power switching/overcurrent*/ static bool distrust_firmware = true; module_param(distrust_firmware, bool, 0); -MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren" +MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurrent" "t setup"); static DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait); /* -- cgit From c2c1c66b5d4360debc9564d0aa11c873ed2e7aaa Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 23 Sep 2019 17:49:56 +0200 Subject: usb: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Acked-by: Johan Hovold Link: https://lore.kernel.org/r/20190923154956.6868-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/Kconfig | 4 +-- drivers/usb/gadget/legacy/Kconfig | 20 +++++------ drivers/usb/gadget/udc/Kconfig | 2 +- drivers/usb/host/Kconfig | 68 +++++++++++++++++++------------------- drivers/usb/misc/Kconfig | 8 ++--- drivers/usb/misc/sisusbvga/Kconfig | 2 +- drivers/usb/serial/Kconfig | 44 ++++++++++++------------ 7 files changed, 74 insertions(+), 74 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 89abc6078703..cc431376fcd0 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -103,7 +103,7 @@ config USB_DWC3_MESON_G12A default USB_DWC3 select USB_ROLE_SWITCH help - Support USB2/3 functionality in Amlogic G12A platforms. + Support USB2/3 functionality in Amlogic G12A platforms. Say 'Y' or 'M' if you have one such device. config USB_DWC3_OF_SIMPLE @@ -111,7 +111,7 @@ config USB_DWC3_OF_SIMPLE depends on OF && COMMON_CLK default USB_DWC3 help - Support USB2/3 functionality in simple SoC integrations. + Support USB2/3 functionality in simple SoC integrations. Currently supports Xilinx and Qualcomm DWC USB3 IP. Say 'Y' or 'M' if you have one such device. diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 69ff7f8c86f5..38eaa9417b38 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -154,16 +154,16 @@ config USB_ETH_EEM select USB_LIBCOMPOSITE select USB_F_EEM help - CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM - and therefore can be supported by more hardware. Technically ECM and - EEM are designed for different applications. The ECM model extends - the network interface to the target (e.g. a USB cable modem), and the - EEM model is for mobile devices to communicate with hosts using - ethernet over USB. For Linux gadgets, however, the interface with - the host is the same (a usbX device), so the differences are minimal. - - If you say "y" here, the Ethernet gadget driver will use the EEM - protocol rather than ECM. If unsure, say "n". + CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM + and therefore can be supported by more hardware. Technically ECM and + EEM are designed for different applications. The ECM model extends + the network interface to the target (e.g. a USB cable modem), and the + EEM model is for mobile devices to communicate with hosts using + ethernet over USB. For Linux gadgets, however, the interface with + the host is the same (a usbX device), so the differences are minimal. + + If you say "y" here, the Ethernet gadget driver will use the EEM + protocol rather than ECM. If unsure, say "n". config USB_G_NCM tristate "Network Control Model (NCM) support" diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index d7e611645533..485c92ef888a 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -123,7 +123,7 @@ config USB_GR_UDC tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" depends on HAS_DMA help - Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB + Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB VHDL IP core library. config USB_OMAP diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 79b2e79dddd0..d6164ede63d3 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -220,12 +220,12 @@ config USB_EHCI_HCD_ORION Marvell PXA/MMP USB controller" for those. config USB_EHCI_HCD_SPEAR - tristate "Support for ST SPEAr on-chip EHCI USB controller" - depends on USB_EHCI_HCD && PLAT_SPEAR - default y - ---help--- - Enables support for the on-chip EHCI controller on - ST SPEAr chips. + tristate "Support for ST SPEAr on-chip EHCI USB controller" + depends on USB_EHCI_HCD && PLAT_SPEAR + default y + ---help--- + Enables support for the on-chip EHCI controller on + ST SPEAr chips. config USB_EHCI_HCD_STI tristate "Support for ST STiHxxx on-chip EHCI USB controller" @@ -237,12 +237,12 @@ config USB_EHCI_HCD_STI STMicroelectronics consumer electronics SoC's. config USB_EHCI_HCD_AT91 - tristate "Support for Atmel on-chip EHCI USB controller" - depends on USB_EHCI_HCD && ARCH_AT91 - default y - ---help--- - Enables support for the on-chip EHCI controller on - Atmel chips. + tristate "Support for Atmel on-chip EHCI USB controller" + depends on USB_EHCI_HCD && ARCH_AT91 + default y + ---help--- + Enables support for the on-chip EHCI controller on + Atmel chips. config USB_EHCI_TEGRA tristate "NVIDIA Tegra HCD support" @@ -250,8 +250,8 @@ config USB_EHCI_TEGRA select USB_EHCI_ROOT_HUB_TT select USB_TEGRA_PHY help - This driver enables support for the internal USB Host Controllers - found in NVIDIA Tegra SoCs. The controllers are EHCI compliant. + This driver enables support for the internal USB Host Controllers + found in NVIDIA Tegra SoCs. The controllers are EHCI compliant. config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" @@ -409,12 +409,12 @@ config USB_OHCI_HCD_OMAP1 Enables support for the OHCI controller on OMAP1/2 chips. config USB_OHCI_HCD_SPEAR - tristate "Support for ST SPEAr on-chip OHCI USB controller" - depends on USB_OHCI_HCD && PLAT_SPEAR - default y - ---help--- - Enables support for the on-chip OHCI controller on - ST SPEAr chips. + tristate "Support for ST SPEAr on-chip OHCI USB controller" + depends on USB_OHCI_HCD && PLAT_SPEAR + default y + ---help--- + Enables support for the on-chip OHCI controller on + ST SPEAr chips. config USB_OHCI_HCD_STI tristate "Support for ST STiHxxx on-chip OHCI USB controller" @@ -426,12 +426,12 @@ config USB_OHCI_HCD_STI STMicroelectronics consumer electronics SoC's. config USB_OHCI_HCD_S3C2410 - tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series" - depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX) - default y - ---help--- - Enables support for the on-chip OHCI controller on - S3C24xx/S3C64xx chips. + tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series" + depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX) + default y + ---help--- + Enables support for the on-chip OHCI controller on + S3C24xx/S3C64xx chips. config USB_OHCI_HCD_LPC32XX tristate "Support for LPC on-chip OHCI USB controller" @@ -440,8 +440,8 @@ config USB_OHCI_HCD_LPC32XX depends on USB_ISP1301 default y ---help--- - Enables support for the on-chip OHCI controller on - NXP chips. + Enables support for the on-chip OHCI controller on + NXP chips. config USB_OHCI_HCD_PXA27X tristate "Support for PXA27X/PXA3XX on-chip OHCI USB controller" @@ -456,8 +456,8 @@ config USB_OHCI_HCD_AT91 depends on USB_OHCI_HCD && ARCH_AT91 && OF default y ---help--- - Enables support for the on-chip OHCI controller on - Atmel chips. + Enables support for the on-chip OHCI controller on + Atmel chips. config USB_OHCI_HCD_OMAP3 tristate "OHCI support for OMAP3 and later chips" @@ -716,11 +716,11 @@ config USB_IMX21_HCD tristate "i.MX21 HCD support" depends on ARM && ARCH_MXC help - This driver enables support for the on-chip USB host in the - i.MX21 processor. + This driver enables support for the on-chip USB host in the + i.MX21 processor. - To compile this driver as a module, choose M here: the - module will be called "imx21-hcd". + To compile this driver as a module, choose M here: the + module will be called "imx21-hcd". config USB_HCD_BCMA tristate "BCMA usb host driver" diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index bdae62b2ffe0..664d27bb6086 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -191,8 +191,8 @@ config USB_TEST including sample test device firmware and "how to use it". config USB_EHSET_TEST_FIXTURE - tristate "USB EHSET Test Fixture driver" - help + tristate "USB EHSET Test Fixture driver" + help Say Y here if you want to support the special test fixture device used for the USB-IF Embedded Host High-Speed Electrical Test procedure. @@ -247,13 +247,13 @@ config USB_HSIC_USB3503 depends on I2C select REGMAP_I2C help - This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver. + This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver. config USB_HSIC_USB4604 tristate "USB4604 HSIC to USB20 Driver" depends on I2C help - This option enables support for SMSC USB4604 HSIC to USB 2.0 Driver. + This option enables support for SMSC USB4604 HSIC to USB 2.0 Driver. config USB_LINK_LAYER_TEST tristate "USB Link Layer Test driver" diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig index 9b632ab24f03..c16121276a21 100644 --- a/drivers/usb/misc/sisusbvga/Kconfig +++ b/drivers/usb/misc/sisusbvga/Kconfig @@ -4,7 +4,7 @@ config USB_SISUSBVGA tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)" depends on (USB_MUSB_HDRC || USB_EHCI_HCD) select FONT_SUPPORT if USB_SISUSBVGA_CON - ---help--- + ---help--- Say Y here if you intend to attach a USB2VGA dongle based on a Net2280 and a SiS315 chip. diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 67279c6bce33..0a8c16a8cda2 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -271,17 +271,17 @@ config USB_SERIAL_F8153X config USB_SERIAL_GARMIN tristate "USB Garmin GPS driver" help - Say Y here if you want to connect to your Garmin GPS. - Should work with most Garmin GPS devices which have a native USB port. + Say Y here if you want to connect to your Garmin GPS. + Should work with most Garmin GPS devices which have a native USB port. - See for the latest - version of the driver. + See for the latest + version of the driver. - To compile this driver as a module, choose M here: the - module will be called garmin_gps. + To compile this driver as a module, choose M here: the + module will be called garmin_gps. config USB_SERIAL_IPW - tristate "USB IPWireless (3G UMTS TDD) Driver" + tristate "USB IPWireless (3G UMTS TDD) Driver" select USB_SERIAL_WWAN help Say Y here if you want to use a IPWireless USB modem such as @@ -341,20 +341,20 @@ config USB_SERIAL_KLSI module will be called kl5kusb105. config USB_SERIAL_KOBIL_SCT - tristate "USB KOBIL chipcard reader" - ---help--- - Say Y here if you want to use one of the following KOBIL USB chipcard - readers: - - - USB TWIN - - KAAN Standard Plus - - KAAN SIM - - SecOVID Reader Plus - - B1 Professional - - KAAN Professional - - Note that you need a current CT-API. - To compile this driver as a module, choose M here: the + tristate "USB KOBIL chipcard reader" + ---help--- + Say Y here if you want to use one of the following KOBIL USB chipcard + readers: + + - USB TWIN + - KAAN Standard Plus + - KAAN SIM + - SecOVID Reader Plus + - B1 Professional + - KAAN Professional + + Note that you need a current CT-API. + To compile this driver as a module, choose M here: the module will be called kobil_sct. config USB_SERIAL_MCT_U232 @@ -458,7 +458,7 @@ config USB_SERIAL_OTI6858 tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge Controller" help Say Y here if you want to use the OTi-6858 single port USB to serial - converter device. + converter device. To compile this driver as a module, choose M here: the module will be called oti6858. -- cgit From f906d0614f5f5742c46db9773c0fa5f4c521549f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 26 Sep 2019 13:45:53 +0100 Subject: usb: ftdi-elan: move a couple of statements to next line There are a couple of statements that follow the end brace of while loops that should be moved to the next line to clean up the coding style. Cleans up style warnings from smatch. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190926124553.15177-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ftdi-elan.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index cdee3af33ad7..8a3d9c0c8d8b 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -333,7 +333,8 @@ static void ftdi_elan_abandon_completions(struct usb_ftdi *ftdi) *respond->result = -ESHUTDOWN; *respond->value = 0; complete(&respond->wait_completion); - } mutex_unlock(&ftdi->u132_lock); + } + mutex_unlock(&ftdi->u132_lock); } static void ftdi_elan_abandon_targets(struct usb_ftdi *ftdi) @@ -763,7 +764,8 @@ static int ftdi_elan_total_command_size(struct usb_ftdi *ftdi, int command_size) struct u132_command *command = &ftdi->command[COMMAND_MASK & i++]; total_size += 5 + command->follows; - } return total_size; + } + return total_size; } static int ftdi_elan_command_engine(struct usb_ftdi *ftdi) -- cgit From faf4b0dcea31f28979b5cde706d3e5d7b4ad840d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 27 Sep 2019 10:20:00 +0100 Subject: usbip: clean up an indentation issue There is a return statement that is indented incorrectly, fix this. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190927092000.19373-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index 36010a82b359..b1c2f6781cb3 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -291,7 +291,7 @@ static int stub_send_ret_submit(struct stub_device *sdev) kfree(iov); usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); - return -1; + return -1; } } -- cgit From c0ee57ce66fafbe866bf00e097b969d0f4524bb8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 27 Sep 2019 09:50:31 +0100 Subject: USB: gadget: udc: clean up an indentation issue There is a statement that is indented too deeply, remove the extraneous tabs. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20190927085031.14739-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/bdc/bdc_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index 7bfd58c846f7..248426a3e88a 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -195,7 +195,7 @@ static void handle_link_state_change(struct bdc *bdc, u32 uspc) break; case BDC_LINK_STATE_U0: if (bdc->devstatus & REMOTE_WAKEUP_ISSUED) { - bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED; + bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED; if (bdc->gadget.speed == USB_SPEED_SUPER) { bdc_function_wake_fh(bdc, 0); bdc->devstatus |= FUNC_WAKE_ISSUED; -- cgit From 29e56c0cf5d910a07b9932cf5924ade9d98a8143 Mon Sep 17 00:00:00 2001 From: Saurav Girepunje Date: Wed, 2 Oct 2019 20:02:45 +0530 Subject: usb: musb: musb_gadget.c: Remove unused variable Remove unused variable status from musb_gadget_disable(). Signed-off-by: Saurav Girepunje Link: https://lore.kernel.org/r/20191002143241.GA11615@saurav Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index ffe462a657b1..2cb31fc0cd60 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1085,7 +1085,6 @@ static int musb_gadget_disable(struct usb_ep *ep) u8 epnum; struct musb_ep *musb_ep; void __iomem *epio; - int status = 0; musb_ep = to_musb_ep(ep); musb = musb_ep->musb; @@ -1118,7 +1117,7 @@ static int musb_gadget_disable(struct usb_ep *ep) musb_dbg(musb, "%s", musb_ep->end_point.name); - return status; + return 0; } /* -- cgit From 9ac0fc3906ec88663282f325f92486cf863f9c2b Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Wed, 2 Oct 2019 09:19:33 -0400 Subject: usb: chipidea: tegra: clean up tegra_udc flag code All Tegra devices handled by tegra-udc use the same flags. Consolidate all the entries under one roof. Signed-off-by: Peter Geis Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20191002131933.6206-1-pgwipeout@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_tegra.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 12025358bb3c..0c9911d44ee5 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -24,35 +24,23 @@ struct tegra_udc_soc_info { unsigned long flags; }; -static const struct tegra_udc_soc_info tegra20_udc_soc_info = { - .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, -}; - -static const struct tegra_udc_soc_info tegra30_udc_soc_info = { - .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, -}; - -static const struct tegra_udc_soc_info tegra114_udc_soc_info = { - .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, -}; - -static const struct tegra_udc_soc_info tegra124_udc_soc_info = { +static const struct tegra_udc_soc_info tegra_udc_soc_info = { .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, }; static const struct of_device_id tegra_udc_of_match[] = { { .compatible = "nvidia,tegra20-udc", - .data = &tegra20_udc_soc_info, + .data = &tegra_udc_soc_info, }, { .compatible = "nvidia,tegra30-udc", - .data = &tegra30_udc_soc_info, + .data = &tegra_udc_soc_info, }, { .compatible = "nvidia,tegra114-udc", - .data = &tegra114_udc_soc_info, + .data = &tegra_udc_soc_info, }, { .compatible = "nvidia,tegra124-udc", - .data = &tegra124_udc_soc_info, + .data = &tegra_udc_soc_info, }, { /* sentinel */ } -- cgit From 5d88ef9eead8b5756122cc799593780d15dae471 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 1 Oct 2019 15:23:33 +0200 Subject: usb: host: xhci-tegra: use regulator_bulk_set_supply_names() Use the new regulator helper instead of a for loop. Signed-off-by: Bartosz Golaszewski Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20191001132333.20146-4-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 742960adde94..111a16376087 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1126,8 +1126,9 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_powerdomains; } - for (i = 0; i < tegra->soc->num_supplies; i++) - tegra->supplies[i].supply = tegra->soc->supply_names[i]; + regulator_bulk_set_supply_names(tegra->supplies, + tegra->soc->supply_names, + tegra->soc->num_supplies); err = devm_regulator_bulk_get(&pdev->dev, tegra->soc->num_supplies, tegra->supplies); -- cgit From 9c4567fa0a44e3733f49f93a2197d9907b836d4a Mon Sep 17 00:00:00 2001 From: Boris Krasnovskiy Date: Wed, 11 Sep 2019 08:41:52 +0200 Subject: USB: host: ohci-at91: completely shutdown the controller in at91_stop_hc() When removing the ohci-at91 module, the fact of not running complete shutdown of all the ports was keeping additional analog cells consuming power for no reason. Doing Reset (OHCI_HCR) to HcCommandStatus register is the way to go, but using the OHCI controller shutdown procedure is just perfect for this. Signed-off-by: Boris Krasnovskiy Signed-off-by: Nicolas Ferre Acked-by: Alan Stern Link: https://lore.kernel.org/r/20190911064154.28633-2-nicolas.ferre@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index fc35a7993b7b..cb63bcd5049a 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -123,7 +123,7 @@ static void at91_stop_hc(struct platform_device *pdev) /* * Put the USB host controller into reset. */ - writel(0, ®s->control); + usb_hcd_platform_shutdown(pdev); /* * Stop the USB clocks. -- cgit From a3bf4d6816556baa9a503c9232eb7e3f018ff8dc Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 11 Sep 2019 08:41:53 +0200 Subject: USB: host: ohci-at91: suspend: delay needed before to stop clocks In order to completely remove marginal power consumption in PM suspend, we need to let the controller settle down before being stopped. In ohci_hcd_at91_drv_suspend() function, one additional delay is needed before to stop the clocks. Reported-by: Boris Krasnovskiy Signed-off-by: Nicolas Ferre Acked-by: Alan Stern Link: https://lore.kernel.org/r/20190911064154.28633-3-nicolas.ferre@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index cb63bcd5049a..85d67fe42d59 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -628,6 +628,7 @@ ohci_hcd_at91_drv_suspend(struct device *dev) /* flush the writes */ (void) ohci_readl (ohci, &ohci->regs->control); + msleep(1); at91_stop_clock(ohci_at91); } -- cgit From 87e8dfa6f7ebdb3e2802a606bda44d26054f92a8 Mon Sep 17 00:00:00 2001 From: Boris Krasnovskiy Date: Wed, 11 Sep 2019 08:41:54 +0200 Subject: USB: host: ohci-at91: resume: balance the clock start call There is a clock enable counter run away problem in resume ohci_at91. Code enables clock that was never disabled in case of non wakeup interface. That would make clock unstoppable in future. Use proper alternative to start clocks only if they were stopped before. Signed-off-by: Boris Krasnovskiy Signed-off-by: Nicolas Ferre Acked-by: Alan Stern Link: https://lore.kernel.org/r/20190911064154.28633-4-nicolas.ferre@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 85d67fe42d59..513e48397743 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -643,8 +643,8 @@ ohci_hcd_at91_drv_resume(struct device *dev) if (ohci_at91->wakeup) disable_irq_wake(hcd->irq); - - at91_start_clock(ohci_at91); + else + at91_start_clock(ohci_at91); ohci_resume(hcd, false); -- cgit From 5022204a464c598e36470717f5dda18576234bce Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Wed, 11 Sep 2019 15:15:54 +0200 Subject: usb: renesas_usbhs: simplify usbhs_status_get_device_state() Similar to usbhs_status_get_ctrl_stage(), *_get_device_state() is not supposed to return any error code since its return value is the DVSQ bitfield of the INTSTS0 register. According to SoC HW manual rev1.00, every single value of DVSQ[2:0] is valid and none is an error: ----8<---- Device State 000: Powered state 001: Default state 010: Address state 011: Configuration state 1xx: Suspended state ----8<---- Hence, simplify the function body. The motivation behind dropping the switch/case construct is being able to implement reading the suspended state. The latter (based on the above DVSQ[2:0] description) doesn't have a unique value, but is rather a list of states (which makes switch/case less suitable for reading/validating it): 100: (Suspended) Powered state 101: (Suspended) Default state 110: (Suspended) Address state 111: (Suspended) Configuration state Signed-off-by: Eugeniu Rosca Signed-off-by: Veeraiyan Chidambaram Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/1568207756-22325-1-git-send-email-external.veeraiyan.c@de.adit-jv.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/mod.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 10fc65596014..1b3ae5c9baef 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -169,17 +169,7 @@ void usbhs_mod_remove(struct usbhs_priv *priv) */ int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state) { - int state = irq_state->intsts0 & DVSQ_MASK; - - switch (state) { - case POWER_STATE: - case DEFAULT_STATE: - case ADDRESS_STATE: - case CONFIGURATION_STATE: - return state; - } - - return -EIO; + return (int)irq_state->intsts0 & DVSQ_MASK; } int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state) -- cgit From fef22636889e18517c2fc0ff88e2ae401bb9a76d Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Wed, 11 Sep 2019 15:15:55 +0200 Subject: usb: renesas_usbhs: enable DVSE interrupt Commit [1] enabled the possibility of checking the DVST (Device State Transition) bit of INTSTS0 (Interrupt Status Register 0) and calling the irq_dev_state() handler if the DVST bit is set. But neither commit [1] nor commit [2] actually enabled the DVSE (Device State Transition Interrupt Enable) bit in the INTENB0 (Interrupt Enable Register 0). As a consequence, irq_dev_state() handler is getting called as a side effect of other (non-DVSE) interrupts being fired, which definitely can't be relied upon, if DVST notifications are of any value. Why this doesn't hurt is because usbhsg_irq_dev_state() currently doesn't do much except of a dev_dbg(). Once more work is added to the handler (e.g. detecting device "Suspended" state and notifying other USB gadget components about it), enabling DVSE becomes a hard requirement. Do it in a standalone commit for better visibility and clear explanation. [1] commit f1407d5c6624 ("usb: renesas_usbhs: Add Renesas USBHS common code") [2] commit 2f98382dcdfe ("usb: renesas_usbhs: Add Renesas USBHS Gadget") Signed-off-by: Eugeniu Rosca Signed-off-by: Veeraiyan Chidambaram Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/1568207756-22325-2-git-send-email-external.veeraiyan.c@de.adit-jv.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/mod.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 1b3ae5c9baef..b98112cefaa4 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -338,10 +338,6 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) * usbhs_interrupt */ - /* - * it don't enable DVSE (intenb0) here - * but "mod->irq_dev_state" will be called. - */ if (info->irq_vbus) intenb0 |= VBSE; @@ -352,6 +348,9 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) if (mod->irq_ctrl_stage) intenb0 |= CTRE; + if (mod->irq_dev_state) + intenb0 |= DVSE; + if (mod->irq_empty && mod->irq_bempsts) { usbhs_write(priv, BEMPENB, mod->irq_bempsts); intenb0 |= BEMPE; -- cgit From 39abcc84846bbc0538f13c190b6a9c7e36890cd2 Mon Sep 17 00:00:00 2001 From: Veeraiyan Chidambaram Date: Wed, 11 Sep 2019 15:15:56 +0200 Subject: usb: renesas_usbhs: add suspend event support in gadget mode When R-Car Gen3 USB 2.0 is in Gadget mode, if host is detached an interrupt will be generated and Suspended state bit is set in interrupt status register. Interrupt handler will call driver->suspend(composite_suspend) if suspended state bit is set. composite_suspend will call ffs_func_suspend which will post FUNCTIONFS_SUSPEND and will be consumed by user space application via /dev/ep0. To be able to detect host detach, extend the DVSQ_MASK to cover the Suspended bit of the DVSQ[2:0] bitfield from the Interrupt Status Register 0 (INTSTS0) register and perform appropriate action in the DVST interrupt handler (usbhsg_irq_dev_state). Without this commit, disconnection of the phone from R-Car-H3 ES2.0 Salvator-X CN9 port is not recognized and reverse role switch does not happen. If phone is connected again it does not enumerate. With this commit, disconnection will be recognized and reverse role switch will happen by a user space application. If phone is connected again it will enumerate properly and will become visible in the output of 'lsusb'. Signed-off-by: Veeraiyan Chidambaram Signed-off-by: Eugeniu Rosca Reviewed-by: Yoshihiro Shimoda Tested-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/1568207756-22325-3-git-send-email-external.veeraiyan.c@de.adit-jv.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.h | 3 ++- drivers/usb/renesas_usbhs/mod_gadget.c | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index d1a0a35ecfff..7b75a548c0ed 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -161,11 +161,12 @@ struct usbhs_priv; #define VBSTS (1 << 7) /* VBUS_0 and VBUSIN_0 Input Status */ #define VALID (1 << 3) /* USB Request Receive */ -#define DVSQ_MASK (0x3 << 4) /* Device State */ +#define DVSQ_MASK (0x7 << 4) /* Device State */ #define POWER_STATE (0 << 4) #define DEFAULT_STATE (1 << 4) #define ADDRESS_STATE (2 << 4) #define CONFIGURATION_STATE (3 << 4) +#define SUSPENDED_STATE (4 << 4) #define CTSQ_MASK (0x7) /* Control Transfer Stage */ #define IDLE_SETUP_STAGE 0 /* Idle stage or setup stage */ diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 4d571a5205e2..1235f3c1f62a 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -457,12 +457,18 @@ static int usbhsg_irq_dev_state(struct usbhs_priv *priv, { struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); struct device *dev = usbhsg_gpriv_to_dev(gpriv); + int state = usbhs_status_get_device_state(irq_state); gpriv->gadget.speed = usbhs_bus_get_speed(priv); - dev_dbg(dev, "state = %x : speed : %d\n", - usbhs_status_get_device_state(irq_state), - gpriv->gadget.speed); + dev_dbg(dev, "state = %x : speed : %d\n", state, gpriv->gadget.speed); + + if (gpriv->gadget.speed != USB_SPEED_UNKNOWN && + (state & SUSPENDED_STATE)) { + if (gpriv->driver && gpriv->driver->suspend) + gpriv->driver->suspend(&gpriv->gadget); + usb_gadget_set_state(&gpriv->gadget, USB_STATE_SUSPENDED); + } return 0; } -- cgit From 1c48c759ef4bb9031b3347277f04484e07e27d97 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 4 Sep 2019 09:15:40 +0100 Subject: usb: typec: driver for TI HD3SS3220 USB Type-C DRP port controller Driver for TI HD3SS3220 USB Type-C DRP port controller. The driver currently registers the port and supports data role swapping. Signed-off-by: Biju Das Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/1567584941-13690-3-git-send-email-biju.das@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/Kconfig | 10 ++ drivers/usb/typec/Makefile | 1 + drivers/usb/typec/hd3ss3220.c | 259 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+) create mode 100644 drivers/usb/typec/hd3ss3220.c (limited to 'drivers/usb') diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 895e2418de53..aceb2af6a998 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -50,6 +50,16 @@ source "drivers/usb/typec/tcpm/Kconfig" source "drivers/usb/typec/ucsi/Kconfig" +config TYPEC_HD3SS3220 + tristate "TI HD3SS3220 Type-C DRP Port controller driver" + depends on I2C + help + Say Y or M here if your system has TI HD3SS3220 Type-C DRP Port + controller driver. + + If you choose to build this driver as a dynamically linked module, the + module will be called hd3ss3220.ko. + config TYPEC_TPS6598X tristate "TI TPS6598x USB Power Delivery controller driver" depends on I2C diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 6696b7263d61..7753a5c3cd46 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -4,5 +4,6 @@ typec-y := class.o mux.o bus.o obj-$(CONFIG_TYPEC) += altmodes/ obj-$(CONFIG_TYPEC_TCPM) += tcpm/ obj-$(CONFIG_TYPEC_UCSI) += ucsi/ +obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o obj-$(CONFIG_TYPEC) += mux/ diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c new file mode 100644 index 000000000000..b8f247e792b8 --- /dev/null +++ b/drivers/usb/typec/hd3ss3220.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI HD3SS3220 Type-C DRP Port Controller Driver + * + * Copyright (C) 2019 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HD3SS3220_REG_CN_STAT_CTRL 0x09 +#define HD3SS3220_REG_GEN_CTRL 0x0A +#define HD3SS3220_REG_DEV_REV 0xA0 + +/* Register HD3SS3220_REG_CN_STAT_CTRL*/ +#define HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK (BIT(7) | BIT(6)) +#define HD3SS3220_REG_CN_STAT_CTRL_AS_DFP BIT(6) +#define HD3SS3220_REG_CN_STAT_CTRL_AS_UFP BIT(7) +#define HD3SS3220_REG_CN_STAT_CTRL_TO_ACCESSORY (BIT(7) | BIT(6)) +#define HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS BIT(4) + +/* Register HD3SS3220_REG_GEN_CTRL*/ +#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK (BIT(2) | BIT(1)) +#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT 0x00 +#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK BIT(1) +#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC (BIT(2) | BIT(1)) + +struct hd3ss3220 { + struct device *dev; + struct regmap *regmap; + struct usb_role_switch *role_sw; + struct typec_port *port; + struct typec_capability typec_cap; +}; + +static int hd3ss3220_set_source_pref(struct hd3ss3220 *hd3ss3220, int src_pref) +{ + return regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL, + HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK, + src_pref); +} + +static enum usb_role hd3ss3220_get_attached_state(struct hd3ss3220 *hd3ss3220) +{ + unsigned int reg_val; + enum usb_role attached_state; + int ret; + + ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, + ®_val); + if (ret < 0) + return ret; + + switch (reg_val & HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK) { + case HD3SS3220_REG_CN_STAT_CTRL_AS_DFP: + attached_state = USB_ROLE_HOST; + break; + case HD3SS3220_REG_CN_STAT_CTRL_AS_UFP: + attached_state = USB_ROLE_DEVICE; + break; + default: + attached_state = USB_ROLE_NONE; + break; + } + + return attached_state; +} + +static int hd3ss3220_dr_set(const struct typec_capability *cap, + enum typec_data_role role) +{ + struct hd3ss3220 *hd3ss3220 = container_of(cap, struct hd3ss3220, + typec_cap); + enum usb_role role_val; + int pref, ret = 0; + + if (role == TYPEC_HOST) { + role_val = USB_ROLE_HOST; + pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC; + } else { + role_val = USB_ROLE_DEVICE; + pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK; + } + + ret = hd3ss3220_set_source_pref(hd3ss3220, pref); + usleep_range(10, 100); + + usb_role_switch_set_role(hd3ss3220->role_sw, role_val); + typec_set_data_role(hd3ss3220->port, role); + + return ret; +} + +static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220) +{ + enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220); + + usb_role_switch_set_role(hd3ss3220->role_sw, role_state); + if (role_state == USB_ROLE_NONE) + hd3ss3220_set_source_pref(hd3ss3220, + HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT); + + switch (role_state) { + case USB_ROLE_HOST: + typec_set_data_role(hd3ss3220->port, TYPEC_HOST); + break; + case USB_ROLE_DEVICE: + typec_set_data_role(hd3ss3220->port, TYPEC_DEVICE); + break; + default: + break; + } +} + +irqreturn_t hd3ss3220_irq(struct hd3ss3220 *hd3ss3220) +{ + int err; + + hd3ss3220_set_role(hd3ss3220); + err = regmap_update_bits_base(hd3ss3220->regmap, + HD3SS3220_REG_CN_STAT_CTRL, + HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS, + HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS, + NULL, false, true); + if (err < 0) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static irqreturn_t hd3ss3220_irq_handler(int irq, void *data) +{ + struct i2c_client *client = to_i2c_client(data); + struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client); + + return hd3ss3220_irq(hd3ss3220); +} + +static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0A, +}; + +static int hd3ss3220_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct hd3ss3220 *hd3ss3220; + struct fwnode_handle *connector; + int ret; + unsigned int data; + + hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220), + GFP_KERNEL); + if (!hd3ss3220) + return -ENOMEM; + + i2c_set_clientdata(client, hd3ss3220); + + hd3ss3220->dev = &client->dev; + hd3ss3220->regmap = devm_regmap_init_i2c(client, &config); + if (IS_ERR(hd3ss3220->regmap)) + return PTR_ERR(hd3ss3220->regmap); + + hd3ss3220_set_source_pref(hd3ss3220, + HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT); + connector = device_get_named_child_node(hd3ss3220->dev, "connector"); + if (IS_ERR(connector)) + return PTR_ERR(connector); + + hd3ss3220->role_sw = fwnode_usb_role_switch_get(connector); + fwnode_handle_put(connector); + if (IS_ERR_OR_NULL(hd3ss3220->role_sw)) + return PTR_ERR(hd3ss3220->role_sw); + + hd3ss3220->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + hd3ss3220->typec_cap.dr_set = hd3ss3220_dr_set; + hd3ss3220->typec_cap.type = TYPEC_PORT_DRP; + hd3ss3220->typec_cap.data = TYPEC_PORT_DRD; + + hd3ss3220->port = typec_register_port(&client->dev, + &hd3ss3220->typec_cap); + if (IS_ERR(hd3ss3220->port)) + return PTR_ERR(hd3ss3220->port); + + hd3ss3220_set_role(hd3ss3220); + ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, &data); + if (ret < 0) + goto error; + + if (data & HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS) { + ret = regmap_write(hd3ss3220->regmap, + HD3SS3220_REG_CN_STAT_CTRL, + data | HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS); + if (ret < 0) + goto error; + } + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, + hd3ss3220_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "hd3ss3220", &client->dev); + if (ret) + goto error; + } + + ret = i2c_smbus_read_byte_data(client, HD3SS3220_REG_DEV_REV); + if (ret < 0) + goto error; + + dev_info(&client->dev, "probed revision=0x%x\n", ret); + + return 0; +error: + typec_unregister_port(hd3ss3220->port); + usb_role_switch_put(hd3ss3220->role_sw); + + return ret; +} + +static int hd3ss3220_remove(struct i2c_client *client) +{ + struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client); + + typec_unregister_port(hd3ss3220->port); + usb_role_switch_put(hd3ss3220->role_sw); + + return 0; +} + +static const struct of_device_id dev_ids[] = { + { .compatible = "ti,hd3ss3220"}, + {} +}; +MODULE_DEVICE_TABLE(of, dev_ids); + +static struct i2c_driver hd3ss3220_driver = { + .driver = { + .name = "hd3ss3220", + .of_match_table = of_match_ptr(dev_ids), + }, + .probe = hd3ss3220_probe, + .remove = hd3ss3220_remove, +}; + +module_i2c_driver(hd3ss3220_driver); + +MODULE_AUTHOR("Biju Das "); +MODULE_DESCRIPTION("TI HD3SS3220 DRP Port Controller Driver"); +MODULE_LICENSE("GPL"); -- cgit From 4d7201cda226d7cffb591d94ca66ca0960891126 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Tue, 17 Sep 2019 16:44:47 +0200 Subject: usb: usb251xb: add vdd supply support Currently we don't handle the supply. We need to add the supply support to be able to switch the supply off e.g. during a suspend-to-ram operation. So we can guarantee a correct (re-)initialization. Signed-off-by: Marco Felsch Reviewed-by: Richard Leitner Tested-by: Richard Leitner Link: https://lore.kernel.org/r/20190917144449.32739-3-m.felsch@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb251xb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 6ca9111d150a..05819167604d 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -17,6 +17,7 @@ #include #include #include +#include #include /* Internal Register Set Addresses & Default Values acc. to DS00001692C */ @@ -116,6 +117,7 @@ struct usb251xb { struct device *dev; struct i2c_client *i2c; + struct regulator *vdd; u8 skip_config; struct gpio_desc *gpio_reset; u16 vendor_id; @@ -420,6 +422,10 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, return err; } + hub->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(hub->vdd)) + return PTR_ERR(hub->vdd); + if (of_property_read_u16_array(np, "vendor-id", &hub->vendor_id, 1)) hub->vendor_id = USB251XB_DEF_VENDOR_ID; @@ -665,6 +671,10 @@ static int usb251xb_probe(struct usb251xb *hub) if (err) return err; + err = regulator_enable(hub->vdd); + if (err) + return err; + err = usb251xb_connect(hub); if (err) { dev_err(dev, "Failed to connect hub (%d)\n", err); -- cgit From a9bab25556f142c696c660f4be54dba9808403d2 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Tue, 17 Sep 2019 16:44:48 +0200 Subject: usb: usb251xb: simplify reset helper Currently the reset handler was always called to deassert the reset line because assert the line was done during probe. Now if we want to support pm by turn of the supply we need to call this routine twice and the i2c_lock_bus is done twice too. To simplify that we can drop the state and just do a reset in one go. So a future pm operation don't need to lock the i2c bus twice. Signed-off-by: Marco Felsch Reviewed-by: Richard Leitner Tested-by: Richard Leitner Link: https://lore.kernel.org/r/20190917144449.32739-4-m.felsch@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb251xb.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 05819167604d..bc031d33f433 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -263,20 +263,19 @@ static int usb251x_check_gpio_chip(struct usb251xb *hub) } #endif -static void usb251xb_reset(struct usb251xb *hub, int state) +static void usb251xb_reset(struct usb251xb *hub) { if (!hub->gpio_reset) return; i2c_lock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); - gpiod_set_value_cansleep(hub->gpio_reset, state); + gpiod_set_value_cansleep(hub->gpio_reset, 1); + usleep_range(1, 10); /* >=1us RESET_N asserted */ + gpiod_set_value_cansleep(hub->gpio_reset, 0); /* wait for hub recovery/stabilization */ - if (!state) - usleep_range(500, 750); /* >=500us at power on */ - else - usleep_range(1, 10); /* >=1us at power down */ + usleep_range(500, 750); /* >=500us after RESET_N deasserted */ i2c_unlock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); } @@ -294,7 +293,7 @@ static int usb251xb_connect(struct usb251xb *hub) i2c_wb[0] = 0x01; i2c_wb[1] = USB251XB_STATUS_COMMAND_ATTACH; - usb251xb_reset(hub, 0); + usb251xb_reset(hub); err = i2c_smbus_write_i2c_block_data(hub->i2c, USB251XB_ADDR_STATUS_COMMAND, 2, i2c_wb); @@ -344,7 +343,7 @@ static int usb251xb_connect(struct usb251xb *hub) i2c_wb[USB251XB_ADDR_PORT_MAP_7] = hub->port_map7; i2c_wb[USB251XB_ADDR_STATUS_COMMAND] = USB251XB_STATUS_COMMAND_ATTACH; - usb251xb_reset(hub, 0); + usb251xb_reset(hub); /* write registers */ for (i = 0; i < (USB251XB_I2C_REG_SZ / USB251XB_I2C_WRITE_SZ); i++) { -- cgit From 905eccc6a509d2818e3dd1304c55dc5291b7ea88 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Tue, 17 Sep 2019 16:44:49 +0200 Subject: usb: usb251xb: add pm_ops Currently the driver don't support pm_ops. These ops are not necessary if the supply isn't switchable (always on). This assumptions seems to be wrong because no one needs a powered hub during suspend-to-ram/disk. So adding simple_dev_pm_ops to be able to switch off the hub during suspend and to restore the config after a resume operation. Signed-off-by: Marco Felsch Acked-by: Richard Leitner Link: https://lore.kernel.org/r/20190917144449.32739-5-m.felsch@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb251xb.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index bc031d33f433..5bba19937da1 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -701,6 +701,29 @@ static int usb251xb_i2c_probe(struct i2c_client *i2c, return usb251xb_probe(hub); } +static int __maybe_unused usb251xb_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb251xb *hub = i2c_get_clientdata(client); + + return regulator_disable(hub->vdd); +} + +static int __maybe_unused usb251xb_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb251xb *hub = i2c_get_clientdata(client); + int err; + + err = regulator_enable(hub->vdd); + if (err) + return err; + + return usb251xb_connect(hub); +} + +static SIMPLE_DEV_PM_OPS(usb251xb_pm_ops, usb251xb_suspend, usb251xb_resume); + static const struct i2c_device_id usb251xb_id[] = { { "usb2512b", 0 }, { "usb2512bi", 0 }, @@ -718,6 +741,7 @@ static struct i2c_driver usb251xb_i2c_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(usb251xb_of_match), + .pm = &usb251xb_pm_ops, }, .probe = usb251xb_i2c_probe, .id_table = usb251xb_id, -- cgit From dd3fd317e2beb899cbffcf364de049b9f9a02db5 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sun, 6 Oct 2019 05:57:27 +0800 Subject: usb: typec: hd3ss3220_irq() can be static Fixes: 1c48c759ef4b ("usb: typec: driver for TI HD3SS3220 USB Type-C DRP port controller") Signed-off-by: kbuild test robot Link: https://lore.kernel.org/r/20191005215727.qfypxoswkiyu45ak@332d0cec05f4 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/hd3ss3220.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index b8f247e792b8..1900910c637e 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -120,7 +120,7 @@ static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220) } } -irqreturn_t hd3ss3220_irq(struct hd3ss3220 *hd3ss3220) +static irqreturn_t hd3ss3220_irq(struct hd3ss3220 *hd3ss3220) { int err; -- cgit From a570ec79641a697db12a28960d6a5318ca4f37f2 Mon Sep 17 00:00:00 2001 From: Saurav Girepunje Date: Sat, 5 Oct 2019 11:19:35 +0530 Subject: usb: image: microtek.c: Remove unused variable Remove unused variable "err" from mts_scsi_queuecommand_lck(). Signed-off-by: Saurav Girepunje Link: https://lore.kernel.org/r/20191005054931.GA11565@saurav Signed-off-by: Greg Kroah-Hartman --- drivers/usb/image/microtek.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 0a57c2cc8e5a..ebe1362ac47f 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -566,7 +566,6 @@ static int mts_scsi_queuecommand_lck(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); - int err = 0; int res; MTS_DEBUG_GOT_HERE(); @@ -613,7 +612,7 @@ mts_scsi_queuecommand_lck(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback } out: - return err; + return 0; } static DEF_SCSI_QCMD(mts_scsi_queuecommand) -- cgit From 233450310b6884c5fe4174e6229800b94f8a52c0 Mon Sep 17 00:00:00 2001 From: Saurav Girepunje Date: Sat, 5 Oct 2019 11:07:36 +0530 Subject: usb: isp1760: isp1760-hcd.c: Drop condition with no effect As the "else if" and "else" branch body are identical the condition has no effect. So drop the else if condition. Signed-off-by: Saurav Girepunje Link: https://lore.kernel.org/r/20191005053733.GA10727@saurav Signed-off-by: Greg Kroah-Hartman --- drivers/usb/isp1760/isp1760-hcd.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index 320fc4739835..579a21bd70ad 100644 --- a/drivers/usb/isp1760/isp1760-hcd.c +++ b/drivers/usb/isp1760/isp1760-hcd.c @@ -1032,8 +1032,6 @@ static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd, urb->status = -EOVERFLOW; else if (FROM_DW3_CERR(ptd->dw3)) urb->status = -EPIPE; /* Stall */ - else if (ptd->dw3 & DW3_ERROR_BIT) - urb->status = -EPROTO; /* XactErr */ else urb->status = -EPROTO; /* Unknown */ /* -- cgit From 8a02a23f33be50a52cfdbf3aa825b5e0c649e67e Mon Sep 17 00:00:00 2001 From: JC Kuo Date: Sat, 5 Oct 2019 00:29:00 +0800 Subject: xhci: tegra: Parameterize mailbox register addresses Tegra194 XUSB host controller has rearranged mailbox registers. This commit makes mailbox registers address a part of "soc" data so that xhci-tegra driver can be used for Tegra194. Signed-off-by: JC Kuo Link: https://lore.kernel.org/r/20191004162906.4818-2-jckuo@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 58 +++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 16 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 111a16376087..9e78d8120a8c 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -42,19 +42,18 @@ #define XUSB_CFG_CSB_BASE_ADDR 0x800 /* FPCI mailbox registers */ -#define XUSB_CFG_ARU_MBOX_CMD 0x0e4 +/* XUSB_CFG_ARU_MBOX_CMD */ #define MBOX_DEST_FALC BIT(27) #define MBOX_DEST_PME BIT(28) #define MBOX_DEST_SMI BIT(29) #define MBOX_DEST_XHCI BIT(30) #define MBOX_INT_EN BIT(31) -#define XUSB_CFG_ARU_MBOX_DATA_IN 0x0e8 +/* XUSB_CFG_ARU_MBOX_DATA_IN and XUSB_CFG_ARU_MBOX_DATA_OUT */ #define CMD_DATA_SHIFT 0 #define CMD_DATA_MASK 0xffffff #define CMD_TYPE_SHIFT 24 #define CMD_TYPE_MASK 0xff -#define XUSB_CFG_ARU_MBOX_DATA_OUT 0x0ec -#define XUSB_CFG_ARU_MBOX_OWNER 0x0f0 +/* XUSB_CFG_ARU_MBOX_OWNER */ #define MBOX_OWNER_NONE 0 #define MBOX_OWNER_FW 1 #define MBOX_OWNER_SW 2 @@ -146,6 +145,13 @@ struct tegra_xusb_phy_type { unsigned int num; }; +struct tega_xusb_mbox_regs { + u16 cmd; + u16 data_in; + u16 data_out; + u16 owner; +}; + struct tegra_xusb_soc { const char *firmware; const char * const *supply_names; @@ -160,6 +166,8 @@ struct tegra_xusb_soc { } usb2, ulpi, hsic, usb3; } ports; + struct tega_xusb_mbox_regs mbox; + bool scale_ss_clock; bool has_ipfs; }; @@ -395,15 +403,15 @@ static int tegra_xusb_mbox_send(struct tegra_xusb *tegra, * ACK/NAK messages. */ if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) { - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); + value = fpci_readl(tegra, tegra->soc->mbox.owner); if (value != MBOX_OWNER_NONE) { dev_err(tegra->dev, "mailbox is busy\n"); return -EBUSY; } - fpci_writel(tegra, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER); + fpci_writel(tegra, MBOX_OWNER_SW, tegra->soc->mbox.owner); - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); + value = fpci_readl(tegra, tegra->soc->mbox.owner); if (value != MBOX_OWNER_SW) { dev_err(tegra->dev, "failed to acquire mailbox\n"); return -EBUSY; @@ -413,17 +421,17 @@ static int tegra_xusb_mbox_send(struct tegra_xusb *tegra, } value = tegra_xusb_mbox_pack(msg); - fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_DATA_IN); + fpci_writel(tegra, value, tegra->soc->mbox.data_in); - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD); + value = fpci_readl(tegra, tegra->soc->mbox.cmd); value |= MBOX_INT_EN | MBOX_DEST_FALC; - fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD); + fpci_writel(tegra, value, tegra->soc->mbox.cmd); if (wait_for_idle) { unsigned long timeout = jiffies + msecs_to_jiffies(250); while (time_before(jiffies, timeout)) { - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); + value = fpci_readl(tegra, tegra->soc->mbox.owner); if (value == MBOX_OWNER_NONE) break; @@ -431,7 +439,7 @@ static int tegra_xusb_mbox_send(struct tegra_xusb *tegra, } if (time_after(jiffies, timeout)) - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); + value = fpci_readl(tegra, tegra->soc->mbox.owner); if (value != MBOX_OWNER_NONE) return -ETIMEDOUT; @@ -598,16 +606,16 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) mutex_lock(&tegra->lock); - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_DATA_OUT); + value = fpci_readl(tegra, tegra->soc->mbox.data_out); tegra_xusb_mbox_unpack(&msg, value); - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD); + value = fpci_readl(tegra, tegra->soc->mbox.cmd); value &= ~MBOX_DEST_SMI; - fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD); + fpci_writel(tegra, value, tegra->soc->mbox.cmd); /* clear mailbox owner if no ACK/NAK is required */ if (!tegra_xusb_mbox_cmd_requires_ack(msg.cmd)) - fpci_writel(tegra, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER); + fpci_writel(tegra, MBOX_OWNER_NONE, tegra->soc->mbox.owner); tegra_xusb_mbox_handle(tegra, &msg); @@ -1374,6 +1382,12 @@ static const struct tegra_xusb_soc tegra124_soc = { }, .scale_ss_clock = true, .has_ipfs = true, + .mbox = { + .cmd = 0xe4, + .data_in = 0xe8, + .data_out = 0xec, + .owner = 0xf0, + }, }; MODULE_FIRMWARE("nvidia/tegra124/xusb.bin"); @@ -1406,6 +1420,12 @@ static const struct tegra_xusb_soc tegra210_soc = { }, .scale_ss_clock = false, .has_ipfs = true, + .mbox = { + .cmd = 0xe4, + .data_in = 0xe8, + .data_out = 0xec, + .owner = 0xf0, + }, }; MODULE_FIRMWARE("nvidia/tegra210/xusb.bin"); @@ -1431,6 +1451,12 @@ static const struct tegra_xusb_soc tegra186_soc = { }, .scale_ss_clock = false, .has_ipfs = false, + .mbox = { + .cmd = 0xe4, + .data_in = 0xe8, + .data_out = 0xec, + .owner = 0xf0, + }, }; static const struct of_device_id tegra_xusb_of_match[] = { -- cgit From 2538f0ee8a2920d58812687e84c854b4a9882013 Mon Sep 17 00:00:00 2001 From: JC Kuo Date: Sat, 5 Oct 2019 00:29:01 +0800 Subject: usb: host: xhci-tegra: Add Tegra194 XHCI support This commit adds Tegra194 XUSB host mode controller support. This is very similar to the existing Tegra124/Tegra210/Tegra186 XHCI, except 1. the number of ports and PHYs differs 2. the IPFS wrapper being removed 3. mailbox registers address changes Signed-off-by: JC Kuo Link: https://lore.kernel.org/r/20191004162906.4818-3-jckuo@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 9e78d8120a8c..540b47a99824 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1459,10 +1459,40 @@ static const struct tegra_xusb_soc tegra186_soc = { }, }; +static const char * const tegra194_supply_names[] = { +}; + +static const struct tegra_xusb_phy_type tegra194_phy_types[] = { + { .name = "usb3", .num = 4, }, + { .name = "usb2", .num = 4, }, +}; + +static const struct tegra_xusb_soc tegra194_soc = { + .firmware = "nvidia/tegra194/xusb.bin", + .supply_names = tegra194_supply_names, + .num_supplies = ARRAY_SIZE(tegra194_supply_names), + .phy_types = tegra194_phy_types, + .num_types = ARRAY_SIZE(tegra194_phy_types), + .ports = { + .usb3 = { .offset = 0, .count = 4, }, + .usb2 = { .offset = 4, .count = 4, }, + }, + .scale_ss_clock = false, + .has_ipfs = false, + .mbox = { + .cmd = 0x68, + .data_in = 0x6c, + .data_out = 0x70, + .owner = 0x74, + }, +}; +MODULE_FIRMWARE("nvidia/tegra194/xusb.bin"); + static const struct of_device_id tegra_xusb_of_match[] = { { .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc }, { .compatible = "nvidia,tegra210-xusb", .data = &tegra210_soc }, { .compatible = "nvidia,tegra186-xusb", .data = &tegra186_soc }, + { .compatible = "nvidia,tegra194-xusb", .data = &tegra194_soc }, { }, }; MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); -- cgit From da4b5d18dd949abdda7c8ea76c9483b5edd49616 Mon Sep 17 00:00:00 2001 From: Mao Wenan Date: Wed, 9 Oct 2019 09:47:07 +0800 Subject: usb: typec: add dependency for TYPEC_HD3SS3220 If CONFIG_TYPEC_HD3SS3220=y, CONFIG_USB_ROLE_SWITCH=m, below errors can be found: drivers/usb/typec/hd3ss3220.o: In function `hd3ss3220_remove': hd3ss3220.c:(.text+0x64): undefined reference to `usb_role_switch_put' drivers/usb/typec/hd3ss3220.o: In function `hd3ss3220_dr_set': hd3ss3220.c:(.text+0x154): undefined reference to `usb_role_switch_set_role' drivers/usb/typec/hd3ss3220.o: In function `hd3ss3220_set_role': hd3ss3220.c:(.text+0x294): undefined reference to `usb_role_switch_set_role' hd3ss3220.c:(.text+0x2f4): undefined reference to `usb_role_switch_set_role' hd3ss3220.c:(.text+0x348): undefined reference to `usb_role_switch_set_role' hd3ss3220.c:(.text+0x390): undefined reference to `usb_role_switch_set_role' drivers/usb/typec/hd3ss3220.o: In function `hd3ss3220_probe': hd3ss3220.c:(.text+0x5e8): undefined reference to `fwnode_usb_role_switch_get' hd3ss3220.c:(.text+0x8a4): undefined reference to `usb_role_switch_put' make: *** [vmlinux] Error 1 This patch add dependency USB_ROLE_SWITCH for TYPEC_HD3SS3220. Fixes: 1c48c759ef4b ("usb: typec: driver for TI HD3SS3220 USB Type-C DRP port controller") Reported-by: Hulk Robot Signed-off-by: Mao Wenan Reviewed-by: Heikki Krogerus Reviewed-by: Biju Das Link: https://lore.kernel.org/r/20191009014707.38716-1-maowenan@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index aceb2af6a998..b4f2aac7ae8a 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -53,6 +53,7 @@ source "drivers/usb/typec/ucsi/Kconfig" config TYPEC_HD3SS3220 tristate "TI HD3SS3220 Type-C DRP Port controller driver" depends on I2C + depends on USB_ROLE_SWITCH help Say Y or M here if your system has TI HD3SS3220 Type-C DRP Port controller driver. -- cgit From 71460342d48b69038037aac62b34aaa65adcc184 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 9 Oct 2019 17:04:59 +0800 Subject: usb: mtu3: add a new function to do status stage Exact a new static function to do status stage Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1570611900-7112-1-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_gadget_ep0.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 4da216c99726..df3fd055792f 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -153,6 +153,15 @@ static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy) set ? "SEND" : "CLEAR", decode_ep0_state(mtu)); } +static void ep0_do_status_stage(struct mtu3 *mtu) +{ + void __iomem *mbase = mtu->mac_base; + u32 value; + + value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; + mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND); +} + static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq); static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req) @@ -297,8 +306,7 @@ static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup) ep0_load_test_packet(mtu); /* send status before entering test mode. */ - value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; - mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND); + ep0_do_status_stage(mtu); /* wait for ACK status sent by host */ readl_poll_timeout_atomic(mbase + U3D_EP0CSR, value, @@ -632,7 +640,6 @@ __acquires(mtu->lock) { struct usb_ctrlrequest setup; struct mtu3_request *mreq; - void __iomem *mbase = mtu->mac_base; int handled = 0; ep0_read_setup(mtu, &setup); @@ -668,10 +675,7 @@ finish: mtu->delayed_status = true; } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */ - mtu3_writel(mbase, U3D_EP0CSR, - (mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS) - | EP0_SETUPPKTRDY | EP0_DATAEND); - + ep0_do_status_stage(mtu); /* complete zlp request directly */ mreq = next_ep0_request(mtu); if (mreq && !mreq->request.length) @@ -802,12 +806,9 @@ static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq) } if (mtu->delayed_status) { - u32 csr; mtu->delayed_status = false; - csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS; - csr |= EP0_SETUPPKTRDY | EP0_DATAEND; - mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr); + ep0_do_status_stage(mtu); /* needn't giveback the request for handling delay STATUS */ return 0; } -- cgit From b1a71c9047639e0a05683b55d37ff1a5c9bbcd07 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 9 Oct 2019 17:05:00 +0800 Subject: usb: mtu3: fix race condition about delayed_status usb_composite_setup_continue() may be called before composite_setup() return USB_GADGET_DELAYED_STATUS, then the controller driver will delay status stage after composite_setup() finish, but the class driver don't ask the controller to continue delayed status anymore, this will cause control transfer timeout. happens when use mass storage (also enabled other class driver): cpu1: cpu2 handle_setup(SET_CONFIG) //gadget driver unlock (g->lock) gadget_driver->setup() composite_setup() lock(cdev->lock) set_config() fsg_set_alt() // maybe some times due to many class are enabled raise FSG_STATE_CONFIG_CHANGE return USB_GADGET_DELAYED_STATUS handle_exception() usb_composite_setup_continue() unlock(cdev->lock) lock(cdev->lock) ep0_queue() lock (g->lock) //no delayed status, nothing todo unlock (g->lock) unlock(cdev->lock) return USB_GADGET_DELAYED_STATUS // composite_setup lock (g->lock) get USB_GADGET_DELAYED_STATUS //handle_setup [1] Try to fix the race condition as following: After the driver gets USB_GADGET_DELAYED_STATUS at [1], if we find there is a usb_request in ep0 request list, it means composite already asked us to continue delayed status by usb_composite_setup_continue(), so we skip request about delayed_status by composite_setup() and still do status stage. Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1570611900-7112-2-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_gadget_ep0.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index df3fd055792f..2be182bd793a 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -671,8 +671,16 @@ finish: if (mtu->test_mode) { ; /* nothing to do */ } else if (handled == USB_GADGET_DELAYED_STATUS) { - /* handle the delay STATUS phase till receive ep_queue on ep0 */ - mtu->delayed_status = true; + + mreq = next_ep0_request(mtu); + if (mreq) { + /* already asked us to continue delayed status */ + ep0_do_status_stage(mtu); + ep0_req_giveback(mtu, &mreq->request); + } else { + /* do delayed STATUS stage till receive ep0_queue */ + mtu->delayed_status = true; + } } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */ ep0_do_status_stage(mtu); -- cgit From 29234e3bb557fbddc14a815bec6da5c9ceab37cc Mon Sep 17 00:00:00 2001 From: zhengbin Date: Wed, 9 Oct 2019 16:40:33 +0800 Subject: usb: gadget: Remove set but not used variable 'opts' in acm_ms_do_config Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/gadget/legacy/acm_ms.c: In function acm_ms_do_config: drivers/usb/gadget/legacy/acm_ms.c:108:19: warning: variable opts set but not used [-Wunused-but-set-variable] It is not used since commit f78bbcae86e6 ("usb: f_mass_storage: test whether thread is running before starting another") Reported-by: Hulk Robot Signed-off-by: zhengbin Link: https://lore.kernel.org/r/1570610434-68293-2-git-send-email-zhengbin13@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/acm_ms.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c index af16672d5118..59be2d8417c9 100644 --- a/drivers/usb/gadget/legacy/acm_ms.c +++ b/drivers/usb/gadget/legacy/acm_ms.c @@ -105,7 +105,6 @@ static struct usb_function *f_msg; */ static int acm_ms_do_config(struct usb_configuration *c) { - struct fsg_opts *opts; int status; if (gadget_is_otg(c->cdev->gadget)) { @@ -113,8 +112,6 @@ static int acm_ms_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - opts = fsg_opts_from_func_inst(fi_msg); - f_acm = usb_get_function(f_acm_inst); if (IS_ERR(f_acm)) return PTR_ERR(f_acm); -- cgit From db0386312f294abccc472b90ee2bf940da12dd0d Mon Sep 17 00:00:00 2001 From: zhengbin Date: Wed, 9 Oct 2019 16:40:34 +0800 Subject: usb: gadget: Remove set but not used variable 'opts' in msg_do_config Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/gadget/legacy/mass_storage.c: In function msg_do_config: drivers/usb/gadget/legacy/mass_storage.c:108:19: warning: variable opts set but not used [-Wunused-but-set-variable] It is not used since commit f78bbcae86e6 ("usb: f_mass_storage: test whether thread is running before starting another") Reported-by: Hulk Robot Signed-off-by: zhengbin Link: https://lore.kernel.org/r/1570610434-68293-3-git-send-email-zhengbin13@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/mass_storage.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index fd5595ac5bf7..f18f77584fc2 100644 --- a/drivers/usb/gadget/legacy/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c @@ -105,7 +105,6 @@ FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); static int msg_do_config(struct usb_configuration *c) { - struct fsg_opts *opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -113,8 +112,6 @@ static int msg_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - opts = fsg_opts_from_func_inst(fi_msg); - f_msg = usb_get_function(fi_msg); if (IS_ERR(f_msg)) return PTR_ERR(f_msg); -- cgit From a4f55d8b8c146f9d99fe004bc9d1403d4c149ae3 Mon Sep 17 00:00:00 2001 From: David Heinzelmann Date: Wed, 9 Oct 2019 06:46:47 +0200 Subject: usb: hub: Check device descriptor before resusciation If a device connected to an xHCI host controller disconnects from the USB bus and then reconnects, e.g. triggered by a firmware update, then the host controller automatically activates the connection and the port is enabled. The implementation of hub_port_connect_change() assumes that if the port is enabled then nothing has changed. There is no check if the USB descriptors have changed. As a result, the kernel's internal copy of the descriptors ends up being incorrect and the device doesn't work properly anymore. The solution to the problem is for hub_port_connect_change() always to check whether the device's descriptors have changed before resuscitating an enabled port. Signed-off-by: David Heinzelmann Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191009044647.24536-1-heinzelmann.david@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 196 ++++++++++++++++++++++++++++--------------------- 1 file changed, 111 insertions(+), 85 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 236313f41f4a..fdcfa85b5b12 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4930,6 +4930,91 @@ hub_power_remaining(struct usb_hub *hub) return remaining; } + +static int descriptors_changed(struct usb_device *udev, + struct usb_device_descriptor *old_device_descriptor, + struct usb_host_bos *old_bos) +{ + int changed = 0; + unsigned index; + unsigned serial_len = 0; + unsigned len; + unsigned old_length; + int length; + char *buf; + + if (memcmp(&udev->descriptor, old_device_descriptor, + sizeof(*old_device_descriptor)) != 0) + return 1; + + if ((old_bos && !udev->bos) || (!old_bos && udev->bos)) + return 1; + if (udev->bos) { + len = le16_to_cpu(udev->bos->desc->wTotalLength); + if (len != le16_to_cpu(old_bos->desc->wTotalLength)) + return 1; + if (memcmp(udev->bos->desc, old_bos->desc, len)) + return 1; + } + + /* Since the idVendor, idProduct, and bcdDevice values in the + * device descriptor haven't changed, we will assume the + * Manufacturer and Product strings haven't changed either. + * But the SerialNumber string could be different (e.g., a + * different flash card of the same brand). + */ + if (udev->serial) + serial_len = strlen(udev->serial) + 1; + + len = serial_len; + for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { + old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); + len = max(len, old_length); + } + + buf = kmalloc(len, GFP_NOIO); + if (!buf) + /* assume the worst */ + return 1; + + for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { + old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); + length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf, + old_length); + if (length != old_length) { + dev_dbg(&udev->dev, "config index %d, error %d\n", + index, length); + changed = 1; + break; + } + if (memcmp(buf, udev->rawdescriptors[index], old_length) + != 0) { + dev_dbg(&udev->dev, "config index %d changed (#%d)\n", + index, + ((struct usb_config_descriptor *) buf)-> + bConfigurationValue); + changed = 1; + break; + } + } + + if (!changed && serial_len) { + length = usb_string(udev, udev->descriptor.iSerialNumber, + buf, serial_len); + if (length + 1 != serial_len) { + dev_dbg(&udev->dev, "serial string error %d\n", + length); + changed = 1; + } else if (memcmp(buf, udev->serial, length) != 0) { + dev_dbg(&udev->dev, "serial string changed\n"); + changed = 1; + } + } + + kfree(buf); + return changed; +} + static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange) { @@ -5167,7 +5252,9 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, { struct usb_port *port_dev = hub->ports[port1 - 1]; struct usb_device *udev = port_dev->child; + struct usb_device_descriptor descriptor; int status = -ENODEV; + int retval; dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus, portchange, portspeed(hub, portstatus)); @@ -5188,7 +5275,30 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && udev->state != USB_STATE_NOTATTACHED) { if (portstatus & USB_PORT_STAT_ENABLE) { - status = 0; /* Nothing to do */ + /* + * USB-3 connections are initialized automatically by + * the hostcontroller hardware. Therefore check for + * changed device descriptors before resuscitating the + * device. + */ + descriptor = udev->descriptor; + retval = usb_get_device_descriptor(udev, + sizeof(udev->descriptor)); + if (retval < 0) { + dev_dbg(&udev->dev, + "can't read device descriptor %d\n", + retval); + } else { + if (descriptors_changed(udev, &descriptor, + udev->bos)) { + dev_dbg(&udev->dev, + "device descriptor has changed\n"); + /* for disconnect() calls */ + udev->descriptor = descriptor; + } else { + status = 0; /* Nothing to do */ + } + } #ifdef CONFIG_PM } else if (udev->state == USB_STATE_SUSPENDED && udev->persist_enabled) { @@ -5550,90 +5660,6 @@ void usb_hub_cleanup(void) usb_deregister(&hub_driver); } /* usb_hub_cleanup() */ -static int descriptors_changed(struct usb_device *udev, - struct usb_device_descriptor *old_device_descriptor, - struct usb_host_bos *old_bos) -{ - int changed = 0; - unsigned index; - unsigned serial_len = 0; - unsigned len; - unsigned old_length; - int length; - char *buf; - - if (memcmp(&udev->descriptor, old_device_descriptor, - sizeof(*old_device_descriptor)) != 0) - return 1; - - if ((old_bos && !udev->bos) || (!old_bos && udev->bos)) - return 1; - if (udev->bos) { - len = le16_to_cpu(udev->bos->desc->wTotalLength); - if (len != le16_to_cpu(old_bos->desc->wTotalLength)) - return 1; - if (memcmp(udev->bos->desc, old_bos->desc, len)) - return 1; - } - - /* Since the idVendor, idProduct, and bcdDevice values in the - * device descriptor haven't changed, we will assume the - * Manufacturer and Product strings haven't changed either. - * But the SerialNumber string could be different (e.g., a - * different flash card of the same brand). - */ - if (udev->serial) - serial_len = strlen(udev->serial) + 1; - - len = serial_len; - for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { - old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); - len = max(len, old_length); - } - - buf = kmalloc(len, GFP_NOIO); - if (!buf) - /* assume the worst */ - return 1; - - for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { - old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); - length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf, - old_length); - if (length != old_length) { - dev_dbg(&udev->dev, "config index %d, error %d\n", - index, length); - changed = 1; - break; - } - if (memcmp(buf, udev->rawdescriptors[index], old_length) - != 0) { - dev_dbg(&udev->dev, "config index %d changed (#%d)\n", - index, - ((struct usb_config_descriptor *) buf)-> - bConfigurationValue); - changed = 1; - break; - } - } - - if (!changed && serial_len) { - length = usb_string(udev, udev->descriptor.iSerialNumber, - buf, serial_len); - if (length + 1 != serial_len) { - dev_dbg(&udev->dev, "serial string error %d\n", - length); - changed = 1; - } else if (memcmp(buf, udev->serial, length) != 0) { - dev_dbg(&udev->dev, "serial string changed\n"); - changed = 1; - } - } - - kfree(buf); - return changed; -} - /** * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) -- cgit From 5a9a8a4c505851cfb1fe5772851fb528476575bb Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 7 Oct 2019 16:38:49 +0100 Subject: usb: typec: hd3ss3220: hd3ss3220_probe() warn: passing zero to 'PTR_ERR' This patch fixes the warning passing zero to 'PTR_ERR' by changing the check from 'IS_ERR_OR_NULL' to 'IS_ERR'. Also improved the error handling on probe function. Fixes: 1c48c759ef4b ("usb: typec: driver for TI HD3SS3220 USB Type-C DRP port controller") Reported-by: kbuild test robot Reported-by: Dan Carpenter Signed-off-by: Biju Das Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/1570462729-25722-1-git-send-email-biju.das@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/hd3ss3220.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index 1900910c637e..7e5f3f73f6f4 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -178,7 +178,7 @@ static int hd3ss3220_probe(struct i2c_client *client, hd3ss3220->role_sw = fwnode_usb_role_switch_get(connector); fwnode_handle_put(connector); - if (IS_ERR_OR_NULL(hd3ss3220->role_sw)) + if (IS_ERR(hd3ss3220->role_sw)) return PTR_ERR(hd3ss3220->role_sw); hd3ss3220->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; @@ -188,20 +188,22 @@ static int hd3ss3220_probe(struct i2c_client *client, hd3ss3220->port = typec_register_port(&client->dev, &hd3ss3220->typec_cap); - if (IS_ERR(hd3ss3220->port)) - return PTR_ERR(hd3ss3220->port); + if (IS_ERR(hd3ss3220->port)) { + ret = PTR_ERR(hd3ss3220->port); + goto err_put_role; + } hd3ss3220_set_role(hd3ss3220); ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, &data); if (ret < 0) - goto error; + goto err_unreg_port; if (data & HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS) { ret = regmap_write(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, data | HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS); if (ret < 0) - goto error; + goto err_unreg_port; } if (client->irq > 0) { @@ -210,18 +212,19 @@ static int hd3ss3220_probe(struct i2c_client *client, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "hd3ss3220", &client->dev); if (ret) - goto error; + goto err_unreg_port; } ret = i2c_smbus_read_byte_data(client, HD3SS3220_REG_DEV_REV); if (ret < 0) - goto error; + goto err_unreg_port; dev_info(&client->dev, "probed revision=0x%x\n", ret); return 0; -error: +err_unreg_port: typec_unregister_port(hd3ss3220->port); +err_put_role: usb_role_switch_put(hd3ss3220->role_sw); return ret; -- cgit From dd2057e544dc92addd581a450742b8179e44e949 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 8 Oct 2019 11:02:40 +0200 Subject: USB: core: drop OOM message Drop redundant OOM message on allocation failures which would already have been logged by the allocator. This also allows us to clean up the error paths somewhat. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191008090240.30376-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 151a74a54386..ff9f50f7218f 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -800,10 +800,10 @@ int usb_get_configuration(struct usb_device *dev) { struct device *ddev = &dev->dev; int ncfg = dev->descriptor.bNumConfigurations; - int result = -ENOMEM; unsigned int cfgno, length; unsigned char *bigbuffer; struct usb_config_descriptor *desc; + int result; if (ncfg > USB_MAXCONFIG) { dev_warn(ddev, "too many configurations: %d, " @@ -819,16 +819,16 @@ int usb_get_configuration(struct usb_device *dev) length = ncfg * sizeof(struct usb_host_config); dev->config = kzalloc(length, GFP_KERNEL); if (!dev->config) - goto err2; + return -ENOMEM; length = ncfg * sizeof(char *); dev->rawdescriptors = kzalloc(length, GFP_KERNEL); if (!dev->rawdescriptors) - goto err2; + return -ENOMEM; desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL); if (!desc) - goto err2; + return -ENOMEM; for (cfgno = 0; cfgno < ncfg; cfgno++) { /* We grab just the first descriptor so we know how long @@ -890,9 +890,7 @@ int usb_get_configuration(struct usb_device *dev) err: kfree(desc); dev->descriptor.bNumConfigurations = cfgno; -err2: - if (result == -ENOMEM) - dev_err(ddev, "out of memory\n"); + return result; } -- cgit From 71464db9c2492478f2b396d3954e71b38d28e8c3 Mon Sep 17 00:00:00 2001 From: Saurav Girepunje Date: Mon, 7 Oct 2019 23:56:53 +0530 Subject: usb: core: devio.c: Fix assignment of 0/1 to bool variables Use true/false for is_in bool type in function proc_do_submiturb. Signed-off-by: Saurav Girepunje Link: https://lore.kernel.org/r/20191007182649.GA7068@saurav Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 3f899552f6e3..786580901f30 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1550,10 +1550,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb uurb->buffer_length = le16_to_cpu(dr->wLength); uurb->buffer += 8; if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) { - is_in = 1; + is_in = true; uurb->endpoint |= USB_DIR_IN; } else { - is_in = 0; + is_in = false; uurb->endpoint &= ~USB_DIR_IN; } if (is_in) -- cgit From 1141a7522e3961e8d4b20db7495d4dfb1e07d7ae Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 9 Oct 2019 12:04:49 +0000 Subject: usb: typec: remove duplicated include from hd3ss3220.c Remove duplicated include. Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20191009120449.44899-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/hd3ss3220.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index 7e5f3f73f6f4..9715600aeb04 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include -- cgit From 46f62f887b2cb008b91d5bb39a14178fd5c56fbc Mon Sep 17 00:00:00 2001 From: "Ben Dooks (Codethink)" Date: Tue, 15 Oct 2019 16:50:44 +0100 Subject: usb: renesas_usbhs: fix __le16 warnings Fix the warnings generated by casting to/from __le16 without using the correct functions. Fixes the following sparse warnings: drivers/usb/renesas_usbhs/common.c:165:25: warning: incorrect type in assignment (different base types) drivers/usb/renesas_usbhs/common.c:165:25: expected restricted __le16 [usertype] wValue drivers/usb/renesas_usbhs/common.c:165:25: got unsigned short drivers/usb/renesas_usbhs/common.c:166:25: warning: incorrect type in assignment (different base types) drivers/usb/renesas_usbhs/common.c:166:25: expected restricted __le16 [usertype] wIndex drivers/usb/renesas_usbhs/common.c:166:25: got unsigned short drivers/usb/renesas_usbhs/common.c:167:25: warning: incorrect type in assignment (different base types) drivers/usb/renesas_usbhs/common.c:167:25: expected restricted __le16 [usertype] wLength drivers/usb/renesas_usbhs/common.c:167:25: got unsigned short drivers/usb/renesas_usbhs/common.c:173:39: warning: incorrect type in argument 3 (different base types) drivers/usb/renesas_usbhs/common.c:173:39: expected unsigned short [usertype] data drivers/usb/renesas_usbhs/common.c:173:39: got restricted __le16 [usertype] wValue drivers/usb/renesas_usbhs/common.c:174:39: warning: incorrect type in argument 3 (different base types) drivers/usb/renesas_usbhs/common.c:174:39: expected unsigned short [usertype] data drivers/usb/renesas_usbhs/common.c:174:39: got restricted __le16 [usertype] wIndex drivers/usb/renesas_usbhs/common.c:175:39: warning: incorrect type in argument 3 (different base types) drivers/usb/renesas_usbhs/common.c:175:39: expected unsigned short [usertype] data Note. I belive this to be correct, and should be a no-op on arm. Signed-off-by: Ben Dooks Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20191015155044.11858-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 18d5d1a0593e..d438b7871446 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -162,17 +162,17 @@ void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) req->bRequest = (val >> 8) & 0xFF; req->bRequestType = (val >> 0) & 0xFF; - req->wValue = usbhs_read(priv, USBVAL); - req->wIndex = usbhs_read(priv, USBINDX); - req->wLength = usbhs_read(priv, USBLENG); + req->wValue = cpu_to_le16(usbhs_read(priv, USBVAL)); + req->wIndex = cpu_to_le16(usbhs_read(priv, USBINDX)); + req->wLength = cpu_to_le16(usbhs_read(priv, USBLENG)); } void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) { usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); - usbhs_write(priv, USBVAL, req->wValue); - usbhs_write(priv, USBINDX, req->wIndex); - usbhs_write(priv, USBLENG, req->wLength); + usbhs_write(priv, USBVAL, le16_to_cpu(req->wValue)); + usbhs_write(priv, USBINDX, le16_to_cpu(req->wIndex)); + usbhs_write(priv, USBLENG, le16_to_cpu(req->wLength)); usbhs_bset(priv, DCPCTR, SUREQ, SUREQ); } -- cgit From 67f195806afcb17e46a4578130526602d3465781 Mon Sep 17 00:00:00 2001 From: "Ben Dooks (Codethink)" Date: Tue, 15 Oct 2019 16:30:17 +0100 Subject: usb: renesas_usbhs: fix type of buf Fix the type of buf in __usbhsg_recip_send_status to be __le16 to avoid the following sparse warning: drivers/usb/renesas_usbhs/mod_gadget.c:335:14: warning: incorrect type in assignment (different base types) drivers/usb/renesas_usbhs/mod_gadget.c:335:14: expected unsigned short drivers/usb/renesas_usbhs/mod_gadget.c:335:14: got restricted __le16 [usertype] Signed-off-by: Ben Dooks Link: https://lore.kernel.org/r/20191015153017.10858-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/mod_gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index b47e70ffeda6..72eda9f926fe 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -315,7 +315,7 @@ static void __usbhsg_recip_send_status(struct usbhsg_gpriv *gpriv, struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp); struct device *dev = usbhsg_gpriv_to_dev(gpriv); struct usb_request *req; - unsigned short *buf; + __le16 *buf; /* alloc new usb_request for recip */ req = usb_ep_alloc_request(&dcp->ep, GFP_ATOMIC); -- cgit From c8dd192be821039d7023e0a5869d2cdf3acdef7e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 11 Oct 2019 21:50:55 +0300 Subject: usb: typec: fix an IS_ERR() vs NULL bug in hd3ss3220_probe() The device_get_named_child_node() function doesn't return error pointers, it returns NULL on error. Fixes: 1c48c759ef4b ("usb: typec: driver for TI HD3SS3220 USB Type-C DRP port controller") Signed-off-by: Dan Carpenter Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191011185055.GA20972@mwanda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/hd3ss3220.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index 9715600aeb04..8afaf5768a17 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -172,8 +172,8 @@ static int hd3ss3220_probe(struct i2c_client *client, hd3ss3220_set_source_pref(hd3ss3220, HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT); connector = device_get_named_child_node(hd3ss3220->dev, "connector"); - if (IS_ERR(connector)) - return PTR_ERR(connector); + if (!connector) + return -ENODEV; hd3ss3220->role_sw = fwnode_usb_role_switch_get(connector); fwnode_handle_put(connector); -- cgit From be731286685af4de728851991b98cb8f579f58f6 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Fri, 11 Oct 2019 11:59:50 -0700 Subject: USB: host: ohci-at91: Remove unused variable regs in at91_stop_hc drivers/usb/host/ohci-at91.c:118:28: warning: unused variable 'regs' [-Wunused-variable] struct ohci_regs __iomem *regs = hcd->regs; ^ 1 warning generated. Fixes: 9c4567fa0a44 ("USB: host: ohci-at91: completely shutdown the controller in at91_stop_hc()") Signed-off-by: Nathan Chancellor Acked-by: Nicolas Ferre Link: https://lore.kernel.org/r/20191011185950.1470-1-natechancellor@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 513e48397743..b635c6a1b1a9 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -115,7 +115,6 @@ static void at91_start_hc(struct platform_device *pdev) static void at91_stop_hc(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct ohci_regs __iomem *regs = hcd->regs; struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd); dev_dbg(&pdev->dev, "stop\n"); -- cgit From abb0b3d96a1f9407dd66831ae33985a386d4200d Mon Sep 17 00:00:00 2001 From: Ingo Rohloff Date: Fri, 11 Oct 2019 13:55:18 +0200 Subject: usb: usbfs: Suppress problematic bind and unbind uevents. commit 1455cf8dbfd0 ("driver core: emit uevents when device is bound to a driver") added bind and unbind uevents when a driver is bound or unbound to a physical device. For USB devices which are handled via the generic usbfs layer (via libusb for example), this is problematic: Each time a user space program calls ioctl(usb_fd, USBDEVFS_CLAIMINTERFACE, &usb_intf_nr); and then later ioctl(usb_fd, USBDEVFS_RELEASEINTERFACE, &usb_intf_nr); The kernel will now produce a bind or unbind event, which does not really contain any useful information. This allows a user space program to run a DoS attack against programs which listen to uevents (in particular systemd/eudev/upowerd): A malicious user space program just has to call in a tight loop ioctl(usb_fd, USBDEVFS_CLAIMINTERFACE, &usb_intf_nr); ioctl(usb_fd, USBDEVFS_RELEASEINTERFACE, &usb_intf_nr); With this loop the malicious user space program floods the kernel and all programs listening to uevents with tons of bind and unbind events. This patch suppresses uevents for ioctls USBDEVFS_CLAIMINTERFACE and USBDEVFS_RELEASEINTERFACE. Signed-off-by: Ingo Rohloff Link: https://lore.kernel.org/r/20191011115518.2801-1-ingo.rohloff@lauterbach.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 786580901f30..879d03f5127c 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -764,8 +764,15 @@ static int claimintf(struct usb_dev_state *ps, unsigned int ifnum) intf = usb_ifnum_to_if(dev, ifnum); if (!intf) err = -ENOENT; - else + else { + unsigned int old_suppress; + + /* suppress uevents while claiming interface */ + old_suppress = dev_get_uevent_suppress(&intf->dev); + dev_set_uevent_suppress(&intf->dev, 1); err = usb_driver_claim_interface(&usbfs_driver, intf, ps); + dev_set_uevent_suppress(&intf->dev, old_suppress); + } if (err == 0) set_bit(ifnum, &ps->ifclaimed); return err; @@ -785,7 +792,13 @@ static int releaseintf(struct usb_dev_state *ps, unsigned int ifnum) if (!intf) err = -ENOENT; else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) { + unsigned int old_suppress; + + /* suppress uevents while releasing interface */ + old_suppress = dev_get_uevent_suppress(&intf->dev); + dev_set_uevent_suppress(&intf->dev, 1); usb_driver_release_interface(&usbfs_driver, intf); + dev_set_uevent_suppress(&intf->dev, old_suppress); err = 0; } return err; -- cgit From 9ba3aca8fe82318805709036bd50bee64570088b Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 9 Aug 2019 12:15:52 -0700 Subject: usb: dwc3: Disable phy suspend after power-on reset For DRD controllers, the programming guide recommended that GUSB3PIPECTL.SUSPENDABLE and GUSB2PHYCFG.SUSPHY to be cleared after power-on reset and only set after the controller initialization is completed. This can be done after device soft-reset in dwc3_core_init(). This patch makes sure to clear GUSB3PIPECTL.SUSPENDABLE and GUSB2PHYCFG.SUSPHY before core initialization and only set them after the device soft-reset is completed. Reference: DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 1.2.49 and 1.2.45 Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 999ce5e84d3c..2991e5056600 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -567,8 +567,11 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc) */ static int dwc3_phy_setup(struct dwc3 *dwc) { + unsigned int hw_mode; u32 reg; + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); /* @@ -586,6 +589,14 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->revision > DWC3_REVISION_194A) reg |= DWC3_GUSB3PIPECTL_SUSPHY; + /* + * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be cleared after + * power-on reset, and it can be set after core initialization, which is + * after device soft-reset during initialization. + */ + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + if (dwc->u2ss_inp3_quirk) reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK; @@ -669,6 +680,14 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->revision > DWC3_REVISION_194A) reg |= DWC3_GUSB2PHYCFG_SUSPHY; + /* + * For DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared after + * power-on reset, and it can be set after core initialization, which is + * after device soft-reset during initialization. + */ + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + if (dwc->dis_u2_susphy_quirk) reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; @@ -903,9 +922,12 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) */ static int dwc3_core_init(struct dwc3 *dwc) { + unsigned int hw_mode; u32 reg; int ret; + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + /* * Write Linux Version Code to our GUID register so it's easy to figure * out which kernel version a bug was found. @@ -941,6 +963,21 @@ static int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err0a; + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && + dwc->revision > DWC3_REVISION_194A) { + if (!dwc->dis_u3_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + } + + if (!dwc->dis_u2_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + } + dwc3_core_setup_global_control(dwc); dwc3_core_num_eps(dwc); -- cgit From daf82bd24e308c5a83758047aff1bd81edda4f11 Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw Date: Sat, 10 Aug 2019 10:42:48 +0200 Subject: usb: gadget: u_serial: add missing port entry locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gserial_alloc_line() misses locking (for a release barrier) while resetting port entry on TTY allocation failure. Fix this. Cc: stable@vger.kernel.org Signed-off-by: MichaÅ‚ MirosÅ‚aw Reviewed-by: Greg Kroah-Hartman Tested-by: Ladislav Michl Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_serial.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 65f634ec7fc2..bb1e2e1d0076 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1239,8 +1239,10 @@ int gserial_alloc_line(unsigned char *line_num) __func__, port_num, PTR_ERR(tty_dev)); ret = PTR_ERR(tty_dev); + mutex_lock(&ports[port_num].lock); port = ports[port_num].port; ports[port_num].port = NULL; + mutex_unlock(&ports[port_num].lock); gserial_free_port(port); goto err; } -- cgit From fe1ea63ad8064d8518b5e3fd1a6869b4463b5c73 Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw Date: Sat, 10 Aug 2019 10:42:49 +0200 Subject: usb: gadget: u_serial: reimplement console support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite console support to fix a few shortcomings of the old code preventing its use with multiple ports. This removes some duplicated code and replaces a custom kthread with simpler workqueue item. Only port ttyGS0 gets to be a console for now. Signed-off-by: MichaÅ‚ MirosÅ‚aw Reviewed-by: Greg Kroah-Hartman Tested-by: Ladislav Michl Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_serial.c | 339 +++++++++++++++------------------ 1 file changed, 158 insertions(+), 181 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index bb1e2e1d0076..94f6999e8262 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -82,14 +82,12 @@ #define GS_CONSOLE_BUF_SIZE 8192 /* console info */ -struct gscons_info { - struct gs_port *port; - struct task_struct *console_thread; - struct kfifo con_buf; - /* protect the buf and busy flag */ - spinlock_t con_lock; - int req_busy; - struct usb_request *console_req; +struct gs_console { + struct console console; + struct work_struct work; + spinlock_t lock; + struct usb_request *req; + struct kfifo buf; }; /* @@ -101,6 +99,9 @@ struct gs_port { spinlock_t port_lock; /* guard port_* access */ struct gserial *port_usb; +#ifdef CONFIG_U_SERIAL_CONSOLE + struct gs_console *console; +#endif bool openclose; /* open/close in progress */ u8 port_num; @@ -889,36 +890,9 @@ static struct tty_driver *gs_tty_driver; #ifdef CONFIG_U_SERIAL_CONSOLE -static struct gscons_info gscons_info; -static struct console gserial_cons; - -static struct usb_request *gs_request_new(struct usb_ep *ep) +static void gs_console_complete_out(struct usb_ep *ep, struct usb_request *req) { - struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (!req) - return NULL; - - req->buf = kmalloc(ep->maxpacket, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - return NULL; - } - - return req; -} - -static void gs_request_free(struct usb_request *req, struct usb_ep *ep) -{ - if (!req) - return; - - kfree(req->buf); - usb_ep_free_request(ep, req); -} - -static void gs_complete_out(struct usb_ep *ep, struct usb_request *req) -{ - struct gscons_info *info = &gscons_info; + struct gs_console *cons = req->context; switch (req->status) { default: @@ -927,12 +901,12 @@ static void gs_complete_out(struct usb_ep *ep, struct usb_request *req) /* fall through */ case 0: /* normal completion */ - spin_lock(&info->con_lock); - info->req_busy = 0; - spin_unlock(&info->con_lock); - - wake_up_process(info->console_thread); + spin_lock(&cons->lock); + req->length = 0; + schedule_work(&cons->work); + spin_unlock(&cons->lock); break; + case -ECONNRESET: case -ESHUTDOWN: /* disconnect */ pr_vdebug("%s: %s shutdown\n", __func__, ep->name); @@ -940,190 +914,190 @@ static void gs_complete_out(struct usb_ep *ep, struct usb_request *req) } } -static int gs_console_connect(int port_num) +static void __gs_console_push(struct gs_console *cons) { - struct gscons_info *info = &gscons_info; - struct gs_port *port; + struct usb_request *req = cons->req; struct usb_ep *ep; + size_t size; - if (port_num != gserial_cons.index) { - pr_err("%s: port num [%d] is not support console\n", - __func__, port_num); - return -ENXIO; - } + if (!req) + return; /* disconnected */ - port = ports[port_num].port; - ep = port->port_usb->in; - if (!info->console_req) { - info->console_req = gs_request_new(ep); - if (!info->console_req) - return -ENOMEM; - info->console_req->complete = gs_complete_out; - } + if (req->length) + return; /* busy */ - info->port = port; - spin_lock(&info->con_lock); - info->req_busy = 0; - spin_unlock(&info->con_lock); - pr_vdebug("port[%d] console connect!\n", port_num); - return 0; + ep = cons->console.data; + size = kfifo_out(&cons->buf, req->buf, ep->maxpacket); + if (!size) + return; + + req->length = size; + if (usb_ep_queue(ep, req, GFP_ATOMIC)) + req->length = 0; } -static void gs_console_disconnect(struct usb_ep *ep) +static void gs_console_work(struct work_struct *work) { - struct gscons_info *info = &gscons_info; - struct usb_request *req = info->console_req; + struct gs_console *cons = container_of(work, struct gs_console, work); + + spin_lock_irq(&cons->lock); - gs_request_free(req, ep); - info->console_req = NULL; + __gs_console_push(cons); + + spin_unlock_irq(&cons->lock); } -static int gs_console_thread(void *data) +static void gs_console_write(struct console *co, + const char *buf, unsigned count) { - struct gscons_info *info = &gscons_info; - struct gs_port *port; - struct usb_request *req; - struct usb_ep *ep; - int xfer, ret, count, size; + struct gs_console *cons = container_of(co, struct gs_console, console); + unsigned long flags; - do { - port = info->port; - set_current_state(TASK_INTERRUPTIBLE); - if (!port || !port->port_usb - || !port->port_usb->in || !info->console_req) - goto sched; - - req = info->console_req; - ep = port->port_usb->in; - - spin_lock_irq(&info->con_lock); - count = kfifo_len(&info->con_buf); - size = ep->maxpacket; - - if (count > 0 && !info->req_busy) { - set_current_state(TASK_RUNNING); - if (count < size) - size = count; - - xfer = kfifo_out(&info->con_buf, req->buf, size); - req->length = xfer; - - spin_unlock(&info->con_lock); - ret = usb_ep_queue(ep, req, GFP_ATOMIC); - spin_lock(&info->con_lock); - if (ret < 0) - info->req_busy = 0; - else - info->req_busy = 1; - - spin_unlock_irq(&info->con_lock); - } else { - spin_unlock_irq(&info->con_lock); -sched: - if (kthread_should_stop()) { - set_current_state(TASK_RUNNING); - break; - } - schedule(); - } - } while (1); + spin_lock_irqsave(&cons->lock, flags); - return 0; + kfifo_in(&cons->buf, buf, count); + + if (cons->req && !cons->req->length) + schedule_work(&cons->work); + + spin_unlock_irqrestore(&cons->lock, flags); } -static int gs_console_setup(struct console *co, char *options) +static struct tty_driver *gs_console_device(struct console *co, int *index) { - struct gscons_info *info = &gscons_info; - int status; + *index = co->index; + return gs_tty_driver; +} - info->port = NULL; - info->console_req = NULL; - info->req_busy = 0; - spin_lock_init(&info->con_lock); +static int gs_console_connect(struct gs_port *port) +{ + struct gs_console *cons = port->console; + struct usb_request *req; + struct usb_ep *ep; - status = kfifo_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL); - if (status) { - pr_err("%s: allocate console buffer failed\n", __func__); - return status; - } + if (!cons) + return 0; - info->console_thread = kthread_create(gs_console_thread, - co, "gs_console"); - if (IS_ERR(info->console_thread)) { - pr_err("%s: cannot create console thread\n", __func__); - kfifo_free(&info->con_buf); - return PTR_ERR(info->console_thread); - } - wake_up_process(info->console_thread); + ep = port->port_usb->in; + req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC); + if (!req) + return -ENOMEM; + req->complete = gs_console_complete_out; + req->context = cons; + req->length = 0; + + spin_lock(&cons->lock); + cons->req = req; + cons->console.data = ep; + spin_unlock(&cons->lock); + + pr_debug("ttyGS%d: console connected!\n", port->port_num); + + schedule_work(&cons->work); return 0; } -static void gs_console_write(struct console *co, - const char *buf, unsigned count) +static void gs_console_disconnect(struct gs_port *port) { - struct gscons_info *info = &gscons_info; - unsigned long flags; + struct gs_console *cons = port->console; + struct usb_request *req; + struct usb_ep *ep; + + if (!cons) + return; - spin_lock_irqsave(&info->con_lock, flags); - kfifo_in(&info->con_buf, buf, count); - spin_unlock_irqrestore(&info->con_lock, flags); + spin_lock(&cons->lock); - wake_up_process(info->console_thread); + req = cons->req; + ep = cons->console.data; + cons->req = NULL; + + spin_unlock(&cons->lock); + + if (!req) + return; + + usb_ep_dequeue(ep, req); + gs_free_req(ep, req); } -static struct tty_driver *gs_console_device(struct console *co, int *index) +static int gs_console_init(struct gs_port *port) { - struct tty_driver **p = (struct tty_driver **)co->data; + struct gs_console *cons; + int err; - if (!*p) - return NULL; + if (port->console) + return 0; - *index = co->index; - return *p; -} + cons = kzalloc(sizeof(*port->console), GFP_KERNEL); + if (!cons) + return -ENOMEM; -static struct console gserial_cons = { - .name = "ttyGS", - .write = gs_console_write, - .device = gs_console_device, - .setup = gs_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &gs_tty_driver, -}; + strcpy(cons->console.name, "ttyGS"); + cons->console.write = gs_console_write; + cons->console.device = gs_console_device; + cons->console.flags = CON_PRINTBUFFER; + cons->console.index = port->port_num; -static void gserial_console_init(void) -{ - register_console(&gserial_cons); + INIT_WORK(&cons->work, gs_console_work); + spin_lock_init(&cons->lock); + + err = kfifo_alloc(&cons->buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL); + if (err) { + pr_err("ttyGS%d: allocate console buffer failed\n", port->port_num); + kfree(cons); + return err; + } + + port->console = cons; + register_console(&cons->console); + + spin_lock_irq(&port->port_lock); + if (port->port_usb) + gs_console_connect(port); + spin_unlock_irq(&port->port_lock); + + return 0; } -static void gserial_console_exit(void) +static void gs_console_exit(struct gs_port *port) { - struct gscons_info *info = &gscons_info; + struct gs_console *cons = port->console; + + if (!cons) + return; + + unregister_console(&cons->console); + + spin_lock_irq(&port->port_lock); + if (cons->req) + gs_console_disconnect(port); + spin_unlock_irq(&port->port_lock); - unregister_console(&gserial_cons); - if (!IS_ERR_OR_NULL(info->console_thread)) - kthread_stop(info->console_thread); - kfifo_free(&info->con_buf); + cancel_work_sync(&cons->work); + kfifo_free(&cons->buf); + kfree(cons); + port->console = NULL; } #else -static int gs_console_connect(int port_num) +static int gs_console_connect(struct gs_port *port) { return 0; } -static void gs_console_disconnect(struct usb_ep *ep) +static void gs_console_disconnect(struct gs_port *port) { } -static void gserial_console_init(void) +static int gs_console_init(struct gs_port *port) { + return -ENOSYS; } -static void gserial_console_exit(void) +static void gs_console_exit(struct gs_port *port) { } @@ -1197,18 +1171,19 @@ void gserial_free_line(unsigned char port_num) return; } port = ports[port_num].port; + gs_console_exit(port); ports[port_num].port = NULL; mutex_unlock(&ports[port_num].lock); gserial_free_port(port); tty_unregister_device(gs_tty_driver, port_num); - gserial_console_exit(); } EXPORT_SYMBOL_GPL(gserial_free_line); int gserial_alloc_line(unsigned char *line_num) { struct usb_cdc_line_coding coding; + struct gs_port *port; struct device *tty_dev; int ret; int port_num; @@ -1231,23 +1206,24 @@ int gserial_alloc_line(unsigned char *line_num) /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ - tty_dev = tty_port_register_device(&ports[port_num].port->port, + port = ports[port_num].port; + tty_dev = tty_port_register_device(&port->port, gs_tty_driver, port_num, NULL); if (IS_ERR(tty_dev)) { - struct gs_port *port; pr_err("%s: failed to register tty for port %d, err %ld\n", __func__, port_num, PTR_ERR(tty_dev)); ret = PTR_ERR(tty_dev); mutex_lock(&ports[port_num].lock); - port = ports[port_num].port; ports[port_num].port = NULL; mutex_unlock(&ports[port_num].lock); gserial_free_port(port); goto err; } *line_num = port_num; - gserial_console_init(); + + if (!port_num) + gs_console_init(port); err: return ret; } @@ -1329,7 +1305,7 @@ int gserial_connect(struct gserial *gser, u8 port_num) gser->disconnect(gser); } - status = gs_console_connect(port_num); + status = gs_console_connect(port); spin_unlock_irqrestore(&port->port_lock, flags); return status; @@ -1361,6 +1337,8 @@ void gserial_disconnect(struct gserial *gser) /* tell the TTY glue not to do I/O here any more */ spin_lock_irqsave(&port->port_lock, flags); + gs_console_disconnect(port); + /* REVISIT as above: how best to track this? */ port->port_line_coding = gser->port_line_coding; @@ -1388,7 +1366,6 @@ void gserial_disconnect(struct gserial *gser) port->read_allocated = port->read_started = port->write_allocated = port->write_started = 0; - gs_console_disconnect(gser->in); spin_unlock_irqrestore(&port->port_lock, flags); } EXPORT_SYMBOL_GPL(gserial_disconnect); -- cgit From b417343c6a0f50487d59aef975da0a6c7b69d348 Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw Date: Sat, 10 Aug 2019 10:42:50 +0200 Subject: usb: gadget: u_serial: make OBEX port not a console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent OBEX serial port from ever becoming a console. Console messages will definitely break the protocol, and since you have to instantiate the port making it explicitly for OBEX, there is no point in allowing console to break it by mistake. Signed-off-by: MichaÅ‚ MirosÅ‚aw Reviewed-by: Greg Kroah-Hartman Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_obex.c | 2 +- drivers/usb/gadget/function/u_serial.c | 16 ++++++++++++---- drivers/usb/gadget/function/u_serial.h | 1 + 3 files changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index 55b7f57d2dc7..ab26d84ed95e 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -432,7 +432,7 @@ static struct usb_function_instance *obex_alloc_inst(void) return ERR_PTR(-ENOMEM); opts->func_inst.free_func_inst = obex_free_inst; - ret = gserial_alloc_line(&opts->port_num); + ret = gserial_alloc_line_no_console(&opts->port_num); if (ret) { kfree(opts); return ERR_PTR(ret); diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 94f6999e8262..62280c23cde2 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1180,7 +1180,7 @@ void gserial_free_line(unsigned char port_num) } EXPORT_SYMBOL_GPL(gserial_free_line); -int gserial_alloc_line(unsigned char *line_num) +int gserial_alloc_line_no_console(unsigned char *line_num) { struct usb_cdc_line_coding coding; struct gs_port *port; @@ -1221,12 +1221,20 @@ int gserial_alloc_line(unsigned char *line_num) goto err; } *line_num = port_num; - - if (!port_num) - gs_console_init(port); err: return ret; } +EXPORT_SYMBOL_GPL(gserial_alloc_line_no_console); + +int gserial_alloc_line(unsigned char *line_num) +{ + int ret = gserial_alloc_line_no_console(line_num); + + if (!ret && !*line_num) + gs_console_init(ports[*line_num].port); + + return ret; +} EXPORT_SYMBOL_GPL(gserial_alloc_line); /** diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h index 9acaac1cbb75..8b472b0c8cb4 100644 --- a/drivers/usb/gadget/function/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h @@ -54,6 +54,7 @@ struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags); void gs_free_req(struct usb_ep *, struct usb_request *req); /* management of individual TTY ports */ +int gserial_alloc_line_no_console(unsigned char *port_line); int gserial_alloc_line(unsigned char *port_line); void gserial_free_line(unsigned char port_line); -- cgit From d7cb8fb7aa7dc47d1b751c17c15672068667053c Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw Date: Sat, 10 Aug 2019 10:42:51 +0200 Subject: usb: gadget: u_serial: allow more console gadget ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow configuring more than one console using USB serial or ACM gadget. By default, only first (ttyGS0) is a console, but this may be changed using function's new "console" attribute. Signed-off-by: MichaÅ‚ MirosÅ‚aw Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_acm.c | 21 +++++++++++++++ drivers/usb/gadget/function/f_serial.c | 21 +++++++++++++++ drivers/usb/gadget/function/u_serial.c | 48 ++++++++++++++++++++++++++++++++++ drivers/usb/gadget/function/u_serial.h | 7 +++++ 4 files changed, 97 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 9fc98de83624..7c152c28b26c 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -771,6 +771,24 @@ static struct configfs_item_operations acm_item_ops = { .release = acm_attr_release, }; +#ifdef CONFIG_U_SERIAL_CONSOLE + +static ssize_t f_acm_console_store(struct config_item *item, + const char *page, size_t count) +{ + return gserial_set_console(to_f_serial_opts(item)->port_num, + page, count); +} + +static ssize_t f_acm_console_show(struct config_item *item, char *page) +{ + return gserial_get_console(to_f_serial_opts(item)->port_num, page); +} + +CONFIGFS_ATTR(f_acm_, console); + +#endif /* CONFIG_U_SERIAL_CONSOLE */ + static ssize_t f_acm_port_num_show(struct config_item *item, char *page) { return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num); @@ -779,6 +797,9 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page) CONFIGFS_ATTR_RO(f_acm_, port_num); static struct configfs_attribute *acm_attrs[] = { +#ifdef CONFIG_U_SERIAL_CONSOLE + &f_acm_attr_console, +#endif &f_acm_attr_port_num, NULL, }; diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index c860f30a0ea2..1406255d0865 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -266,6 +266,24 @@ static struct configfs_item_operations serial_item_ops = { .release = serial_attr_release, }; +#ifdef CONFIG_U_SERIAL_CONSOLE + +static ssize_t f_serial_console_store(struct config_item *item, + const char *page, size_t count) +{ + return gserial_set_console(to_f_serial_opts(item)->port_num, + page, count); +} + +static ssize_t f_serial_console_show(struct config_item *item, char *page) +{ + return gserial_get_console(to_f_serial_opts(item)->port_num, page); +} + +CONFIGFS_ATTR(f_serial_, console); + +#endif /* CONFIG_U_SERIAL_CONSOLE */ + static ssize_t f_serial_port_num_show(struct config_item *item, char *page) { return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num); @@ -274,6 +292,9 @@ static ssize_t f_serial_port_num_show(struct config_item *item, char *page) CONFIGFS_ATTR_RO(f_serial_, port_num); static struct configfs_attribute *acm_attrs[] = { +#ifdef CONFIG_U_SERIAL_CONSOLE + &f_serial_attr_console, +#endif &f_serial_attr_port_num, NULL, }; diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 62280c23cde2..0da00546006f 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1081,6 +1081,54 @@ static void gs_console_exit(struct gs_port *port) port->console = NULL; } +ssize_t gserial_set_console(unsigned char port_num, const char *page, size_t count) +{ + struct gs_port *port; + bool enable; + int ret; + + ret = strtobool(page, &enable); + if (ret) + return ret; + + mutex_lock(&ports[port_num].lock); + port = ports[port_num].port; + + if (WARN_ON(port == NULL)) { + ret = -ENXIO; + goto out; + } + + if (enable) + ret = gs_console_init(port); + else + gs_console_exit(port); +out: + mutex_unlock(&ports[port_num].lock); + + return ret < 0 ? ret : count; +} +EXPORT_SYMBOL_GPL(gserial_set_console); + +ssize_t gserial_get_console(unsigned char port_num, char *page) +{ + struct gs_port *port; + ssize_t ret; + + mutex_lock(&ports[port_num].lock); + port = ports[port_num].port; + + if (WARN_ON(port == NULL)) + ret = -ENXIO; + else + ret = sprintf(page, "%u\n", !!port->console); + + mutex_unlock(&ports[port_num].lock); + + return ret; +} +EXPORT_SYMBOL_GPL(gserial_get_console); + #else static int gs_console_connect(struct gs_port *port) diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h index 8b472b0c8cb4..e5b08ab8cf7a 100644 --- a/drivers/usb/gadget/function/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h @@ -58,6 +58,13 @@ int gserial_alloc_line_no_console(unsigned char *port_line); int gserial_alloc_line(unsigned char *port_line); void gserial_free_line(unsigned char port_line); +#ifdef CONFIG_U_SERIAL_CONSOLE + +ssize_t gserial_set_console(unsigned char port_num, const char *page, size_t count); +ssize_t gserial_get_console(unsigned char port_num, char *page); + +#endif /* CONFIG_U_SERIAL_CONSOLE */ + /* connect/disconnect is handled by individual functions */ int gserial_connect(struct gserial *, u8 port_num); void gserial_disconnect(struct gserial *); -- cgit From bd25a14edb75038f6a177ac566ba2e6d7a2439be Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw Date: Sat, 10 Aug 2019 10:42:52 +0200 Subject: usb: gadget: legacy/serial: allow dynamic removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Legacy serial USB gadget is still useful as an early console, before userspace is up. Later it could be replaced with proper configfs-configured composite gadget - that use case is enabled by this patch. Signed-off-by: MichaÅ‚ MirosÅ‚aw Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/serial.c | 49 +++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/serial.c b/drivers/usb/gadget/legacy/serial.c index de30d7628eef..da44f89f5e73 100644 --- a/drivers/usb/gadget/legacy/serial.c +++ b/drivers/usb/gadget/legacy/serial.c @@ -97,6 +97,36 @@ static unsigned n_ports = 1; module_param(n_ports, uint, 0); MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); +static bool enable = true; + +static int switch_gserial_enable(bool do_enable); + +static int enable_set(const char *s, const struct kernel_param *kp) +{ + bool do_enable; + int ret; + + if (!s) /* called for no-arg enable == default */ + return 0; + + ret = strtobool(s, &do_enable); + if (ret || enable == do_enable) + return ret; + + ret = switch_gserial_enable(do_enable); + if (!ret) + enable = do_enable; + + return ret; +} + +static const struct kernel_param_ops enable_ops = { + .set = enable_set, + .get = param_get_bool, +}; + +module_param_cb(enable, &enable_ops, &enable, 0644); + /*-------------------------------------------------------------------------*/ static struct usb_configuration serial_config_driver = { @@ -240,6 +270,19 @@ static struct usb_composite_driver gserial_driver = { .unbind = gs_unbind, }; +static int switch_gserial_enable(bool do_enable) +{ + if (!serial_config_driver.label) + /* init() was not called, yet */ + return 0; + + if (do_enable) + return usb_composite_probe(&gserial_driver); + + usb_composite_unregister(&gserial_driver); + return 0; +} + static int __init init(void) { /* We *could* export two configs; that'd be much cleaner... @@ -266,12 +309,16 @@ static int __init init(void) } strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; + if (!enable) + return 0; + return usb_composite_probe(&gserial_driver); } module_init(init); static void __exit cleanup(void) { - usb_composite_unregister(&gserial_driver); + if (enable) + usb_composite_unregister(&gserial_driver); } module_exit(cleanup); -- cgit From ef9b457d0dd2438b492d773c9e98c3d1bd9ec037 Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw Date: Sat, 10 Aug 2019 10:42:52 +0200 Subject: usb: gadget: u_serial: diagnose missed console messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Insert markers in console stream marking places where data is missing. This makes the hole in the data stand out clearly instead of glueing together unrelated messages. Example output as seen from USB host side: [ 0.064078] pinctrl core: registered pin 16 (UART3_RTS_N PC0) on 70000868.pinmux [ 0.064130] pinctrl [missed 114987 bytes] [ 4.302299] udevd[134]: starting version 3.2.5 [ 4.306845] random: udevd: uninitialized urandom read (16 bytes read) Signed-off-by: MichaÅ‚ MirosÅ‚aw Reviewed-by: Greg Kroah-Hartman Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_serial.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 0da00546006f..a248ed0fd5d2 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -88,6 +88,7 @@ struct gs_console { spinlock_t lock; struct usb_request *req; struct kfifo buf; + size_t missed; }; /* @@ -931,6 +932,15 @@ static void __gs_console_push(struct gs_console *cons) if (!size) return; + if (cons->missed && ep->maxpacket >= 64) { + char buf[64]; + size_t len; + + len = sprintf(buf, "\n[missed %zu bytes]\n", cons->missed); + kfifo_in(&cons->buf, buf, len); + cons->missed = 0; + } + req->length = size; if (usb_ep_queue(ep, req, GFP_ATOMIC)) req->length = 0; @@ -952,10 +962,13 @@ static void gs_console_write(struct console *co, { struct gs_console *cons = container_of(co, struct gs_console, console); unsigned long flags; + size_t n; spin_lock_irqsave(&cons->lock, flags); - kfifo_in(&cons->buf, buf, count); + n = kfifo_in(&cons->buf, buf, count); + if (n < count) + cons->missed += count - n; if (cons->req && !cons->req->length) schedule_work(&cons->work); -- cgit From c6561082b833afde1469346c7e65e0e6b04fc322 Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw Date: Sat, 10 Aug 2019 10:42:53 +0200 Subject: usb: gadget: u_serial: use mutex for serialising open()s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove home-made waiting mechanism from gs_open() and rely on portmaster's mutex to do the job. Note: This releases thread waiting on close() when another thread open()s simultaneously. Signed-off-by: MichaÅ‚ MirosÅ‚aw Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_serial.c | 112 +++++++++++---------------------- 1 file changed, 37 insertions(+), 75 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index a248ed0fd5d2..f986e5c55974 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -104,7 +104,6 @@ struct gs_port { struct gs_console *console; #endif - bool openclose; /* open/close in progress */ u8 port_num; struct list_head read_pool; @@ -588,82 +587,45 @@ static int gs_open(struct tty_struct *tty, struct file *file) { int port_num = tty->index; struct gs_port *port; - int status; - - do { - mutex_lock(&ports[port_num].lock); - port = ports[port_num].port; - if (!port) - status = -ENODEV; - else { - spin_lock_irq(&port->port_lock); - - /* already open? Great. */ - if (port->port.count) { - status = 0; - port->port.count++; - - /* currently opening/closing? wait ... */ - } else if (port->openclose) { - status = -EBUSY; - - /* ... else we do the work */ - } else { - status = -EAGAIN; - port->openclose = true; - } - spin_unlock_irq(&port->port_lock); - } - mutex_unlock(&ports[port_num].lock); + int status = 0; - switch (status) { - default: - /* fully handled */ - return status; - case -EAGAIN: - /* must do the work */ - break; - case -EBUSY: - /* wait for EAGAIN task to finish */ - msleep(1); - /* REVISIT could have a waitchannel here, if - * concurrent open performance is important - */ - break; - } - } while (status != -EAGAIN); + mutex_lock(&ports[port_num].lock); + port = ports[port_num].port; + if (!port) { + status = -ENODEV; + goto out; + } - /* Do the "real open" */ spin_lock_irq(&port->port_lock); /* allocate circular buffer on first open */ if (!kfifo_initialized(&port->port_write_buf)) { spin_unlock_irq(&port->port_lock); + + /* + * portmaster's mutex still protects from simultaneous open(), + * and close() can't happen, yet. + */ + status = kfifo_alloc(&port->port_write_buf, WRITE_BUF_SIZE, GFP_KERNEL); - spin_lock_irq(&port->port_lock); - if (status) { pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n", - port->port_num, tty, file); - port->openclose = false; - goto exit_unlock_port; + port_num, tty, file); + goto out; } - } - /* REVISIT if REMOVED (ports[].port NULL), abort the open - * to let rmmod work faster (but this way isn't wrong). - */ + spin_lock_irq(&port->port_lock); + } - /* REVISIT maybe wait for "carrier detect" */ + /* already open? Great. */ + if (port->port.count++) + goto exit_unlock_port; tty->driver_data = port; port->port.tty = tty; - port->port.count = 1; - port->openclose = false; - /* if connected, start the I/O stream */ if (port->port_usb) { struct gserial *gser = port->port_usb; @@ -677,20 +639,21 @@ static int gs_open(struct tty_struct *tty, struct file *file) pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file); - status = 0; - exit_unlock_port: spin_unlock_irq(&port->port_lock); +out: + mutex_unlock(&ports[port_num].lock); return status; } -static int gs_writes_finished(struct gs_port *p) +static int gs_close_flush_done(struct gs_port *p) { int cond; - /* return true on disconnect or empty buffer */ + /* return true on disconnect or empty buffer or if raced with open() */ spin_lock_irq(&p->port_lock); - cond = (p->port_usb == NULL) || !kfifo_len(&p->port_write_buf); + cond = p->port_usb == NULL || !kfifo_len(&p->port_write_buf) || + p->port.count > 1; spin_unlock_irq(&p->port_lock); return cond; @@ -704,6 +667,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) spin_lock_irq(&port->port_lock); if (port->port.count != 1) { +raced_with_open: if (port->port.count == 0) WARN_ON(1); else @@ -713,12 +677,6 @@ static void gs_close(struct tty_struct *tty, struct file *file) pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file); - /* mark port as closing but in use; we can drop port lock - * and sleep if necessary - */ - port->openclose = true; - port->port.count = 0; - gser = port->port_usb; if (gser && gser->disconnect) gser->disconnect(gser); @@ -729,9 +687,13 @@ static void gs_close(struct tty_struct *tty, struct file *file) if (kfifo_len(&port->port_write_buf) > 0 && gser) { spin_unlock_irq(&port->port_lock); wait_event_interruptible_timeout(port->drain_wait, - gs_writes_finished(port), + gs_close_flush_done(port), GS_CLOSE_TIMEOUT * HZ); spin_lock_irq(&port->port_lock); + + if (port->port.count != 1) + goto raced_with_open; + gser = port->port_usb; } @@ -744,10 +706,9 @@ static void gs_close(struct tty_struct *tty, struct file *file) else kfifo_reset(&port->port_write_buf); + port->port.count = 0; port->port.tty = NULL; - port->openclose = false; - pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", port->port_num, tty, file); @@ -1207,8 +1168,9 @@ static int gs_closed(struct gs_port *port) int cond; spin_lock_irq(&port->port_lock); - cond = (port->port.count == 0) && !port->openclose; + cond = port->port.count == 0; spin_unlock_irq(&port->port_lock); + return cond; } @@ -1413,7 +1375,7 @@ void gserial_disconnect(struct gserial *gser) port->port_usb = NULL; gser->ioport = NULL; - if (port->port.count > 0 || port->openclose) { + if (port->port.count > 0) { wake_up_interruptible(&port->drain_wait); if (port->port.tty) tty_hangup(port->port.tty); @@ -1426,7 +1388,7 @@ void gserial_disconnect(struct gserial *gser) /* finally, free any unused/unusable I/O buffers */ spin_lock_irqsave(&port->port_lock, flags); - if (port->port.count == 0 && !port->openclose) + if (port->port.count == 0) kfifo_free(&port->port_write_buf); gs_free_requests(gser->out, &port->read_pool, NULL); gs_free_requests(gser->out, &port->read_queue, NULL); -- cgit From b45ca31a6a4c9b342d7d2ced493aae9cd31d6308 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 16:45:58 +0800 Subject: usb: phy: keystone: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-keystone.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index 19871266312d..110e6e9ad621 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -66,15 +66,13 @@ static int keystone_usbphy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct keystone_usbphy *k_phy; - struct resource *res; int ret; k_phy = devm_kzalloc(dev, sizeof(*k_phy), GFP_KERNEL); if (!k_phy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - k_phy->phy_ctrl = devm_ioremap_resource(dev, res); + k_phy->phy_ctrl = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(k_phy->phy_ctrl)) return PTR_ERR(k_phy->phy_ctrl); -- cgit From 53490989ff91f5826bbb12190dffbb1a32aa1ea0 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 16:48:27 +0800 Subject: usb: phy: mxs: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 70b8c8248caf..67b39dc62b37 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -710,7 +710,6 @@ static enum usb_charger_type mxs_phy_charger_detect(struct usb_phy *phy) static int mxs_phy_probe(struct platform_device *pdev) { - struct resource *res; void __iomem *base; struct clk *clk; struct mxs_phy *mxs_phy; @@ -723,8 +722,7 @@ static int mxs_phy_probe(struct platform_device *pdev) if (!of_id) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); -- cgit From 836283dd7bffdde80734d7a2f45dd35c40d0065f Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 16:50:45 +0800 Subject: usb: renesas_usbhs: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 4c3de777ef6c..18d5d1a0593e 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -590,7 +590,7 @@ static int usbhs_probe(struct platform_device *pdev) { const struct renesas_usbhs_platform_info *info; struct usbhs_priv *priv; - struct resource *res, *irq_res; + struct resource *irq_res; struct device *dev = &pdev->dev; int ret, gpio; u32 tmp; @@ -619,8 +619,7 @@ static int usbhs_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); -- cgit From 2e78dd5147061709385b01579c0a6a1bfb9791d6 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:02:39 +0800 Subject: usb: gadget: at91_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/at91_udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 194ffb1ed462..1b2b548c59a0 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1808,7 +1808,6 @@ static int at91udc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct at91_udc *udc; int retval; - struct resource *res; struct at91_ep *ep; int i; @@ -1839,8 +1838,7 @@ static int at91udc_probe(struct platform_device *pdev) ep->is_pingpong = 1; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->udp_baseaddr = devm_ioremap_resource(dev, res); + udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->udp_baseaddr)) return PTR_ERR(udc->udp_baseaddr); -- cgit From 4496bf250539a1dbe8b2c3662cc77cf74bc8cb5a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:32:27 +0800 Subject: usb: gadget: bcm63xx_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bcm63xx_udc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index 97b16463f3ef..7fcf4a8eaeb6 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2282,7 +2282,6 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct bcm63xx_usbd_platform_data *pd = dev_get_platdata(dev); struct bcm63xx_udc *udc; - struct resource *res; int rc = -ENOMEM, i, irq; udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL); @@ -2298,13 +2297,11 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->usbd_regs = devm_ioremap_resource(dev, res); + udc->usbd_regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->usbd_regs)) return PTR_ERR(udc->usbd_regs); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - udc->iudma_regs = devm_ioremap_resource(dev, res); + udc->iudma_regs = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(udc->iudma_regs)) return PTR_ERR(udc->iudma_regs); -- cgit From 586a5fd60c3ca6a6d0056966651a4e063f4d556e Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:33:35 +0800 Subject: usb: bdc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index cc4a16e253ac..02a3a774670b 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -480,7 +480,6 @@ static void bdc_phy_exit(struct bdc *bdc) static int bdc_probe(struct platform_device *pdev) { struct bdc *bdc; - struct resource *res; int ret = -ENOMEM; int irq; u32 temp; @@ -508,8 +507,7 @@ static int bdc_probe(struct platform_device *pdev) bdc->clk = clk; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - bdc->regs = devm_ioremap_resource(dev, res); + bdc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(bdc->regs)) { dev_err(dev, "ioremap error\n"); return -ENOMEM; -- cgit From 60b6465be786441a24d824d9ab4cad63fcf8a37e Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:40:33 +0800 Subject: usb: gadget: gr_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/gr_udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 7a0e9a58c2d8..c63383221b5a 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2118,7 +2118,6 @@ static int gr_request_irq(struct gr_udc *dev, int irq) static int gr_probe(struct platform_device *pdev) { struct gr_udc *dev; - struct resource *res; struct gr_regs __iomem *regs; int retval; u32 status; @@ -2128,8 +2127,7 @@ static int gr_probe(struct platform_device *pdev) return -ENOMEM; dev->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(dev->dev, res); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); -- cgit From 623128070e78f616ed57f55702a9cfc726b88925 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:42:22 +0800 Subject: usb: gadget: pxa25x_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pxa25x_udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index d4be53559f2e..cfafdd92c2a8 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -2321,7 +2321,6 @@ static int pxa25x_udc_probe(struct platform_device *pdev) struct pxa25x_udc *dev = &memory; int retval, irq; u32 chiprev; - struct resource *res; pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); @@ -2367,8 +2366,7 @@ static int pxa25x_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->regs = devm_ioremap_resource(&pdev->dev, res); + dev->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dev->regs)) return PTR_ERR(dev->regs); -- cgit From e7705d269b28f718ff57a730ec5bc505fe8ee6f0 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:45:57 +0800 Subject: usb: gadget: pxa27x_udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pxa27x_udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 014233252299..5f107f277f30 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -2356,7 +2356,6 @@ MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids); */ static int pxa_udc_probe(struct platform_device *pdev) { - struct resource *regs; struct pxa_udc *udc = &memory; int retval = 0, gpio; struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev); @@ -2378,8 +2377,7 @@ static int pxa_udc_probe(struct platform_device *pdev) udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->regs = devm_ioremap_resource(&pdev->dev, regs); + udc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->regs)) return PTR_ERR(udc->regs); udc->irq = platform_get_irq(pdev, 0); -- cgit From 195ad9c36d82527aed95ffaba35b8ce95ee5ff61 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:47:38 +0800 Subject: usb: gadget: r8a66597-udc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/r8a66597-udc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 11e25a3f4f1f..582a16165ea9 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1838,7 +1838,7 @@ static int r8a66597_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; char clk_name[8]; - struct resource *res, *ires; + struct resource *ires; int irq; void __iomem *reg = NULL; struct r8a66597 *r8a66597 = NULL; @@ -1846,8 +1846,7 @@ static int r8a66597_probe(struct platform_device *pdev) int i; unsigned long irq_trigger; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg = devm_ioremap_resource(&pdev->dev, res); + reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(reg)) return PTR_ERR(reg); -- cgit From e17d87668ecddfd6f7ebbc784b4008e74d4e017c Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:48:36 +0800 Subject: usb: gadget: renesas_usb3: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/renesas_usb3.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index e098f16c01cb..922a19be1135 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2732,7 +2732,6 @@ static struct usb_role_switch_desc renesas_usb3_role_switch_desc = { static int renesas_usb3_probe(struct platform_device *pdev) { struct renesas_usb3 *usb3; - struct resource *res; int irq, ret; const struct renesas_usb3_priv *priv; const struct soc_device_attribute *attr; @@ -2751,8 +2750,7 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (!usb3) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - usb3->reg = devm_ioremap_resource(&pdev->dev, res); + usb3->reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(usb3->reg)) return PTR_ERR(usb3->reg); -- cgit From 6a3893bfbe0266c5703ed8d1e0487ac841c50c18 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 4 Sep 2019 17:50:22 +0800 Subject: usb: gadget: s3c-hsudc: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/s3c-hsudc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c index 858993c73442..21252fbc0319 100644 --- a/drivers/usb/gadget/udc/s3c-hsudc.c +++ b/drivers/usb/gadget/udc/s3c-hsudc.c @@ -1263,7 +1263,6 @@ static const struct usb_gadget_ops s3c_hsudc_gadget_ops = { static int s3c_hsudc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct s3c_hsudc *hsudc; struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev); int ret, i; @@ -1290,9 +1289,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev) goto err_supplies; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - hsudc->regs = devm_ioremap_resource(&pdev->dev, res); + hsudc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hsudc->regs)) { ret = PTR_ERR(hsudc->regs); goto err_res; -- cgit From e961c47e66f1f03ebfacde4360f240757bdea7ac Mon Sep 17 00:00:00 2001 From: Veeraiyan Chidambaram Date: Thu, 5 Sep 2019 11:17:54 +0200 Subject: usb: gadget: udc: renesas_usb3: add suspend event support In R-Car Gen3 USB 3.0 Function, if host is detached an interrupt will be generated and Suspended state bit is set in interrupt status register. Interrupt handler will call driver->suspend(composite_suspend) if suspended state bit is set. composite_suspend will call ffs_func_suspend which will post FUNCTIONFS_SUSPEND and will be consumed by user space application via /dev/ep0. To be able to detect the host detach, USB_INT_1_B2_SPND to cover the Suspended bit of the B2_SPND_OUT[9] from the USB Status Register (USB_STA) register and perform appropriate action in the usb3_irq_epc_int_1 function. Without this commit, disconnection of the phone from R-Car H3 ES2.0 Salvator-X CN11 port is not recognized and reverse role switch does not happen. If phone is connected again it does not enumerate. With this commit, disconnection will be recognized and reverse role switch will happen by a user space application. If phone is connected again it will enumerate properly and will become visible in the output of 'lsusb'. Reviewed-by: Yoshihiro Shimoda Tested-by: Yoshihiro Shimoda Signed-off-by: Veeraiyan Chidambaram Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/renesas_usb3.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 922a19be1135..3ba66a13d789 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -775,6 +775,18 @@ static void usb3_irq_epc_int_1_resume(struct renesas_usb3 *usb3) usb3_transition_to_default_state(usb3, false); } +static void usb3_irq_epc_int_1_suspend(struct renesas_usb3 *usb3) +{ + usb3_disable_irq_1(usb3, USB_INT_1_B2_SPND); + + if (usb3->gadget.speed != USB_SPEED_UNKNOWN && + usb3->gadget.state != USB_STATE_NOTATTACHED) { + if (usb3->driver && usb3->driver->suspend) + usb3->driver->suspend(&usb3->gadget); + usb_gadget_set_state(&usb3->gadget, USB_STATE_SUSPENDED); + } +} + static void usb3_irq_epc_int_1_disable(struct renesas_usb3 *usb3) { usb3_stop_usb3_connection(usb3); @@ -860,6 +872,9 @@ static void usb3_irq_epc_int_1(struct renesas_usb3 *usb3, u32 int_sta_1) if (int_sta_1 & USB_INT_1_B2_RSUM) usb3_irq_epc_int_1_resume(usb3); + if (int_sta_1 & USB_INT_1_B2_SPND) + usb3_irq_epc_int_1_suspend(usb3); + if (int_sta_1 & USB_INT_1_SPEED) usb3_irq_epc_int_1_speed(usb3); -- cgit From 8b20d00f0f089b72f0a1e08d602a29f874bb0a35 Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Mon, 9 Sep 2019 12:54:39 +0200 Subject: usb: renesas_usbhs: enable DVSE interrupt Commit [1] enabled the possibility of checking the DVST (Device State Transition) bit of INTSTS0 (Interrupt Status Register 0) and calling the irq_dev_state() handler if the DVST bit is set. But neither commit [1] nor commit [2] actually enabled the DVSE (Device State Transition Interrupt Enable) bit in the INTENB0 (Interrupt Enable Register 0). As a consequence, irq_dev_state() handler is getting called as a side effect of other (non-DVSE) interrupts being fired, which definitely can't be relied upon, if DVST notifications are of any value. Why this doesn't hurt is because usbhsg_irq_dev_state() currently doesn't do much except of a dev_dbg(). Once more work is added to the handler (e.g. detecting device "Suspended" state and notifying other USB gadget components about it), enabling DVSE becomes a hard requirement. Do it in a standalone commit for better visibility and clear explanation. [1] f1407d5 ("usb: renesas_usbhs: Add Renesas USBHS common code") [2] 2f98382 ("usb: renesas_usbhs: Add Renesas USBHS Gadget") Signed-off-by: Eugeniu Rosca Signed-off-by: Veeraiyan Chidambaram Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/mod.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 10fc65596014..31ee5dc8333a 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -348,10 +348,6 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) * usbhs_interrupt */ - /* - * it don't enable DVSE (intenb0) here - * but "mod->irq_dev_state" will be called. - */ if (info->irq_vbus) intenb0 |= VBSE; @@ -362,6 +358,9 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) if (mod->irq_ctrl_stage) intenb0 |= CTRE; + if (mod->irq_dev_state) + intenb0 |= DVSE; + if (mod->irq_empty && mod->irq_bempsts) { usbhs_write(priv, BEMPENB, mod->irq_bempsts); intenb0 |= BEMPE; -- cgit From d2802865f7e5230778567e339510cae05eec1706 Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Mon, 9 Sep 2019 12:54:40 +0200 Subject: usb: renesas_usbhs: simplify usbhs_status_get_device_state() Similar to usbhs_status_get_ctrl_stage(), *_get_device_state() is not supposed to return any error code since its return value is the DVSQ bitfield of the INTSTS0 register. According to SoC HW manual rev1.00, every single value of DVSQ[2:0] is valid and none is an error: ----8<---- Device State 000: Powered state 001: Default state 010: Address state 011: Configuration state 1xx: Suspended state ----8<---- Hence, simplify the function body. The motivation behind dropping the switch/case construct is being able to implement reading the suspended state. The latter (based on the above DVSQ[2:0] description) doesn't have a unique value, but is rather a list of states (which makes switch/case less suitable for reading/validating it): 100: (Suspended) Powered state 101: (Suspended) Default state 110: (Suspended) Address state 111: (Suspended) Configuration state Signed-off-by: Eugeniu Rosca Signed-off-by: Veeraiyan Chidambaram Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/mod.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 31ee5dc8333a..b98112cefaa4 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -169,17 +169,7 @@ void usbhs_mod_remove(struct usbhs_priv *priv) */ int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state) { - int state = irq_state->intsts0 & DVSQ_MASK; - - switch (state) { - case POWER_STATE: - case DEFAULT_STATE: - case ADDRESS_STATE: - case CONFIGURATION_STATE: - return state; - } - - return -EIO; + return (int)irq_state->intsts0 & DVSQ_MASK; } int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state) -- cgit From f2b06c953911276eaa25259f9b77e717d78392b2 Mon Sep 17 00:00:00 2001 From: Veeraiyan Chidambaram Date: Mon, 9 Sep 2019 12:54:41 +0200 Subject: usb: renesas_usbhs: add suspend event support in gadget mode When R-Car Gen3 USB 2.0 is in Gadget mode, if host is detached an interrupt will be generated and Suspended state bit is set in interrupt status register. Interrupt handler will call driver->suspend(composite_suspend) if suspended state bit is set. composite_suspend will call ffs_func_suspend which will post FUNCTIONFS_SUSPEND and will be consumed by user space application via /dev/ep0. To be able to detect host detach, extend the DVSQ_MASK to cover the Suspended bit of the DVSQ[2:0] bitfield from the Interrupt Status Register 0 (INTSTS0) register and perform appropriate action in the DVST interrupt handler (usbhsg_irq_dev_state). Without this commit, disconnection of the phone from R-Car H3 ES2.0 Salvator-X CN9 port is not recognized and reverse role switch does not not happen. If phone is connected again it does not enumerate. With this commit, disconnection will be recognized and reverse role switch will happen by a user space application. If phone is connected again it will enumerate properly and will become visible in the output of 'lsusb'. Signed-off-by: Veeraiyan Chidambaram Signed-off-by: Eugeniu Rosca Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.h | 3 ++- drivers/usb/renesas_usbhs/mod_gadget.c | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 0824099b905e..ef1735d014da 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -161,11 +161,12 @@ struct usbhs_priv; #define VBSTS (1 << 7) /* VBUS_0 and VBUSIN_0 Input Status */ #define VALID (1 << 3) /* USB Request Receive */ -#define DVSQ_MASK (0x3 << 4) /* Device State */ +#define DVSQ_MASK (0x7 << 4) /* Device State */ #define POWER_STATE (0 << 4) #define DEFAULT_STATE (1 << 4) #define ADDRESS_STATE (2 << 4) #define CONFIGURATION_STATE (3 << 4) +#define SUSPENDED_STATE (4 << 4) #define CTSQ_MASK (0x7) /* Control Transfer Stage */ #define IDLE_SETUP_STAGE 0 /* Idle stage or setup stage */ diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index e5ef56991dba..b47e70ffeda6 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -457,12 +457,18 @@ static int usbhsg_irq_dev_state(struct usbhs_priv *priv, { struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); struct device *dev = usbhsg_gpriv_to_dev(gpriv); + int state = usbhs_status_get_device_state(irq_state); gpriv->gadget.speed = usbhs_bus_get_speed(priv); - dev_dbg(dev, "state = %x : speed : %d\n", - usbhs_status_get_device_state(irq_state), - gpriv->gadget.speed); + dev_dbg(dev, "state = %x : speed : %d\n", state, gpriv->gadget.speed); + + if (gpriv->gadget.speed != USB_SPEED_UNKNOWN && + (state & SUSPENDED_STATE)) { + if (gpriv->driver && gpriv->driver->suspend) + gpriv->driver->suspend(&gpriv->gadget); + usb_gadget_set_state(&gpriv->gadget, USB_STATE_SUSPENDED); + } return 0; } -- cgit From 1bff4a4e66b91ef55ef0265944d01d13be0eae43 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Mon, 21 Oct 2019 16:20:58 +0200 Subject: USB: dummy-hcd: increase max number of devices to 32 When fuzzing the USB subsystem with syzkaller, we currently use 8 testing processes within one VM. To isolate testing processes from one another it is desirable to assign a dedicated USB bus to each of those, which means we need at least 8 Dummy UDC/HCD devices. This patch increases the maximum number of Dummy UDC/HCD devices to 32 (more than 8 in case we need more of them in the future). Signed-off-by: Andrey Konovalov Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/dummy_hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 3d499d93c083..a8f1e5707c14 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2725,7 +2725,7 @@ static struct platform_driver dummy_hcd_driver = { }; /*-------------------------------------------------------------------------*/ -#define MAX_NUM_UDC 2 +#define MAX_NUM_UDC 32 static struct platform_device *the_udc_pdev[MAX_NUM_UDC]; static struct platform_device *the_hcd_pdev[MAX_NUM_UDC]; -- cgit From 15ac1d99b84578e6fac804bffcb5bec8d0424d77 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Mon, 21 Oct 2019 16:20:59 +0200 Subject: USB: dummy-hcd: use usb_urb_dir_in instead of usb_pipein Commit fea3409112a9 ("USB: add direction bit to urb->transfer_flags") has added a usb_urb_dir_in() helper function that can be used to determine the direction of the URB. With that patch USB_DIR_IN control requests with wLength == 0 are considered out requests by real USB HCDs. This patch changes dummy-hcd to use the usb_urb_dir_in() helper to match that behavior. Signed-off-by: Andrey Konovalov Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/dummy_hcd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index a8f1e5707c14..4c9d1e49d5ed 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1321,7 +1321,7 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, u32 this_sg; bool next_sg; - to_host = usb_pipein(urb->pipe); + to_host = usb_urb_dir_in(urb); rbuf = req->req.buf + req->req.actual; if (!urb->num_sgs) { @@ -1409,7 +1409,7 @@ top: /* FIXME update emulated data toggle too */ - to_host = usb_pipein(urb->pipe); + to_host = usb_urb_dir_in(urb); if (unlikely(len == 0)) is_short = 1; else { @@ -1830,7 +1830,7 @@ restart: /* find the gadget's ep for this request (if configured) */ address = usb_pipeendpoint (urb->pipe); - if (usb_pipein(urb->pipe)) + if (usb_urb_dir_in(urb)) address |= USB_DIR_IN; ep = find_endpoint(dum, address); if (!ep) { @@ -2385,7 +2385,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) s = "?"; break; } s; }), - ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "", + ep, ep ? (usb_urb_dir_in(urb) ? "in" : "out") : "", ({ char *s; \ switch (usb_pipetype(urb->pipe)) { \ case PIPE_CONTROL: \ -- cgit From eb23c8b4fd9805ceba7725e7069d20d60e20e377 Mon Sep 17 00:00:00 2001 From: Nikhil Badola Date: Mon, 21 Oct 2019 18:21:52 +0800 Subject: usb: fsl: Remove unused variable Remove unused variable td_complete Signed-off-by: Nikhil Badola Reviewed-by: Ran Wang Reviewed-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/fsl_udc_core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 20141c3096f6..1e78ef39ba01 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1595,14 +1595,13 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, struct fsl_req *curr_req) { struct ep_td_struct *curr_td; - int td_complete, actual, remaining_length, j, tmp; + int actual, remaining_length, j, tmp; int status = 0; int errors = 0; struct ep_queue_head *curr_qh = &udc->ep_qh[pipe]; int direction = pipe % 2; curr_td = curr_req->head; - td_complete = 0; actual = curr_req->req.length; for (j = 0; j < curr_req->dtd_count; j++) { @@ -1647,11 +1646,9 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, status = -EPROTO; break; } else { - td_complete++; break; } } else { - td_complete++; VDBG("dTD transmitted successful"); } -- cgit From 75eaa498c99eebf9f9237656f69469e50197cc0b Mon Sep 17 00:00:00 2001 From: Nikhil Badola Date: Mon, 21 Oct 2019 18:21:53 +0800 Subject: usb: gadget: Correct NULL pointer checking in fsl gadget Correct NULL pointer checking for endpoint descriptor before it gets dereferenced Signed-off-by: Nikhil Badola Signed-off-by: Ran Wang Reviewed-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/fsl_udc_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 1e78ef39ba01..334b160333a1 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1052,10 +1052,11 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep) u32 bitmask; struct ep_queue_head *qh; - ep = container_of(_ep, struct fsl_ep, ep); - if (!_ep || (!ep->ep.desc && ep_index(ep) != 0)) + if (!_ep || _ep->desc || !(_ep->desc->bEndpointAddress&0xF)) return -ENODEV; + ep = container_of(_ep, struct fsl_ep, ep); + udc = (struct fsl_udc *)ep->udc; if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) -- cgit From 0b8b1a1fede01eb727646e70a1b4872cdbd327aa Mon Sep 17 00:00:00 2001 From: Jayshri Pawar Date: Mon, 21 Oct 2019 07:35:00 +0100 Subject: usb: gadget: f_tcm: Provide support to get alternate setting in tcm function Providing tcm_get_alt in tcm function to support Bulk only protocol and USB Attached SCSI protocol Signed-off-by: Jayshri Pawar Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_tcm.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 7f01f78b1d23..36504931b2d1 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -846,7 +846,7 @@ static void uasp_set_alt(struct f_uas *fu) fu->flags = USBG_IS_UAS; - if (gadget->speed == USB_SPEED_SUPER) + if (gadget->speed >= USB_SPEED_SUPER) fu->flags |= USBG_USE_STREAMS; config_ep_by_speed(gadget, f, fu->ep_in); @@ -2093,6 +2093,16 @@ static void tcm_delayed_set_alt(struct work_struct *wq) usb_composite_setup_continue(fu->function.config->cdev); } +static int tcm_get_alt(struct usb_function *f, unsigned intf) +{ + if (intf == bot_intf_desc.bInterfaceNumber) + return USB_G_ALT_INT_BBB; + if (intf == uasp_intf_desc.bInterfaceNumber) + return USB_G_ALT_INT_UAS; + + return -EOPNOTSUPP; +} + static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_uas *fu = to_f_uas(f); @@ -2300,6 +2310,7 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi) fu->function.bind = tcm_bind; fu->function.unbind = tcm_unbind; fu->function.set_alt = tcm_set_alt; + fu->function.get_alt = tcm_get_alt; fu->function.setup = tcm_setup; fu->function.disable = tcm_disable; fu->function.free_func = tcm_free; -- cgit From 3654aaa922f30dd380569d60996c92f32c2d2347 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 14 Oct 2019 14:39:51 +0800 Subject: usb: gadget: configfs: fix concurrent issue between composite APIs We meet several NULL pointer issues if configfs_composite_unbind and composite_setup (or composite_disconnect) are running together. These issues occur when do the function switch stress test, the configfs_compsoite_unbind is called from user mode by echo "" to /sys/../UDC entry, and meanwhile, the setup interrupt or disconnect interrupt occurs by hardware. The composite_setup will get the cdev from get_gadget_data, but configfs_composite_unbind will set gadget data as NULL, so the NULL pointer issue occurs. This concurrent is hard to reproduce by native kernel, but can be reproduced by android kernel. In this commit, we introduce one spinlock belongs to structure gadget_info since we can't use the same spinlock in usb_composite_dev due to exclusive running together between composite_setup and configfs_composite_unbind. And one bit flag 'unbind' to indicate the code is at unbind routine, this bit is needed due to we release the lock at during configfs_composite_unbind sometimes, and composite_setup may be run at that time. Several oops: oops 1: android_work: sent uevent USB_STATE=CONNECTED configfs-gadget gadget: super-speed config #1: b android_work: sent uevent USB_STATE=CONFIGURED init: Received control message 'start' for 'adbd' from pid: 3515 (system_server) Unable to handle kernel NULL pointer dereference at virtual address 0000002a init: Received control message 'stop' for 'adbd' from pid: 3375 (/vendor/bin/hw/android.hardware.usb@1.1-servic) Mem abort info: Exception class = DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 Data abort info: ISV = 0, ISS = 0x00000004 CM = 0, WnR = 0 user pgtable: 4k pages, 48-bit VAs, pgd = ffff8008f1b7f000 [000000000000002a] *pgd=0000000000000000 Internal error: Oops: 96000004 [#1] PREEMPT SMP Modules linked in: CPU: 4 PID: 2457 Comm: irq/125-5b11000 Not tainted 4.14.98-07846-g0b40a9b-dirty #16 Hardware name: Freescale i.MX8QM MEK (DT) task: ffff8008f2a98000 task.stack: ffff00000b7b8000 PC is at composite_setup+0x44/0x1508 LR is at android_setup+0xb8/0x13c pc : [] lr : [] pstate: 800001c5 sp : ffff00000b7bbb80 x29: ffff00000b7bbb80 x28: ffff8008f2a3c010 x27: 0000000000000001 x26: 0000000000000000 [1232/1897] audit: audit_lost=25791 audit_rate_limit=5 audit_backlog_limit=64 x25: 00000000ffffffa1 x24: ffff8008f2a3c010 audit: rate limit exceeded x23: 0000000000000409 x22: ffff000009c8e000 x21: ffff8008f7a8b428 x20: ffff00000afae000 x19: ffff0000089ff000 x18: 0000000000000000 x17: 0000000000000000 x16: ffff0000082b7c9c x15: 0000000000000000 x14: f1866f5b952aca46 x13: e35502e30d44349c x12: 0000000000000008 x11: 0000000000000008 x10: 0000000000000a30 x9 : ffff00000b7bbd00 x8 : ffff8008f2a98a90 x7 : ffff8008f27a9c90 x6 : 0000000000000001 x5 : 0000000000000000 x4 : 0000000000000001 x3 : 0000000000000000 x2 : 0000000000000006 x1 : ffff0000089ff8d0 x0 : 732a010310b9ed00 X7: 0xffff8008f27a9c10: 9c10 00000002 00000000 00000001 00000000 13110000 ffff0000 00000002 00208040 9c30 00000000 00000000 00000000 00000000 00000000 00000005 00000029 00000000 9c50 00051778 00000001 f27a8e00 ffff8008 00000005 00000000 00000078 00000078 9c70 00000078 00000000 09031d48 ffff0000 00100000 00000000 00400000 00000000 9c90 00000001 00000000 00000000 00000000 00000000 00000000 ffefb1a0 ffff8008 9cb0 f27a9ca8 ffff8008 00000000 00000000 b9d88037 00000173 1618a3eb 00000001 9cd0 870a792a 0000002e 16188fe6 00000001 0000242b 00000000 00000000 00000000 using random self ethernet address 9cf0 019a4646 00000000 000547f3 00000000 ecfd6c33 00000002 00000000 using random host ethernet address 00000000 X8: 0xffff8008f2a98a10: 8a10 00000000 00000000 f7788d00 ffff8008 00000001 00000000 00000000 00000000 8a30 eb218000 ffff8008 f2a98000 ffff8008 f2a98000 ffff8008 09885000 ffff0000 8a50 f34df480 ffff8008 00000000 00000000 f2a98648 ffff8008 09c8e000 ffff0000 8a70 fff2c800 ffff8008 09031d48 ffff0000 0b7bbd00 ffff0000 0b7bbd00 ffff0000 8a90 080861bc ffff0000 00000000 00000000 00000000 00000000 00000000 00000000 8ab0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 8ad0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 8af0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 X21: 0xffff8008f7a8b3a8: b3a8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 b3c8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 b3e8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 b408 00000000 00000000 00000000 00000000 00000000 00000000 00000001 00000000 b428 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 b448 0053004d 00540046 00300031 00010030 eb07b520 ffff8008 20011201 00000003 b468 e418d109 0104404e 00010302 00000000 eb07b558 ffff8008 eb07b558 ffff8008 b488 f7a8b488 ffff8008 f7a8b488 ffff8008 f7a8b300 ffff8008 00000000 00000000 X24: 0xffff8008f2a3bf90: bf90 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 bfb0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 bfd0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 bff0 00000000 00000000 00000000 00000000 f76c8010 ffff8008 f76c8010 ffff8008 c010 00000000 00000000 f2a3c018 ffff8008 f2a3c018 ffff8008 08a067dc ffff0000 c030 f2a5a000 ffff8008 091c3650 ffff0000 f716fd18 ffff8008 f716fe30 ffff8008 c050 f2ce4a30 ffff8008 00000000 00000005 00000000 00000000 095d1568 ffff0000 c070 f76c8010 ffff8008 f2ce4b00 ffff8008 095cac68 ffff0000 f2a5a028 ffff8008 X28: 0xffff8008f2a3bf90: bf90 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 bfb0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 bfd0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 bff0 00000000 00000000 00000000 00000000 f76c8010 ffff8008 f76c8010 ffff8008 c010 00000000 00000000 f2a3c018 ffff8008 f2a3c018 ffff8008 08a067dc ffff0000 c030 f2a5a000 ffff8008 091c3650 ffff0000 f716fd18 ffff8008 f716fe30 ffff8008 c050 f2ce4a30 ffff8008 00000000 00000005 00000000 00000000 095d1568 ffff0000 c070 f76c8010 ffff8008 f2ce4b00 ffff8008 095cac68 ffff0000 f2a5a028 ffff8008 Process irq/125-5b11000 (pid: 2457, stack limit = 0xffff00000b7b8000) Call trace: Exception stack(0xffff00000b7bba40 to 0xffff00000b7bbb80) ba40: 732a010310b9ed00 ffff0000089ff8d0 0000000000000006 0000000000000000 ba60: 0000000000000001 0000000000000000 0000000000000001 ffff8008f27a9c90 ba80: ffff8008f2a98a90 ffff00000b7bbd00 0000000000000a30 0000000000000008 baa0: 0000000000000008 e35502e30d44349c f1866f5b952aca46 0000000000000000 bac0: ffff0000082b7c9c 0000000000000000 0000000000000000 ffff0000089ff000 bae0: ffff00000afae000 ffff8008f7a8b428 ffff000009c8e000 0000000000000409 bb00: ffff8008f2a3c010 00000000ffffffa1 0000000000000000 0000000000000001 bb20: ffff8008f2a3c010 ffff00000b7bbb80 ffff000008a032fc ffff00000b7bbb80 bb40: ffff0000089ffb3c 00000000800001c5 ffff00000b7bbb80 732a010310b9ed00 bb60: ffffffffffffffff ffff0000080f777c ffff00000b7bbb80 ffff0000089ffb3c [] composite_setup+0x44/0x1508 [] android_setup+0xb8/0x13c [] cdns3_ep0_delegate_req+0x44/0x70 [] cdns3_check_ep0_interrupt_proceed+0x33c/0x654 [] cdns3_device_thread_irq_handler+0x4b0/0x4bc [] cdns3_thread_irq+0x48/0x68 [] irq_thread_fn+0x28/0x88 [] irq_thread+0x13c/0x228 [] kthread+0x104/0x130 [] ret_from_fork+0x10/0x18 oops2: composite_disconnect: Calling disconnect on a Gadget that is not connected android_work: did not send uevent (0 0 (null)) init: Received control message 'stop' for 'adbd' from pid: 3359 (/vendor/bin/hw/android.hardware.usb@1.1-service.imx) init: Sending signal 9 to service 'adbd' (pid 22343) process group... ------------[ cut here ]------------ audit: audit_lost=180038 audit_rate_limit=5 audit_backlog_limit=64 audit: rate limit exceeded WARNING: CPU: 0 PID: 3468 at kernel_imx/drivers/usb/gadget/composite.c:2009 composite_disconnect+0x80/0x88 Modules linked in: CPU: 0 PID: 3468 Comm: HWC-UEvent-Thre Not tainted 4.14.98-07846-g0b40a9b-dirty #16 Hardware name: Freescale i.MX8QM MEK (DT) task: ffff8008f2349c00 task.stack: ffff00000b0a8000 PC is at composite_disconnect+0x80/0x88 LR is at composite_disconnect+0x80/0x88 pc : [] lr : [] pstate: 600001c5 sp : ffff000008003dd0 x29: ffff000008003dd0 x28: ffff8008f2349c00 x27: ffff000009885018 x26: ffff000008004000 Timeout for IPC response! x25: ffff000009885018 x24: ffff000009c8e280 x23: ffff8008f2d98010 x22: 00000000000001c0 x21: ffff8008f2d98394 x20: ffff8008f2d98010 x19: 0000000000000000 x18: 0000e3956f4f075a fxos8700 4-001e: i2c block read acc failed x17: 0000e395735727e8 x16: ffff00000829f4d4 x15: ffffffffffffffff x14: 7463656e6e6f6320 x13: 746f6e2009090920 x12: 7369207461687420 x11: 7465676461472061 x10: 206e6f207463656e x9 : 6e6f637369642067 x8 : ffff000009c8e280 x7 : ffff0000086ca6cc x6 : ffff000009f15e78 x5 : 0000000000000000 x4 : 0000000000000000 x3 : ffffffffffffffff x2 : c3f28b86000c3900 x1 : c3f28b86000c3900 x0 : 000000000000004e X20: 0xffff8008f2d97f90: 7f90 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 7fb0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 libprocessgroup: Failed to kill process cgroup uid 0 pid 22343 in 215ms, 1 processes remain 7fd0 Timeout for IPC response! 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 using random self ethernet address 7ff0 00000000 00000000 00000000 00000000 f76c8010 ffff8008 f76c8010 ffff8008 8010 00000100 00000000 f2d98018 ffff8008 f2d98018 ffff8008 08a067dc using random host ethernet address ffff0000 8030 f206d800 ffff8008 091c3650 ffff0000 f7957b18 ffff8008 f7957730 ffff8008 8050 f716a630 ffff8008 00000000 00000005 00000000 00000000 095d1568 ffff0000 8070 f76c8010 ffff8008 f716a800 ffff8008 095cac68 ffff0000 f206d828 ffff8008 X21: 0xffff8008f2d98314: 8314 ffff8008 00000000 00000000 00000000 00000000 00000000 00000000 00000000 8334 00000000 00000000 00000000 00000000 00000000 08a04cf4 ffff0000 00000000 8354 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 8374 00000000 00000000 00000000 00001001 00000000 00000000 00000000 00000000 8394 e4bbe4bb 0f230000 ffff0000 0afae000 ffff0000 ae001000 00000000 f206d400 Timeout for IPC response! 83b4 ffff8008 00000000 00000000 f7957b18 ffff8008 f7957718 ffff8008 f7957018 83d4 ffff8008 f7957118 ffff8008 f7957618 ffff8008 f7957818 ffff8008 f7957918 83f4 ffff8008 f7957d18 ffff8008 00000000 00000000 00000000 00000000 00000000 X23: 0xffff8008f2d97f90: 7f90 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 7fb0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 7fd0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 7ff0 00000000 00000000 00000000 00000000 f76c8010 ffff8008 f76c8010 ffff8008 8010 00000100 00000000 f2d98018 ffff8008 f2d98018 ffff8008 08a067dc ffff0000 8030 f206d800 ffff8008 091c3650 ffff0000 f7957b18 ffff8008 f7957730 ffff8008 8050 f716a630 ffff8008 00000000 00000005 00000000 00000000 095d1568 ffff0000 8070 f76c8010 ffff8008 f716a800 ffff8008 095cac68 ffff0000 f206d828 ffff8008 X28: 0xffff8008f2349b80: 9b80 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9ba0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9bc0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9be0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9c00 00000022 00000000 ffffffff ffffffff 00010001 00000000 00000000 00000000 9c20 0b0a8000 ffff0000 00000002 00404040 00000000 00000000 00000000 00000000 9c40 00000001 00000000 00000001 00000000 001ebd44 00000001 f390b800 ffff8008 9c60 00000000 00000001 00000070 00000070 00000070 00000000 09031d48 ffff0000 Call trace: Exception stack(0xffff000008003c90 to 0xffff000008003dd0) 3c80: 000000000000004e c3f28b86000c3900 3ca0: c3f28b86000c3900 ffffffffffffffff 0000000000000000 0000000000000000 3cc0: ffff000009f15e78 ffff0000086ca6cc ffff000009c8e280 6e6f637369642067 3ce0: 206e6f207463656e 7465676461472061 7369207461687420 746f6e2009090920 3d00: 7463656e6e6f6320 ffffffffffffffff ffff00000829f4d4 0000e395735727e8 3d20: 0000e3956f4f075a 0000000000000000 ffff8008f2d98010 ffff8008f2d98394 3d40: 00000000000001c0 ffff8008f2d98010 ffff000009c8e280 ffff000009885018 3d60: ffff000008004000 ffff000009885018 ffff8008f2349c00 ffff000008003dd0 3d80: ffff0000089ff9b0 ffff000008003dd0 ffff0000089ff9b0 00000000600001c5 3da0: ffff8008f33f2cd8 0000000000000000 0000ffffffffffff 0000000000000000 init: Received control message 'start' for 'adbd' from pid: 3359 (/vendor/bin/hw/android.hardware.usb@1.1-service.imx) 3dc0: ffff000008003dd0 ffff0000089ff9b0 [] composite_disconnect+0x80/0x88 [] android_disconnect+0x3c/0x68 [] cdns3_device_irq_handler+0xfc/0x2c8 [] cdns3_irq+0x44/0x94 [] __handle_irq_event_percpu+0x60/0x24c [] handle_irq_event+0x58/0xc0 [] handle_fasteoi_irq+0x98/0x180 [] generic_handle_irq+0x24/0x38 [] __handle_domain_irq+0x60/0xac [] gic_handle_irq+0xd4/0x17c Cc: Andrzej Pietrasiewicz Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/configfs.c | 110 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 025129942894..33852c2b29d1 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -61,6 +61,8 @@ struct gadget_info { bool use_os_desc; char b_vendor_code; char qw_sign[OS_STRING_QW_SIGN_LEN]; + spinlock_t spinlock; + bool unbind; }; static inline struct gadget_info *to_gadget_info(struct config_item *item) @@ -1244,6 +1246,7 @@ static int configfs_composite_bind(struct usb_gadget *gadget, int ret; /* the gi->lock is hold by the caller */ + gi->unbind = 0; cdev->gadget = gadget; set_gadget_data(gadget, cdev); ret = composite_dev_prepare(composite, cdev); @@ -1376,31 +1379,128 @@ static void configfs_composite_unbind(struct usb_gadget *gadget) { struct usb_composite_dev *cdev; struct gadget_info *gi; + unsigned long flags; /* the gi->lock is hold by the caller */ cdev = get_gadget_data(gadget); gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + gi->unbind = 1; + spin_unlock_irqrestore(&gi->spinlock, flags); kfree(otg_desc[0]); otg_desc[0] = NULL; purge_configs_funcs(gi); composite_dev_cleanup(cdev); usb_ep_autoconfig_reset(cdev->gadget); + spin_lock_irqsave(&gi->spinlock, flags); cdev->gadget = NULL; set_gadget_data(gadget, NULL); + spin_unlock_irqrestore(&gi->spinlock, flags); +} + +static int configfs_composite_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + unsigned long flags; + int ret; + + cdev = get_gadget_data(gadget); + if (!cdev) + return 0; + + gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + cdev = get_gadget_data(gadget); + if (!cdev || gi->unbind) { + spin_unlock_irqrestore(&gi->spinlock, flags); + return 0; + } + + ret = composite_setup(gadget, ctrl); + spin_unlock_irqrestore(&gi->spinlock, flags); + return ret; +} + +static void configfs_composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + unsigned long flags; + + cdev = get_gadget_data(gadget); + if (!cdev) + return; + + gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + cdev = get_gadget_data(gadget); + if (!cdev || gi->unbind) { + spin_unlock_irqrestore(&gi->spinlock, flags); + return; + } + + composite_disconnect(gadget); + spin_unlock_irqrestore(&gi->spinlock, flags); +} + +static void configfs_composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + unsigned long flags; + + cdev = get_gadget_data(gadget); + if (!cdev) + return; + + gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + cdev = get_gadget_data(gadget); + if (!cdev || gi->unbind) { + spin_unlock_irqrestore(&gi->spinlock, flags); + return; + } + + composite_suspend(gadget); + spin_unlock_irqrestore(&gi->spinlock, flags); +} + +static void configfs_composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + unsigned long flags; + + cdev = get_gadget_data(gadget); + if (!cdev) + return; + + gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + cdev = get_gadget_data(gadget); + if (!cdev || gi->unbind) { + spin_unlock_irqrestore(&gi->spinlock, flags); + return; + } + + composite_resume(gadget); + spin_unlock_irqrestore(&gi->spinlock, flags); } static const struct usb_gadget_driver configfs_driver_template = { .bind = configfs_composite_bind, .unbind = configfs_composite_unbind, - .setup = composite_setup, - .reset = composite_disconnect, - .disconnect = composite_disconnect, + .setup = configfs_composite_setup, + .reset = configfs_composite_disconnect, + .disconnect = configfs_composite_disconnect, - .suspend = composite_suspend, - .resume = composite_resume, + .suspend = configfs_composite_suspend, + .resume = configfs_composite_resume, .max_speed = USB_SPEED_SUPER, .driver = { -- cgit From e6fda6e69db26670f9f09b1742525329e477304a Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 9 Oct 2019 17:04:59 +0800 Subject: usb: mtu3: add a new function to do status stage Exact a new static function to do status stage Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_gadget_ep0.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 4da216c99726..df3fd055792f 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -153,6 +153,15 @@ static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy) set ? "SEND" : "CLEAR", decode_ep0_state(mtu)); } +static void ep0_do_status_stage(struct mtu3 *mtu) +{ + void __iomem *mbase = mtu->mac_base; + u32 value; + + value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; + mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND); +} + static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq); static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req) @@ -297,8 +306,7 @@ static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup) ep0_load_test_packet(mtu); /* send status before entering test mode. */ - value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; - mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND); + ep0_do_status_stage(mtu); /* wait for ACK status sent by host */ readl_poll_timeout_atomic(mbase + U3D_EP0CSR, value, @@ -632,7 +640,6 @@ __acquires(mtu->lock) { struct usb_ctrlrequest setup; struct mtu3_request *mreq; - void __iomem *mbase = mtu->mac_base; int handled = 0; ep0_read_setup(mtu, &setup); @@ -668,10 +675,7 @@ finish: mtu->delayed_status = true; } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */ - mtu3_writel(mbase, U3D_EP0CSR, - (mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS) - | EP0_SETUPPKTRDY | EP0_DATAEND); - + ep0_do_status_stage(mtu); /* complete zlp request directly */ mreq = next_ep0_request(mtu); if (mreq && !mreq->request.length) @@ -802,12 +806,9 @@ static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq) } if (mtu->delayed_status) { - u32 csr; mtu->delayed_status = false; - csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS; - csr |= EP0_SETUPPKTRDY | EP0_DATAEND; - mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr); + ep0_do_status_stage(mtu); /* needn't giveback the request for handling delay STATUS */ return 0; } -- cgit From 6973dbdd1c1f34cebf2b93ad0c9a1aba84c9c04a Mon Sep 17 00:00:00 2001 From: zhengbin Date: Wed, 9 Oct 2019 16:40:33 +0800 Subject: usb: gadget: Remove set but not used variable 'opts' in acm_ms_do_config Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/gadget/legacy/acm_ms.c: In function acm_ms_do_config: drivers/usb/gadget/legacy/acm_ms.c:108:19: warning: variable opts set but not used [-Wunused-but-set-variable] It is not used since commit f78bbcae86e6 ("usb: f_mass_storage: test whether thread is running before starting another") Reported-by: Hulk Robot Signed-off-by: zhengbin Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/acm_ms.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c index af16672d5118..59be2d8417c9 100644 --- a/drivers/usb/gadget/legacy/acm_ms.c +++ b/drivers/usb/gadget/legacy/acm_ms.c @@ -105,7 +105,6 @@ static struct usb_function *f_msg; */ static int acm_ms_do_config(struct usb_configuration *c) { - struct fsg_opts *opts; int status; if (gadget_is_otg(c->cdev->gadget)) { @@ -113,8 +112,6 @@ static int acm_ms_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - opts = fsg_opts_from_func_inst(fi_msg); - f_acm = usb_get_function(f_acm_inst); if (IS_ERR(f_acm)) return PTR_ERR(f_acm); -- cgit From e804cd46b2a214ae5e02e20daf591934a23a66c6 Mon Sep 17 00:00:00 2001 From: zhengbin Date: Wed, 9 Oct 2019 16:40:34 +0800 Subject: usb: gadget: Remove set but not used variable 'opts' in msg_do_config Fixes gcc '-Wunused-but-set-variable' warning: drivers/usb/gadget/legacy/mass_storage.c: In function msg_do_config: drivers/usb/gadget/legacy/mass_storage.c:108:19: warning: variable opts set but not used [-Wunused-but-set-variable] It is not used since commit f78bbcae86e6 ("usb: f_mass_storage: test whether thread is running before starting another") Reported-by: Hulk Robot Signed-off-by: zhengbin Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/mass_storage.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index fd5595ac5bf7..f18f77584fc2 100644 --- a/drivers/usb/gadget/legacy/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c @@ -105,7 +105,6 @@ FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); static int msg_do_config(struct usb_configuration *c) { - struct fsg_opts *opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -113,8 +112,6 @@ static int msg_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - opts = fsg_opts_from_func_inst(fi_msg); - f_msg = usb_get_function(fi_msg); if (IS_ERR(f_msg)) return PTR_ERR(f_msg); -- cgit From 1cbfb8c4f62d667f6b8b3948949737edb92992cc Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 30 Sep 2019 22:44:34 +0930 Subject: usb: gadget: Quieten gadget config message On a system that often re-configures a USB gadget device the kernel log is filled with: configfs-gadget gadget: high-speed config #1: c Reduce the verbosity of this print to debug. Signed-off-by: Joel Stanley Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index d516e8d6cd7f..bb0c744d5b44 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -794,9 +794,9 @@ static int set_config(struct usb_composite_dev *cdev, result = 0; } - INFO(cdev, "%s config #%d: %s\n", - usb_speed_string(gadget->speed), - number, c ? c->label : "unconfigured"); + DBG(cdev, "%s config #%d: %s\n", + usb_speed_string(gadget->speed), + number, c ? c->label : "unconfigured"); if (!c) goto done; -- cgit From 5a15007747518a6f26481c4b001c4c143d75c8a5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 27 Sep 2019 09:50:31 +0100 Subject: USB: gadget: udc: clean up an indentation issue There is a statement that is indented too deeply, remove the extraneous tabs. Signed-off-by: Colin Ian King Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index 7bfd58c846f7..248426a3e88a 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -195,7 +195,7 @@ static void handle_link_state_change(struct bdc *bdc, u32 uspc) break; case BDC_LINK_STATE_U0: if (bdc->devstatus & REMOTE_WAKEUP_ISSUED) { - bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED; + bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED; if (bdc->gadget.speed == USB_SPEED_SUPER) { bdc_function_wake_fh(bdc, 0); bdc->devstatus |= FUNC_WAKE_ISSUED; -- cgit From a9a367d0ade8ed3b8690a5c4b136454c541784ba Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 19 Sep 2019 15:47:24 +0200 Subject: usb: gadget: udc: lpc32xx: Use devm_platform_ioremap_resource() in lpc32xx_udc_probe() Simplify this function implementation by using a known wrapper function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/lpc32xx_udc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 2b1f3cc7819b..4939343e8176 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3000,7 +3000,6 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct lpc32xx_udc *udc; int retval, i; - struct resource *res; dma_addr_t dma_handle; struct device_node *isp1301_node; @@ -3048,9 +3047,6 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) * IORESOURCE_IRQ, USB device interrupt number * IORESOURCE_IRQ, USB transceiver interrupt number */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; spin_lock_init(&udc->lock); @@ -3061,7 +3057,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) return udc->udp_irq[i]; } - udc->udp_baseaddr = devm_ioremap_resource(dev, res); + udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->udp_baseaddr)) { dev_err(udc->dev, "IO map failure\n"); return PTR_ERR(udc->udp_baseaddr); -- cgit From 6689f0f4bb14e50917ba42eb9b41c25e0184970c Mon Sep 17 00:00:00 2001 From: Mathias Kresin Date: Sun, 7 Jul 2019 16:22:01 +0200 Subject: usb: dwc2: use a longer core rest timeout in dwc2_core_reset() Testing on different generations of Lantiq MIPS SoC based boards, showed that it takes up to 1500 us until the core reset bit is cleared. The driver from the vendor SDK (ifxhcd) uses a 1 second timeout. Use the same timeout to fix wrong hang detections and make the driver work for Lantiq MIPS SoCs. At least till kernel 4.14 the hanging reset only caused a warning but the driver was probed successful. With kernel 4.19 errors out with EBUSY. Cc: linux-stable # 4.19+ Signed-off-by: Mathias Kresin Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 8e41d70fd298..78a4925aa118 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -524,7 +524,7 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) greset |= GRSTCTL_CSFTRST; dwc2_writel(hsotg, greset, GRSTCTL); - if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 50)) { + if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 10000)) { dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n", __func__); return -EBUSY; -- cgit From 038761ce68c2354106fca276c8273351d5fe19a1 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Tue, 22 Oct 2019 12:10:16 -0700 Subject: usb: dwc3: debug: Remove newline printout The newline from the unknown link state tracepoint doesn't follow the other tracepoints, and it looks unsightly. Let's remove it. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debug.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 9baabed87d61..e56beb9d1e36 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -112,7 +112,7 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state) case DWC3_LINK_STATE_RESUME: return "Resume"; default: - return "UNKNOWN link state\n"; + return "UNKNOWN link state"; } } @@ -141,7 +141,7 @@ dwc3_gadget_hs_link_string(enum dwc3_link_state link_state) case DWC3_LINK_STATE_RESUME: return "Resume"; default: - return "UNKNOWN link state\n"; + return "UNKNOWN link state"; } } -- cgit From 49db427232fe2c357d23a2d62e2db1d431f95051 Mon Sep 17 00:00:00 2001 From: Nagarjuna Kristam Date: Fri, 18 Oct 2019 15:08:15 +0530 Subject: usb: gadget: Add UDC driver for tegra XUSB device mode controller This patch adds UDC driver for tegra XUSB 3.0 device mode controller. XUSB device mode controller supports SS, HS and FS modes Based on work by: Mark Kuo Hui Fu Andrew Bresticker Signed-off-by: Nagarjuna Kristam Acked-by: Thierry Reding Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/Kconfig | 11 + drivers/usb/gadget/udc/Makefile | 1 + drivers/usb/gadget/udc/tegra-xudc.c | 3810 +++++++++++++++++++++++++++++++++++ 3 files changed, 3822 insertions(+) create mode 100644 drivers/usb/gadget/udc/tegra-xudc.c (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index d354036ff6c8..c1f93647280c 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -441,6 +441,17 @@ config USB_GADGET_XILINX dynamically linked module called "udc-xilinx" and force all gadget drivers to also be dynamically linked. +config USB_TEGRA_XUDC + tristate "NVIDIA Tegra Superspeed USB 3.0 Device Controller" + depends on ARCH_TEGRA || COMPILE_TEST + depends on PHY_TEGRA_XUSB + help + Enables NVIDIA Tegra USB 3.0 device mode controller driver. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "tegra_xudc" and force all + gadget drivers to also be dynamically linked. + source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig" # diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 897f648f3cf1..f6777e654a8e 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o fsl_usb2_udc-y := fsl_udc_core.o fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o +obj-$(CONFIG_USB_TEGRA_XUDC) += tegra-xudc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_RENESAS_USB3) += renesas_usb3.o diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c new file mode 100644 index 000000000000..634c2c19a176 --- /dev/null +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -0,0 +1,3810 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NVIDIA Tegra XUSB device mode controller + * + * Copyright (c) 2013-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2015, Google Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* XUSB_DEV registers */ +#define SPARAM 0x000 +#define SPARAM_ERSTMAX_MASK GENMASK(20, 16) +#define SPARAM_ERSTMAX(x) (((x) << 16) & SPARAM_ERSTMAX_MASK) +#define DB 0x004 +#define DB_TARGET_MASK GENMASK(15, 8) +#define DB_TARGET(x) (((x) << 8) & DB_TARGET_MASK) +#define DB_STREAMID_MASK GENMASK(31, 16) +#define DB_STREAMID(x) (((x) << 16) & DB_STREAMID_MASK) +#define ERSTSZ 0x008 +#define ERSTSZ_ERSTXSZ_SHIFT(x) ((x) * 16) +#define ERSTSZ_ERSTXSZ_MASK GENMASK(15, 0) +#define ERSTXBALO(x) (0x010 + 8 * (x)) +#define ERSTXBAHI(x) (0x014 + 8 * (x)) +#define ERDPLO 0x020 +#define ERDPLO_EHB BIT(3) +#define ERDPHI 0x024 +#define EREPLO 0x028 +#define EREPLO_ECS BIT(0) +#define EREPLO_SEGI BIT(1) +#define EREPHI 0x02c +#define CTRL 0x030 +#define CTRL_RUN BIT(0) +#define CTRL_LSE BIT(1) +#define CTRL_IE BIT(4) +#define CTRL_SMI_EVT BIT(5) +#define CTRL_SMI_DSE BIT(6) +#define CTRL_EWE BIT(7) +#define CTRL_DEVADDR_MASK GENMASK(30, 24) +#define CTRL_DEVADDR(x) (((x) << 24) & CTRL_DEVADDR_MASK) +#define CTRL_ENABLE BIT(31) +#define ST 0x034 +#define ST_RC BIT(0) +#define ST_IP BIT(4) +#define RT_IMOD 0x038 +#define RT_IMOD_IMODI_MASK GENMASK(15, 0) +#define RT_IMOD_IMODI(x) ((x) & RT_IMOD_IMODI_MASK) +#define RT_IMOD_IMODC_MASK GENMASK(31, 16) +#define RT_IMOD_IMODC(x) (((x) << 16) & RT_IMOD_IMODC_MASK) +#define PORTSC 0x03c +#define PORTSC_CCS BIT(0) +#define PORTSC_PED BIT(1) +#define PORTSC_PR BIT(4) +#define PORTSC_PLS_SHIFT 5 +#define PORTSC_PLS_MASK GENMASK(8, 5) +#define PORTSC_PLS_U0 0x0 +#define PORTSC_PLS_U2 0x2 +#define PORTSC_PLS_U3 0x3 +#define PORTSC_PLS_DISABLED 0x4 +#define PORTSC_PLS_RXDETECT 0x5 +#define PORTSC_PLS_INACTIVE 0x6 +#define PORTSC_PLS_RESUME 0xf +#define PORTSC_PLS(x) (((x) << PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK) +#define PORTSC_PS_SHIFT 10 +#define PORTSC_PS_MASK GENMASK(13, 10) +#define PORTSC_PS_UNDEFINED 0x0 +#define PORTSC_PS_FS 0x1 +#define PORTSC_PS_LS 0x2 +#define PORTSC_PS_HS 0x3 +#define PORTSC_PS_SS 0x4 +#define PORTSC_LWS BIT(16) +#define PORTSC_CSC BIT(17) +#define PORTSC_WRC BIT(19) +#define PORTSC_PRC BIT(21) +#define PORTSC_PLC BIT(22) +#define PORTSC_CEC BIT(23) +#define PORTSC_WPR BIT(30) +#define PORTSC_CHANGE_MASK (PORTSC_CSC | PORTSC_WRC | PORTSC_PRC | \ + PORTSC_PLC | PORTSC_CEC) +#define ECPLO 0x040 +#define ECPHI 0x044 +#define MFINDEX 0x048 +#define MFINDEX_FRAME_SHIFT 3 +#define MFINDEX_FRAME_MASK GENMASK(13, 3) +#define PORTPM 0x04c +#define PORTPM_L1S_MASK GENMASK(1, 0) +#define PORTPM_L1S_DROP 0x0 +#define PORTPM_L1S_ACCEPT 0x1 +#define PORTPM_L1S_NYET 0x2 +#define PORTPM_L1S_STALL 0x3 +#define PORTPM_L1S(x) ((x) & PORTPM_L1S_MASK) +#define PORTPM_RWE BIT(3) +#define PORTPM_U2TIMEOUT_MASK GENMASK(15, 8) +#define PORTPM_U1TIMEOUT_MASK GENMASK(23, 16) +#define PORTPM_FLA BIT(24) +#define PORTPM_VBA BIT(25) +#define PORTPM_WOC BIT(26) +#define PORTPM_WOD BIT(27) +#define PORTPM_U1E BIT(28) +#define PORTPM_U2E BIT(29) +#define PORTPM_FRWE BIT(30) +#define PORTPM_PNG_CYA BIT(31) +#define EP_HALT 0x050 +#define EP_PAUSE 0x054 +#define EP_RELOAD 0x058 +#define EP_STCHG 0x05c +#define DEVNOTIF_LO 0x064 +#define DEVNOTIF_LO_TRIG BIT(0) +#define DEVNOTIF_LO_TYPE_MASK GENMASK(7, 4) +#define DEVNOTIF_LO_TYPE(x) (((x) << 4) & DEVNOTIF_LO_TYPE_MASK) +#define DEVNOTIF_LO_TYPE_FUNCTION_WAKE 0x1 +#define DEVNOTIF_HI 0x068 +#define PORTHALT 0x06c +#define PORTHALT_HALT_LTSSM BIT(0) +#define PORTHALT_HALT_REJECT BIT(1) +#define PORTHALT_STCHG_REQ BIT(20) +#define PORTHALT_STCHG_INTR_EN BIT(24) +#define PORT_TM 0x070 +#define EP_THREAD_ACTIVE 0x074 +#define EP_STOPPED 0x078 +#define HSFSPI_COUNT0 0x100 +#define HSFSPI_COUNT13 0x134 +#define HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK GENMASK(29, 0) +#define HSFSPI_COUNT13_U2_RESUME_K_DURATION(x) ((x) & \ + HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK) +#define BLCG 0x840 +#define SSPX_CORE_CNT0 0x610 +#define SSPX_CORE_CNT0_PING_TBURST_MASK GENMASK(7, 0) +#define SSPX_CORE_CNT0_PING_TBURST(x) ((x) & SSPX_CORE_CNT0_PING_TBURST_MASK) +#define SSPX_CORE_CNT30 0x688 +#define SSPX_CORE_CNT30_LMPITP_TIMER_MASK GENMASK(19, 0) +#define SSPX_CORE_CNT30_LMPITP_TIMER(x) ((x) & \ + SSPX_CORE_CNT30_LMPITP_TIMER_MASK) +#define SSPX_CORE_CNT32 0x690 +#define SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK GENMASK(7, 0) +#define SSPX_CORE_CNT32_POLL_TBURST_MAX(x) ((x) & \ + SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK) +#define SSPX_CORE_PADCTL4 0x750 +#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK GENMASK(19, 0) +#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(x) ((x) & \ + SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK) +#define BLCG_DFPCI BIT(0) +#define BLCG_UFPCI BIT(1) +#define BLCG_FE BIT(2) +#define BLCG_COREPLL_PWRDN BIT(8) +#define BLCG_IOPLL_0_PWRDN BIT(9) +#define BLCG_IOPLL_1_PWRDN BIT(10) +#define BLCG_IOPLL_2_PWRDN BIT(11) +#define BLCG_ALL 0x1ff +#define CFG_DEV_SSPI_XFER 0x858 +#define CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK GENMASK(31, 0) +#define CFG_DEV_SSPI_XFER_ACKTIMEOUT(x) ((x) & \ + CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK) +#define CFG_DEV_FE 0x85c +#define CFG_DEV_FE_PORTREGSEL_MASK GENMASK(1, 0) +#define CFG_DEV_FE_PORTREGSEL_SS_PI 1 +#define CFG_DEV_FE_PORTREGSEL_HSFS_PI 2 +#define CFG_DEV_FE_PORTREGSEL(x) ((x) & CFG_DEV_FE_PORTREGSEL_MASK) +#define CFG_DEV_FE_INFINITE_SS_RETRY BIT(29) + +/* FPCI registers */ +#define XUSB_DEV_CFG_1 0x004 +#define XUSB_DEV_CFG_1_IO_SPACE_EN BIT(0) +#define XUSB_DEV_CFG_1_MEMORY_SPACE_EN BIT(1) +#define XUSB_DEV_CFG_1_BUS_MASTER_EN BIT(2) +#define XUSB_DEV_CFG_4 0x010 +#define XUSB_DEV_CFG_4_BASE_ADDR_MASK GENMASK(31, 15) +#define XUSB_DEV_CFG_5 0x014 + +/* IPFS registers */ +#define XUSB_DEV_CONFIGURATION_0 0x180 +#define XUSB_DEV_CONFIGURATION_0_EN_FPCI BIT(0) +#define XUSB_DEV_INTR_MASK_0 0x188 +#define XUSB_DEV_INTR_MASK_0_IP_INT_MASK BIT(16) + +struct tegra_xudc_ep_context { + __le32 info0; + __le32 info1; + __le32 deq_lo; + __le32 deq_hi; + __le32 tx_info; + __le32 rsvd[11]; +}; + +#define EP_STATE_DISABLED 0 +#define EP_STATE_RUNNING 1 +#define EP_STATE_HALTED 2 +#define EP_STATE_STOPPED 3 +#define EP_STATE_ERROR 4 + +#define EP_TYPE_INVALID 0 +#define EP_TYPE_ISOCH_OUT 1 +#define EP_TYPE_BULK_OUT 2 +#define EP_TYPE_INTERRUPT_OUT 3 +#define EP_TYPE_CONTROL 4 +#define EP_TYPE_ISCOH_IN 5 +#define EP_TYPE_BULK_IN 6 +#define EP_TYPE_INTERRUPT_IN 7 + +#define BUILD_EP_CONTEXT_RW(name, member, shift, mask) \ +static inline u32 ep_ctx_read_##name(struct tegra_xudc_ep_context *ctx) \ +{ \ + return (le32_to_cpu(ctx->member) >> (shift)) & (mask); \ +} \ +static inline void \ +ep_ctx_write_##name(struct tegra_xudc_ep_context *ctx, u32 val) \ +{ \ + u32 tmp; \ + \ + tmp = le32_to_cpu(ctx->member) & ~((mask) << (shift)); \ + tmp |= (val & (mask)) << (shift); \ + ctx->member = cpu_to_le32(tmp); \ +} + +BUILD_EP_CONTEXT_RW(state, info0, 0, 0x7) +BUILD_EP_CONTEXT_RW(mult, info0, 8, 0x3) +BUILD_EP_CONTEXT_RW(max_pstreams, info0, 10, 0x1f) +BUILD_EP_CONTEXT_RW(lsa, info0, 15, 0x1) +BUILD_EP_CONTEXT_RW(interval, info0, 16, 0xff) +BUILD_EP_CONTEXT_RW(cerr, info1, 1, 0x3) +BUILD_EP_CONTEXT_RW(type, info1, 3, 0x7) +BUILD_EP_CONTEXT_RW(hid, info1, 7, 0x1) +BUILD_EP_CONTEXT_RW(max_burst_size, info1, 8, 0xff) +BUILD_EP_CONTEXT_RW(max_packet_size, info1, 16, 0xffff) +BUILD_EP_CONTEXT_RW(dcs, deq_lo, 0, 0x1) +BUILD_EP_CONTEXT_RW(deq_lo, deq_lo, 4, 0xfffffff) +BUILD_EP_CONTEXT_RW(deq_hi, deq_hi, 0, 0xffffffff) +BUILD_EP_CONTEXT_RW(avg_trb_len, tx_info, 0, 0xffff) +BUILD_EP_CONTEXT_RW(max_esit_payload, tx_info, 16, 0xffff) +BUILD_EP_CONTEXT_RW(edtla, rsvd[0], 0, 0xffffff) +BUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 24, 0xff) +BUILD_EP_CONTEXT_RW(partial_td, rsvd[0], 25, 0x1) +BUILD_EP_CONTEXT_RW(cerrcnt, rsvd[1], 18, 0x3) +BUILD_EP_CONTEXT_RW(data_offset, rsvd[2], 0, 0x1ffff) +BUILD_EP_CONTEXT_RW(numtrbs, rsvd[2], 22, 0x1f) +BUILD_EP_CONTEXT_RW(devaddr, rsvd[6], 0, 0x7f) + +static inline u64 ep_ctx_read_deq_ptr(struct tegra_xudc_ep_context *ctx) +{ + return ((u64)ep_ctx_read_deq_hi(ctx) << 32) | + (ep_ctx_read_deq_lo(ctx) << 4); +} + +static inline void +ep_ctx_write_deq_ptr(struct tegra_xudc_ep_context *ctx, u64 addr) +{ + ep_ctx_write_deq_lo(ctx, lower_32_bits(addr) >> 4); + ep_ctx_write_deq_hi(ctx, upper_32_bits(addr)); +} + +struct tegra_xudc_trb { + __le32 data_lo; + __le32 data_hi; + __le32 status; + __le32 control; +}; + +#define TRB_TYPE_RSVD 0 +#define TRB_TYPE_NORMAL 1 +#define TRB_TYPE_SETUP_STAGE 2 +#define TRB_TYPE_DATA_STAGE 3 +#define TRB_TYPE_STATUS_STAGE 4 +#define TRB_TYPE_ISOCH 5 +#define TRB_TYPE_LINK 6 +#define TRB_TYPE_TRANSFER_EVENT 32 +#define TRB_TYPE_PORT_STATUS_CHANGE_EVENT 34 +#define TRB_TYPE_STREAM 48 +#define TRB_TYPE_SETUP_PACKET_EVENT 63 + +#define TRB_CMPL_CODE_INVALID 0 +#define TRB_CMPL_CODE_SUCCESS 1 +#define TRB_CMPL_CODE_DATA_BUFFER_ERR 2 +#define TRB_CMPL_CODE_BABBLE_DETECTED_ERR 3 +#define TRB_CMPL_CODE_USB_TRANS_ERR 4 +#define TRB_CMPL_CODE_TRB_ERR 5 +#define TRB_CMPL_CODE_STALL 6 +#define TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR 10 +#define TRB_CMPL_CODE_SHORT_PACKET 13 +#define TRB_CMPL_CODE_RING_UNDERRUN 14 +#define TRB_CMPL_CODE_RING_OVERRUN 15 +#define TRB_CMPL_CODE_EVENT_RING_FULL_ERR 21 +#define TRB_CMPL_CODE_STOPPED 26 +#define TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN 31 +#define TRB_CMPL_CODE_STREAM_NUMP_ERROR 219 +#define TRB_CMPL_CODE_PRIME_PIPE_RECEIVED 220 +#define TRB_CMPL_CODE_HOST_REJECTED 221 +#define TRB_CMPL_CODE_CTRL_DIR_ERR 222 +#define TRB_CMPL_CODE_CTRL_SEQNUM_ERR 223 + +#define BUILD_TRB_RW(name, member, shift, mask) \ +static inline u32 trb_read_##name(struct tegra_xudc_trb *trb) \ +{ \ + return (le32_to_cpu(trb->member) >> (shift)) & (mask); \ +} \ +static inline void \ +trb_write_##name(struct tegra_xudc_trb *trb, u32 val) \ +{ \ + u32 tmp; \ + \ + tmp = le32_to_cpu(trb->member) & ~((mask) << (shift)); \ + tmp |= (val & (mask)) << (shift); \ + trb->member = cpu_to_le32(tmp); \ +} + +BUILD_TRB_RW(data_lo, data_lo, 0, 0xffffffff) +BUILD_TRB_RW(data_hi, data_hi, 0, 0xffffffff) +BUILD_TRB_RW(seq_num, status, 0, 0xffff) +BUILD_TRB_RW(transfer_len, status, 0, 0xffffff) +BUILD_TRB_RW(td_size, status, 17, 0x1f) +BUILD_TRB_RW(cmpl_code, status, 24, 0xff) +BUILD_TRB_RW(cycle, control, 0, 0x1) +BUILD_TRB_RW(toggle_cycle, control, 1, 0x1) +BUILD_TRB_RW(isp, control, 2, 0x1) +BUILD_TRB_RW(chain, control, 4, 0x1) +BUILD_TRB_RW(ioc, control, 5, 0x1) +BUILD_TRB_RW(type, control, 10, 0x3f) +BUILD_TRB_RW(stream_id, control, 16, 0xffff) +BUILD_TRB_RW(endpoint_id, control, 16, 0x1f) +BUILD_TRB_RW(tlbpc, control, 16, 0xf) +BUILD_TRB_RW(data_stage_dir, control, 16, 0x1) +BUILD_TRB_RW(frame_id, control, 20, 0x7ff) +BUILD_TRB_RW(sia, control, 31, 0x1) + +static inline u64 trb_read_data_ptr(struct tegra_xudc_trb *trb) +{ + return ((u64)trb_read_data_hi(trb) << 32) | + trb_read_data_lo(trb); +} + +static inline void trb_write_data_ptr(struct tegra_xudc_trb *trb, u64 addr) +{ + trb_write_data_lo(trb, lower_32_bits(addr)); + trb_write_data_hi(trb, upper_32_bits(addr)); +} + +struct tegra_xudc_request { + struct usb_request usb_req; + + size_t buf_queued; + unsigned int trbs_queued; + unsigned int trbs_needed; + bool need_zlp; + + struct tegra_xudc_trb *first_trb; + struct tegra_xudc_trb *last_trb; + + struct list_head list; +}; + +struct tegra_xudc_ep { + struct tegra_xudc *xudc; + struct usb_ep usb_ep; + unsigned int index; + char name[8]; + + struct tegra_xudc_ep_context *context; + +#define XUDC_TRANSFER_RING_SIZE 64 + struct tegra_xudc_trb *transfer_ring; + dma_addr_t transfer_ring_phys; + + unsigned int enq_ptr; + unsigned int deq_ptr; + bool pcs; + bool ring_full; + bool stream_rejected; + + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + const struct usb_ss_ep_comp_descriptor *comp_desc; +}; + +struct tegra_xudc_sel_timing { + __u8 u1sel; + __u8 u1pel; + __le16 u2sel; + __le16 u2pel; +}; + +enum tegra_xudc_setup_state { + WAIT_FOR_SETUP, + DATA_STAGE_XFER, + DATA_STAGE_RECV, + STATUS_STAGE_XFER, + STATUS_STAGE_RECV, +}; + +struct tegra_xudc_setup_packet { + struct usb_ctrlrequest ctrl_req; + unsigned int seq_num; +}; + +struct tegra_xudc_save_regs { + u32 ctrl; + u32 portpm; +}; + +struct tegra_xudc { + struct device *dev; + const struct tegra_xudc_soc *soc; + struct tegra_xusb_padctl *padctl; + + spinlock_t lock; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + +#define XUDC_NR_EVENT_RINGS 2 +#define XUDC_EVENT_RING_SIZE 4096 + struct tegra_xudc_trb *event_ring[XUDC_NR_EVENT_RINGS]; + dma_addr_t event_ring_phys[XUDC_NR_EVENT_RINGS]; + unsigned int event_ring_index; + unsigned int event_ring_deq_ptr; + bool ccs; + +#define XUDC_NR_EPS 32 + struct tegra_xudc_ep ep[XUDC_NR_EPS]; + struct tegra_xudc_ep_context *ep_context; + dma_addr_t ep_context_phys; + + struct device *genpd_dev_device; + struct device *genpd_dev_ss; + struct device_link *genpd_dl_device; + struct device_link *genpd_dl_ss; + + struct dma_pool *transfer_ring_pool; + + bool queued_setup_packet; + struct tegra_xudc_setup_packet setup_packet; + enum tegra_xudc_setup_state setup_state; + u16 setup_seq_num; + + u16 dev_addr; + u16 isoch_delay; + struct tegra_xudc_sel_timing sel_timing; + u8 test_mode_pattern; + u16 status_buf; + struct tegra_xudc_request *ep0_req; + + bool pullup; + + unsigned int nr_enabled_eps; + unsigned int nr_isoch_eps; + + unsigned int device_state; + unsigned int resume_state; + + int irq; + + void __iomem *base; + resource_size_t phys_base; + void __iomem *ipfs; + void __iomem *fpci; + + struct regulator_bulk_data *supplies; + + struct clk_bulk_data *clks; + + enum usb_role device_mode; + struct usb_role_switch *usb_role_sw; + struct work_struct usb_role_sw_work; + + struct phy *usb3_phy; + struct phy *utmi_phy; + + struct tegra_xudc_save_regs saved_regs; + bool suspended; + bool powergated; + + struct completion disconnect_complete; + + bool selfpowered; + +#define TOGGLE_VBUS_WAIT_MS 100 + struct delayed_work plc_reset_work; + bool wait_csc; + + struct delayed_work port_reset_war_work; + bool wait_for_sec_prc; +}; + +#define XUDC_TRB_MAX_BUFFER_SIZE 65536 +#define XUDC_MAX_ISOCH_EPS 4 +#define XUDC_INTERRUPT_MODERATION_US 0 + +static struct usb_endpoint_descriptor tegra_xudc_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(64), +}; + +struct tegra_xudc_soc { + const char * const *supply_names; + unsigned int num_supplies; + const char * const *clock_names; + unsigned int num_clks; + bool u1_enable; + bool u2_enable; + bool lpm_enable; + bool invalid_seq_num; + bool pls_quirk; + bool port_reset_quirk; + bool has_ipfs; +}; + +static inline u32 fpci_readl(struct tegra_xudc *xudc, unsigned int offset) +{ + return readl(xudc->fpci + offset); +} + +static inline void fpci_writel(struct tegra_xudc *xudc, u32 val, + unsigned int offset) +{ + writel(val, xudc->fpci + offset); +} + +static inline u32 ipfs_readl(struct tegra_xudc *xudc, unsigned int offset) +{ + return readl(xudc->ipfs + offset); +} + +static inline void ipfs_writel(struct tegra_xudc *xudc, u32 val, + unsigned int offset) +{ + writel(val, xudc->ipfs + offset); +} + +static inline u32 xudc_readl(struct tegra_xudc *xudc, unsigned int offset) +{ + return readl(xudc->base + offset); +} + +static inline void xudc_writel(struct tegra_xudc *xudc, u32 val, + unsigned int offset) +{ + writel(val, xudc->base + offset); +} + +static inline int xudc_readl_poll(struct tegra_xudc *xudc, + unsigned int offset, u32 mask, u32 val) +{ + u32 regval; + + return readl_poll_timeout_atomic(xudc->base + offset, regval, + (regval & mask) == val, 1, 100); +} + +static inline struct tegra_xudc *to_xudc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct tegra_xudc, gadget); +} + +static inline struct tegra_xudc_ep *to_xudc_ep(struct usb_ep *ep) +{ + return container_of(ep, struct tegra_xudc_ep, usb_ep); +} + +static inline struct tegra_xudc_request *to_xudc_req(struct usb_request *req) +{ + return container_of(req, struct tegra_xudc_request, usb_req); +} + +static inline void dump_trb(struct tegra_xudc *xudc, const char *type, + struct tegra_xudc_trb *trb) +{ + dev_dbg(xudc->dev, + "%s: %p, lo = %#x, hi = %#x, status = %#x, control = %#x\n", + type, trb, trb->data_lo, trb->data_hi, trb->status, + trb->control); +} + +static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc) +{ + int err; + + pm_runtime_get_sync(xudc->dev); + + err = phy_power_on(xudc->utmi_phy); + if (err < 0) + dev_err(xudc->dev, "utmi power on failed %d\n", err); + + err = phy_power_on(xudc->usb3_phy); + if (err < 0) + dev_err(xudc->dev, "usb3 phy power on failed %d\n", err); + + dev_dbg(xudc->dev, "device mode on\n"); + + tegra_xusb_padctl_set_vbus_override(xudc->padctl, true); + + xudc->device_mode = USB_ROLE_DEVICE; +} + +static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) +{ + bool connected = false; + u32 pls, val; + int err; + + dev_dbg(xudc->dev, "device mode off\n"); + + connected = !!(xudc_readl(xudc, PORTSC) & PORTSC_CCS); + + reinit_completion(&xudc->disconnect_complete); + + tegra_xusb_padctl_set_vbus_override(xudc->padctl, false); + + pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> + PORTSC_PLS_SHIFT; + + /* Direct link to U0 if disconnected in RESUME or U2. */ + if (xudc->soc->pls_quirk && xudc->gadget.speed == USB_SPEED_SUPER && + (pls == PORTSC_PLS_RESUME || pls == PORTSC_PLS_U2)) { + val = xudc_readl(xudc, PORTPM); + val |= PORTPM_FRWE; + xudc_writel(xudc, val, PORTPM); + + val = xudc_readl(xudc, PORTSC); + val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); + val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0); + xudc_writel(xudc, val, PORTSC); + } + + xudc->device_mode = USB_ROLE_NONE; + + /* Wait for disconnect event. */ + if (connected) + wait_for_completion(&xudc->disconnect_complete); + + /* Make sure interrupt handler has completed before powergating. */ + synchronize_irq(xudc->irq); + + err = phy_power_off(xudc->utmi_phy); + if (err < 0) + dev_err(xudc->dev, "utmi_phy power off failed %d\n", err); + + err = phy_power_off(xudc->usb3_phy); + if (err < 0) + dev_err(xudc->dev, "usb3_phy power off failed %d\n", err); + + pm_runtime_put(xudc->dev); +} + +static void tegra_xudc_usb_role_sw_work(struct work_struct *work) +{ + struct tegra_xudc *xudc = container_of(work, struct tegra_xudc, + usb_role_sw_work); + + if (!xudc->usb_role_sw || + usb_role_switch_get_role(xudc->usb_role_sw) == USB_ROLE_DEVICE) + tegra_xudc_device_mode_on(xudc); + else + tegra_xudc_device_mode_off(xudc); + +} + +static int tegra_xudc_usb_role_sw_set(struct device *dev, enum usb_role role) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + unsigned long flags; + + dev_dbg(dev, "%s role is %d\n", __func__, role); + + spin_lock_irqsave(&xudc->lock, flags); + + if (!xudc->suspended) + schedule_work(&xudc->usb_role_sw_work); + + spin_unlock_irqrestore(&xudc->lock, flags); + + return 0; +} + +static void tegra_xudc_plc_reset_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tegra_xudc *xudc = container_of(dwork, struct tegra_xudc, + plc_reset_work); + unsigned long flags; + + spin_lock_irqsave(&xudc->lock, flags); + + if (xudc->wait_csc) { + u32 pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> + PORTSC_PLS_SHIFT; + + if (pls == PORTSC_PLS_INACTIVE) { + dev_info(xudc->dev, "PLS = Inactive. Toggle VBUS\n"); + tegra_xusb_padctl_set_vbus_override(xudc->padctl, + false); + tegra_xusb_padctl_set_vbus_override(xudc->padctl, true); + xudc->wait_csc = false; + } + } + + spin_unlock_irqrestore(&xudc->lock, flags); +} + +static void tegra_xudc_port_reset_war_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tegra_xudc *xudc = + container_of(dwork, struct tegra_xudc, port_reset_war_work); + unsigned long flags; + u32 pls; + int ret; + + spin_lock_irqsave(&xudc->lock, flags); + + if ((xudc->device_mode == USB_ROLE_DEVICE) + && xudc->wait_for_sec_prc) { + pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> + PORTSC_PLS_SHIFT; + dev_dbg(xudc->dev, "pls = %x\n", pls); + + if (pls == PORTSC_PLS_DISABLED) { + dev_dbg(xudc->dev, "toggle vbus\n"); + /* PRC doesn't complete in 100ms, toggle the vbus */ + ret = tegra_phy_xusb_utmi_port_reset(xudc->utmi_phy); + if (ret == 1) + xudc->wait_for_sec_prc = 0; + } + } + + spin_unlock_irqrestore(&xudc->lock, flags); +} + +static dma_addr_t trb_virt_to_phys(struct tegra_xudc_ep *ep, + struct tegra_xudc_trb *trb) +{ + unsigned int index; + + index = trb - ep->transfer_ring; + + if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE)) + return 0; + + return (ep->transfer_ring_phys + index * sizeof(*trb)); +} + +static struct tegra_xudc_trb *trb_phys_to_virt(struct tegra_xudc_ep *ep, + dma_addr_t addr) +{ + struct tegra_xudc_trb *trb; + unsigned int index; + + index = (addr - ep->transfer_ring_phys) / sizeof(*trb); + + if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE)) + return NULL; + + trb = &ep->transfer_ring[index]; + + return trb; +} + +static void ep_reload(struct tegra_xudc *xudc, unsigned int ep) +{ + xudc_writel(xudc, BIT(ep), EP_RELOAD); + xudc_readl_poll(xudc, EP_RELOAD, BIT(ep), 0); +} + +static void ep_pause(struct tegra_xudc *xudc, unsigned int ep) +{ + u32 val; + + val = xudc_readl(xudc, EP_PAUSE); + if (val & BIT(ep)) + return; + val |= BIT(ep); + + xudc_writel(xudc, val, EP_PAUSE); + + xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); + + xudc_writel(xudc, BIT(ep), EP_STCHG); +} + +static void ep_unpause(struct tegra_xudc *xudc, unsigned int ep) +{ + u32 val; + + val = xudc_readl(xudc, EP_PAUSE); + if (!(val & BIT(ep))) + return; + val &= ~BIT(ep); + + xudc_writel(xudc, val, EP_PAUSE); + + xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); + + xudc_writel(xudc, BIT(ep), EP_STCHG); +} + +static void ep_unpause_all(struct tegra_xudc *xudc) +{ + u32 val; + + val = xudc_readl(xudc, EP_PAUSE); + + xudc_writel(xudc, 0, EP_PAUSE); + + xudc_readl_poll(xudc, EP_STCHG, val, val); + + xudc_writel(xudc, val, EP_STCHG); +} + +static void ep_halt(struct tegra_xudc *xudc, unsigned int ep) +{ + u32 val; + + val = xudc_readl(xudc, EP_HALT); + if (val & BIT(ep)) + return; + val |= BIT(ep); + xudc_writel(xudc, val, EP_HALT); + + xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); + + xudc_writel(xudc, BIT(ep), EP_STCHG); +} + +static void ep_unhalt(struct tegra_xudc *xudc, unsigned int ep) +{ + u32 val; + + val = xudc_readl(xudc, EP_HALT); + if (!(val & BIT(ep))) + return; + val &= ~BIT(ep); + xudc_writel(xudc, val, EP_HALT); + + xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); + + xudc_writel(xudc, BIT(ep), EP_STCHG); +} + +static void ep_unhalt_all(struct tegra_xudc *xudc) +{ + u32 val; + + val = xudc_readl(xudc, EP_HALT); + if (!val) + return; + xudc_writel(xudc, 0, EP_HALT); + + xudc_readl_poll(xudc, EP_STCHG, val, val); + + xudc_writel(xudc, val, EP_STCHG); +} + +static void ep_wait_for_stopped(struct tegra_xudc *xudc, unsigned int ep) +{ + xudc_readl_poll(xudc, EP_STOPPED, BIT(ep), BIT(ep)); + xudc_writel(xudc, BIT(ep), EP_STOPPED); +} + +static void ep_wait_for_inactive(struct tegra_xudc *xudc, unsigned int ep) +{ + xudc_readl_poll(xudc, EP_THREAD_ACTIVE, BIT(ep), 0); +} + +static void tegra_xudc_req_done(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req, int status) +{ + struct tegra_xudc *xudc = ep->xudc; + + dev_dbg(xudc->dev, "completing request %p on EP %u with status %d\n", + req, ep->index, status); + + if (likely(req->usb_req.status == -EINPROGRESS)) + req->usb_req.status = status; + + list_del_init(&req->list); + + if (usb_endpoint_xfer_control(ep->desc)) { + usb_gadget_unmap_request(&xudc->gadget, &req->usb_req, + (xudc->setup_state == + DATA_STAGE_XFER)); + } else { + usb_gadget_unmap_request(&xudc->gadget, &req->usb_req, + usb_endpoint_dir_in(ep->desc)); + } + + spin_unlock(&xudc->lock); + usb_gadget_giveback_request(&ep->usb_ep, &req->usb_req); + spin_lock(&xudc->lock); +} + +static void tegra_xudc_ep_nuke(struct tegra_xudc_ep *ep, int status) +{ + struct tegra_xudc_request *req; + + while (!list_empty(&ep->queue)) { + req = list_first_entry(&ep->queue, struct tegra_xudc_request, + list); + tegra_xudc_req_done(ep, req, status); + } +} + +static unsigned int ep_available_trbs(struct tegra_xudc_ep *ep) +{ + if (ep->ring_full) + return 0; + + if (ep->deq_ptr > ep->enq_ptr) + return ep->deq_ptr - ep->enq_ptr - 1; + + return XUDC_TRANSFER_RING_SIZE - (ep->enq_ptr - ep->deq_ptr) - 2; +} + +static void tegra_xudc_queue_one_trb(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req, + struct tegra_xudc_trb *trb, + bool ioc) +{ + struct tegra_xudc *xudc = ep->xudc; + dma_addr_t buf_addr; + size_t len; + + len = min_t(size_t, XUDC_TRB_MAX_BUFFER_SIZE, req->usb_req.length - + req->buf_queued); + if (len > 0) + buf_addr = req->usb_req.dma + req->buf_queued; + else + buf_addr = 0; + + trb_write_data_ptr(trb, buf_addr); + + trb_write_transfer_len(trb, len); + trb_write_td_size(trb, req->trbs_needed - req->trbs_queued - 1); + + if (req->trbs_queued == req->trbs_needed - 1 || + (req->need_zlp && req->trbs_queued == req->trbs_needed - 2)) + trb_write_chain(trb, 0); + else + trb_write_chain(trb, 1); + + trb_write_ioc(trb, ioc); + + if (usb_endpoint_dir_out(ep->desc) || + (usb_endpoint_xfer_control(ep->desc) && + (xudc->setup_state == DATA_STAGE_RECV))) + trb_write_isp(trb, 1); + else + trb_write_isp(trb, 0); + + if (usb_endpoint_xfer_control(ep->desc)) { + if (xudc->setup_state == DATA_STAGE_XFER || + xudc->setup_state == DATA_STAGE_RECV) + trb_write_type(trb, TRB_TYPE_DATA_STAGE); + else + trb_write_type(trb, TRB_TYPE_STATUS_STAGE); + + if (xudc->setup_state == DATA_STAGE_XFER || + xudc->setup_state == STATUS_STAGE_XFER) + trb_write_data_stage_dir(trb, 1); + else + trb_write_data_stage_dir(trb, 0); + } else if (usb_endpoint_xfer_isoc(ep->desc)) { + trb_write_type(trb, TRB_TYPE_ISOCH); + trb_write_sia(trb, 1); + trb_write_frame_id(trb, 0); + trb_write_tlbpc(trb, 0); + } else if (usb_ss_max_streams(ep->comp_desc)) { + trb_write_type(trb, TRB_TYPE_STREAM); + trb_write_stream_id(trb, req->usb_req.stream_id); + } else { + trb_write_type(trb, TRB_TYPE_NORMAL); + trb_write_stream_id(trb, 0); + } + + trb_write_cycle(trb, ep->pcs); + + req->trbs_queued++; + req->buf_queued += len; + + dump_trb(xudc, "TRANSFER", trb); +} + +static unsigned int tegra_xudc_queue_trbs(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req) +{ + unsigned int i, count, available; + bool wait_td = false; + + available = ep_available_trbs(ep); + count = req->trbs_needed - req->trbs_queued; + if (available < count) { + count = available; + ep->ring_full = true; + } + + /* + * To generate zero-length packet on USB bus, SW needs schedule a + * standalone zero-length TD. According to HW's behavior, SW needs + * to schedule TDs in different ways for different endpoint types. + * + * For control endpoint: + * - Data stage TD (IOC = 1, CH = 0) + * - Ring doorbell and wait transfer event + * - Data stage TD for ZLP (IOC = 1, CH = 0) + * - Ring doorbell + * + * For bulk and interrupt endpoints: + * - Normal transfer TD (IOC = 0, CH = 0) + * - Normal transfer TD for ZLP (IOC = 1, CH = 0) + * - Ring doorbell + */ + + if (req->need_zlp && usb_endpoint_xfer_control(ep->desc) && count > 1) + wait_td = true; + + if (!req->first_trb) + req->first_trb = &ep->transfer_ring[ep->enq_ptr]; + + for (i = 0; i < count; i++) { + struct tegra_xudc_trb *trb = &ep->transfer_ring[ep->enq_ptr]; + bool ioc = false; + + if ((i == count - 1) || (wait_td && i == count - 2)) + ioc = true; + + tegra_xudc_queue_one_trb(ep, req, trb, ioc); + req->last_trb = trb; + + ep->enq_ptr++; + if (ep->enq_ptr == XUDC_TRANSFER_RING_SIZE - 1) { + trb = &ep->transfer_ring[ep->enq_ptr]; + trb_write_cycle(trb, ep->pcs); + ep->pcs = !ep->pcs; + ep->enq_ptr = 0; + } + + if (ioc) + break; + } + + return count; +} + +static void tegra_xudc_ep_ring_doorbell(struct tegra_xudc_ep *ep) +{ + struct tegra_xudc *xudc = ep->xudc; + u32 val; + + if (list_empty(&ep->queue)) + return; + + val = DB_TARGET(ep->index); + if (usb_endpoint_xfer_control(ep->desc)) { + val |= DB_STREAMID(xudc->setup_seq_num); + } else if (usb_ss_max_streams(ep->comp_desc) > 0) { + struct tegra_xudc_request *req; + + /* Don't ring doorbell if the stream has been rejected. */ + if (ep->stream_rejected) + return; + + req = list_first_entry(&ep->queue, struct tegra_xudc_request, + list); + val |= DB_STREAMID(req->usb_req.stream_id); + } + + dev_dbg(xudc->dev, "ring doorbell: %#x\n", val); + xudc_writel(xudc, val, DB); +} + +static void tegra_xudc_ep_kick_queue(struct tegra_xudc_ep *ep) +{ + struct tegra_xudc_request *req; + bool trbs_queued = false; + + list_for_each_entry(req, &ep->queue, list) { + if (ep->ring_full) + break; + + if (tegra_xudc_queue_trbs(ep, req) > 0) + trbs_queued = true; + } + + if (trbs_queued) + tegra_xudc_ep_ring_doorbell(ep); +} + +static int +__tegra_xudc_ep_queue(struct tegra_xudc_ep *ep, struct tegra_xudc_request *req) +{ + struct tegra_xudc *xudc = ep->xudc; + int err; + + if (usb_endpoint_xfer_control(ep->desc) && !list_empty(&ep->queue)) { + dev_err(xudc->dev, "control EP has pending transfers\n"); + return -EINVAL; + } + + if (usb_endpoint_xfer_control(ep->desc)) { + err = usb_gadget_map_request(&xudc->gadget, &req->usb_req, + (xudc->setup_state == + DATA_STAGE_XFER)); + } else { + err = usb_gadget_map_request(&xudc->gadget, &req->usb_req, + usb_endpoint_dir_in(ep->desc)); + } + + if (err < 0) { + dev_err(xudc->dev, "failed to map request: %d\n", err); + return err; + } + + req->first_trb = NULL; + req->last_trb = NULL; + req->buf_queued = 0; + req->trbs_queued = 0; + req->need_zlp = false; + req->trbs_needed = DIV_ROUND_UP(req->usb_req.length, + XUDC_TRB_MAX_BUFFER_SIZE); + if (req->usb_req.length == 0) + req->trbs_needed++; + + if (!usb_endpoint_xfer_isoc(ep->desc) && + req->usb_req.zero && req->usb_req.length && + ((req->usb_req.length % ep->usb_ep.maxpacket) == 0)) { + req->trbs_needed++; + req->need_zlp = true; + } + + req->usb_req.status = -EINPROGRESS; + req->usb_req.actual = 0; + + list_add_tail(&req->list, &ep->queue); + + tegra_xudc_ep_kick_queue(ep); + + return 0; +} + +static int +tegra_xudc_ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, + gfp_t gfp) +{ + struct tegra_xudc_request *req; + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep || !usb_req) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + req = to_xudc_req(usb_req); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated || !ep->desc) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = __tegra_xudc_ep_queue(ep, req); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static void squeeze_transfer_ring(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req) +{ + struct tegra_xudc_trb *trb = req->first_trb; + bool pcs_enq = trb_read_cycle(trb); + bool pcs; + + /* + * Clear out all the TRBs part of or after the cancelled request, + * and must correct trb cycle bit to the last un-enqueued state. + */ + while (trb != &ep->transfer_ring[ep->enq_ptr]) { + pcs = trb_read_cycle(trb); + memset(trb, 0, sizeof(*trb)); + trb_write_cycle(trb, !pcs); + trb++; + + if (trb_read_type(trb) == TRB_TYPE_LINK) + trb = ep->transfer_ring; + } + + /* Requests will be re-queued at the start of the cancelled request. */ + ep->enq_ptr = req->first_trb - ep->transfer_ring; + /* + * Retrieve the correct cycle bit state from the first trb of + * the cancelled request. + */ + ep->pcs = pcs_enq; + ep->ring_full = false; + list_for_each_entry_continue(req, &ep->queue, list) { + req->usb_req.status = -EINPROGRESS; + req->usb_req.actual = 0; + + req->first_trb = NULL; + req->last_trb = NULL; + req->buf_queued = 0; + req->trbs_queued = 0; + } +} + +/* + * Determine if the given TRB is in the range [first trb, last trb] for the + * given request. + */ +static bool trb_in_request(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req, + struct tegra_xudc_trb *trb) +{ + dev_dbg(ep->xudc->dev, "%s: request %p -> %p; trb %p\n", __func__, + req->first_trb, req->last_trb, trb); + + if (trb >= req->first_trb && (trb <= req->last_trb || + req->last_trb < req->first_trb)) + return true; + + if (trb < req->first_trb && trb <= req->last_trb && + req->last_trb < req->first_trb) + return true; + + return false; +} + +/* + * Determine if the given TRB is in the range [EP enqueue pointer, first TRB) + * for the given endpoint and request. + */ +static bool trb_before_request(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req, + struct tegra_xudc_trb *trb) +{ + struct tegra_xudc_trb *enq_trb = &ep->transfer_ring[ep->enq_ptr]; + + dev_dbg(ep->xudc->dev, "%s: request %p -> %p; enq ptr: %p; trb %p\n", + __func__, req->first_trb, req->last_trb, enq_trb, trb); + + if (trb < req->first_trb && (enq_trb <= trb || + req->first_trb < enq_trb)) + return true; + + if (trb > req->first_trb && req->first_trb < enq_trb && enq_trb <= trb) + return true; + + return false; +} + +static int +__tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req) +{ + struct tegra_xudc *xudc = ep->xudc; + struct tegra_xudc_request *r; + struct tegra_xudc_trb *deq_trb; + bool busy, kick_queue = false; + int ret = 0; + + /* Make sure the request is actually queued to this endpoint. */ + list_for_each_entry(r, &ep->queue, list) { + if (r == req) + break; + } + + if (r != req) + return -EINVAL; + + /* Request hasn't been queued in the transfer ring yet. */ + if (!req->trbs_queued) { + tegra_xudc_req_done(ep, req, -ECONNRESET); + return 0; + } + + /* Halt DMA for this endpiont. */ + if (ep_ctx_read_state(ep->context) == EP_STATE_RUNNING) { + ep_pause(xudc, ep->index); + ep_wait_for_inactive(xudc, ep->index); + } + + deq_trb = trb_phys_to_virt(ep, ep_ctx_read_deq_ptr(ep->context)); + /* Is the hardware processing the TRB at the dequeue pointer? */ + busy = (trb_read_cycle(deq_trb) == ep_ctx_read_dcs(ep->context)); + + if (trb_in_request(ep, req, deq_trb) && busy) { + /* + * Request has been partially completed or it hasn't + * started processing yet. + */ + dma_addr_t deq_ptr; + + squeeze_transfer_ring(ep, req); + + req->usb_req.actual = ep_ctx_read_edtla(ep->context); + tegra_xudc_req_done(ep, req, -ECONNRESET); + kick_queue = true; + + /* EDTLA is > 0: request has been partially completed */ + if (req->usb_req.actual > 0) { + /* + * Abort the pending transfer and update the dequeue + * pointer + */ + ep_ctx_write_edtla(ep->context, 0); + ep_ctx_write_partial_td(ep->context, 0); + ep_ctx_write_data_offset(ep->context, 0); + + deq_ptr = trb_virt_to_phys(ep, + &ep->transfer_ring[ep->enq_ptr]); + + if (dma_mapping_error(xudc->dev, deq_ptr)) { + ret = -EINVAL; + } else { + ep_ctx_write_deq_ptr(ep->context, deq_ptr); + ep_ctx_write_dcs(ep->context, ep->pcs); + ep_reload(xudc, ep->index); + } + } + } else if (trb_before_request(ep, req, deq_trb) && busy) { + /* Request hasn't started processing yet. */ + squeeze_transfer_ring(ep, req); + + tegra_xudc_req_done(ep, req, -ECONNRESET); + kick_queue = true; + } else { + /* + * Request has completed, but we haven't processed the + * completion event yet. + */ + tegra_xudc_req_done(ep, req, -ECONNRESET); + ret = -EINVAL; + } + + /* Resume the endpoint. */ + ep_unpause(xudc, ep->index); + + if (kick_queue) + tegra_xudc_ep_kick_queue(ep); + + return ret; +} + +static int +tegra_xudc_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) +{ + struct tegra_xudc_request *req; + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep || !usb_req) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + req = to_xudc_req(usb_req); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + + if (xudc->powergated || !ep->desc) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = __tegra_xudc_ep_dequeue(ep, req); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt) +{ + struct tegra_xudc *xudc = ep->xudc; + + if (!ep->desc) + return -EINVAL; + + if (usb_endpoint_xfer_isoc(ep->desc)) { + dev_err(xudc->dev, "can't halt isoc EP\n"); + return -ENOTSUPP; + } + + if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) { + dev_dbg(xudc->dev, "EP %u already %s\n", ep->index, + halt ? "halted" : "not halted"); + return 0; + } + + if (halt) { + ep_halt(xudc, ep->index); + } else { + ep_ctx_write_state(ep->context, EP_STATE_DISABLED); + + ep_reload(xudc, ep->index); + + ep_ctx_write_state(ep->context, EP_STATE_RUNNING); + ep_ctx_write_seq_num(ep->context, 0); + + ep_reload(xudc, ep->index); + ep_unpause(xudc, ep->index); + ep_unhalt(xudc, ep->index); + + tegra_xudc_ep_ring_doorbell(ep); + } + + return 0; +} + +static int tegra_xudc_ep_set_halt(struct usb_ep *usb_ep, int value) +{ + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + + if (value && usb_endpoint_dir_in(ep->desc) && + !list_empty(&ep->queue)) { + dev_err(xudc->dev, "can't halt EP with requests pending\n"); + ret = -EAGAIN; + goto unlock; + } + + ret = __tegra_xudc_ep_set_halt(ep, value); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static void tegra_xudc_ep_context_setup(struct tegra_xudc_ep *ep) +{ + const struct usb_endpoint_descriptor *desc = ep->desc; + const struct usb_ss_ep_comp_descriptor *comp_desc = ep->comp_desc; + struct tegra_xudc *xudc = ep->xudc; + u16 maxpacket, maxburst = 0, esit = 0; + u32 val; + + maxpacket = usb_endpoint_maxp(desc) & 0x7ff; + if (xudc->gadget.speed == USB_SPEED_SUPER) { + if (!usb_endpoint_xfer_control(desc)) + maxburst = comp_desc->bMaxBurst; + + if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) + esit = le16_to_cpu(comp_desc->wBytesPerInterval); + } else if ((xudc->gadget.speed < USB_SPEED_SUPER) && + (usb_endpoint_xfer_int(desc) || + usb_endpoint_xfer_isoc(desc))) { + if (xudc->gadget.speed == USB_SPEED_HIGH) { + maxburst = (usb_endpoint_maxp(desc) >> 11) & 0x3; + if (maxburst == 0x3) { + dev_warn(xudc->dev, + "invalid endpoint maxburst\n"); + maxburst = 0x2; + } + } + esit = maxpacket * (maxburst + 1); + } + + memset(ep->context, 0, sizeof(*ep->context)); + + ep_ctx_write_state(ep->context, EP_STATE_RUNNING); + ep_ctx_write_interval(ep->context, desc->bInterval); + if (xudc->gadget.speed == USB_SPEED_SUPER) { + if (usb_endpoint_xfer_isoc(desc)) { + ep_ctx_write_mult(ep->context, + comp_desc->bmAttributes & 0x3); + } + + if (usb_endpoint_xfer_bulk(desc)) { + ep_ctx_write_max_pstreams(ep->context, + comp_desc->bmAttributes & + 0x1f); + ep_ctx_write_lsa(ep->context, 1); + } + } + + if (!usb_endpoint_xfer_control(desc) && usb_endpoint_dir_out(desc)) + val = usb_endpoint_type(desc); + else + val = usb_endpoint_type(desc) + EP_TYPE_CONTROL; + + ep_ctx_write_type(ep->context, val); + ep_ctx_write_cerr(ep->context, 0x3); + ep_ctx_write_max_packet_size(ep->context, maxpacket); + ep_ctx_write_max_burst_size(ep->context, maxburst); + + ep_ctx_write_deq_ptr(ep->context, ep->transfer_ring_phys); + ep_ctx_write_dcs(ep->context, ep->pcs); + + /* Select a reasonable average TRB length based on endpoint type. */ + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_CONTROL: + val = 8; + break; + case USB_ENDPOINT_XFER_INT: + val = 1024; + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_ISOC: + default: + val = 3072; + break; + } + + ep_ctx_write_avg_trb_len(ep->context, val); + ep_ctx_write_max_esit_payload(ep->context, esit); + + ep_ctx_write_cerrcnt(ep->context, 0x3); +} + +static void setup_link_trb(struct tegra_xudc_ep *ep, + struct tegra_xudc_trb *trb) +{ + trb_write_data_ptr(trb, ep->transfer_ring_phys); + trb_write_type(trb, TRB_TYPE_LINK); + trb_write_toggle_cycle(trb, 1); +} + +static int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep) +{ + struct tegra_xudc *xudc = ep->xudc; + + if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) { + dev_err(xudc->dev, "endpoint %u already disabled\n", + ep->index); + return -EINVAL; + } + + ep_ctx_write_state(ep->context, EP_STATE_DISABLED); + + ep_reload(xudc, ep->index); + + tegra_xudc_ep_nuke(ep, -ESHUTDOWN); + + xudc->nr_enabled_eps--; + if (usb_endpoint_xfer_isoc(ep->desc)) + xudc->nr_isoch_eps--; + + ep->desc = NULL; + ep->comp_desc = NULL; + + memset(ep->context, 0, sizeof(*ep->context)); + + ep_unpause(xudc, ep->index); + ep_unhalt(xudc, ep->index); + if (xudc_readl(xudc, EP_STOPPED) & BIT(ep->index)) + xudc_writel(xudc, BIT(ep->index), EP_STOPPED); + + /* + * If this is the last endpoint disabled in a de-configure request, + * switch back to address state. + */ + if ((xudc->device_state == USB_STATE_CONFIGURED) && + (xudc->nr_enabled_eps == 1)) { + u32 val; + + xudc->device_state = USB_STATE_ADDRESS; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + val = xudc_readl(xudc, CTRL); + val &= ~CTRL_RUN; + xudc_writel(xudc, val, CTRL); + } + + dev_info(xudc->dev, "ep %u disabled\n", ep->index); + + return 0; +} + +static int tegra_xudc_ep_disable(struct usb_ep *usb_ep) +{ + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = __tegra_xudc_ep_disable(ep); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static int __tegra_xudc_ep_enable(struct tegra_xudc_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct tegra_xudc *xudc = ep->xudc; + unsigned int i; + u32 val; + + if (xudc->gadget.speed == USB_SPEED_SUPER && + !usb_endpoint_xfer_control(desc) && !ep->usb_ep.comp_desc) + return -EINVAL; + + /* Disable the EP if it is not disabled */ + if (ep_ctx_read_state(ep->context) != EP_STATE_DISABLED) + __tegra_xudc_ep_disable(ep); + + ep->desc = desc; + ep->comp_desc = ep->usb_ep.comp_desc; + + if (usb_endpoint_xfer_isoc(desc)) { + if (xudc->nr_isoch_eps > XUDC_MAX_ISOCH_EPS) { + dev_err(xudc->dev, "too many isoch endpoints\n"); + return -EBUSY; + } + xudc->nr_isoch_eps++; + } + + memset(ep->transfer_ring, 0, XUDC_TRANSFER_RING_SIZE * + sizeof(*ep->transfer_ring)); + setup_link_trb(ep, &ep->transfer_ring[XUDC_TRANSFER_RING_SIZE - 1]); + + ep->enq_ptr = 0; + ep->deq_ptr = 0; + ep->pcs = true; + ep->ring_full = false; + xudc->nr_enabled_eps++; + + tegra_xudc_ep_context_setup(ep); + + /* + * No need to reload and un-halt EP0. This will be done automatically + * once a valid SETUP packet is received. + */ + if (usb_endpoint_xfer_control(desc)) + goto out; + + /* + * Transition to configured state once the first non-control + * endpoint is enabled. + */ + if (xudc->device_state == USB_STATE_ADDRESS) { + val = xudc_readl(xudc, CTRL); + val |= CTRL_RUN; + xudc_writel(xudc, val, CTRL); + + xudc->device_state = USB_STATE_CONFIGURED; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + } + + if (usb_endpoint_xfer_isoc(desc)) { + /* + * Pause all bulk endpoints when enabling an isoch endpoint + * to ensure the isoch endpoint is allocated enough bandwidth. + */ + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) { + if (xudc->ep[i].desc && + usb_endpoint_xfer_bulk(xudc->ep[i].desc)) + ep_pause(xudc, i); + } + } + + ep_reload(xudc, ep->index); + ep_unpause(xudc, ep->index); + ep_unhalt(xudc, ep->index); + + if (usb_endpoint_xfer_isoc(desc)) { + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) { + if (xudc->ep[i].desc && + usb_endpoint_xfer_bulk(xudc->ep[i].desc)) + ep_unpause(xudc, i); + } + } + +out: + dev_info(xudc->dev, "EP %u (type: %s, dir: %s) enabled\n", ep->index, + usb_ep_type_string(usb_endpoint_type(ep->desc)), + usb_endpoint_dir_in(ep->desc) ? "in" : "out"); + + return 0; +} + +static int tegra_xudc_ep_enable(struct usb_ep *usb_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep || !desc || (desc->bDescriptorType != USB_DT_ENDPOINT)) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = __tegra_xudc_ep_enable(ep, desc); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static struct usb_request * +tegra_xudc_ep_alloc_request(struct usb_ep *usb_ep, gfp_t gfp) +{ + struct tegra_xudc_request *req; + + req = kzalloc(sizeof(*req), gfp); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->list); + + return &req->usb_req; +} + +static void tegra_xudc_ep_free_request(struct usb_ep *usb_ep, + struct usb_request *usb_req) +{ + struct tegra_xudc_request *req = to_xudc_req(usb_req); + + kfree(req); +} + +static struct usb_ep_ops tegra_xudc_ep_ops = { + .enable = tegra_xudc_ep_enable, + .disable = tegra_xudc_ep_disable, + .alloc_request = tegra_xudc_ep_alloc_request, + .free_request = tegra_xudc_ep_free_request, + .queue = tegra_xudc_ep_queue, + .dequeue = tegra_xudc_ep_dequeue, + .set_halt = tegra_xudc_ep_set_halt, +}; + +static int tegra_xudc_ep0_enable(struct usb_ep *usb_ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EBUSY; +} + +static int tegra_xudc_ep0_disable(struct usb_ep *usb_ep) +{ + return -EBUSY; +} + +static struct usb_ep_ops tegra_xudc_ep0_ops = { + .enable = tegra_xudc_ep0_enable, + .disable = tegra_xudc_ep0_disable, + .alloc_request = tegra_xudc_ep_alloc_request, + .free_request = tegra_xudc_ep_free_request, + .queue = tegra_xudc_ep_queue, + .dequeue = tegra_xudc_ep_dequeue, + .set_halt = tegra_xudc_ep_set_halt, +}; + +static int tegra_xudc_gadget_get_frame(struct usb_gadget *gadget) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + int ret; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = (xudc_readl(xudc, MFINDEX) & MFINDEX_FRAME_MASK) >> + MFINDEX_FRAME_SHIFT; +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static void tegra_xudc_resume_device_state(struct tegra_xudc *xudc) +{ + unsigned int i; + u32 val; + + ep_unpause_all(xudc); + + /* Direct link to U0. */ + val = xudc_readl(xudc, PORTSC); + if (((val & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT) != PORTSC_PLS_U0) { + val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); + val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0); + xudc_writel(xudc, val, PORTSC); + } + + if (xudc->device_state == USB_STATE_SUSPENDED) { + xudc->device_state = xudc->resume_state; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + xudc->resume_state = 0; + } + + /* + * Doorbells may be dropped if they are sent too soon (< ~200ns) + * after unpausing the endpoint. Wait for 500ns just to be safe. + */ + ndelay(500); + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) + tegra_xudc_ep_ring_doorbell(&xudc->ep[i]); +} + +static int tegra_xudc_gadget_wakeup(struct usb_gadget *gadget) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + int ret = 0; + u32 val; + + spin_lock_irqsave(&xudc->lock, flags); + + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + val = xudc_readl(xudc, PORTPM); + dev_dbg(xudc->dev, "%s: PORTPM=%#x, speed=%x\n", __func__, + val, gadget->speed); + + if (((xudc->gadget.speed <= USB_SPEED_HIGH) && + (val & PORTPM_RWE)) || + ((xudc->gadget.speed == USB_SPEED_SUPER) && + (val & PORTPM_FRWE))) { + tegra_xudc_resume_device_state(xudc); + + /* Send Device Notification packet. */ + if (xudc->gadget.speed == USB_SPEED_SUPER) { + val = DEVNOTIF_LO_TYPE(DEVNOTIF_LO_TYPE_FUNCTION_WAKE) + | DEVNOTIF_LO_TRIG; + xudc_writel(xudc, 0, DEVNOTIF_HI); + xudc_writel(xudc, val, DEVNOTIF_LO); + } + } + +unlock: + dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret); + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static int tegra_xudc_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + u32 val; + + pm_runtime_get_sync(xudc->dev); + + spin_lock_irqsave(&xudc->lock, flags); + + if (is_on != xudc->pullup) { + val = xudc_readl(xudc, CTRL); + if (is_on) + val |= CTRL_ENABLE; + else + val &= ~CTRL_ENABLE; + xudc_writel(xudc, val, CTRL); + } + + xudc->pullup = is_on; + dev_dbg(xudc->dev, "%s: pullup:%d", __func__, is_on); + + spin_unlock_irqrestore(&xudc->lock, flags); + + pm_runtime_put(xudc->dev); + + return 0; +} + +static int tegra_xudc_gadget_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + u32 val; + int ret; + + if (!driver) + return -EINVAL; + + pm_runtime_get_sync(xudc->dev); + + spin_lock_irqsave(&xudc->lock, flags); + + if (xudc->driver) { + ret = -EBUSY; + goto unlock; + } + + xudc->setup_state = WAIT_FOR_SETUP; + xudc->device_state = USB_STATE_DEFAULT; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + ret = __tegra_xudc_ep_enable(&xudc->ep[0], &tegra_xudc_ep0_desc); + if (ret < 0) + goto unlock; + + val = xudc_readl(xudc, CTRL); + val |= CTRL_IE | CTRL_LSE; + xudc_writel(xudc, val, CTRL); + + val = xudc_readl(xudc, PORTHALT); + val |= PORTHALT_STCHG_INTR_EN; + xudc_writel(xudc, val, PORTHALT); + + if (xudc->pullup) { + val = xudc_readl(xudc, CTRL); + val |= CTRL_ENABLE; + xudc_writel(xudc, val, CTRL); + } + + xudc->driver = driver; +unlock: + dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret); + spin_unlock_irqrestore(&xudc->lock, flags); + + pm_runtime_put(xudc->dev); + + return ret; +} + +static int tegra_xudc_gadget_stop(struct usb_gadget *gadget) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + u32 val; + + pm_runtime_get_sync(xudc->dev); + + spin_lock_irqsave(&xudc->lock, flags); + + val = xudc_readl(xudc, CTRL); + val &= ~(CTRL_IE | CTRL_ENABLE); + xudc_writel(xudc, val, CTRL); + + __tegra_xudc_ep_disable(&xudc->ep[0]); + + xudc->driver = NULL; + dev_dbg(xudc->dev, "Gadget stopped"); + + spin_unlock_irqrestore(&xudc->lock, flags); + + pm_runtime_put(xudc->dev); + + return 0; +} + +static int tegra_xudc_set_selfpowered(struct usb_gadget *gadget, int is_on) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + + dev_dbg(xudc->dev, "%s: %d\n", __func__, is_on); + xudc->selfpowered = !!is_on; + + return 0; +} + +static struct usb_gadget_ops tegra_xudc_gadget_ops = { + .get_frame = tegra_xudc_gadget_get_frame, + .wakeup = tegra_xudc_gadget_wakeup, + .pullup = tegra_xudc_gadget_pullup, + .udc_start = tegra_xudc_gadget_start, + .udc_stop = tegra_xudc_gadget_stop, + .set_selfpowered = tegra_xudc_set_selfpowered, +}; + +static void no_op_complete(struct usb_ep *ep, struct usb_request *req) +{ +} + +static int +tegra_xudc_ep0_queue_status(struct tegra_xudc *xudc, + void (*cmpl)(struct usb_ep *, struct usb_request *)) +{ + xudc->ep0_req->usb_req.buf = NULL; + xudc->ep0_req->usb_req.dma = 0; + xudc->ep0_req->usb_req.length = 0; + xudc->ep0_req->usb_req.complete = cmpl; + xudc->ep0_req->usb_req.context = xudc; + + return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req); +} + +static int +tegra_xudc_ep0_queue_data(struct tegra_xudc *xudc, void *buf, size_t len, + void (*cmpl)(struct usb_ep *, struct usb_request *)) +{ + xudc->ep0_req->usb_req.buf = buf; + xudc->ep0_req->usb_req.length = len; + xudc->ep0_req->usb_req.complete = cmpl; + xudc->ep0_req->usb_req.context = xudc; + + return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req); +} + +static void tegra_xudc_ep0_req_done(struct tegra_xudc *xudc) +{ + switch (xudc->setup_state) { + case DATA_STAGE_XFER: + xudc->setup_state = STATUS_STAGE_RECV; + tegra_xudc_ep0_queue_status(xudc, no_op_complete); + break; + case DATA_STAGE_RECV: + xudc->setup_state = STATUS_STAGE_XFER; + tegra_xudc_ep0_queue_status(xudc, no_op_complete); + break; + default: + xudc->setup_state = WAIT_FOR_SETUP; + break; + } +} + +static int tegra_xudc_ep0_delegate_req(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + int ret; + + spin_unlock(&xudc->lock); + ret = xudc->driver->setup(&xudc->gadget, ctrl); + spin_lock(&xudc->lock); + + return ret; +} + +static void set_feature_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct tegra_xudc *xudc = req->context; + + if (xudc->test_mode_pattern) { + xudc_writel(xudc, xudc->test_mode_pattern, PORT_TM); + xudc->test_mode_pattern = 0; + } +} + +static int tegra_xudc_ep0_set_feature(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); + u32 feature = le16_to_cpu(ctrl->wValue); + u32 index = le16_to_cpu(ctrl->wIndex); + u32 val, ep; + int ret; + + if (le16_to_cpu(ctrl->wLength) != 0) + return -EINVAL; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (feature) { + case USB_DEVICE_REMOTE_WAKEUP: + if ((xudc->gadget.speed == USB_SPEED_SUPER) || + (xudc->device_state == USB_STATE_DEFAULT)) + return -EINVAL; + + val = xudc_readl(xudc, PORTPM); + if (set) + val |= PORTPM_RWE; + else + val &= ~PORTPM_RWE; + + xudc_writel(xudc, val, PORTPM); + break; + case USB_DEVICE_U1_ENABLE: + case USB_DEVICE_U2_ENABLE: + if ((xudc->device_state != USB_STATE_CONFIGURED) || + (xudc->gadget.speed != USB_SPEED_SUPER)) + return -EINVAL; + + val = xudc_readl(xudc, PORTPM); + if ((feature == USB_DEVICE_U1_ENABLE) && + xudc->soc->u1_enable) { + if (set) + val |= PORTPM_U1E; + else + val &= ~PORTPM_U1E; + } + + if ((feature == USB_DEVICE_U2_ENABLE) && + xudc->soc->u2_enable) { + if (set) + val |= PORTPM_U2E; + else + val &= ~PORTPM_U2E; + } + + xudc_writel(xudc, val, PORTPM); + break; + case USB_DEVICE_TEST_MODE: + if (xudc->gadget.speed != USB_SPEED_HIGH) + return -EINVAL; + + if (!set) + return -EINVAL; + + xudc->test_mode_pattern = index >> 8; + break; + default: + return -EINVAL; + } + + break; + case USB_RECIP_INTERFACE: + if (xudc->device_state != USB_STATE_CONFIGURED) + return -EINVAL; + + switch (feature) { + case USB_INTRF_FUNC_SUSPEND: + if (set) { + val = xudc_readl(xudc, PORTPM); + + if (index & USB_INTRF_FUNC_SUSPEND_RW) + val |= PORTPM_FRWE; + else + val &= ~PORTPM_FRWE; + + xudc_writel(xudc, val, PORTPM); + } + + return tegra_xudc_ep0_delegate_req(xudc, ctrl); + default: + return -EINVAL; + } + + break; + case USB_RECIP_ENDPOINT: + ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 + + ((index & USB_DIR_IN) ? 1 : 0); + + if ((xudc->device_state == USB_STATE_DEFAULT) || + ((xudc->device_state == USB_STATE_ADDRESS) && + (index != 0))) + return -EINVAL; + + ret = __tegra_xudc_ep_set_halt(&xudc->ep[ep], set); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + return tegra_xudc_ep0_queue_status(xudc, set_feature_complete); +} + +static int tegra_xudc_ep0_get_status(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + struct tegra_xudc_ep_context *ep_ctx; + u32 val, ep, index = le16_to_cpu(ctrl->wIndex); + u16 status = 0; + + if (!(ctrl->bRequestType & USB_DIR_IN)) + return -EINVAL; + + if ((le16_to_cpu(ctrl->wValue) != 0) || + (le16_to_cpu(ctrl->wLength) != 2)) + return -EINVAL; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + val = xudc_readl(xudc, PORTPM); + + if (xudc->selfpowered) + status |= BIT(USB_DEVICE_SELF_POWERED); + + if ((xudc->gadget.speed < USB_SPEED_SUPER) && + (val & PORTPM_RWE)) + status |= BIT(USB_DEVICE_REMOTE_WAKEUP); + + if (xudc->gadget.speed == USB_SPEED_SUPER) { + if (val & PORTPM_U1E) + status |= BIT(USB_DEV_STAT_U1_ENABLED); + if (val & PORTPM_U2E) + status |= BIT(USB_DEV_STAT_U2_ENABLED); + } + break; + case USB_RECIP_INTERFACE: + if (xudc->gadget.speed == USB_SPEED_SUPER) { + status |= USB_INTRF_STAT_FUNC_RW_CAP; + val = xudc_readl(xudc, PORTPM); + if (val & PORTPM_FRWE) + status |= USB_INTRF_STAT_FUNC_RW; + } + break; + case USB_RECIP_ENDPOINT: + ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 + + ((index & USB_DIR_IN) ? 1 : 0); + ep_ctx = &xudc->ep_context[ep]; + + if ((xudc->device_state != USB_STATE_CONFIGURED) && + ((xudc->device_state != USB_STATE_ADDRESS) || (ep != 0))) + return -EINVAL; + + if (ep_ctx_read_state(ep_ctx) == EP_STATE_DISABLED) + return -EINVAL; + + if (xudc_readl(xudc, EP_HALT) & BIT(ep)) + status |= BIT(USB_ENDPOINT_HALT); + break; + default: + return -EINVAL; + } + + xudc->status_buf = cpu_to_le16(status); + return tegra_xudc_ep0_queue_data(xudc, &xudc->status_buf, + sizeof(xudc->status_buf), + no_op_complete); +} + +static void set_sel_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* Nothing to do with SEL values */ +} + +static int tegra_xudc_ep0_set_sel(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE | + USB_TYPE_STANDARD)) + return -EINVAL; + + if (xudc->device_state == USB_STATE_DEFAULT) + return -EINVAL; + + if ((le16_to_cpu(ctrl->wIndex) != 0) || + (le16_to_cpu(ctrl->wValue) != 0) || + (le16_to_cpu(ctrl->wLength) != 6)) + return -EINVAL; + + return tegra_xudc_ep0_queue_data(xudc, &xudc->sel_timing, + sizeof(xudc->sel_timing), + set_sel_complete); +} + +static void set_isoch_delay_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* Nothing to do with isoch delay */ +} + +static int tegra_xudc_ep0_set_isoch_delay(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + u32 delay = le16_to_cpu(ctrl->wValue); + + if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE | + USB_TYPE_STANDARD)) + return -EINVAL; + + if ((delay > 65535) || (le16_to_cpu(ctrl->wIndex) != 0) || + (le16_to_cpu(ctrl->wLength) != 0)) + return -EINVAL; + + xudc->isoch_delay = delay; + + return tegra_xudc_ep0_queue_status(xudc, set_isoch_delay_complete); +} + +static void set_address_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct tegra_xudc *xudc = req->context; + + if ((xudc->device_state == USB_STATE_DEFAULT) && + (xudc->dev_addr != 0)) { + xudc->device_state = USB_STATE_ADDRESS; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + } else if ((xudc->device_state == USB_STATE_ADDRESS) && + (xudc->dev_addr == 0)) { + xudc->device_state = USB_STATE_DEFAULT; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + } +} + +static int tegra_xudc_ep0_set_address(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + struct tegra_xudc_ep *ep0 = &xudc->ep[0]; + u32 val, addr = le16_to_cpu(ctrl->wValue); + + if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE | + USB_TYPE_STANDARD)) + return -EINVAL; + + if ((addr > 127) || (le16_to_cpu(ctrl->wIndex) != 0) || + (le16_to_cpu(ctrl->wLength) != 0)) + return -EINVAL; + + if (xudc->device_state == USB_STATE_CONFIGURED) + return -EINVAL; + + dev_dbg(xudc->dev, "set address: %u\n", addr); + + xudc->dev_addr = addr; + val = xudc_readl(xudc, CTRL); + val &= ~(CTRL_DEVADDR_MASK); + val |= CTRL_DEVADDR(addr); + xudc_writel(xudc, val, CTRL); + + ep_ctx_write_devaddr(ep0->context, addr); + + return tegra_xudc_ep0_queue_status(xudc, set_address_complete); +} + +static int tegra_xudc_ep0_standard_req(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + int ret; + + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + dev_dbg(xudc->dev, "USB_REQ_GET_STATUS\n"); + ret = tegra_xudc_ep0_get_status(xudc, ctrl); + break; + case USB_REQ_SET_ADDRESS: + dev_dbg(xudc->dev, "USB_REQ_SET_ADDRESS\n"); + ret = tegra_xudc_ep0_set_address(xudc, ctrl); + break; + case USB_REQ_SET_SEL: + dev_dbg(xudc->dev, "USB_REQ_SET_SEL\n"); + ret = tegra_xudc_ep0_set_sel(xudc, ctrl); + break; + case USB_REQ_SET_ISOCH_DELAY: + dev_dbg(xudc->dev, "USB_REQ_SET_ISOCH_DELAY\n"); + ret = tegra_xudc_ep0_set_isoch_delay(xudc, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + dev_dbg(xudc->dev, "USB_REQ_CLEAR/SET_FEATURE\n"); + ret = tegra_xudc_ep0_set_feature(xudc, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + dev_dbg(xudc->dev, "USB_REQ_SET_CONFIGURATION\n"); + /* + * In theory we need to clear RUN bit before status stage of + * deconfig request sent, but this seems to be causing problems. + * Clear RUN once all endpoints are disabled instead. + */ + fallthrough; + default: + ret = tegra_xudc_ep0_delegate_req(xudc, ctrl); + break; + } + + return ret; +} + +static void tegra_xudc_handle_ep0_setup_packet(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl, + u16 seq_num) +{ + int ret; + + xudc->setup_seq_num = seq_num; + + /* Ensure EP0 is unhalted. */ + ep_unhalt(xudc, 0); + + /* + * On Tegra210, setup packets with sequence numbers 0xfffe or 0xffff + * are invalid. Halt EP0 until we get a valid packet. + */ + if (xudc->soc->invalid_seq_num && + (seq_num == 0xfffe || seq_num == 0xffff)) { + dev_warn(xudc->dev, "invalid sequence number detected\n"); + ep_halt(xudc, 0); + return; + } + + if (ctrl->wLength) + xudc->setup_state = (ctrl->bRequestType & USB_DIR_IN) ? + DATA_STAGE_XFER : DATA_STAGE_RECV; + else + xudc->setup_state = STATUS_STAGE_XFER; + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + ret = tegra_xudc_ep0_standard_req(xudc, ctrl); + else + ret = tegra_xudc_ep0_delegate_req(xudc, ctrl); + + if (ret < 0) { + dev_warn(xudc->dev, "setup request failed: %d\n", ret); + xudc->setup_state = WAIT_FOR_SETUP; + ep_halt(xudc, 0); + } +} + +static void tegra_xudc_handle_ep0_event(struct tegra_xudc *xudc, + struct tegra_xudc_trb *event) +{ + struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)event; + u16 seq_num = trb_read_seq_num(event); + + if (xudc->setup_state != WAIT_FOR_SETUP) { + /* + * The controller is in the process of handling another + * setup request. Queue subsequent requests and handle + * the last one once the controller reports a sequence + * number error. + */ + memcpy(&xudc->setup_packet.ctrl_req, ctrl, sizeof(*ctrl)); + xudc->setup_packet.seq_num = seq_num; + xudc->queued_setup_packet = true; + } else { + tegra_xudc_handle_ep0_setup_packet(xudc, ctrl, seq_num); + } +} + +static struct tegra_xudc_request * +trb_to_request(struct tegra_xudc_ep *ep, struct tegra_xudc_trb *trb) +{ + struct tegra_xudc_request *req; + + list_for_each_entry(req, &ep->queue, list) { + if (!req->trbs_queued) + break; + + if (trb_in_request(ep, req, trb)) + return req; + } + + return NULL; +} + +static void tegra_xudc_handle_transfer_completion(struct tegra_xudc *xudc, + struct tegra_xudc_ep *ep, + struct tegra_xudc_trb *event) +{ + struct tegra_xudc_request *req; + struct tegra_xudc_trb *trb; + bool short_packet; + + short_packet = (trb_read_cmpl_code(event) == + TRB_CMPL_CODE_SHORT_PACKET); + + trb = trb_phys_to_virt(ep, trb_read_data_ptr(event)); + req = trb_to_request(ep, trb); + + /* + * TDs are complete on short packet or when the completed TRB is the + * last TRB in the TD (the CHAIN bit is unset). + */ + if (req && (short_packet || (!trb_read_chain(trb) && + (req->trbs_needed == req->trbs_queued)))) { + struct tegra_xudc_trb *last = req->last_trb; + unsigned int residual; + + residual = trb_read_transfer_len(event); + req->usb_req.actual = req->usb_req.length - residual; + + dev_dbg(xudc->dev, "bytes transferred %u / %u\n", + req->usb_req.actual, req->usb_req.length); + + tegra_xudc_req_done(ep, req, 0); + + if (ep->desc && usb_endpoint_xfer_control(ep->desc)) + tegra_xudc_ep0_req_done(xudc); + + /* + * Advance the dequeue pointer past the end of the current TD + * on short packet completion. + */ + if (short_packet) { + ep->deq_ptr = (last - ep->transfer_ring) + 1; + if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1) + ep->deq_ptr = 0; + } + } else if (!req) { + dev_warn(xudc->dev, "transfer event on dequeued request\n"); + } + + if (ep->desc) + tegra_xudc_ep_kick_queue(ep); +} + +static void tegra_xudc_handle_transfer_event(struct tegra_xudc *xudc, + struct tegra_xudc_trb *event) +{ + unsigned int ep_index = trb_read_endpoint_id(event); + struct tegra_xudc_ep *ep = &xudc->ep[ep_index]; + struct tegra_xudc_trb *trb; + u16 comp_code; + + if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) { + dev_warn(xudc->dev, "transfer event on disabled EP %u\n", + ep_index); + return; + } + + /* Update transfer ring dequeue pointer. */ + trb = trb_phys_to_virt(ep, trb_read_data_ptr(event)); + comp_code = trb_read_cmpl_code(event); + if (comp_code != TRB_CMPL_CODE_BABBLE_DETECTED_ERR) { + ep->deq_ptr = (trb - ep->transfer_ring) + 1; + + if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1) + ep->deq_ptr = 0; + ep->ring_full = false; + } + + switch (comp_code) { + case TRB_CMPL_CODE_SUCCESS: + case TRB_CMPL_CODE_SHORT_PACKET: + tegra_xudc_handle_transfer_completion(xudc, ep, event); + break; + case TRB_CMPL_CODE_HOST_REJECTED: + dev_info(xudc->dev, "stream rejected on EP %u\n", ep_index); + + ep->stream_rejected = true; + break; + case TRB_CMPL_CODE_PRIME_PIPE_RECEIVED: + dev_info(xudc->dev, "prime pipe received on EP %u\n", ep_index); + + if (ep->stream_rejected) { + ep->stream_rejected = false; + /* + * An EP is stopped when a stream is rejected. Wait + * for the EP to report that it is stopped and then + * un-stop it. + */ + ep_wait_for_stopped(xudc, ep_index); + } + tegra_xudc_ep_ring_doorbell(ep); + break; + case TRB_CMPL_CODE_BABBLE_DETECTED_ERR: + /* + * Wait for the EP to be stopped so the controller stops + * processing doorbells. + */ + ep_wait_for_stopped(xudc, ep_index); + ep->enq_ptr = ep->deq_ptr; + tegra_xudc_ep_nuke(ep, -EIO); + /* FALLTHROUGH */ + case TRB_CMPL_CODE_STREAM_NUMP_ERROR: + case TRB_CMPL_CODE_CTRL_DIR_ERR: + case TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR: + case TRB_CMPL_CODE_RING_UNDERRUN: + case TRB_CMPL_CODE_RING_OVERRUN: + case TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN: + case TRB_CMPL_CODE_USB_TRANS_ERR: + case TRB_CMPL_CODE_TRB_ERR: + dev_err(xudc->dev, "completion error %#x on EP %u\n", + comp_code, ep_index); + + ep_halt(xudc, ep_index); + break; + case TRB_CMPL_CODE_CTRL_SEQNUM_ERR: + dev_info(xudc->dev, "sequence number error\n"); + + /* + * Kill any queued control request and skip to the last + * setup packet we received. + */ + tegra_xudc_ep_nuke(ep, -EINVAL); + xudc->setup_state = WAIT_FOR_SETUP; + if (!xudc->queued_setup_packet) + break; + + tegra_xudc_handle_ep0_setup_packet(xudc, + &xudc->setup_packet.ctrl_req, + xudc->setup_packet.seq_num); + xudc->queued_setup_packet = false; + break; + case TRB_CMPL_CODE_STOPPED: + dev_dbg(xudc->dev, "stop completion code on EP %u\n", + ep_index); + + /* Disconnected. */ + tegra_xudc_ep_nuke(ep, -ECONNREFUSED); + break; + default: + dev_dbg(xudc->dev, "completion event %#x on EP %u\n", + comp_code, ep_index); + break; + } +} + +static void tegra_xudc_reset(struct tegra_xudc *xudc) +{ + struct tegra_xudc_ep *ep0 = &xudc->ep[0]; + dma_addr_t deq_ptr; + unsigned int i; + + xudc->setup_state = WAIT_FOR_SETUP; + xudc->device_state = USB_STATE_DEFAULT; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + ep_unpause_all(xudc); + + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) + tegra_xudc_ep_nuke(&xudc->ep[i], -ESHUTDOWN); + + /* + * Reset sequence number and dequeue pointer to flush the transfer + * ring. + */ + ep0->deq_ptr = ep0->enq_ptr; + ep0->ring_full = false; + + xudc->setup_seq_num = 0; + xudc->queued_setup_packet = false; + + ep_ctx_write_seq_num(ep0->context, xudc->setup_seq_num); + + deq_ptr = trb_virt_to_phys(ep0, &ep0->transfer_ring[ep0->deq_ptr]); + + if (!dma_mapping_error(xudc->dev, deq_ptr)) { + ep_ctx_write_deq_ptr(ep0->context, deq_ptr); + ep_ctx_write_dcs(ep0->context, ep0->pcs); + } + + ep_unhalt_all(xudc); + ep_reload(xudc, 0); + ep_unpause(xudc, 0); +} + +static void tegra_xudc_port_connect(struct tegra_xudc *xudc) +{ + struct tegra_xudc_ep *ep0 = &xudc->ep[0]; + u16 maxpacket; + u32 val; + + val = (xudc_readl(xudc, PORTSC) & PORTSC_PS_MASK) >> PORTSC_PS_SHIFT; + switch (val) { + case PORTSC_PS_LS: + xudc->gadget.speed = USB_SPEED_LOW; + break; + case PORTSC_PS_FS: + xudc->gadget.speed = USB_SPEED_FULL; + break; + case PORTSC_PS_HS: + xudc->gadget.speed = USB_SPEED_HIGH; + break; + case PORTSC_PS_SS: + xudc->gadget.speed = USB_SPEED_SUPER; + break; + default: + xudc->gadget.speed = USB_SPEED_UNKNOWN; + break; + } + + xudc->device_state = USB_STATE_DEFAULT; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + xudc->setup_state = WAIT_FOR_SETUP; + + if (xudc->gadget.speed == USB_SPEED_SUPER) + maxpacket = 512; + else + maxpacket = 64; + + ep_ctx_write_max_packet_size(ep0->context, maxpacket); + tegra_xudc_ep0_desc.wMaxPacketSize = cpu_to_le16(maxpacket); + usb_ep_set_maxpacket_limit(&ep0->usb_ep, maxpacket); + + if (!xudc->soc->u1_enable) { + val = xudc_readl(xudc, PORTPM); + val &= ~(PORTPM_U1TIMEOUT_MASK); + xudc_writel(xudc, val, PORTPM); + } + + if (!xudc->soc->u2_enable) { + val = xudc_readl(xudc, PORTPM); + val &= ~(PORTPM_U2TIMEOUT_MASK); + xudc_writel(xudc, val, PORTPM); + } + + if (xudc->gadget.speed <= USB_SPEED_HIGH) { + val = xudc_readl(xudc, PORTPM); + val &= ~(PORTPM_L1S_MASK); + if (xudc->soc->lpm_enable) + val |= PORTPM_L1S(PORTPM_L1S_ACCEPT); + else + val |= PORTPM_L1S(PORTPM_L1S_NYET); + xudc_writel(xudc, val, PORTPM); + } + + val = xudc_readl(xudc, ST); + if (val & ST_RC) + xudc_writel(xudc, ST_RC, ST); +} + +static void tegra_xudc_port_disconnect(struct tegra_xudc *xudc) +{ + tegra_xudc_reset(xudc); + + if (xudc->driver && xudc->driver->disconnect) { + spin_unlock(&xudc->lock); + xudc->driver->disconnect(&xudc->gadget); + spin_lock(&xudc->lock); + } + + xudc->device_state = USB_STATE_NOTATTACHED; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + complete(&xudc->disconnect_complete); +} + +static void tegra_xudc_port_reset(struct tegra_xudc *xudc) +{ + tegra_xudc_reset(xudc); + + if (xudc->driver) { + spin_unlock(&xudc->lock); + usb_gadget_udc_reset(&xudc->gadget, xudc->driver); + spin_lock(&xudc->lock); + } + + tegra_xudc_port_connect(xudc); +} + +static void tegra_xudc_port_suspend(struct tegra_xudc *xudc) +{ + dev_dbg(xudc->dev, "port suspend\n"); + + xudc->resume_state = xudc->device_state; + xudc->device_state = USB_STATE_SUSPENDED; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + if (xudc->driver->suspend) { + spin_unlock(&xudc->lock); + xudc->driver->suspend(&xudc->gadget); + spin_lock(&xudc->lock); + } +} + +static void tegra_xudc_port_resume(struct tegra_xudc *xudc) +{ + dev_dbg(xudc->dev, "port resume\n"); + + tegra_xudc_resume_device_state(xudc); + + if (xudc->driver->resume) { + spin_unlock(&xudc->lock); + xudc->driver->resume(&xudc->gadget); + spin_lock(&xudc->lock); + } +} + +static inline void clear_port_change(struct tegra_xudc *xudc, u32 flag) +{ + u32 val; + + val = xudc_readl(xudc, PORTSC); + val &= ~PORTSC_CHANGE_MASK; + val |= flag; + xudc_writel(xudc, val, PORTSC); +} + +static void __tegra_xudc_handle_port_status(struct tegra_xudc *xudc) +{ + u32 portsc, porthalt; + + porthalt = xudc_readl(xudc, PORTHALT); + if ((porthalt & PORTHALT_STCHG_REQ) && + (porthalt & PORTHALT_HALT_LTSSM)) { + dev_dbg(xudc->dev, "STCHG_REQ, PORTHALT = %#x\n", porthalt); + porthalt &= ~PORTHALT_HALT_LTSSM; + xudc_writel(xudc, porthalt, PORTHALT); + } + + portsc = xudc_readl(xudc, PORTSC); + if ((portsc & PORTSC_PRC) && (portsc & PORTSC_PR)) { + dev_dbg(xudc->dev, "PRC, PR, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_PRC | PORTSC_PED); +#define TOGGLE_VBUS_WAIT_MS 100 + if (xudc->soc->port_reset_quirk) { + schedule_delayed_work(&xudc->port_reset_war_work, + msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS)); + xudc->wait_for_sec_prc = 1; + } + } + + if ((portsc & PORTSC_PRC) && !(portsc & PORTSC_PR)) { + dev_dbg(xudc->dev, "PRC, Not PR, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_PRC | PORTSC_PED); + tegra_xudc_port_reset(xudc); + cancel_delayed_work(&xudc->port_reset_war_work); + xudc->wait_for_sec_prc = 0; + } + + portsc = xudc_readl(xudc, PORTSC); + if (portsc & PORTSC_WRC) { + dev_dbg(xudc->dev, "WRC, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_WRC | PORTSC_PED); + if (!(xudc_readl(xudc, PORTSC) & PORTSC_WPR)) + tegra_xudc_port_reset(xudc); + } + + portsc = xudc_readl(xudc, PORTSC); + if (portsc & PORTSC_CSC) { + dev_dbg(xudc->dev, "CSC, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_CSC); + + if (portsc & PORTSC_CCS) + tegra_xudc_port_connect(xudc); + else + tegra_xudc_port_disconnect(xudc); + + if (xudc->wait_csc) { + cancel_delayed_work(&xudc->plc_reset_work); + xudc->wait_csc = false; + } + } + + portsc = xudc_readl(xudc, PORTSC); + if (portsc & PORTSC_PLC) { + u32 pls = (portsc & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT; + + dev_dbg(xudc->dev, "PLC, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_PLC); + switch (pls) { + case PORTSC_PLS_U3: + tegra_xudc_port_suspend(xudc); + break; + case PORTSC_PLS_U0: + if (xudc->gadget.speed < USB_SPEED_SUPER) + tegra_xudc_port_resume(xudc); + break; + case PORTSC_PLS_RESUME: + if (xudc->gadget.speed == USB_SPEED_SUPER) + tegra_xudc_port_resume(xudc); + break; + case PORTSC_PLS_INACTIVE: + schedule_delayed_work(&xudc->plc_reset_work, + msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS)); + xudc->wait_csc = true; + break; + default: + break; + } + } + + if (portsc & PORTSC_CEC) { + dev_warn(xudc->dev, "CEC, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_CEC); + } + + dev_dbg(xudc->dev, "PORTSC = %#x\n", xudc_readl(xudc, PORTSC)); +} + +static void tegra_xudc_handle_port_status(struct tegra_xudc *xudc) +{ + while ((xudc_readl(xudc, PORTSC) & PORTSC_CHANGE_MASK) || + (xudc_readl(xudc, PORTHALT) & PORTHALT_STCHG_REQ)) + __tegra_xudc_handle_port_status(xudc); +} + +static void tegra_xudc_handle_event(struct tegra_xudc *xudc, + struct tegra_xudc_trb *event) +{ + u32 type = trb_read_type(event); + + dump_trb(xudc, "EVENT", event); + + switch (type) { + case TRB_TYPE_PORT_STATUS_CHANGE_EVENT: + tegra_xudc_handle_port_status(xudc); + break; + case TRB_TYPE_TRANSFER_EVENT: + tegra_xudc_handle_transfer_event(xudc, event); + break; + case TRB_TYPE_SETUP_PACKET_EVENT: + tegra_xudc_handle_ep0_event(xudc, event); + break; + default: + dev_info(xudc->dev, "Unrecognized TRB type = %#x\n", type); + break; + } +} + +static void tegra_xudc_process_event_ring(struct tegra_xudc *xudc) +{ + struct tegra_xudc_trb *event; + dma_addr_t erdp; + + while (true) { + event = xudc->event_ring[xudc->event_ring_index] + + xudc->event_ring_deq_ptr; + + if (trb_read_cycle(event) != xudc->ccs) + break; + + tegra_xudc_handle_event(xudc, event); + + xudc->event_ring_deq_ptr++; + if (xudc->event_ring_deq_ptr == XUDC_EVENT_RING_SIZE) { + xudc->event_ring_deq_ptr = 0; + xudc->event_ring_index++; + } + + if (xudc->event_ring_index == XUDC_NR_EVENT_RINGS) { + xudc->event_ring_index = 0; + xudc->ccs = !xudc->ccs; + } + } + + erdp = xudc->event_ring_phys[xudc->event_ring_index] + + xudc->event_ring_deq_ptr * sizeof(*event); + + xudc_writel(xudc, upper_32_bits(erdp), ERDPHI); + xudc_writel(xudc, lower_32_bits(erdp) | ERDPLO_EHB, ERDPLO); +} + +static irqreturn_t tegra_xudc_irq(int irq, void *data) +{ + struct tegra_xudc *xudc = data; + unsigned long flags; + u32 val; + + val = xudc_readl(xudc, ST); + if (!(val & ST_IP)) + return IRQ_NONE; + xudc_writel(xudc, ST_IP, ST); + + spin_lock_irqsave(&xudc->lock, flags); + tegra_xudc_process_event_ring(xudc); + spin_unlock_irqrestore(&xudc->lock, flags); + + return IRQ_HANDLED; +} + +static int tegra_xudc_alloc_ep(struct tegra_xudc *xudc, unsigned int index) +{ + struct tegra_xudc_ep *ep = &xudc->ep[index]; + + ep->xudc = xudc; + ep->index = index; + ep->context = &xudc->ep_context[index]; + INIT_LIST_HEAD(&ep->queue); + + /* + * EP1 would be the input endpoint corresponding to EP0, but since + * EP0 is bi-directional, EP1 is unused. + */ + if (index == 1) + return 0; + + ep->transfer_ring = dma_pool_alloc(xudc->transfer_ring_pool, + GFP_KERNEL, + &ep->transfer_ring_phys); + if (!ep->transfer_ring) + return -ENOMEM; + + if (index) { + snprintf(ep->name, sizeof(ep->name), "ep%u%s", index / 2, + (index % 2 == 0) ? "out" : "in"); + ep->usb_ep.name = ep->name; + usb_ep_set_maxpacket_limit(&ep->usb_ep, 1024); + ep->usb_ep.max_streams = 16; + ep->usb_ep.ops = &tegra_xudc_ep_ops; + ep->usb_ep.caps.type_bulk = true; + ep->usb_ep.caps.type_int = true; + if (index & 1) + ep->usb_ep.caps.dir_in = true; + else + ep->usb_ep.caps.dir_out = true; + list_add_tail(&ep->usb_ep.ep_list, &xudc->gadget.ep_list); + } else { + strscpy(ep->name, "ep0", 3); + ep->usb_ep.name = ep->name; + usb_ep_set_maxpacket_limit(&ep->usb_ep, 512); + ep->usb_ep.ops = &tegra_xudc_ep0_ops; + ep->usb_ep.caps.type_control = true; + ep->usb_ep.caps.dir_in = true; + ep->usb_ep.caps.dir_out = true; + } + + return 0; +} + +static void tegra_xudc_free_ep(struct tegra_xudc *xudc, unsigned int index) +{ + struct tegra_xudc_ep *ep = &xudc->ep[index]; + + /* + * EP1 would be the input endpoint corresponding to EP0, but since + * EP0 is bi-directional, EP1 is unused. + */ + if (index == 1) + return; + + dma_pool_free(xudc->transfer_ring_pool, ep->transfer_ring, + ep->transfer_ring_phys); +} + +static int tegra_xudc_alloc_eps(struct tegra_xudc *xudc) +{ + struct usb_request *req; + unsigned int i; + int err; + + xudc->ep_context = + dma_alloc_coherent(xudc->dev, XUDC_NR_EPS * + sizeof(*xudc->ep_context), + &xudc->ep_context_phys, GFP_KERNEL); + if (!xudc->ep_context) + return -ENOMEM; + + xudc->transfer_ring_pool = + dmam_pool_create(dev_name(xudc->dev), xudc->dev, + XUDC_TRANSFER_RING_SIZE * + sizeof(struct tegra_xudc_trb), + sizeof(struct tegra_xudc_trb), 0); + if (!xudc->transfer_ring_pool) { + err = -ENOMEM; + goto free_ep_context; + } + + INIT_LIST_HEAD(&xudc->gadget.ep_list); + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) { + err = tegra_xudc_alloc_ep(xudc, i); + if (err < 0) + goto free_eps; + } + + req = tegra_xudc_ep_alloc_request(&xudc->ep[0].usb_ep, GFP_KERNEL); + if (!req) { + err = -ENOMEM; + goto free_eps; + } + xudc->ep0_req = to_xudc_req(req); + + return 0; + +free_eps: + for (; i > 0; i--) + tegra_xudc_free_ep(xudc, i - 1); +free_ep_context: + dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context), + xudc->ep_context, xudc->ep_context_phys); + return err; +} + +static void tegra_xudc_init_eps(struct tegra_xudc *xudc) +{ + xudc_writel(xudc, lower_32_bits(xudc->ep_context_phys), ECPLO); + xudc_writel(xudc, upper_32_bits(xudc->ep_context_phys), ECPHI); +} + +static void tegra_xudc_free_eps(struct tegra_xudc *xudc) +{ + unsigned int i; + + tegra_xudc_ep_free_request(&xudc->ep[0].usb_ep, + &xudc->ep0_req->usb_req); + + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) + tegra_xudc_free_ep(xudc, i); + + dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context), + xudc->ep_context, xudc->ep_context_phys); +} + +static int tegra_xudc_alloc_event_ring(struct tegra_xudc *xudc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) { + xudc->event_ring[i] = + dma_alloc_coherent(xudc->dev, XUDC_EVENT_RING_SIZE * + sizeof(*xudc->event_ring[i]), + &xudc->event_ring_phys[i], + GFP_KERNEL); + if (!xudc->event_ring[i]) + goto free_dma; + } + + return 0; + +free_dma: + for (; i > 0; i--) { + dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE * + sizeof(*xudc->event_ring[i - 1]), + xudc->event_ring[i - 1], + xudc->event_ring_phys[i - 1]); + } + return -ENOMEM; +} + +static void tegra_xudc_init_event_ring(struct tegra_xudc *xudc) +{ + unsigned int i; + u32 val; + + val = xudc_readl(xudc, SPARAM); + val &= ~(SPARAM_ERSTMAX_MASK); + val |= SPARAM_ERSTMAX(XUDC_NR_EVENT_RINGS); + xudc_writel(xudc, val, SPARAM); + + for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) { + memset(xudc->event_ring[i], 0, XUDC_EVENT_RING_SIZE * + sizeof(*xudc->event_ring[i])); + + val = xudc_readl(xudc, ERSTSZ); + val &= ~(ERSTSZ_ERSTXSZ_MASK << ERSTSZ_ERSTXSZ_SHIFT(i)); + val |= XUDC_EVENT_RING_SIZE << ERSTSZ_ERSTXSZ_SHIFT(i); + xudc_writel(xudc, val, ERSTSZ); + + xudc_writel(xudc, lower_32_bits(xudc->event_ring_phys[i]), + ERSTXBALO(i)); + xudc_writel(xudc, upper_32_bits(xudc->event_ring_phys[i]), + ERSTXBAHI(i)); + } + + val = lower_32_bits(xudc->event_ring_phys[0]); + xudc_writel(xudc, val, ERDPLO); + val |= EREPLO_ECS; + xudc_writel(xudc, val, EREPLO); + + val = upper_32_bits(xudc->event_ring_phys[0]); + xudc_writel(xudc, val, ERDPHI); + xudc_writel(xudc, val, EREPHI); + + xudc->ccs = true; + xudc->event_ring_index = 0; + xudc->event_ring_deq_ptr = 0; +} + +static void tegra_xudc_free_event_ring(struct tegra_xudc *xudc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) { + dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE * + sizeof(*xudc->event_ring[i]), + xudc->event_ring[i], + xudc->event_ring_phys[i]); + } +} + +static void tegra_xudc_fpci_ipfs_init(struct tegra_xudc *xudc) +{ + u32 val; + + if (xudc->soc->has_ipfs) { + val = ipfs_readl(xudc, XUSB_DEV_CONFIGURATION_0); + val |= XUSB_DEV_CONFIGURATION_0_EN_FPCI; + ipfs_writel(xudc, val, XUSB_DEV_CONFIGURATION_0); + usleep_range(10, 15); + } + + /* Enable bus master */ + val = XUSB_DEV_CFG_1_IO_SPACE_EN | XUSB_DEV_CFG_1_MEMORY_SPACE_EN | + XUSB_DEV_CFG_1_BUS_MASTER_EN; + fpci_writel(xudc, val, XUSB_DEV_CFG_1); + + /* Program BAR0 space */ + val = fpci_readl(xudc, XUSB_DEV_CFG_4); + val &= ~(XUSB_DEV_CFG_4_BASE_ADDR_MASK); + val |= xudc->phys_base & (XUSB_DEV_CFG_4_BASE_ADDR_MASK); + + fpci_writel(xudc, val, XUSB_DEV_CFG_4); + fpci_writel(xudc, upper_32_bits(xudc->phys_base), XUSB_DEV_CFG_5); + + usleep_range(100, 200); + + if (xudc->soc->has_ipfs) { + /* Enable interrupt assertion */ + val = ipfs_readl(xudc, XUSB_DEV_INTR_MASK_0); + val |= XUSB_DEV_INTR_MASK_0_IP_INT_MASK; + ipfs_writel(xudc, val, XUSB_DEV_INTR_MASK_0); + } +} + +static void tegra_xudc_device_params_init(struct tegra_xudc *xudc) +{ + u32 val, imod; + + if (xudc->soc->has_ipfs) { + val = xudc_readl(xudc, BLCG); + val |= BLCG_ALL; + val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE | + BLCG_COREPLL_PWRDN); + val |= BLCG_IOPLL_0_PWRDN; + val |= BLCG_IOPLL_1_PWRDN; + val |= BLCG_IOPLL_2_PWRDN; + + xudc_writel(xudc, val, BLCG); + } + + /* Set a reasonable U3 exit timer value. */ + val = xudc_readl(xudc, SSPX_CORE_PADCTL4); + val &= ~(SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK); + val |= SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(0x5dc0); + xudc_writel(xudc, val, SSPX_CORE_PADCTL4); + + /* Default ping LFPS tBurst is too large. */ + val = xudc_readl(xudc, SSPX_CORE_CNT0); + val &= ~(SSPX_CORE_CNT0_PING_TBURST_MASK); + val |= SSPX_CORE_CNT0_PING_TBURST(0xa); + xudc_writel(xudc, val, SSPX_CORE_CNT0); + + /* Default tPortConfiguration timeout is too small. */ + val = xudc_readl(xudc, SSPX_CORE_CNT30); + val &= ~(SSPX_CORE_CNT30_LMPITP_TIMER_MASK); + val |= SSPX_CORE_CNT30_LMPITP_TIMER(0x978); + xudc_writel(xudc, val, SSPX_CORE_CNT30); + + if (xudc->soc->lpm_enable) { + /* Set L1 resume duration to 95 us. */ + val = xudc_readl(xudc, HSFSPI_COUNT13); + val &= ~(HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK); + val |= HSFSPI_COUNT13_U2_RESUME_K_DURATION(0x2c88); + xudc_writel(xudc, val, HSFSPI_COUNT13); + } + + /* + * Compliacne suite appears to be violating polling LFPS tBurst max + * of 1.4us. Send 1.45us instead. + */ + val = xudc_readl(xudc, SSPX_CORE_CNT32); + val &= ~(SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK); + val |= SSPX_CORE_CNT32_POLL_TBURST_MAX(0xb0); + xudc_writel(xudc, val, SSPX_CORE_CNT32); + + /* Direct HS/FS port instance to RxDetect. */ + val = xudc_readl(xudc, CFG_DEV_FE); + val &= ~(CFG_DEV_FE_PORTREGSEL_MASK); + val |= CFG_DEV_FE_PORTREGSEL(CFG_DEV_FE_PORTREGSEL_HSFS_PI); + xudc_writel(xudc, val, CFG_DEV_FE); + + val = xudc_readl(xudc, PORTSC); + val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); + val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT); + xudc_writel(xudc, val, PORTSC); + + /* Direct SS port instance to RxDetect. */ + val = xudc_readl(xudc, CFG_DEV_FE); + val &= ~(CFG_DEV_FE_PORTREGSEL_MASK); + val |= CFG_DEV_FE_PORTREGSEL_SS_PI & CFG_DEV_FE_PORTREGSEL_MASK; + xudc_writel(xudc, val, CFG_DEV_FE); + + val = xudc_readl(xudc, PORTSC); + val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); + val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT); + xudc_writel(xudc, val, PORTSC); + + /* Restore port instance. */ + val = xudc_readl(xudc, CFG_DEV_FE); + val &= ~(CFG_DEV_FE_PORTREGSEL_MASK); + xudc_writel(xudc, val, CFG_DEV_FE); + + /* + * Enable INFINITE_SS_RETRY to prevent device from entering + * Disabled.Error when attached to buggy SuperSpeed hubs. + */ + val = xudc_readl(xudc, CFG_DEV_FE); + val |= CFG_DEV_FE_INFINITE_SS_RETRY; + xudc_writel(xudc, val, CFG_DEV_FE); + + /* Set interrupt moderation. */ + imod = XUDC_INTERRUPT_MODERATION_US * 4; + val = xudc_readl(xudc, RT_IMOD); + val &= ~((RT_IMOD_IMODI_MASK) | (RT_IMOD_IMODC_MASK)); + val |= (RT_IMOD_IMODI(imod) | RT_IMOD_IMODC(imod)); + xudc_writel(xudc, val, RT_IMOD); + + /* increase SSPI transaction timeout from 32us to 512us */ + val = xudc_readl(xudc, CFG_DEV_SSPI_XFER); + val &= ~(CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK); + val |= CFG_DEV_SSPI_XFER_ACKTIMEOUT(0xf000); + xudc_writel(xudc, val, CFG_DEV_SSPI_XFER); +} + +static int tegra_xudc_phy_init(struct tegra_xudc *xudc) +{ + int err; + + err = phy_init(xudc->utmi_phy); + if (err < 0) { + dev_err(xudc->dev, "utmi phy init failed: %d\n", err); + return err; + } + + err = phy_init(xudc->usb3_phy); + if (err < 0) { + dev_err(xudc->dev, "usb3 phy init failed: %d\n", err); + goto exit_utmi_phy; + } + + return 0; + +exit_utmi_phy: + phy_exit(xudc->utmi_phy); + return err; +} + +static void tegra_xudc_phy_exit(struct tegra_xudc *xudc) +{ + phy_exit(xudc->usb3_phy); + phy_exit(xudc->utmi_phy); +} + +static const char * const tegra210_xudc_supply_names[] = { + "hvdd-usb", + "avddio-usb", +}; + +static const char * const tegra210_xudc_clock_names[] = { + "dev", + "ss", + "ss_src", + "hs_src", + "fs_src", +}; + +static const char * const tegra186_xudc_clock_names[] = { + "dev", + "ss", + "ss_src", + "fs_src", +}; + +static struct tegra_xudc_soc tegra210_xudc_soc_data = { + .supply_names = tegra210_xudc_supply_names, + .num_supplies = ARRAY_SIZE(tegra210_xudc_supply_names), + .clock_names = tegra210_xudc_clock_names, + .num_clks = ARRAY_SIZE(tegra210_xudc_clock_names), + .u1_enable = false, + .u2_enable = true, + .lpm_enable = false, + .invalid_seq_num = true, + .pls_quirk = true, + .port_reset_quirk = true, + .has_ipfs = true, +}; + +static struct tegra_xudc_soc tegra186_xudc_soc_data = { + .clock_names = tegra186_xudc_clock_names, + .num_clks = ARRAY_SIZE(tegra186_xudc_clock_names), + .u1_enable = true, + .u2_enable = true, + .lpm_enable = false, + .invalid_seq_num = false, + .pls_quirk = false, + .port_reset_quirk = false, + .has_ipfs = false, +}; + +static const struct of_device_id tegra_xudc_of_match[] = { + { + .compatible = "nvidia,tegra210-xudc", + .data = &tegra210_xudc_soc_data + }, + { + .compatible = "nvidia,tegra186-xudc", + .data = &tegra186_xudc_soc_data + }, + { } +}; +MODULE_DEVICE_TABLE(of, tegra_xudc_of_match); + +static void tegra_xudc_powerdomain_remove(struct tegra_xudc *xudc) +{ + if (xudc->genpd_dl_ss) + device_link_del(xudc->genpd_dl_ss); + if (xudc->genpd_dl_device) + device_link_del(xudc->genpd_dl_device); + if (xudc->genpd_dev_ss) + dev_pm_domain_detach(xudc->genpd_dev_ss, true); + if (xudc->genpd_dev_device) + dev_pm_domain_detach(xudc->genpd_dev_device, true); +} + +static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc) +{ + struct device *dev = xudc->dev; + int err; + + xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, + "dev"); + if (IS_ERR(xudc->genpd_dev_device)) { + err = PTR_ERR(xudc->genpd_dev_device); + dev_err(dev, "failed to get dev pm-domain: %d\n", err); + return err; + } + + xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss"); + if (IS_ERR(xudc->genpd_dev_ss)) { + err = PTR_ERR(xudc->genpd_dev_ss); + dev_err(dev, "failed to get superspeed pm-domain: %d\n", err); + return err; + } + + xudc->genpd_dl_device = device_link_add(dev, xudc->genpd_dev_device, + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS); + if (!xudc->genpd_dl_device) { + dev_err(dev, "adding usb device device link failed!\n"); + return -ENODEV; + } + + xudc->genpd_dl_ss = device_link_add(dev, xudc->genpd_dev_ss, + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS); + if (!xudc->genpd_dl_ss) { + dev_err(dev, "adding superspeed device link failed!\n"); + return -ENODEV; + } + + return 0; +} + +static int tegra_xudc_probe(struct platform_device *pdev) +{ + struct tegra_xudc *xudc; + struct resource *res; + struct usb_role_switch_desc role_sx_desc = { 0 }; + unsigned int i; + int err; + + xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_ATOMIC); + if (!xudc) + return -ENOMEM; + + xudc->dev = &pdev->dev; + platform_set_drvdata(pdev, xudc); + + xudc->soc = of_device_get_match_data(&pdev->dev); + if (!xudc->soc) + return -ENODEV; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); + xudc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xudc->base)) + return PTR_ERR(xudc->base); + xudc->phys_base = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fpci"); + xudc->fpci = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xudc->fpci)) + return PTR_ERR(xudc->fpci); + + if (xudc->soc->has_ipfs) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "ipfs"); + xudc->ipfs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xudc->ipfs)) + return PTR_ERR(xudc->ipfs); + } + + xudc->irq = platform_get_irq(pdev, 0); + if (xudc->irq < 0) { + dev_err(xudc->dev, "failed to get IRQ: %d\n", + xudc->irq); + return xudc->irq; + } + + err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0, + dev_name(&pdev->dev), xudc); + if (err < 0) { + dev_err(xudc->dev, "failed to claim IRQ#%u: %d\n", xudc->irq, + err); + return err; + } + + xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, + sizeof(*xudc->clks), GFP_KERNEL); + if (!xudc->clks) + return -ENOMEM; + + for (i = 0; i < xudc->soc->num_clks; i++) + xudc->clks[i].id = xudc->soc->clock_names[i]; + + err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, + xudc->clks); + if (err) { + dev_err(xudc->dev, "failed to request clks %d\n", err); + return err; + } + + xudc->supplies = devm_kcalloc(&pdev->dev, xudc->soc->num_supplies, + sizeof(*xudc->supplies), GFP_KERNEL); + if (!xudc->supplies) + return -ENOMEM; + + for (i = 0; i < xudc->soc->num_supplies; i++) + xudc->supplies[i].supply = xudc->soc->supply_names[i]; + + err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies, + xudc->supplies); + if (err) { + dev_err(xudc->dev, "failed to request regulators %d\n", err); + return err; + } + + xudc->padctl = tegra_xusb_padctl_get(&pdev->dev); + if (IS_ERR(xudc->padctl)) + return PTR_ERR(xudc->padctl); + + err = regulator_bulk_enable(xudc->soc->num_supplies, xudc->supplies); + if (err) { + dev_err(xudc->dev, "failed to enable regulators %d\n", err); + goto put_padctl; + } + + xudc->usb3_phy = devm_phy_optional_get(&pdev->dev, "usb3"); + if (IS_ERR(xudc->usb3_phy)) { + err = PTR_ERR(xudc->usb3_phy); + dev_err(xudc->dev, "failed to get usb3 phy: %d\n", err); + goto disable_regulator; + } + + xudc->utmi_phy = devm_phy_optional_get(&pdev->dev, "usb2"); + if (IS_ERR(xudc->utmi_phy)) { + err = PTR_ERR(xudc->utmi_phy); + dev_err(xudc->dev, "failed to get usb2 phy: %d\n", err); + goto disable_regulator; + } + + err = tegra_xudc_powerdomain_init(xudc); + if (err) + goto put_powerdomains; + + err = tegra_xudc_phy_init(xudc); + if (err) + goto put_powerdomains; + + err = tegra_xudc_alloc_event_ring(xudc); + if (err) + goto disable_phy; + + err = tegra_xudc_alloc_eps(xudc); + if (err) + goto free_event_ring; + + spin_lock_init(&xudc->lock); + + init_completion(&xudc->disconnect_complete); + + INIT_WORK(&xudc->usb_role_sw_work, tegra_xudc_usb_role_sw_work); + + INIT_DELAYED_WORK(&xudc->plc_reset_work, tegra_xudc_plc_reset_work); + + INIT_DELAYED_WORK(&xudc->port_reset_war_work, + tegra_xudc_port_reset_war_work); + + if (of_property_read_bool(xudc->dev->of_node, "usb-role-switch")) { + role_sx_desc.set = tegra_xudc_usb_role_sw_set; + role_sx_desc.fwnode = dev_fwnode(xudc->dev); + + xudc->usb_role_sw = usb_role_switch_register(xudc->dev, + &role_sx_desc); + if (IS_ERR(xudc->usb_role_sw)) { + err = PTR_ERR(xudc->usb_role_sw); + dev_err(xudc->dev, "Failed to register USB role SW: %d", + err); + goto free_eps; + } + } else { + /* Set the mode as device mode and this keeps phy always ON */ + dev_info(xudc->dev, "Set usb role to device mode always"); + schedule_work(&xudc->usb_role_sw_work); + } + + pm_runtime_enable(&pdev->dev); + + xudc->gadget.ops = &tegra_xudc_gadget_ops; + xudc->gadget.ep0 = &xudc->ep[0].usb_ep; + xudc->gadget.name = "tegra-xudc"; + xudc->gadget.max_speed = USB_SPEED_SUPER; + + err = usb_add_gadget_udc(&pdev->dev, &xudc->gadget); + if (err) { + dev_err(&pdev->dev, "failed to add USB gadget: %d\n", err); + goto free_eps; + } + + return 0; + +free_eps: + tegra_xudc_free_eps(xudc); +free_event_ring: + tegra_xudc_free_event_ring(xudc); +disable_phy: + tegra_xudc_phy_exit(xudc); +put_powerdomains: + tegra_xudc_powerdomain_remove(xudc); +disable_regulator: + regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); +put_padctl: + tegra_xusb_padctl_put(xudc->padctl); + + return err; +} + +static int tegra_xudc_remove(struct platform_device *pdev) +{ + struct tegra_xudc *xudc = platform_get_drvdata(pdev); + + pm_runtime_get_sync(xudc->dev); + + cancel_delayed_work(&xudc->plc_reset_work); + + if (xudc->usb_role_sw) { + usb_role_switch_unregister(xudc->usb_role_sw); + cancel_work_sync(&xudc->usb_role_sw_work); + } + + usb_del_gadget_udc(&xudc->gadget); + + tegra_xudc_free_eps(xudc); + tegra_xudc_free_event_ring(xudc); + + tegra_xudc_powerdomain_remove(xudc); + + regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); + + phy_power_off(xudc->utmi_phy); + phy_power_off(xudc->usb3_phy); + + tegra_xudc_phy_exit(xudc); + + pm_runtime_disable(xudc->dev); + pm_runtime_put(xudc->dev); + + tegra_xusb_padctl_put(xudc->padctl); + + return 0; +} + +static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc) +{ + unsigned long flags; + + dev_dbg(xudc->dev, "entering ELPG\n"); + + spin_lock_irqsave(&xudc->lock, flags); + + xudc->powergated = true; + xudc->saved_regs.ctrl = xudc_readl(xudc, CTRL); + xudc->saved_regs.portpm = xudc_readl(xudc, PORTPM); + xudc_writel(xudc, 0, CTRL); + + spin_unlock_irqrestore(&xudc->lock, flags); + + clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks); + + regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); + + dev_dbg(xudc->dev, "entering ELPG done\n"); + return 0; +} + +static int __maybe_unused tegra_xudc_unpowergate(struct tegra_xudc *xudc) +{ + unsigned long flags; + int err; + + dev_dbg(xudc->dev, "exiting ELPG\n"); + + err = regulator_bulk_enable(xudc->soc->num_supplies, + xudc->supplies); + if (err < 0) + return err; + + err = clk_bulk_prepare_enable(xudc->soc->num_clks, xudc->clks); + if (err < 0) + return err; + + tegra_xudc_fpci_ipfs_init(xudc); + + tegra_xudc_device_params_init(xudc); + + tegra_xudc_init_event_ring(xudc); + + tegra_xudc_init_eps(xudc); + + xudc_writel(xudc, xudc->saved_regs.portpm, PORTPM); + xudc_writel(xudc, xudc->saved_regs.ctrl, CTRL); + + spin_lock_irqsave(&xudc->lock, flags); + xudc->powergated = false; + spin_unlock_irqrestore(&xudc->lock, flags); + + dev_dbg(xudc->dev, "exiting ELPG done\n"); + return 0; +} + +static int __maybe_unused tegra_xudc_suspend(struct device *dev) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&xudc->lock, flags); + xudc->suspended = true; + spin_unlock_irqrestore(&xudc->lock, flags); + + flush_work(&xudc->usb_role_sw_work); + + /* Forcibly disconnect before powergating. */ + tegra_xudc_device_mode_off(xudc); + + if (!pm_runtime_status_suspended(dev)) + tegra_xudc_powergate(xudc); + + pm_runtime_disable(dev); + + return 0; +} + +static int __maybe_unused tegra_xudc_resume(struct device *dev) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + unsigned long flags; + int err; + + err = tegra_xudc_unpowergate(xudc); + if (err < 0) + return err; + + spin_lock_irqsave(&xudc->lock, flags); + xudc->suspended = false; + spin_unlock_irqrestore(&xudc->lock, flags); + + schedule_work(&xudc->usb_role_sw_work); + + pm_runtime_enable(dev); + + return 0; +} + +static int __maybe_unused tegra_xudc_runtime_suspend(struct device *dev) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + + return tegra_xudc_powergate(xudc); +} + +static int __maybe_unused tegra_xudc_runtime_resume(struct device *dev) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + + return tegra_xudc_unpowergate(xudc); +} + +static const struct dev_pm_ops tegra_xudc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_xudc_suspend, tegra_xudc_resume) + SET_RUNTIME_PM_OPS(tegra_xudc_runtime_suspend, + tegra_xudc_runtime_resume, NULL) +}; + +static struct platform_driver tegra_xudc_driver = { + .probe = tegra_xudc_probe, + .remove = tegra_xudc_remove, + .driver = { + .name = "tegra-xudc", + .pm = &tegra_xudc_pm_ops, + .of_match_table = tegra_xudc_of_match, + }, +}; +module_platform_driver(tegra_xudc_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra XUSB Device Controller"); +MODULE_AUTHOR("Andrew Bresticker "); +MODULE_AUTHOR("Hui Fu "); +MODULE_AUTHOR("Nagarjuna Kristam "); +MODULE_LICENSE("GPL v2"); -- cgit From f3088e6a12fe516d89cd431877123041235fcd74 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 9 Oct 2019 17:05:00 +0800 Subject: usb: mtu3: fix race condition about delayed_status usb_composite_setup_continue() may be called before composite_setup() return USB_GADGET_DELAYED_STATUS, then the controller driver will delay status stage after composite_setup() finish, but the class driver don't ask the controller to continue delayed status anymore, this will cause control transfer timeout. happens when use mass storage (also enabled other class driver): cpu1: cpu2 handle_setup(SET_CONFIG) //gadget driver unlock (g->lock) gadget_driver->setup() composite_setup() lock(cdev->lock) set_config() fsg_set_alt() // maybe some times due to many class are enabled raise FSG_STATE_CONFIG_CHANGE return USB_GADGET_DELAYED_STATUS handle_exception() usb_composite_setup_continue() unlock(cdev->lock) lock(cdev->lock) ep0_queue() lock (g->lock) //no delayed status, nothing todo unlock (g->lock) unlock(cdev->lock) return USB_GADGET_DELAYED_STATUS // composite_setup lock (g->lock) get USB_GADGET_DELAYED_STATUS //handle_setup [1] Try to fix the race condition as following: After the driver gets USB_GADGET_DELAYED_STATUS at [1], if we find there is a usb_request in ep0 request list, it means composite already asked us to continue delayed status by usb_composite_setup_continue(), so we skip request about delayed_status by composite_setup() and still do status stage. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_gadget_ep0.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index df3fd055792f..2be182bd793a 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -671,8 +671,16 @@ finish: if (mtu->test_mode) { ; /* nothing to do */ } else if (handled == USB_GADGET_DELAYED_STATUS) { - /* handle the delay STATUS phase till receive ep_queue on ep0 */ - mtu->delayed_status = true; + + mreq = next_ep0_request(mtu); + if (mreq) { + /* already asked us to continue delayed status */ + ep0_do_status_stage(mtu); + ep0_req_giveback(mtu, &mreq->request); + } else { + /* do delayed STATUS stage till receive ep0_queue */ + mtu->delayed_status = true; + } } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */ ep0_do_status_stage(mtu); -- cgit From ebd09f1cd417fce9c85de3abcabf51eadf907a2a Mon Sep 17 00:00:00 2001 From: Charles Yeh Date: Tue, 24 Sep 2019 20:14:00 +0800 Subject: USB: serial: pl2303: add support for PL2303HXN Prolific has developed a new USB to UART chip: PL2303HXN PL2303HXN : PL2303GC/PL2303GS/PL2303GT/PL2303GL/PL2303GE/PL2303GB The Vendor request used by the PL2303HXN (TYPE_HXN) is different from the existing PL2303 series (TYPE_HX & TYPE_01). Therefore, different Vendor requests are used to issue related commands. 1. Added a new TYPE_HXN type in pl2303_type_data, and then executes new Vendor request,new flow control and other related instructions if TYPE_HXN is recognized. 2. Because the new PL2303HXN only accept the new Vendor request, the old Vendor request cannot be accepted (the error message will be returned) So first determine the TYPE_HX or TYPE_HXN through PL2303_READ_TYPE_HX_STATUS in pl2303_startup. 2.1 If the return message is "1", then the PL2303 is the existing TYPE_HX/ TYPE_01 series. The other settings in pl2303_startup are to continue execution. 2.2 If the return message is "not 1", then the PL2303 is the new TYPE_HXN series. The other settings in pl2303_startup are ignored. (PL2303HXN will directly use the default value in the hardware, no need to add additional settings through the software) 3. In pl2303_open: Because TYPE_HXN is different from the instruction of reset down/up stream used by TYPE_HX. Therefore, we will also execute different instructions here. 4. In pl2303_set_termios: The UART flow control instructions used by TYPE_HXN/TYPE_HX/TYPE_01 are different. Therefore, we will also execute different instructions here. 5. In pl2303_vendor_read & pl2303_vendor_write, since TYPE_HXN is different from the vendor request instruction used by TYPE_HX/TYPE_01, it will also execute different instructions here. 6. In pl2303_update_reg: TYPE_HXN used different register for flow control. Therefore, we will also execute different instructions here. Signed-off-by: Charles Yeh Signed-off-by: Johan Hovold --- drivers/usb/serial/pl2303.c | 124 ++++++++++++++++++++++++++++++++++++-------- drivers/usb/serial/pl2303.h | 6 +++ 2 files changed, 107 insertions(+), 23 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 9d27b76c5c6e..aab737e1e7b6 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -47,6 +47,12 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_TB) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GC) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GB) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GT) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GL) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GE) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GS) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID), @@ -130,9 +136,11 @@ MODULE_DEVICE_TABLE(usb, id_table); #define VENDOR_WRITE_REQUEST_TYPE 0x40 #define VENDOR_WRITE_REQUEST 0x01 +#define VENDOR_WRITE_NREQUEST 0x80 #define VENDOR_READ_REQUEST_TYPE 0xc0 #define VENDOR_READ_REQUEST 0x01 +#define VENDOR_READ_NREQUEST 0x81 #define UART_STATE_INDEX 8 #define UART_STATE_MSR_MASK 0x8b @@ -148,11 +156,24 @@ MODULE_DEVICE_TABLE(usb, id_table); #define PL2303_FLOWCTRL_MASK 0xf0 +#define PL2303_READ_TYPE_HX_STATUS 0x8080 + +#define PL2303_HXN_RESET_REG 0x07 +#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02 +#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01 + +#define PL2303_HXN_FLOWCTRL_REG 0x0a +#define PL2303_HXN_FLOWCTRL_MASK 0x1c +#define PL2303_HXN_FLOWCTRL_NONE 0x1c +#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 +#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c + static void pl2303_set_break(struct usb_serial_port *port, bool enable); enum pl2303_type { TYPE_01, /* Type 0 and 1 (difference unknown) */ TYPE_HX, /* HX version of the pl2303 chip */ + TYPE_HXN, /* HXN version of the pl2303 chip */ TYPE_COUNT }; @@ -184,16 +205,26 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { [TYPE_HX] = { .max_baud_rate = 12000000, }, + [TYPE_HXN] = { + .max_baud_rate = 12000000, + }, }; static int pl2303_vendor_read(struct usb_serial *serial, u16 value, unsigned char buf[1]) { + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); struct device *dev = &serial->interface->dev; + u8 request; int res; + if (spriv->type == &pl2303_type_data[TYPE_HXN]) + request = VENDOR_READ_NREQUEST; + else + request = VENDOR_READ_REQUEST; + res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE, + request, VENDOR_READ_REQUEST_TYPE, value, 0, buf, 1, 100); if (res != 1) { dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__, @@ -211,13 +242,20 @@ static int pl2303_vendor_read(struct usb_serial *serial, u16 value, static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index) { + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); struct device *dev = &serial->interface->dev; + u8 request; int res; dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index); + if (spriv->type == &pl2303_type_data[TYPE_HXN]) + request = VENDOR_WRITE_NREQUEST; + else + request = VENDOR_WRITE_REQUEST; + res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE, + request, VENDOR_WRITE_REQUEST_TYPE, value, index, NULL, 0, 100); if (res) { dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__, @@ -230,6 +268,7 @@ static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index) static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val) { + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); int ret = 0; u8 *buf; @@ -237,7 +276,11 @@ static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val) if (!buf) return -ENOMEM; - ret = pl2303_vendor_read(serial, reg | 0x80, buf); + if (spriv->type == &pl2303_type_data[TYPE_HXN]) + ret = pl2303_vendor_read(serial, reg, buf); + else + ret = pl2303_vendor_read(serial, reg | 0x80, buf); + if (ret) goto out_free; @@ -320,6 +363,7 @@ static int pl2303_startup(struct usb_serial *serial) struct pl2303_serial_private *spriv; enum pl2303_type type = TYPE_01; unsigned char *buf; + int res; spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); if (!spriv) @@ -341,26 +385,37 @@ static int pl2303_startup(struct usb_serial *serial) type = TYPE_01; /* type 1 */ dev_dbg(&serial->interface->dev, "device type: %d\n", type); + if (type == TYPE_HX) { + res = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE, + PL2303_READ_TYPE_HX_STATUS, 0, buf, 1, 100); + if (res != 1) + type = TYPE_HXN; + } + spriv->type = &pl2303_type_data[type]; spriv->quirks = (unsigned long)usb_get_serial_data(serial); spriv->quirks |= spriv->type->quirks; usb_set_serial_data(serial, spriv); - pl2303_vendor_read(serial, 0x8484, buf); - pl2303_vendor_write(serial, 0x0404, 0); - pl2303_vendor_read(serial, 0x8484, buf); - pl2303_vendor_read(serial, 0x8383, buf); - pl2303_vendor_read(serial, 0x8484, buf); - pl2303_vendor_write(serial, 0x0404, 1); - pl2303_vendor_read(serial, 0x8484, buf); - pl2303_vendor_read(serial, 0x8383, buf); - pl2303_vendor_write(serial, 0, 1); - pl2303_vendor_write(serial, 1, 0); - if (spriv->quirks & PL2303_QUIRK_LEGACY) - pl2303_vendor_write(serial, 2, 0x24); - else - pl2303_vendor_write(serial, 2, 0x44); + if (type != TYPE_HXN) { + pl2303_vendor_read(serial, 0x8484, buf); + pl2303_vendor_write(serial, 0x0404, 0); + pl2303_vendor_read(serial, 0x8484, buf); + pl2303_vendor_read(serial, 0x8383, buf); + pl2303_vendor_read(serial, 0x8484, buf); + pl2303_vendor_write(serial, 0x0404, 1); + pl2303_vendor_read(serial, 0x8484, buf); + pl2303_vendor_read(serial, 0x8383, buf); + pl2303_vendor_write(serial, 0, 1); + pl2303_vendor_write(serial, 1, 0); + if (spriv->quirks & PL2303_QUIRK_LEGACY) + pl2303_vendor_write(serial, 2, 0x24); + else + pl2303_vendor_write(serial, 2, 0x44); + } kfree(buf); @@ -719,14 +774,31 @@ static void pl2303_set_termios(struct tty_struct *tty, } if (C_CRTSCTS(tty)) { - if (spriv->quirks & PL2303_QUIRK_LEGACY) + if (spriv->quirks & PL2303_QUIRK_LEGACY) { pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x40); - else + } else if (spriv->type == &pl2303_type_data[TYPE_HXN]) { + pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, + PL2303_HXN_FLOWCTRL_MASK, + PL2303_HXN_FLOWCTRL_RTS_CTS); + } else { pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60); + } } else if (pl2303_enable_xonxoff(tty, spriv->type)) { - pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0); + if (spriv->type == &pl2303_type_data[TYPE_HXN]) { + pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, + PL2303_HXN_FLOWCTRL_MASK, + PL2303_HXN_FLOWCTRL_XON_XOFF); + } else { + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0); + } } else { - pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0); + if (spriv->type == &pl2303_type_data[TYPE_HXN]) { + pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, + PL2303_HXN_FLOWCTRL_MASK, + PL2303_HXN_FLOWCTRL_NONE); + } else { + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0); + } } kfree(buf); @@ -767,8 +839,14 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) usb_clear_halt(serial->dev, port->read_urb->pipe); } else { /* reset upstream data pipes */ - pl2303_vendor_write(serial, 8, 0); - pl2303_vendor_write(serial, 9, 0); + if (spriv->type == &pl2303_type_data[TYPE_HXN]) { + pl2303_vendor_write(serial, PL2303_HXN_RESET_REG, + PL2303_HXN_RESET_UPSTREAM_PIPE | + PL2303_HXN_RESET_DOWNSTREAM_PIPE); + } else { + pl2303_vendor_write(serial, 8, 0); + pl2303_vendor_write(serial, 9, 0); + } } /* Setup termios */ diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index b0175f17d1a2..a019ea7e6e0e 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -9,6 +9,12 @@ #define PL2303_VENDOR_ID 0x067b #define PL2303_PRODUCT_ID 0x2303 #define PL2303_PRODUCT_ID_TB 0x2304 +#define PL2303_PRODUCT_ID_GC 0x23a3 +#define PL2303_PRODUCT_ID_GB 0x23b3 +#define PL2303_PRODUCT_ID_GT 0x23c3 +#define PL2303_PRODUCT_ID_GL 0x23d3 +#define PL2303_PRODUCT_ID_GE 0x23e3 +#define PL2303_PRODUCT_ID_GS 0x23f3 #define PL2303_PRODUCT_ID_RSAQ2 0x04bb #define PL2303_PRODUCT_ID_DCU11 0x1234 #define PL2303_PRODUCT_ID_PHAROS 0xaaa0 -- cgit From 387c359b84f71ca29c1a9fa24293c65a257f6bf5 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 28 Oct 2019 11:32:49 +0200 Subject: usb: cdns3: Add TI specific wrapper driver The J721e platform comes with 2 Cadence USB3 controller instances. This driver supports the TI specific wrapper on this platform. Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori Reviewed-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/Kconfig | 10 ++ drivers/usb/cdns3/Makefile | 1 + drivers/usb/cdns3/cdns3-ti.c | 236 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 drivers/usb/cdns3/cdns3-ti.c (limited to 'drivers/usb') diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index d0331613a355..2a1e89d12ed9 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -43,4 +43,14 @@ config USB_CDNS3_PCI_WRAP If you choose to build this driver as module it will be dynamically linked and module will be called cdns3-pci.ko +config USB_CDNS3_TI + tristate "Cadence USB3 support on TI platforms" + depends on ARCH_K3 || COMPILE_TEST + default USB_CDNS3 + help + Say 'Y' or 'M' here if you are building for Texas Instruments + platforms that contain Cadence USB3 controller core. + + e.g. J721e. + endif diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index a703547350bb..948e6b88d1a9 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -14,3 +14,4 @@ endif cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o +obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c new file mode 100644 index 000000000000..c6a79ca15858 --- /dev/null +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * cdns3-ti.c - TI specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB Wrapper register offsets */ +#define USBSS_PID 0x0 +#define USBSS_W1 0x4 +#define USBSS_STATIC_CONFIG 0x8 +#define USBSS_PHY_TEST 0xc +#define USBSS_DEBUG_CTRL 0x10 +#define USBSS_DEBUG_INFO 0x14 +#define USBSS_DEBUG_LINK_STATE 0x18 +#define USBSS_DEVICE_CTRL 0x1c + +/* Wrapper 1 register bits */ +#define USBSS_W1_PWRUP_RST BIT(0) +#define USBSS_W1_OVERCURRENT_SEL BIT(8) +#define USBSS_W1_MODESTRAP_SEL BIT(9) +#define USBSS_W1_OVERCURRENT BIT(16) +#define USBSS_W1_MODESTRAP_MASK GENMASK(18, 17) +#define USBSS_W1_MODESTRAP_SHIFT 17 +#define USBSS_W1_USB2_ONLY BIT(19) + +/* Static config register bits */ +#define USBSS1_STATIC_PLL_REF_SEL_MASK GENMASK(8, 5) +#define USBSS1_STATIC_PLL_REF_SEL_SHIFT 5 +#define USBSS1_STATIC_LOOPBACK_MODE_MASK GENMASK(4, 3) +#define USBSS1_STATIC_LOOPBACK_MODE_SHIFT 3 +#define USBSS1_STATIC_VBUS_SEL_MASK GENMASK(2, 1) +#define USBSS1_STATIC_VBUS_SEL_SHIFT 1 +#define USBSS1_STATIC_LANE_REVERSE BIT(0) + +/* Modestrap modes */ +enum modestrap_mode { USBSS_MODESTRAP_MODE_NONE, + USBSS_MODESTRAP_MODE_HOST, + USBSS_MODESTRAP_MODE_PERIPHERAL}; + +struct cdns_ti { + struct device *dev; + void __iomem *usbss; + int usb2_only:1; + int vbus_divider:1; + struct clk *usb2_refclk; + struct clk *lpm_clk; +}; + +static const int cdns_ti_rate_table[] = { /* in KHZ */ + 9600, + 10000, + 12000, + 19200, + 20000, + 24000, + 25000, + 26000, + 38400, + 40000, + 58000, + 50000, + 52000, +}; + +static inline u32 cdns_ti_readl(struct cdns_ti *data, u32 offset) +{ + return readl(data->usbss + offset); +} + +static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value) +{ + writel(value, data->usbss + offset); +} + +static int cdns_ti_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct cdns_ti *data; + int error; + u32 reg; + int rate_code, i; + unsigned long rate; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + data->dev = dev; + + data->usbss = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->usbss)) { + dev_err(dev, "can't map IOMEM resource\n"); + return PTR_ERR(data->usbss); + } + + data->usb2_refclk = devm_clk_get(dev, "ref"); + if (IS_ERR(data->usb2_refclk)) { + dev_err(dev, "can't get usb2_refclk\n"); + return PTR_ERR(data->usb2_refclk); + } + + data->lpm_clk = devm_clk_get(dev, "lpm"); + if (IS_ERR(data->lpm_clk)) { + dev_err(dev, "can't get lpm_clk\n"); + return PTR_ERR(data->lpm_clk); + } + + rate = clk_get_rate(data->usb2_refclk); + rate /= 1000; /* To KHz */ + for (i = 0; i < ARRAY_SIZE(cdns_ti_rate_table); i++) { + if (cdns_ti_rate_table[i] == rate) + break; + } + + if (i == ARRAY_SIZE(cdns_ti_rate_table)) { + dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate); + return -EINVAL; + } + + rate_code = i; + + pm_runtime_enable(dev); + error = pm_runtime_get_sync(dev); + if (error < 0) { + dev_err(dev, "pm_runtime_get_sync failed: %d\n", error); + goto err_get; + } + + /* assert RESET */ + reg = cdns_ti_readl(data, USBSS_W1); + reg &= ~USBSS_W1_PWRUP_RST; + cdns_ti_writel(data, USBSS_W1, reg); + + /* set static config */ + reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); + reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK; + reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT; + + reg &= ~USBSS1_STATIC_VBUS_SEL_MASK; + data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); + if (data->vbus_divider) + reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT; + + cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg); + reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); + + /* set USB2_ONLY mode if requested */ + reg = cdns_ti_readl(data, USBSS_W1); + data->usb2_only = device_property_read_bool(dev, "ti,usb2-only"); + if (data->usb2_only) + reg |= USBSS_W1_USB2_ONLY; + + /* set default modestrap */ + reg |= USBSS_W1_MODESTRAP_SEL; + reg &= ~USBSS_W1_MODESTRAP_MASK; + reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT; + cdns_ti_writel(data, USBSS_W1, reg); + + /* de-assert RESET */ + reg |= USBSS_W1_PWRUP_RST; + cdns_ti_writel(data, USBSS_W1, reg); + + error = of_platform_populate(node, NULL, NULL, dev); + if (error) { + dev_err(dev, "failed to create children: %d\n", error); + goto err; + } + + return 0; + +err: + pm_runtime_put_sync(data->dev); +err_get: + pm_runtime_disable(data->dev); + + return error; +} + +static int cdns_ti_remove_core(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static int cdns_ti_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + device_for_each_child(dev, NULL, cdns_ti_remove_core); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id cdns_ti_of_match[] = { + { .compatible = "ti,j721e-usb", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cdns_ti_of_match); + +static struct platform_driver cdns_ti_driver = { + .probe = cdns_ti_probe, + .remove = cdns_ti_remove, + .driver = { + .name = "cdns3-ti", + .of_match_table = cdns_ti_of_match, + }, +}; + +module_platform_driver(cdns_ti_driver); + +MODULE_ALIAS("platform:cdns3-ti"); +MODULE_AUTHOR("Roger Quadros "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USB3 TI Glue Layer"); -- cgit From 726b4fba94bec7e4c16bc681316e82455652c3a8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 Oct 2019 12:56:11 +0200 Subject: usb: dwc3: of-simple: add a shutdown In case we're loading a new kernel via kexec, let's make sure to cleanup the dwc3 address space correctly. This means that we should run the same steps from driver remove, so just extract a reusable function for both cases. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-of-simple.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index bdac3e7d7b18..e64754be47b4 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -110,12 +110,9 @@ err_resetc_put: return ret; } -static int dwc3_of_simple_remove(struct platform_device *pdev) +static void __dwc3_of_simple_teardown(struct dwc3_of_simple *simple) { - struct dwc3_of_simple *simple = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; - - of_platform_depopulate(dev); + of_platform_depopulate(simple->dev); clk_bulk_disable_unprepare(simple->num_clocks, simple->clks); clk_bulk_put_all(simple->num_clocks, simple->clks); @@ -126,13 +123,27 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) reset_control_put(simple->resets); - pm_runtime_disable(dev); - pm_runtime_put_noidle(dev); - pm_runtime_set_suspended(dev); + pm_runtime_disable(simple->dev); + pm_runtime_put_noidle(simple->dev); + pm_runtime_set_suspended(simple->dev); +} + +static int dwc3_of_simple_remove(struct platform_device *pdev) +{ + struct dwc3_of_simple *simple = platform_get_drvdata(pdev); + + __dwc3_of_simple_teardown(simple); return 0; } +static void dwc3_of_simple_shutdown(struct platform_device *pdev) +{ + struct dwc3_of_simple *simple = platform_get_drvdata(pdev); + + __dwc3_of_simple_teardown(simple); +} + static int __maybe_unused dwc3_of_simple_runtime_suspend(struct device *dev) { struct dwc3_of_simple *simple = dev_get_drvdata(dev); @@ -190,6 +201,7 @@ MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); static struct platform_driver dwc3_of_simple_driver = { .probe = dwc3_of_simple_probe, .remove = dwc3_of_simple_remove, + .shutdown = dwc3_of_simple_shutdown, .driver = { .name = "dwc3-of-simple", .of_match_table = of_dwc3_simple_match, -- cgit From 35714565089e5e8b091c1155517b67e29118f09d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 1 Nov 2019 18:24:10 +0100 Subject: USB: serial: ch341: reimplement line-speed handling The current ch341 divisor algorithm was known to give inaccurate results for certain higher line speeds. Jonathan Olds investigated this, determined the basic equations used to derive the divisors and confirmed them experimentally [1]. The equations Jonathan used could be generalised further to: baudrate = 48000000 / (2^(12 - 3 * ps - fact) * div), where 0 <= ps <= 3, 0 <= fact <= 1, 2 <= div <= 256 if fact = 0, or 9 <= div <= 256 if fact = 1 which will also give better results for lower rates. Notably the error is reduced for the following standard rates: 1152000 (4.0% instead of 15% error) 921600 (0.16% instead of -7.5% error) 576000 (-0.80% instead of -5.6% error) 200 (0.16% instead of -0.69% error) 134 (-0.05% instead of -0.63% error) 110 (0.03% instead of -0.44% error) but also for many non-standard ones. The current algorithm also suffered from rounding issues (e.g. requesting 2950000 Bd resulted in a rate of 2 MBd instead of 3 MBd and thus a -32% instead of 1.7% error). The new algorithm was inspired by the current vendor driver even if that one only handles two higher rates that require fact=1 by hard coding the corresponding divisors [2]. Michael Dreher also did a similar generalisation of Jonathan's work and has published his results with a very good summary that provides further insights into how this device works [3]. [1] https://lkml.kernel.org/r/000001d51f34$bad6afd0$30840f70$@co.nz [2] http://www.wch.cn/download/CH341SER_LINUX_ZIP.html [3] https://github.com/nospam2000/ch341-baudrate-calculation Reported-by: Jonathan Olds Tested-by: Jonathan Olds Cc: Michael Dreher Signed-off-by: Johan Hovold --- drivers/usb/serial/ch341.c | 97 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 22 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 3bb1fff02bed..df582fe855f0 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -48,12 +48,6 @@ #define CH341_BIT_DCD 0x08 #define CH341_BITS_MODEM_STAT 0x0f /* all bits */ -/*******************************/ -/* baudrate calculation factor */ -/*******************************/ -#define CH341_BAUDBASE_FACTOR 1532620800 -#define CH341_BAUDBASE_DIVMAX 3 - /* Break support - the information used to implement this was gleaned from * the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato. */ @@ -144,37 +138,96 @@ static int ch341_control_in(struct usb_device *dev, return 0; } +#define CH341_CLKRATE 48000000 +#define CH341_CLK_DIV(ps, fact) (1 << (12 - 3 * (ps) - (fact))) +#define CH341_MIN_RATE(ps) (CH341_CLKRATE / (CH341_CLK_DIV((ps), 1) * 512)) + +static const speed_t ch341_min_rates[] = { + CH341_MIN_RATE(0), + CH341_MIN_RATE(1), + CH341_MIN_RATE(2), + CH341_MIN_RATE(3), +}; + +/* + * The device line speed is given by the following equation: + * + * baudrate = 48000000 / (2^(12 - 3 * ps - fact) * div), where + * + * 0 <= ps <= 3, + * 0 <= fact <= 1, + * 2 <= div <= 256 if fact = 0, or + * 9 <= div <= 256 if fact = 1 + */ +static int ch341_get_divisor(speed_t speed) +{ + unsigned int fact, div, clk_div; + int ps; + + /* + * Clamp to supported range, this makes the (ps < 0) and (div < 2) + * sanity checks below redundant. + */ + speed = clamp(speed, 46U, 3000000U); + + /* + * Start with highest possible base clock (fact = 1) that will give a + * divisor strictly less than 512. + */ + fact = 1; + for (ps = 3; ps >= 0; ps--) { + if (speed > ch341_min_rates[ps]) + break; + } + + if (ps < 0) + return -EINVAL; + + /* Determine corresponding divisor, rounding down. */ + clk_div = CH341_CLK_DIV(ps, fact); + div = CH341_CLKRATE / (clk_div * speed); + + /* Halve base clock (fact = 0) if required. */ + if (div < 9 || div > 255) { + div /= 2; + clk_div *= 2; + fact = 0; + } + + if (div < 2) + return -EINVAL; + + /* + * Pick next divisor if resulting rate is closer to the requested one, + * scale up to avoid rounding errors on low rates. + */ + if (16 * CH341_CLKRATE / (clk_div * div) - 16 * speed >= + 16 * speed - 16 * CH341_CLKRATE / (clk_div * (div + 1))) + div++; + + return (0x100 - div) << 8 | fact << 2 | ps; +} + static int ch341_set_baudrate_lcr(struct usb_device *dev, struct ch341_private *priv, u8 lcr) { - short a; + int val; int r; - unsigned long factor; - short divisor; if (!priv->baud_rate) return -EINVAL; - factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate); - divisor = CH341_BAUDBASE_DIVMAX; - - while ((factor > 0xfff0) && divisor) { - factor >>= 3; - divisor--; - } - if (factor > 0xfff0) + val = ch341_get_divisor(priv->baud_rate); + if (val < 0) return -EINVAL; - factor = 0x10000 - factor; - a = (factor & 0xff00) | divisor; - /* * CH341A buffers data until a full endpoint-size packet (32 bytes) * has been received unless bit 7 is set. */ - a |= BIT(7); + val |= BIT(7); - r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a); + r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, val); if (r) return r; -- cgit From c6919d5e0cd168a732034d8dc19fdc3dff683a2b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 8 Oct 2019 15:25:59 +0300 Subject: usb: roles: Add usb_role_switch_find_by_fwnode() Simple wrapper function that searches USB role switches with class_find_device_by_fwnode(). Signed-off-by: Heikki Krogerus Reviewed-by: Hans de Goede Tested-by: Hans de Goede Link: https://lore.kernel.org/r/20191008122600.22340-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/roles/class.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 94b4e7db2b94..8273126ffdf4 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -175,6 +175,27 @@ void usb_role_switch_put(struct usb_role_switch *sw) } EXPORT_SYMBOL_GPL(usb_role_switch_put); +/** + * usb_role_switch_find_by_fwnode - Find USB role switch with its fwnode + * @fwnode: fwnode of the USB Role Switch + * + * Finds and returns role switch with @fwnode. The reference count for the + * found switch is incremented. + */ +struct usb_role_switch * +usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode) +{ + struct device *dev; + + if (!fwnode) + return NULL; + + dev = class_find_device_by_fwnode(role_class, fwnode); + + return dev ? to_role_switch(dev) : NULL; +} +EXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode); + static umode_t usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) { -- cgit From 09e2a8b78cbd628f4f428b76cb3fed64cdfbc657 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 15 Oct 2019 15:19:45 +0100 Subject: usb: ohci-nxp: fix use of integer as pointer The hcd pointer in ohci_hcd_nxp_probe() is being initialised with a 0, so fix to NULL to avoid the following sparse warning: drivers/usb/host/ohci-nxp.c:153:31: warning: Using plain integer as NULL pointer Signed-off-by: Ben Dooks Acked-by: Sylvain Lemieux Link: https://lore.kernel.org/r/20191015141945.16067-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-nxp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index c561881d0e79..85878e8ad331 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -150,7 +150,7 @@ static void ohci_nxp_stop_hc(void) static int ohci_hcd_nxp_probe(struct platform_device *pdev) { - struct usb_hcd *hcd = 0; + struct usb_hcd *hcd = NULL; const struct hc_driver *driver = &ohci_nxp_hc_driver; struct resource *res; int ret = 0, irq; -- cgit From f3de5d857bb2362b00e2a8d4bc886cd49dcb66db Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 16 Oct 2019 16:35:32 +0800 Subject: USB: bcma: Add a check for devm_gpiod_get bcma_hcd_probe misses a check for devm_gpiod_get and may miss the error. Add a check for it and return the error if a failure occurs. Signed-off-by: Chuhong Yuan Link: https://lore.kernel.org/r/20191016083531.5734-1-hslester96@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/bcma-hcd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c index 2400a826397a..652fa29beb27 100644 --- a/drivers/usb/host/bcma-hcd.c +++ b/drivers/usb/host/bcma-hcd.c @@ -406,9 +406,12 @@ static int bcma_hcd_probe(struct bcma_device *core) return -ENOMEM; usb_dev->core = core; - if (core->dev.of_node) + if (core->dev.of_node) { usb_dev->gpio_desc = devm_gpiod_get(&core->dev, "vcc", GPIOD_OUT_HIGH); + if (IS_ERR(usb_dev->gpio_desc)) + return PTR_ERR(usb_dev->gpio_desc); + } switch (core->id.id) { case BCMA_CORE_USB20_HOST: -- cgit From 6f9ac343c0d2bc2f40ec7bed307a322497894e1d Mon Sep 17 00:00:00 2001 From: "Ben Dooks (Codethink)" Date: Thu, 17 Oct 2019 18:19:34 +0100 Subject: usb: host: oxu210hp-hcd: fix __iomem annotations There are a number of places in the driver where it fails to maintain __iomem on pointers used to access registers so fixup the warnings by adding these in the appropriate places. Examples of the sparse warnings fixed: drivers/usb/host/oxu210hp-hcd.c:686:9: warning: incorrect type in argument 2 (different address spaces) drivers/usb/host/oxu210hp-hcd.c:686:9: expected void volatile [noderef] *addr drivers/usb/host/oxu210hp-hcd.c:686:9: got void * drivers/usb/host/oxu210hp-hcd.c:686:9: warning: incorrect type in argument 2 (different address spaces) drivers/usb/host/oxu210hp-hcd.c:686:9: expected void volatile [noderef] *addr drivers/usb/host/oxu210hp-hcd.c:686:9: got void * drivers/usb/host/oxu210hp-hcd.c:686:9: warning: incorrect type in argument 2 (different address spaces) drivers/usb/host/oxu210hp-hcd.c:686:9: expected void volatile [noderef] *addr drivers/usb/host/oxu210hp-hcd.c:686:9: got void * drivers/usb/host/oxu210hp-hcd.c:681:16: warning: incorrect type in argument 1 (different address spaces) drivers/usb/host/oxu210hp-hcd.c:681:16: expected void const volatile [noderef] *addr drivers/usb/host/oxu210hp-hcd.c:681:16: got void * Signed-off-by: Ben Dooks Link: https://lore.kernel.org/r/20191017171934.8771-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/oxu210hp-hcd.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index e67242e437ed..fe09b8626329 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -676,12 +676,12 @@ static int oxu_hub_control(struct usb_hcd *hcd, */ /* Low level read/write registers functions */ -static inline u32 oxu_readl(void *base, u32 reg) +static inline u32 oxu_readl(void __iomem *base, u32 reg) { return readl(base + reg); } -static inline void oxu_writel(void *base, u32 reg, u32 val) +static inline void oxu_writel(void __iomem *base, u32 reg, u32 val) { writel(val, base + reg); } @@ -4063,7 +4063,7 @@ static const struct hc_driver oxu_hc_driver = { * Module stuff */ -static void oxu_configuration(struct platform_device *pdev, void *base) +static void oxu_configuration(struct platform_device *pdev, void __iomem *base) { u32 tmp; @@ -4093,7 +4093,7 @@ static void oxu_configuration(struct platform_device *pdev, void *base) oxu_writel(base, OXU_CHIPIRQEN_SET, OXU_USBSPHLPWUI | OXU_USBOTGLPWUI); } -static int oxu_verify_id(struct platform_device *pdev, void *base) +static int oxu_verify_id(struct platform_device *pdev, void __iomem *base) { u32 id; static const char * const bo[] = { @@ -4121,7 +4121,7 @@ static int oxu_verify_id(struct platform_device *pdev, void *base) static const struct hc_driver oxu_hc_driver; static struct usb_hcd *oxu_create(struct platform_device *pdev, unsigned long memstart, unsigned long memlen, - void *base, int irq, int otg) + void __iomem *base, int irq, int otg) { struct device *dev = &pdev->dev; @@ -4158,7 +4158,7 @@ static struct usb_hcd *oxu_create(struct platform_device *pdev, static int oxu_init(struct platform_device *pdev, unsigned long memstart, unsigned long memlen, - void *base, int irq) + void __iomem *base, int irq) { struct oxu_info *info = platform_get_drvdata(pdev); struct usb_hcd *hcd; @@ -4207,7 +4207,7 @@ error_create_otg: static int oxu_drv_probe(struct platform_device *pdev) { struct resource *res; - void *base; + void __iomem *base; unsigned long memstart, memlen; int irq, ret; struct oxu_info *info; -- cgit From 2a59aa7711e0013f6ff27ba77e7b56eb7674ca97 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 23 Oct 2019 12:52:48 +0200 Subject: usb: usb251xb: Drop some unused defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The five removed symbols are unused since they were introduced in commit 3ec72a2a1e5d ("usb: misc: add USB251xB/xBi Hi-Speed Hub Controller Driver") back in 2017. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20191023105250.16537-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb251xb.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 5bba19937da1..753d6fcd393b 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -27,10 +27,6 @@ #define USB251XB_ADDR_PRODUCT_ID_LSB 0x02 #define USB251XB_ADDR_PRODUCT_ID_MSB 0x03 -#define USB251XB_DEF_PRODUCT_ID_12 0x2512 /* USB2512B/12Bi */ -#define USB251XB_DEF_PRODUCT_ID_13 0x2513 /* USB2513B/13Bi */ -#define USB251XB_DEF_PRODUCT_ID_14 0x2514 /* USB2514B/14Bi */ -#define USB251XB_DEF_PRODUCT_ID_17 0x2517 /* USB2517/17i */ #define USB251XB_ADDR_DEVICE_ID_LSB 0x04 #define USB251XB_ADDR_DEVICE_ID_MSB 0x05 @@ -75,7 +71,6 @@ #define USB251XB_ADDR_PRODUCT_STRING_LEN 0x14 #define USB251XB_ADDR_PRODUCT_STRING 0x54 -#define USB251XB_DEF_PRODUCT_STRING "USB251xB/xBi/7i" #define USB251XB_ADDR_SERIAL_STRING_LEN 0x15 #define USB251XB_ADDR_SERIAL_STRING 0x92 -- cgit From cd7da3bc6c580e398e30349d88ff664113c9408e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 23 Oct 2019 12:52:50 +0200 Subject: usb: usb251xb: Add support for USB2422 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The USB2422 uses a different package that the USB251x and only comes in a variant with 2 downstream ports. Other than that it is software compatible. Tested-by: Carsten Stelling Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20191023105250.16537-3-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb251xb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 753d6fcd393b..10c9e7f6273e 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -156,6 +156,14 @@ struct usb251xb_data { char product_str[USB251XB_STRING_BUFSIZE / 2]; /* ASCII string */ }; +static const struct usb251xb_data usb2422_data = { + .product_id = 0x2422, + .port_cnt = 2, + .led_support = false, + .bat_support = true, + .product_str = "USB2422", +}; + static const struct usb251xb_data usb2512b_data = { .product_id = 0x2512, .port_cnt = 2, @@ -593,6 +601,9 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, static const struct of_device_id usb251xb_of_match[] = { { + .compatible = "microchip,usb2422", + .data = &usb2422_data, + }, { .compatible = "microchip,usb2512b", .data = &usb2512b_data, }, { @@ -720,6 +731,7 @@ static int __maybe_unused usb251xb_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(usb251xb_pm_ops, usb251xb_suspend, usb251xb_resume); static const struct i2c_device_id usb251xb_id[] = { + { "usb2422", 0 }, { "usb2512b", 0 }, { "usb2512bi", 0 }, { "usb2513b", 0 }, -- cgit From c1aa81da1c64a090da7cf335595f69fd76f3c0bf Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 24 Oct 2019 17:28:33 +0200 Subject: usb: Spelling s/enpoint/endpoint/ Fix misspellings of "endpoint". Signed-off-by: Geert Uytterhoeven Acked-by: Li Yang Acked-by: Minas Harutyunyan Link: https://lore.kernel.org/r/20191024152833.30698-1-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/core.h | 2 +- drivers/usb/gadget/udc/fsl_qe_udc.h | 4 ++-- drivers/usb/gadget/udc/mv_u3d.h | 2 +- drivers/usb/musb/musb_gadget.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index d08d070a0fb6..968e03b89d04 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -134,7 +134,7 @@ struct dwc2_hsotg_req; * @target_frame: Targeted frame num to setup next ISOC transfer * @frame_overrun: Indicates SOF number overrun in DSTS * - * This is the driver's state for each registered enpoint, allowing it + * This is the driver's state for each registered endpoint, allowing it * to keep track of transactions that need doing. Each endpoint has a * lock to protect the state, to try and avoid using an overall lock * for the host controller as much as possible. diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h index 2c537a904ee7..53ca0ff7c2cb 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.h +++ b/drivers/usb/gadget/udc/fsl_qe_udc.h @@ -333,8 +333,8 @@ struct qe_udc { u32 resume_state; /* USB state to resume*/ u32 usb_state; /* USB current state */ u32 usb_next_state; /* USB next state */ - u32 ep0_state; /* Enpoint zero state */ - u32 ep0_dir; /* Enpoint zero direction: can be + u32 ep0_state; /* Endpoint zero state */ + u32 ep0_dir; /* Endpoint zero direction: can be USB_DIR_IN or USB_DIR_OUT*/ u32 usb_sof_count; /* SOF count */ u32 errors; /* USB ERRORs count */ diff --git a/drivers/usb/gadget/udc/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h index 982625b7197a..66b84f792f64 100644 --- a/drivers/usb/gadget/udc/mv_u3d.h +++ b/drivers/usb/gadget/udc/mv_u3d.h @@ -138,7 +138,7 @@ struct mv_u3d_op_regs { u32 doorbell; /* doorbell register */ }; -/* control enpoint enable registers */ +/* control endpoint enable registers */ struct epxcr { u32 epxoutcr0; /* ep out control 0 register */ u32 epxoutcr1; /* ep out control 1 register */ diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 2cb31fc0cd60..f62ffaede1ab 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1315,7 +1315,7 @@ done: } /* - * Set or clear the halt bit of an endpoint. A halted enpoint won't tx/rx any + * Set or clear the halt bit of an endpoint. A halted endpoint won't tx/rx any * data but will queue requests. * * exported to ep0 code -- cgit From 4ff0eccbb5c4487ef065931f5c566d85a5bfb5c9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 24 Oct 2019 17:27:47 +0200 Subject: usb: Spelling s/disconnet/disconnect/ Fix misspellings of "disconnect". Signed-off-by: Geert Uytterhoeven Acked-by: Peter Chen Link: https://lore.kernel.org/r/20191024152747.30617-1-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 2 +- drivers/usb/gadget/udc/fsl_udc_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 8f18e7b6cadf..0b6166a64d72 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1612,7 +1612,7 @@ static int ci_udc_selfpowered(struct usb_gadget *_gadget, int is_on) } /* Change Data+ pullup status - * this func is used by usb_gadget_connect/disconnet + * this func is used by usb_gadget_connect/disconnect */ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) { diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 9a05863b2876..7c8bea7c1695 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1208,7 +1208,7 @@ static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA) } /* Change Data+ pullup status - * this func is used by usb_gadget_connect/disconnet + * this func is used by usb_gadget_connect/disconnect */ static int fsl_pullup(struct usb_gadget *gadget, int is_on) { -- cgit From a363d50515eb464a4e2aade12312cfdc1b156944 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Sat, 2 Nov 2019 14:22:45 +0800 Subject: usb: host: fotg210: add missed clk_put calls The driver forgets to call clk_put when probe fails and remove. Add the calls to fix it. Signed-off-by: Chuhong Yuan Link: https://lore.kernel.org/r/20191102062245.4014-1-hslester96@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/fotg210-hcd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 9e0c98d6bdb0..f967adf2d8df 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -5646,8 +5646,10 @@ static int fotg210_hcd_probe(struct platform_device *pdev) return retval; failed_dis_clk: - if (!IS_ERR(fotg210->pclk)) + if (!IS_ERR(fotg210->pclk)) { clk_disable_unprepare(fotg210->pclk); + clk_put(fotg210->pclk); + } failed_put_hcd: usb_put_hcd(hcd); fail_create_hcd: @@ -5665,8 +5667,10 @@ static int fotg210_hcd_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - if (!IS_ERR(fotg210->pclk)) + if (!IS_ERR(fotg210->pclk)) { clk_disable_unprepare(fotg210->pclk); + clk_put(fotg210->pclk); + } usb_remove_hcd(hcd); usb_put_hcd(hcd); -- cgit From 8442b02bf3c6770e0d7e7ea17be36c30e95987b6 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Mon, 21 Oct 2019 16:20:58 +0200 Subject: USB: dummy-hcd: increase max number of devices to 32 When fuzzing the USB subsystem with syzkaller, we currently use 8 testing processes within one VM. To isolate testing processes from one another it is desirable to assign a dedicated USB bus to each of those, which means we need at least 8 Dummy UDC/HCD devices. This patch increases the maximum number of Dummy UDC/HCD devices to 32 (more than 8 in case we need more of them in the future). Signed-off-by: Andrey Konovalov Link: https://lore.kernel.org/r/665578f904484069bb6100fb20283b22a046ad9b.1571667489.git.andreyknvl@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 3d499d93c083..a8f1e5707c14 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2725,7 +2725,7 @@ static struct platform_driver dummy_hcd_driver = { }; /*-------------------------------------------------------------------------*/ -#define MAX_NUM_UDC 2 +#define MAX_NUM_UDC 32 static struct platform_device *the_udc_pdev[MAX_NUM_UDC]; static struct platform_device *the_hcd_pdev[MAX_NUM_UDC]; -- cgit From 6dabeb891c001c592645df2f477fed9f5d959987 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Mon, 21 Oct 2019 16:20:59 +0200 Subject: USB: dummy-hcd: use usb_urb_dir_in instead of usb_pipein Commit fea3409112a9 ("USB: add direction bit to urb->transfer_flags") has added a usb_urb_dir_in() helper function that can be used to determine the direction of the URB. With that patch USB_DIR_IN control requests with wLength == 0 are considered out requests by real USB HCDs. This patch changes dummy-hcd to use the usb_urb_dir_in() helper to match that behavior. Signed-off-by: Andrey Konovalov Link: https://lore.kernel.org/r/4ae9e68ebca02f08a93ac61fe065057c9a01f0a8.1571667489.git.andreyknvl@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index a8f1e5707c14..4c9d1e49d5ed 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1321,7 +1321,7 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, u32 this_sg; bool next_sg; - to_host = usb_pipein(urb->pipe); + to_host = usb_urb_dir_in(urb); rbuf = req->req.buf + req->req.actual; if (!urb->num_sgs) { @@ -1409,7 +1409,7 @@ top: /* FIXME update emulated data toggle too */ - to_host = usb_pipein(urb->pipe); + to_host = usb_urb_dir_in(urb); if (unlikely(len == 0)) is_short = 1; else { @@ -1830,7 +1830,7 @@ restart: /* find the gadget's ep for this request (if configured) */ address = usb_pipeendpoint (urb->pipe); - if (usb_pipein(urb->pipe)) + if (usb_urb_dir_in(urb)) address |= USB_DIR_IN; ep = find_endpoint(dum, address); if (!ep) { @@ -2385,7 +2385,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) s = "?"; break; } s; }), - ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "", + ep, ep ? (usb_urb_dir_in(urb) ? "in" : "out") : "", ({ char *s; \ switch (usb_pipetype(urb->pipe)) { \ case PIPE_CONTROL: \ -- cgit From ad772c39b2fbb24ff6104d73d14fc125d8f02711 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:18 +0300 Subject: usb: typec: Copy everything from struct typec_capability during registration Copying everything from struct typec_capability to struct typec_port during port registration. This will make sure that under no circumstances the driver can change the values in the struct typec_capability that the port uses. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 94a3eda62add..7749933ffce5 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -52,6 +52,7 @@ struct typec_port { struct typec_switch *sw; struct typec_mux *mux; + const struct typec_capability *orig_cap; /* to be removed */ const struct typec_capability *cap; }; @@ -968,7 +969,7 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EINVAL; } - ret = port->cap->try_role(port->cap, role); + ret = port->cap->try_role(port->orig_cap, role); if (ret) return ret; @@ -1014,7 +1015,7 @@ static ssize_t data_role_store(struct device *dev, goto unlock_and_ret; } - ret = port->cap->dr_set(port->cap, ret); + ret = port->cap->dr_set(port->orig_cap, ret); if (ret) goto unlock_and_ret; @@ -1071,7 +1072,7 @@ static ssize_t power_role_store(struct device *dev, goto unlock_and_ret; } - ret = port->cap->pr_set(port->cap, ret); + ret = port->cap->pr_set(port->orig_cap, ret); if (ret) goto unlock_and_ret; @@ -1119,7 +1120,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, goto unlock_and_ret; } - ret = port->cap->port_type_set(port->cap, type); + ret = port->cap->port_type_set(port->orig_cap, type); if (ret) goto unlock_and_ret; @@ -1184,7 +1185,7 @@ static ssize_t vconn_source_store(struct device *dev, if (ret) return ret; - ret = port->cap->vconn_set(port->cap, (enum typec_role)source); + ret = port->cap->vconn_set(port->orig_cap, (enum typec_role)source); if (ret) return ret; @@ -1278,6 +1279,7 @@ static void typec_release(struct device *dev) ida_destroy(&port->mode_ids); typec_switch_put(port->sw); typec_mux_put(port->mux); + kfree(port->cap); kfree(port); } @@ -1579,7 +1581,7 @@ struct typec_port *typec_register_port(struct device *parent, mutex_init(&port->port_type_lock); port->id = id; - port->cap = cap; + port->orig_cap = cap; port->port_type = cap->type; port->prefer_role = cap->prefer_role; @@ -1590,6 +1592,12 @@ struct typec_port *typec_register_port(struct device *parent, port->dev.type = &typec_port_dev_type; dev_set_name(&port->dev, "port%d", id); + port->cap = kmemdup(cap, sizeof(*cap), GFP_KERNEL); + if (!port->cap) { + put_device(&port->dev); + return ERR_PTR(-ENOMEM); + } + port->sw = typec_switch_get(&port->dev); if (IS_ERR(port->sw)) { put_device(&port->dev); -- cgit From 8c127a42af89c39560a8c5bd5accadaaa5741f8c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:19 +0300 Subject: usb: typec: Introduce typec_get_drvdata() Leaving the private driver_data pointer of the port device to the port drivers. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 7749933ffce5..51154634c2c0 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1488,6 +1488,16 @@ EXPORT_SYMBOL_GPL(typec_set_mode); /* --------------------------------------- */ +/** + * typec_get_drvdata - Return private driver data pointer + * @port: USB Type-C port + */ +void *typec_get_drvdata(struct typec_port *port) +{ + return dev_get_drvdata(&port->dev); +} +EXPORT_SYMBOL_GPL(typec_get_drvdata); + /** * typec_port_register_altmode - Register USB Type-C Port Alternate Mode * @port: USB Type-C Port that supports the alternate mode @@ -1591,6 +1601,7 @@ struct typec_port *typec_register_port(struct device *parent, port->dev.fwnode = cap->fwnode; port->dev.type = &typec_port_dev_type; dev_set_name(&port->dev, "port%d", id); + dev_set_drvdata(&port->dev, cap->driver_data); port->cap = kmemdup(cap, sizeof(*cap), GFP_KERNEL); if (!port->cap) { -- cgit From 46310e4dade2bc3b574d540e421e3aa9f32cfd5f Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:20 +0300 Subject: usb: typec: Separate the operations vector Introducing struct typec_operations which has the same callbacks as struct typec_capability. The old callbacks are kept for now, but after all users have been converted, they will be removed. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-4-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 51154634c2c0..b934a006535a 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -54,6 +54,7 @@ struct typec_port { const struct typec_capability *orig_cap; /* to be removed */ const struct typec_capability *cap; + const struct typec_operations *ops; }; #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev) @@ -956,7 +957,7 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EOPNOTSUPP; } - if (!port->cap->try_role) { + if (!port->cap->try_role && (!port->ops || !port->ops->try_role)) { dev_dbg(dev, "Setting preferred role not supported\n"); return -EOPNOTSUPP; } @@ -969,7 +970,10 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EINVAL; } - ret = port->cap->try_role(port->orig_cap, role); + if (port->ops && port->ops->try_role) + ret = port->ops->try_role(port, role); + else + ret = port->cap->try_role(port->orig_cap, role); if (ret) return ret; @@ -1000,7 +1004,7 @@ static ssize_t data_role_store(struct device *dev, struct typec_port *port = to_typec_port(dev); int ret; - if (!port->cap->dr_set) { + if (!port->cap->dr_set && (!port->ops || !port->ops->dr_set)) { dev_dbg(dev, "data role swapping not supported\n"); return -EOPNOTSUPP; } @@ -1015,7 +1019,10 @@ static ssize_t data_role_store(struct device *dev, goto unlock_and_ret; } - ret = port->cap->dr_set(port->orig_cap, ret); + if (port->ops && port->ops->dr_set) + ret = port->ops->dr_set(port, ret); + else + ret = port->cap->dr_set(port->orig_cap, ret); if (ret) goto unlock_and_ret; @@ -1050,7 +1057,7 @@ static ssize_t power_role_store(struct device *dev, return -EOPNOTSUPP; } - if (!port->cap->pr_set) { + if (!port->cap->pr_set && (!port->ops || !port->ops->pr_set)) { dev_dbg(dev, "power role swapping not supported\n"); return -EOPNOTSUPP; } @@ -1072,7 +1079,10 @@ static ssize_t power_role_store(struct device *dev, goto unlock_and_ret; } - ret = port->cap->pr_set(port->orig_cap, ret); + if (port->ops && port->ops->dr_set) + ret = port->ops->pr_set(port, ret); + else + ret = port->cap->pr_set(port->orig_cap, ret); if (ret) goto unlock_and_ret; @@ -1103,7 +1113,8 @@ port_type_store(struct device *dev, struct device_attribute *attr, int ret; enum typec_port_type type; - if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) { + if (port->cap->type != TYPEC_PORT_DRP || (!port->cap->port_type_set && + (!port->ops || !port->ops->port_type_set))) { dev_dbg(dev, "changing port type not supported\n"); return -EOPNOTSUPP; } @@ -1120,7 +1131,10 @@ port_type_store(struct device *dev, struct device_attribute *attr, goto unlock_and_ret; } - ret = port->cap->port_type_set(port->orig_cap, type); + if (port->ops && port->ops->port_type_set) + ret = port->ops->port_type_set(port, type); + else + ret = port->cap->port_type_set(port->orig_cap, type); if (ret) goto unlock_and_ret; @@ -1176,7 +1190,7 @@ static ssize_t vconn_source_store(struct device *dev, return -EOPNOTSUPP; } - if (!port->cap->vconn_set) { + if (!port->cap->vconn_set && (!port->ops || !port->ops->vconn_set)) { dev_dbg(dev, "VCONN swapping not supported\n"); return -EOPNOTSUPP; } @@ -1185,7 +1199,11 @@ static ssize_t vconn_source_store(struct device *dev, if (ret) return ret; - ret = port->cap->vconn_set(port->orig_cap, (enum typec_role)source); + if (port->ops && port->ops->vconn_set) + ret = port->ops->vconn_set(port, (enum typec_role)source); + else + ret = port->cap->vconn_set(port->orig_cap, + (enum typec_role)source); if (ret) return ret; @@ -1591,6 +1609,7 @@ struct typec_port *typec_register_port(struct device *parent, mutex_init(&port->port_type_lock); port->id = id; + port->ops = cap->ops; port->orig_cap = cap; port->port_type = cap->type; port->prefer_role = cap->prefer_role; -- cgit From 00ec21e58dc63dd899a1e1cb3f1d44fa18ca22dd Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:21 +0300 Subject: usb: typec: tcpm: Start using struct typec_operations Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-5-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 45 +++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 25 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 5f61d9977a15..bc9edb4c013b 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -390,12 +390,6 @@ static enum tcpm_state tcpm_default_state(struct tcpm_port *port) return SRC_UNATTACHED; } -static inline -struct tcpm_port *typec_cap_to_tcpm(const struct typec_capability *cap) -{ - return container_of(cap, struct tcpm_port, typec_caps); -} - static bool tcpm_port_is_disconnected(struct tcpm_port *port) { return (!port->attached && port->cc1 == TYPEC_CC_OPEN && @@ -3970,10 +3964,9 @@ void tcpm_pd_hard_reset(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset); -static int tcpm_dr_set(const struct typec_capability *cap, - enum typec_data_role data) +static int tcpm_dr_set(struct typec_port *p, enum typec_data_role data) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); int ret; mutex_lock(&port->swap_lock); @@ -4038,10 +4031,9 @@ swap_unlock: return ret; } -static int tcpm_pr_set(const struct typec_capability *cap, - enum typec_role role) +static int tcpm_pr_set(struct typec_port *p, enum typec_role role) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); int ret; mutex_lock(&port->swap_lock); @@ -4082,10 +4074,9 @@ swap_unlock: return ret; } -static int tcpm_vconn_set(const struct typec_capability *cap, - enum typec_role role) +static int tcpm_vconn_set(struct typec_port *p, enum typec_role role) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); int ret; mutex_lock(&port->swap_lock); @@ -4122,9 +4113,9 @@ swap_unlock: return ret; } -static int tcpm_try_role(const struct typec_capability *cap, int role) +static int tcpm_try_role(struct typec_port *p, int role) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); struct tcpc_dev *tcpc = port->tcpc; int ret = 0; @@ -4331,10 +4322,9 @@ static void tcpm_init(struct tcpm_port *port) tcpm_set_state(port, PORT_RESET, 0); } -static int tcpm_port_type_set(const struct typec_capability *cap, - enum typec_port_type type) +static int tcpm_port_type_set(struct typec_port *p, enum typec_port_type type) { - struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpm_port *port = typec_get_drvdata(p); mutex_lock(&port->lock); if (type == port->port_type) @@ -4359,6 +4349,14 @@ port_unlock: return 0; } +static const struct typec_operations tcpm_ops = { + .try_role = tcpm_try_role, + .dr_set = tcpm_dr_set, + .pr_set = tcpm_pr_set, + .vconn_set = tcpm_vconn_set, + .port_type_set = tcpm_port_type_set +}; + void tcpm_tcpc_reset(struct tcpm_port *port) { mutex_lock(&port->lock); @@ -4772,11 +4770,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.fwnode = tcpc->fwnode; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ - port->typec_caps.dr_set = tcpm_dr_set; - port->typec_caps.pr_set = tcpm_pr_set; - port->typec_caps.vconn_set = tcpm_vconn_set; - port->typec_caps.try_role = tcpm_try_role; - port->typec_caps.port_type_set = tcpm_port_type_set; + port->typec_caps.driver_data = port; + port->typec_caps.ops = &tcpm_ops; port->partner_desc.identity = &port->partner_ident; port->port_type = port->typec_caps.type; -- cgit From 1007dda3be1c9f924746f892c094219be250928e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:22 +0300 Subject: usb: typec: tps6598x: Start using struct typec_operations Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. After this there is not need to keep the capabilities stored anywhere in the driver. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-6-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tps6598x.c | 49 +++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 23 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index a38d1409f15b..0698addd1185 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -94,7 +94,6 @@ struct tps6598x { struct typec_port *port; struct typec_partner *partner; struct usb_pd_identity partner_identity; - struct typec_capability typec_cap; }; /* @@ -307,11 +306,10 @@ static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, return 0; } -static int -tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role) +static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role) { - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); const char *cmd = (role == TYPEC_DEVICE) ? "SWUF" : "SWDF"; + struct tps6598x *tps = typec_get_drvdata(port); u32 status; int ret; @@ -338,11 +336,10 @@ out_unlock: return ret; } -static int -tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role) +static int tps6598x_pr_set(struct typec_port *port, enum typec_role role) { - struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); const char *cmd = (role == TYPEC_SINK) ? "SWSk" : "SWSr"; + struct tps6598x *tps = typec_get_drvdata(port); u32 status; int ret; @@ -369,6 +366,11 @@ out_unlock: return ret; } +static const struct typec_operations tps6598x_ops = { + .dr_set = tps6598x_dr_set, + .pr_set = tps6598x_pr_set, +}; + static irqreturn_t tps6598x_interrupt(int irq, void *data) { struct tps6598x *tps = data; @@ -448,6 +450,7 @@ static const struct regmap_config tps6598x_regmap_config = { static int tps6598x_probe(struct i2c_client *client) { + struct typec_capability typec_cap = { }; struct tps6598x *tps; u32 status; u32 conf; @@ -492,40 +495,40 @@ static int tps6598x_probe(struct i2c_client *client) if (ret < 0) return ret; - tps->typec_cap.revision = USB_TYPEC_REV_1_2; - tps->typec_cap.pd_revision = 0x200; - tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; - tps->typec_cap.pr_set = tps6598x_pr_set; - tps->typec_cap.dr_set = tps6598x_dr_set; + typec_cap.revision = USB_TYPEC_REV_1_2; + typec_cap.pd_revision = 0x200; + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + typec_cap.driver_data = tps; + typec_cap.ops = &tps6598x_ops; switch (TPS_SYSCONF_PORTINFO(conf)) { case TPS_PORTINFO_SINK_ACCESSORY: case TPS_PORTINFO_SINK: - tps->typec_cap.type = TYPEC_PORT_SNK; - tps->typec_cap.data = TYPEC_PORT_UFP; + typec_cap.type = TYPEC_PORT_SNK; + typec_cap.data = TYPEC_PORT_UFP; break; case TPS_PORTINFO_DRP_UFP_DRD: case TPS_PORTINFO_DRP_DFP_DRD: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_DRD; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_DRD; break; case TPS_PORTINFO_DRP_UFP: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_UFP; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_UFP; break; case TPS_PORTINFO_DRP_DFP: - tps->typec_cap.type = TYPEC_PORT_DRP; - tps->typec_cap.data = TYPEC_PORT_DFP; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_DFP; break; case TPS_PORTINFO_SOURCE: - tps->typec_cap.type = TYPEC_PORT_SRC; - tps->typec_cap.data = TYPEC_PORT_DFP; + typec_cap.type = TYPEC_PORT_SRC; + typec_cap.data = TYPEC_PORT_DFP; break; default: return -ENODEV; } - tps->port = typec_register_port(&client->dev, &tps->typec_cap); + tps->port = typec_register_port(&client->dev, &typec_cap); if (IS_ERR(tps->port)) return PTR_ERR(tps->port); -- cgit From 6df475f804e62887f5d2132832ecabe6e596cd1c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:23 +0300 Subject: usb: typec: ucsi: Start using struct typec_operations Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-7-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index ba288b964dc8..edd722fb88b8 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -17,9 +17,6 @@ #include "ucsi.h" #include "trace.h" -#define to_ucsi_connector(_cap_) container_of(_cap_, struct ucsi_connector, \ - typec_cap) - /* * UCSI_TIMEOUT_MS - PPM communication timeout * @@ -713,10 +710,9 @@ static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) return ret; } -static int -ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role) +static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) { - struct ucsi_connector *con = to_ucsi_connector(cap); + struct ucsi_connector *con = typec_get_drvdata(port); struct ucsi_control ctrl; int ret = 0; @@ -748,10 +744,9 @@ out_unlock: return ret < 0 ? ret : 0; } -static int -ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role) +static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) { - struct ucsi_connector *con = to_ucsi_connector(cap); + struct ucsi_connector *con = typec_get_drvdata(port); struct ucsi_control ctrl; int ret = 0; @@ -788,6 +783,11 @@ out_unlock: return ret; } +static const struct typec_operations ucsi_ops = { + .dr_set = ucsi_dr_swap, + .pr_set = ucsi_pr_swap +}; + static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) { struct fwnode_handle *fwnode; @@ -843,8 +843,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) *accessory = TYPEC_ACCESSORY_DEBUG; cap->fwnode = ucsi_find_fwnode(con); - cap->dr_set = ucsi_dr_swap; - cap->pr_set = ucsi_pr_swap; + cap->driver_data = con; + cap->ops = &ucsi_ops; /* Register the connector */ con->port = typec_register_port(ucsi->dev, cap); -- cgit From 642b1017dc4371bff291964186c1dfbb8f29ee25 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:24 +0300 Subject: usb: typec: hd3ss3220: Start using struct typec_operations Supplying the operation callbacks as part of a struct typec_operations instead of as part of struct typec_capability during port registration. After this there is not need to keep the capabilities stored anywhere in the driver. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-8-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/hd3ss3220.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index 8afaf5768a17..db09fa0d85f2 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -37,7 +37,6 @@ struct hd3ss3220 { struct regmap *regmap; struct usb_role_switch *role_sw; struct typec_port *port; - struct typec_capability typec_cap; }; static int hd3ss3220_set_source_pref(struct hd3ss3220 *hd3ss3220, int src_pref) @@ -73,11 +72,9 @@ static enum usb_role hd3ss3220_get_attached_state(struct hd3ss3220 *hd3ss3220) return attached_state; } -static int hd3ss3220_dr_set(const struct typec_capability *cap, - enum typec_data_role role) +static int hd3ss3220_dr_set(struct typec_port *port, enum typec_data_role role) { - struct hd3ss3220 *hd3ss3220 = container_of(cap, struct hd3ss3220, - typec_cap); + struct hd3ss3220 *hd3ss3220 = typec_get_drvdata(port); enum usb_role role_val; int pref, ret = 0; @@ -98,6 +95,10 @@ static int hd3ss3220_dr_set(const struct typec_capability *cap, return ret; } +static const struct typec_operations hd3ss3220_ops = { + .dr_set = hd3ss3220_dr_set +}; + static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220) { enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220); @@ -152,6 +153,7 @@ static const struct regmap_config config = { static int hd3ss3220_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct typec_capability typec_cap = { }; struct hd3ss3220 *hd3ss3220; struct fwnode_handle *connector; int ret; @@ -180,13 +182,13 @@ static int hd3ss3220_probe(struct i2c_client *client, if (IS_ERR(hd3ss3220->role_sw)) return PTR_ERR(hd3ss3220->role_sw); - hd3ss3220->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; - hd3ss3220->typec_cap.dr_set = hd3ss3220_dr_set; - hd3ss3220->typec_cap.type = TYPEC_PORT_DRP; - hd3ss3220->typec_cap.data = TYPEC_PORT_DRD; + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + typec_cap.driver_data = hd3ss3220; + typec_cap.type = TYPEC_PORT_DRP; + typec_cap.data = TYPEC_PORT_DRD; + typec_cap.ops = &hd3ss3220_ops; - hd3ss3220->port = typec_register_port(&client->dev, - &hd3ss3220->typec_cap); + hd3ss3220->port = typec_register_port(&client->dev, &typec_cap); if (IS_ERR(hd3ss3220->port)) { ret = PTR_ERR(hd3ss3220->port); goto err_put_role; -- cgit From 8c038ea8b65fc803cd35423b8a1ff7057dd52f8b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:25 +0300 Subject: usb: typec: Remove the callback members from struct typec_capability There are no more users for them. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-9-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index b934a006535a..7ece6ca6e690 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -52,7 +52,6 @@ struct typec_port { struct typec_switch *sw; struct typec_mux *mux; - const struct typec_capability *orig_cap; /* to be removed */ const struct typec_capability *cap; const struct typec_operations *ops; }; @@ -957,7 +956,7 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EOPNOTSUPP; } - if (!port->cap->try_role && (!port->ops || !port->ops->try_role)) { + if (!port->ops || !port->ops->try_role) { dev_dbg(dev, "Setting preferred role not supported\n"); return -EOPNOTSUPP; } @@ -970,10 +969,7 @@ preferred_role_store(struct device *dev, struct device_attribute *attr, return -EINVAL; } - if (port->ops && port->ops->try_role) - ret = port->ops->try_role(port, role); - else - ret = port->cap->try_role(port->orig_cap, role); + ret = port->ops->try_role(port, role); if (ret) return ret; @@ -1004,7 +1000,7 @@ static ssize_t data_role_store(struct device *dev, struct typec_port *port = to_typec_port(dev); int ret; - if (!port->cap->dr_set && (!port->ops || !port->ops->dr_set)) { + if (!port->ops || !port->ops->dr_set) { dev_dbg(dev, "data role swapping not supported\n"); return -EOPNOTSUPP; } @@ -1019,10 +1015,7 @@ static ssize_t data_role_store(struct device *dev, goto unlock_and_ret; } - if (port->ops && port->ops->dr_set) - ret = port->ops->dr_set(port, ret); - else - ret = port->cap->dr_set(port->orig_cap, ret); + ret = port->ops->dr_set(port, ret); if (ret) goto unlock_and_ret; @@ -1057,7 +1050,7 @@ static ssize_t power_role_store(struct device *dev, return -EOPNOTSUPP; } - if (!port->cap->pr_set && (!port->ops || !port->ops->pr_set)) { + if (!port->ops || !port->ops->pr_set) { dev_dbg(dev, "power role swapping not supported\n"); return -EOPNOTSUPP; } @@ -1079,10 +1072,7 @@ static ssize_t power_role_store(struct device *dev, goto unlock_and_ret; } - if (port->ops && port->ops->dr_set) - ret = port->ops->pr_set(port, ret); - else - ret = port->cap->pr_set(port->orig_cap, ret); + ret = port->ops->pr_set(port, ret); if (ret) goto unlock_and_ret; @@ -1113,8 +1103,8 @@ port_type_store(struct device *dev, struct device_attribute *attr, int ret; enum typec_port_type type; - if (port->cap->type != TYPEC_PORT_DRP || (!port->cap->port_type_set && - (!port->ops || !port->ops->port_type_set))) { + if (port->cap->type != TYPEC_PORT_DRP || + !port->ops || !port->ops->port_type_set) { dev_dbg(dev, "changing port type not supported\n"); return -EOPNOTSUPP; } @@ -1131,10 +1121,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, goto unlock_and_ret; } - if (port->ops && port->ops->port_type_set) - ret = port->ops->port_type_set(port, type); - else - ret = port->cap->port_type_set(port->orig_cap, type); + ret = port->ops->port_type_set(port, type); if (ret) goto unlock_and_ret; @@ -1190,7 +1177,7 @@ static ssize_t vconn_source_store(struct device *dev, return -EOPNOTSUPP; } - if (!port->cap->vconn_set && (!port->ops || !port->ops->vconn_set)) { + if (!port->ops || !port->ops->vconn_set) { dev_dbg(dev, "VCONN swapping not supported\n"); return -EOPNOTSUPP; } @@ -1199,11 +1186,7 @@ static ssize_t vconn_source_store(struct device *dev, if (ret) return ret; - if (port->ops && port->ops->vconn_set) - ret = port->ops->vconn_set(port, (enum typec_role)source); - else - ret = port->cap->vconn_set(port->orig_cap, - (enum typec_role)source); + ret = port->ops->vconn_set(port, (enum typec_role)source); if (ret) return ret; @@ -1610,7 +1593,6 @@ struct typec_port *typec_register_port(struct device *parent, port->id = id; port->ops = cap->ops; - port->orig_cap = cap; port->port_type = cap->type; port->prefer_role = cap->prefer_role; -- cgit From 24dab5380512874e323629350dd07defff09f1a7 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:27 +0300 Subject: usb: typec: hd3ss3220: Give the connector fwnode to the port device The driver already finds the node in order to get reference to the USB role switch. Signed-off-by: Heikki Krogerus Tested-by: Biju Das Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-11-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/hd3ss3220.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index db09fa0d85f2..323dfa8160ab 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -178,15 +178,17 @@ static int hd3ss3220_probe(struct i2c_client *client, return -ENODEV; hd3ss3220->role_sw = fwnode_usb_role_switch_get(connector); - fwnode_handle_put(connector); - if (IS_ERR(hd3ss3220->role_sw)) - return PTR_ERR(hd3ss3220->role_sw); + if (IS_ERR(hd3ss3220->role_sw)) { + ret = PTR_ERR(hd3ss3220->role_sw); + goto err_put_fwnode; + } typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; typec_cap.driver_data = hd3ss3220; typec_cap.type = TYPEC_PORT_DRP; typec_cap.data = TYPEC_PORT_DRD; typec_cap.ops = &hd3ss3220_ops; + typec_cap.fwnode = connector; hd3ss3220->port = typec_register_port(&client->dev, &typec_cap); if (IS_ERR(hd3ss3220->port)) { @@ -220,6 +222,8 @@ static int hd3ss3220_probe(struct i2c_client *client, if (ret < 0) goto err_unreg_port; + fwnode_handle_put(connector); + dev_info(&client->dev, "probed revision=0x%x\n", ret); return 0; @@ -227,6 +231,8 @@ err_unreg_port: typec_unregister_port(hd3ss3220->port); err_put_role: usb_role_switch_put(hd3ss3220->role_sw); +err_put_fwnode: + fwnode_handle_put(connector); return ret; } -- cgit From bdc62f2bae8fb0e8e99574de5232f0a3c54a27df Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:28 +0300 Subject: usb: typec: ucsi: Simplified registration and I/O API Adding more simplified API for interface registration and read and write operations. The registration is split into separate creation and registration phases. That allows the drivers to properly initialize the interface before registering it if necessary. The read and write operations are supplied in a completely separate struct ucsi_operations that is passed to the ucsi_register() function during registration. The new read and write operations will work more traditionally so that the read callback function reads a requested amount of data from an offset, and the write callback functions write the given data to the offset. The drivers will have to support both non-blocking writing and blocking writing. In blocking writing the driver itself is responsible of waiting for the completion event. The new API makes it possible for the drivers to perform tasks also independently of the core ucsi.c, and that should allow for example quirks to be handled completely in the drivers without the need to touch ucsi.c. The old API is kept until all drivers have been converted to the new API. Signed-off-by: Heikki Krogerus Tested-by: Ajay Gupta Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-12-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 330 ++++++++++++++++++++++++++++++++++++++---- drivers/usb/typec/ucsi/ucsi.h | 58 ++++++++ 2 files changed, 359 insertions(+), 29 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index edd722fb88b8..dffc2cf8db6f 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -98,6 +98,101 @@ out_clear_bit: return ret; } +static int ucsi_acknowledge_command(struct ucsi *ucsi) +{ + u64 ctrl; + + ctrl = UCSI_ACK_CC_CI; + ctrl |= UCSI_ACK_COMMAND_COMPLETE; + + return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl)); +} + +static int ucsi_acknowledge_connector_change(struct ucsi *ucsi) +{ + u64 ctrl; + + ctrl = UCSI_ACK_CC_CI; + ctrl |= UCSI_ACK_CONNECTOR_CHANGE; + + return ucsi->ops->async_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl)); +} + +static int ucsi_exec_command(struct ucsi *ucsi, u64 command); + +static int ucsi_read_error(struct ucsi *ucsi) +{ + u16 error; + int ret; + + /* Acknowlege the command that failed */ + ret = ucsi_acknowledge_command(ucsi); + if (ret) + return ret; + + ret = ucsi_exec_command(ucsi, UCSI_GET_ERROR_STATUS); + if (ret < 0) + return ret; + + ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, &error, sizeof(error)); + if (ret) + return ret; + + switch (error) { + case UCSI_ERROR_INCOMPATIBLE_PARTNER: + return -EOPNOTSUPP; + case UCSI_ERROR_CC_COMMUNICATION_ERR: + return -ECOMM; + case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL: + return -EPROTO; + case UCSI_ERROR_DEAD_BATTERY: + dev_warn(ucsi->dev, "Dead battery condition!\n"); + return -EPERM; + /* The following mean a bug in this driver */ + case UCSI_ERROR_INVALID_CON_NUM: + case UCSI_ERROR_UNREGONIZED_CMD: + case UCSI_ERROR_INVALID_CMD_ARGUMENT: + dev_err(ucsi->dev, "possible UCSI driver bug (0x%x)\n", error); + return -EINVAL; + default: + dev_err(ucsi->dev, "%s: error without status\n", __func__); + return -EIO; + } + + return 0; +} + +static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd) +{ + u32 cci; + int ret; + + ret = ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd)); + if (ret) + return ret; + + ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci)); + if (ret) + return ret; + + if (cci & UCSI_CCI_BUSY) + return -EBUSY; + + if (!(cci & UCSI_CCI_COMMAND_COMPLETE)) + return -EIO; + + if (cci & UCSI_CCI_NOT_SUPPORTED) + return -EOPNOTSUPP; + + if (cci & UCSI_CCI_ERROR) { + if (cmd == UCSI_GET_ERROR_STATUS) + return -EIO; + return ucsi_read_error(ucsi); + } + + return UCSI_CCI_LENGTH(cci); +} + static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, void *data, size_t size) { @@ -106,6 +201,26 @@ static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, u16 error; int ret; + if (ucsi->ops) { + ret = ucsi_exec_command(ucsi, ctrl->raw_cmd); + if (ret < 0) + return ret; + + data_length = ret; + + if (data) { + ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, data, size); + if (ret) + return ret; + } + + ret = ucsi_acknowledge_command(ucsi); + if (ret) + return ret; + + return data_length; + } + ret = ucsi_command(ucsi, ctrl); if (ret) goto err; @@ -518,7 +633,7 @@ static void ucsi_partner_change(struct ucsi_connector *con) ucsi_altmode_update_active(con); } -static void ucsi_connector_change(struct work_struct *work) +static void ucsi_handle_connector_change(struct work_struct *work) { struct ucsi_connector *con = container_of(work, struct ucsi_connector, work); @@ -580,7 +695,10 @@ static void ucsi_connector_change(struct work_struct *work) if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) ucsi_partner_change(con); - ret = ucsi_ack(ucsi, UCSI_ACK_EVENT); + if (ucsi->ops) + ret = ucsi_acknowledge_connector_change(ucsi); + else + ret = ucsi_ack(ucsi, UCSI_ACK_EVENT); if (ret) dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); @@ -591,6 +709,20 @@ out_unlock: mutex_unlock(&con->lock); } +/** + * ucsi_connector_change - Process Connector Change Event + * @ucsi: UCSI Interface + * @num: Connector number + */ +void ucsi_connector_change(struct ucsi *ucsi, u8 num) +{ + struct ucsi_connector *con = &ucsi->connector[num - 1]; + + if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) + schedule_work(&con->work); +} +EXPORT_SYMBOL_GPL(ucsi_connector_change); + /** * ucsi_notify - PPM notification handler * @ucsi: Source UCSI Interface for the notifications @@ -647,6 +779,40 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) unsigned long tmo; int ret; + if (ucsi->ops) { + u64 command = UCSI_PPM_RESET; + u32 cci; + + ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command, + sizeof(command)); + if (ret < 0) + return ret; + + tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS); + + do { + if (time_is_before_jiffies(tmo)) + return -ETIMEDOUT; + + ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci)); + if (ret) + return ret; + + /* If the PPM is still doing something else, reset it again. */ + if (cci & ~UCSI_CCI_RESET_COMPLETE) { + ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, + &command, + sizeof(command)); + if (ret < 0) + return ret; + } + + msleep(20); + } while (!(cci & UCSI_CCI_RESET_COMPLETE)); + + return 0; + } + ctrl.raw_cmd = 0; ctrl.cmd.cmd = UCSI_PPM_RESET; trace_ucsi_command(&ctrl); @@ -807,7 +973,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) struct ucsi_control ctrl; int ret; - INIT_WORK(&con->work, ucsi_connector_change); + INIT_WORK(&con->work, ucsi_handle_connector_change); init_completion(&con->complete); mutex_init(&con->lock); con->num = index + 1; @@ -898,9 +1064,14 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) return 0; } -static void ucsi_init(struct work_struct *work) +/** + * ucsi_init - Initialize UCSI interface + * @ucsi: UCSI to be initialized + * + * Registers all ports @ucsi has and enables all notification events. + */ +int ucsi_init(struct ucsi *ucsi) { - struct ucsi *ucsi = container_of(work, struct ucsi, work); struct ucsi_connector *con; struct ucsi_control ctrl; int ret; @@ -956,7 +1127,7 @@ static void ucsi_init(struct work_struct *work) mutex_unlock(&ucsi->ppm_lock); - return; + return 0; err_unregister: for (con = ucsi->connector; con->port; con++) { @@ -970,49 +1141,106 @@ err_reset: ucsi_reset_ppm(ucsi); err: mutex_unlock(&ucsi->ppm_lock); - dev_err(ucsi->dev, "PPM init failed (%d)\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(ucsi_init); + +static void ucsi_init_work(struct work_struct *work) +{ + struct ucsi *ucsi = container_of(work, struct ucsi, work); + int ret; + + ret = ucsi_init(ucsi); + if (ret) + dev_err(ucsi->dev, "PPM init failed (%d)\n", ret); } /** - * ucsi_register_ppm - Register UCSI PPM Interface - * @dev: Device interface to the PPM - * @ppm: The PPM interface - * - * Allocates UCSI instance, associates it with @ppm and returns it to the - * caller, and schedules initialization of the interface. + * ucsi_get_drvdata - Return private driver data pointer + * @ucsi: UCSI interface */ -struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm) +void *ucsi_get_drvdata(struct ucsi *ucsi) +{ + return ucsi->driver_data; +} +EXPORT_SYMBOL_GPL(ucsi_get_drvdata); + +/** + * ucsi_get_drvdata - Assign private driver data pointer + * @ucsi: UCSI interface + * @data: Private data pointer + */ +void ucsi_set_drvdata(struct ucsi *ucsi, void *data) +{ + ucsi->driver_data = data; +} +EXPORT_SYMBOL_GPL(ucsi_set_drvdata); + +/** + * ucsi_create - Allocate UCSI instance + * @dev: Device interface to the PPM (Platform Policy Manager) + * @ops: I/O routines + */ +struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops) { struct ucsi *ucsi; + if (!ops || !ops->read || !ops->sync_write || !ops->async_write) + return ERR_PTR(-EINVAL); + ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL); if (!ucsi) return ERR_PTR(-ENOMEM); - INIT_WORK(&ucsi->work, ucsi_init); - init_completion(&ucsi->complete); + INIT_WORK(&ucsi->work, ucsi_init_work); mutex_init(&ucsi->ppm_lock); - ucsi->dev = dev; - ucsi->ppm = ppm; + ucsi->ops = ops; + + return ucsi; +} +EXPORT_SYMBOL_GPL(ucsi_create); + +/** + * ucsi_destroy - Free UCSI instance + * @ucsi: UCSI instance to be freed + */ +void ucsi_destroy(struct ucsi *ucsi) +{ + kfree(ucsi); +} +EXPORT_SYMBOL_GPL(ucsi_destroy); + +/** + * ucsi_register - Register UCSI interface + * @ucsi: UCSI instance + */ +int ucsi_register(struct ucsi *ucsi) +{ + int ret; + + ret = ucsi->ops->read(ucsi, UCSI_VERSION, &ucsi->version, + sizeof(ucsi->version)); + if (ret) + return ret; + + if (!ucsi->version) + return -ENODEV; - /* - * Communication with the PPM takes a lot of time. It is not reasonable - * to initialize the driver here. Using a work for now. - */ queue_work(system_long_wq, &ucsi->work); - return ucsi; + return 0; } -EXPORT_SYMBOL_GPL(ucsi_register_ppm); +EXPORT_SYMBOL_GPL(ucsi_register); /** - * ucsi_unregister_ppm - Unregister UCSI PPM Interface - * @ucsi: struct ucsi associated with the PPM + * ucsi_unregister - Unregister UCSI interface + * @ucsi: UCSI interface to be unregistered * - * Unregister UCSI PPM that was created with ucsi_register(). + * Unregister UCSI interface that was created with ucsi_register(). */ -void ucsi_unregister_ppm(struct ucsi *ucsi) +void ucsi_unregister(struct ucsi *ucsi) { struct ucsi_control ctrl; int i; @@ -1035,7 +1263,51 @@ void ucsi_unregister_ppm(struct ucsi *ucsi) ucsi_reset_ppm(ucsi); kfree(ucsi->connector); - kfree(ucsi); +} +EXPORT_SYMBOL_GPL(ucsi_unregister); + +/** + * ucsi_register_ppm - Register UCSI PPM Interface + * @dev: Device interface to the PPM + * @ppm: The PPM interface + * + * Allocates UCSI instance, associates it with @ppm and returns it to the + * caller, and schedules initialization of the interface. + */ +struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm) +{ + struct ucsi *ucsi; + + ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL); + if (!ucsi) + return ERR_PTR(-ENOMEM); + + INIT_WORK(&ucsi->work, ucsi_init_work); + init_completion(&ucsi->complete); + mutex_init(&ucsi->ppm_lock); + + ucsi->dev = dev; + ucsi->ppm = ppm; + + /* + * Communication with the PPM takes a lot of time. It is not reasonable + * to initialize the driver here. Using a work for now. + */ + queue_work(system_long_wq, &ucsi->work); + + return ucsi; +} +EXPORT_SYMBOL_GPL(ucsi_register_ppm); + +/** + * ucsi_unregister_ppm - Unregister UCSI PPM Interface + * @ucsi: struct ucsi associated with the PPM + * + * Unregister UCSI PPM that was created with ucsi_register(). + */ +void ucsi_unregister_ppm(struct ucsi *ucsi) +{ + ucsi_unregister(ucsi); } EXPORT_SYMBOL_GPL(ucsi_unregister_ppm); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index de87d0b8319d..d8a8e8f2f912 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -10,6 +10,56 @@ /* -------------------------------------------------------------------------- */ +struct ucsi; + +/* UCSI offsets (Bytes) */ +#define UCSI_VERSION 0 +#define UCSI_CCI 4 +#define UCSI_CONTROL 8 +#define UCSI_MESSAGE_IN 16 +#define UCSI_MESSAGE_OUT 32 + +/* Command Status and Connector Change Indication (CCI) bits */ +#define UCSI_CCI_CONNECTOR(_c_) (((_c_) & GENMASK(7, 0)) >> 1) +#define UCSI_CCI_LENGTH(_c_) (((_c_) & GENMASK(15, 8)) >> 8) +#define UCSI_CCI_NOT_SUPPORTED BIT(25) +#define UCSI_CCI_CANCEL_COMPLETE BIT(26) +#define UCSI_CCI_RESET_COMPLETE BIT(27) +#define UCSI_CCI_BUSY BIT(28) +#define UCSI_CCI_ACK_COMPLETE BIT(29) +#define UCSI_CCI_ERROR BIT(30) +#define UCSI_CCI_COMMAND_COMPLETE BIT(31) + +/** + * struct ucsi_operations - UCSI I/O operations + * @read: Read operation + * @sync_write: Blocking write operation + * @async_write: Non-blocking write operation + * + * Read and write routines for UCSI interface. @sync_write must wait for the + * Command Completion Event from the PPM before returning, and @async_write must + * return immediately after sending the data to the PPM. + */ +struct ucsi_operations { + int (*read)(struct ucsi *ucsi, unsigned int offset, + void *val, size_t val_len); + int (*sync_write)(struct ucsi *ucsi, unsigned int offset, + const void *val, size_t val_len); + int (*async_write)(struct ucsi *ucsi, unsigned int offset, + const void *val, size_t val_len); +}; + +struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops); +void ucsi_destroy(struct ucsi *ucsi); +int ucsi_register(struct ucsi *ucsi); +void ucsi_unregister(struct ucsi *ucsi); +void *ucsi_get_drvdata(struct ucsi *ucsi); +void ucsi_set_drvdata(struct ucsi *ucsi, void *data); + +void ucsi_connector_change(struct ucsi *ucsi, u8 num); + +/* -------------------------------------------------------------------------- */ + /* Command Status and Connector Change Indication (CCI) data structure */ struct ucsi_cci { u8:1; /* reserved */ @@ -207,6 +257,10 @@ struct ucsi_control { #define UCSI_ACK_EVENT 1 #define UCSI_ACK_CMD 2 +/* Bits for ACK CC or CI */ +#define UCSI_ACK_CONNECTOR_CHANGE BIT(16) +#define UCSI_ACK_COMMAND_COMPLETE BIT(17) + /* Bits for SET_NOTIFICATION_ENABLE command */ #define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(0) #define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(1) @@ -383,8 +437,12 @@ enum ucsi_status { }; struct ucsi { + u16 version; struct device *dev; struct ucsi_ppm *ppm; + struct driver_data *driver_data; + + const struct ucsi_operations *ops; enum ucsi_status status; struct completion complete; -- cgit From f56de278e8ec963637995944156527162dcf3021 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:29 +0300 Subject: usb: typec: ucsi: acpi: Move to the new API Replacing the old "cmd" and "sync" callbacks with an implementation of struct ucsi_operations. The ACPI notification (interrupt) handler will from now on read the CCI (Command Status and Connector Change Indication) register, and call ucsi_connector_change() function and/or complete pending command completions based on it. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-13-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_acpi.c | 91 ++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 19 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index a18112a83fae..3f1786170098 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -19,7 +19,9 @@ struct ucsi_acpi { struct device *dev; struct ucsi *ucsi; - struct ucsi_ppm ppm; + void __iomem *base; + struct completion complete; + unsigned long flags; guid_t guid; }; @@ -39,27 +41,73 @@ static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func) return 0; } -static int ucsi_acpi_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl) +static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset, + void *val, size_t val_len) { - struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm); + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + int ret; + + ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); + if (ret) + return ret; + + memcpy(val, (const void __force *)(ua->base + offset), val_len); + + return 0; +} + +static int ucsi_acpi_async_write(struct ucsi *ucsi, unsigned int offset, + const void *val, size_t val_len) +{ + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - ppm->data->ctrl.raw_cmd = ctrl->raw_cmd; + memcpy((void __force *)(ua->base + offset), val, val_len); return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE); } -static int ucsi_acpi_sync(struct ucsi_ppm *ppm) +static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset, + const void *val, size_t val_len) { - struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm); + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + int ret; + + set_bit(COMMAND_PENDING, &ua->flags); + + ret = ucsi_acpi_async_write(ucsi, offset, val, val_len); + if (ret) + goto out_clear_bit; - return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); + if (!wait_for_completion_timeout(&ua->complete, msecs_to_jiffies(5000))) + ret = -ETIMEDOUT; + +out_clear_bit: + clear_bit(COMMAND_PENDING, &ua->flags); + + return ret; } +static const struct ucsi_operations ucsi_acpi_ops = { + .read = ucsi_acpi_read, + .sync_write = ucsi_acpi_sync_write, + .async_write = ucsi_acpi_async_write +}; + static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data) { struct ucsi_acpi *ua = data; + u32 cci; + int ret; + + ret = ucsi_acpi_read(ua->ucsi, UCSI_CCI, &cci, sizeof(cci)); + if (ret) + return; - ucsi_notify(ua->ucsi); + if (test_bit(COMMAND_PENDING, &ua->flags) && + cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) + complete(&ua->complete); + else if (UCSI_CCI_CONNECTOR(cci)) + ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci)); } static int ucsi_acpi_probe(struct platform_device *pdev) @@ -90,35 +138,39 @@ static int ucsi_acpi_probe(struct platform_device *pdev) * it can not be requested here, and we can not use * devm_ioremap_resource(). */ - ua->ppm.data = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!ua->ppm.data) + ua->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!ua->base) return -ENOMEM; - if (!ua->ppm.data->version) - return -ENODEV; - ret = guid_parse(UCSI_DSM_UUID, &ua->guid); if (ret) return ret; - ua->ppm.cmd = ucsi_acpi_cmd; - ua->ppm.sync = ucsi_acpi_sync; + init_completion(&ua->complete); ua->dev = &pdev->dev; + ua->ucsi = ucsi_create(&pdev->dev, &ucsi_acpi_ops); + if (IS_ERR(ua->ucsi)) + return PTR_ERR(ua->ucsi); + + ucsi_set_drvdata(ua->ucsi, ua); + status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY, ucsi_acpi_notify, ua); if (ACPI_FAILURE(status)) { dev_err(&pdev->dev, "failed to install notify handler\n"); + ucsi_destroy(ua->ucsi); return -ENODEV; } - ua->ucsi = ucsi_register_ppm(&pdev->dev, &ua->ppm); - if (IS_ERR(ua->ucsi)) { + ret = ucsi_register(ua->ucsi); + if (ret) { acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY, ucsi_acpi_notify); - return PTR_ERR(ua->ucsi); + ucsi_destroy(ua->ucsi); + return ret; } platform_set_drvdata(pdev, ua); @@ -130,7 +182,8 @@ static int ucsi_acpi_remove(struct platform_device *pdev) { struct ucsi_acpi *ua = platform_get_drvdata(pdev); - ucsi_unregister_ppm(ua->ucsi); + ucsi_unregister(ua->ucsi); + ucsi_destroy(ua->ucsi); acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY, ucsi_acpi_notify); -- cgit From e32fd989ac1c45f993fbe89ad0a89aa9ea6993d2 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:30 +0300 Subject: usb: typec: ucsi: ccg: Move to the new API Replacing the old "cmd" and "sync" callbacks with an implementation of struct ucsi_operations. The interrupt handler will from now on read the CCI (Command Status and Connector Change Indication) register, and call ucsi_connector_change() function and/or complete pending command completions based on it. Signed-off-by: Heikki Krogerus Tested-by: Ajay Gupta Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-14-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_ccg.c | 166 +++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 85 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index d772fce51905..3370b3fc37b1 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -176,8 +176,8 @@ struct ccg_resp { struct ucsi_ccg { struct device *dev; struct ucsi *ucsi; - struct ucsi_ppm ppm; struct i2c_client *client; + struct ccg_dev_info info; /* version info for boot, primary and secondary */ struct version_info version[FW2 + 1]; @@ -196,6 +196,8 @@ struct ucsi_ccg { /* fw build with vendor information */ u16 fw_build; struct work_struct pm_work; + + struct completion complete; }; static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) @@ -243,7 +245,7 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) return 0; } -static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) +static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len) { struct i2c_client *client = uc->client; unsigned char *buf; @@ -317,88 +319,85 @@ static int ucsi_ccg_init(struct ucsi_ccg *uc) return -ETIMEDOUT; } -static int ucsi_ccg_send_data(struct ucsi_ccg *uc) +static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset, + void *val, size_t val_len) { - u8 *ppm = (u8 *)uc->ppm.data; - int status; - u16 rab; + u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset); - rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out)); - status = ccg_write(uc, rab, ppm + - offsetof(struct ucsi_data, message_out), - sizeof(uc->ppm.data->message_out)); - if (status < 0) - return status; - - rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl)); - return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl), - sizeof(uc->ppm.data->ctrl)); + return ccg_read(ucsi_get_drvdata(ucsi), reg, val, val_len); } -static int ucsi_ccg_recv_data(struct ucsi_ccg *uc) +static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset, + const void *val, size_t val_len) { - u8 *ppm = (u8 *)uc->ppm.data; - int status; - u16 rab; + u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset); - rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci)); - status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci), - sizeof(uc->ppm.data->cci)); - if (status < 0) - return status; - - rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in)); - return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in), - sizeof(uc->ppm.data->message_in)); + return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len); } -static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc) +static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset, + const void *val, size_t val_len) { - int status; - unsigned char data; + struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); + int ret; - status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); - if (status < 0) - return status; + mutex_lock(&uc->lock); + pm_runtime_get_sync(uc->dev); + set_bit(DEV_CMD_PENDING, &uc->flags); - return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); -} + ret = ucsi_ccg_async_write(ucsi, offset, val, val_len); + if (ret) + goto err_clear_bit; -static int ucsi_ccg_sync(struct ucsi_ppm *ppm) -{ - struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); - int status; + if (!wait_for_completion_timeout(&uc->complete, msecs_to_jiffies(5000))) + ret = -ETIMEDOUT; - status = ucsi_ccg_recv_data(uc); - if (status < 0) - return status; +err_clear_bit: + clear_bit(DEV_CMD_PENDING, &uc->flags); + pm_runtime_put_sync(uc->dev); + mutex_unlock(&uc->lock); - /* ack interrupt to allow next command to run */ - return ucsi_ccg_ack_interrupt(uc); + return ret; } -static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl) -{ - struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); - - ppm->data->ctrl.raw_cmd = ctrl->raw_cmd; - return ucsi_ccg_send_data(uc); -} +static const struct ucsi_operations ucsi_ccg_ops = { + .read = ucsi_ccg_read, + .sync_write = ucsi_ccg_sync_write, + .async_write = ucsi_ccg_async_write +}; static irqreturn_t ccg_irq_handler(int irq, void *data) { + u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CCI); struct ucsi_ccg *uc = data; + u8 intr_reg; + u32 cci; + int ret; + + ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg)); + if (ret) + return ret; + + ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci)); + if (ret) + goto err_clear_irq; + + if (UCSI_CCI_CONNECTOR(cci)) + ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci)); + + if (test_bit(DEV_CMD_PENDING, &uc->flags) && + cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) + complete(&uc->complete); - ucsi_notify(uc->ucsi); +err_clear_irq: + ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg)); return IRQ_HANDLED; } static void ccg_pm_workaround_work(struct work_struct *pm_work) { - struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work); - - ucsi_notify(uc->ucsi); + ccg_irq_handler(0, container_of(pm_work, struct ucsi_ccg, pm_work)); } static int get_fw_info(struct ucsi_ccg *uc) @@ -1027,10 +1026,10 @@ static int ccg_restart(struct ucsi_ccg *uc) return status; } - uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); - if (IS_ERR(uc->ucsi)) { - dev_err(uc->dev, "ucsi_register_ppm failed\n"); - return PTR_ERR(uc->ucsi); + status = ucsi_register(uc->ucsi); + if (status) { + dev_err(uc->dev, "failed to register the interface\n"); + return status; } return 0; @@ -1047,7 +1046,7 @@ static void ccg_update_firmware(struct work_struct *work) return; if (flash_mode != FLASH_NOT_NEEDED) { - ucsi_unregister_ppm(uc->ucsi); + ucsi_unregister(uc->ucsi); free_irq(uc->irq, uc); ccg_fw_update(uc, flash_mode); @@ -1091,21 +1090,15 @@ static int ucsi_ccg_probe(struct i2c_client *client, struct device *dev = &client->dev; struct ucsi_ccg *uc; int status; - u16 rab; uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); if (!uc) return -ENOMEM; - uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL); - if (!uc->ppm.data) - return -ENOMEM; - - uc->ppm.cmd = ucsi_ccg_cmd; - uc->ppm.sync = ucsi_ccg_sync; uc->dev = dev; uc->client = client; mutex_init(&uc->lock); + init_completion(&uc->complete); INIT_WORK(&uc->work, ccg_update_firmware); INIT_WORK(&uc->pm_work, ccg_pm_workaround_work); @@ -1133,30 +1126,25 @@ static int ucsi_ccg_probe(struct i2c_client *client, if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK) uc->port_num++; + uc->ucsi = ucsi_create(dev, &ucsi_ccg_ops); + if (IS_ERR(uc->ucsi)) + return PTR_ERR(uc->ucsi); + + ucsi_set_drvdata(uc->ucsi, uc); + status = request_threaded_irq(client->irq, NULL, ccg_irq_handler, IRQF_ONESHOT | IRQF_TRIGGER_HIGH, dev_name(dev), uc); if (status < 0) { dev_err(uc->dev, "request_threaded_irq failed - %d\n", status); - return status; + goto out_ucsi_destroy; } uc->irq = client->irq; - uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); - if (IS_ERR(uc->ucsi)) { - dev_err(uc->dev, "ucsi_register_ppm failed\n"); - return PTR_ERR(uc->ucsi); - } - - rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version)); - status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) + - offsetof(struct ucsi_data, version), - sizeof(uc->ppm.data->version)); - if (status < 0) { - ucsi_unregister_ppm(uc->ucsi); - return status; - } + status = ucsi_register(uc->ucsi); + if (status) + goto out_free_irq; i2c_set_clientdata(client, uc); @@ -1167,6 +1155,13 @@ static int ucsi_ccg_probe(struct i2c_client *client, pm_runtime_idle(uc->dev); return 0; + +out_free_irq: + free_irq(uc->irq, uc); +out_ucsi_destroy: + ucsi_destroy(uc->ucsi); + + return status; } static int ucsi_ccg_remove(struct i2c_client *client) @@ -1175,8 +1170,9 @@ static int ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->pm_work); cancel_work_sync(&uc->work); - ucsi_unregister_ppm(uc->ucsi); pm_runtime_disable(uc->dev); + ucsi_unregister(uc->ucsi); + ucsi_destroy(uc->ucsi); free_irq(uc->irq, uc); return 0; -- cgit From 2ede55468ca8cc236da66579359c2c406d4c1cba Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:31 +0300 Subject: usb: typec: ucsi: Remove the old API The drivers now only use the new API, so removing the old one. Signed-off-by: Heikki Krogerus Tested-by: Ajay Gupta Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-15-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 24 ++- drivers/usb/typec/ucsi/trace.h | 17 -- drivers/usb/typec/ucsi/ucsi.c | 346 ++++------------------------------- drivers/usb/typec/ucsi/ucsi.h | 41 ----- 4 files changed, 43 insertions(+), 385 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index d99700cb4dca..47424935bc81 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -48,6 +48,7 @@ struct ucsi_dp { static int ucsi_displayport_enter(struct typec_altmode *alt) { struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); + struct ucsi *ucsi = dp->con->ucsi; struct ucsi_control ctrl; u8 cur = 0; int ret; @@ -59,25 +60,21 @@ static int ucsi_displayport_enter(struct typec_altmode *alt) dev_warn(&p->dev, "firmware doesn't support alternate mode overriding\n"); - mutex_unlock(&dp->con->lock); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto err_unlock; } UCSI_CMD_GET_CURRENT_CAM(ctrl, dp->con->num); - ret = ucsi_send_command(dp->con->ucsi, &ctrl, &cur, sizeof(cur)); + ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur)); if (ret < 0) { - if (dp->con->ucsi->ppm->data->version > 0x0100) { - mutex_unlock(&dp->con->lock); - return ret; - } + if (ucsi->version > 0x0100) + goto err_unlock; cur = 0xff; } if (cur != 0xff) { - mutex_unlock(&dp->con->lock); - if (dp->con->port_altmode[cur] == alt) - return 0; - return -EBUSY; + ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; + goto err_unlock; } /* @@ -94,10 +91,11 @@ static int ucsi_displayport_enter(struct typec_altmode *alt) dp->vdo_size = 1; schedule_work(&dp->work); - + ret = 0; +err_unlock: mutex_unlock(&dp->con->lock); - return 0; + return ret; } static int ucsi_displayport_exit(struct typec_altmode *alt) diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h index 783ec9c72055..6e3d510b236e 100644 --- a/drivers/usb/typec/ucsi/trace.h +++ b/drivers/usb/typec/ucsi/trace.h @@ -75,23 +75,6 @@ DEFINE_EVENT(ucsi_log_command, ucsi_reset_ppm, TP_ARGS(ctrl, ret) ); -DECLARE_EVENT_CLASS(ucsi_log_cci, - TP_PROTO(u32 cci), - TP_ARGS(cci), - TP_STRUCT__entry( - __field(u32, cci) - ), - TP_fast_assign( - __entry->cci = cci; - ), - TP_printk("CCI=%08x %s", __entry->cci, ucsi_cci_str(__entry->cci)) -); - -DEFINE_EVENT(ucsi_log_cci, ucsi_notify, - TP_PROTO(u32 cci), - TP_ARGS(cci) -); - DECLARE_EVENT_CLASS(ucsi_log_connector_status, TP_PROTO(int port, struct ucsi_connector_status *status), TP_ARGS(port, status), diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index dffc2cf8db6f..6462dadd7540 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -36,68 +36,6 @@ */ #define UCSI_SWAP_TIMEOUT_MS 5000 -static inline int ucsi_sync(struct ucsi *ucsi) -{ - if (ucsi->ppm && ucsi->ppm->sync) - return ucsi->ppm->sync(ucsi->ppm); - return 0; -} - -static int ucsi_command(struct ucsi *ucsi, struct ucsi_control *ctrl) -{ - int ret; - - trace_ucsi_command(ctrl); - - set_bit(COMMAND_PENDING, &ucsi->flags); - - ret = ucsi->ppm->cmd(ucsi->ppm, ctrl); - if (ret) - goto err_clear_flag; - - if (!wait_for_completion_timeout(&ucsi->complete, - msecs_to_jiffies(UCSI_TIMEOUT_MS))) { - dev_warn(ucsi->dev, "PPM NOT RESPONDING\n"); - ret = -ETIMEDOUT; - } - -err_clear_flag: - clear_bit(COMMAND_PENDING, &ucsi->flags); - - return ret; -} - -static int ucsi_ack(struct ucsi *ucsi, u8 ack) -{ - struct ucsi_control ctrl; - int ret; - - trace_ucsi_ack(ack); - - set_bit(ACK_PENDING, &ucsi->flags); - - UCSI_CMD_ACK(ctrl, ack); - ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl); - if (ret) - goto out_clear_bit; - - /* Waiting for ACK with ACK CMD, but not with EVENT for now */ - if (ack == UCSI_ACK_EVENT) - goto out_clear_bit; - - if (!wait_for_completion_timeout(&ucsi->complete, - msecs_to_jiffies(UCSI_TIMEOUT_MS))) - ret = -ETIMEDOUT; - -out_clear_bit: - clear_bit(ACK_PENDING, &ucsi->flags); - - if (ret) - dev_err(ucsi->dev, "%s: failed\n", __func__); - - return ret; -} - static int ucsi_acknowledge_command(struct ucsi *ucsi) { u64 ctrl; @@ -196,115 +134,26 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd) static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, void *data, size_t size) { - struct ucsi_control _ctrl; - u8 data_length; - u16 error; + u8 length; int ret; - if (ucsi->ops) { - ret = ucsi_exec_command(ucsi, ctrl->raw_cmd); - if (ret < 0) - return ret; - - data_length = ret; + ret = ucsi_exec_command(ucsi, ctrl->raw_cmd); + if (ret < 0) + return ret; - if (data) { - ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, data, size); - if (ret) - return ret; - } + length = ret; - ret = ucsi_acknowledge_command(ucsi); + if (data) { + ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, data, size); if (ret) return ret; - - return data_length; } - ret = ucsi_command(ucsi, ctrl); + ret = ucsi_acknowledge_command(ucsi); if (ret) - goto err; - - switch (ucsi->status) { - case UCSI_IDLE: - ret = ucsi_sync(ucsi); - if (ret) - dev_warn(ucsi->dev, "%s: sync failed\n", __func__); - - if (data) - memcpy(data, ucsi->ppm->data->message_in, size); - - data_length = ucsi->ppm->data->cci.data_length; - - ret = ucsi_ack(ucsi, UCSI_ACK_CMD); - if (!ret) - ret = data_length; - break; - case UCSI_BUSY: - /* The caller decides whether to cancel or not */ - ret = -EBUSY; - break; - case UCSI_ERROR: - ret = ucsi_ack(ucsi, UCSI_ACK_CMD); - if (ret) - break; - - _ctrl.raw_cmd = 0; - _ctrl.cmd.cmd = UCSI_GET_ERROR_STATUS; - ret = ucsi_command(ucsi, &_ctrl); - if (ret) { - dev_err(ucsi->dev, "reading error failed!\n"); - break; - } - - memcpy(&error, ucsi->ppm->data->message_in, sizeof(error)); - - /* Something has really gone wrong */ - if (WARN_ON(ucsi->status == UCSI_ERROR)) { - ret = -ENODEV; - break; - } - - ret = ucsi_ack(ucsi, UCSI_ACK_CMD); - if (ret) - break; - - switch (error) { - case UCSI_ERROR_INCOMPATIBLE_PARTNER: - ret = -EOPNOTSUPP; - break; - case UCSI_ERROR_CC_COMMUNICATION_ERR: - ret = -ECOMM; - break; - case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL: - ret = -EPROTO; - break; - case UCSI_ERROR_DEAD_BATTERY: - dev_warn(ucsi->dev, "Dead battery condition!\n"); - ret = -EPERM; - break; - /* The following mean a bug in this driver */ - case UCSI_ERROR_INVALID_CON_NUM: - case UCSI_ERROR_UNREGONIZED_CMD: - case UCSI_ERROR_INVALID_CMD_ARGUMENT: - dev_warn(ucsi->dev, - "%s: possible UCSI driver bug - error 0x%x\n", - __func__, error); - ret = -EINVAL; - break; - default: - dev_warn(ucsi->dev, - "%s: error without status\n", __func__); - ret = -EIO; - break; - } - break; - } - -err: - trace_ucsi_run_command(ctrl, ret); + return ret; - return ret; + return length; } int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, @@ -334,7 +183,7 @@ EXPORT_SYMBOL_GPL(ucsi_resume); void ucsi_altmode_update_active(struct ucsi_connector *con) { const struct typec_altmode *altmode = NULL; - struct ucsi_control ctrl; + u64 command; int ret; u8 cur; int i; @@ -342,7 +191,7 @@ void ucsi_altmode_update_active(struct ucsi_connector *con) UCSI_CMD_GET_CURRENT_CAM(ctrl, con->num); ret = ucsi_run_command(con->ucsi, &ctrl, &cur, sizeof(cur)); if (ret < 0) { - if (con->ucsi->ppm->data->version > 0x0100) { + if (con->ucsi->version > 0x0100) { dev_err(con->ucsi->dev, "GET_CURRENT_CAM command failed\n"); return; @@ -695,10 +544,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) ucsi_partner_change(con); - if (ucsi->ops) - ret = ucsi_acknowledge_connector_change(ucsi); - else - ret = ucsi_ack(ucsi, UCSI_ACK_EVENT); + ret = ucsi_acknowledge_connector_change(ucsi); if (ret) dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); @@ -723,45 +569,6 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num) } EXPORT_SYMBOL_GPL(ucsi_connector_change); -/** - * ucsi_notify - PPM notification handler - * @ucsi: Source UCSI Interface for the notifications - * - * Handle notifications from PPM of @ucsi. - */ -void ucsi_notify(struct ucsi *ucsi) -{ - struct ucsi_cci *cci; - - /* There is no requirement to sync here, but no harm either. */ - ucsi_sync(ucsi); - - cci = &ucsi->ppm->data->cci; - - if (cci->error) - ucsi->status = UCSI_ERROR; - else if (cci->busy) - ucsi->status = UCSI_BUSY; - else - ucsi->status = UCSI_IDLE; - - if (cci->cmd_complete && test_bit(COMMAND_PENDING, &ucsi->flags)) { - complete(&ucsi->complete); - } else if (cci->ack_complete && test_bit(ACK_PENDING, &ucsi->flags)) { - complete(&ucsi->complete); - } else if (cci->connector_change) { - struct ucsi_connector *con; - - con = &ucsi->connector[cci->connector_change - 1]; - - if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) - schedule_work(&con->work); - } - - trace_ucsi_notify(ucsi->ppm->data->raw_cci); -} -EXPORT_SYMBOL_GPL(ucsi_notify); - /* -------------------------------------------------------------------------- */ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) @@ -775,83 +582,39 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) static int ucsi_reset_ppm(struct ucsi *ucsi) { - struct ucsi_control ctrl; + u64 command = UCSI_PPM_RESET; unsigned long tmo; + u32 cci; int ret; - if (ucsi->ops) { - u64 command = UCSI_PPM_RESET; - u32 cci; - - ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command, - sizeof(command)); - if (ret < 0) - return ret; - - tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS); - - do { - if (time_is_before_jiffies(tmo)) - return -ETIMEDOUT; - - ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci)); - if (ret) - return ret; - - /* If the PPM is still doing something else, reset it again. */ - if (cci & ~UCSI_CCI_RESET_COMPLETE) { - ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, - &command, - sizeof(command)); - if (ret < 0) - return ret; - } - - msleep(20); - } while (!(cci & UCSI_CCI_RESET_COMPLETE)); - - return 0; - } - - ctrl.raw_cmd = 0; - ctrl.cmd.cmd = UCSI_PPM_RESET; - trace_ucsi_command(&ctrl); - ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl); - if (ret) - goto err; + ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command, + sizeof(command)); + if (ret < 0) + return ret; tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS); do { - /* Here sync is critical. */ - ret = ucsi_sync(ucsi); - if (ret) - goto err; + if (time_is_before_jiffies(tmo)) + return -ETIMEDOUT; - if (ucsi->ppm->data->cci.reset_complete) - break; + ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci)); + if (ret) + return ret; /* If the PPM is still doing something else, reset it again. */ - if (ucsi->ppm->data->raw_cci) { - dev_warn_ratelimited(ucsi->dev, - "Failed to reset PPM! Trying again..\n"); - - trace_ucsi_command(&ctrl); - ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl); - if (ret) - goto err; + if (cci & ~UCSI_CCI_RESET_COMPLETE) { + ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, + &command, + sizeof(command)); + if (ret < 0) + return ret; } - /* Letting the PPM settle down. */ msleep(20); + } while (!(cci & UCSI_CCI_RESET_COMPLETE)); - ret = -ETIMEDOUT; - } while (time_is_after_jiffies(tmo)); - -err: - trace_ucsi_reset_ppm(&ctrl, ret); - - return ret; + return 0; } static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) @@ -1266,51 +1029,6 @@ void ucsi_unregister(struct ucsi *ucsi) } EXPORT_SYMBOL_GPL(ucsi_unregister); -/** - * ucsi_register_ppm - Register UCSI PPM Interface - * @dev: Device interface to the PPM - * @ppm: The PPM interface - * - * Allocates UCSI instance, associates it with @ppm and returns it to the - * caller, and schedules initialization of the interface. - */ -struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm) -{ - struct ucsi *ucsi; - - ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL); - if (!ucsi) - return ERR_PTR(-ENOMEM); - - INIT_WORK(&ucsi->work, ucsi_init_work); - init_completion(&ucsi->complete); - mutex_init(&ucsi->ppm_lock); - - ucsi->dev = dev; - ucsi->ppm = ppm; - - /* - * Communication with the PPM takes a lot of time. It is not reasonable - * to initialize the driver here. Using a work for now. - */ - queue_work(system_long_wq, &ucsi->work); - - return ucsi; -} -EXPORT_SYMBOL_GPL(ucsi_register_ppm); - -/** - * ucsi_unregister_ppm - Unregister UCSI PPM Interface - * @ucsi: struct ucsi associated with the PPM - * - * Unregister UCSI PPM that was created with ucsi_register(). - */ -void ucsi_unregister_ppm(struct ucsi *ucsi) -{ - ucsi_unregister(ucsi); -} -EXPORT_SYMBOL_GPL(ucsi_unregister_ppm); - MODULE_AUTHOR("Heikki Krogerus "); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("USB Type-C Connector System Software Interface driver"); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index d8a8e8f2f912..29f9e7f0d212 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -398,54 +398,13 @@ struct ucsi_connector_status { /* -------------------------------------------------------------------------- */ -struct ucsi; - -struct ucsi_data { - u16 version; - u16 reserved; - union { - u32 raw_cci; - struct ucsi_cci cci; - }; - struct ucsi_control ctrl; - u32 message_in[4]; - u32 message_out[4]; -} __packed; - -/* - * struct ucsi_ppm - Interface to UCSI Platform Policy Manager - * @data: memory location to the UCSI data structures - * @cmd: UCSI command execution routine - * @sync: Refresh UCSI mailbox (the data structures) - */ -struct ucsi_ppm { - struct ucsi_data *data; - int (*cmd)(struct ucsi_ppm *, struct ucsi_control *); - int (*sync)(struct ucsi_ppm *); -}; - -struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm); -void ucsi_unregister_ppm(struct ucsi *ucsi); -void ucsi_notify(struct ucsi *ucsi); - -/* -------------------------------------------------------------------------- */ - -enum ucsi_status { - UCSI_IDLE = 0, - UCSI_BUSY, - UCSI_ERROR, -}; - struct ucsi { u16 version; struct device *dev; - struct ucsi_ppm *ppm; struct driver_data *driver_data; const struct ucsi_operations *ops; - enum ucsi_status status; - struct completion complete; struct ucsi_capability cap; struct ucsi_connector *connector; -- cgit From 470ce43a1a810117f09aa4bcad6ca2be6b29c8d1 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:32 +0300 Subject: usb: typec: ucsi: Remove struct ucsi_control That data structure was used for constructing the commands before executing them, but it was never really useful. Using the structure just complicated the driver. The commands are 64-bit wide, so it is enough to simply fill a u64 variable. No data structures needed. This simplifies the driver considerable and makes it much easier to for example add support for big endian systems later on. Signed-off-by: Heikki Krogerus Tested-by: Ajay Gupta Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-16-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 16 +-- drivers/usb/typec/ucsi/trace.c | 11 -- drivers/usb/typec/ucsi/trace.h | 50 ++------ drivers/usb/typec/ucsi/ucsi.c | 107 +++++++++------- drivers/usb/typec/ucsi/ucsi.h | 231 ++++++----------------------------- 5 files changed, 115 insertions(+), 300 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 47424935bc81..d4d5189edfb8 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -49,7 +49,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt) { struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); struct ucsi *ucsi = dp->con->ucsi; - struct ucsi_control ctrl; + u64 command; u8 cur = 0; int ret; @@ -64,7 +64,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt) goto err_unlock; } - UCSI_CMD_GET_CURRENT_CAM(ctrl, dp->con->num); + command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num); ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur)); if (ret < 0) { if (ucsi->version > 0x0100) @@ -101,7 +101,7 @@ err_unlock: static int ucsi_displayport_exit(struct typec_altmode *alt) { struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); - struct ucsi_control ctrl; + u64 command; int ret = 0; mutex_lock(&dp->con->lock); @@ -115,8 +115,8 @@ static int ucsi_displayport_exit(struct typec_altmode *alt) goto out_unlock; } - ctrl.raw_cmd = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0); - ret = ucsi_send_command(dp->con->ucsi, &ctrl, NULL, 0); + command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0); + ret = ucsi_send_command(dp->con->ucsi, command, NULL, 0); if (ret < 0) goto out_unlock; @@ -170,14 +170,14 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) static int ucsi_displayport_configure(struct ucsi_dp *dp) { u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf); - struct ucsi_control ctrl; + u64 command; if (!dp->override) return 0; - ctrl.raw_cmd = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); + command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); - return ucsi_send_command(dp->con->ucsi, &ctrl, NULL, 0); + return ucsi_send_command(dp->con->ucsi, command, NULL, 0); } static int ucsi_displayport_vdm(struct typec_altmode *alt, diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c index 1dabafb74320..48ad1dc1b1b2 100644 --- a/drivers/usb/typec/ucsi/trace.c +++ b/drivers/usb/typec/ucsi/trace.c @@ -33,17 +33,6 @@ const char *ucsi_cmd_str(u64 raw_cmd) return ucsi_cmd_strs[(cmd >= ARRAY_SIZE(ucsi_cmd_strs)) ? 0 : cmd]; } -static const char * const ucsi_ack_strs[] = { - [0] = "", - [UCSI_ACK_EVENT] = "event", - [UCSI_ACK_CMD] = "command", -}; - -const char *ucsi_ack_str(u8 ack) -{ - return ucsi_ack_strs[(ack >= ARRAY_SIZE(ucsi_ack_strs)) ? 0 : ack]; -} - const char *ucsi_cci_str(u32 cci) { if (cci & GENMASK(7, 0)) { diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h index 6e3d510b236e..2262229dae8e 100644 --- a/drivers/usb/typec/ucsi/trace.h +++ b/drivers/usb/typec/ucsi/trace.h @@ -10,54 +10,18 @@ #include const char *ucsi_cmd_str(u64 raw_cmd); -const char *ucsi_ack_str(u8 ack); const char *ucsi_cci_str(u32 cci); const char *ucsi_recipient_str(u8 recipient); -DECLARE_EVENT_CLASS(ucsi_log_ack, - TP_PROTO(u8 ack), - TP_ARGS(ack), - TP_STRUCT__entry( - __field(u8, ack) - ), - TP_fast_assign( - __entry->ack = ack; - ), - TP_printk("ACK %s", ucsi_ack_str(__entry->ack)) -); - -DEFINE_EVENT(ucsi_log_ack, ucsi_ack, - TP_PROTO(u8 ack), - TP_ARGS(ack) -); - -DECLARE_EVENT_CLASS(ucsi_log_control, - TP_PROTO(struct ucsi_control *ctrl), - TP_ARGS(ctrl), - TP_STRUCT__entry( - __field(u64, ctrl) - ), - TP_fast_assign( - __entry->ctrl = ctrl->raw_cmd; - ), - TP_printk("control=%08llx (%s)", __entry->ctrl, - ucsi_cmd_str(__entry->ctrl)) -); - -DEFINE_EVENT(ucsi_log_control, ucsi_command, - TP_PROTO(struct ucsi_control *ctrl), - TP_ARGS(ctrl) -); - DECLARE_EVENT_CLASS(ucsi_log_command, - TP_PROTO(struct ucsi_control *ctrl, int ret), - TP_ARGS(ctrl, ret), + TP_PROTO(u64 command, int ret), + TP_ARGS(command, ret), TP_STRUCT__entry( __field(u64, ctrl) __field(int, ret) ), TP_fast_assign( - __entry->ctrl = ctrl->raw_cmd; + __entry->ctrl = command; __entry->ret = ret; ), TP_printk("%s -> %s (err=%d)", ucsi_cmd_str(__entry->ctrl), @@ -66,13 +30,13 @@ DECLARE_EVENT_CLASS(ucsi_log_command, ); DEFINE_EVENT(ucsi_log_command, ucsi_run_command, - TP_PROTO(struct ucsi_control *ctrl, int ret), - TP_ARGS(ctrl, ret) + TP_PROTO(u64 command, int ret), + TP_ARGS(command, ret) ); DEFINE_EVENT(ucsi_log_command, ucsi_reset_ppm, - TP_PROTO(struct ucsi_control *ctrl, int ret), - TP_ARGS(ctrl, ret) + TP_PROTO(u64 command, int ret), + TP_ARGS(command, ret) ); DECLARE_EVENT_CLASS(ucsi_log_connector_status, diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 6462dadd7540..373dd575c68d 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -131,13 +131,13 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd) return UCSI_CCI_LENGTH(cci); } -static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, +static int ucsi_run_command(struct ucsi *ucsi, u64 command, void *data, size_t size) { u8 length; int ret; - ret = ucsi_exec_command(ucsi, ctrl->raw_cmd); + ret = ucsi_exec_command(ucsi, command); if (ret < 0) return ret; @@ -156,13 +156,13 @@ static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl, return length; } -int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, +int ucsi_send_command(struct ucsi *ucsi, u64 command, void *retval, size_t size) { int ret; mutex_lock(&ucsi->ppm_lock); - ret = ucsi_run_command(ucsi, ctrl, retval, size); + ret = ucsi_run_command(ucsi, command, retval, size); mutex_unlock(&ucsi->ppm_lock); return ret; @@ -171,11 +171,12 @@ EXPORT_SYMBOL_GPL(ucsi_send_command); int ucsi_resume(struct ucsi *ucsi) { - struct ucsi_control ctrl; + u64 command; /* Restore UCSI notification enable mask after system resume */ - UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL); - return ucsi_send_command(ucsi, &ctrl, NULL, 0); + command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL; + + return ucsi_send_command(ucsi, command, NULL, 0); } EXPORT_SYMBOL_GPL(ucsi_resume); /* -------------------------------------------------------------------------- */ @@ -188,8 +189,8 @@ void ucsi_altmode_update_active(struct ucsi_connector *con) u8 cur; int i; - UCSI_CMD_GET_CURRENT_CAM(ctrl, con->num); - ret = ucsi_run_command(con->ucsi, &ctrl, &cur, sizeof(cur)); + command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num); + ret = ucsi_run_command(con->ucsi, command, &cur, sizeof(cur)); if (ret < 0) { if (con->ucsi->version > 0x0100) { dev_err(con->ucsi->dev, @@ -307,7 +308,7 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) int max_altmodes = UCSI_MAX_ALTMODES; struct typec_altmode_desc desc; struct ucsi_altmode alt[2]; - struct ucsi_control ctrl; + u64 command; int num = 1; int ret; int len; @@ -325,8 +326,11 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) for (i = 0; i < max_altmodes;) { memset(alt, 0, sizeof(alt)); - UCSI_CMD_GET_ALTERNATE_MODES(ctrl, recipient, con->num, i, 1); - len = ucsi_run_command(con->ucsi, &ctrl, alt, sizeof(alt)); + command = UCSI_GET_ALTERNATE_MODES; + command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); + command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); + command |= UCSI_GET_ALTMODE_OFFSET(i); + len = ucsi_run_command(con->ucsi, command, alt, sizeof(alt)); if (len <= 0) return len; @@ -487,13 +491,14 @@ static void ucsi_handle_connector_change(struct work_struct *work) struct ucsi_connector *con = container_of(work, struct ucsi_connector, work); struct ucsi *ucsi = con->ucsi; - struct ucsi_control ctrl; + u64 command; int ret; mutex_lock(&con->lock); - UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num); - ret = ucsi_send_command(ucsi, &ctrl, &con->status, sizeof(con->status)); + command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); + ret = ucsi_send_command(ucsi, command, &con->status, + sizeof(con->status)); if (ret < 0) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", __func__, ret); @@ -537,8 +542,9 @@ static void ucsi_handle_connector_change(struct work_struct *work) * Running GET_CAM_SUPPORTED command just to make sure the PPM * does not get stuck in case it assumes we do so. */ - UCSI_CMD_GET_CAM_SUPPORTED(ctrl, con->num); - ucsi_run_command(con->ucsi, &ctrl, NULL, 0); + command = UCSI_GET_CAM_SUPPORTED; + command |= UCSI_CONNECTOR_NUMBER(con->num); + ucsi_run_command(con->ucsi, command, NULL, 0); } if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) @@ -573,11 +579,12 @@ EXPORT_SYMBOL_GPL(ucsi_connector_change); static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) { - struct ucsi_control ctrl; + u64 command; - UCSI_CMD_CONNECTOR_RESET(ctrl, con, hard); + command = UCSI_CONNECTOR_RESET | UCSI_CONNECTOR_NUMBER(con->num); + command |= hard ? UCSI_CONNECTOR_RESET_HARD : 0; - return ucsi_send_command(con->ucsi, &ctrl, NULL, 0); + return ucsi_send_command(con->ucsi, command, NULL, 0); } static int ucsi_reset_ppm(struct ucsi *ucsi) @@ -617,21 +624,21 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) return 0; } -static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) +static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) { int ret; - ret = ucsi_send_command(con->ucsi, ctrl, NULL, 0); + ret = ucsi_send_command(con->ucsi, command, NULL, 0); if (ret == -ETIMEDOUT) { - struct ucsi_control c; + u64 c; /* PPM most likely stopped responding. Resetting everything. */ mutex_lock(&con->ucsi->ppm_lock); ucsi_reset_ppm(con->ucsi); mutex_unlock(&con->ucsi->ppm_lock); - UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL); - ucsi_send_command(con->ucsi, &c, NULL, 0); + c = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL; + ucsi_send_command(con->ucsi, c, NULL, 0); ucsi_reset_connector(con, true); } @@ -642,7 +649,7 @@ static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) { struct ucsi_connector *con = typec_get_drvdata(port); - struct ucsi_control ctrl; + u64 command; int ret = 0; mutex_lock(&con->lock); @@ -658,8 +665,10 @@ static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) role == TYPEC_HOST)) goto out_unlock; - UCSI_CMD_SET_UOR(ctrl, con, role); - ret = ucsi_role_cmd(con, &ctrl); + command = UCSI_SET_UOR | UCSI_CONNECTOR_NUMBER(con->num); + command |= UCSI_SET_UOR_ROLE(role); + command |= UCSI_SET_UOR_ACCEPT_ROLE_SWAPS; + ret = ucsi_role_cmd(con, command); if (ret < 0) goto out_unlock; @@ -676,7 +685,7 @@ out_unlock: static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) { struct ucsi_connector *con = typec_get_drvdata(port); - struct ucsi_control ctrl; + u64 command; int ret = 0; mutex_lock(&con->lock); @@ -689,8 +698,10 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) if (con->status.pwr_dir == role) goto out_unlock; - UCSI_CMD_SET_PDR(ctrl, con, role); - ret = ucsi_role_cmd(con, &ctrl); + command = UCSI_SET_PDR | UCSI_CONNECTOR_NUMBER(con->num); + command |= UCSI_SET_PDR_ROLE(role); + command |= UCSI_SET_PDR_ACCEPT_ROLE_SWAPS; + ret = ucsi_role_cmd(con, command); if (ret < 0) goto out_unlock; @@ -733,7 +744,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) struct ucsi_connector *con = &ucsi->connector[index]; struct typec_capability *cap = &con->typec_cap; enum typec_accessory *accessory = cap->accessory; - struct ucsi_control ctrl; + u64 command; int ret; INIT_WORK(&con->work, ucsi_handle_connector_change); @@ -743,8 +754,9 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) con->ucsi = ucsi; /* Get connector capability */ - UCSI_CMD_GET_CONNECTOR_CAPABILITY(ctrl, con->num); - ret = ucsi_run_command(ucsi, &ctrl, &con->cap, sizeof(con->cap)); + command = UCSI_GET_CONNECTOR_CAPABILITY; + command |= UCSI_CONNECTOR_NUMBER(con->num); + ret = ucsi_run_command(ucsi, command, &con->cap, sizeof(con->cap)); if (ret < 0) return ret; @@ -787,8 +799,9 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) con->num); /* Get the status */ - UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num); - ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status)); + command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); + ret = ucsi_run_command(ucsi, command, &con->status, + sizeof(con->status)); if (ret < 0) { dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); return 0; @@ -836,7 +849,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) int ucsi_init(struct ucsi *ucsi) { struct ucsi_connector *con; - struct ucsi_control ctrl; + u64 command; int ret; int i; @@ -850,15 +863,15 @@ int ucsi_init(struct ucsi *ucsi) } /* Enable basic notifications */ - UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE | - UCSI_ENABLE_NTFY_ERROR); - ret = ucsi_run_command(ucsi, &ctrl, NULL, 0); + command = UCSI_SET_NOTIFICATION_ENABLE; + command |= UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR; + ret = ucsi_run_command(ucsi, command, NULL, 0); if (ret < 0) goto err_reset; /* Get PPM capabilities */ - UCSI_CMD_GET_CAPABILITY(ctrl); - ret = ucsi_run_command(ucsi, &ctrl, &ucsi->cap, sizeof(ucsi->cap)); + command = UCSI_GET_CAPABILITY; + ret = ucsi_run_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap)); if (ret < 0) goto err_reset; @@ -883,8 +896,8 @@ int ucsi_init(struct ucsi *ucsi) } /* Enable all notifications */ - UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL); - ret = ucsi_run_command(ucsi, &ctrl, NULL, 0); + command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL; + ret = ucsi_run_command(ucsi, command, NULL, 0); if (ret < 0) goto err_unregister; @@ -1005,15 +1018,15 @@ EXPORT_SYMBOL_GPL(ucsi_register); */ void ucsi_unregister(struct ucsi *ucsi) { - struct ucsi_control ctrl; + u64 command; int i; /* Make sure that we are not in the middle of driver initialization */ cancel_work_sync(&ucsi->work); /* Disable everything except command complete notification */ - UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE) - ucsi_send_command(ucsi, &ctrl, NULL, 0); + command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_CMD_COMPLETE; + ucsi_send_command(ucsi, command, NULL, 0); for (i = 0; i < ucsi->cap.num_connectors; i++) { cancel_work_sync(&ucsi->connector[i].work); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 29f9e7f0d212..4f1409e06d22 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -60,178 +60,6 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); /* -------------------------------------------------------------------------- */ -/* Command Status and Connector Change Indication (CCI) data structure */ -struct ucsi_cci { - u8:1; /* reserved */ - u8 connector_change:7; - u8 data_length; - u16:9; /* reserved */ - u16 not_supported:1; - u16 cancel_complete:1; - u16 reset_complete:1; - u16 busy:1; - u16 ack_complete:1; - u16 error:1; - u16 cmd_complete:1; -} __packed; - -/* Default fields in CONTROL data structure */ -struct ucsi_command { - u8 cmd; - u8 length; - u64 data:48; -} __packed; - -/* ACK Command structure */ -struct ucsi_ack_cmd { - u8 cmd; - u8 length; - u8 cci_ack:1; - u8 cmd_ack:1; - u8:6; /* reserved */ -} __packed; - -/* Connector Reset Command structure */ -struct ucsi_con_rst { - u8 cmd; - u8 length; - u8 con_num:7; - u8 hard_reset:1; -} __packed; - -/* Set USB Operation Mode Command structure */ -struct ucsi_uor_cmd { - u8 cmd; - u8 length; - u16 con_num:7; - u16 role:3; -#define UCSI_UOR_ROLE_DFP BIT(0) -#define UCSI_UOR_ROLE_UFP BIT(1) -#define UCSI_UOR_ROLE_DRP BIT(2) - u16:6; /* reserved */ -} __packed; - -/* Get Alternate Modes Command structure */ -struct ucsi_altmode_cmd { - u8 cmd; - u8 length; - u8 recipient; -#define UCSI_RECIPIENT_CON 0 -#define UCSI_RECIPIENT_SOP 1 -#define UCSI_RECIPIENT_SOP_P 2 -#define UCSI_RECIPIENT_SOP_PP 3 - u8 con_num; - u8 offset; - u8 num_altmodes; -} __packed; - -struct ucsi_control { - union { - u64 raw_cmd; - struct ucsi_command cmd; - struct ucsi_uor_cmd uor; - struct ucsi_ack_cmd ack; - struct ucsi_con_rst con_rst; - struct ucsi_altmode_cmd alt; - }; -}; - -#define __UCSI_CMD(_ctrl_, _cmd_) \ -{ \ - (_ctrl_).raw_cmd = 0; \ - (_ctrl_).cmd.cmd = _cmd_; \ -} - -/* Helper for preparing ucsi_control for CONNECTOR_RESET command. */ -#define UCSI_CMD_CONNECTOR_RESET(_ctrl_, _con_, _hard_) \ -{ \ - __UCSI_CMD(_ctrl_, UCSI_CONNECTOR_RESET) \ - (_ctrl_).con_rst.con_num = (_con_)->num; \ - (_ctrl_).con_rst.hard_reset = _hard_; \ -} - -/* Helper for preparing ucsi_control for ACK_CC_CI command. */ -#define UCSI_CMD_ACK(_ctrl_, _ack_) \ -{ \ - __UCSI_CMD(_ctrl_, UCSI_ACK_CC_CI) \ - (_ctrl_).ack.cci_ack = ((_ack_) == UCSI_ACK_EVENT); \ - (_ctrl_).ack.cmd_ack = ((_ack_) == UCSI_ACK_CMD); \ -} - -/* Helper for preparing ucsi_control for SET_NOTIFY_ENABLE command. */ -#define UCSI_CMD_SET_NTFY_ENABLE(_ctrl_, _ntfys_) \ -{ \ - __UCSI_CMD(_ctrl_, UCSI_SET_NOTIFICATION_ENABLE) \ - (_ctrl_).cmd.data = _ntfys_; \ -} - -/* Helper for preparing ucsi_control for GET_CAPABILITY command. */ -#define UCSI_CMD_GET_CAPABILITY(_ctrl_) \ -{ \ - __UCSI_CMD(_ctrl_, UCSI_GET_CAPABILITY) \ -} - -/* Helper for preparing ucsi_control for GET_CONNECTOR_CAPABILITY command. */ -#define UCSI_CMD_GET_CONNECTOR_CAPABILITY(_ctrl_, _con_) \ -{ \ - __UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_CAPABILITY) \ - (_ctrl_).cmd.data = _con_; \ -} - -/* Helper for preparing ucsi_control for GET_ALTERNATE_MODES command. */ -#define UCSI_CMD_GET_ALTERNATE_MODES(_ctrl_, _r_, _con_num_, _o_, _num_)\ -{ \ - __UCSI_CMD((_ctrl_), UCSI_GET_ALTERNATE_MODES) \ - _ctrl_.alt.recipient = (_r_); \ - _ctrl_.alt.con_num = (_con_num_); \ - _ctrl_.alt.offset = (_o_); \ - _ctrl_.alt.num_altmodes = (_num_) - 1; \ -} - -/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */ -#define UCSI_CMD_GET_CAM_SUPPORTED(_ctrl_, _con_) \ -{ \ - __UCSI_CMD((_ctrl_), UCSI_GET_CAM_SUPPORTED) \ - _ctrl_.cmd.data = (_con_); \ -} - -/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */ -#define UCSI_CMD_GET_CURRENT_CAM(_ctrl_, _con_) \ -{ \ - __UCSI_CMD((_ctrl_), UCSI_GET_CURRENT_CAM) \ - _ctrl_.cmd.data = (_con_); \ -} - -/* Helper for preparing ucsi_control for GET_CONNECTOR_STATUS command. */ -#define UCSI_CMD_GET_CONNECTOR_STATUS(_ctrl_, _con_) \ -{ \ - __UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_STATUS) \ - (_ctrl_).cmd.data = _con_; \ -} - -#define __UCSI_ROLE(_ctrl_, _cmd_, _con_num_) \ -{ \ - __UCSI_CMD(_ctrl_, _cmd_) \ - (_ctrl_).uor.con_num = _con_num_; \ - (_ctrl_).uor.role = UCSI_UOR_ROLE_DRP; \ -} - -/* Helper for preparing ucsi_control for SET_UOR command. */ -#define UCSI_CMD_SET_UOR(_ctrl_, _con_, _role_) \ -{ \ - __UCSI_ROLE(_ctrl_, UCSI_SET_UOR, (_con_)->num) \ - (_ctrl_).uor.role |= (_role_) == TYPEC_HOST ? UCSI_UOR_ROLE_DFP : \ - UCSI_UOR_ROLE_UFP; \ -} - -/* Helper for preparing ucsi_control for SET_PDR command. */ -#define UCSI_CMD_SET_PDR(_ctrl_, _con_, _role_) \ -{ \ - __UCSI_ROLE(_ctrl_, UCSI_SET_PDR, (_con_)->num) \ - (_ctrl_).uor.role |= (_role_) == TYPEC_SOURCE ? UCSI_UOR_ROLE_DFP : \ - UCSI_UOR_ROLE_UFP; \ -} - /* Commands */ #define UCSI_PPM_RESET 0x01 #define UCSI_CANCEL 0x02 @@ -253,28 +81,49 @@ struct ucsi_control { #define UCSI_GET_CONNECTOR_STATUS 0x12 #define UCSI_GET_ERROR_STATUS 0x13 -/* ACK_CC_CI commands */ -#define UCSI_ACK_EVENT 1 -#define UCSI_ACK_CMD 2 +#define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16) + +/* CONNECTOR_RESET command bits */ +#define UCSI_CONNECTOR_RESET_HARD BIT(23) /* Deprecated in v1.1 */ -/* Bits for ACK CC or CI */ +/* ACK_CC_CI bits */ #define UCSI_ACK_CONNECTOR_CHANGE BIT(16) #define UCSI_ACK_COMMAND_COMPLETE BIT(17) -/* Bits for SET_NOTIFICATION_ENABLE command */ -#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(0) -#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(1) -#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(2) -#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(5) -#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(6) -#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(7) -#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(8) -#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(9) -#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(11) -#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(12) -#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(14) -#define UCSI_ENABLE_NTFY_ERROR BIT(15) -#define UCSI_ENABLE_NTFY_ALL 0xdbe7 +/* SET_NOTIFICATION_ENABLE command bits */ +#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(16) +#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(17) +#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(18) +#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(19) +#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(20) +#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(21) +#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(22) +#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(23) +#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(24) +#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(25) +#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(26) +#define UCSI_ENABLE_NTFY_ERROR BIT(27) +#define UCSI_ENABLE_NTFY_ALL 0xdbe70000 + +/* SET_UOR command bits */ +#define UCSI_SET_UOR_ROLE(_r_) (((_r_) == TYPEC_HOST ? 1 : 2) << 23) +#define UCSI_SET_UOR_ACCEPT_ROLE_SWAPS BIT(25) + +/* SET_PDF command bits */ +#define UCSI_SET_PDR_ROLE(_r_) (((_r_) == TYPEC_SOURCE ? 1 : 2) << 23) +#define UCSI_SET_PDR_ACCEPT_ROLE_SWAPS BIT(25) + +/* GET_ALTERNATE_MODES command bits */ +#define UCSI_GET_ALTMODE_RECIPIENT(_r_) ((u64)(_r_) << 16) +#define UCSI_RECIPIENT_CON 0 +#define UCSI_RECIPIENT_SOP 1 +#define UCSI_RECIPIENT_SOP_P 2 +#define UCSI_RECIPIENT_SOP_PP 3 +#define UCSI_GET_ALTMODE_CONNECTOR_NUMBER(_r_) ((u64)(_r_) << 24) +#define UCSI_GET_ALTMODE_OFFSET(_r_) ((u64)(_r_) << 32) +#define UCSI_GET_ALTMODE_NUM_ALTMODES(_r_) ((u64)(_r_) << 40) + +/* -------------------------------------------------------------------------- */ /* Error information returned by PPM in response to GET_ERROR_STATUS command. */ #define UCSI_ERROR_UNREGONIZED_CMD BIT(0) @@ -443,7 +292,7 @@ struct ucsi_connector { struct ucsi_connector_capability cap; }; -int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, +int ucsi_send_command(struct ucsi *ucsi, u64 command, void *retval, size_t size); void ucsi_altmode_update_active(struct ucsi_connector *con); -- cgit From 3cf657f07918bc2d9295bf896a71bd31e407bb0c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:33 +0300 Subject: usb: typec: ucsi: Remove all bit-fields We can't use bit fields with data that is received or send to/from the device. Signed-off-by: Heikki Krogerus Tested-by: Ajay Gupta Link: https://lore.kernel.org/r/20191104142435.29960-17-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/trace.h | 12 +++--- drivers/usb/typec/ucsi/ucsi.c | 52 ++++++++++++++--------- drivers/usb/typec/ucsi/ucsi.h | 93 +++++++++++++++++++++--------------------- 3 files changed, 85 insertions(+), 72 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h index 2262229dae8e..a0d3a934d3d9 100644 --- a/drivers/usb/typec/ucsi/trace.h +++ b/drivers/usb/typec/ucsi/trace.h @@ -56,13 +56,13 @@ DECLARE_EVENT_CLASS(ucsi_log_connector_status, TP_fast_assign( __entry->port = port - 1; __entry->change = status->change; - __entry->opmode = status->pwr_op_mode; - __entry->connected = status->connected; - __entry->pwr_dir = status->pwr_dir; - __entry->partner_flags = status->partner_flags; - __entry->partner_type = status->partner_type; + __entry->opmode = UCSI_CONSTAT_PWR_OPMODE(status->flags); + __entry->connected = !!(status->flags & UCSI_CONSTAT_CONNECTED); + __entry->pwr_dir = !!(status->flags & UCSI_CONSTAT_PWR_DIR); + __entry->partner_flags = UCSI_CONSTAT_PARTNER_FLAGS(status->flags); + __entry->partner_type = UCSI_CONSTAT_PARTNER_TYPE(status->flags); __entry->request_data_obj = status->request_data_obj; - __entry->bc_status = status->bc_status; + __entry->bc_status = UCSI_CONSTAT_BC_STATUS(status->pwr_status); ), TP_printk("port%d status: change=%04x, opmode=%x, connected=%d, " "sourcing=%d, partner_flags=%x, partner_type=%x, " diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 373dd575c68d..6c6def96a55b 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -392,7 +392,7 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { - switch (con->status.pwr_op_mode) { + switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { case UCSI_CONSTAT_PWR_OPMODE_PD: typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD); break; @@ -410,6 +410,7 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con) static int ucsi_register_partner(struct ucsi_connector *con) { + u8 pwr_opmode = UCSI_CONSTAT_PWR_OPMODE(con->status.flags); struct typec_partner_desc desc; struct typec_partner *partner; @@ -418,7 +419,7 @@ static int ucsi_register_partner(struct ucsi_connector *con) memset(&desc, 0, sizeof(desc)); - switch (con->status.partner_type) { + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: desc.accessory = TYPEC_ACCESSORY_DEBUG; break; @@ -429,7 +430,7 @@ static int ucsi_register_partner(struct ucsi_connector *con) break; } - desc.usb_pd = con->status.pwr_op_mode == UCSI_CONSTAT_PWR_OPMODE_PD; + desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD; partner = typec_register_partner(con->port, &desc); if (IS_ERR(partner)) { @@ -461,7 +462,7 @@ static void ucsi_partner_change(struct ucsi_connector *con) if (!con->partner) return; - switch (con->status.partner_type) { + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: typec_set_data_role(con->port, TYPEC_HOST); break; @@ -491,6 +492,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) struct ucsi_connector *con = container_of(work, struct ucsi_connector, work); struct ucsi *ucsi = con->ucsi; + enum typec_role role; u64 command; int ret; @@ -505,11 +507,13 @@ static void ucsi_handle_connector_change(struct work_struct *work) goto out_unlock; } + role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); + if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE) ucsi_pwr_opmode_change(con); if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) { - typec_set_pwr_role(con->port, con->status.pwr_dir); + typec_set_pwr_role(con->port, role); /* Complete pending power role swap */ if (!completion_done(&con->complete)) @@ -517,9 +521,9 @@ static void ucsi_handle_connector_change(struct work_struct *work) } if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) { - typec_set_pwr_role(con->port, con->status.pwr_dir); + typec_set_pwr_role(con->port, role); - switch (con->status.partner_type) { + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: typec_set_data_role(con->port, TYPEC_HOST); break; @@ -530,7 +534,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) break; } - if (con->status.connected) + if (con->status.flags & UCSI_CONSTAT_CONNECTED) ucsi_register_partner(con); else ucsi_unregister_partner(con); @@ -649,6 +653,7 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) { struct ucsi_connector *con = typec_get_drvdata(port); + u8 partner_type; u64 command; int ret = 0; @@ -659,9 +664,10 @@ static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) goto out_unlock; } - if ((con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP && + partner_type = UCSI_CONSTAT_PARTNER_TYPE(con->status.flags); + if ((partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP && role == TYPEC_DEVICE) || - (con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP && + (partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP && role == TYPEC_HOST)) goto out_unlock; @@ -685,6 +691,7 @@ out_unlock: static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) { struct ucsi_connector *con = typec_get_drvdata(port); + enum typec_role cur_role; u64 command; int ret = 0; @@ -695,7 +702,9 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) goto out_unlock; } - if (con->status.pwr_dir == role) + cur_role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); + + if (cur_role == role) goto out_unlock; command = UCSI_SET_PDR | UCSI_CONNECTOR_NUMBER(con->num); @@ -712,7 +721,8 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) } /* Something has gone wrong while swapping the role */ - if (con->status.pwr_op_mode != UCSI_CONSTAT_PWR_OPMODE_PD) { + if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) != + UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_reset_connector(con, true); ret = -EPROTO; } @@ -767,11 +777,12 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP) cap->data = TYPEC_PORT_UFP; - if (con->cap.provider && con->cap.consumer) + if ((con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) && + (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER)) cap->type = TYPEC_PORT_DRP; - else if (con->cap.provider) + else if (con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) cap->type = TYPEC_PORT_SRC; - else if (con->cap.consumer) + else if (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER) cap->type = TYPEC_PORT_SNK; cap->revision = ucsi->cap.typec_version; @@ -807,10 +818,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) return 0; } - ucsi_pwr_opmode_change(con); - typec_set_pwr_role(con->port, con->status.pwr_dir); - - switch (con->status.partner_type) { + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: typec_set_data_role(con->port, TYPEC_HOST); break; @@ -822,8 +830,12 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) } /* Check if there is already something connected */ - if (con->status.connected) + if (con->status.flags & UCSI_CONSTAT_CONNECTED) { + typec_set_pwr_role(con->port, + !!(con->status.flags & UCSI_CONSTAT_PWR_DIR)); + ucsi_pwr_opmode_change(con); ucsi_register_partner(con); + } if (con->partner) { ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 4f1409e06d22..bc2254e7a3a3 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -144,8 +144,8 @@ struct ucsi_capability { #define UCSI_CAP_ATTR_POWER_AC_SUPPLY BIT(8) #define UCSI_CAP_ATTR_POWER_OTHER BIT(10) #define UCSI_CAP_ATTR_POWER_VBUS BIT(14) - u32 num_connectors:8; - u32 features:24; + u8 num_connectors; + u8 features; #define UCSI_CAP_SET_UOM BIT(0) #define UCSI_CAP_SET_PDM BIT(1) #define UCSI_CAP_ALT_MODE_DETAILS BIT(2) @@ -154,8 +154,9 @@ struct ucsi_capability { #define UCSI_CAP_CABLE_DETAILS BIT(5) #define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6) #define UCSI_CAP_PD_RESET BIT(7) + u16 reserved_1; u8 num_alt_modes; - u8 reserved; + u8 reserved_2; u16 bc_version; u16 pd_version; u16 typec_version; @@ -172,9 +173,9 @@ struct ucsi_connector_capability { #define UCSI_CONCAP_OPMODE_USB2 BIT(5) #define UCSI_CONCAP_OPMODE_USB3 BIT(6) #define UCSI_CONCAP_OPMODE_ALT_MODE BIT(7) - u8 provider:1; - u8 consumer:1; - u8:6; /* reserved */ + u8 flags; +#define UCSI_CONCAP_FLAG_PROVIDER BIT(0) +#define UCSI_CONCAP_FLAG_CONSUMER BIT(1) } __packed; struct ucsi_altmode { @@ -186,18 +187,17 @@ struct ucsi_altmode { struct ucsi_cable_property { u16 speed_supported; u8 current_capability; - u8 vbus_in_cable:1; - u8 active_cable:1; - u8 directionality:1; - u8 plug_type:2; -#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A 0 -#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B 1 -#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C 2 -#define UCSI_CABLE_PROPERTY_PLUG_OTHER 3 - u8 mode_support:1; - u8:2; /* reserved */ - u8 latency:4; - u8:4; /* reserved */ + u8 flags; +#define UCSI_CABLE_PROP_FLAG_VBUS_IN_CABLE BIT(0) +#define UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE BIT(1) +#define UCSI_CABLE_PROP_FLAG_DIRECTIONALITY BIT(2) +#define UCSI_CABLE_PROP_FLAG_PLUG_TYPE(_f_) ((_f_) & GENMASK(3, 0)) +#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A 0 +#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B 1 +#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C 2 +#define UCSI_CABLE_PROPERTY_PLUG_OTHER 3 +#define UCSI_CABLE_PROP_MODE_SUPPORT BIT(5) + u8 latency; } __packed; /* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */ @@ -214,35 +214,36 @@ struct ucsi_connector_status { #define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12) #define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) #define UCSI_CONSTAT_ERROR BIT(15) - u16 pwr_op_mode:3; -#define UCSI_CONSTAT_PWR_OPMODE_NONE 0 -#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1 -#define UCSI_CONSTAT_PWR_OPMODE_BC 2 -#define UCSI_CONSTAT_PWR_OPMODE_PD 3 -#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4 -#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5 - u16 connected:1; - u16 pwr_dir:1; - u16 partner_flags:8; -#define UCSI_CONSTAT_PARTNER_FLAG_USB BIT(0) -#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE BIT(1) - u16 partner_type:3; -#define UCSI_CONSTAT_PARTNER_TYPE_DFP 1 -#define UCSI_CONSTAT_PARTNER_TYPE_UFP 2 -#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */ -#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */ -#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5 -#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6 + u16 flags; +#define UCSI_CONSTAT_PWR_OPMODE(_f_) ((_f_) & GENMASK(2, 0)) +#define UCSI_CONSTAT_PWR_OPMODE_NONE 0 +#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1 +#define UCSI_CONSTAT_PWR_OPMODE_BC 2 +#define UCSI_CONSTAT_PWR_OPMODE_PD 3 +#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4 +#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5 +#define UCSI_CONSTAT_CONNECTED BIT(3) +#define UCSI_CONSTAT_PWR_DIR BIT(4) +#define UCSI_CONSTAT_PARTNER_FLAGS(_f_) (((_f_) & GENMASK(12, 5)) >> 5) +#define UCSI_CONSTAT_PARTNER_FLAG_USB 1 +#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE 2 +#define UCSI_CONSTAT_PARTNER_TYPE(_f_) (((_f_) & GENMASK(15, 13)) >> 13) +#define UCSI_CONSTAT_PARTNER_TYPE_DFP 1 +#define UCSI_CONSTAT_PARTNER_TYPE_UFP 2 +#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */ +#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */ +#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5 +#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6 u32 request_data_obj; - u8 bc_status:2; -#define UCSI_CONSTAT_BC_NOT_CHARGING 0 -#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1 -#define UCSI_CONSTAT_BC_SLOW_CHARGING 2 -#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 - u8 provider_cap_limit_reason:4; -#define UCSI_CONSTAT_CAP_PWR_LOWERED 0 -#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1 - u8:2; /* reserved */ + u8 pwr_status; +#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_) & GENMASK(2, 0)) +#define UCSI_CONSTAT_BC_NOT_CHARGING 0 +#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1 +#define UCSI_CONSTAT_BC_SLOW_CHARGING 2 +#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 +#define UCSI_CONSTAT_PROVIDER_CAP_LIMIT(_p_) (((_p_) & GENMASK(6, 3)) >> 3) +#define UCSI_CONSTAT_CAP_PWR_LOWERED 0 +#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1 } __packed; /* -------------------------------------------------------------------------- */ -- cgit From e716bb38edb44b6a7dd9b825f57450118b3066ae Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:34 +0300 Subject: usb: typec: ucsi: New error codes Adding new error codes to the driver that were introduced in UCSI specification v1.1. Signed-off-by: Heikki Krogerus Tested-by: Ajay Gupta Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-18-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 25 ++++++++++++++++++++----- drivers/usb/typec/ucsi/ucsi.h | 6 ++++++ 2 files changed, 26 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 6c6def96a55b..772f55c92ba3 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -86,18 +86,33 @@ static int ucsi_read_error(struct ucsi *ucsi) case UCSI_ERROR_DEAD_BATTERY: dev_warn(ucsi->dev, "Dead battery condition!\n"); return -EPERM; - /* The following mean a bug in this driver */ case UCSI_ERROR_INVALID_CON_NUM: case UCSI_ERROR_UNREGONIZED_CMD: case UCSI_ERROR_INVALID_CMD_ARGUMENT: - dev_err(ucsi->dev, "possible UCSI driver bug (0x%x)\n", error); + dev_err(ucsi->dev, "possible UCSI driver bug %u\n", error); return -EINVAL; + case UCSI_ERROR_OVERCURRENT: + dev_warn(ucsi->dev, "Overcurrent condition\n"); + break; + case UCSI_ERROR_PARTNER_REJECTED_SWAP: + dev_warn(ucsi->dev, "Partner rejected swap\n"); + break; + case UCSI_ERROR_HARD_RESET: + dev_warn(ucsi->dev, "Hard reset occurred\n"); + break; + case UCSI_ERROR_PPM_POLICY_CONFLICT: + dev_warn(ucsi->dev, "PPM Policy conflict\n"); + break; + case UCSI_ERROR_SWAP_REJECTED: + dev_warn(ucsi->dev, "Swap rejected\n"); + break; + case UCSI_ERROR_UNDEFINED: default: - dev_err(ucsi->dev, "%s: error without status\n", __func__); - return -EIO; + dev_err(ucsi->dev, "unknown error %u\n", error); + break; } - return 0; + return -EIO; } static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index bc2254e7a3a3..8569bbd3762f 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -133,6 +133,12 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_ERROR_CC_COMMUNICATION_ERR BIT(4) #define UCSI_ERROR_DEAD_BATTERY BIT(5) #define UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL BIT(6) +#define UCSI_ERROR_OVERCURRENT BIT(7) +#define UCSI_ERROR_UNDEFINED BIT(8) +#define UCSI_ERROR_PARTNER_REJECTED_SWAP BIT(9) +#define UCSI_ERROR_HARD_RESET BIT(10) +#define UCSI_ERROR_PPM_POLICY_CONFLICT BIT(11) +#define UCSI_ERROR_SWAP_REJECTED BIT(12) /* Data structure filled by PPM in response to GET_CAPABILITY command. */ struct ucsi_capability { -- cgit From 74ce3e41274805e4a5598f4d98ee4d5160466d07 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Nov 2019 17:24:35 +0300 Subject: usb: typec: ucsi: Optimise ucsi_unregister() There is no need to reset the PPM when the interface is unregistered. Quietly silencing the notifications and then unregistering everything is enough. This speeds up ucsi_unregister() a lot. Signed-off-by: Heikki Krogerus Tested-by: Ajay Gupta Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20191104142435.29960-19-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 772f55c92ba3..4459bc68aa33 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1045,15 +1045,14 @@ EXPORT_SYMBOL_GPL(ucsi_register); */ void ucsi_unregister(struct ucsi *ucsi) { - u64 command; + u64 cmd = UCSI_SET_NOTIFICATION_ENABLE; int i; /* Make sure that we are not in the middle of driver initialization */ cancel_work_sync(&ucsi->work); - /* Disable everything except command complete notification */ - command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_CMD_COMPLETE; - ucsi_send_command(ucsi, command, NULL, 0); + /* Disable notifications */ + ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd)); for (i = 0; i < ucsi->cap.num_connectors; i++) { cancel_work_sync(&ucsi->connector[i].work); @@ -1063,8 +1062,6 @@ void ucsi_unregister(struct ucsi *ucsi) typec_unregister_port(ucsi->connector[i].port); } - ucsi_reset_ppm(ucsi); - kfree(ucsi->connector); } EXPORT_SYMBOL_GPL(ucsi_unregister); -- cgit From e696d00e65e81d46e911f24b12e441037bf11b38 Mon Sep 17 00:00:00 2001 From: Pavel Löbl Date: Fri, 1 Nov 2019 08:01:50 +0100 Subject: USB: serial: mos7840: add USB ID to support Moxa UPort 2210 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add USB ID for MOXA UPort 2210. This device contains mos7820 but it passes GPIO0 check implemented by driver and it's detected as mos7840. Hence product id check is added to force mos7820 mode. Signed-off-by: Pavel Löbl Cc: stable [ johan: rename id defines and add vendor-id check ] Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index a698d46ba773..3eeeee38debc 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -119,11 +119,15 @@ /* This driver also supports * ATEN UC2324 device using Moschip MCS7840 * ATEN UC2322 device using Moschip MCS7820 + * MOXA UPort 2210 device using Moschip MCS7820 */ #define USB_VENDOR_ID_ATENINTL 0x0557 #define ATENINTL_DEVICE_ID_UC2324 0x2011 #define ATENINTL_DEVICE_ID_UC2322 0x7820 +#define USB_VENDOR_ID_MOXA 0x110a +#define MOXA_DEVICE_ID_2210 0x2210 + /* Interrupt Routine Defines */ #define SERIAL_IIR_RLS 0x06 @@ -195,6 +199,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL2_4)}, {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2324)}, {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2322)}, + {USB_DEVICE(USB_VENDOR_ID_MOXA, MOXA_DEVICE_ID_2210)}, {} /* terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); @@ -2020,6 +2025,7 @@ static int mos7840_probe(struct usb_serial *serial, const struct usb_device_id *id) { u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); + u16 vid = le16_to_cpu(serial->dev->descriptor.idVendor); u8 *buf; int device_type; @@ -2030,6 +2036,11 @@ static int mos7840_probe(struct usb_serial *serial, goto out; } + if (vid == USB_VENDOR_ID_MOXA && product == MOXA_DEVICE_ID_2210) { + device_type = MOSCHIP_DEVICE_ID_7820; + goto out; + } + buf = kzalloc(VENDOR_READ_LENGTH, GFP_KERNEL); if (!buf) return -ENOMEM; -- cgit From 66d1b0c0580b7f1b1850ee4423f32ac42afa2e92 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 6 Nov 2019 14:28:21 -0600 Subject: usb: gadget: pch_udc: fix use after free Remove pointer dereference after free. pci_pool_free doesn't care about contents of td. It's just a void* for it Addresses-Coverity-ID: 1091173 ("Use after free") Cc: stable@vger.kernel.org Acked-by: Michal Nazarewicz Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20191106202821.GA20347@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/pch_udc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 265dab2bbfac..3344fb8c4181 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1519,7 +1519,6 @@ static void pch_udc_free_dma_chain(struct pch_udc_dev *dev, td = phys_to_virt(addr); addr2 = (dma_addr_t)td->next; dma_pool_free(dev->data_requests, td, addr); - td->next = 0x00; addr = addr2; } req->chain_len = 1; -- cgit From e76b3bf7654c3c94554c24ba15a3d105f4006c80 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Wed, 6 Nov 2019 14:27:10 +0800 Subject: usb: Allow USB device to be warm reset in suspended state On Dell WD15 dock, sometimes USB ethernet cannot be detected after plugging cable to the ethernet port, the hub and roothub get runtime resumed and runtime suspended immediately: ... [ 433.315169] xhci_hcd 0000:3a:00.0: hcd_pci_runtime_resume: 0 [ 433.315204] usb usb4: usb auto-resume [ 433.315226] hub 4-0:1.0: hub_resume [ 433.315239] xhci_hcd 0000:3a:00.0: Get port status 4-1 read: 0x10202e2, return 0x10343 [ 433.315264] usb usb4-port1: status 0343 change 0001 [ 433.315279] xhci_hcd 0000:3a:00.0: clear port1 connect change, portsc: 0x10002e2 [ 433.315293] xhci_hcd 0000:3a:00.0: Get port status 4-2 read: 0x2a0, return 0x2a0 [ 433.317012] xhci_hcd 0000:3a:00.0: xhci_hub_status_data: stopping port polling. [ 433.422282] xhci_hcd 0000:3a:00.0: Get port status 4-1 read: 0x10002e2, return 0x343 [ 433.422307] usb usb4-port1: do warm reset [ 433.422311] usb 4-1: device reset not allowed in state 8 [ 433.422339] hub 4-0:1.0: state 7 ports 2 chg 0002 evt 0000 [ 433.422346] xhci_hcd 0000:3a:00.0: Get port status 4-1 read: 0x10002e2, return 0x343 [ 433.422356] usb usb4-port1: do warm reset [ 433.422358] usb 4-1: device reset not allowed in state 8 [ 433.422428] xhci_hcd 0000:3a:00.0: set port remote wake mask, actual port 0 status = 0xf0002e2 [ 433.422455] xhci_hcd 0000:3a:00.0: set port remote wake mask, actual port 1 status = 0xe0002a0 [ 433.422465] hub 4-0:1.0: hub_suspend [ 433.422475] usb usb4: bus auto-suspend, wakeup 1 [ 433.426161] xhci_hcd 0000:3a:00.0: xhci_hub_status_data: stopping port polling. [ 433.466209] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.510204] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.554051] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.598235] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.642154] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.686204] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.730205] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.774203] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.818207] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.862040] xhci_hcd 0000:3a:00.0: port 0 polling in bus suspend, waiting [ 433.862053] xhci_hcd 0000:3a:00.0: xhci_hub_status_data: stopping port polling. [ 433.862077] xhci_hcd 0000:3a:00.0: xhci_suspend: stopping port polling. [ 433.862096] xhci_hcd 0000:3a:00.0: // Setting command ring address to 0x8578fc001 [ 433.862312] xhci_hcd 0000:3a:00.0: hcd_pci_runtime_suspend: 0 [ 433.862445] xhci_hcd 0000:3a:00.0: PME# enabled [ 433.902376] xhci_hcd 0000:3a:00.0: restoring config space at offset 0xc (was 0x0, writing 0x20) [ 433.902395] xhci_hcd 0000:3a:00.0: restoring config space at offset 0x4 (was 0x100000, writing 0x100403) [ 433.902490] xhci_hcd 0000:3a:00.0: PME# disabled [ 433.902504] xhci_hcd 0000:3a:00.0: enabling bus mastering [ 433.902547] xhci_hcd 0000:3a:00.0: // Setting command ring address to 0x8578fc001 [ 433.902649] pcieport 0000:00:1b.0: PME: Spurious native interrupt! [ 433.902839] xhci_hcd 0000:3a:00.0: Port change event, 4-1, id 3, portsc: 0xb0202e2 [ 433.902842] xhci_hcd 0000:3a:00.0: resume root hub [ 433.902845] xhci_hcd 0000:3a:00.0: handle_port_status: starting port polling. [ 433.902877] xhci_hcd 0000:3a:00.0: xhci_resume: starting port polling. [ 433.902889] xhci_hcd 0000:3a:00.0: xhci_hub_status_data: stopping port polling. [ 433.902891] xhci_hcd 0000:3a:00.0: hcd_pci_runtime_resume: 0 [ 433.902919] usb usb4: usb wakeup-resume [ 433.902942] usb usb4: usb auto-resume [ 433.902966] hub 4-0:1.0: hub_resume ... As Mathias pointed out, the hub enters Cold Attach Status state and requires a warm reset. However usb_reset_device() bails out early when the device is in suspended state, as its callers port_event() and hub_event() don't always resume the device. Since there's nothing wrong to reset a suspended device, allow usb_reset_device() to do so to solve the issue. Signed-off-by: Kai-Heng Feng Acked-by: Alan Stern Cc: stable Link: https://lore.kernel.org/r/20191106062710.29880-1-kai.heng.feng@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index fdcfa85b5b12..1709895387b9 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -5840,7 +5840,7 @@ re_enumerate_no_bos: /** * usb_reset_device - warn interface drivers and perform a USB port reset - * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) + * @udev: device to reset (not in NOTATTACHED state) * * Warns all drivers bound to registered interfaces (using their pre_reset * method), performs the port reset, and then lets the drivers know that @@ -5868,8 +5868,7 @@ int usb_reset_device(struct usb_device *udev) struct usb_host_config *config = udev->actconfig; struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); - if (udev->state == USB_STATE_NOTATTACHED || - udev->state == USB_STATE_SUSPENDED) { + if (udev->state == USB_STATE_NOTATTACHED) { dev_dbg(&udev->dev, "device reset not allowed in state %d\n", udev->state); return -EINVAL; -- cgit From 79c36a704a87533ba1551170354f3fb507ff5b70 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 11:36:36 +0100 Subject: USB: idmouse: simplify disconnect handling Since commit d4ead16f50f9 ("USB: prevent char device open/deregister race") core prevents further calls to open() after usb_deregister_dev() returns so there's no need to use the interface data for synchronisation. This effectively reverts commit 54d2bc068fd2 ("USB: fix locking in idmouse") with respect to the open-disconnect race. Note that the driver already uses a present flag to suppress I/O post disconnect (even if all USB I/O take place at open). Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105103638.4929-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/idmouse.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 20b0f91a5d9b..0386bac224c4 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -60,7 +60,6 @@ static const struct usb_device_id idmouse_table[] = { USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000) MODULE_DEVICE_TABLE(usb, idmouse_table); -static DEFINE_MUTEX(open_disc_mutex); /* structure to hold all of our device specific stuff */ struct usb_idmouse { @@ -227,17 +226,13 @@ static int idmouse_open(struct inode *inode, struct file *file) if (!interface) return -ENODEV; - mutex_lock(&open_disc_mutex); /* get the device information block from the interface */ dev = usb_get_intfdata(interface); - if (!dev) { - mutex_unlock(&open_disc_mutex); + if (!dev) return -ENODEV; - } /* lock this device */ mutex_lock(&dev->lock); - mutex_unlock(&open_disc_mutex); /* check if already open */ if (dev->open) { @@ -280,14 +275,12 @@ static int idmouse_release(struct inode *inode, struct file *file) if (dev == NULL) return -ENODEV; - mutex_lock(&open_disc_mutex); /* lock our device */ mutex_lock(&dev->lock); /* are we really open? */ if (dev->open <= 0) { mutex_unlock(&dev->lock); - mutex_unlock(&open_disc_mutex); return -ENODEV; } @@ -296,11 +289,9 @@ static int idmouse_release(struct inode *inode, struct file *file) if (!dev->present) { /* the device was unplugged before the file was released */ mutex_unlock(&dev->lock); - mutex_unlock(&open_disc_mutex); idmouse_delete(dev); } else { mutex_unlock(&dev->lock); - mutex_unlock(&open_disc_mutex); } return 0; } @@ -379,7 +370,6 @@ static int idmouse_probe(struct usb_interface *interface, if (result) { /* something prevented us from registering this device */ dev_err(&interface->dev, "Unable to allocate minor number.\n"); - usb_set_intfdata(interface, NULL); idmouse_delete(dev); return result; } @@ -392,19 +382,13 @@ static int idmouse_probe(struct usb_interface *interface, static void idmouse_disconnect(struct usb_interface *interface) { - struct usb_idmouse *dev; - - /* get device structure */ - dev = usb_get_intfdata(interface); + struct usb_idmouse *dev = usb_get_intfdata(interface); /* give back our minor */ usb_deregister_dev(interface, &idmouse_class); - mutex_lock(&open_disc_mutex); - usb_set_intfdata(interface, NULL); /* lock the device */ mutex_lock(&dev->lock); - mutex_unlock(&open_disc_mutex); /* prevent device read, write and ioctl */ dev->present = 0; -- cgit From 6710f773b51f21ad7d10198ca6424bea7faaf9d4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 11:36:37 +0100 Subject: USB: idmouse: drop redundant open-count check from release The open count will always be exactly one when release is called, so drop the redundant sanity check. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105103638.4929-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/idmouse.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 0386bac224c4..9b9d5df829d5 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -278,12 +278,6 @@ static int idmouse_release(struct inode *inode, struct file *file) /* lock our device */ mutex_lock(&dev->lock); - /* are we really open? */ - if (dev->open <= 0) { - mutex_unlock(&dev->lock); - return -ENODEV; - } - --dev->open; if (!dev->present) { -- cgit From d3db9c4dc14dae5ebfb74dd5a5ca502a8db63535 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 11:36:38 +0100 Subject: USB: idmouse: clean up runaway white space Drop space between function identifiers and opening parenthesis, which was no longer even used consistently within the driver. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105103638.4929-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/idmouse.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 9b9d5df829d5..4afb5ddfd361 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -56,7 +56,7 @@ static const struct usb_device_id idmouse_table[] = { #define FTIP_SCROLL 0x24 #define ftip_command(dev, command, value, index) \ - usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), command, \ + usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), command, \ USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000) MODULE_DEVICE_TABLE(usb, idmouse_table); @@ -157,8 +157,8 @@ static int idmouse_create_image(struct usb_idmouse *dev) /* loop over a blocking bulk read to get data from the device */ while (bytes_read < IMGSIZE) { - result = usb_bulk_msg (dev->udev, - usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr), + result = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), dev->bulk_in_buffer + bytes_read, dev->bulk_in_size, &bulk_read, 5000); if (result < 0) { @@ -222,7 +222,7 @@ static int idmouse_open(struct inode *inode, struct file *file) int result; /* get the interface from minor number and driver information */ - interface = usb_find_interface (&idmouse_driver, iminor (inode)); + interface = usb_find_interface(&idmouse_driver, iminor(inode)); if (!interface) return -ENODEV; @@ -246,7 +246,7 @@ static int idmouse_open(struct inode *inode, struct file *file) result = usb_autopm_get_interface(interface); if (result) goto error; - result = idmouse_create_image (dev); + result = idmouse_create_image(dev); usb_autopm_put_interface(interface); if (result) goto error; -- cgit From fa5e146eece5657b8bbfae9facce13b7b7dc65b5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:39 +0100 Subject: USB: legousbtower: drop redundant MODULE_LICENSE ifdef The MODULE_LICENSE macro is unconditionally defined in module.h, no need to ifdef its use. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 23061f1526b4..1be84dc13067 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -962,6 +962,4 @@ module_usb_driver(tower_driver); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -#ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); -#endif -- cgit From 07efa8738853696f100b0495b767823308330b3d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:40 +0100 Subject: USB: legousbtower: drop redundant NULL check Drop redundant NULL check from tower_abort_transfers(), which is never called with a NULL argument. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 1be84dc13067..a3ae843e0a3a 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -457,9 +457,6 @@ exit: */ static void tower_abort_transfers (struct lego_usb_tower *dev) { - if (dev == NULL) - return; - /* shutdown transfer */ if (dev->interrupt_in_running) { dev->interrupt_in_running = 0; -- cgit From 30da837a4938438a0259a7bc38dc05d66fa77c08 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:41 +0100 Subject: USB: legousbtower: zero driver data at allocation Zero the driver data at allocation rather than depend on explicit zeroing, which easy to miss. Also drop an unnecessary driver-data pointer initialisation. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index a3ae843e0a3a..c880d58e8683 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -791,45 +791,24 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device { struct device *idev = &interface->dev; struct usb_device *udev = interface_to_usbdev(interface); - struct lego_usb_tower *dev = NULL; + struct lego_usb_tower *dev; struct tower_get_version_reply *get_version_reply = NULL; int retval = -ENOMEM; int result; /* allocate memory for our device state and initialize it */ - - dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL); - + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) goto exit; mutex_init(&dev->lock); - dev->udev = usb_get_dev(udev); - dev->open_count = 0; - dev->disconnected = 0; - - dev->read_buffer = NULL; - dev->read_buffer_length = 0; - dev->read_packet_length = 0; spin_lock_init (&dev->read_buffer_lock); dev->packet_timeout_jiffies = msecs_to_jiffies(packet_timeout); dev->read_last_arrival = jiffies; - init_waitqueue_head (&dev->read_wait); init_waitqueue_head (&dev->write_wait); - dev->interrupt_in_buffer = NULL; - dev->interrupt_in_endpoint = NULL; - dev->interrupt_in_urb = NULL; - dev->interrupt_in_running = 0; - dev->interrupt_in_done = 0; - - dev->interrupt_out_buffer = NULL; - dev->interrupt_out_endpoint = NULL; - dev->interrupt_out_urb = NULL; - dev->interrupt_out_busy = 0; - result = usb_find_common_endpoints_reverse(interface->cur_altsetting, NULL, NULL, &dev->interrupt_in_endpoint, -- cgit From b5a80252e09c2791380cdf10a6ebd7987900748c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:42 +0100 Subject: USB: legousbtower: drop redundant open_count check Drop redundant open_count check in release; the open count is used as a flag and is only set to 0 or 1. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-5-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index c880d58e8683..1626a0d2b12b 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -421,13 +421,6 @@ static int tower_release (struct inode *inode, struct file *file) mutex_lock(&dev->lock); - if (dev->open_count != 1) { - dev_dbg(&dev->udev->dev, "%s: device not opened exactly once\n", - __func__); - retval = -ENODEV; - goto unlock_exit; - } - if (dev->disconnected) { /* the device was unplugged before the file was released */ @@ -444,7 +437,6 @@ static int tower_release (struct inode *inode, struct file *file) tower_abort_transfers (dev); dev->open_count = 0; -unlock_exit: mutex_unlock(&dev->lock); exit: return retval; -- cgit From 835bd2b5672b632b7aedf29e48e3453c182a1b9a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:43 +0100 Subject: USB: legousbtower: drop noisy disconnect messages User space already sees -ENODEV in case it tries to do I/O post disconnect, no need to spam the logs with printk messages that don't even include any device-id information. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-6-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 1626a0d2b12b..937bce23adf0 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -540,7 +540,6 @@ static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, /* verify that the device wasn't unplugged */ if (dev->disconnected) { retval = -ENODEV; - pr_err("No device or device unplugged %d\n", retval); goto unlock_exit; } @@ -626,7 +625,6 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t /* verify that the device wasn't unplugged */ if (dev->disconnected) { retval = -ENODEV; - pr_err("No device or device unplugged %d\n", retval); goto unlock_exit; } -- cgit From 728fcd55e9ac3c7058949d78a443bedc7251b320 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:44 +0100 Subject: USB: legousbtower: drop redundant interrupt-in running flag Drop the redundant interrupt-in-running flag, which tried to keep track of when the interrupt-in URB was in flight. This isn't needed since we can stop the URB unconditionally in tower_abort_transfers() and the URB can not be submitted while usb_kill_urb() is running anyway. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-7-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 937bce23adf0..33533faa019e 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -206,7 +206,6 @@ struct lego_usb_tower { struct usb_endpoint_descriptor* interrupt_in_endpoint; struct urb* interrupt_in_urb; int interrupt_in_interval; - int interrupt_in_running; int interrupt_in_done; char* interrupt_out_buffer; @@ -379,7 +378,6 @@ static int tower_open (struct inode *inode, struct file *file) dev, dev->interrupt_in_interval); - dev->interrupt_in_running = 1; dev->interrupt_in_done = 0; mb(); @@ -387,7 +385,6 @@ static int tower_open (struct inode *inode, struct file *file) if (retval) { dev_err(&dev->udev->dev, "Couldn't submit interrupt_in_urb %d\n", retval); - dev->interrupt_in_running = 0; goto unlock_exit; } @@ -450,11 +447,7 @@ exit: static void tower_abort_transfers (struct lego_usb_tower *dev) { /* shutdown transfer */ - if (dev->interrupt_in_running) { - dev->interrupt_in_running = 0; - mb(); - usb_kill_urb(dev->interrupt_in_urb); - } + usb_kill_urb(dev->interrupt_in_urb); if (dev->interrupt_out_busy) usb_kill_urb(dev->interrupt_out_urb); } @@ -731,15 +724,11 @@ static void tower_interrupt_in_callback (struct urb *urb) } resubmit: - /* resubmit if we're still running */ - if (dev->interrupt_in_running) { - retval = usb_submit_urb (dev->interrupt_in_urb, GFP_ATOMIC); - if (retval) - dev_err(&dev->udev->dev, - "%s: usb_submit_urb failed (%d)\n", - __func__, retval); + retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); + if (retval) { + dev_err(&dev->udev->dev, "%s: usb_submit_urb failed (%d)\n", + __func__, retval); } - exit: dev->interrupt_in_done = 1; wake_up_interruptible (&dev->read_wait); -- cgit From bafd1b1c2cee5513a400efffa58e9d02d8577423 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:45 +0100 Subject: USB: legousbtower: stop interrupt-out URB unconditionally Stop also the interrupt-out URB unconditionally in tower_abort_transfers() which is called from release() (for connected devices). Calling usb_kill_urb() for an idle URB is perfectly fine. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-8-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 33533faa019e..3e409dfe172c 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -448,8 +448,7 @@ static void tower_abort_transfers (struct lego_usb_tower *dev) { /* shutdown transfer */ usb_kill_urb(dev->interrupt_in_urb); - if (dev->interrupt_out_busy) - usb_kill_urb(dev->interrupt_out_urb); + usb_kill_urb(dev->interrupt_out_urb); } -- cgit From aa6f3832458dab256f17f451e00270ae3f5736e4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:46 +0100 Subject: USB: legousbtower: remove tower_abort_transfers() Drop the tower_abort_transfers() function which is now only called from release and instead explicitly kill the two URBs. This incidentally also fixes the outdated comment about freeing memory. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-9-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 3e409dfe172c..f85b6ed2fb8b 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -226,7 +226,6 @@ static int tower_release (struct inode *inode, struct file *file); static __poll_t tower_poll (struct file *file, poll_table *wait); static loff_t tower_llseek (struct file *file, loff_t off, int whence); -static void tower_abort_transfers (struct lego_usb_tower *dev); static void tower_check_for_read_packet (struct lego_usb_tower *dev); static void tower_interrupt_in_callback (struct urb *urb); static void tower_interrupt_out_callback (struct urb *urb); @@ -431,7 +430,11 @@ static int tower_release (struct inode *inode, struct file *file) if (dev->interrupt_out_busy) { wait_event_interruptible_timeout (dev->write_wait, !dev->interrupt_out_busy, 2 * HZ); } - tower_abort_transfers (dev); + + /* shutdown transfers */ + usb_kill_urb(dev->interrupt_in_urb); + usb_kill_urb(dev->interrupt_out_urb); + dev->open_count = 0; mutex_unlock(&dev->lock); @@ -439,19 +442,6 @@ exit: return retval; } - -/** - * tower_abort_transfers - * aborts transfers and frees associated data structures - */ -static void tower_abort_transfers (struct lego_usb_tower *dev) -{ - /* shutdown transfer */ - usb_kill_urb(dev->interrupt_in_urb); - usb_kill_urb(dev->interrupt_out_urb); -} - - /** * tower_check_for_read_packet * -- cgit From 7cfa11a5739d2d4bd9f143b7cd986bccbf7e5d01 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:47 +0100 Subject: USB: legousbtower: clean up pointer declarations in driver data Clean up the pointer declarations in the driver data, whose style wasn't even consistent with the rest of the driver. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-10-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index f85b6ed2fb8b..c8ed8792b931 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -186,13 +186,13 @@ MODULE_DEVICE_TABLE (usb, tower_table); /* Structure to hold all of our device specific stuff */ struct lego_usb_tower { struct mutex lock; /* locks this structure */ - struct usb_device* udev; /* save off the usb device pointer */ + struct usb_device *udev; /* save off the usb device pointer */ unsigned char minor; /* the starting minor number for this device */ int open_count; /* number of times this port has been opened */ unsigned long disconnected:1; - char* read_buffer; + char *read_buffer; size_t read_buffer_length; /* this much came in */ size_t read_packet_length; /* this much will be returned on read */ spinlock_t read_buffer_lock; @@ -202,15 +202,15 @@ struct lego_usb_tower { wait_queue_head_t read_wait; wait_queue_head_t write_wait; - char* interrupt_in_buffer; - struct usb_endpoint_descriptor* interrupt_in_endpoint; - struct urb* interrupt_in_urb; + char *interrupt_in_buffer; + struct usb_endpoint_descriptor *interrupt_in_endpoint; + struct urb *interrupt_in_urb; int interrupt_in_interval; int interrupt_in_done; - char* interrupt_out_buffer; - struct usb_endpoint_descriptor* interrupt_out_endpoint; - struct urb* interrupt_out_urb; + char *interrupt_out_buffer; + struct usb_endpoint_descriptor *interrupt_out_endpoint; + struct urb *interrupt_out_urb; int interrupt_out_interval; int interrupt_out_busy; -- cgit From a76c234faf7a904e57515346f66317b0b3543d3c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:48 +0100 Subject: USB: legousbtower: drop unnecessary packed attributes Drop the packed attributes from the two message structs whose fields are naturally aligned and do not have any padding. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-11-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index c8ed8792b931..a9b08468a302 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -160,7 +160,7 @@ struct tower_reset_reply { __le16 size; /* little-endian */ __u8 err_code; __u8 spare; -} __attribute__ ((packed)); +}; struct tower_get_version_reply { __le16 size; /* little-endian */ @@ -169,7 +169,7 @@ struct tower_get_version_reply { __u8 major; __u8 minor; __le16 build_no; /* little-endian */ -} __attribute__ ((packed)); +}; /* table of devices that work with this driver */ -- cgit From 728772489d9d9dde24739bb9f0b4a4fc8ce61506 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:49 +0100 Subject: USB: legousbtower: drop redundant endianness comments The endianness is already encoded in the type specifier so drop the redundant little-endian comments from the message structs. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-12-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index a9b08468a302..03f6861dfdf0 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -157,18 +157,18 @@ MODULE_PARM_DESC(interrupt_out_interval, "Interrupt out interval in ms"); #define LEGO_USB_TOWER_REQUEST_GET_VERSION 0xFD struct tower_reset_reply { - __le16 size; /* little-endian */ + __le16 size; __u8 err_code; __u8 spare; }; struct tower_get_version_reply { - __le16 size; /* little-endian */ + __le16 size; __u8 err_code; __u8 spare; __u8 major; __u8 minor; - __le16 build_no; /* little-endian */ + __le16 build_no; }; -- cgit From a0cd1df9d3e2de949379738b9e2e06172494c5d8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:50 +0100 Subject: USB: legousbtower: clean up runaway white space Drop space between function identifiers and opening parenthesis, which was no longer even used consistently within the driver. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-13-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 172 ++++++++++++++++++++-------------------- 1 file changed, 87 insertions(+), 85 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 03f6861dfdf0..6f34b3802332 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -178,7 +178,7 @@ static const struct usb_device_id tower_table[] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, tower_table); +MODULE_DEVICE_TABLE(usb, tower_table); #define LEGO_USB_TOWER_MINOR_BASE 160 @@ -218,20 +218,20 @@ struct lego_usb_tower { /* local function prototypes */ -static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos); -static ssize_t tower_write (struct file *file, const char __user *buffer, size_t count, loff_t *ppos); -static inline void tower_delete (struct lego_usb_tower *dev); -static int tower_open (struct inode *inode, struct file *file); -static int tower_release (struct inode *inode, struct file *file); -static __poll_t tower_poll (struct file *file, poll_table *wait); -static loff_t tower_llseek (struct file *file, loff_t off, int whence); +static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos); +static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); +static inline void tower_delete(struct lego_usb_tower *dev); +static int tower_open(struct inode *inode, struct file *file); +static int tower_release(struct inode *inode, struct file *file); +static __poll_t tower_poll(struct file *file, poll_table *wait); +static loff_t tower_llseek(struct file *file, loff_t off, int whence); -static void tower_check_for_read_packet (struct lego_usb_tower *dev); -static void tower_interrupt_in_callback (struct urb *urb); -static void tower_interrupt_out_callback (struct urb *urb); +static void tower_check_for_read_packet(struct lego_usb_tower *dev); +static void tower_interrupt_in_callback(struct urb *urb); +static void tower_interrupt_out_callback(struct urb *urb); -static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id); -static void tower_disconnect (struct usb_interface *interface); +static int tower_probe(struct usb_interface *interface, const struct usb_device_id *id); +static void tower_disconnect(struct usb_interface *interface); /* file operations needed when we register this driver */ @@ -286,23 +286,23 @@ static inline void lego_usb_tower_debug_data(struct device *dev, /** * tower_delete */ -static inline void tower_delete (struct lego_usb_tower *dev) +static inline void tower_delete(struct lego_usb_tower *dev) { /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); - kfree (dev->read_buffer); - kfree (dev->interrupt_in_buffer); - kfree (dev->interrupt_out_buffer); + kfree(dev->read_buffer); + kfree(dev->interrupt_in_buffer); + kfree(dev->interrupt_out_buffer); usb_put_dev(dev->udev); - kfree (dev); + kfree(dev); } /** * tower_open */ -static int tower_open (struct inode *inode, struct file *file) +static int tower_open(struct inode *inode, struct file *file) { struct lego_usb_tower *dev = NULL; int subminor; @@ -321,7 +321,7 @@ static int tower_open (struct inode *inode, struct file *file) nonseekable_open(inode, file); subminor = iminor(inode); - interface = usb_find_interface (&tower_driver, subminor); + interface = usb_find_interface(&tower_driver, subminor); if (!interface) { pr_err("error, can't find device for minor %d\n", subminor); @@ -349,15 +349,15 @@ static int tower_open (struct inode *inode, struct file *file) } /* reset the tower */ - result = usb_control_msg (dev->udev, - usb_rcvctrlpipe(dev->udev, 0), - LEGO_USB_TOWER_REQUEST_RESET, - USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, - 0, - 0, - reset_reply, - sizeof(*reset_reply), - 1000); + result = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + LEGO_USB_TOWER_REQUEST_RESET, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + reset_reply, + sizeof(*reset_reply), + 1000); if (result < 0) { dev_err(&dev->udev->dev, "LEGO USB Tower reset control request failed\n"); @@ -368,19 +368,19 @@ static int tower_open (struct inode *inode, struct file *file) /* initialize in direction */ dev->read_buffer_length = 0; dev->read_packet_length = 0; - usb_fill_int_urb (dev->interrupt_in_urb, - dev->udev, - usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), - dev->interrupt_in_buffer, - usb_endpoint_maxp(dev->interrupt_in_endpoint), - tower_interrupt_in_callback, - dev, - dev->interrupt_in_interval); + usb_fill_int_urb(dev->interrupt_in_urb, + dev->udev, + usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), + dev->interrupt_in_buffer, + usb_endpoint_maxp(dev->interrupt_in_endpoint), + tower_interrupt_in_callback, + dev, + dev->interrupt_in_interval); dev->interrupt_in_done = 0; mb(); - retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL); + retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); if (retval) { dev_err(&dev->udev->dev, "Couldn't submit interrupt_in_urb %d\n", retval); @@ -403,7 +403,7 @@ exit: /** * tower_release */ -static int tower_release (struct inode *inode, struct file *file) +static int tower_release(struct inode *inode, struct file *file) { struct lego_usb_tower *dev; int retval = 0; @@ -422,13 +422,14 @@ static int tower_release (struct inode *inode, struct file *file) /* unlock here as tower_delete frees dev */ mutex_unlock(&dev->lock); - tower_delete (dev); + tower_delete(dev); goto exit; } /* wait until write transfer is finished */ if (dev->interrupt_out_busy) { - wait_event_interruptible_timeout (dev->write_wait, !dev->interrupt_out_busy, 2 * HZ); + wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, + 2 * HZ); } /* shutdown transfers */ @@ -450,23 +451,23 @@ exit: * until it has been there unchanged for at least * dev->packet_timeout_jiffies, or until the buffer is full. */ -static void tower_check_for_read_packet (struct lego_usb_tower *dev) +static void tower_check_for_read_packet(struct lego_usb_tower *dev) { - spin_lock_irq (&dev->read_buffer_lock); + spin_lock_irq(&dev->read_buffer_lock); if (!packet_timeout || time_after(jiffies, dev->read_last_arrival + dev->packet_timeout_jiffies) || dev->read_buffer_length == read_buffer_size) { dev->read_packet_length = dev->read_buffer_length; } dev->interrupt_in_done = 0; - spin_unlock_irq (&dev->read_buffer_lock); + spin_unlock_irq(&dev->read_buffer_lock); } /** * tower_poll */ -static __poll_t tower_poll (struct file *file, poll_table *wait) +static __poll_t tower_poll(struct file *file, poll_table *wait) { struct lego_usb_tower *dev; __poll_t mask = 0; @@ -494,7 +495,7 @@ static __poll_t tower_poll (struct file *file, poll_table *wait) /** * tower_llseek */ -static loff_t tower_llseek (struct file *file, loff_t off, int whence) +static loff_t tower_llseek(struct file *file, loff_t off, int whence) { return -ESPIPE; /* unseekable */ } @@ -503,7 +504,7 @@ static loff_t tower_llseek (struct file *file, loff_t off, int whence) /** * tower_read */ -static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos) +static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct lego_usb_tower *dev; size_t bytes_to_read; @@ -536,7 +537,7 @@ static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, } /* wait for data */ - tower_check_for_read_packet (dev); + tower_check_for_read_packet(dev); while (dev->read_packet_length == 0) { if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; @@ -553,28 +554,28 @@ static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, timeout = jiffies + msecs_to_jiffies(read_timeout); } /* check for read timeout */ - if (read_timeout && time_after (jiffies, timeout)) { + if (read_timeout && time_after(jiffies, timeout)) { retval = -ETIMEDOUT; goto unlock_exit; } - tower_check_for_read_packet (dev); + tower_check_for_read_packet(dev); } /* copy the data from read_buffer into userspace */ bytes_to_read = min(count, dev->read_packet_length); - if (copy_to_user (buffer, dev->read_buffer, bytes_to_read)) { + if (copy_to_user(buffer, dev->read_buffer, bytes_to_read)) { retval = -EFAULT; goto unlock_exit; } - spin_lock_irq (&dev->read_buffer_lock); + spin_lock_irq(&dev->read_buffer_lock); dev->read_buffer_length -= bytes_to_read; dev->read_packet_length -= bytes_to_read; for (i=0; iread_buffer_length; i++) { dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read]; } - spin_unlock_irq (&dev->read_buffer_lock); + spin_unlock_irq(&dev->read_buffer_lock); retval = bytes_to_read; @@ -590,7 +591,7 @@ exit: /** * tower_write */ -static ssize_t tower_write (struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct lego_usb_tower *dev; size_t bytes_to_write; @@ -622,7 +623,8 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t retval = -EAGAIN; goto unlock_exit; } - retval = wait_event_interruptible (dev->write_wait, !dev->interrupt_out_busy); + retval = wait_event_interruptible(dev->write_wait, + !dev->interrupt_out_busy); if (retval) { goto unlock_exit; } @@ -633,7 +635,7 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t dev_dbg(&dev->udev->dev, "%s: count = %zd, bytes_to_write = %zd\n", __func__, count, bytes_to_write); - if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) { + if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) { retval = -EFAULT; goto unlock_exit; } @@ -651,7 +653,7 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t dev->interrupt_out_busy = 1; wmb(); - retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL); + retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL); if (retval) { dev->interrupt_out_busy = 0; dev_err(&dev->udev->dev, @@ -672,7 +674,7 @@ exit: /** * tower_interrupt_in_callback */ -static void tower_interrupt_in_callback (struct urb *urb) +static void tower_interrupt_in_callback(struct urb *urb) { struct lego_usb_tower *dev = urb->context; int status = urb->status; @@ -698,9 +700,9 @@ static void tower_interrupt_in_callback (struct urb *urb) if (urb->actual_length > 0) { spin_lock_irqsave(&dev->read_buffer_lock, flags); if (dev->read_buffer_length + urb->actual_length < read_buffer_size) { - memcpy (dev->read_buffer + dev->read_buffer_length, - dev->interrupt_in_buffer, - urb->actual_length); + memcpy(dev->read_buffer + dev->read_buffer_length, + dev->interrupt_in_buffer, + urb->actual_length); dev->read_buffer_length += urb->actual_length; dev->read_last_arrival = jiffies; dev_dbg(&dev->udev->dev, "%s: received %d bytes\n", @@ -720,14 +722,14 @@ resubmit: } exit: dev->interrupt_in_done = 1; - wake_up_interruptible (&dev->read_wait); + wake_up_interruptible(&dev->read_wait); } /** * tower_interrupt_out_callback */ -static void tower_interrupt_out_callback (struct urb *urb) +static void tower_interrupt_out_callback(struct urb *urb) { struct lego_usb_tower *dev = urb->context; int status = urb->status; @@ -755,7 +757,7 @@ static void tower_interrupt_out_callback (struct urb *urb) * Called by the usb core when a new device is connected that it thinks * this driver might be interested in. */ -static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id) +static int tower_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct device *idev = &interface->dev; struct usb_device *udev = interface_to_usbdev(interface); @@ -771,11 +773,11 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device mutex_init(&dev->lock); dev->udev = usb_get_dev(udev); - spin_lock_init (&dev->read_buffer_lock); + spin_lock_init(&dev->read_buffer_lock); dev->packet_timeout_jiffies = msecs_to_jiffies(packet_timeout); dev->read_last_arrival = jiffies; - init_waitqueue_head (&dev->read_wait); - init_waitqueue_head (&dev->write_wait); + init_waitqueue_head(&dev->read_wait); + init_waitqueue_head(&dev->write_wait); result = usb_find_common_endpoints_reverse(interface->cur_altsetting, NULL, NULL, @@ -787,16 +789,16 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device goto error; } - dev->read_buffer = kmalloc (read_buffer_size, GFP_KERNEL); + dev->read_buffer = kmalloc(read_buffer_size, GFP_KERNEL); if (!dev->read_buffer) goto error; - dev->interrupt_in_buffer = kmalloc (usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL); + dev->interrupt_in_buffer = kmalloc(usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL); if (!dev->interrupt_in_buffer) goto error; dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_in_urb) goto error; - dev->interrupt_out_buffer = kmalloc (write_buffer_size, GFP_KERNEL); + dev->interrupt_out_buffer = kmalloc(write_buffer_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) goto error; dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -813,15 +815,15 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device } /* get the firmware version and log it */ - result = usb_control_msg (udev, - usb_rcvctrlpipe(udev, 0), - LEGO_USB_TOWER_REQUEST_GET_VERSION, - USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, - 0, - 0, - get_version_reply, - sizeof(*get_version_reply), - 1000); + result = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + LEGO_USB_TOWER_REQUEST_GET_VERSION, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + get_version_reply, + sizeof(*get_version_reply), + 1000); if (result != sizeof(*get_version_reply)) { if (result >= 0) result = -EIO; @@ -836,9 +838,9 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device le16_to_cpu(get_version_reply->build_no)); /* we can register the device now, as it is ready */ - usb_set_intfdata (interface, dev); + usb_set_intfdata(interface, dev); - retval = usb_register_dev (interface, &tower_class); + retval = usb_register_dev(interface, &tower_class); if (retval) { /* something prevented us from registering this driver */ @@ -868,17 +870,17 @@ error: * * Called by the usb core when the device is removed from the system. */ -static void tower_disconnect (struct usb_interface *interface) +static void tower_disconnect(struct usb_interface *interface) { struct lego_usb_tower *dev; int minor; - dev = usb_get_intfdata (interface); + dev = usb_get_intfdata(interface); minor = dev->minor; /* give back our minor and prevent further open() */ - usb_deregister_dev (interface, &tower_class); + usb_deregister_dev(interface, &tower_class); /* stop I/O */ usb_poison_urb(dev->interrupt_in_urb); @@ -889,7 +891,7 @@ static void tower_disconnect (struct usb_interface *interface) /* if the device is not opened, then we clean up right now */ if (!dev->open_count) { mutex_unlock(&dev->lock); - tower_delete (dev); + tower_delete(dev); } else { dev->disconnected = 1; /* wake up pollers */ -- cgit From 3c84f4bbe33f1f83a217499248a9f7dc20764040 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:51 +0100 Subject: USB: legousbtower: drop superfluous brackets Drop superfluous brackets around single-line blocks. Also add missing white space around operators in a for-expression being modified. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-14-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 6f34b3802332..6b7ea80d5876 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -481,12 +481,10 @@ static __poll_t tower_poll(struct file *file, poll_table *wait) poll_wait(file, &dev->write_wait, wait); tower_check_for_read_packet(dev); - if (dev->read_packet_length > 0) { + if (dev->read_packet_length > 0) mask |= EPOLLIN | EPOLLRDNORM; - } - if (!dev->interrupt_out_busy) { + if (!dev->interrupt_out_busy) mask |= EPOLLOUT | EPOLLWRNORM; - } return mask; } @@ -532,9 +530,8 @@ static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, goto unlock_exit; } - if (read_timeout) { + if (read_timeout) timeout = jiffies + msecs_to_jiffies(read_timeout); - } /* wait for data */ tower_check_for_read_packet(dev); @@ -544,9 +541,8 @@ static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, goto unlock_exit; } retval = wait_event_interruptible_timeout(dev->read_wait, dev->interrupt_in_done, dev->packet_timeout_jiffies); - if (retval < 0) { + if (retval < 0) goto unlock_exit; - } /* reset read timeout during read or write activity */ if (read_timeout @@ -572,9 +568,8 @@ static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, spin_lock_irq(&dev->read_buffer_lock); dev->read_buffer_length -= bytes_to_read; dev->read_packet_length -= bytes_to_read; - for (i=0; iread_buffer_length; i++) { + for (i = 0; i < dev->read_buffer_length; i++) dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read]; - } spin_unlock_irq(&dev->read_buffer_lock); retval = bytes_to_read; @@ -625,9 +620,8 @@ static ssize_t tower_write(struct file *file, const char __user *buffer, size_t } retval = wait_event_interruptible(dev->write_wait, !dev->interrupt_out_busy); - if (retval) { + if (retval) goto unlock_exit; - } } /* write the data into interrupt_out_buffer from userspace */ -- cgit From c1f602da92ccc40cfe6ea60c1b3dcd172b4f7b5e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 Nov 2019 09:41:52 +0100 Subject: USB: legousbtower: drop superfluous newlines Drop some superfluous newlines before conditionals which made the code harder to read. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191105084152.16322-15-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 6b7ea80d5876..ab4b98b04115 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -312,7 +312,6 @@ static int tower_open(struct inode *inode, struct file *file) int result; reset_reply = kmalloc(sizeof(*reset_reply), GFP_KERNEL); - if (!reset_reply) { retval = -ENOMEM; goto exit; @@ -322,7 +321,6 @@ static int tower_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&tower_driver, subminor); - if (!interface) { pr_err("error, can't find device for minor %d\n", subminor); retval = -ENODEV; @@ -409,7 +407,6 @@ static int tower_release(struct inode *inode, struct file *file) int retval = 0; dev = file->private_data; - if (dev == NULL) { retval = -ENODEV; goto exit; @@ -802,7 +799,6 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; get_version_reply = kmalloc(sizeof(*get_version_reply), GFP_KERNEL); - if (!get_version_reply) { retval = -ENOMEM; goto error; @@ -835,7 +831,6 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ usb_set_intfdata(interface, dev); retval = usb_register_dev(interface, &tower_class); - if (retval) { /* something prevented us from registering this driver */ dev_err(idev, "Not able to get a minor for this device.\n"); -- cgit From 91feb01596e5efc0cc922cc73f5583114dccf4d2 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 6 Nov 2019 13:49:01 +0100 Subject: appledisplay: fix error handling in the scheduled work The work item can operate on 1. stale memory left over from the last transfer the actual length of the data transfered needs to be checked 2. memory already freed the error handling in appledisplay_probe() needs to cancel the work in that case Reported-and-tested-by: syzbot+495dab1f175edc9c2f13@syzkaller.appspotmail.com Signed-off-by: Oliver Neukum Cc: stable Link: https://lore.kernel.org/r/20191106124902.7765-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/appledisplay.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index ac92725458b5..ba1eaabc7796 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -164,7 +164,12 @@ static int appledisplay_bl_get_brightness(struct backlight_device *bd) 0, pdata->msgdata, 2, ACD_USB_TIMEOUT); - brightness = pdata->msgdata[1]; + if (retval < 2) { + if (retval >= 0) + retval = -EMSGSIZE; + } else { + brightness = pdata->msgdata[1]; + } mutex_unlock(&pdata->sysfslock); if (retval < 0) @@ -299,6 +304,7 @@ error: if (pdata) { if (pdata->urb) { usb_kill_urb(pdata->urb); + cancel_delayed_work_sync(&pdata->work); if (pdata->urbdata) usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN, pdata->urbdata, pdata->urb->transfer_dma); -- cgit From 957c31ea082e3fe5196f46d5b04018b10de47400 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Thu, 7 Nov 2019 11:55:08 +0100 Subject: USB: serial: option: add support for DW5821e with eSIM support The device exposes AT, NMEA and DIAG ports in both USB configurations. Exactly same layout as the default DW5821e module, just a different vid/pid. P: Vendor=413c ProdID=81e0 Rev=03.18 S: Manufacturer=Dell Inc. S: Product=DW5821e-eSIM Snapdragon X20 LTE S: SerialNumber=0123456789ABCDEF C: #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=500mA I: If#=0x0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan I: If#=0x1 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=00 Prot=00 Driver=usbhid I: If#=0x2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option P: Vendor=413c ProdID=81e0 Rev=03.18 S: Manufacturer=Dell Inc. S: Product=DW5821e-eSIM Snapdragon X20 LTE S: SerialNumber=0123456789ABCDEF C: #Ifs= 7 Cfg#= 2 Atr=a0 MxPwr=500mA I: If#=0x0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim I: If#=0x1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I: If#=0x2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option I: If#=0x6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) Signed-off-by: Aleksander Morgado Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 06ab016be0b6..2023f1f4edaf 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -197,6 +197,7 @@ static void option_instat_callback(struct urb *urb); #define DELL_PRODUCT_5804_MINICARD_ATT 0x819b /* Novatel E371 */ #define DELL_PRODUCT_5821E 0x81d7 +#define DELL_PRODUCT_5821E_ESIM 0x81e0 #define KYOCERA_VENDOR_ID 0x0c88 #define KYOCERA_PRODUCT_KPC650 0x17da @@ -1044,6 +1045,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5804_MINICARD_ATT, 0xff, 0xff, 0xff) }, { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5821E), .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5821E_ESIM), + .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) }, -- cgit From 92aa5986f4f7b5a8bf282ca0f50967f4326559f5 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 7 Nov 2019 15:28:55 +0100 Subject: USB: chaoskey: fix error case of a timeout In case of a timeout or if a signal aborts a read communication with the device needs to be ended lest we overwrite an active URB the next time we do IO to the device, as the URB may still be active. Signed-off-by: Oliver Neukum Cc: stable Link: https://lore.kernel.org/r/20191107142856.16774-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/chaoskey.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 34e6cd6f40d3..87067c3d6109 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -384,13 +384,17 @@ static int _chaoskey_fill(struct chaoskey *dev) !dev->reading, (started ? NAK_TIMEOUT : ALEA_FIRST_TIMEOUT) ); - if (result < 0) + if (result < 0) { + usb_kill_urb(dev->urb); goto out; + } - if (result == 0) + if (result == 0) { result = -ETIMEDOUT; - else + usb_kill_urb(dev->urb); + } else { result = dev->valid; + } out: /* Let the device go back to sleep eventually */ usb_autopm_put_interface(dev->interface); @@ -526,7 +530,21 @@ static int chaoskey_suspend(struct usb_interface *interface, static int chaoskey_resume(struct usb_interface *interface) { + struct chaoskey *dev; + struct usb_device *udev = interface_to_usbdev(interface); + usb_dbg(interface, "resume"); + dev = usb_get_intfdata(interface); + + /* + * We may have lost power. + * In that case the device that needs a long time + * for the first requests needs an extended timeout + * again + */ + if (le16_to_cpu(udev->descriptor.idVendor) == ALEA_VENDOR_ID) + dev->reads_started = false; + return 0; } #else -- cgit From ea422312a462696093b5db59d294439796cba4ad Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:21:18 +0100 Subject: USB: serial: mos7720: fix remote wakeup The driver was setting the device remote-wakeup feature during probe in violation of the USB specification (which says it should only be set just prior to suspending the device). This could potentially waste power during suspend as well as lead to spurious wakeups. Note that USB core would clear the remote-wakeup feature at first resume. Fixes: 0f64478cbc7a ("USB: add USB serial mos7720 driver") Cc: stable # 2.6.19 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7720.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 18110225d506..2ec4eeacebc7 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1833,10 +1833,6 @@ static int mos7720_startup(struct usb_serial *serial) product = le16_to_cpu(serial->dev->descriptor.idProduct); dev = serial->dev; - /* setting configuration feature to one */ - usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000); - if (product == MOSCHIP_DEVICE_ID_7715) { struct urb *urb = serial->port[0]->interrupt_in_urb; -- cgit From 92fe35fb9c70a00d8fbbf5bd6172c921dd9c7815 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:21:19 +0100 Subject: USB: serial: mos7840: fix remote wakeup The driver was setting the device remote-wakeup feature during probe in violation of the USB specification (which says it should only be set just prior to suspending the device). This could potentially waste power during suspend as well as lead to spurious wakeups. Note that USB core would clear the remote-wakeup feature at first resume. Fixes: 3f5429746d91 ("USB: Moschip 7840 USB-Serial Driver") Cc: stable # 2.6.19 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 3eeeee38debc..ab4bf8d6d7df 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2290,11 +2290,6 @@ out: goto error; } else dev_dbg(&port->dev, "ZLP_REG5 Writing success status%d\n", status); - - /* setting configuration feature to one */ - usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - 0x03, 0x00, 0x01, 0x00, NULL, 0x00, - MOS_WDR_TIMEOUT); } return 0; error: -- cgit From 375cb533c00a237264cc1d810d22f70de30362c2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:28:54 +0100 Subject: USB: serial: mos7840: clean up device-type handling The current device-type detection is fragile and can't really be relied upon. Instead of sprinkling device-id conditionals throughout the driver, let's use the device-id table to encode the number of ports and whether the device has a driver-controlled activity LED (MCS7810). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 121 ++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 77 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index ab4bf8d6d7df..f13cf723fa6c 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -89,17 +89,10 @@ /* For higher baud Rates use TIOCEXBAUD */ #define TIOCEXBAUD 0x5462 -/* vendor id and device id defines */ - -/* The native mos7840/7820 component */ -#define USB_VENDOR_ID_MOSCHIP 0x9710 -#define MOSCHIP_DEVICE_ID_7840 0x7840 -#define MOSCHIP_DEVICE_ID_7843 0x7843 -#define MOSCHIP_DEVICE_ID_7820 0x7820 -#define MOSCHIP_DEVICE_ID_7810 0x7810 -/* The native component can have its vendor/device id's overridden - * in vendor-specific implementations. Such devices can be handled - * by making a change here, in id_table. +/* + * Vendor id and device id defines + * + * NOTE: Do not add new defines, add entries directly to the id_table instead. */ #define USB_VENDOR_ID_BANDB 0x0856 #define BANDB_DEVICE_ID_USO9ML2_2 0xAC22 @@ -116,18 +109,6 @@ #define BANDB_DEVICE_ID_USOPTL4_4P 0xBC03 #define BANDB_DEVICE_ID_USOPTL2_4 0xAC24 -/* This driver also supports - * ATEN UC2324 device using Moschip MCS7840 - * ATEN UC2322 device using Moschip MCS7820 - * MOXA UPort 2210 device using Moschip MCS7820 - */ -#define USB_VENDOR_ID_ATENINTL 0x0557 -#define ATENINTL_DEVICE_ID_UC2324 0x2011 -#define ATENINTL_DEVICE_ID_UC2322 0x7820 - -#define USB_VENDOR_ID_MOXA 0x110a -#define MOXA_DEVICE_ID_2210 0x2210 - /* Interrupt Routine Defines */ #define SERIAL_IIR_RLS 0x06 @@ -179,27 +160,34 @@ enum mos7840_flag { MOS7840_FLAG_LED_BUSY, }; +#define MCS_PORT_MASK GENMASK(2, 0) +#define MCS_PORTS(nr) ((nr) & MCS_PORT_MASK) +#define MCS_LED BIT(3) + +#define MCS_DEVICE(vid, pid, flags) \ + USB_DEVICE((vid), (pid)), .driver_info = (flags) + static const struct usb_device_id id_table[] = { - {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, - {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7843)}, - {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, - {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7810)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4P)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2P)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4P)}, - {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL2_4)}, - {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2324)}, - {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2322)}, - {USB_DEVICE(USB_VENDOR_ID_MOXA, MOXA_DEVICE_ID_2210)}, + { MCS_DEVICE(0x0557, 0x2011, MCS_PORTS(4)) }, /* ATEN UC2324 */ + { MCS_DEVICE(0x0557, 0x7820, MCS_PORTS(2)) }, /* ATEN UC2322 */ + { MCS_DEVICE(0x110a, 0x2210, MCS_PORTS(2)) }, /* Moxa UPort 2210 */ + { MCS_DEVICE(0x9710, 0x7810, MCS_PORTS(1) | MCS_LED) }, /* ASIX MCS7810 */ + { MCS_DEVICE(0x9710, 0x7820, MCS_PORTS(2)) }, /* MosChip MCS7820 */ + { MCS_DEVICE(0x9710, 0x7840, MCS_PORTS(4)) }, /* MosChip MCS7840 */ + { MCS_DEVICE(0x9710, 0x7843, MCS_PORTS(3)) }, /* ASIX MCS7840 3 port */ + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4P) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2P) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4P) }, + { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL2_4) }, {} /* terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); @@ -2024,22 +2012,12 @@ static int mos7810_check(struct usb_serial *serial) static int mos7840_probe(struct usb_serial *serial, const struct usb_device_id *id) { - u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); - u16 vid = le16_to_cpu(serial->dev->descriptor.idVendor); + unsigned long device_flags = id->driver_info; u8 *buf; - int device_type; - if (product == MOSCHIP_DEVICE_ID_7810 || - product == MOSCHIP_DEVICE_ID_7820 || - product == MOSCHIP_DEVICE_ID_7843) { - device_type = product; + /* Skip device-type detection if we already have device flags. */ + if (device_flags) goto out; - } - - if (vid == USB_VENDOR_ID_MOXA && product == MOXA_DEVICE_ID_2210) { - device_type = MOSCHIP_DEVICE_ID_7820; - goto out; - } buf = kzalloc(VENDOR_READ_LENGTH, GFP_KERNEL); if (!buf) @@ -2051,15 +2029,15 @@ static int mos7840_probe(struct usb_serial *serial, /* For a MCS7840 device GPIO0 must be set to 1 */ if (buf[0] & 0x01) - device_type = MOSCHIP_DEVICE_ID_7840; + device_flags = MCS_PORTS(4); else if (mos7810_check(serial)) - device_type = MOSCHIP_DEVICE_ID_7810; + device_flags = MCS_PORTS(1) | MCS_LED; else - device_type = MOSCHIP_DEVICE_ID_7820; + device_flags = MCS_PORTS(2); kfree(buf); out: - usb_set_serial_data(serial, (void *)(unsigned long)device_type); + usb_set_serial_data(serial, (void *)device_flags); return 0; } @@ -2067,19 +2045,10 @@ out: static int mos7840_calc_num_ports(struct usb_serial *serial, struct usb_serial_endpoints *epds) { - int device_type = (unsigned long)usb_get_serial_data(serial); - int num_ports; + unsigned long device_flags = (unsigned long)usb_get_serial_data(serial); + int num_ports = MCS_PORTS(device_flags); - if (device_type == MOSCHIP_DEVICE_ID_7843) - num_ports = 3; - else - num_ports = (device_type >> 4) & 0x000F; - - /* - * num_ports is currently never zero as device_type is one of - * MOSCHIP_DEVICE_ID_78{1,2,4}0. - */ - if (num_ports == 0) + if (num_ports == 0 || num_ports > 4) return -ENODEV; if (epds->num_bulk_in < num_ports || epds->num_bulk_out < num_ports) { @@ -2093,7 +2062,7 @@ static int mos7840_calc_num_ports(struct usb_serial *serial, static int mos7840_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - int device_type = (unsigned long)usb_get_serial_data(serial); + unsigned long device_flags = (unsigned long)usb_get_serial_data(serial); struct moschip_port *mos7840_port; int status; int pnum; @@ -2255,12 +2224,10 @@ static int mos7840_port_probe(struct usb_serial_port *port) goto error; } - mos7840_port->has_led = false; + mos7840_port->has_led = device_flags & MCS_LED; /* Initialize LED timers */ - if (device_type == MOSCHIP_DEVICE_ID_7810) { - mos7840_port->has_led = true; - + if (mos7840_port->has_led) { mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL); mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr), GFP_KERNEL); -- cgit From 1c333550ea92a9c7a5fdf0f3474eba3387478f20 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:28:55 +0100 Subject: USB: serial: mos7840: document MCS7810 detection hack Document the MCS7810 detection hack which relies on having the GPO and GPI pins connected as recommended by ASIX. Note that GPO (pin 42) is really RTS of the third port which will be toggled for the corresponding physical port on two- and four-port devices. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index f13cf723fa6c..6de41a3c2dab 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1955,6 +1955,13 @@ static int mos7840_ioctl(struct tty_struct *tty, return -ENOIOCTLCMD; } +/* + * Check if GPO (pin 42) is connected to GPI (pin 33) as recommended by ASIX + * for MCS7810 by bit-banging a 16-bit word. + * + * Note that GPO is really RTS of the third port so this will toggle RTS of + * port two or three on two- and four-port devices. + */ static int mos7810_check(struct usb_serial *serial) { int i, pass_count = 0; -- cgit From 960fbd1ca584a5b4cd818255769769d42bfc6dbe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:28:56 +0100 Subject: USB: serial: mos7840: fix probe error handling The driver would return success and leave the port structures half-initialised if any of the register accesses during probe fails. This would specifically leave the port control urb unallocated, something which could trigger a NULL pointer dereference on interrupt events. Fortunately the interrupt implementation is completely broken and has never even been enabled... Note that the zero-length-enable register write used to set the zle-flag for all ports is moved to attach. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 48 ++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 6de41a3c2dab..ddd8db3be110 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2066,6 +2066,23 @@ static int mos7840_calc_num_ports(struct usb_serial *serial, return num_ports; } +static int mos7840_attach(struct usb_serial *serial) +{ + struct device *dev = &serial->interface->dev; + int status; + u16 val; + + /* Zero Length flag enable */ + val = 0x0f; + status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, val); + if (status < 0) + dev_dbg(dev, "Writing ZLP_REG5 failed status-0x%x\n", status); + else + dev_dbg(dev, "ZLP_REG5 Writing success status%d\n", status); + + return status; +} + static int mos7840_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; @@ -2123,7 +2140,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) mos7840_port->ControlRegOffset, &Data); if (status < 0) { dev_dbg(&port->dev, "Reading ControlReg failed status-0x%x\n", status); - goto out; + goto error; } else dev_dbg(&port->dev, "ControlReg Reading success val is %x, status%d\n", Data, status); Data |= 0x08; /* setting driver done bit */ @@ -2135,7 +2152,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) mos7840_port->ControlRegOffset, Data); if (status < 0) { dev_dbg(&port->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status); - goto out; + goto error; } else dev_dbg(&port->dev, "ControlReg Writing success(rx_disable) status%d\n", status); @@ -2146,7 +2163,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) (__u16) (mos7840_port->DcrRegOffset + 0), Data); if (status < 0) { dev_dbg(&port->dev, "Writing DCR0 failed status-0x%x\n", status); - goto out; + goto error; } else dev_dbg(&port->dev, "DCR0 Writing success status%d\n", status); @@ -2155,7 +2172,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) (__u16) (mos7840_port->DcrRegOffset + 1), Data); if (status < 0) { dev_dbg(&port->dev, "Writing DCR1 failed status-0x%x\n", status); - goto out; + goto error; } else dev_dbg(&port->dev, "DCR1 Writing success status%d\n", status); @@ -2164,7 +2181,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) (__u16) (mos7840_port->DcrRegOffset + 2), Data); if (status < 0) { dev_dbg(&port->dev, "Writing DCR2 failed status-0x%x\n", status); - goto out; + goto error; } else dev_dbg(&port->dev, "DCR2 Writing success status%d\n", status); @@ -2173,7 +2190,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) status = mos7840_set_reg_sync(port, CLK_START_VALUE_REGISTER, Data); if (status < 0) { dev_dbg(&port->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status); - goto out; + goto error; } else dev_dbg(&port->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status); @@ -2190,7 +2207,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) status = mos7840_set_uart_reg(port, SCRATCH_PAD_REGISTER, Data); if (status < 0) { dev_dbg(&port->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status); - goto out; + goto error; } else dev_dbg(&port->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status); @@ -2204,7 +2221,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num))); if (status < 0) { dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 2, status); - goto out; + goto error; } else dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 2, status); } else { @@ -2216,7 +2233,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1)); if (status < 0) { dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 1, status); - goto out; + goto error; } else dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status); @@ -2254,17 +2271,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) /* Turn off LED */ mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300); } -out: - if (pnum == serial->num_ports - 1) { - /* Zero Length flag enable */ - Data = 0x0f; - status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data); - if (status < 0) { - dev_dbg(&port->dev, "Writing ZLP_REG5 failed status-0x%x\n", status); - goto error; - } else - dev_dbg(&port->dev, "ZLP_REG5 Writing success status%d\n", status); - } + return 0; error: kfree(mos7840_port->led_dr); @@ -2320,6 +2327,7 @@ static struct usb_serial_driver moschip7840_4port_device = { .unthrottle = mos7840_unthrottle, .calc_num_ports = mos7840_calc_num_ports, .probe = mos7840_probe, + .attach = mos7840_attach, .ioctl = mos7840_ioctl, .get_serial = mos7840_get_serial_info, .set_termios = mos7840_set_termios, -- cgit From 7183192196a63e265254631ca53984e6c7899c3c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:28:57 +0100 Subject: USB: serial: mos7840: rip out broken interrupt handling The interrupt handling is completely broken and has in fact never been been enabled due to an inverted test for an interrupt endpoint in open() that prevented the interrupt URB from being submitted. Other highlights include missing interrupt URB resubmission (had it ever been submitted), missing locking when managing the open-port count, and NULL-pointer dereferences that could have been triggered by a malicious device. Rip out this broken crap which is beyond repair instead of pretending we support this feature. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 294 +------------------------------------------ 1 file changed, 3 insertions(+), 291 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index ddd8db3be110..9c5956fbd600 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -156,7 +156,6 @@ #define LED_OFF_MS 500 enum mos7840_flag { - MOS7840_FLAG_CTRL_BUSY, MOS7840_FLAG_LED_BUSY, }; @@ -200,18 +199,12 @@ struct moschip_port { __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ char open; - char open_ports; struct usb_serial_port *port; /* loop back to the owner of this object */ /* Offsets */ __u8 SpRegOffset; __u8 ControlRegOffset; __u8 DcrRegOffset; - /* for processing control URBS in interrupt context */ - struct urb *control_urb; - struct usb_ctrlrequest *dr; - char *ctrl_buf; - int MsrLsr; spinlock_t pool_lock; struct urb *write_urb_pool[NUM_URBS]; @@ -371,55 +364,6 @@ static inline struct moschip_port *mos7840_get_port_private(struct return (struct moschip_port *)usb_get_serial_port_data(port); } -static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) -{ - struct moschip_port *mos7840_port; - struct async_icount *icount; - mos7840_port = port; - if (new_msr & - (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI | - MOS_MSR_DELTA_CD)) { - icount = &mos7840_port->port->icount; - - /* update input line counters */ - if (new_msr & MOS_MSR_DELTA_CTS) - icount->cts++; - if (new_msr & MOS_MSR_DELTA_DSR) - icount->dsr++; - if (new_msr & MOS_MSR_DELTA_CD) - icount->dcd++; - if (new_msr & MOS_MSR_DELTA_RI) - icount->rng++; - - wake_up_interruptible(&port->port->port.delta_msr_wait); - } -} - -static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) -{ - struct async_icount *icount; - - if (new_lsr & SERIAL_LSR_BI) { - /* - * Parity and Framing errors only count if they - * occur exclusive of a break being - * received. - */ - new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI); - } - - /* update input line counters */ - icount = &port->port->icount; - if (new_lsr & SERIAL_LSR_BI) - icount->brk++; - if (new_lsr & SERIAL_LSR_OE) - icount->overrun++; - if (new_lsr & SERIAL_LSR_PE) - icount->parity++; - if (new_lsr & SERIAL_LSR_FE) - icount->frame++; -} - /************************************************************************/ /************************************************************************/ /* U S B C A L L B A C K F U N C T I O N S */ @@ -427,76 +371,6 @@ static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) /************************************************************************/ /************************************************************************/ -static void mos7840_control_callback(struct urb *urb) -{ - unsigned char *data; - struct moschip_port *mos7840_port; - struct device *dev = &urb->dev->dev; - __u8 regval = 0x0; - int status = urb->status; - - mos7840_port = urb->context; - - switch (status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); - goto out; - default: - dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); - goto out; - } - - dev_dbg(dev, "%s urb buffer size is %d\n", __func__, urb->actual_length); - if (urb->actual_length < 1) - goto out; - - dev_dbg(dev, "%s mos7840_port->MsrLsr is %d port %d\n", __func__, - mos7840_port->MsrLsr, mos7840_port->port_num); - data = urb->transfer_buffer; - regval = (__u8) data[0]; - dev_dbg(dev, "%s data is %x\n", __func__, regval); - if (mos7840_port->MsrLsr == 0) - mos7840_handle_new_msr(mos7840_port, regval); - else if (mos7840_port->MsrLsr == 1) - mos7840_handle_new_lsr(mos7840_port, regval); -out: - clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mos7840_port->flags); -} - -static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg, - __u16 *val) -{ - struct usb_device *dev = mcs->port->serial->dev; - struct usb_ctrlrequest *dr = mcs->dr; - unsigned char *buffer = mcs->ctrl_buf; - int ret; - - if (test_and_set_bit_lock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags)) - return -EBUSY; - - dr->bRequestType = MCS_RD_RTYPE; - dr->bRequest = MCS_RDREQ; - dr->wValue = cpu_to_le16(Wval); /* 0 */ - dr->wIndex = cpu_to_le16(reg); - dr->wLength = cpu_to_le16(2); - - usb_fill_control_urb(mcs->control_urb, dev, usb_rcvctrlpipe(dev, 0), - (unsigned char *)dr, buffer, 2, - mos7840_control_callback, mcs); - mcs->control_urb->transfer_buffer_length = 2; - ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC); - if (ret) - clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags); - - return ret; -} - static void mos7840_set_led_callback(struct urb *urb) { switch (urb->status) { @@ -572,100 +446,6 @@ static void mos7840_led_activity(struct usb_serial_port *port) jiffies + msecs_to_jiffies(LED_ON_MS)); } -/***************************************************************************** - * mos7840_interrupt_callback - * this is the callback function for when we have received data on the - * interrupt endpoint. - *****************************************************************************/ - -static void mos7840_interrupt_callback(struct urb *urb) -{ - int result; - int length; - struct moschip_port *mos7840_port; - struct usb_serial *serial; - __u16 Data; - unsigned char *data; - __u8 sp[5]; - int i, rv = 0; - __u16 wval, wreg = 0; - int status = urb->status; - - switch (status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", - __func__, status); - return; - default: - dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", - __func__, status); - goto exit; - } - - length = urb->actual_length; - data = urb->transfer_buffer; - - serial = urb->context; - - /* Moschip get 5 bytes - * Byte 1 IIR Port 1 (port.number is 0) - * Byte 2 IIR Port 2 (port.number is 1) - * Byte 3 IIR Port 3 (port.number is 2) - * Byte 4 IIR Port 4 (port.number is 3) - * Byte 5 FIFO status for both */ - - if (length > 5) { - dev_dbg(&urb->dev->dev, "%s", "Wrong data !!!\n"); - return; - } - - sp[0] = (__u8) data[0]; - sp[1] = (__u8) data[1]; - sp[2] = (__u8) data[2]; - sp[3] = (__u8) data[3]; - - for (i = 0; i < serial->num_ports; i++) { - mos7840_port = mos7840_get_port_private(serial->port[i]); - wval = ((__u16)serial->port[i]->port_number + 1) << 8; - if (mos7840_port->open) { - if (sp[i] & 0x01) { - dev_dbg(&urb->dev->dev, "SP%d No Interrupt !!!\n", i); - } else { - switch (sp[i] & 0x0f) { - case SERIAL_IIR_RLS: - dev_dbg(&urb->dev->dev, "Serial Port %d: Receiver status error or \n", i); - dev_dbg(&urb->dev->dev, "address bit detected in 9-bit mode\n"); - mos7840_port->MsrLsr = 1; - wreg = LINE_STATUS_REGISTER; - break; - case SERIAL_IIR_MS: - dev_dbg(&urb->dev->dev, "Serial Port %d: Modem status change\n", i); - mos7840_port->MsrLsr = 0; - wreg = MODEM_STATUS_REGISTER; - break; - } - rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data); - } - } - } - if (!(rv < 0)) - /* the completion handler for the control urb will resubmit */ - return; -exit: - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) { - dev_err(&urb->dev->dev, - "%s - Error %d submitting interrupt urb\n", - __func__, result); - } -} - static int mos7840_port_paranoia_check(struct usb_serial_port *port, const char *function) { @@ -836,7 +616,6 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) __u16 Data; int status; struct moschip_port *mos7840_port; - struct moschip_port *port0; if (mos7840_port_paranoia_check(port, __func__)) return -ENODEV; @@ -847,14 +626,11 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) return -ENODEV; mos7840_port = mos7840_get_port_private(port); - port0 = mos7840_get_port_private(serial->port[0]); - - if (mos7840_port == NULL || port0 == NULL) + if (mos7840_port == NULL) return -ENODEV; usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); - port0->open_ports++; /* Initialising the write urb pool */ for (j = 0; j < NUM_URBS; ++j) { @@ -1005,41 +781,6 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); - /* Check to see if we've set up our endpoint info yet * - * (can't set it up in mos7840_startup as the structures * - * were not set up at that time.) */ - if (port0->open_ports == 1) { - /* FIXME: Buffer never NULL, so URB is not submitted. */ - if (serial->port[0]->interrupt_in_buffer == NULL) { - /* set up interrupt urb */ - usb_fill_int_urb(serial->port[0]->interrupt_in_urb, - serial->dev, - usb_rcvintpipe(serial->dev, - serial->port[0]->interrupt_in_endpointAddress), - serial->port[0]->interrupt_in_buffer, - serial->port[0]->interrupt_in_urb-> - transfer_buffer_length, - mos7840_interrupt_callback, - serial, - serial->port[0]->interrupt_in_urb->interval); - - /* start interrupt read for mos7840 */ - response = - usb_submit_urb(serial->port[0]->interrupt_in_urb, - GFP_KERNEL); - if (response) { - dev_err(&port->dev, "%s - Error %d submitting " - "interrupt urb\n", __func__, response); - } - - } - - } - - /* see if we've set up our endpoint info yet * - * (can't set it up in mos7840_startup as the * - * structures were not set up at that time.) */ - dev_dbg(&port->dev, "port number is %d\n", port->port_number); dev_dbg(&port->dev, "minor number is %d\n", port->minor); dev_dbg(&port->dev, "Bulkin endpoint is %d\n", port->bulk_in_endpointAddress); @@ -1142,7 +883,6 @@ static void mos7840_close(struct usb_serial_port *port) { struct usb_serial *serial; struct moschip_port *mos7840_port; - struct moschip_port *port0; int j; __u16 Data; @@ -1154,9 +894,7 @@ static void mos7840_close(struct usb_serial_port *port) return; mos7840_port = mos7840_get_port_private(port); - port0 = mos7840_get_port_private(serial->port[0]); - - if (mos7840_port == NULL || port0 == NULL) + if (mos7840_port == NULL) return; for (j = 0; j < NUM_URBS; ++j) @@ -1173,15 +911,6 @@ static void mos7840_close(struct usb_serial_port *port) usb_kill_urb(mos7840_port->read_urb); mos7840_port->read_urb_busy = false; - port0->open_ports--; - dev_dbg(&port->dev, "%s in close%d\n", __func__, port0->open_ports); - if (port0->open_ports == 0) { - if (serial->port[0]->interrupt_in_urb) { - dev_dbg(&port->dev, "Shutdown interrupt_in_urb\n"); - usb_kill_urb(serial->port[0]->interrupt_in_urb); - } - } - Data = 0x0; mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); @@ -2238,15 +1967,6 @@ static int mos7840_port_probe(struct usb_serial_port *port) dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status); } - mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL); - mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL); - mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest), - GFP_KERNEL); - if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf || - !mos7840_port->dr) { - status = -ENOMEM; - goto error; - } mos7840_port->has_led = device_flags & MCS_LED; @@ -2276,9 +1996,6 @@ static int mos7840_port_probe(struct usb_serial_port *port) error: kfree(mos7840_port->led_dr); usb_free_urb(mos7840_port->led_urb); - kfree(mos7840_port->dr); - kfree(mos7840_port->ctrl_buf); - usb_free_urb(mos7840_port->control_urb); kfree(mos7840_port); return status; @@ -2301,10 +2018,7 @@ static int mos7840_port_remove(struct usb_serial_port *port) usb_free_urb(mos7840_port->led_urb); kfree(mos7840_port->led_dr); } - usb_kill_urb(mos7840_port->control_urb); - usb_free_urb(mos7840_port->control_urb); - kfree(mos7840_port->ctrl_buf); - kfree(mos7840_port->dr); + kfree(mos7840_port); return 0; @@ -2334,12 +2048,10 @@ static struct usb_serial_driver moschip7840_4port_device = { .break_ctl = mos7840_break, .tiocmget = mos7840_tiocmget, .tiocmset = mos7840_tiocmset, - .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .port_probe = mos7840_port_probe, .port_remove = mos7840_port_remove, .read_bulk_callback = mos7840_bulk_in_callback, - .read_int_callback = mos7840_interrupt_callback, }; static struct usb_serial_driver * const serial_drivers[] = { -- cgit From 3ec9fb6f385461923b77de6072403e97d2efe01b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:28:58 +0100 Subject: USB: serial: mos7840: drop redundant urb context check The bulk-in URB context is set to the driver-data struct at open and is never cleared so drop the redundant check in the completion callback. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 9c5956fbd600..92180687097f 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -500,17 +500,13 @@ static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port, static void mos7840_bulk_in_callback(struct urb *urb) { + struct moschip_port *mos7840_port = urb->context; int retval; unsigned char *data; struct usb_serial *serial; struct usb_serial_port *port; - struct moschip_port *mos7840_port; int status = urb->status; - mos7840_port = urb->context; - if (!mos7840_port) - return; - if (status) { dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status); mos7840_port->read_urb_busy = false; -- cgit From ce039bd4b21fdd158e6dbe8baeb577e6347ea7aa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:28:59 +0100 Subject: USB: serial: mos7840: drop paranoid port checks Drop the paranoid port structure sanity checks which are confusing at best. The driver data port pointer is set at port probe and never cleared, while USB serial core sets the tty driver data at install and won't call any driver functions with a NULL port pointer. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 92 ++++---------------------------------------- 1 file changed, 8 insertions(+), 84 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 92180687097f..dcb1e04a0514 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -446,21 +446,6 @@ static void mos7840_led_activity(struct usb_serial_port *port) jiffies + msecs_to_jiffies(LED_ON_MS)); } -static int mos7840_port_paranoia_check(struct usb_serial_port *port, - const char *function) -{ - if (!port) { - pr_debug("%s - port == NULL\n", function); - return -1; - } - if (!port->serial) { - pr_debug("%s - port->serial == NULL\n", function); - return -1; - } - - return 0; -} - /* Inline functions to check the sanity of a pointer that is passed to us */ static int mos7840_serial_paranoia_check(struct usb_serial *serial, const char *function) @@ -482,7 +467,6 @@ static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port, { /* if no port was specified, or it fails a paranoia check */ if (!port || - mos7840_port_paranoia_check(port, function) || mos7840_serial_paranoia_check(port->serial, function)) { /* then say that we don't have a valid usb_serial thing, * which will end up genrating -ENODEV return values */ @@ -501,10 +485,10 @@ static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port, static void mos7840_bulk_in_callback(struct urb *urb) { struct moschip_port *mos7840_port = urb->context; + struct usb_serial_port *port = mos7840_port->port; int retval; unsigned char *data; struct usb_serial *serial; - struct usb_serial_port *port; int status = urb->status; if (status) { @@ -513,12 +497,6 @@ static void mos7840_bulk_in_callback(struct urb *urb) return; } - port = mos7840_port->port; - if (mos7840_port_paranoia_check(port, __func__)) { - mos7840_port->read_urb_busy = false; - return; - } - serial = mos7840_get_usb_serial(port, __func__); if (!serial) { mos7840_port->read_urb_busy = false; @@ -562,14 +540,12 @@ static void mos7840_bulk_in_callback(struct urb *urb) static void mos7840_bulk_out_data_callback(struct urb *urb) { - struct moschip_port *mos7840_port; - struct usb_serial_port *port; + struct moschip_port *mos7840_port = urb->context; + struct usb_serial_port *port = mos7840_port->port; int status = urb->status; unsigned long flags; int i; - mos7840_port = urb->context; - port = mos7840_port->port; spin_lock_irqsave(&mos7840_port->pool_lock, flags); for (i = 0; i < NUM_URBS; i++) { if (urb == mos7840_port->write_urb_pool[i]) { @@ -584,9 +560,6 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) return; } - if (mos7840_port_paranoia_check(port, __func__)) - return; - if (mos7840_port->open) tty_port_tty_wakeup(&port->port); @@ -605,19 +578,14 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) { + struct usb_serial *serial = port->serial; int response; int j; - struct usb_serial *serial; struct urb *urb; __u16 Data; int status; struct moschip_port *mos7840_port; - if (mos7840_port_paranoia_check(port, __func__)) - return -ENODEV; - - serial = port->serial; - if (mos7840_serial_paranoia_check(serial, __func__)) return -ENODEV; @@ -850,9 +818,6 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty) unsigned long flags; struct moschip_port *mos7840_port; - if (mos7840_port_paranoia_check(port, __func__)) - return 0; - mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) return 0; @@ -882,9 +847,6 @@ static void mos7840_close(struct usb_serial_port *port) int j; __u16 Data; - if (mos7840_port_paranoia_check(port, __func__)) - return; - serial = mos7840_get_usb_serial(port, __func__); if (!serial) return; @@ -927,9 +889,6 @@ static void mos7840_break(struct tty_struct *tty, int break_state) struct usb_serial *serial; struct moschip_port *mos7840_port; - if (mos7840_port_paranoia_check(port, __func__)) - return; - serial = mos7840_get_usb_serial(port, __func__); if (!serial) return; @@ -967,9 +926,6 @@ static int mos7840_write_room(struct tty_struct *tty) unsigned long flags; struct moschip_port *mos7840_port; - if (mos7840_port_paranoia_check(port, __func__)) - return -1; - mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) return -1; @@ -998,6 +954,7 @@ static int mos7840_write_room(struct tty_struct *tty) static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count) { + struct usb_serial *serial = port->serial; int status; int i; int bytes_sent = 0; @@ -1005,15 +962,10 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, unsigned long flags; struct moschip_port *mos7840_port; - struct usb_serial *serial; struct urb *urb; /* __u16 Data; */ const unsigned char *current_position = data; - if (mos7840_port_paranoia_check(port, __func__)) - return -1; - - serial = port->serial; if (mos7840_serial_paranoia_check(serial, __func__)) return -1; @@ -1104,9 +1056,6 @@ static void mos7840_throttle(struct tty_struct *tty) struct moschip_port *mos7840_port; int status; - if (mos7840_port_paranoia_check(port, __func__)) - return; - mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) @@ -1146,9 +1095,6 @@ static void mos7840_unthrottle(struct tty_struct *tty) int status; struct moschip_port *mos7840_port = mos7840_get_port_private(port); - if (mos7840_port_paranoia_check(port, __func__)) - return; - if (mos7840_port == NULL) return; @@ -1296,18 +1242,11 @@ static int mos7840_calc_baud_rate_divisor(struct usb_serial_port *port, static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, int baudRate) { + struct usb_serial_port *port = mos7840_port->port; int divisor = 0; int status; __u16 Data; __u16 clk_sel_val; - struct usb_serial_port *port; - - if (mos7840_port == NULL) - return -1; - - port = mos7840_port->port; - if (mos7840_port_paranoia_check(port, __func__)) - return -1; if (mos7840_serial_paranoia_check(port->serial, __func__)) return -1; @@ -1399,6 +1338,7 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, static void mos7840_change_port_settings(struct tty_struct *tty, struct moschip_port *mos7840_port, struct ktermios *old_termios) { + struct usb_serial_port *port = mos7840_port->port; int baud; unsigned cflag; __u8 lData; @@ -1406,15 +1346,6 @@ static void mos7840_change_port_settings(struct tty_struct *tty, __u8 lStop; int status; __u16 Data; - struct usb_serial_port *port; - - if (mos7840_port == NULL) - return; - - port = mos7840_port->port; - - if (mos7840_port_paranoia_check(port, __func__)) - return; if (mos7840_serial_paranoia_check(port->serial, __func__)) return; @@ -1557,14 +1488,10 @@ static void mos7840_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { + struct usb_serial *serial = port->serial; int status; - struct usb_serial *serial; struct moschip_port *mos7840_port; - if (mos7840_port_paranoia_check(port, __func__)) - return; - - serial = port->serial; if (mos7840_serial_paranoia_check(serial, __func__)) return; @@ -1659,9 +1586,6 @@ static int mos7840_ioctl(struct tty_struct *tty, void __user *argp = (void __user *)arg; struct moschip_port *mos7840_port; - if (mos7840_port_paranoia_check(port, __func__)) - return -1; - mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) -- cgit From 6d3471eded5e64c0310ceeff4adf9ca6d324ddd7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:29:00 +0100 Subject: USB: serial: mos7840: drop paranoid serial checks Drop the likewise paranoid serial structure sanity checks. USB serial core sets the serial pointer in the port structures at initialisation and it is never cleared, and similar for the serial structure type. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index dcb1e04a0514..207b88d948a9 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -446,28 +446,11 @@ static void mos7840_led_activity(struct usb_serial_port *port) jiffies + msecs_to_jiffies(LED_ON_MS)); } -/* Inline functions to check the sanity of a pointer that is passed to us */ -static int mos7840_serial_paranoia_check(struct usb_serial *serial, - const char *function) -{ - if (!serial) { - pr_debug("%s - serial == NULL\n", function); - return -1; - } - if (!serial->type) { - pr_debug("%s - serial->type == NULL!\n", function); - return -1; - } - - return 0; -} - static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port, const char *function) { - /* if no port was specified, or it fails a paranoia check */ - if (!port || - mos7840_serial_paranoia_check(port->serial, function)) { + /* if no port was specified */ + if (!port) { /* then say that we don't have a valid usb_serial thing, * which will end up genrating -ENODEV return values */ return NULL; @@ -586,9 +569,6 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) int status; struct moschip_port *mos7840_port; - if (mos7840_serial_paranoia_check(serial, __func__)) - return -ENODEV; - mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) return -ENODEV; @@ -966,9 +946,6 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, /* __u16 Data; */ const unsigned char *current_position = data; - if (mos7840_serial_paranoia_check(serial, __func__)) - return -1; - mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) return -1; @@ -1248,9 +1225,6 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, __u16 Data; __u16 clk_sel_val; - if (mos7840_serial_paranoia_check(port->serial, __func__)) - return -1; - dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudRate); /* reset clk_uart_sel in spregOffset */ if (baudRate > 115200) { @@ -1347,9 +1321,6 @@ static void mos7840_change_port_settings(struct tty_struct *tty, int status; __u16 Data; - if (mos7840_serial_paranoia_check(port->serial, __func__)) - return; - if (!mos7840_port->open) { dev_dbg(&port->dev, "%s - port not opened\n", __func__); return; @@ -1488,14 +1459,9 @@ static void mos7840_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - struct usb_serial *serial = port->serial; int status; struct moschip_port *mos7840_port; - - if (mos7840_serial_paranoia_check(serial, __func__)) - return; - mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) -- cgit From 2d52f0763f8ea965d35dc13958b85d0e88e596b2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:29:01 +0100 Subject: USB: serial: mos7840: drop serial struct accessor Drop the helper used to retrieve the serial struct pointer from the port structure. Note that this helper was only used when the serial structure was actually not needed. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 207b88d948a9..01787685a4ee 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -446,19 +446,6 @@ static void mos7840_led_activity(struct usb_serial_port *port) jiffies + msecs_to_jiffies(LED_ON_MS)); } -static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port, - const char *function) -{ - /* if no port was specified */ - if (!port) { - /* then say that we don't have a valid usb_serial thing, - * which will end up genrating -ENODEV return values */ - return NULL; - } - - return port->serial; -} - /***************************************************************************** * mos7840_bulk_in_callback * this is the callback function for when we have received data on the @@ -471,7 +458,6 @@ static void mos7840_bulk_in_callback(struct urb *urb) struct usb_serial_port *port = mos7840_port->port; int retval; unsigned char *data; - struct usb_serial *serial; int status = urb->status; if (status) { @@ -480,12 +466,6 @@ static void mos7840_bulk_in_callback(struct urb *urb) return; } - serial = mos7840_get_usb_serial(port, __func__); - if (!serial) { - mos7840_port->read_urb_busy = false; - return; - } - data = urb->transfer_buffer; usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); @@ -822,15 +802,10 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty) static void mos7840_close(struct usb_serial_port *port) { - struct usb_serial *serial; struct moschip_port *mos7840_port; int j; __u16 Data; - serial = mos7840_get_usb_serial(port, __func__); - if (!serial) - return; - mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) return; @@ -866,13 +841,8 @@ static void mos7840_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; unsigned char data; - struct usb_serial *serial; struct moschip_port *mos7840_port; - serial = mos7840_get_usb_serial(port, __func__); - if (!serial) - return; - mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) -- cgit From 7b2faede671adc0a7edbb5f6b1e228aa8b62f7c0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:29:02 +0100 Subject: USB: serial: mos7840: drop port driver data accessors Drop the custom port driver data accessors and paranoid sanity checks. The driver data is not cleared until the driver is unbound. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 104 ++++++------------------------------------- 1 file changed, 13 insertions(+), 91 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 01787685a4ee..11706f2d4145 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -344,26 +344,6 @@ static void mos7840_dump_serial_port(struct usb_serial_port *port, } -/************************************************************************/ -/************************************************************************/ -/* I N T E R F A C E F U N C T I O N S */ -/* I N T E R F A C E F U N C T I O N S */ -/************************************************************************/ -/************************************************************************/ - -static inline void mos7840_set_port_private(struct usb_serial_port *port, - struct moschip_port *data) -{ - usb_set_serial_port_data(port, (void *)data); -} - -static inline struct moschip_port *mos7840_get_port_private(struct - usb_serial_port - *port) -{ - return (struct moschip_port *)usb_get_serial_port_data(port); -} - /************************************************************************/ /************************************************************************/ /* U S B C A L L B A C K F U N C T I O N S */ @@ -541,17 +521,13 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) { + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; int response; int j; struct urb *urb; __u16 Data; int status; - struct moschip_port *mos7840_port; - - mos7840_port = mos7840_get_port_private(port); - if (mos7840_port == NULL) - return -ENODEV; usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); @@ -773,14 +749,10 @@ err: static int mos7840_chars_in_buffer(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); int i; int chars = 0; unsigned long flags; - struct moschip_port *mos7840_port; - - mos7840_port = mos7840_get_port_private(port); - if (mos7840_port == NULL) - return 0; spin_lock_irqsave(&mos7840_port->pool_lock, flags); for (i = 0; i < NUM_URBS; ++i) { @@ -802,14 +774,10 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty) static void mos7840_close(struct usb_serial_port *port) { - struct moschip_port *mos7840_port; + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); int j; __u16 Data; - mos7840_port = mos7840_get_port_private(port); - if (mos7840_port == NULL) - return; - for (j = 0; j < NUM_URBS; ++j) usb_kill_urb(mos7840_port->write_urb_pool[j]); @@ -840,13 +808,8 @@ static void mos7840_close(struct usb_serial_port *port) static void mos7840_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); unsigned char data; - struct moschip_port *mos7840_port; - - mos7840_port = mos7840_get_port_private(port); - - if (mos7840_port == NULL) - return; if (break_state == -1) data = mos7840_port->shadowLCR | LCR_SET_BREAK; @@ -871,14 +834,10 @@ static void mos7840_break(struct tty_struct *tty, int break_state) static int mos7840_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); int i; int room = 0; unsigned long flags; - struct moschip_port *mos7840_port; - - mos7840_port = mos7840_get_port_private(port); - if (mos7840_port == NULL) - return -1; spin_lock_irqsave(&mos7840_port->pool_lock, flags); for (i = 0; i < NUM_URBS; ++i) { @@ -904,22 +863,17 @@ static int mos7840_write_room(struct tty_struct *tty) static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count) { + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; int status; int i; int bytes_sent = 0; int transfer_size; unsigned long flags; - - struct moschip_port *mos7840_port; struct urb *urb; /* __u16 Data; */ const unsigned char *current_position = data; - mos7840_port = mos7840_get_port_private(port); - if (mos7840_port == NULL) - return -1; - /* try to find a free urb in the list */ urb = NULL; @@ -1000,14 +954,9 @@ exit: static void mos7840_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct moschip_port *mos7840_port; + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); int status; - mos7840_port = mos7840_get_port_private(port); - - if (mos7840_port == NULL) - return; - if (!mos7840_port->open) { dev_dbg(&port->dev, "%s", "port not opened\n"); return; @@ -1039,11 +988,8 @@ static void mos7840_throttle(struct tty_struct *tty) static void mos7840_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); int status; - struct moschip_port *mos7840_port = mos7840_get_port_private(port); - - if (mos7840_port == NULL) - return; if (!mos7840_port->open) { dev_dbg(&port->dev, "%s - port not opened\n", __func__); @@ -1071,15 +1017,10 @@ static void mos7840_unthrottle(struct tty_struct *tty) static int mos7840_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct moschip_port *mos7840_port; unsigned int result; __u16 msr; __u16 mcr; int status; - mos7840_port = mos7840_get_port_private(port); - - if (mos7840_port == NULL) - return -ENODEV; status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr); if (status < 0) @@ -1104,15 +1045,10 @@ static int mos7840_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; - struct moschip_port *mos7840_port; + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); unsigned int mcr; int status; - mos7840_port = mos7840_get_port_private(port); - - if (mos7840_port == NULL) - return -ENODEV; - /* FIXME: What locks the port registers ? */ mcr = mos7840_port->shadowMCR; if (clear & TIOCM_RTS) @@ -1429,13 +1365,8 @@ static void mos7840_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); int status; - struct moschip_port *mos7840_port; - - mos7840_port = mos7840_get_port_private(port); - - if (mos7840_port == NULL) - return; if (!mos7840_port->open) { dev_dbg(&port->dev, "%s - port not opened\n", __func__); @@ -1497,7 +1428,7 @@ static int mos7840_get_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct usb_serial_port *port = tty->driver_data; - struct moschip_port *mos7840_port = mos7840_get_port_private(port); + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); ss->type = PORT_16550A; ss->line = mos7840_port->port->minor; @@ -1520,12 +1451,6 @@ static int mos7840_ioctl(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; void __user *argp = (void __user *)arg; - struct moschip_port *mos7840_port; - - mos7840_port = mos7840_get_port_private(port); - - if (mos7840_port == NULL) - return -1; switch (cmd) { /* return number of bytes available */ @@ -1692,7 +1617,6 @@ static int mos7840_port_probe(struct usb_serial_port *port) * common to all port */ mos7840_port->port = port; - mos7840_set_port_private(port, mos7840_port); spin_lock_init(&mos7840_port->pool_lock); /* minor is not initialised until later by @@ -1718,7 +1642,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) mos7840_port->DcrRegOffset = 0x16 + 3 * (phy_num - 2); } mos7840_dump_serial_port(port, mos7840_port); - mos7840_set_port_private(port, mos7840_port); + usb_set_serial_port_data(port, mos7840_port); /* enable rx_disable bit in control register */ status = mos7840_get_reg_sync(port, @@ -1859,9 +1783,7 @@ error: static int mos7840_port_remove(struct usb_serial_port *port) { - struct moschip_port *mos7840_port; - - mos7840_port = mos7840_get_port_private(port); + struct moschip_port *mos7840_port = usb_get_serial_port_data(port); if (mos7840_port->has_led) { /* Turn off LED */ -- cgit From f8e8dcaf14d9c2bfb34018cb02e7a40f3d56c8eb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:29:03 +0100 Subject: USB: serial: mos7840: drop read-urb check Drop read-urb check which is always false from completion the callback. The driver read-urb pointer is set at every open and is never cleared. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 11706f2d4145..f5c08effa3ab 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -457,12 +457,6 @@ static void mos7840_bulk_in_callback(struct urb *urb) dev_dbg(&port->dev, "icount.rx is %d:\n", port->icount.rx); } - if (!mos7840_port->read_urb) { - dev_dbg(&port->dev, "%s", "URB KILLED !!!\n"); - mos7840_port->read_urb_busy = false; - return; - } - if (mos7840_port->has_led) mos7840_led_activity(port); @@ -1377,11 +1371,6 @@ static void mos7840_set_termios(struct tty_struct *tty, mos7840_change_port_settings(tty, mos7840_port, old_termios); - if (!mos7840_port->read_urb) { - dev_dbg(&port->dev, "%s", "URB KILLED !!!!!\n"); - return; - } - if (!mos7840_port->read_urb_busy) { mos7840_port->read_urb_busy = true; status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL); -- cgit From 067814c97494ea99e9cf8b77523e17369d38ec25 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Nov 2019 14:29:04 +0100 Subject: USB: serial: mos7840: drop port open flag Drop the redundant port open flag and corresponding checks. USB serial core will not call any of these driver callbacks for a closed port, and the write URBs are stopped at close(). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index f5c08effa3ab..23f91d658cb4 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -198,7 +198,6 @@ struct moschip_port { struct urb *read_urb; /* read URB for this port */ __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ - char open; struct usb_serial_port *port; /* loop back to the owner of this object */ /* Offsets */ @@ -497,8 +496,7 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) return; } - if (mos7840_port->open) - tty_port_tty_wakeup(&port->port); + tty_port_tty_wakeup(&port->port); } @@ -714,9 +712,6 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) /* initialize our port settings */ /* Must set to enable ints! */ mos7840_port->shadowMCR = MCR_MASTER_IE; - /* send a open port command */ - mos7840_port->open = 1; - /* mos7840_change_port_settings(mos7840_port,old_termios); */ return 0; err: @@ -791,8 +786,6 @@ static void mos7840_close(struct usb_serial_port *port) Data = 0x00; mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); - - mos7840_port->open = 0; } /***************************************************************************** @@ -951,11 +944,6 @@ static void mos7840_throttle(struct tty_struct *tty) struct moschip_port *mos7840_port = usb_get_serial_port_data(port); int status; - if (!mos7840_port->open) { - dev_dbg(&port->dev, "%s", "port not opened\n"); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); @@ -985,11 +973,6 @@ static void mos7840_unthrottle(struct tty_struct *tty) struct moschip_port *mos7840_port = usb_get_serial_port_data(port); int status; - if (!mos7840_port->open) { - dev_dbg(&port->dev, "%s - port not opened\n", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); @@ -1221,11 +1204,6 @@ static void mos7840_change_port_settings(struct tty_struct *tty, int status; __u16 Data; - if (!mos7840_port->open) { - dev_dbg(&port->dev, "%s - port not opened\n", __func__); - return; - } - lData = LCR_BITS_8; lStop = LCR_STOP_1; lParity = LCR_PAR_NONE; @@ -1362,11 +1340,6 @@ static void mos7840_set_termios(struct tty_struct *tty, struct moschip_port *mos7840_port = usb_get_serial_port_data(port); int status; - if (!mos7840_port->open) { - dev_dbg(&port->dev, "%s - port not opened\n", __func__); - return; - } - /* change the port settings to the new ones specified */ mos7840_change_port_settings(tty, mos7840_port, old_termios); -- cgit From 1ec13abac58b6f24e32f0d3081ef4e7456e62ed8 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 12 Nov 2019 16:49:39 +0100 Subject: USBIP: add config dependency for SGL_ALLOC USBIP uses lib/scatterlist.h Hence it needs to set CONFIG_SGL_ALLOC Signed-off-by: Oliver Neukum Cc: stable Acked-by: Shuah Khan Link: https://lore.kernel.org/r/20191112154939.21217-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig index 2f86b28fa3da..7bbae7a08642 100644 --- a/drivers/usb/usbip/Kconfig +++ b/drivers/usb/usbip/Kconfig @@ -4,6 +4,7 @@ config USBIP_CORE tristate "USB/IP support" depends on NET select USB_COMMON + select SGL_ALLOC ---help--- This enables pushing USB packets over IP to allow remote machines direct access to USB devices. It provides the -- cgit From 2a9125317b247f2cf35c196f968906dcf062ae2d Mon Sep 17 00:00:00 2001 From: Suwan Kim Date: Mon, 11 Nov 2019 23:10:35 +0900 Subject: usbip: Fix uninitialized symbol 'nents' in stub_recv_cmd_submit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Smatch reported that nents is not initialized and used in stub_recv_cmd_submit(). nents is currently initialized by sgl_alloc() and used to allocate multiple URBs when host controller doesn't support scatter-gather DMA. The use of uninitialized nents means that buf_len is zero and use_sg is true. But buffer length should not be zero when an URB uses scatter-gather DMA. To prevent this situation, add the conditional that checks buf_len and use_sg. And move the use of nents right after the sgl_alloc() to avoid the use of uninitialized nents. If the error occurs, it adds SDEV_EVENT_ERROR_MALLOC and stub_priv will be released by stub event handler and connection will be shut down. Fixes: ea44d190764b ("usbip: Implement SG support to vhci-hcd and stub driver") Reported-by: kbuild test robot Reported-by: Dan Carpenter Signed-off-by: Suwan Kim Acked-by: Shuah Khan Cc: stable Link: https://lore.kernel.org/r/20191111141035.27788-1-suwan.kim027@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_rx.c | 50 +++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 66edfeea68fe..e2b019532234 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -470,18 +470,50 @@ static void stub_recv_cmd_submit(struct stub_device *sdev, if (pipe == -1) return; + /* + * Smatch reported the error case where use_sg is true and buf_len is 0. + * In this case, It adds SDEV_EVENT_ERROR_MALLOC and stub_priv will be + * released by stub event handler and connection will be shut down. + */ priv = stub_priv_alloc(sdev, pdu); if (!priv) return; buf_len = (unsigned long long)pdu->u.cmd_submit.transfer_buffer_length; + if (use_sg && !buf_len) { + dev_err(&udev->dev, "sg buffer with zero length\n"); + goto err_malloc; + } + /* allocate urb transfer buffer, if needed */ if (buf_len) { if (use_sg) { sgl = sgl_alloc(buf_len, GFP_KERNEL, &nents); if (!sgl) goto err_malloc; + + /* Check if the server's HCD supports SG */ + if (!udev->bus->sg_tablesize) { + /* + * If the server's HCD doesn't support SG, break + * a single SG request into several URBs and map + * each SG list entry to corresponding URB + * buffer. The previously allocated SG list is + * stored in priv->sgl (If the server's HCD + * support SG, SG list is stored only in + * urb->sg) and it is used as an indicator that + * the server split single SG request into + * several URBs. Later, priv->sgl is used by + * stub_complete() and stub_send_ret_submit() to + * reassemble the divied URBs. + */ + support_sg = 0; + num_urbs = nents; + priv->completed_urbs = 0; + pdu->u.cmd_submit.transfer_flags &= + ~URB_DMA_MAP_SG; + } } else { buffer = kzalloc(buf_len, GFP_KERNEL); if (!buffer) @@ -489,24 +521,6 @@ static void stub_recv_cmd_submit(struct stub_device *sdev, } } - /* Check if the server's HCD supports SG */ - if (use_sg && !udev->bus->sg_tablesize) { - /* - * If the server's HCD doesn't support SG, break a single SG - * request into several URBs and map each SG list entry to - * corresponding URB buffer. The previously allocated SG - * list is stored in priv->sgl (If the server's HCD support SG, - * SG list is stored only in urb->sg) and it is used as an - * indicator that the server split single SG request into - * several URBs. Later, priv->sgl is used by stub_complete() and - * stub_send_ret_submit() to reassemble the divied URBs. - */ - support_sg = 0; - num_urbs = nents; - priv->completed_urbs = 0; - pdu->u.cmd_submit.transfer_flags &= ~URB_DMA_MAP_SG; - } - /* allocate urb array */ priv->num_urbs = num_urbs; priv->urbs = kmalloc_array(num_urbs, sizeof(*priv->urbs), GFP_KERNEL); -- cgit From f0797095423e6ea3b4be61134ee353c7f504d440 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 13 Nov 2019 11:14:05 +0100 Subject: USB: serial: option: add support for Foxconn T77W968 LTE modules These are the Foxconn-branded variants of the Dell DW5821e modules, same USB layout as those. The device exposes AT, NMEA and DIAG ports in both USB configurations. P: Vendor=0489 ProdID=e0b4 Rev=03.18 S: Manufacturer=FII S: Product=T77W968 LTE S: SerialNumber=0123456789ABCDEF C: #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=500mA I: If#=0x0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan I: If#=0x1 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=00 Prot=00 Driver=usbhid I: If#=0x2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option P: Vendor=0489 ProdID=e0b4 Rev=03.18 S: Manufacturer=FII S: Product=T77W968 LTE S: SerialNumber=0123456789ABCDEF C: #Ifs= 7 Cfg#= 2 Atr=a0 MxPwr=500mA I: If#=0x0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim I: If#=0x1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I: If#=0x2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#=0x5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option I: If#=0x6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) Signed-off-by: Aleksander Morgado [ johan: drop id defines ] Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 2023f1f4edaf..e9491d400a24 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1993,6 +1993,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x13) }, { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x14) }, { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x1b) }, + { USB_DEVICE(0x0489, 0xe0b4), /* Foxconn T77W968 */ + .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, + { USB_DEVICE(0x0489, 0xe0b5), /* Foxconn T77W968 ESIM */ + .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 */ .driver_info = RSVD(4) | RSVD(5) | RSVD(6) }, { USB_DEVICE(0x2cb7, 0x0104), /* Fibocom NL678 series */ -- cgit From ba9f0f6eff1a34971f345cd04b13200c1d0b9b83 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 13 Nov 2019 11:48:38 +0200 Subject: usb: renesas_usbhs: Use dma_request_chan() directly for channel request dma_request_slave_channel_reason() is: #define dma_request_slave_channel_reason(dev, name) \ dma_request_chan(dev, name) Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20191113094838.2141-1-peter.ujfalusi@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/fifo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 86637cd066cf..01c6a48c41bc 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1273,11 +1273,11 @@ static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo, */ snprintf(name, sizeof(name), "ch%d", channel); if (channel & 1) { - fifo->tx_chan = dma_request_slave_channel_reason(dev, name); + fifo->tx_chan = dma_request_chan(dev, name); if (IS_ERR(fifo->tx_chan)) fifo->tx_chan = NULL; } else { - fifo->rx_chan = dma_request_slave_channel_reason(dev, name); + fifo->rx_chan = dma_request_chan(dev, name); if (IS_ERR(fifo->rx_chan)) fifo->rx_chan = NULL; } -- cgit From b8029ba97b3124cbb86be045b38ac933d4e8f0ae Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Tue, 12 Nov 2019 14:51:48 +0800 Subject: usb: chipidea: debug: create debugfs directory under usb root Move it's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1573541519-28488-2-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index fcc91a338875..e0376ee646ad 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -342,7 +342,7 @@ DEFINE_SHOW_ATTRIBUTE(ci_registers); */ void dbg_create_files(struct ci_hdrc *ci) { - ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL); + ci->debugfs = debugfs_create_dir(dev_name(ci->dev), usb_debug_root); debugfs_create_file("device", S_IRUGO, ci->debugfs, ci, &ci_device_fops); -- cgit From ee93c83bf7565ef5ff29438dd71732162afdc3af Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Tue, 12 Nov 2019 14:51:49 +0800 Subject: usb: host: imx21: create debugfs directory under usb root Move it's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1573541519-28488-3-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/imx21-dbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c index 7fcf1d9dd7f3..02a1344fbd6a 100644 --- a/drivers/usb/host/imx21-dbg.c +++ b/drivers/usb/host/imx21-dbg.c @@ -419,7 +419,7 @@ static void create_debug_files(struct imx21 *imx21) { struct dentry *root; - root = debugfs_create_dir(dev_name(imx21->dev), NULL); + root = debugfs_create_dir(dev_name(imx21->dev), usb_debug_root); imx21->debug_root = root; debugfs_create_file("status", S_IRUGO, root, imx21, &debug_status_fops); -- cgit From dcf5a2c390a3c956fc66bb52b1d69c0ad27298a6 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Tue, 12 Nov 2019 14:51:50 +0800 Subject: usb: dwc3: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move dwc3's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1573541519-28488-4-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 1c792710348f..4fe8b1e1485c 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -916,7 +916,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc) dwc->regset->nregs = ARRAY_SIZE(dwc3_regs); dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START; - root = debugfs_create_dir(dev_name(dwc->dev), NULL); + root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root); dwc->root = root; debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset); -- cgit From b3c69ec8e2326980e83a7b0f4d0fb78778f1299a Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Tue, 12 Nov 2019 14:51:52 +0800 Subject: usb: musb: dsps: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move dsps's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1573541519-28488-6-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_dsps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 327d4f7baaf7..88923175f71e 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -411,7 +411,7 @@ static int dsps_musb_dbg_init(struct musb *musb, struct dsps_glue *glue) char buf[128]; sprintf(buf, "%s.dsps", dev_name(musb->controller)); - root = debugfs_create_dir(buf, NULL); + root = debugfs_create_dir(buf, usb_debug_root); glue->dbgfs_root = root; glue->regset.regs = dsps_musb_regs; -- cgit From de4c73982cce5a3ac385d82924d0c1fc42ac3d57 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Tue, 12 Nov 2019 14:51:53 +0800 Subject: usb: dwc2: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move dwc2's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1573541519-28488-7-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index 7f62f4cdc265..b8f2790abf91 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -770,7 +770,7 @@ int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) int ret; struct dentry *root; - root = debugfs_create_dir(dev_name(hsotg->dev), NULL); + root = debugfs_create_dir(dev_name(hsotg->dev), usb_debug_root); hsotg->debug_root = root; debugfs_create_file("params", 0444, root, hsotg, ¶ms_fops); -- cgit From a66ada4f241c9a5a0bbabc5af511ea5dd4c2cbb8 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Tue, 12 Nov 2019 14:51:54 +0800 Subject: usb: gadget: bcm63xx_udc: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move it's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1573541519-28488-8-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/bcm63xx_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index 7fcf4a8eaeb6..54501814dc3f 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2248,7 +2248,7 @@ static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc) if (!IS_ENABLED(CONFIG_USB_GADGET_DEBUG_FS)) return; - root = debugfs_create_dir(udc->gadget.name, NULL); + root = debugfs_create_dir(udc->gadget.name, usb_debug_root); udc->debugfs_root = root; debugfs_create_file("usbd", 0400, root, udc, &bcm63xx_usbd_dbg_fops); -- cgit From 626557a352b2a4a2440b6d68f6a5c562246fe0f7 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Tue, 12 Nov 2019 14:51:56 +0800 Subject: usb: gadget: udc: renesas_usb3: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move it's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1573541519-28488-10-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/renesas_usb3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index b18ad83342e3..0895db5381b8 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2536,7 +2536,7 @@ static const struct file_operations renesas_usb3_b_device_fops = { static void renesas_usb3_debugfs_init(struct renesas_usb3 *usb3, struct device *dev) { - usb3->dentry = debugfs_create_dir(dev_name(dev), NULL); + usb3->dentry = debugfs_create_dir(dev_name(dev), usb_debug_root); debugfs_create_file("b_device", 0644, usb3->dentry, usb3, &renesas_usb3_b_device_fops); -- cgit From 3a35bc8df4888204ceee0fced06cede431ddba02 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Tue, 12 Nov 2019 14:51:57 +0800 Subject: usb: gadget: pxa27x: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move it's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1573541519-28488-11-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/pxa27x_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 5f107f277f30..78902d13fc27 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -207,7 +207,7 @@ static void pxa_init_debugfs(struct pxa_udc *udc) { struct dentry *root; - root = debugfs_create_dir(udc->gadget.name, NULL); + root = debugfs_create_dir(udc->gadget.name, usb_debug_root); udc->debugfs_root = root; debugfs_create_file("udcstate", 0400, root, udc, &state_dbg_fops); -- cgit From e47ff01b94b64f5cea3b43dbfa4f27bb605789a4 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Tue, 12 Nov 2019 14:51:59 +0800 Subject: usb: gadget: udc: s3c2410_udc: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move it's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1573541519-28488-13-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/s3c2410_udc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index f82208fbc249..0507a2ca0f55 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -1978,7 +1978,8 @@ static int __init udc_init(void) dprintk(DEBUG_NORMAL, "%s\n", gadget_name); - s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL); + s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, + usb_debug_root); retval = platform_driver_register(&udc_driver_24x0); if (retval) -- cgit From c1a1f273d0825774c80896b8deb1c9ea1d0b91e3 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Thu, 14 Nov 2019 01:30:53 +0000 Subject: USB: serial: ftdi_sio: add device IDs for U-Blox C099-F9P This device presents itself as a USB hub with three attached devices: - An ACM serial port connected to the GPS module (not affected by this commit) - An FTDI serial port connected to the GPS module (1546:0502) - Another FTDI serial port connected to the ODIN-W2 radio module (1546:0503) This commit registers U-Blox's VID and the PIDs of the second and third devices. Datasheet: https://www.u-blox.com/sites/default/files/C099-F9P-AppBoard-Mbed-OS3-FW_UserGuide_%28UBX-18063024%29.pdf Signed-off-by: Fabio D'Urso Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 3 +++ drivers/usb/serial/ftdi_sio_ids.h | 7 +++++++ 2 files changed, 10 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 25e81faf4c24..9ad44a96dfe3 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1033,6 +1033,9 @@ static const struct usb_device_id id_table_combined[] = { /* Sienna devices */ { USB_DEVICE(FTDI_VID, FTDI_SIENNA_PID) }, { USB_DEVICE(ECHELON_VID, ECHELON_U20_PID) }, + /* U-Blox devices */ + { USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ZED_PID) }, + { USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ODIN_PID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 22d66217cb41..e8373528264c 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1558,3 +1558,10 @@ */ #define UNJO_VID 0x22B7 #define UNJO_ISODEBUG_V1_PID 0x150D + +/* + * U-Blox products (http://www.u-blox.com). + */ +#define UBLOX_VID 0x1546 +#define UBLOX_C099F9P_ZED_PID 0x0502 +#define UBLOX_C099F9P_ODIN_PID 0x0503 -- cgit From dc0ffbea5729a3abafa577ebfce87f18b79e294b Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 15 Nov 2019 18:50:00 +0200 Subject: usb: host: xhci: update event ring dequeue pointer on purpose On some situations, the software handles TRB events slower than adding TRBs, then xhci_handle_event can't return zero long time, the xHC will consider the event ring is full, and trigger "Event Ring Full" error, but in fact, the software has already finished lots of events, just no chance to update ERDP (event ring dequeue pointer). In this commit, we force update ERDP if half of TRBS_PER_SEGMENT events have handled to avoid "Event Ring Full" error. Signed-off-by: Peter Chen Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/1573836603-10871-2-git-send-email-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 60 +++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e7aab31fd9a5..55084adf1faf 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2740,6 +2740,42 @@ static int xhci_handle_event(struct xhci_hcd *xhci) return 1; } +/* + * Update Event Ring Dequeue Pointer: + * - When all events have finished + * - To avoid "Event Ring Full Error" condition + */ +static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, + union xhci_trb *event_ring_deq) +{ + u64 temp_64; + dma_addr_t deq; + + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + /* If necessary, update the HW's version of the event ring deq ptr. */ + if (event_ring_deq != xhci->event_ring->dequeue) { + deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, + xhci->event_ring->dequeue); + if (deq == 0) + xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr\n"); + /* + * Per 4.9.4, Software writes to the ERDP register shall + * always advance the Event Ring Dequeue Pointer value. + */ + if ((temp_64 & (u64) ~ERST_PTR_MASK) == + ((u64) deq & (u64) ~ERST_PTR_MASK)) + return; + + /* Update HC event ring dequeue pointer */ + temp_64 &= ERST_PTR_MASK; + temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); + } + + /* Clear the event handler busy flag (RW1C) */ + temp_64 |= ERST_EHB; + xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); +} + /* * xHCI spec says we can get an interrupt, and if the HC has an error condition, * we might get bad data out of the event ring. Section 4.10.2.7 has a list of @@ -2751,9 +2787,9 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) union xhci_trb *event_ring_deq; irqreturn_t ret = IRQ_NONE; unsigned long flags; - dma_addr_t deq; u64 temp_64; u32 status; + int event_loop = 0; spin_lock_irqsave(&xhci->lock, flags); /* Check if the xHC generated the interrupt, or the irq is shared */ @@ -2807,24 +2843,14 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) /* FIXME this should be a delayed service routine * that clears the EHB. */ - while (xhci_handle_event(xhci) > 0) {} - - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - /* If necessary, update the HW's version of the event ring deq ptr. */ - if (event_ring_deq != xhci->event_ring->dequeue) { - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); - if (deq == 0) - xhci_warn(xhci, "WARN something wrong with SW event " - "ring dequeue ptr.\n"); - /* Update HC event ring dequeue pointer */ - temp_64 &= ERST_PTR_MASK; - temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); + while (xhci_handle_event(xhci) > 0) { + if (event_loop++ < TRBS_PER_SEGMENT / 2) + continue; + xhci_update_erst_dequeue(xhci, event_ring_deq); + event_loop = 0; } - /* Clear the event handler busy flag (RW1C); event ring is empty. */ - temp_64 |= ERST_EHB; - xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); + xhci_update_erst_dequeue(xhci, event_ring_deq); ret = IRQ_HANDLED; out: -- cgit From 58b9d71a0f55e2514211875553dd092f74dc2cbc Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 15 Nov 2019 18:50:01 +0200 Subject: xhci: Add tracing for xhci doorbell register writes Trace when a register in the doorbell array is written, both for host controller command doorbell and device doorbells, including for which endpoint and stream Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/1573836603-10871-3-git-send-email-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 6 ++++++ drivers/usb/host/xhci-trace.h | 26 ++++++++++++++++++++++++++ drivers/usb/host/xhci.h | 29 +++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 55084adf1faf..bfd4d34c2535 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -280,6 +280,9 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) return; xhci_dbg(xhci, "// Ding dong!\n"); + + trace_xhci_ring_host_doorbell(0, DB_VALUE_HOST); + writel(DB_VALUE_HOST, &xhci->dba->doorbell[0]); /* Flush PCI posted writes */ readl(&xhci->dba->doorbell[0]); @@ -401,6 +404,9 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) || (ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT)) return; + + trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id)); + writel(DB_VALUE(ep_index, stream_id), db_addr); /* The CPU has better things to do at this point than wait for a * write-posting flush. It'll get there soon enough. diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 052a269d86f2..56eb867803a6 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -560,6 +560,32 @@ DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data, TP_ARGS(portnum, portsc) ); +DECLARE_EVENT_CLASS(xhci_log_doorbell, + TP_PROTO(u32 slot, u32 doorbell), + TP_ARGS(slot, doorbell), + TP_STRUCT__entry( + __field(u32, slot) + __field(u32, doorbell) + ), + TP_fast_assign( + __entry->slot = slot; + __entry->doorbell = doorbell; + ), + TP_printk("Ring doorbell for %s", + xhci_decode_doorbell(__entry->slot, __entry->doorbell) + ) +); + +DEFINE_EVENT(xhci_log_doorbell, xhci_ring_ep_doorbell, + TP_PROTO(u32 slot, u32 doorbell), + TP_ARGS(slot, doorbell) +); + +DEFINE_EVENT(xhci_log_doorbell, xhci_ring_host_doorbell, + TP_PROTO(u32 slot, u32 doorbell), + TP_ARGS(slot, doorbell) +); + DECLARE_EVENT_CLASS(xhci_dbc_log_request, TP_PROTO(struct dbc_request *req), TP_ARGS(req), diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f9f88626a57a..dc6f62a4b197 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2580,6 +2580,35 @@ static inline const char *xhci_decode_portsc(u32 portsc) return str; } +static inline const char *xhci_decode_doorbell(u32 slot, u32 doorbell) +{ + static char str[256]; + u8 ep; + u16 stream; + int ret; + + ep = (doorbell & 0xff); + stream = doorbell >> 16; + + if (slot == 0) { + sprintf(str, "Command Ring %d", doorbell); + return str; + } + ret = sprintf(str, "Slot %d ", slot); + if (ep > 0 && ep < 32) + ret = sprintf(str + ret, "ep%d%s", + ep / 2, + ep % 2 ? "in" : "out"); + else if (ep == 0 || ep < 248) + ret = sprintf(str + ret, "Reserved %d", ep); + else + ret = sprintf(str + ret, "Vendor Defined %d", ep); + if (stream) + ret = sprintf(str + ret, " Stream %d", stream); + + return str; +} + static inline const char *xhci_ep_state_string(u8 state) { switch (state) { -- cgit From 36dc01657b4973ce2e52352e69db2b879c488176 Mon Sep 17 00:00:00 2001 From: Suwan Kim Date: Fri, 15 Nov 2019 18:50:02 +0200 Subject: usb: host: xhci: Support running urb giveback in tasklet context Patch "USB: HCD: support giveback of URB in tasklet context"[1] introduced giveback of urb in tasklet context. [1] This patch was applied to ehci but not xhci. [2] This patch significantly reduces the hard irq time of xhci. Especially for uvc driver, the hard irq including the uvc completion function runs quite long but applying this patch reduces the hard irq time of xhci. I have tested four SS devices to check if performance degradation occurs when urb completion functions run in the tasklet context. As a result of the test, all devices works well and shows very similar performance with the upstream kernel. Moreover, usb ethernet adapter show better performance than the upstream kernel about 5% for RX and 2% for TX. Four SS devices is as follows. SS devices for test 1. WD My Passport 2TB (external hard drive) 2. Sandisk Ultra Flair USB 3.0 32GB 3. Logitech Brio webcam 4. Iptime 1gigabit ethernet adapter (Mediatek RTL8153) Test description 1. Mass storage (hard drive) performance test - run below command 10 times and compute the average performance dd if=/dev/sdN iflag=direct of=/dev/null bs=1G count=1 2. Mass storage (flash memory) performance test - run below command 10 times and compute the average performance dd if=/dev/sdN iflag=direct of=/dev/null bs=1G count=1 3. Webcam streaming performance test - run simple capture program and get the average frame rate per second - capture 1500 frames - program link https://github.com/asfaca/Webcam-performance-analyzing-tool - video resolution : 4096 X 2160 (4K) at 30 or 24 fps - device (Logitech Brio) spec url for the highest resolution and fps https://support.logitech.com/en_gb/product/brio-stream/specs 4. USB Ethernet adapter performance test - directly connect two linux machines with ethernet cable - run pktgen of linux kernel and send 1500 bytes packets - run vnstat to measure the network bandwidth for 180 seconds Test machine - CPU : Intel i5-7600 @ 3.5GHz Test results 1. Mass storage (hard drive) performance test WD My Passport 2TB (external hard drive) -------------------------------------------------------------------- xhci without tasklet | xhci with tasklet -------------------------------------------------------------------- 103.667MB/s | 103.692MB/s -------------------------------------------------------------------- 2. Mass storage (flash memory) performance test Sandisk Ultra Flair USB 3.0 32GB -------------------------------------------------------------------- xhci without tasklet | xhci with tasklet -------------------------------------------------------------------- 129.727MB/s | 130.2MB/s -------------------------------------------------------------------- 3. Webcam streaming performance test Logitech Brio webcam -------------------------------------------------------------------- xhci without tasklet | xhci with tasklet -------------------------------------------------------------------- 26.4451 fps | 26.3949 fps -------------------------------------------------------------------- 4. USB Ethernet adapter performance test Iptime 1gigabit ethernet adapter (Mediatek RTL8153) -------------------------------------------------------------------- xhci without tasklet | xhci with tasklet -------------------------------------------------------------------- RX 933.86 Mbit/s | 983.86 Mbit/s -------------------------------------------------------------------- TX 830.18 Mbit/s | 882.75 Mbit/s -------------------------------------------------------------------- [1], https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=94dfd7edfd5c9b605caf7b562de7a813d216e011 [2], https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=428aac8a81058e2303677a8fbf26670229e51d3a Signed-off-by: Suwan Kim Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/1573836603-10871-4-git-send-email-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 2 -- drivers/usb/host/xhci.c | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index bfd4d34c2535..6475c3d3b43b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -657,10 +657,8 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, } xhci_urb_free_priv(urb_priv); usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock(&xhci->lock); trace_xhci_urb_giveback(urb); usb_hcd_giveback_urb(hcd, urb, status); - spin_lock(&xhci->lock); } static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6c17e3fe181a..6721d059f58a 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5301,7 +5301,8 @@ static const struct hc_driver xhci_hc_driver = { * generic hardware linkage */ .irq = xhci_irq, - .flags = HCD_MEMORY | HCD_DMA | HCD_USB3 | HCD_SHARED, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB3 | HCD_SHARED | + HCD_BH, /* * basic lifecycle operations -- cgit From 07a594f353655b1628f598add352e7e754f44869 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 15 Nov 2019 18:50:03 +0200 Subject: xhci-pci: Allow host runtime PM as default also for Intel Ice Lake xHCI Intel Ice Lake has two xHCI controllers one on PCH and the other as part of the CPU itself. The latter is also part of the so called Type C Subsystem (TCSS) sharing ACPI power resources with the PCIe root ports and the Thunderbolt controllers. In order to put the whole TCSS block into D3cold the xHCI needs to be runtime suspended as well when idle. For this reason allow runtime PM as default for Ice Lake TCSS xHCI controller. Signed-off-by: Mika Westerberg Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/1573836603-10871-5-git-send-email-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 1e0236e90687..a0025d23b257 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -48,6 +48,7 @@ #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI 0x15e9 #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI 0x15ec #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI 0x15f0 +#define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13 #define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9 #define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba @@ -212,7 +213,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI)) + pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI)) xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; if (pdev->vendor == PCI_VENDOR_ID_ETRON && -- cgit From a079973f462a3d506c6a7f00c770a55b167ed094 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Nov 2019 12:18:40 +0100 Subject: usb: typec: tcpm: Remove tcpc_config configuration mechanism All configuration can and should be done through fwnodes instead of through the tcpc_config struct and there are no existing users left of struct tcpc_config, so lets remove it. Signed-off-by: Hans de Goede Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191114111840.40876-1-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 90 ++----------------------------------------- 1 file changed, 3 insertions(+), 87 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index bc9edb4c013b..56fc356bc55c 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -380,9 +380,6 @@ static enum tcpm_state tcpm_default_state(struct tcpm_port *port) return SNK_UNATTACHED; else if (port->try_role == TYPEC_SOURCE) return SRC_UNATTACHED; - else if (port->tcpc->config && - port->tcpc->config->default_role == TYPEC_SINK) - return SNK_UNATTACHED; /* Fall through to return SRC_UNATTACHED */ } else if (port->port_type == TYPEC_PORT_SNK) { return SNK_UNATTACHED; @@ -4122,7 +4119,7 @@ static int tcpm_try_role(struct typec_port *p, int role) mutex_lock(&port->lock); if (tcpc->try_role) ret = tcpc->try_role(tcpc, role); - if (!ret && (!tcpc->config || !tcpc->config->try_role_hw)) + if (!ret) port->try_role = role; port->try_src_count = 0; port->try_snk_count = 0; @@ -4366,34 +4363,6 @@ void tcpm_tcpc_reset(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); -static int tcpm_copy_pdos(u32 *dest_pdo, const u32 *src_pdo, - unsigned int nr_pdo) -{ - unsigned int i; - - if (nr_pdo > PDO_MAX_OBJECTS) - nr_pdo = PDO_MAX_OBJECTS; - - for (i = 0; i < nr_pdo; i++) - dest_pdo[i] = src_pdo[i]; - - return nr_pdo; -} - -static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo, - unsigned int nr_vdo) -{ - unsigned int i; - - if (nr_vdo > VDO_MAX_OBJECTS) - nr_vdo = VDO_MAX_OBJECTS; - - for (i = 0; i < nr_vdo; i++) - dest_vdo[i] = src_vdo[i]; - - return nr_vdo; -} - static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { @@ -4696,35 +4665,10 @@ static int devm_tcpm_psy_register(struct tcpm_port *port) return PTR_ERR_OR_ZERO(port->psy); } -static int tcpm_copy_caps(struct tcpm_port *port, - const struct tcpc_config *tcfg) -{ - if (tcpm_validate_caps(port, tcfg->src_pdo, tcfg->nr_src_pdo) || - tcpm_validate_caps(port, tcfg->snk_pdo, tcfg->nr_snk_pdo)) - return -EINVAL; - - port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcfg->src_pdo, - tcfg->nr_src_pdo); - port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcfg->snk_pdo, - tcfg->nr_snk_pdo); - - port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcfg->snk_vdo, - tcfg->nr_snk_vdo); - - port->operating_snk_mw = tcfg->operating_snk_mw; - - port->typec_caps.prefer_role = tcfg->default_role; - port->typec_caps.type = tcfg->type; - port->typec_caps.data = tcfg->data; - port->self_powered = tcfg->self_powered; - - return 0; -} - struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) { struct tcpm_port *port; - int i, err; + int err; if (!dev || !tcpc || !tcpc->get_vbus || !tcpc->set_cc || !tcpc->get_cc || @@ -4757,15 +4701,10 @@ 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) && tcpc->config) - err = tcpm_copy_caps(port, tcpc->config); if (err < 0) goto out_destroy_wq; - if (!tcpc->config || !tcpc->config->try_role_hw) - port->try_role = port->typec_caps.prefer_role; - else - port->try_role = TYPEC_NO_PREFERRED_ROLE; + 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 */ @@ -4792,29 +4731,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) goto out_role_sw_put; } - if (tcpc->config && tcpc->config->alt_modes) { - const struct typec_altmode_desc *paltmode = tcpc->config->alt_modes; - - i = 0; - while (paltmode->svid && i < ARRAY_SIZE(port->port_altmode)) { - struct typec_altmode *alt; - - alt = typec_port_register_altmode(port->typec_port, - paltmode); - if (IS_ERR(alt)) { - tcpm_log(port, - "%s: failed to register port alternate mode 0x%x", - dev_name(dev), paltmode->svid); - break; - } - typec_altmode_set_drvdata(alt, port); - alt->ops = &tcpm_altmode_ops; - port->port_altmode[i] = alt; - i++; - paltmode++; - } - } - mutex_lock(&port->lock); tcpm_init(port); mutex_unlock(&port->lock); -- cgit From d16ab536aad208421c5ed32cdcb01b5ab6aa1f19 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 10 Sep 2019 10:54:52 +0800 Subject: usb: chipidea: udc: add new API ci_hdrc_gadget_connect This API is used enable device function, it is called at below situations: - VBUS is connected during boots up - Hot plug occurs during runtime Signed-off-by: Peter Chen Signed-off-by: Jun Li --- drivers/usb/chipidea/udc.c | 63 +++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 0b6166a64d72..8c4383862373 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1524,6 +1524,33 @@ static const struct usb_ep_ops usb_ep_ops = { /****************************************************************************** * GADGET block *****************************************************************************/ +/** + * ci_hdrc_gadget_connect: caller makes sure gadget driver is binded + */ +static void ci_hdrc_gadget_connect(struct usb_gadget *_gadget, int is_active) +{ + struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); + + if (is_active) { + pm_runtime_get_sync(&_gadget->dev); + hw_device_reset(ci); + hw_device_state(ci, ci->ep0out->qh.dma); + usb_gadget_set_state(_gadget, USB_STATE_POWERED); + usb_udc_vbus_handler(_gadget, true); + } else { + usb_udc_vbus_handler(_gadget, false); + if (ci->driver) + ci->driver->disconnect(&ci->gadget); + hw_device_state(ci, 0); + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, + CI_HDRC_CONTROLLER_STOPPED_EVENT); + _gadget_stop_activity(&ci->gadget); + pm_runtime_put_sync(&_gadget->dev); + usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED); + } +} + static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); @@ -1540,26 +1567,8 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) usb_phy_set_charger_state(ci->usb_phy, is_active ? USB_CHARGER_PRESENT : USB_CHARGER_ABSENT); - if (gadget_ready) { - if (is_active) { - pm_runtime_get_sync(&_gadget->dev); - hw_device_reset(ci); - hw_device_state(ci, ci->ep0out->qh.dma); - usb_gadget_set_state(_gadget, USB_STATE_POWERED); - usb_udc_vbus_handler(_gadget, true); - } else { - usb_udc_vbus_handler(_gadget, false); - if (ci->driver) - ci->driver->disconnect(&ci->gadget); - hw_device_state(ci, 0); - if (ci->platdata->notify_event) - ci->platdata->notify_event(ci, - CI_HDRC_CONTROLLER_STOPPED_EVENT); - _gadget_stop_activity(&ci->gadget); - pm_runtime_put_sync(&_gadget->dev); - usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED); - } - } + if (gadget_ready) + ci_hdrc_gadget_connect(_gadget, is_active); return 0; } @@ -1785,18 +1794,10 @@ static int ci_udc_start(struct usb_gadget *gadget, return retval; } - pm_runtime_get_sync(&ci->gadget.dev); - if (ci->vbus_active) { - hw_device_reset(ci); - } else { + if (ci->vbus_active) + ci_hdrc_gadget_connect(gadget, 1); + else usb_udc_vbus_handler(&ci->gadget, false); - pm_runtime_put_sync(&ci->gadget.dev); - return retval; - } - - retval = hw_device_state(ci, ci->ep0out->qh.dma); - if (retval) - pm_runtime_put_sync(&ci->gadget.dev); return retval; } -- cgit From 72dc8df7920fc24eba0f586c56e900a1643ff2b3 Mon Sep 17 00:00:00 2001 From: Jun Li Date: Tue, 10 Sep 2019 14:54:57 +0800 Subject: usb: chipidea: udc: protect usb interrupt enable We hit the problem with below sequence: - ci_udc_vbus_session() update vbus_active flag and ci->driver is valid, - before calling the ci_hdrc_gadget_connect(), usb_gadget_udc_stop() is called by application remove gadget driver, - ci_udc_vbus_session() will contine do ci_hdrc_gadget_connect() as gadget_ready is 1, so udc interrupt is enabled, but ci->driver is NULL. - USB connection irq generated but ci->driver is NULL. As udc irq only should be enabled when gadget driver is binded, so add spinlock to protect the usb irq enable for vbus session handling. Signed-off-by: Jun Li Signed-off-by: Peter Chen --- drivers/usb/chipidea/udc.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 8c4383862373..ffaf46f5d062 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1530,13 +1530,18 @@ static const struct usb_ep_ops usb_ep_ops = { static void ci_hdrc_gadget_connect(struct usb_gadget *_gadget, int is_active) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); + unsigned long flags; if (is_active) { pm_runtime_get_sync(&_gadget->dev); hw_device_reset(ci); - hw_device_state(ci, ci->ep0out->qh.dma); - usb_gadget_set_state(_gadget, USB_STATE_POWERED); - usb_udc_vbus_handler(_gadget, true); + spin_lock_irqsave(&ci->lock, flags); + if (ci->driver) { + hw_device_state(ci, ci->ep0out->qh.dma); + usb_gadget_set_state(_gadget, USB_STATE_POWERED); + usb_udc_vbus_handler(_gadget, true); + } + spin_unlock_irqrestore(&ci->lock, flags); } else { usb_udc_vbus_handler(_gadget, false); if (ci->driver) @@ -1555,19 +1560,16 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); unsigned long flags; - int gadget_ready = 0; spin_lock_irqsave(&ci->lock, flags); ci->vbus_active = is_active; - if (ci->driver) - gadget_ready = 1; spin_unlock_irqrestore(&ci->lock, flags); if (ci->usb_phy) usb_phy_set_charger_state(ci->usb_phy, is_active ? USB_CHARGER_PRESENT : USB_CHARGER_ABSENT); - if (gadget_ready) + if (ci->driver) ci_hdrc_gadget_connect(_gadget, is_active); return 0; @@ -1827,6 +1829,7 @@ static int ci_udc_stop(struct usb_gadget *gadget) unsigned long flags; spin_lock_irqsave(&ci->lock, flags); + ci->driver = NULL; if (ci->vbus_active) { hw_device_state(ci, 0); @@ -1839,7 +1842,6 @@ static int ci_udc_stop(struct usb_gadget *gadget) pm_runtime_put(&ci->gadget.dev); } - ci->driver = NULL; spin_unlock_irqrestore(&ci->lock, flags); ci_udc_stop_for_otg_fsm(ci); -- cgit From 93c2c7330a3b6d973cd82dfd7bcbd6df035752f6 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 9 Sep 2019 14:41:41 +0800 Subject: usb: chipidea: imx: enable vbus and id wakeup only for OTG events If ID or VBUS is from external block, don't enable its wakeup because it isn't used at all. Signed-off-by: Li Jun Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_imx.c | 8 ++++++++ drivers/usb/chipidea/ci_hdrc_imx.h | 2 ++ drivers/usb/chipidea/usbmisc_imx.c | 31 +++++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index df8812c30640..2fde44ca4a48 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -433,6 +433,14 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) goto err_clk; } + if (!IS_ERR(pdata.id_extcon.edev) || + of_property_read_bool(np, "usb-role-switch")) + data->usbmisc_data->ext_id = 1; + + if (!IS_ERR(pdata.vbus_extcon.edev) || + of_property_read_bool(np, "usb-role-switch")) + data->usbmisc_data->ext_vbus = 1; + ret = imx_usbmisc_init_post(data->usbmisc_data); if (ret) { dev_err(dev, "usbmisc post failed, ret=%d\n", ret); diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index c842e03f8767..de2aac9a2868 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -22,6 +22,8 @@ struct imx_usbmisc_data { unsigned int evdo:1; /* set external vbus divider option */ unsigned int ulpi:1; /* connected to an ULPI phy */ unsigned int hsic:1; /* HSIC controlller */ + unsigned int ext_id:1; /* ID from exteranl event */ + unsigned int ext_vbus:1; /* Vbus from exteranl event */ }; int imx_usbmisc_init(struct imx_usbmisc_data *data); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 078c1fdce493..e81e33c26e6c 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -100,6 +100,9 @@ #define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2) #define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3) +#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \ + MX6_BM_ID_WAKEUP) + struct usbmisc_ops { /* It's called once when probe a usb device */ int (*init)(struct imx_usbmisc_data *data); @@ -330,14 +333,25 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data) return 0; } +static u32 usbmisc_wakeup_setting(struct imx_usbmisc_data *data) +{ + u32 wakeup_setting = MX6_USB_OTG_WAKEUP_BITS; + + if (data->ext_id) + wakeup_setting &= ~MX6_BM_ID_WAKEUP; + + if (data->ext_vbus) + wakeup_setting &= ~MX6_BM_VBUS_WAKEUP; + + return wakeup_setting; +} + static int usbmisc_imx6q_set_wakeup (struct imx_usbmisc_data *data, bool enabled) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 val; - u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE | - MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP); int ret = 0; if (data->index > 3) @@ -346,11 +360,12 @@ static int usbmisc_imx6q_set_wakeup spin_lock_irqsave(&usbmisc->lock, flags); val = readl(usbmisc->base + data->index * 4); if (enabled) { - val |= wakeup_setting; + val &= ~MX6_USB_OTG_WAKEUP_BITS; + val |= usbmisc_wakeup_setting(data); } else { if (val & MX6_BM_WAKEUP_INTR) pr_debug("wakeup int at ci_hdrc.%d\n", data->index); - val &= ~wakeup_setting; + val &= ~MX6_USB_OTG_WAKEUP_BITS; } writel(val, usbmisc->base + data->index * 4); spin_unlock_irqrestore(&usbmisc->lock, flags); @@ -547,17 +562,17 @@ static int usbmisc_imx7d_set_wakeup struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 val; - u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE | - MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP); spin_lock_irqsave(&usbmisc->lock, flags); val = readl(usbmisc->base); if (enabled) { - writel(val | wakeup_setting, usbmisc->base); + val &= ~MX6_USB_OTG_WAKEUP_BITS; + val |= usbmisc_wakeup_setting(data); + writel(val, usbmisc->base); } else { if (val & MX6_BM_WAKEUP_INTR) dev_dbg(data->dev, "wakeup int\n"); - writel(val & ~wakeup_setting, usbmisc->base); + writel(val & ~MX6_USB_OTG_WAKEUP_BITS, usbmisc->base); } spin_unlock_irqrestore(&usbmisc->lock, flags); -- cgit From 782c1c49f3dbf335de6b95f2d97b105cca236bc9 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 23 Sep 2019 10:34:38 +0800 Subject: usb: chipidea: core: change vbus-regulator as optional Vbus regualtor is an optional regulator, for platforms, which doesn't have this regulator, it will get a dummy regulator and show warning message. Signed-off-by: Peter Chen --- drivers/usb/chipidea/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 98ee575ee500..dce5db41501c 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -683,7 +683,7 @@ static int ci_get_platdata(struct device *dev, if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { /* Get the vbus regulator */ - platdata->reg_vbus = devm_regulator_get(dev, "vbus"); + platdata->reg_vbus = devm_regulator_get_optional(dev, "vbus"); if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { return -EPROBE_DEFER; } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { -- cgit From df17aa9fb31f6a41aad37b984909d8ef1a958271 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 9 Oct 2019 10:52:28 +0800 Subject: usb: chipidea: imx: check data->usbmisc_data against NULL before access As usbmisc_data is optional, so add the check before access its member, this fix below static checker warning: drivers/usb/chipidea/ci_hdrc_imx.c:438 ci_hdrc_imx_probe() warn: 'data->usbmisc_data' can also be NULL which is introduced by Patch 15b80f7c3a7f: "usb: chipidea: imx: enable vbus and id wakeup only for OTG events" Reported-by: Dan Carpenter Signed-off-by: Li Jun Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_imx.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 2fde44ca4a48..16700170bc34 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -433,13 +433,15 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) goto err_clk; } - if (!IS_ERR(pdata.id_extcon.edev) || - of_property_read_bool(np, "usb-role-switch")) - data->usbmisc_data->ext_id = 1; - - if (!IS_ERR(pdata.vbus_extcon.edev) || - of_property_read_bool(np, "usb-role-switch")) - data->usbmisc_data->ext_vbus = 1; + if (data->usbmisc_data) { + if (!IS_ERR(pdata.id_extcon.edev) || + of_property_read_bool(np, "usb-role-switch")) + data->usbmisc_data->ext_id = 1; + + if (!IS_ERR(pdata.vbus_extcon.edev) || + of_property_read_bool(np, "usb-role-switch")) + data->usbmisc_data->ext_vbus = 1; + } ret = imx_usbmisc_init_post(data->usbmisc_data); if (ret) { -- cgit From 7d5ec335f94e74e885ca2f6c97a3479fe9fe3b15 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Oct 2019 08:40:46 +0800 Subject: usb: chipidea: imx: change hsic power regulator as optional Not every platform needs this regulator. Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_imx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 16700170bc34..25a38ed27aa8 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -359,7 +359,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) return PTR_ERR(data->pinctrl_hsic_active); } - data->hsic_pad_regulator = devm_regulator_get(dev, "hsic"); + data->hsic_pad_regulator = + devm_regulator_get_optional(dev, "hsic"); if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) { return -EPROBE_DEFER; } else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { -- cgit From 3f4aad6e1a4c26a20700fb4f630e4e6c6831db47 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Oct 2019 08:59:14 +0800 Subject: usb: chipidea: imx: refine the error handling for hsic - -EPROBE_DEFER is an error, but without need show error message - If pintrol is not existed, as pintrol is NULL Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_imx.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 25a38ed27aa8..c34fcc079cd4 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -330,8 +330,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) pdata.flags |= CI_HDRC_IMX_IS_HSIC; data->usbmisc_data->hsic = 1; data->pinctrl = devm_pinctrl_get(dev); - if (IS_ERR(data->pinctrl)) { - dev_err(dev, "pinctrl get failed, err=%ld\n", + if (PTR_ERR(data->pinctrl) == -ENODEV) + data->pinctrl = NULL; + else if (IS_ERR(data->pinctrl)) { + if (PTR_ERR(data->pinctrl) != -EPROBE_DEFER) + dev_err(dev, "pinctrl get failed, err=%ld\n", PTR_ERR(data->pinctrl)); return PTR_ERR(data->pinctrl); } @@ -361,13 +364,13 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) data->hsic_pad_regulator = devm_regulator_get_optional(dev, "hsic"); - if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { + if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { /* no pad regualator is needed */ data->hsic_pad_regulator = NULL; } else if (IS_ERR(data->hsic_pad_regulator)) { - dev_err(dev, "Get HSIC pad regulator error: %ld\n", + if (PTR_ERR(data->hsic_pad_regulator) != -EPROBE_DEFER) + dev_err(dev, + "Get HSIC pad regulator error: %ld\n", PTR_ERR(data->hsic_pad_regulator)); return PTR_ERR(data->hsic_pad_regulator); } -- cgit From 4d6141288c33b73027260e73df262464cbe1fd0d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Oct 2019 09:20:26 +0800 Subject: usb: chipidea: imx: pinctrl for HSIC is optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For imx chipidea controllers, if they use mxs PHY, they need pinctrl for HSIC. Otherwise, it doesn't need pinctrl and usbmisc control. Like imx7d and imx8mm. Reported-by: AndrĂ© Draszik Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_imx.c | 63 +++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 28 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index c34fcc079cd4..d8e7eb2f97b9 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -274,11 +274,14 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) switch (event) { case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: - ret = pinctrl_select_state(data->pinctrl, - data->pinctrl_hsic_active); - if (ret) - dev_err(dev, "hsic_active select failed, err=%d\n", - ret); + if (data->pinctrl) { + ret = pinctrl_select_state(data->pinctrl, + data->pinctrl_hsic_active); + if (ret) + dev_err(dev, + "hsic_active select failed, err=%d\n", + ret); + } break; case CI_HDRC_IMX_HSIC_SUSPEND_EVENT: ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data); @@ -306,7 +309,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) const struct ci_hdrc_imx_platform_flag *imx_platform_flag; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - struct pinctrl_state *pinctrl_hsic_idle; of_id = of_match_device(ci_hdrc_imx_dt_ids, dev); if (!of_id) @@ -339,6 +341,33 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) return PTR_ERR(data->pinctrl); } + data->hsic_pad_regulator = + devm_regulator_get_optional(dev, "hsic"); + if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { + /* no pad regualator is needed */ + data->hsic_pad_regulator = NULL; + } else if (IS_ERR(data->hsic_pad_regulator)) { + if (PTR_ERR(data->hsic_pad_regulator) != -EPROBE_DEFER) + dev_err(dev, + "Get HSIC pad regulator error: %ld\n", + PTR_ERR(data->hsic_pad_regulator)); + return PTR_ERR(data->hsic_pad_regulator); + } + + if (data->hsic_pad_regulator) { + ret = regulator_enable(data->hsic_pad_regulator); + if (ret) { + dev_err(dev, + "Failed to enable HSIC pad regulator\n"); + return ret; + } + } + } + + /* HSIC pinctrl handling */ + if (data->pinctrl) { + struct pinctrl_state *pinctrl_hsic_idle; + pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle"); if (IS_ERR(pinctrl_hsic_idle)) { dev_err(dev, @@ -361,28 +390,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) PTR_ERR(data->pinctrl_hsic_active)); return PTR_ERR(data->pinctrl_hsic_active); } - - data->hsic_pad_regulator = - devm_regulator_get_optional(dev, "hsic"); - if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { - /* no pad regualator is needed */ - data->hsic_pad_regulator = NULL; - } else if (IS_ERR(data->hsic_pad_regulator)) { - if (PTR_ERR(data->hsic_pad_regulator) != -EPROBE_DEFER) - dev_err(dev, - "Get HSIC pad regulator error: %ld\n", - PTR_ERR(data->hsic_pad_regulator)); - return PTR_ERR(data->hsic_pad_regulator); - } - - if (data->hsic_pad_regulator) { - ret = regulator_enable(data->hsic_pad_regulator); - if (ret) { - dev_err(dev, - "Failed to enable HSIC pad regulator\n"); - return ret; - } - } } if (pdata.flags & CI_HDRC_PMQOS) -- cgit From 347bc8cb26388791c5881a3775cb14a3f765a674 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 18 Nov 2019 10:21:19 +0100 Subject: usb-serial: cp201x: support Mark-10 digital force gauge Add support for the Mark-10 digital force gauge device to the cp201x driver. Based on a report and a larger patch from Joel Jennings Reported-by: Joel Jennings Cc: stable Acked-by: Johan Hovold Link: https://lore.kernel.org/r/20191118092119.GA153852@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 979bef9bfb6b..f5143eedbc48 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -125,6 +125,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */ { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ + { USB_DEVICE(0x10C4, 0x83AA) }, /* Mark-10 Digital Force Gauge */ { USB_DEVICE(0x10C4, 0x83D8) }, /* DekTec DTA Plus VHF/UHF Booster/Attenuator */ { USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */ { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ -- cgit From 6351653febbb784d86fdf83afe41f7523a61b392 Mon Sep 17 00:00:00 2001 From: Nagarjuna Kristam Date: Mon, 4 Nov 2019 14:54:30 +0530 Subject: usb: host: xhci-tegra: Correct phy enable sequence XUSB phy needs to be enabled before un-powergating the power partitions. However in the current sequence, it happens opposite. Correct the phy enable and powergating partition sequence to avoid any boot hangs. Signed-off-by: Nagarjuna Kristam Cc: stable Signed-off-by: Jui Chang Kuo Tested-by: Jon Hunter Acked-by: Thierry Reding Link: https://lore.kernel.org/r/1572859470-7823-1-git-send-email-nkristam@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 540b47a99824..bf9065438320 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -763,7 +763,6 @@ static int tegra_xusb_runtime_suspend(struct device *dev) { struct tegra_xusb *tegra = dev_get_drvdata(dev); - tegra_xusb_phy_disable(tegra); regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); tegra_xusb_clk_disable(tegra); @@ -787,16 +786,8 @@ static int tegra_xusb_runtime_resume(struct device *dev) goto disable_clk; } - err = tegra_xusb_phy_enable(tegra); - if (err < 0) { - dev_err(dev, "failed to enable PHYs: %d\n", err); - goto disable_regulator; - } - return 0; -disable_regulator: - regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); disable_clk: tegra_xusb_clk_disable(tegra); return err; @@ -1188,6 +1179,12 @@ static int tegra_xusb_probe(struct platform_device *pdev) */ platform_set_drvdata(pdev, tegra); + err = tegra_xusb_phy_enable(tegra); + if (err < 0) { + dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err); + goto put_hcd; + } + pm_runtime_enable(&pdev->dev); if (pm_runtime_enabled(&pdev->dev)) err = pm_runtime_get_sync(&pdev->dev); @@ -1196,7 +1193,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) if (err < 0) { dev_err(&pdev->dev, "failed to enable device: %d\n", err); - goto disable_rpm; + goto disable_phy; } tegra_xusb_config(tegra, regs); @@ -1282,9 +1279,11 @@ remove_usb2: put_rpm: if (!pm_runtime_status_suspended(&pdev->dev)) tegra_xusb_runtime_suspend(&pdev->dev); -disable_rpm: - pm_runtime_disable(&pdev->dev); +put_hcd: usb_put_hcd(tegra->hcd); +disable_phy: + tegra_xusb_phy_disable(tegra); + pm_runtime_disable(&pdev->dev); put_powerdomains: if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) { tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); @@ -1321,6 +1320,8 @@ static int tegra_xusb_remove(struct platform_device *pdev) tegra_xusb_powerdomain_remove(&pdev->dev, tegra); } + tegra_xusb_phy_disable(tegra); + tegra_xusb_padctl_put(tegra->padctl); return 0; -- cgit From bff000cae1eec750d62e265c4ba2db9af57b17e1 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 14 Nov 2019 12:27:56 +0100 Subject: USB: uas: honor flag to avoid CAPACITY16 Copy the support over from usb-storage to get feature parity Signed-off-by: Oliver Neukum Cc: stable Link: https://lore.kernel.org/r/20191114112758.32747-2-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 34538253f12c..def2d4aba549 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -825,6 +825,10 @@ static int uas_slave_configure(struct scsi_device *sdev) sdev->wce_default_on = 1; } + /* Some disks cannot handle READ_CAPACITY_16 */ + if (devinfo->flags & US_FL_NO_READ_CAPACITY_16) + sdev->no_read_capacity_16 = 1; + /* * Some disks return the total number of blocks in response * to READ CAPACITY rather than the highest block number. -- cgit From 335cbbd5762d5e5c67a8ddd6e6362c2aa42a328f Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 14 Nov 2019 12:27:57 +0100 Subject: USB: uas: heed CAPACITY_HEURISTICS There is no need to ignore this flag. We should be as close to storage in that regard as makes sense, so honor flags whose cost is tiny. Signed-off-by: Oliver Neukum Cc: stable Link: https://lore.kernel.org/r/20191114112758.32747-3-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index def2d4aba549..475b9c692827 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -837,6 +837,12 @@ static int uas_slave_configure(struct scsi_device *sdev) if (devinfo->flags & US_FL_FIX_CAPACITY) sdev->fix_capacity = 1; + /* + * in some cases we have to guess + */ + if (devinfo->flags & US_FL_CAPACITY_HEURISTICS) + sdev->guess_capacity = 1; + /* * Some devices don't like MODE SENSE with page=0x3f, * which is the command used for checking if a device -- cgit From bc3bdb12bbb3492067c8719011576370e959a2e6 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Tue, 8 Sep 2015 09:53:38 -0700 Subject: usb-storage: Disable UAS on JMicron SATA enclosure Steve Ellis reported incorrect block sizes and alignement offsets with a SATA enclosure. Adding a quirk to disable UAS fixes the problems. Reported-by: Steven Ellis Cc: Pacho Ramos Signed-off-by: Laura Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_uas.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index d0bdebd87ce3..1b23741036ee 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -87,12 +87,15 @@ UNUSUAL_DEV(0x2537, 0x1068, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_IGNORE_UAS), -/* Reported-by: Takeo Nakayama */ +/* + * Initially Reported-by: Takeo Nakayama + * UAS Ignore Reported by Steven Ellis + */ UNUSUAL_DEV(0x357d, 0x7788, 0x0000, 0x9999, "JMicron", "JMS566", USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_REPORT_OPCODES), + US_FL_NO_REPORT_OPCODES | US_FL_IGNORE_UAS), /* Reported-by: Hans de Goede */ UNUSUAL_DEV(0x4971, 0x1012, 0x0000, 0x9999, -- cgit From 093edc2baad2c258b1f55d1ab9c63c2b5ae67e42 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 30 Oct 2019 03:40:46 +0000 Subject: usb: gadget: configfs: Fix missing spin_lock_init() The driver allocates the spinlock but not initialize it. Use spin_lock_init() on it to initialize it correctly. This is detected by Coccinelle semantic patch. Fixes: 1a1c851bbd70 ("usb: gadget: configfs: fix concurrent issue between composite APIs") Signed-off-by: Wei Yongjun Cc: stable Reviewed-by: Peter Chen Link: https://lore.kernel.org/r/20191030034046.188808-1-weiyongjun1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/configfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 33852c2b29d1..ab9ac48a751a 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1544,6 +1544,7 @@ static struct config_group *gadgets_make( gi->composite.resume = NULL; gi->composite.max_speed = USB_SPEED_SUPER; + spin_lock_init(&gi->spinlock); mutex_init(&gi->lock); INIT_LIST_HEAD(&gi->string_list); INIT_LIST_HEAD(&gi->available_func); -- cgit From c3afa22243453d7a09694bd2975074a8db53d4bc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 21:29:14 +0800 Subject: usb: dwc3: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191121132914.29368-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/Kconfig | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 0d97e6bfaf36..206caa0ea1c6 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -97,24 +97,24 @@ config USB_DWC3_KEYSTONE Say 'Y' or 'M' here if you have one such device config USB_DWC3_MESON_G12A - tristate "Amlogic Meson G12A Platforms" - depends on OF && COMMON_CLK - depends on ARCH_MESON || COMPILE_TEST - default USB_DWC3 - select USB_ROLE_SWITCH + tristate "Amlogic Meson G12A Platforms" + depends on OF && COMMON_CLK + depends on ARCH_MESON || COMPILE_TEST + default USB_DWC3 + select USB_ROLE_SWITCH select REGMAP_MMIO - help - Support USB2/3 functionality in Amlogic G12A platforms. - Say 'Y' or 'M' if you have one such device. + help + Support USB2/3 functionality in Amlogic G12A platforms. + Say 'Y' or 'M' if you have one such device. config USB_DWC3_OF_SIMPLE - tristate "Generic OF Simple Glue Layer" - depends on OF && COMMON_CLK - default USB_DWC3 - help - Support USB2/3 functionality in simple SoC integrations. - Currently supports Xilinx and Qualcomm DWC USB3 IP. - Say 'Y' or 'M' if you have one such device. + tristate "Generic OF Simple Glue Layer" + depends on OF && COMMON_CLK + default USB_DWC3 + help + Support USB2/3 functionality in simple SoC integrations. + Currently supports Xilinx and Qualcomm DWC USB3 IP. + Say 'Y' or 'M' if you have one such device. config USB_DWC3_ST tristate "STMicroelectronics Platforms" -- cgit From 2f8b6e9a2b5ee23e5a14eed6a294f05dc669757b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 21:29:10 +0800 Subject: usb: host: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191121132910.29310-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index d6164ede63d3..8d730180db06 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -38,9 +38,9 @@ config USB_XHCI_DBGCAP before enabling this option. If unsure, say 'N'. config USB_XHCI_PCI - tristate - depends on USB_PCI - default y + tristate + depends on USB_PCI + default y config USB_XHCI_PLATFORM tristate "Generic xHCI driver for a platform device" @@ -245,13 +245,13 @@ config USB_EHCI_HCD_AT91 Atmel chips. config USB_EHCI_TEGRA - tristate "NVIDIA Tegra HCD support" - depends on ARCH_TEGRA - select USB_EHCI_ROOT_HUB_TT - select USB_TEGRA_PHY - help - This driver enables support for the internal USB Host Controllers - found in NVIDIA Tegra SoCs. The controllers are EHCI compliant. + tristate "NVIDIA Tegra HCD support" + depends on ARCH_TEGRA + select USB_EHCI_ROOT_HUB_TT + select USB_TEGRA_PHY + help + This driver enables support for the internal USB Host Controllers + found in NVIDIA Tegra SoCs. The controllers are EHCI compliant. config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" @@ -269,10 +269,10 @@ config USB_EHCI_SH If you use the PCI EHCI controller, this option is not necessary. config USB_EHCI_EXYNOS - tristate "EHCI support for Samsung S5P/EXYNOS SoC Series" - depends on ARCH_S5PV210 || ARCH_EXYNOS - help - Enable support for the Samsung Exynos SOC's on-chip EHCI controller. + tristate "EHCI support for Samsung S5P/EXYNOS SoC Series" + depends on ARCH_S5PV210 || ARCH_EXYNOS + help + Enable support for the Samsung Exynos SOC's on-chip EHCI controller. config USB_EHCI_MV tristate "EHCI support for Marvell PXA/MMP USB controller" @@ -545,7 +545,7 @@ config USB_OHCI_EXYNOS tristate "OHCI support for Samsung S5P/EXYNOS SoC Series" depends on ARCH_S5PV210 || ARCH_EXYNOS help - Enable support for the Samsung Exynos SOC's on-chip OHCI controller. + Enable support for the Samsung Exynos SOC's on-chip OHCI controller. config USB_CNS3XXX_OHCI bool "Cavium CNS3XXX OHCI Module (DEPRECATED)" @@ -609,8 +609,8 @@ config USB_UHCI_PLATFORM default y if (ARCH_VT8500 || ARCH_ASPEED) config USB_UHCI_ASPEED - bool - default y if ARCH_ASPEED + bool + default y if ARCH_ASPEED config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" @@ -713,14 +713,14 @@ config USB_RENESAS_USBHS_HCD module will be called renesas-usbhs. config USB_IMX21_HCD - tristate "i.MX21 HCD support" - depends on ARM && ARCH_MXC - help - This driver enables support for the on-chip USB host in the - i.MX21 processor. - - To compile this driver as a module, choose M here: the - module will be called "imx21-hcd". + tristate "i.MX21 HCD support" + depends on ARM && ARCH_MXC + help + This driver enables support for the on-chip USB host in the + i.MX21 processor. + + To compile this driver as a module, choose M here: the + module will be called "imx21-hcd". config USB_HCD_BCMA tristate "BCMA usb host driver" -- cgit From b14f8b90341bb3258e536914ad0b1ad1a076233f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 21:29:05 +0800 Subject: usb: gadget: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191121132905.29248-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/Kconfig | 26 +++++++++++++------------- drivers/usb/gadget/udc/Kconfig | 6 +++--- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 38eaa9417b38..119a4e47681f 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -149,21 +149,21 @@ config USB_ETH_RNDIS is given in comments found in that info file. config USB_ETH_EEM - bool "Ethernet Emulation Model (EEM) support" - depends on USB_ETH + bool "Ethernet Emulation Model (EEM) support" + depends on USB_ETH select USB_LIBCOMPOSITE select USB_F_EEM - help - CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM - and therefore can be supported by more hardware. Technically ECM and - EEM are designed for different applications. The ECM model extends - the network interface to the target (e.g. a USB cable modem), and the - EEM model is for mobile devices to communicate with hosts using - ethernet over USB. For Linux gadgets, however, the interface with - the host is the same (a usbX device), so the differences are minimal. - - If you say "y" here, the Ethernet gadget driver will use the EEM - protocol rather than ECM. If unsure, say "n". + help + CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM + and therefore can be supported by more hardware. Technically ECM and + EEM are designed for different applications. The ECM model extends + the network interface to the target (e.g. a USB cable modem), and the + EEM model is for mobile devices to communicate with hosts using + ethernet over USB. For Linux gadgets, however, the interface with + the host is the same (a usbX device), so the differences are minimal. + + If you say "y" here, the Ethernet gadget driver will use the EEM + protocol rather than ECM. If unsure, say "n". config USB_G_NCM tristate "Network Control Model (NCM) support" diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index acaec3ae6ad7..ae70ce29d5e4 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -120,9 +120,9 @@ config USB_FOTG210_UDC dynamically linked module called "fotg210_udc". config USB_GR_UDC - tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" - depends on HAS_DMA - help + tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" + depends on HAS_DMA + help Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB VHDL IP core library. -- cgit From 6aad39f63949901de57f4c78aa3da79625df4bad Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 21:29:01 +0800 Subject: usb: misc: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191121132901.29186-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/Kconfig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 653aa34aba6c..834b2494da73 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -233,17 +233,17 @@ config USB_HUB_USB251XB Say Y or M here if you need to configure such a device via SMBus. config USB_HSIC_USB3503 - tristate "USB3503 HSIC to USB20 Driver" - depends on I2C - select REGMAP_I2C - help - This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver. + tristate "USB3503 HSIC to USB20 Driver" + depends on I2C + select REGMAP_I2C + help + This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver. config USB_HSIC_USB4604 - tristate "USB4604 HSIC to USB20 Driver" - depends on I2C - help - This option enables support for SMSC USB4604 HSIC to USB 2.0 Driver. + tristate "USB4604 HSIC to USB20 Driver" + depends on I2C + help + This option enables support for SMSC USB4604 HSIC to USB 2.0 Driver. config USB_LINK_LAYER_TEST tristate "USB Link Layer Test driver" -- cgit From 0b2c26fb1144a671d462195d4514a0453f55ebd1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 21:28:56 +0800 Subject: usb: serial: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191121132856.29130-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/Kconfig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 0a8c16a8cda2..ed4a18b435a0 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -269,16 +269,16 @@ config USB_SERIAL_F8153X config USB_SERIAL_GARMIN - tristate "USB Garmin GPS driver" - help - Say Y here if you want to connect to your Garmin GPS. - Should work with most Garmin GPS devices which have a native USB port. + tristate "USB Garmin GPS driver" + help + Say Y here if you want to connect to your Garmin GPS. + Should work with most Garmin GPS devices which have a native USB port. - See for the latest - version of the driver. + See for the latest + version of the driver. - To compile this driver as a module, choose M here: the - module will be called garmin_gps. + To compile this driver as a module, choose M here: the + module will be called garmin_gps. config USB_SERIAL_IPW tristate "USB IPWireless (3G UMTS TDD) Driver" -- cgit From d4836b69d9352ffbf2a2fea9735b0c7f3388a579 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 20 Nov 2019 14:43:01 +0800 Subject: usb: musb: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move musb's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1574232183-5760-1-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index f42858e2b54c..7b6281ab62ed 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -325,7 +325,7 @@ void musb_init_debugfs(struct musb *musb) { struct dentry *root; - root = debugfs_create_dir(dev_name(musb->controller), NULL); + root = debugfs_create_dir(dev_name(musb->controller), usb_debug_root); musb->debugfs_root = root; debugfs_create_file("regdump", S_IRUGO, root, musb, &musb_regdump_fops); -- cgit From f5b7f7c838216826072c92c8a26aeb2707d707a8 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 20 Nov 2019 14:43:02 +0800 Subject: usb: gadget: atmel: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move it's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1574232183-5760-2-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/atmel_usba_udc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 1d0d8952a74b..8a42768e3213 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -226,7 +227,7 @@ static void usba_init_debugfs(struct usba_udc *udc) struct dentry *root; struct resource *regs_resource; - root = debugfs_create_dir(udc->gadget.name, NULL); + root = debugfs_create_dir(udc->gadget.name, usb_debug_root); udc->debugfs_root = root; regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM, -- cgit From 91a9f2d3f9762e59cca251d2c6cef8cda1a4e62b Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 20 Nov 2019 14:43:03 +0800 Subject: usb: gadget: udc: gr_udc: create debugfs directory under usb root Now the USB gadget subsystem can use the USB debugfs root directory, so move it's directory from the root of the debugfs filesystem into the root of usb Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/1574232183-5760-3-git-send-email-chunfeng.yun@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/gr_udc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index c63383221b5a..64d80c65bb96 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -208,7 +209,7 @@ static void gr_dfs_create(struct gr_udc *dev) { const char *name = "gr_udc_state"; - dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL); + dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), usb_debug_root); debugfs_create_file(name, 0444, dev->dfs_root, dev, &gr_dfs_fops); } -- cgit