diff options
author | Simon Shields <simon@lineageos.org> | 2018-03-22 18:31:24 +1100 |
---|---|---|
committer | Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> | 2020-01-20 02:58:42 +0100 |
commit | b67f14d7fb53652ae3ac472af49a54e0ce3ddb15 (patch) | |
tree | 93b40f1746b49f6d0792c9db2fe7ceca3c96ed41 | |
parent | 4fa647fd602d4a9774d67f226e18a74c2e0b7a83 (diff) | |
download | kernel_replicant_linux-b67f14d7fb53652ae3ac472af49a54e0ce3ddb15.tar.gz kernel_replicant_linux-b67f14d7fb53652ae3ac472af49a54e0ce3ddb15.tar.bz2 kernel_replicant_linux-b67f14d7fb53652ae3ac472af49a54e0ce3ddb15.zip |
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 <simon@lineageos.org>
GNUtoo@cyberdimension.org: rebase, small fixes
Signed-off-by: Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
-rw-r--r-- | drivers/usb/host/ehci-exynos.c | 76 |
1 files changed, 76 insertions, 0 deletions
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); |