aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Shields <simon@lineageos.org>2018-03-22 18:31:24 +1100
committerDenis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>2020-01-20 02:58:42 +0100
commitb67f14d7fb53652ae3ac472af49a54e0ce3ddb15 (patch)
tree93b40f1746b49f6d0792c9db2fe7ceca3c96ed41
parent4fa647fd602d4a9774d67f226e18a74c2e0b7a83 (diff)
downloadkernel_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.c76
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);