diff options
Diffstat (limited to 'drivers/usb/host/uhci-platform.c')
| -rw-r--r-- | drivers/usb/host/uhci-platform.c | 108 |
1 files changed, 78 insertions, 30 deletions
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index d033a0ec7f0d..5e02f2ceafb6 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Generic UHCI HCD (Host Controller Driver) for Platform Devices * @@ -8,13 +9,17 @@ */ #include <linux/of.h> +#include <linux/device.h> #include <linux/platform_device.h> +#include <linux/reset.h> static int uhci_platform_init(struct usb_hcd *hcd) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - uhci->rh_numports = uhci_count_ports(hcd); + /* Probe number of ports if not already provided by DT */ + if (!uhci->rh_numports) + uhci->rh_numports = uhci_count_ports(hcd); /* Set up pointers to to generic functions */ uhci->reset_hc = uhci_generic_reset_hc; @@ -37,7 +42,7 @@ static const struct hc_driver uhci_platform_hc_driver = { /* Generic hardware linkage */ .irq = uhci_irq, - .flags = HCD_MEMORY | HCD_USB11, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB11, /* Basic lifecycle operations */ .reset = uhci_platform_init, @@ -62,6 +67,8 @@ static const struct hc_driver uhci_platform_hc_driver = { static int uhci_hcd_platform_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; + bool dma_mask_64 = false; struct usb_hcd *hcd; struct uhci_hcd *uhci; struct resource *res; @@ -75,63 +82,102 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) * Since shared usb code relies on it, set it here for now. * Once we have dma capability bindings this can go away. */ - if (!pdev->dev.dma_mask) - pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; - if (!pdev->dev.coherent_dma_mask) - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + if (of_device_get_match_data(&pdev->dev)) + dma_mask_64 = true; + + ret = dma_coerce_mask_and_coherent(&pdev->dev, + dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); + if (ret) + return ret; hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev, pdev->name); if (!hcd) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + uhci = hcd_to_uhci(hcd); + + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); + goto err_rmr; + } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - pr_err("%s: request_mem_region failed\n", __func__); - ret = -EBUSY; + uhci->regs = hcd->regs; + + /* Grab some things from the device-tree */ + if (np) { + u32 num_ports; + + if (of_property_read_u32(np, "#ports", &num_ports) == 0) { + uhci->rh_numports = num_ports; + dev_info(&pdev->dev, + "Detected %d ports from device-tree\n", + num_ports); + } + if (of_device_is_compatible(np, "aspeed,ast2400-uhci") || + of_device_is_compatible(np, "aspeed,ast2500-uhci") || + of_device_is_compatible(np, "aspeed,ast2600-uhci") || + of_device_is_compatible(np, "aspeed,ast2700-uhci")) { + uhci->is_aspeed = 1; + dev_info(&pdev->dev, + "Enabled Aspeed implementation workarounds\n"); + } + } + + /* Get and enable clock if any specified */ + uhci->clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(uhci->clk)) { + ret = PTR_ERR(uhci->clk); + goto err_rmr; + } + ret = clk_prepare_enable(uhci->clk); + if (ret) { + dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", ret); goto err_rmr; } - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - pr_err("%s: ioremap failed\n", __func__); - ret = -ENOMEM; - goto err_irq; + uhci->rsts = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(uhci->rsts)) { + ret = PTR_ERR(uhci->rsts); + goto err_clk; } - uhci = hcd_to_uhci(hcd); + ret = reset_control_deassert(uhci->rsts); + if (ret) + goto err_clk; - uhci->regs = hcd->regs; + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_reset; - ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED | - IRQF_SHARED); + ret = usb_add_hcd(hcd, ret, IRQF_SHARED); if (ret) - goto err_uhci; + goto err_reset; + device_wakeup_enable(hcd->self.controller); return 0; -err_uhci: - iounmap(hcd->regs); -err_irq: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_reset: + reset_control_assert(uhci->rsts); +err_clk: + clk_disable_unprepare(uhci->clk); err_rmr: usb_put_hcd(hcd); return ret; } -static int uhci_hcd_platform_remove(struct platform_device *pdev) +static void uhci_hcd_platform_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + reset_control_assert(uhci->rsts); + clk_disable_unprepare(uhci->clk); usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); - - return 0; } /* Make sure the controller is quiescent and that we're not using it @@ -149,9 +195,12 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op) } static const struct of_device_id platform_uhci_ids[] = { + { .compatible = "generic-uhci", }, { .compatible = "platform-uhci", }, + { .compatible = "aspeed,ast2700-uhci", .data = (void *)1 }, {} }; +MODULE_DEVICE_TABLE(of, platform_uhci_ids); static struct platform_driver uhci_platform_driver = { .probe = uhci_hcd_platform_probe, @@ -159,7 +208,6 @@ static struct platform_driver uhci_platform_driver = { .shutdown = uhci_hcd_platform_shutdown, .driver = { .name = "platform-uhci", - .owner = THIS_MODULE, .of_match_table = platform_uhci_ids, }, }; |
