USB: add USB EHCI support for MPC5121 SoC
authorAnatolij Gustschin <agust@denx.de>
Tue, 28 Sep 2010 18:55:21 +0000 (20:55 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 22 Oct 2010 17:21:58 +0000 (10:21 -0700)
Extends FSL EHCI platform driver glue layer to support
MPC5121 USB controllers. MPC5121 Rev 2.0 silicon EHCI
registers are in big endian format. The appropriate flags
are set using the information in the platform data structure.
MPC83xx system interface registers are not available on
MPC512x, so the access to these registers is isolated in
MPC512x case. Furthermore the USB controller clocks
must be enabled before 512x register accesses which is
done by providing platform specific init callback.

The MPC512x internal USB PHY doesn't provide supply voltage.
For boards using different power switches allow specifying
DRVVBUS and PWR_FAULT signal polarity of the MPC5121 internal
PHY using "fsl,invert-drvvbus" and "fsl,invert-pwr-fault"
properties in the device tree USB nodes. Adds documentation
for this new device tree bindings.

Signed-off-by: Anatolij Gustschin <agust@denx.de>
Cc: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Documentation/powerpc/dts-bindings/fsl/usb.txt
drivers/usb/Kconfig
drivers/usb/host/Kconfig
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-fsl.h
drivers/usb/host/ehci-mem.c
drivers/usb/host/fsl-mph-dr-of.c
include/linux/fsl_devices.h

index b001524026943ce0e0cea217767da69d0c6d60c9..bd5723f0b67ecd9f953019fbd2af3c850bbb0433 100644 (file)
@@ -8,6 +8,7 @@ and additions :
 Required properties :
  - compatible : Should be "fsl-usb2-mph" for multi port host USB
    controllers, or "fsl-usb2-dr" for dual role USB controllers
+   or "fsl,mpc5121-usb2-dr" for dual role USB controllers of MPC5121
  - phy_type : For multi port host USB controllers, should be one of
    "ulpi", or "serial". For dual role USB controllers, should be
    one of "ulpi", "utmi", "utmi_wide", or "serial".
@@ -33,6 +34,12 @@ Recommended properties :
  - interrupt-parent : the phandle for the interrupt controller that
    services interrupts for this device.
 
+Optional properties :
+ - fsl,invert-drvvbus : boolean; for MPC5121 USB0 only. Indicates the
+   port power polarity of internal PHY signal DRVVBUS is inverted.
+ - fsl,invert-pwr-fault : boolean; for MPC5121 USB0 only. Indicates
+   the PWR_FAULT signal polarity is inverted.
+
 Example multi port host USB controller device node :
        usb@22000 {
                compatible = "fsl-usb2-mph";
@@ -57,3 +64,18 @@ Example dual role USB controller device node :
                dr_mode = "otg";
                phy = "ulpi";
        };
+
+Example dual role USB controller device node for MPC5121ADS:
+
+       usb@4000 {
+               compatible = "fsl,mpc5121-usb2-dr";
+               reg = <0x4000 0x1000>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+               interrupt-parent = < &ipic >;
+               interrupts = <44 0x8>;
+               dr_mode = "otg";
+               phy_type = "utmi_wide";
+               fsl,invert-drvvbus;
+               fsl,invert-pwr-fault;
+       };
index 4aa00e6e57adea87bf52cb2eca4e37b71ba9828e..67eb3770868fbbb09b94ab26c6f0fc1a96ed13c0 100644 (file)
@@ -59,6 +59,7 @@ config USB_ARCH_HAS_OHCI
 config USB_ARCH_HAS_EHCI
        boolean
        default y if PPC_83xx
+       default y if PPC_MPC512x
        default y if SOC_AU1200
        default y if ARCH_IXP4XX
        default y if ARCH_W90X900
index f3a90b0fc422d5928fc11dfaeca64c1e09b464d3..bf2e7d234533ecec3f0697b359b2f2a8f8277fa7 100644 (file)
@@ -93,12 +93,14 @@ config USB_EHCI_TT_NEWSCHED
 
 config USB_EHCI_BIG_ENDIAN_MMIO
        bool
-       depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX)
+       depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || \
+                                   XPS_USB_HCD_XILINX || PPC_MPC512x)
        default y
 
 config USB_EHCI_BIG_ENDIAN_DESC
        bool
-       depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX)
+       depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \
+                                   PPC_MPC512x)
        default y
 
 config XPS_USB_HCD_XILINX
index 8600317bd60b6619065d175ec7eeaf9d58ada47c..86e42892016d29564dd89bef520138cadd3263ce 100644 (file)
@@ -116,13 +116,33 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
                goto err3;
        }
 
-       /* Enable USB controller */
-       temp = in_be32(hcd->regs + 0x500);
-       out_be32(hcd->regs + 0x500, temp | 0x4);
+       pdata->regs = hcd->regs;
 
-       /* Set to Host mode */
-       temp = in_le32(hcd->regs + 0x1a8);
-       out_le32(hcd->regs + 0x1a8, temp | 0x3);
+       /*
+        * do platform specific init: check the clock, grab/config pins, etc.
+        */
+       if (pdata->init && pdata->init(pdev)) {
+               retval = -ENODEV;
+               goto err3;
+       }
+
+       /*
+        * Check if it is MPC5121 SoC, otherwise set pdata->have_sysif_regs
+        * flag for 83xx or 8536 system interface registers.
+        */
+       if (pdata->big_endian_mmio)
+               temp = in_be32(hcd->regs + FSL_SOC_USB_ID);
+       else
+               temp = in_le32(hcd->regs + FSL_SOC_USB_ID);
+
+       if ((temp & ID_MSK) != (~((temp & NID_MSK) >> 8) & ID_MSK))
+               pdata->have_sysif_regs = 1;
+
+       /* Enable USB controller, 83xx or 8536 */
+       if (pdata->have_sysif_regs)
+               setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4);
+
+       /* Don't need to set host mode here. It will be done by tdi_reset() */
 
        retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
        if (retval != 0)
@@ -137,6 +157,8 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
        usb_put_hcd(hcd);
       err1:
        dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
+       if (pdata->exit)
+               pdata->exit(pdev);
        return retval;
 }
 
@@ -154,17 +176,30 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
 static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
                               struct platform_device *pdev)
 {
+       struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
        usb_remove_hcd(hcd);
+
+       /*
+        * do platform specific un-initialization:
+        * release iomux pins, disable clock, etc.
+        */
+       if (pdata->exit)
+               pdata->exit(pdev);
        iounmap(hcd->regs);
        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
        usb_put_hcd(hcd);
 }
 
-static void mpc83xx_setup_phy(struct ehci_hcd *ehci,
-                             enum fsl_usb2_phy_modes phy_mode,
-                             unsigned int port_offset)
+static void ehci_fsl_setup_phy(struct ehci_hcd *ehci,
+                              enum fsl_usb2_phy_modes phy_mode,
+                              unsigned int port_offset)
 {
-       u32 portsc = 0;
+       u32 portsc;
+
+       portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]);
+       portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);
+
        switch (phy_mode) {
        case FSL_USB2_PHY_ULPI:
                portsc |= PORT_PTS_ULPI;
@@ -184,20 +219,21 @@ static void mpc83xx_setup_phy(struct ehci_hcd *ehci,
        ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]);
 }
 
-static void mpc83xx_usb_setup(struct usb_hcd *hcd)
+static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
 {
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       struct usb_hcd *hcd = ehci_to_hcd(ehci);
        struct fsl_usb2_platform_data *pdata;
        void __iomem *non_ehci = hcd->regs;
        u32 temp;
 
-       pdata =
-           (struct fsl_usb2_platform_data *)hcd->self.controller->
-           platform_data;
+       pdata = hcd->self.controller->platform_data;
+
        /* Enable PHY interface in the control reg. */
-       temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
-       out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
-       out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
+       if (pdata->have_sysif_regs) {
+               temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
+               out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
+               out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
+       }
 
 #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
        /*
@@ -214,7 +250,7 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
 
        if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
                        (pdata->operating_mode == FSL_USB2_DR_OTG))
-               mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
+               ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0);
 
        if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
                unsigned int chip, rev, svr;
@@ -228,25 +264,27 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
                        ehci->has_fsl_port_bug = 1;
 
                if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
-                       mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
+                       ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0);
                if (pdata->port_enables & FSL_USB2_PORT1_ENABLED)
-                       mpc83xx_setup_phy(ehci, pdata->phy_mode, 1);
+                       ehci_fsl_setup_phy(ehci, pdata->phy_mode, 1);
        }
 
+       if (pdata->have_sysif_regs) {
 #ifdef CONFIG_PPC_85xx
-       out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
-       out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
+               out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
+               out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
 #else
-       out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
-       out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
+               out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
+               out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
 #endif
-       out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
+               out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
+       }
 }
 
 /* called after powerup, by probe or system-pm "wakeup" */
 static int ehci_fsl_reinit(struct ehci_hcd *ehci)
 {
-       mpc83xx_usb_setup(ehci_to_hcd(ehci));
+       ehci_fsl_usb_setup(ehci);
        ehci_port_power(ehci, 0);
 
        return 0;
@@ -257,6 +295,11 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
 {
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        int retval;
+       struct fsl_usb2_platform_data *pdata;
+
+       pdata = hcd->self.controller->platform_data;
+       ehci->big_endian_desc = pdata->big_endian_desc;
+       ehci->big_endian_mmio = pdata->big_endian_mmio;
 
        /* EHCI registers start at offset 0x100 */
        ehci->caps = hcd->regs + 0x100;
@@ -370,7 +413,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
         * generic hardware linkage
         */
        .irq = ehci_irq,
-       .flags = HCD_USB2,
+       .flags = HCD_USB2 | HCD_MEMORY,
 
        /*
         * basic lifecycle operations
index eb537aa546106664ea4f99535f8581409bc18a8d..2c8353795226b7f87a3c957c5bc21f6224c477b3 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2005 freescale semiconductor
+/* Copyright (C) 2005-2010 Freescale Semiconductor, Inc.
  * Copyright (c) 2005 MontaVista Software
  *
  * This program is free software; you can redistribute  it and/or modify it
@@ -19,6 +19,9 @@
 #define _EHCI_FSL_H
 
 /* offsets for the non-ehci registers in the FSL SOC USB controller */
+#define FSL_SOC_USB_ID         0x0
+#define ID_MSK                 0x3f
+#define NID_MSK                        0x3f00
 #define FSL_SOC_USB_ULPIVP     0x170
 #define FSL_SOC_USB_PORTSC1    0x184
 #define PORT_PTS_MSK           (3<<30)
 #define        PORT_PTS_SERIAL         (3<<30)
 #define PORT_PTS_PTW           (1<<28)
 #define FSL_SOC_USB_PORTSC2    0x188
+
+#define FSL_SOC_USB_USBGENCTRL 0x200
+#define USBGENCTRL_PPP         (1 << 3)
+#define USBGENCTRL_PFP         (1 << 2)
+#define FSL_SOC_USB_ISIPHYCTRL 0x204
+#define ISIPHYCTRL_PXE         (1)
+#define ISIPHYCTRL_PHYE                (1 << 4)
+
 #define FSL_SOC_USB_SNOOP1     0x400   /* NOTE: big-endian */
 #define FSL_SOC_USB_SNOOP2     0x404   /* NOTE: big-endian */
 #define FSL_SOC_USB_AGECNTTHRSH        0x408   /* NOTE: big-endian */
index 1f3f01eacaf09cecb88af40187910465cb49b0f3..d36e4e75e08d4e476249a71259d8d760d5158a7b 100644 (file)
@@ -40,7 +40,7 @@ static inline void ehci_qtd_init(struct ehci_hcd *ehci, struct ehci_qtd *qtd,
 {
        memset (qtd, 0, sizeof *qtd);
        qtd->qtd_dma = dma;
-       qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
+       qtd->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
        qtd->hw_next = EHCI_LIST_END(ehci);
        qtd->hw_alt_next = EHCI_LIST_END(ehci);
        INIT_LIST_HEAD (&qtd->qtd_list);
index 12db5d5cb0bcdde0189c4ff978fb294728f39e25..574b99ea07009a2467daa902d997a58c1b8657e9 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/of_platform.h>
+#include <linux/clk.h>
 
 struct fsl_usb2_dev_data {
        char *dr_mode;          /* controller mode */
@@ -153,6 +154,12 @@ static int __devinit fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
 
                pdata->operating_mode = FSL_USB2_MPH_HOST;
        } else {
+               if (of_get_property(np, "fsl,invert-drvvbus", NULL))
+                       pdata->invert_drvvbus = 1;
+
+               if (of_get_property(np, "fsl,invert-pwr-fault", NULL))
+                       pdata->invert_pwr_fault = 1;
+
                /* setup mode selected in the device tree */
                pdata->operating_mode = dev_data->op_mode;
        }
@@ -186,9 +193,91 @@ static int __devexit fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev)
        return 0;
 }
 
+#ifdef CONFIG_PPC_MPC512x
+
+#define USBGENCTRL             0x200           /* NOTE: big endian */
+#define GC_WU_INT_CLR          (1 << 5)        /* Wakeup int clear */
+#define GC_ULPI_SEL            (1 << 4)        /* ULPI i/f select (usb0 only)*/
+#define GC_PPP                 (1 << 3)        /* Inv. Port Power Polarity */
+#define GC_PFP                 (1 << 2)        /* Inv. Power Fault Polarity */
+#define GC_WU_ULPI_EN          (1 << 1)        /* Wakeup on ULPI event */
+#define GC_WU_IE               (1 << 1)        /* Wakeup interrupt enable */
+
+#define ISIPHYCTRL             0x204           /* NOTE: big endian */
+#define PHYCTRL_PHYE           (1 << 4)        /* On-chip UTMI PHY enable */
+#define PHYCTRL_BSENH          (1 << 3)        /* Bit Stuff Enable High */
+#define PHYCTRL_BSEN           (1 << 2)        /* Bit Stuff Enable */
+#define PHYCTRL_LSFE           (1 << 1)        /* Line State Filter Enable */
+#define PHYCTRL_PXE            (1 << 0)        /* PHY oscillator enable */
+
+int fsl_usb2_mpc5121_init(struct platform_device *pdev)
+{
+       struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+       struct clk *clk;
+       char clk_name[10];
+       int base, clk_num;
+
+       base = pdev->resource->start & 0xf000;
+       if (base == 0x3000)
+               clk_num = 1;
+       else if (base == 0x4000)
+               clk_num = 2;
+       else
+               return -ENODEV;
+
+       snprintf(clk_name, sizeof(clk_name), "usb%d_clk", clk_num);
+       clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "failed to get clk\n");
+               return PTR_ERR(clk);
+       }
+
+       clk_enable(clk);
+       pdata->clk = clk;
+
+       if (pdata->phy_mode == FSL_USB2_PHY_UTMI_WIDE) {
+               u32 reg = 0;
+
+               if (pdata->invert_drvvbus)
+                       reg |= GC_PPP;
+
+               if (pdata->invert_pwr_fault)
+                       reg |= GC_PFP;
+
+               out_be32(pdata->regs + ISIPHYCTRL, PHYCTRL_PHYE | PHYCTRL_PXE);
+               out_be32(pdata->regs + USBGENCTRL, reg);
+       }
+       return 0;
+}
+
+static void fsl_usb2_mpc5121_exit(struct platform_device *pdev)
+{
+       struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+       pdata->regs = NULL;
+
+       if (pdata->clk) {
+               clk_disable(pdata->clk);
+               clk_put(pdata->clk);
+       }
+}
+
+struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = {
+       .big_endian_desc = 1,
+       .big_endian_mmio = 1,
+       .es = 1,
+       .le_setup_buf = 1,
+       .init = fsl_usb2_mpc5121_init,
+       .exit = fsl_usb2_mpc5121_exit,
+};
+#endif /* CONFIG_PPC_MPC512x */
+
 static const struct of_device_id fsl_usb2_mph_dr_of_match[] = {
        { .compatible = "fsl-usb2-mph", },
        { .compatible = "fsl-usb2-dr", },
+#ifdef CONFIG_PPC_MPC512x
+       { .compatible = "fsl,mpc5121-usb2-dr", .data = &fsl_usb2_mpc5121_pd, },
+#endif
        {},
 };
 
index 28e33fea510736f179ccfdb985e0d630b0aae616..d5f9a7431bd0516da252d0ca316fde7a63cbbf9e 100644 (file)
@@ -58,11 +58,26 @@ enum fsl_usb2_phy_modes {
        FSL_USB2_PHY_SERIAL,
 };
 
+struct clk;
+struct platform_device;
+
 struct fsl_usb2_platform_data {
        /* board specific information */
        enum fsl_usb2_operating_modes   operating_mode;
        enum fsl_usb2_phy_modes         phy_mode;
        unsigned int                    port_enables;
+
+       int             (*init)(struct platform_device *);
+       void            (*exit)(struct platform_device *);
+       void __iomem    *regs;          /* ioremap'd register base */
+       struct clk      *clk;
+       unsigned        big_endian_mmio:1;
+       unsigned        big_endian_desc:1;
+       unsigned        es:1;           /* need USBMODE:ES */
+       unsigned        le_setup_buf:1;
+       unsigned        have_sysif_regs:1;
+       unsigned        invert_drvvbus:1;
+       unsigned        invert_pwr_fault:1;
 };
 
 /* Flags in fsl_usb2_mph_platform_data */