From 28758a9da77954ed323f86123ef448c6a563c037 Mon Sep 17 00:00:00 2001
From: Florian Fainelli <florian@openwrt.org>
Date: Mon, 28 Jan 2013 20:06:22 +0100
Subject: [PATCH 04/11] MIPS: BCM63XX: add OHCI/EHCI configuration bits to
 common USB code

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@freebox.fr>
Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
 arch/mips/bcm63xx/usb-common.c                     |   97 ++++++++++++++++++++
 .../include/asm/mach-bcm63xx/bcm63xx_usb_priv.h    |    2 +
 2 files changed, 99 insertions(+)

--- 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@freebox.fr>
  * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com>
  * 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(u3
 	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_priv_select_phy_mode);
 
 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
 	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_6362() || 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_6362() || 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);
+}
--- 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_ */
