diff --git a/Documentation/powerpc/dts-bindings/fsl/usb.txt b/Documentation/powerpc/dts-bindings/fsl/usb.txt index b001524026943ce0e0cea217767da69d0c6d60c9..bd5723f0b67ecd9f953019fbd2af3c850bbb0433 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 4aa00e6e57adea87bf52cb2eca4e37b71ba9828e..67eb3770868fbbb09b94ab26c6f0fc1a96ed13c0 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -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 diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f3a90b0fc422d5928fc11dfaeca64c1e09b464d3..bf2e7d234533ecec3f0697b359b2f2a8f8277fa7 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 8600317bd60b6619065d175ec7eeaf9d58ada47c..86e42892016d29564dd89bef520138cadd3263ce 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -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 diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index eb537aa546106664ea4f99535f8581409bc18a8d..2c8353795226b7f87a3c957c5bc21f6224c477b3 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,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) @@ -27,6 +30,14 @@ #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 */ diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 1f3f01eacaf09cecb88af40187910465cb49b0f3..d36e4e75e08d4e476249a71259d8d760d5158a7b 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 12db5d5cb0bcdde0189c4ff978fb294728f39e25..574b99ea07009a2467daa902d997a58c1b8657e9 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -15,6 +15,7 @@ #include #include #include +#include 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 {}, }; diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 28e33fea510736f179ccfdb985e0d630b0aae616..d5f9a7431bd0516da252d0ca316fde7a63cbbf9e 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 */