summaryrefslogtreecommitdiff
path: root/drivers/usb/host/ohci-s3c2410.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/ohci-s3c2410.c')
-rw-r--r--drivers/usb/host/ohci-s3c2410.c103
1 files changed, 98 insertions, 5 deletions
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index f46af7a718d4..f35ea2184830 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -25,6 +25,11 @@
#include <mach/hardware.h>
#include <mach/usb-control.h>
+/* For enabling the second root hub port of the S3C2443 */
+#if defined(CONFIG_S3C2443_USB_PHY_PORT)
+#include <mach/regs-s3c2443-clock.h>
+#endif
+
#define valid_port(idx) ((idx) == 1 || (idx) == 2)
/* clock device associated with the hcd */
@@ -32,6 +37,57 @@
static struct clk *clk;
static struct clk *usb_clk;
+
+/*
+ * If the second port is enabled, then only connect the USB PHY to the
+ * host controller path
+ */
+#if defined(CONFIG_S3C2443_USB_PHY_PORT)
+static void usb_hcd_s3c2443_enable_second_port(void)
+{
+ ulong regval;
+
+ /* PHY power enable */
+ regval = __raw_readl(S3C2443_PWRCFG);
+ regval |= S3C2443_PWRCFG_USBPHY_ON;
+ __raw_writel(regval, S3C2443_PWRCFG);
+
+ /*
+ * USB device 2.0 must reset like bellow,
+ * 1st phy reset and after at least 10us, func_reset & host reset
+ * phy reset can reset bellow registers.
+ */
+ regval = S3C2443_URSTCON_PHY;
+ __raw_writel(regval, S3C2443_URSTCON);
+ udelay(20);
+ __raw_writel(0x00, S3C2443_URSTCON);
+
+ /* Function reset, but DONT TOUCH THE HOST! */
+ regval = S3C2443_URSTCON_FUNC;
+ __raw_writel(regval, S3C2443_URSTCON);
+ __raw_writel(0x00, S3C2443_URSTCON);
+
+ /* 48Mhz, Oscillator, External X-tal, device */
+ regval = S3C2443_PHYCTRL_EXTCLK_OSCI | S3C2443_PHYCTRL_DOWN_DEV;
+ __raw_writel(regval, S3C2443_PHYCTRL);
+}
+static inline void usb_hcd_s3c2443_disable_second_port(void)
+{
+ ulong regval;
+
+ /* Reset the USB-function and the PHY */
+ writel(S3C2443_URSTCON_PHY, S3C2443_URSTCON);
+
+ /* PHY power disable */
+ regval = readl(S3C2443_PWRCFG);
+ regval &= ~S3C2443_PWRCFG_USBPHY_ON;
+ writel(regval, S3C2443_PWRCFG);
+}
+#else
+static inline void usb_hcd_s3c2443_enable_second_port(void) { }
+static inline void usb_hcd_s3c2443_disable_second_port(void) { }
+#endif
+
/* forward definitions */
static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc);
@@ -136,7 +192,7 @@ static void s3c2410_usb_set_power(struct s3c2410_hcd_info *info,
if (info->power_control != NULL) {
info->port[port-1].power = to;
- (info->power_control)(port-1, to);
+ (info->power_control)(info, port-1, to);
}
}
@@ -285,6 +341,8 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)
unsigned long flags;
int portno;
+ //printk(KERN_ERR "s3c2410-ohci: Over current (%i)\n", port_oc);
+
if (info == NULL)
return;
@@ -301,8 +359,9 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)
/* ok, once over-current is detected,
the port needs to be powered down */
- s3c2410_usb_set_power(info, portno+1, 0);
- }
+ s3c2410_usb_set_power(info, portno + 1, 0);
+ } else
+ s3c2410_usb_set_power(info, portno + 1, 1);
}
local_irq_restore(flags);
@@ -350,6 +409,9 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,
s3c2410_usb_set_power(dev->dev.platform_data, 1, 1);
s3c2410_usb_set_power(dev->dev.platform_data, 2, 1);
+ /* Enable the second port for the S3C2443 */
+ usb_hcd_s3c2443_enable_second_port();
+
hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");
if (hcd == NULL)
return -ENOMEM;
@@ -473,6 +535,37 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
.start_port_reset = ohci_start_port_reset,
};
+#if defined(CONFIG_PM)
+static int ohci_hcd_s3c2410_drv_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ /* Disable the clocks before entering the suspend mode */
+ clk_disable(clk);
+ clk_disable(usb_clk);
+
+ usb_hcd_s3c2443_disable_second_port();
+
+ return 0;
+}
+
+static int ohci_hcd_s3c2410_drv_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+
+ usb_hcd_s3c2443_enable_second_port();
+
+ /* Restart the clocks */
+ clk_enable(clk);
+ clk_enable(usb_clk);
+
+ hcd = platform_get_drvdata(pdev);
+ ohci_finish_controller_resume(hcd);
+ return 0;
+}
+#else
+#define ohci_hcd_s3c2410_drv_suspend NULL
+#define ohci_hcd_s3c2410_drv_resume NULL
+#endif /* defined(CONFIG_PM) */
+
/* device driver */
static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev)
@@ -492,8 +585,8 @@ static struct platform_driver ohci_hcd_s3c2410_driver = {
.probe = ohci_hcd_s3c2410_drv_probe,
.remove = ohci_hcd_s3c2410_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
- /*.suspend = ohci_hcd_s3c2410_drv_suspend, */
- /*.resume = ohci_hcd_s3c2410_drv_resume, */
+ .suspend = ohci_hcd_s3c2410_drv_suspend,
+ .resume = ohci_hcd_s3c2410_drv_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-ohci",