This patch updates the common USB code touching the USB private registers with the specific bits to properly enable OHCI and EHCI controllers on BCM63xx SoCs. As a result we now need to protect access to Read Modify Write sequences using a spinlock because we cannot guarantee that any of the exposed helper will not be called concurrently. Signed-off-by: Maxime Bizon <mbizon@xxxxxxxxxx> Signed-off-by: Florian Fainelli <florian@xxxxxxxxxxx> --- arch/mips/bcm63xx/usb-common.c | 97 ++++++++++++++++++++ .../include/asm/mach-bcm63xx/bcm63xx_usb_priv.h | 2 + 2 files changed, 99 insertions(+) diff --git a/arch/mips/bcm63xx/usb-common.c b/arch/mips/bcm63xx/usb-common.c index b617cf6..e18ac08 100644 --- a/arch/mips/bcm63xx/usb-common.c +++ b/arch/mips/bcm63xx/usb-common.c @@ -5,10 +5,12 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * + * Copyright (C) 2008 Maxime Bizon <mbizon@xxxxxxxxxx> * Copyright (C) 2012 Kevin Cernekee <cernekee@xxxxxxxxx> * Copyright (C) 2012 Broadcom Corporation * */ +#include <linux/spinlock.h> #include <linux/export.h> #include <bcm63xx_cpu.h> @@ -16,9 +18,14 @@ #include <bcm63xx_io.h> #include <bcm63xx_usb_priv.h> +static DEFINE_SPINLOCK(usb_priv_reg_lock); + void bcm63xx_usb_priv_select_phy_mode(u32 portmask, bool is_device) { u32 val; + unsigned long flags; + + spin_lock_irqsave(&usb_priv_reg_lock, flags); val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG); if (is_device) { @@ -36,12 +43,17 @@ void bcm63xx_usb_priv_select_phy_mode(u32 portmask, bool is_device) else val &= ~USBH_PRIV_SWAP_USBD_MASK; bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_SWAP_6368_REG); + + spin_unlock_irqrestore(&usb_priv_reg_lock, flags); } EXPORT_SYMBOL(bcm63xx_usb_host_priv_cfg_set); void bcm63xx_usb_priv_select_pullup(u32 portmask, bool is_on) { u32 val; + unsigned long flags; + + spin_lock_irqsave(&usb_priv_reg_lock, flags); val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG); if (is_on) @@ -49,5 +61,90 @@ void bcm63xx_usb_priv_select_pullup(u32 portmask, bool is_on) else val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT); bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG); + + spin_unlock_irqrestore(&usb_priv_reg_lock, flags); } EXPORT_SYMBOL(bcm63xx_usb_priv_select_pullup); + +/* The following array represents the meaning of the DESC/DATA + * endian swapping with respect to the CPU configured endianness + * + * DATA ENDN mmio descriptor + * 0 0 BE invalid + * 0 1 BE LE + * 1 0 BE BE + * 1 1 BE invalid + * + * Since BCM63XX SoCs are configured to be in big-endian mode + * we want configuration at line 3. + */ +void bcm63xx_usb_priv_ohci_cfg_set(void) +{ + u32 reg; + unsigned long flags; + + spin_lock_irqsave(&usb_priv_reg_lock, flags); + + if (BCMCPU_IS_6348()) + bcm_rset_writel(RSET_OHCI_PRIV, 0, OHCI_PRIV_REG); + else if (BCMCPU_IS_6358()) { + reg = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SWAP_6358_REG); + reg &= ~USBH_PRIV_SWAP_OHCI_ENDN_MASK; + reg |= USBH_PRIV_SWAP_OHCI_DATA_MASK; + bcm_rset_writel(RSET_USBH_PRIV, reg, USBH_PRIV_SWAP_6358_REG); + /* + * The magic value comes for the original vendor BSP + * and is needed for USB to work. Datasheet does not + * help, so the magic value is used as-is. + */ + bcm_rset_writel(RSET_USBH_PRIV, 0x1c0020, + USBH_PRIV_TEST_6358_REG); + + } else if (BCMCPU_IS_6328() || BCMCPU_IS_6368()) { + reg = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SWAP_6368_REG); + reg &= ~USBH_PRIV_SWAP_OHCI_ENDN_MASK; + reg |= USBH_PRIV_SWAP_OHCI_DATA_MASK; + bcm_rset_writel(RSET_USBH_PRIV, reg, USBH_PRIV_SWAP_6368_REG); + + reg = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SETUP_6368_REG); + reg |= USBH_PRIV_SETUP_IOC_MASK; + bcm_rset_writel(RSET_USBH_PRIV, reg, USBH_PRIV_SETUP_6368_REG); + } + + spin_unlock_irqrestore(&usb_priv_reg_lock, flags); +} + +void bcm63xx_usb_priv_ehci_cfg_set(void) +{ + u32 reg; + unsigned long flags; + + spin_lock_irqsave(&usb_priv_reg_lock, flags); + + if (BCMCPU_IS_6358()) { + reg = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SWAP_6358_REG); + reg &= ~USBH_PRIV_SWAP_EHCI_ENDN_MASK; + reg |= USBH_PRIV_SWAP_EHCI_DATA_MASK; + bcm_rset_writel(RSET_USBH_PRIV, reg, USBH_PRIV_SWAP_6358_REG); + + /* + * The magic value comes for the original vendor BSP + * and is needed for USB to work. Datasheet does not + * help, so the magic value is used as-is. + */ + bcm_rset_writel(RSET_USBH_PRIV, 0x1c0020, + USBH_PRIV_TEST_6358_REG); + + } else if (BCMCPU_IS_6328() || BCMCPU_IS_6368()) { + reg = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SWAP_6368_REG); + reg &= ~USBH_PRIV_SWAP_EHCI_ENDN_MASK; + reg |= USBH_PRIV_SWAP_EHCI_DATA_MASK; + bcm_rset_writel(RSET_USBH_PRIV, reg, USBH_PRIV_SWAP_6368_REG); + + reg = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SETUP_6368_REG); + reg |= USBH_PRIV_SETUP_IOC_MASK; + bcm_rset_writel(RSET_USBH_PRIV, reg, USBH_PRIV_SETUP_6368_REG); + } + + spin_unlock_irqrestore(&usb_priv_reg_lock, flags); +} diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_usb_priv.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_usb_priv.h index f0d4b59..e7c01e4 100644 --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_usb_priv.h +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_usb_priv.h @@ -5,5 +5,7 @@ void bcm63xx_usb_priv_select_phy_mode(u32 portmask, bool is_device); void bcm63xx_usb_priv_select_pullup(u32 portmask, bool is_on); +void bcm63xx_usb_priv_ohci_cfg_set(void); +void bcm63xx_usb_priv_ehci_cfg_set(void); #endif /* BCM63XX_USB_PRIV_H_ */ -- 1.7.10.4