On Thu, Jul 22, 2010 at 10:25 AM, Anatolij Gustschin <agust@xxxxxxx> wrote: > 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@xxxxxxx> > Cc: Grant Likely <grant.likely@xxxxxxxxxxxx> On brief review, looks pretty good to me. g. > --- > Documentation/powerpc/dts-bindings/fsl/usb.txt | 22 +++++ > drivers/usb/Kconfig | 1 + > drivers/usb/host/Kconfig | 6 +- > drivers/usb/host/ehci-fsl.c | 107 +++++++++++++++++------ > drivers/usb/host/ehci-fsl.h | 19 ++++- > drivers/usb/host/ehci-mem.c | 2 +- > drivers/usb/host/fsl-mph-dr-of.c | 89 ++++++++++++++++++++ > include/linux/fsl_devices.h | 15 ++++ > 8 files changed, 229 insertions(+), 32 deletions(-) > > diff --git a/Documentation/powerpc/dts-bindings/fsl/usb.txt b/Documentation/powerpc/dts-bindings/fsl/usb.txt > index b001524..bd5723f 100644 > --- a/Documentation/powerpc/dts-bindings/fsl/usb.txt > +++ b/Documentation/powerpc/dts-bindings/fsl/usb.txt > @@ -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; > + }; > diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig > index 6a58cb1..6e547b5 100644 > --- a/drivers/usb/Kconfig > +++ b/drivers/usb/Kconfig > @@ -58,6 +58,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 > diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig > index 6687523..90e50f0 100644 > --- a/drivers/usb/host/Kconfig > +++ b/drivers/usb/host/Kconfig > @@ -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 > diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c > index a416421..ff50f5c 100644 > --- a/drivers/usb/host/ehci-fsl.c > +++ b/drivers/usb/host/ehci-fsl.c > @@ -116,13 +116,39 @@ 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; > + > + /* > + * 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); > > /* Set to Host mode */ > - temp = in_le32(hcd->regs + 0x1a8); > - out_le32(hcd->regs + 0x1a8, temp | 0x3); > + if (pdata->big_endian_mmio) { > + setbits32(hcd->regs + FSL_SOC_USB_USBMODE, USBMODE_CM_HOST); > + } else { > + clrsetbits_le32(hcd->regs + FSL_SOC_USB_USBMODE, > + USBMODE_CM_MASK, USBMODE_CM_HOST); > + } > > retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); > if (retval != 0) > @@ -137,6 +163,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 +182,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 +225,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 +256,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,27 +270,31 @@ 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); > } > > /* put controller in host mode. */ > - ehci_writel(ehci, 0x00000003, non_ehci + FSL_SOC_USB_USBMODE); > + temp = USBMODE_CM_HOST | (pdata->es ? USBMODE_ES : 0); > + ehci_writel(ehci, temp, non_ehci + FSL_SOC_USB_USBMODE); > + > + 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; > @@ -259,6 +305,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; > @@ -372,7 +423,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 > diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h > index b5e59db..e56be7b 100644 > --- a/drivers/usb/host/ehci-fsl.h > +++ b/drivers/usb/host/ehci-fsl.h > @@ -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,11 @@ > #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_SBUSCFG 0x90 > +#define FSL_SOC_USB_BURSTSIZE 0x160 > #define FSL_SOC_USB_ULPIVP 0x170 > #define FSL_SOC_USB_PORTSC1 0x184 > #define PORT_PTS_MSK (3<<30) > @@ -26,8 +31,20 @@ > #define PORT_PTS_ULPI (2<<30) > #define PORT_PTS_SERIAL (3<<30) > #define PORT_PTS_PTW (1<<28) > +#define PORT_PTS_PHCD (1<<23) > #define FSL_SOC_USB_PORTSC2 0x188 > #define FSL_SOC_USB_USBMODE 0x1a8 > +#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */ > +#define USBMODE_CM_HOST (3 << 0) /* controller mode: host */ > +#define USBMODE_ES (1 << 2) /* (Big) Endian Select */ > + > +#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 */ > diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c > index 1f3f01e..d36e4e7 100644 > --- a/drivers/usb/host/ehci-mem.c > +++ b/drivers/usb/host/ehci-mem.c > @@ -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); > diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c > index 020a939..0ced9fc 100644 > --- a/drivers/usb/host/fsl-mph-dr-of.c > +++ b/drivers/usb/host/fsl-mph-dr-of.c > @@ -14,6 +14,7 @@ > #include <linux/fsl_devices.h> > #include <linux/io.h> > #include <linux/of_platform.h> > +#include <linux/clk.h> > > struct fsl_usb2_dev_data { > char *dr_mode; /* controller mode */ > @@ -146,6 +147,12 @@ static int __devinit fsl_usb2_mph_dr_of_probe(struct of_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; > } > @@ -167,9 +174,91 @@ static int __devinit fsl_usb2_mph_dr_of_probe(struct of_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 > {}, > }; > > diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h > index 28e33fe..d5f9a74 100644 > --- a/include/linux/fsl_devices.h > +++ b/include/linux/fsl_devices.h > @@ -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 */ > -- > 1.7.0.4 > > -- Grant Likely, B.Sc., P.Eng. Secret Lab Technologies Ltd. -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html