usb: musb: add support for Blackfin MUSB

Signed-off-by: Bryan Wu <bryan.wu@analog.com>
Signed-off-by: Cliff Cai <cliff.cai@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Remy Bohmer <linux@bohmer.net>
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index 12e115e..397f5fe 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -27,6 +27,7 @@
 
 COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o
 COBJS-$(CONFIG_MUSB_UDC) += musb_udc.o musb_core.o
+COBJS-$(CONFIG_USB_BLACKFIN) += blackfin_usb.o
 COBJS-$(CONFIG_USB_DAVINCI) += davinci.o
 COBJS-$(CONFIG_USB_OMAP3) += omap3.o
 COBJS-$(CONFIG_USB_DA8XX) += da8xx.o
diff --git a/drivers/usb/musb/blackfin_usb.c b/drivers/usb/musb/blackfin_usb.c
new file mode 100644
index 0000000..38aceb2
--- /dev/null
+++ b/drivers/usb/musb/blackfin_usb.c
@@ -0,0 +1,143 @@
+/*
+ * Blackfin MUSB HCD (Host Controller Driver) for u-boot
+ *
+ * Copyright (c) 2008-2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <common.h>
+
+#include <usb.h>
+
+#include <asm/blackfin.h>
+#include <asm/mach-common/bits/usb.h>
+
+#include "musb_core.h"
+
+/* MUSB platform configuration */
+struct musb_config musb_cfg = {
+	.regs       = (struct musb_regs *)USB_FADDR,
+	.timeout    = 0x3FFFFFF,
+	.musb_speed = 0,
+};
+
+/*
+ * This function read or write data to endpoint fifo
+ * Blackfin use DMA polling method to avoid buffer alignment issues
+ *
+ * ep		- Endpoint number
+ * length	- Number of bytes to write to FIFO
+ * fifo_data	- Pointer to data buffer to be read/write
+ * is_write	- Flag for read or write
+ */
+void rw_fifo(u8 ep, u32 length, void *fifo_data, int is_write)
+{
+	struct bfin_musb_dma_regs *regs;
+	u32 val = (u32)fifo_data;
+
+	blackfin_dcache_flush_invalidate_range(fifo_data, fifo_data + length);
+
+	regs = (void *)USB_DMA_INTERRUPT;
+	regs += ep;
+
+	/* Setup DMA address register */
+	bfin_write16(&regs->addr_low, val);
+	SSYNC();
+
+	bfin_write16(&regs->addr_high, val >> 16);
+	SSYNC();
+
+	/* Setup DMA count register */
+	bfin_write16(&regs->count_low, length);
+	bfin_write16(&regs->count_high, 0);
+	SSYNC();
+
+	/* Enable the DMA */
+	val = (ep << 4) | DMA_ENA | INT_ENA;
+	if (is_write)
+		val |= DIRECTION;
+	bfin_write16(&regs->control, val);
+	SSYNC();
+
+	/* Wait for compelete */
+	while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << ep)))
+		continue;
+
+	/* acknowledge dma interrupt */
+	bfin_write_USB_DMA_INTERRUPT(1 << ep);
+	SSYNC();
+
+	/* Reset DMA */
+	bfin_write16(&regs->control, 0);
+	SSYNC();
+}
+
+void write_fifo(u8 ep, u32 length, void *fifo_data)
+{
+	rw_fifo(ep, length, fifo_data, 1);
+}
+
+void read_fifo(u8 ep, u32 length, void *fifo_data)
+{
+	rw_fifo(ep, length, fifo_data, 0);
+}
+
+
+/*
+ * CPU and board-specific MUSB initializations.  Aliased function
+ * signals caller to move on.
+ */
+static void __def_musb_init(void)
+{
+}
+void board_musb_init(void) __attribute__((weak, alias("__def_musb_init")));
+
+int musb_platform_init(void)
+{
+	/* board specific initialization */
+	board_musb_init();
+
+	if (ANOMALY_05000346) {
+		bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value);
+		SSYNC();
+	}
+
+	if (ANOMALY_05000347) {
+		bfin_write_USB_APHY_CNTRL(0x0);
+		SSYNC();
+	}
+
+	/* Configure PLL oscillator register */
+	bfin_write_USB_PLLOSC_CTRL(0x30a8);
+	SSYNC();
+
+	bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1);
+	SSYNC();
+
+	bfin_write_USB_EP_NI0_RXMAXP(64);
+	SSYNC();
+
+	bfin_write_USB_EP_NI0_TXMAXP(64);
+	SSYNC();
+
+	/* Route INTRUSB/INTR_RX/INTR_TX to USB_INT0*/
+	bfin_write_USB_GLOBINTR(0x7);
+	SSYNC();
+
+	bfin_write_USB_GLOBAL_CTL(GLOBAL_ENA | EP1_TX_ENA | EP2_TX_ENA |
+				EP3_TX_ENA | EP4_TX_ENA | EP5_TX_ENA |
+				EP6_TX_ENA | EP7_TX_ENA | EP1_RX_ENA |
+				EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA |
+				EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA);
+	SSYNC();
+
+	return 0;
+}
+
+/*
+ * This function performs Blackfin platform specific deinitialization for usb.
+*/
+void musb_platform_deinit(void)
+{
+}
diff --git a/drivers/usb/musb/blackfin_usb.h b/drivers/usb/musb/blackfin_usb.h
new file mode 100644
index 0000000..ab26ca2
--- /dev/null
+++ b/drivers/usb/musb/blackfin_usb.h
@@ -0,0 +1,99 @@
+/*
+ * Blackfin MUSB HCD (Host Controller Driver) for u-boot
+ *
+ * Copyright (c) 2008-2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __BLACKFIN_USB_H__
+#define __BLACKFIN_USB_H__
+
+#include <linux/types.h>
+
+/* Every register is 32bit aligned, but only 16bits in size */
+#define ureg(name) u16 name; u16 __pad_##name;
+
+#define musb_regs musb_regs
+struct musb_regs {
+	/* common registers */
+	ureg(faddr)
+	ureg(power)
+	ureg(intrtx)
+	ureg(intrrx)
+	ureg(intrtxe)
+	ureg(intrrxe)
+	ureg(intrusb)
+	ureg(intrusbe)
+	ureg(frame)
+	ureg(index)
+	ureg(testmode)
+	ureg(globintr)
+	ureg(global_ctl)
+	u32	reserved0[3];
+	/* indexed registers */
+	ureg(txmaxp)
+	ureg(txcsr)
+	ureg(rxmaxp)
+	ureg(rxcsr)
+	ureg(rxcount)
+	ureg(txtype)
+	ureg(txinterval)
+	ureg(rxtype)
+	ureg(rxinterval)
+	u32	reserved1;
+	ureg(txcount)
+	u32	reserved2[5];
+	/* fifo */
+	u16	fifox[32];
+	/* OTG, dynamic FIFO, version & vendor registers */
+	u32	reserved3[16];
+	ureg(devctl)
+	ureg(vbus_irq)
+	ureg(vbus_mask)
+	u32 reserved4[15];
+	ureg(linkinfo)
+	ureg(vplen)
+	ureg(hseof1)
+	ureg(fseof1)
+	ureg(lseof1)
+	u32 reserved5[41];
+	/* target address registers */
+	struct musb_tar_regs {
+		ureg(txmaxp)
+		ureg(txcsr)
+		ureg(rxmaxp)
+		ureg(rxcsr)
+		ureg(rxcount)
+		ureg(txtype)
+		ureg(txinternal)
+		ureg(rxtype)
+		ureg(rxinternal)
+		u32	reserved6;
+		ureg(txcount)
+		u32 reserved7[5];
+	} tar[8];
+} __attribute__((packed));
+
+struct bfin_musb_dma_regs {
+	ureg(interrupt);
+	ureg(control);
+	ureg(addr_low);
+	ureg(addr_high);
+	ureg(count_low);
+	ureg(count_high);
+	ureg(pad);
+};
+
+#undef ureg
+
+/* EP5-EP7 are the only ones with 1024 byte FIFOs which BULK really needs */
+#define MUSB_BULK_EP 5
+
+/* Blackfin FIFO's are static */
+#define MUSB_NO_DYNAMIC_FIFO
+
+/* No HUB support :( */
+#define MUSB_NO_MULTIPOINT
+
+#endif
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index cee7a11..f0f0301 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -38,6 +38,10 @@
 #include <usb_defs.h>
 #include <asm/io.h>
 
+#ifdef CONFIG_USB_BLACKFIN
+# include "blackfin_usb.h"
+#endif
+
 #define MUSB_EP0_FIFOSIZE	64	/* This is non-configurable */
 
 /* EP0 */
@@ -71,6 +75,7 @@
 };
 
 /* Mentor USB core register overlay structure */
+#ifndef musb_regs
 struct musb_regs {
 	/* common registers */
 	u8	faddr;
@@ -138,6 +143,7 @@
 	} ep[16];
 
 } __attribute__((packed, aligned(32)));
+#endif
 
 /*
  * MUSB Register bits
@@ -347,4 +353,14 @@
 extern void write_fifo(u8 ep, u32 length, void *fifo_data);
 extern void read_fifo(u8 ep, u32 length, void *fifo_data);
 
+#if defined(CONFIG_USB_BLACKFIN)
+/* Every USB register is accessed as a 16-bit even if the value itself
+ * is only 8-bits in size.  Fun stuff.
+ */
+# undef  readb
+# define readb(addr)     (u8)bfin_read16(addr)
+# undef  writeb
+# define writeb(b, addr) bfin_write16(addr, b)
+#endif
+
 #endif	/* __MUSB_HDRC_DEFS_H__ */
diff --git a/drivers/usb/musb/musb_hcd.h b/drivers/usb/musb/musb_hcd.h
index 17e9091..a437985 100644
--- a/drivers/usb/musb/musb_hcd.h
+++ b/drivers/usb/musb/musb_hcd.h
@@ -38,7 +38,9 @@
 #define MUSB_CONTROL_EP 0
 
 /* This defines the endpoint number used for bulk transfer */
-#define MUSB_BULK_EP 1
+#ifndef MUSB_BULK_EP
+# define MUSB_BULK_EP 1
+#endif
 
 /* This defines the endpoint number used for interrupt transfer */
 #define MUSB_INTR_EP 2