From 314b41b16a71ee824f55e2791fcb92997672da37 Mon Sep 17 00:00:00 2001 From: Wu Liang feng Date: Wed, 24 Dec 2014 18:22:19 +0800 Subject: USB: ehci-platform: Support ehci reset after resume quirk The Rockchip rk3288 EHCI controller doesn't properly detect the case when a device is removed during suspend. Specifically, when usb resume from suspend, the EHCI controller maintaining the USB state (FLAG_CF is 1, Current Connect Status is 1), but a USB device (like a USB camera on rk3288) may have been disconnected actually. Let's add a quirk to force ehci to go into the usb_root_hub_lost_power() path and reset after resume. This should generally reset the whole controller and all ports and initialize everything cleanly again, and bring the devices back up. As part of this, rename the "hibernation" paramter of ehci_resume() to force_reset since hibernation is simply another case where we can't trust the autodetected status and need to force a reset of devices. Signed-off-by: Wu Liang feng Reviewed-by: Julius Werner Reviewed-by: Doug Anderson Reviewed-by: Tomasz Figa Reviewed-by: Pawel Osciak Reviewed-by: Sonny Rao Acked-by: Alan Stern Tested-by: Doug Anderson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb/host/ehci-platform.c') diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 8557803e6154..db5c29edf6db 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -185,6 +185,10 @@ static int ehci_platform_probe(struct platform_device *dev) if (of_property_read_bool(dev->dev.of_node, "big-endian")) ehci->big_endian_mmio = ehci->big_endian_desc = 1; + if (of_property_read_bool(dev->dev.of_node, + "needs-reset-on-resume")) + pdata->reset_on_resume = 1; + priv->phy = devm_phy_get(&dev->dev, "usb"); if (IS_ERR(priv->phy)) { err = PTR_ERR(priv->phy); @@ -340,7 +344,7 @@ static int ehci_platform_resume(struct device *dev) return err; } - ehci_resume(hcd, false); + ehci_resume(hcd, pdata->reset_on_resume); return 0; } #endif /* CONFIG_PM_SLEEP */ -- cgit v1.2.3-59-g8ed1b From a95cfa6b86a19a822877e75d5a73b2a95d249e70 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Tue, 6 Jan 2015 13:48:56 +0100 Subject: USB: host: Remove hard-coded octeon platform information for ehci/ohci Instead rely on device tree information for ehci and ohci. This was suggested with http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=1401358203-60225-4-git-send-email-alex.smith%40imgtec.com "The device tree will *always* have correct ehci/ohci clock configuration, so use it. This allows us to remove a big chunk of platform configuration code from octeon-platform.c." More or less I rebased that patch on Alan's work to remove ehci-octeon and ohci-octeon drivers. Cc: David Daney Cc: Alex Smith Cc: Alan Stern Signed-off-by: Andreas Herrmann Acked-by: Ralf Baechle Tested-by: Aaro Koskinen Signed-off-by: Greg Kroah-Hartman --- arch/mips/cavium-octeon/octeon-platform.c | 148 +++++++++++++----------------- drivers/usb/host/ehci-platform.c | 1 + drivers/usb/host/ohci-platform.c | 1 + 3 files changed, 64 insertions(+), 86 deletions(-) (limited to 'drivers/usb/host/ehci-platform.c') diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c index b67ddf0f8bcd..eea60b6c927b 100644 --- a/arch/mips/cavium-octeon/octeon-platform.c +++ b/arch/mips/cavium-octeon/octeon-platform.c @@ -77,7 +77,7 @@ static DEFINE_MUTEX(octeon2_usb_clocks_mutex); static int octeon2_usb_clock_start_cnt; -static void octeon2_usb_clocks_start(void) +static void octeon2_usb_clocks_start(struct device *dev) { u64 div; union cvmx_uctlx_if_ena if_ena; @@ -86,6 +86,8 @@ static void octeon2_usb_clocks_start(void) union cvmx_uctlx_uphy_portx_ctl_status port_ctl_status; int i; unsigned long io_clk_64_to_ns; + u32 clock_rate = 12000000; + bool is_crystal_clock = false; mutex_lock(&octeon2_usb_clocks_mutex); @@ -96,6 +98,28 @@ static void octeon2_usb_clocks_start(void) io_clk_64_to_ns = 64000000000ull / octeon_get_io_clock_rate(); + if (dev->of_node) { + struct device_node *uctl_node; + const char *clock_type; + + uctl_node = of_get_parent(dev->of_node); + if (!uctl_node) { + dev_err(dev, "No UCTL device node\n"); + goto exit; + } + i = of_property_read_u32(uctl_node, + "refclk-frequency", &clock_rate); + if (i) { + dev_err(dev, "No UCTL \"refclk-frequency\"\n"); + goto exit; + } + i = of_property_read_string(uctl_node, + "refclk-type", &clock_type); + + if (!i && strcmp("crystal", clock_type) == 0) + is_crystal_clock = true; + } + /* * Step 1: Wait for voltages stable. That surely happened * before starting the kernel. @@ -126,9 +150,22 @@ static void octeon2_usb_clocks_start(void) cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); /* 3b */ - /* 12MHz crystal. */ - clk_rst_ctl.s.p_refclk_sel = 0; - clk_rst_ctl.s.p_refclk_div = 0; + clk_rst_ctl.s.p_refclk_sel = is_crystal_clock ? 0 : 1; + switch (clock_rate) { + default: + pr_err("Invalid UCTL clock rate of %u, using 12000000 instead\n", + clock_rate); + /* Fall through */ + case 12000000: + clk_rst_ctl.s.p_refclk_div = 0; + break; + case 24000000: + clk_rst_ctl.s.p_refclk_div = 1; + break; + case 48000000: + clk_rst_ctl.s.p_refclk_div = 2; + break; + } cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); /* 3c */ @@ -259,7 +296,7 @@ static void octeon2_usb_clocks_stop(void) static int octeon_ehci_power_on(struct platform_device *pdev) { - octeon2_usb_clocks_start(); + octeon2_usb_clocks_start(&pdev->dev); return 0; } @@ -277,11 +314,11 @@ static struct usb_ehci_pdata octeon_ehci_pdata = { .power_off = octeon_ehci_power_off, }; -static void __init octeon_ehci_hw_start(void) +static void __init octeon_ehci_hw_start(struct device *dev) { union cvmx_uctlx_ehci_ctl ehci_ctl; - octeon2_usb_clocks_start(); + octeon2_usb_clocks_start(dev); ehci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_EHCI_CTL(0)); /* Use 64-bit addressing. */ @@ -299,59 +336,28 @@ static u64 octeon_ehci_dma_mask = DMA_BIT_MASK(64); static int __init octeon_ehci_device_init(void) { struct platform_device *pd; + struct device_node *ehci_node; int ret = 0; - struct resource usb_resources[] = { - { - .flags = IORESOURCE_MEM, - }, { - .flags = IORESOURCE_IRQ, - } - }; - - /* Only Octeon2 has ehci/ohci */ - if (!OCTEON_IS_MODEL(OCTEON_CN63XX)) + ehci_node = of_find_node_by_name(NULL, "ehci"); + if (!ehci_node) return 0; - if (octeon_is_simulation() || usb_disabled()) - return 0; /* No USB in the simulator. */ - - pd = platform_device_alloc("ehci-platform", 0); - if (!pd) { - ret = -ENOMEM; - goto out; - } - - usb_resources[0].start = 0x00016F0000000000ULL; - usb_resources[0].end = usb_resources[0].start + 0x100; - - usb_resources[1].start = OCTEON_IRQ_USB0; - usb_resources[1].end = OCTEON_IRQ_USB0; - - ret = platform_device_add_resources(pd, usb_resources, - ARRAY_SIZE(usb_resources)); - if (ret) - goto fail; + pd = of_find_device_by_node(ehci_node); + if (!pd) + return 0; pd->dev.dma_mask = &octeon_ehci_dma_mask; pd->dev.platform_data = &octeon_ehci_pdata; - octeon_ehci_hw_start(); - - ret = platform_device_add(pd); - if (ret) - goto fail; + octeon_ehci_hw_start(&pd->dev); - return ret; -fail: - platform_device_put(pd); -out: return ret; } device_initcall(octeon_ehci_device_init); static int octeon_ohci_power_on(struct platform_device *pdev) { - octeon2_usb_clocks_start(); + octeon2_usb_clocks_start(&pdev->dev); return 0; } @@ -369,11 +375,11 @@ static struct usb_ohci_pdata octeon_ohci_pdata = { .power_off = octeon_ohci_power_off, }; -static void __init octeon_ohci_hw_start(void) +static void __init octeon_ohci_hw_start(struct device *dev) { union cvmx_uctlx_ohci_ctl ohci_ctl; - octeon2_usb_clocks_start(); + octeon2_usb_clocks_start(dev); ohci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_OHCI_CTL(0)); ohci_ctl.s.l2c_addr_msb = 0; @@ -387,57 +393,27 @@ static void __init octeon_ohci_hw_start(void) static int __init octeon_ohci_device_init(void) { struct platform_device *pd; + struct device_node *ohci_node; int ret = 0; - struct resource usb_resources[] = { - { - .flags = IORESOURCE_MEM, - }, { - .flags = IORESOURCE_IRQ, - } - }; - - /* Only Octeon2 has ehci/ohci */ - if (!OCTEON_IS_MODEL(OCTEON_CN63XX)) + ohci_node = of_find_node_by_name(NULL, "ohci"); + if (!ohci_node) return 0; - if (octeon_is_simulation() || usb_disabled()) - return 0; /* No USB in the simulator. */ - - pd = platform_device_alloc("ohci-platform", 0); - if (!pd) { - ret = -ENOMEM; - goto out; - } - - usb_resources[0].start = 0x00016F0000000400ULL; - usb_resources[0].end = usb_resources[0].start + 0x100; - - usb_resources[1].start = OCTEON_IRQ_USB0; - usb_resources[1].end = OCTEON_IRQ_USB0; - - ret = platform_device_add_resources(pd, usb_resources, - ARRAY_SIZE(usb_resources)); - if (ret) - goto fail; + pd = of_find_device_by_node(ohci_node); + if (!pd) + return 0; pd->dev.platform_data = &octeon_ohci_pdata; - octeon_ohci_hw_start(); - - ret = platform_device_add(pd); - if (ret) - goto fail; + octeon_ohci_hw_start(&pd->dev); - return ret; -fail: - platform_device_put(pd); -out: return ret; } device_initcall(octeon_ohci_device_init); #endif /* CONFIG_USB */ + static struct of_device_id __initdata octeon_ids[] = { { .compatible = "simple-bus", }, { .compatible = "cavium,octeon-6335-uctl", }, diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index db5c29edf6db..28aae64a8bee 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -353,6 +353,7 @@ static const struct of_device_id vt8500_ehci_ids[] = { { .compatible = "via,vt8500-ehci", }, { .compatible = "wm,prizm-ehci", }, { .compatible = "generic-ehci", }, + { .compatible = "cavium,octeon-6335-ehci", }, {} }; MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index b81d202b15a2..9c06b0197eb9 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -328,6 +328,7 @@ static int ohci_platform_resume(struct device *dev) static const struct of_device_id ohci_platform_ids[] = { { .compatible = "generic-ohci", }, + { .compatible = "cavium,octeon-6335-ohci", }, { } }; MODULE_DEVICE_TABLE(of, ohci_platform_ids); -- cgit v1.2.3-59-g8ed1b From c99e76c55f68eaa0c307ba25803c4e59c2fca1ca Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Mon, 12 Jan 2015 16:05:52 +0100 Subject: USB: host: Introduce flag to enable use of 64-bit dma_mask for ehci-platform ehci-octeon driver used a 64-bit dma_mask. With removal of ehci-octeon and usage of ehci-platform ehci dma_mask is now limited to 32 bits (coerced in ehci_platform_probe). Provide a flag in ehci platform data to allow use of 64 bits for dma_mask. Cc: David Daney Cc: Alex Smith Signed-off-by: Andreas Herrmann Tested-by: Aaro Koskinen Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- arch/mips/cavium-octeon/octeon-platform.c | 4 +--- drivers/usb/host/ehci-platform.c | 3 ++- include/linux/usb/ehci_pdriver.h | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb/host/ehci-platform.c') diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c index eea60b6c927b..12410a2788d8 100644 --- a/arch/mips/cavium-octeon/octeon-platform.c +++ b/arch/mips/cavium-octeon/octeon-platform.c @@ -310,6 +310,7 @@ static struct usb_ehci_pdata octeon_ehci_pdata = { #ifdef __BIG_ENDIAN .big_endian_mmio = 1, #endif + .dma_mask_64 = 1, .power_on = octeon_ehci_power_on, .power_off = octeon_ehci_power_off, }; @@ -331,8 +332,6 @@ static void __init octeon_ehci_hw_start(struct device *dev) octeon2_usb_clocks_stop(); } -static u64 octeon_ehci_dma_mask = DMA_BIT_MASK(64); - static int __init octeon_ehci_device_init(void) { struct platform_device *pd; @@ -347,7 +346,6 @@ static int __init octeon_ehci_device_init(void) if (!pd) return 0; - pd->dev.dma_mask = &octeon_ehci_dma_mask; pd->dev.platform_data = &octeon_ehci_pdata; octeon_ehci_hw_start(&pd->dev); diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 28aae64a8bee..63f2622926c4 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -155,7 +155,8 @@ static int ehci_platform_probe(struct platform_device *dev) if (!pdata) pdata = &ehci_platform_defaults; - err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); + err = dma_coerce_mask_and_coherent(&dev->dev, + pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); if (err) return err; diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h index 6287b398abd9..db0431b39a63 100644 --- a/include/linux/usb/ehci_pdriver.h +++ b/include/linux/usb/ehci_pdriver.h @@ -48,6 +48,7 @@ struct usb_ehci_pdata { unsigned big_endian_mmio:1; unsigned no_io_watchdog:1; unsigned reset_on_resume:1; + unsigned dma_mask_64:1; /* Turn on all power and clocks */ int (*power_on)(struct platform_device *pdev); -- cgit v1.2.3-59-g8ed1b From 7e7a0e67f2c2104fb7515008fb2ba72ffb10b493 Mon Sep 17 00:00:00 2001 From: Arun Ramamurthy Date: Mon, 19 Jan 2015 16:05:29 -0800 Subject: usb: ehci-platform: add support for multiple phys per controller Added support for cases where one controller is connected to multiple phys. Signed-off-by: Arun Ramamurthy Reviewed-by: Ray Jui Reviewed-by: Scott Branden Tested-by: Scott Branden Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 82 +++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 22 deletions(-) (limited to 'drivers/usb/host/ehci-platform.c') diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 63f2622926c4..d8a75a51d6d4 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -43,7 +43,8 @@ struct ehci_platform_priv { struct clk *clks[EHCI_MAX_CLKS]; struct reset_control *rst; - struct phy *phy; + struct phy **phys; + int num_phys; }; static const char hcd_name[] = "ehci-platform"; @@ -78,7 +79,7 @@ static int ehci_platform_power_on(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); - int clk, ret; + int clk, ret, phy_num; for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { ret = clk_prepare_enable(priv->clks[clk]); @@ -86,20 +87,28 @@ static int ehci_platform_power_on(struct platform_device *dev) goto err_disable_clks; } - if (priv->phy) { - ret = phy_init(priv->phy); - if (ret) - goto err_disable_clks; - - ret = phy_power_on(priv->phy); - if (ret) - goto err_exit_phy; + for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { + if (priv->phys[phy_num]) { + ret = phy_init(priv->phys[phy_num]); + if (ret) + goto err_exit_phy; + ret = phy_power_on(priv->phys[phy_num]); + if (ret) { + phy_exit(priv->phys[phy_num]); + goto err_exit_phy; + } + } } return 0; err_exit_phy: - phy_exit(priv->phy); + while (--phy_num >= 0) { + if (priv->phys[phy_num]) { + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); + } + } err_disable_clks: while (--clk >= 0) clk_disable_unprepare(priv->clks[clk]); @@ -111,11 +120,13 @@ static void ehci_platform_power_off(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); - int clk; + int clk, phy_num; - if (priv->phy) { - phy_power_off(priv->phy); - phy_exit(priv->phy); + for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { + if (priv->phys[phy_num]) { + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); + } } for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) @@ -143,7 +154,8 @@ static int ehci_platform_probe(struct platform_device *dev) struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); struct ehci_platform_priv *priv; struct ehci_hcd *ehci; - int err, irq, clk = 0; + const char *phy_name; + int err, irq, phy_num, clk = 0; if (usb_disabled()) return -ENODEV; @@ -190,12 +202,38 @@ static int ehci_platform_probe(struct platform_device *dev) "needs-reset-on-resume")) pdata->reset_on_resume = 1; - priv->phy = devm_phy_get(&dev->dev, "usb"); - if (IS_ERR(priv->phy)) { - err = PTR_ERR(priv->phy); - if (err == -EPROBE_DEFER) - goto err_put_hcd; - priv->phy = NULL; + priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, + "phys", "#phy-cells"); + priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1; + + priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, + sizeof(struct phy *), GFP_KERNEL); + if (!priv->phys) + return -ENOMEM; + + for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { + err = of_property_read_string_index( + dev->dev.of_node, + "phy-names", phy_num, + &phy_name); + + if (err < 0) { + if (priv->num_phys > 1) { + dev_err(&dev->dev, "phy-names not provided"); + goto err_put_hcd; + } else + phy_name = "usb"; + } + + priv->phys[phy_num] = devm_phy_get(&dev->dev, + phy_name); + if (IS_ERR(priv->phys[phy_num])) { + err = PTR_ERR(priv->phys[phy_num]); + if ((priv->num_phys > 1) || + (err == -EPROBE_DEFER)) + goto err_put_hcd; + priv->phys[phy_num] = NULL; + } } for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { -- cgit v1.2.3-59-g8ed1b