From b67f14d7fb53652ae3ac472af49a54e0ce3ddb15 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Thu, 22 Mar 2018 18:31:24 +1100 Subject: HACK: usb: host: ehci-exynos: add ehci_power sysfs node The XMM6262 modem's boot process is roughly: 1. power on modem in bootloader mode 2. load firmware 3. hard reset modem 4. real modem OS comes up The problem is that (1) and (4) use different USB addresses, and the HSIC bus doesn't support hotplugging, so the only way to force a proper re-enumeration of the bus seems to be to power off the EHCI controller and power it back on. Signed-off-by: Simon Shields GNUtoo@cyberdimension.org: rebase, small fixes Signed-off-by: Denis 'GNUtoo' Carikli --- drivers/usb/host/ehci-exynos.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index ea3bffebeb61..2f3886ded442 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -21,6 +21,8 @@ #include "ehci.h" +#define UGLY_HACK + #define DRIVER_DESC "EHCI EXYNOS driver" #define EHCI_INSNREG00(base) (base + 0x90) @@ -44,6 +46,9 @@ struct exynos_ehci_hcd { struct device_node *of_node; struct phy *phy[PHY_NUMBER]; bool legacy_phy; +#ifdef UGLY_HACK + bool power_on; +#endif }; #define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv) @@ -132,6 +137,69 @@ static void exynos_ehci_phy_disable(struct device *dev) phy_power_off(exynos_ehci->phy[i]); } +#ifdef UGLY_HACK +static ssize_t show_ehci_power(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd); + + return snprintf(buf, PAGE_SIZE, "EHCI Power %s\n", exynos_ehci->power_on ? "on" : "off"); +} + +static ssize_t store_ehci_power(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd); + + int power_on; + int irq; + int ret = 0; + + if (sscanf(buf, "%d", &power_on) != 1) + return -EINVAL; + + device_lock(dev); + if (!power_on && exynos_ehci->power_on) { + dev_info(dev, "Powering off EHCI\n"); + exynos_ehci->power_on = false; + usb_remove_hcd(hcd); + exynos_ehci_phy_disable(dev); + } else if (power_on) { + dev_info(dev, "Powering on EHCI\n"); + if (exynos_ehci->power_on) + usb_remove_hcd(hcd); + + exynos_ehci_phy_enable(dev); + + writel(EHCI_INSNREG00_ENABLE_DMA_BURST | EHCI_INSNREG00_OHCI_SUSP_LEGACY, EHCI_INSNREG00(hcd->regs)); + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(dev, "IRQ get failed!\n"); + goto err; + } + + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (ret < 0) { + dev_err(dev, "Power on failed!\n"); + goto err; + } + exynos_ehci->power_on = true; + } +err: + device_unlock(dev); + return count; +} + +static DEVICE_ATTR(ehci_power, 0664, show_ehci_power, store_ehci_power); +#endif + static void exynos_setup_vbus_gpio(struct device *dev) { int err; @@ -240,6 +308,10 @@ static int exynos_ehci_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hcd); +#ifdef UGLY_HACK + device_create_file(hcd->self.controller, &dev_attr_ehci_power); + exynos_ehci->power_on = true; +#endif return 0; fail_add_hcd: @@ -259,6 +331,10 @@ static int exynos_ehci_remove(struct platform_device *pdev) pdev->dev.of_node = exynos_ehci->of_node; +#ifdef UGLY_HACK + exynos_ehci->power_on = false; // TODO: check if there are IRQ handlers left + device_remove_file(hcd->self.controller, &dev_attr_ehci_power); +#endif usb_remove_hcd(hcd); exynos_ehci_phy_disable(&pdev->dev); -- cgit v1.2.3