Mindspeed Comcerto 2000 patch from SDK 3.1-rc4
This patch comes from Mindspeed's SDK 3.1-rc4
sdk-comcerto-openwrt-c2k_3.1-rc4/target/linux/comcerto2000/patches-3.2/000-mspd-kernel_linux_3_2_3_01_3.patch
Change-Id: I30ff3ebaeb108bc9b4e3b94df9049c1ab45cba93
diff --git a/Makefile b/Makefile
index 89f46df..160e758 100644
--- a/Makefile
+++ b/Makefile
@@ -561,8 +561,12 @@
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os -fno-caller-saves
else
+ifdef CONFIG_COMCERTO_CC_OPTIMIZE_O3
+KBUILD_CFLAGS += -O3 -fno-reorder-blocks -fno-tree-ch -fno-caller-saves
+else
KBUILD_CFLAGS += -O2 -fno-reorder-blocks -fno-tree-ch -fno-caller-saves
endif
+endif
include $(srctree)/arch/$(SRCARCH)/Makefile
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 987c72d..45d65c8 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -266,6 +266,16 @@
help
Support for ARM's Integrator platform.
+config ARCH_COMCERTO
+ bool "Mindspeed Comcerto"
+ select ARCH_SUPPORTS_MSI
+ select NEED_MACH_MEMORY_H
+ select ARCH_REQUIRE_GPIOLIB
+ help
+ This enables support for Mindspeed's Comcerto development boards.
+ If you would like to build your kernel to run on one of these boards
+ then you must say 'Y' here. Otherwise say 'N'
+
config ARCH_REALVIEW
bool "ARM Ltd. RealView family"
select ARM_AMBA
@@ -1099,6 +1109,8 @@
source "arch/arm/mach-w90x900/Kconfig"
+source "arch/arm/mach-comcerto/Kconfig"
+
# Definitions to make life easier
config ARCH_ACORN
bool
@@ -1451,7 +1463,7 @@
depends on REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP || \
MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || \
ARCH_EXYNOS4 || ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || \
- ARCH_MSM_SCORPIONMP || ARCH_SHMOBILE || ARCH_HIGHBANK || SOC_IMX6Q
+ ARCH_MSM_SCORPIONMP || ARCH_SHMOBILE || ARCH_HIGHBANK || SOC_IMX6Q || ARCH_COMCERTO
depends on MMU
select USE_GENERIC_SMP_HELPERS
select HAVE_ARM_SCU if !ARCH_MSM_SCORPIONMP
@@ -1515,6 +1527,11 @@
help
This option enables support for the ARM system coherency unit
+config SCU_SPECULATIVE_LINE_FILLS
+ bool "SCU speculative line fills"
+ depends on HAVE_ARM_SCU && CACHE_PL310
+ default n
+
config HAVE_ARM_TWD
bool
depends on SMP
@@ -1532,10 +1549,12 @@
option alone!
config VMSPLIT_3G
+ depends on !COMCERTO_HW_KERNEL_PAGETABLE
bool "3G/1G user/kernel split"
config VMSPLIT_2G
bool "2G/2G user/kernel split"
config VMSPLIT_1G
+ depends on !COMCERTO_HW_KERNEL_PAGETABLE
bool "1G/3G user/kernel split"
endchoice
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index dfcf3b0..c93d06c 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -26,7 +26,7 @@
# Do not use arch/arm/defconfig - it's always outdated.
# Select a platform tht is kept up-to-date
-KBUILD_DEFCONFIG := versatile_defconfig
+KBUILD_DEFCONFIG := c2krtsm_defconfig
# defines filename extension depending memory management type.
ifeq ($(CONFIG_MMU),)
@@ -138,6 +138,10 @@
machine-$(CONFIG_ARCH_BCMRING) := bcmring
machine-$(CONFIG_ARCH_CLPS711X) := clps711x
machine-$(CONFIG_ARCH_CNS3XXX) := cns3xxx
+machine-$(CONFIG_ARCH_COMCERTO) := comcerto
+ifeq ($(CONFIG_ARCH_COMCERTO),y)
+textofs-$(CONFIG_ZONE_DMA) := 0x04008000
+endif
machine-$(CONFIG_ARCH_DAVINCI) := davinci
machine-$(CONFIG_ARCH_DOVE) := dove
machine-$(CONFIG_ARCH_EBSA110) := ebsa110
@@ -199,6 +203,8 @@
machine-$(CONFIG_MACH_SPEAR600) := spear6xx
machine-$(CONFIG_ARCH_ZYNQ) := zynq
+
+
# Platform directory name. This list is sorted alphanumerically
# by CONFIG_* macro name.
plat-$(CONFIG_ARCH_MXC) := mxc
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 21f56ff..da01136 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -33,6 +33,11 @@
#
# Architecture dependencies
#
+
+ifeq ($(CONFIG_ARCH_COMCERTO),y)
+OBJS += head-comcerto.o
+endif
+
ifeq ($(CONFIG_ARCH_ACORN),y)
OBJS += ll_char_wr.o font.o
endif
diff --git a/arch/arm/boot/compressed/head-comcerto.S b/arch/arm/boot/compressed/head-comcerto.S
new file mode 100644
index 0000000..e48f6e6
--- /dev/null
+++ b/arch/arm/boot/compressed/head-comcerto.S
@@ -0,0 +1,6 @@
+#include <asm/mach-types.h>
+
+ .section ".start", "ax"
+ ldr r7,mach_type
+
+mach_type: .word MACH_TYPE_COMCERTO
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 410a546..399302e 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -43,11 +43,12 @@
#include <asm/mach/irq.h>
#include <asm/hardware/gic.h>
+#include <mach/sema.h>
+
static DEFINE_RAW_SPINLOCK(irq_controller_lock);
/* Address of GIC 0 CPU interface */
void __iomem *gic_cpu_base_addr __read_mostly;
-
/*
* Supported arch specific GIC irq extension.
* Default make them NULL.
@@ -90,30 +91,48 @@
static void gic_mask_irq(struct irq_data *d)
{
u32 mask = 1 << (gic_irq(d) % 32);
+ unsigned long flags;
+
+ if ((gic_irq(d) == 87) || (gic_irq(d) == 66) || (gic_irq(d) == 33)) {
+ return;
+ }
raw_spin_lock(&irq_controller_lock);
+ flags = msp_lock_frqsave();
writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4);
if (gic_arch_extn.irq_mask)
gic_arch_extn.irq_mask(d);
+ msp_unlock_frqrestore(flags);
raw_spin_unlock(&irq_controller_lock);
}
static void gic_unmask_irq(struct irq_data *d)
{
u32 mask = 1 << (gic_irq(d) % 32);
+ unsigned long flags;
+
+ if ((gic_irq(d) == 87) || (gic_irq(d) == 66) || (gic_irq(d) == 33)) {
+ return;
+ }
raw_spin_lock(&irq_controller_lock);
+ flags = msp_lock_frqsave();
if (gic_arch_extn.irq_unmask)
gic_arch_extn.irq_unmask(d);
writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
+ msp_unlock_frqrestore(flags);
raw_spin_unlock(&irq_controller_lock);
}
static void gic_eoi_irq(struct irq_data *d)
{
if (gic_arch_extn.irq_eoi) {
+ unsigned long flags;
+
raw_spin_lock(&irq_controller_lock);
+ flags = msp_lock_frqsave();
gic_arch_extn.irq_eoi(d);
+ msp_unlock_frqrestore(flags);
raw_spin_unlock(&irq_controller_lock);
}
@@ -308,6 +327,14 @@
for (i = 32; i < gic_irqs; i += 32)
writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
+#ifdef CONFIG_COMCERTO_MSP
+ /*
+ * Set SPI interrupts are nonSecure
+ */
+ for (i = 32; i < gic_irqs; i += 32)
+ writel_relaxed(0xffffffff, base + GIC_DIST_SECURITY_BIT + i * 4 / 32);
+#endif /* CONFIG_COMCERTO_MSP */
+
/*
* Setup the Linux IRQ subsystem.
*/
@@ -325,7 +352,14 @@
irq_set_chip_data(irq, gic);
}
+#ifdef CONFIG_COMCERTO_MSP
+ /*
+ * Enable NonSecure interrupts in Distributor
+ */
+ writel_relaxed(3, base + GIC_DIST_CTRL);
+#else /* !CONFIG_COMCERTO_MSP */
writel_relaxed(1, base + GIC_DIST_CTRL);
+#endif /* CONFIG_COMCERTO_MSP */
}
static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
@@ -348,9 +382,62 @@
writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
+
+#ifdef CONFIG_COMCERTO_MSP
+ /*
+ * Set PPI and SGI interrupts are nonSecure
+ */
+ writel_relaxed(0xffffffff, dist_base + GIC_DIST_SECURITY_BIT);
+
+ /*
+ * Enable NonSecure interrupts in CPU interface,
+ * Secure interrupts go to FIQ line,
+ * Secure read returns valid NonSecure interrupt ID
+ */
+ writel_relaxed(0xf, base + GIC_CPU_CTRL);
+#else /* !CONFIG_COMCERTO_MSP */
writel_relaxed(1, base + GIC_CPU_CTRL);
+#endif /* CONFIG_COMCERTO_MSP */
}
+#ifdef CONFIG_COMCERTO_MSP
+
+static void __cpuinit gic_cpu_init_irq_only(struct gic_chip_data *gic)
+{
+ void __iomem *dist_base = gic->dist_base;
+ void __iomem *base = gic->cpu_base;
+ int i;
+
+ /*
+ * Deal with the banked PPI and SGI interrupts - disable all
+ * PPI interrupts, ensure all SGI interrupts are enabled.
+ */
+ writel_relaxed(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR);
+ writel_relaxed(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET);
+
+ /*
+ * Set priority on PPI and SGI interrupts
+ */
+ for (i = 0; i < 32; i += 4)
+ writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
+
+ writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
+
+ /*
+ * Set PPI and SGI interrupts are nonSecure
+ */
+ writel_relaxed(0xffffffff, dist_base + GIC_DIST_SECURITY_BIT);
+
+ /*
+ * Enable NonSecure interrupts in CPU interface,
+ * Secure interrupts go to IRQ line,
+ * Secure read returns valid NonSecure interrupt ID
+ */
+ writel_relaxed(0x7, base + GIC_CPU_CTRL);
+}
+
+#endif /* CONFIG_COMCERTO_MSP */
+
#ifdef CONFIG_CPU_PM
/*
* Saves the GIC distributor registers during suspend or idle. Must be called
@@ -625,7 +712,12 @@
{
BUG_ON(gic_nr >= MAX_GIC_NR);
+#ifdef CONFIG_COMCERTO_MSP
+ /* run alternative secondary_boot gic init */
+ gic_cpu_init_irq_only(&gic_data[gic_nr]);
+#else /* !CONFIG_COMCERTO_MSP */
gic_cpu_init(&gic_data[gic_nr]);
+#endif /* CONFIG_COMCERTO_MSP */
}
#ifdef CONFIG_SMP
@@ -645,7 +737,16 @@
dsb();
/* this always happens on GIC0 */
+
+#ifdef CONFIG_COMCERTO_MSP
+#define GIC_SGI_SATT (1 << 15)
+ /*
+ * Send SGI from Secure write to NonSecure target
+ */
+ writel_relaxed(map << 16 | GIC_SGI_SATT | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT);
+#else /* !CONFIG_COMCERTO_MSP */
writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT);
+#endif /* CONFIG_COMCERTO_MSP */
}
#endif
diff --git a/arch/arm/configs/c2kasic_defconfig b/arch/arm/configs/c2kasic_defconfig
new file mode 100644
index 0000000..21cf380
--- /dev/null
+++ b/arch/arm/configs/c2kasic_defconfig
@@ -0,0 +1,1046 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/arm 3.2.2 Kernel Configuration
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
+CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_HAVE_IRQ_WORK=y
+CONFIG_IRQ_WORK=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_FHANDLE is not set
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_HAVE_SPARSE_IRQ=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_IRQ_DOMAIN=y
+# CONFIG_SPARSE_IRQ is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_TREE_RCU=y
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_RCU_TRACE is not set
+CONFIG_RCU_FANOUT=32
+# CONFIG_RCU_FANOUT_EXACT is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+# CONFIG_CGROUP_FREEZER is not set
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_PROC_PID_CPUSET=y
+CONFIG_CGROUP_CPUACCT=y
+# CONFIG_RESOURCE_COUNTERS is not set
+# CONFIG_CGROUP_PERF is not set
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_BLK_CGROUP is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+# CONFIG_SCHED_AUTOGROUP is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_RELAY=y
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EXPERT is not set
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+# CONFIG_EMBEDDED is not set
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_PERF_COUNTERS is not set
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_PROFILING is not set
+CONFIG_TRACEPOINTS=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_USE_GENERIC_SMP_HELPERS=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_STOP_MACHINE=y
+CONFIG_BLOCK=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_INLINE_SPIN_TRYLOCK is not set
+# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK is not set
+# CONFIG_INLINE_SPIN_LOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
+CONFIG_INLINE_SPIN_UNLOCK=y
+# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
+CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
+# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_READ_TRYLOCK is not set
+# CONFIG_INLINE_READ_LOCK is not set
+# CONFIG_INLINE_READ_LOCK_BH is not set
+# CONFIG_INLINE_READ_LOCK_IRQ is not set
+# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
+CONFIG_INLINE_READ_UNLOCK=y
+# CONFIG_INLINE_READ_UNLOCK_BH is not set
+CONFIG_INLINE_READ_UNLOCK_IRQ=y
+# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_WRITE_TRYLOCK is not set
+# CONFIG_INLINE_WRITE_LOCK is not set
+# CONFIG_INLINE_WRITE_LOCK_BH is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
+CONFIG_INLINE_WRITE_UNLOCK=y
+# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
+CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
+# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+CONFIG_MMU=y
+# CONFIG_ARCH_INTEGRATOR is not set
+CONFIG_ARCH_COMCERTO=y
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_BCMRING is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CNS3XXX is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_PRIMA2 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_MXS is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_PICOXCELL is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_EXYNOS is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_TCC_926 is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_ARCH_VT8500 is not set
+# CONFIG_ARCH_ZYNQ is not set
+# CONFIG_ARCH_SUPPORTS_BIG_ENDIAN is not set
+
+#
+# System MMU
+#
+
+#
+# Comcerto Implementation Options
+#
+CONFIG_ARCH_M86XXX=y
+# CONFIG_C2K_EVM is not set
+CONFIG_C2K_ASIC=y
+# CONFIG_RTSM_C2K is not set
+CONFIG_MTD_COMCERTO_NOR=y
+CONFIG_COMCERTO_TDM_CLOCK=y
+# CONFIG_PCI is not set
+CONFIG_COMCERTO_NUM_PCIES=2
+CONFIG_COMCERTO_FP=y
+CONFIG_COMCERTO_UART0_SUPPORT=y
+CONFIG_COMCERTO_UART1_SUPPORT=y
+CONFIG_COMCERTO_USB0_SUPPORT=y
+# CONFIG_COMCERTO_USB1_SUPPORT is not set
+CONFIG_COMCERTO_IPSEC_SUPPORT=y
+CONFIG_COMCERTO_SPI_SUPPORT=y
+CONFIG_COMCERTO_FAST_SPI_SUPPORT=y
+CONFIG_COMCERTO_I2C_SUPPORT=y
+CONFIG_COMCERTO_DW_DMA_SUPPORT=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+# CONFIG_SWP_EMULATE is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_OUTER_CACHE=y
+CONFIG_OUTER_CACHE_SYNC=y
+CONFIG_CACHE_L2X0=y
+CONFIG_CACHE_PL310=y
+CONFIG_ARM_L1_CACHE_SHIFT=5
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+CONFIG_CPU_HAS_PMU=y
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+CONFIG_ARM_ERRATA_742230=y
+CONFIG_ARM_ERRATA_742231=y
+CONFIG_PL310_ERRATA_588369=y
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_PL310_ERRATA_727915=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_PL310_ERRATA_753970=y
+CONFIG_ARM_ERRATA_754322=y
+# CONFIG_ARM_ERRATA_754327 is not set
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_PL310_ERRATA_769419=y
+CONFIG_ARM_GIC=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_SMP=y
+# CONFIG_SMP_ON_UP is not set
+CONFIG_ARM_CPU_TOPOLOGY=y
+# CONFIG_SCHED_MC is not set
+# CONFIG_SCHED_SMT is not set
+CONFIG_HAVE_ARM_SCU=y
+CONFIG_HAVE_ARM_TWD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_NR_CPUS=2
+# CONFIG_HOTPLUG_CPU is not set
+CONFIG_LOCAL_TIMERS=y
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_HAVE_ARCH_PFN_VALID=y
+# CONFIG_HIGHMEM is not set
+CONFIG_HW_PERF_EVENTS=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_COMPACTION is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_CLEANCACHE is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+
+#
+# Boot options
+#
+# CONFIG_USE_OF is not set
+CONFIG_ZBOOT_ROM_TEXT=0x00608000
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_ZBOOT_ROM=y
+CONFIG_CMDLINE=""
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+
+#
+# CPU Power Management
+#
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_SUSPEND is not set
+# CONFIG_PM_RUNTIME is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_ARM_CPU_SUSPEND is not set
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+CONFIG_MTD_RAM=y
+CONFIG_MTD_ROM=y
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_DOCG3 is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_MISC_DEVICES is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_ELANTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_TRACE_SINK is not set
+CONFIG_DEVKMEM=y
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+CONFIG_SERIAL_8250_RSA=y
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_RAMOOPS is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_MUX is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_SMBUS=y
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=m
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_ALGOPCA=m
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+CONFIG_I2C_COMCERTO=y
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_PCA_PLATFORM=m
+# CONFIG_I2C_PXA_PCI is not set
+CONFIG_I2C_SIMTEC=m
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+CONFIG_I2C_PARPORT_LIGHT=m
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_SPI is not set
+CONFIG_SPI_MSPD=y
+CONFIG_COMCERTO_SPI=y
+
+#
+# Miscellaneous I2C Chip support
+#
+CONFIG_EEPROM_AT=y
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+
+#
+# Enable Device Drivers -> PPS to see the PTP clock options.
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB=y
+# CONFIG_SSB_DEBUG is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+CONFIG_DRM=m
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+# CONFIG_FB is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=y
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+
+#
+# Display device support
+#
+CONFIG_DISPLAY_SUPPORT=m
+
+#
+# Display hardware drivers
+#
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+
+#
+# Virtio drivers
+#
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_MMIO is not set
+# CONFIG_STAGING is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_IOMMU_SUPPORT=y
+# CONFIG_VIRT_DRIVERS is not set
+# CONFIG_PM_DEVFREQ is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_FSNOTIFY is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=1
+# CONFIG_JFFS2_FS_WRITEBUFFER is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_LOGFS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_PSTORE is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=2048
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_LOCKUP_DETECTOR is not set
+# CONFIG_HARDLOCKUP_DETECTOR is not set
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_LKDTM is not set
+# CONFIG_FAULT_INJECTION is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_EVENT_POWER_TRACING_DEPRECATED=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+# CONFIG_CRYPTO is not set
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_CRC8 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_XZ_DEC is not set
+# CONFIG_XZ_DEC_BCJ is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+# CONFIG_AVERAGE is not set
+# CONFIG_CORDIC is not set
diff --git a/arch/arm/configs/c2kevm_defconfig b/arch/arm/configs/c2kevm_defconfig
new file mode 100644
index 0000000..caba88d
--- /dev/null
+++ b/arch/arm/configs/c2kevm_defconfig
@@ -0,0 +1,1046 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/arm 3.2.2 Kernel Configuration
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
+CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_HAVE_IRQ_WORK=y
+CONFIG_IRQ_WORK=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_FHANDLE is not set
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_HAVE_SPARSE_IRQ=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_IRQ_DOMAIN=y
+# CONFIG_SPARSE_IRQ is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_TREE_RCU=y
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_RCU_TRACE is not set
+CONFIG_RCU_FANOUT=32
+# CONFIG_RCU_FANOUT_EXACT is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+# CONFIG_CGROUP_FREEZER is not set
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_PROC_PID_CPUSET=y
+CONFIG_CGROUP_CPUACCT=y
+# CONFIG_RESOURCE_COUNTERS is not set
+# CONFIG_CGROUP_PERF is not set
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_BLK_CGROUP is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+# CONFIG_SCHED_AUTOGROUP is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_RELAY=y
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EXPERT is not set
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+# CONFIG_EMBEDDED is not set
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_PERF_COUNTERS is not set
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_PROFILING is not set
+CONFIG_TRACEPOINTS=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_USE_GENERIC_SMP_HELPERS=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_STOP_MACHINE=y
+CONFIG_BLOCK=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_INLINE_SPIN_TRYLOCK is not set
+# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK is not set
+# CONFIG_INLINE_SPIN_LOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
+CONFIG_INLINE_SPIN_UNLOCK=y
+# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
+CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
+# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_READ_TRYLOCK is not set
+# CONFIG_INLINE_READ_LOCK is not set
+# CONFIG_INLINE_READ_LOCK_BH is not set
+# CONFIG_INLINE_READ_LOCK_IRQ is not set
+# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
+CONFIG_INLINE_READ_UNLOCK=y
+# CONFIG_INLINE_READ_UNLOCK_BH is not set
+CONFIG_INLINE_READ_UNLOCK_IRQ=y
+# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_WRITE_TRYLOCK is not set
+# CONFIG_INLINE_WRITE_LOCK is not set
+# CONFIG_INLINE_WRITE_LOCK_BH is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
+CONFIG_INLINE_WRITE_UNLOCK=y
+# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
+CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
+# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+CONFIG_MMU=y
+# CONFIG_ARCH_INTEGRATOR is not set
+CONFIG_ARCH_COMCERTO=y
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_BCMRING is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CNS3XXX is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_PRIMA2 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_MXS is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_PICOXCELL is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_EXYNOS is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_TCC_926 is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_ARCH_VT8500 is not set
+# CONFIG_ARCH_ZYNQ is not set
+# CONFIG_ARCH_SUPPORTS_BIG_ENDIAN is not set
+
+#
+# System MMU
+#
+
+#
+# Comcerto Implementation Options
+#
+CONFIG_ARCH_M86XXX=y
+CONFIG_C2K_EVM=y
+# CONFIG_C2K_ASIC is not set
+# CONFIG_RTSM_C2K is not set
+CONFIG_MTD_COMCERTO_NOR=y
+CONFIG_COMCERTO_TDM_CLOCK=y
+# CONFIG_PCI is not set
+CONFIG_COMCERTO_NUM_PCIES=2
+CONFIG_COMCERTO_FP=y
+# CONFIG_COMCERTO_UART0_SUPPORT is not set
+CONFIG_COMCERTO_UART1_SUPPORT=y
+CONFIG_COMCERTO_USB0_SUPPORT=y
+# CONFIG_COMCERTO_USB1_SUPPORT is not set
+CONFIG_COMCERTO_IPSEC_SUPPORT=y
+CONFIG_COMCERTO_SPI_SUPPORT=y
+CONFIG_COMCERTO_FAST_SPI_SUPPORT=y
+CONFIG_COMCERTO_I2C_SUPPORT=y
+CONFIG_COMCERTO_DW_DMA_SUPPORT=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+# CONFIG_SWP_EMULATE is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_OUTER_CACHE=y
+CONFIG_OUTER_CACHE_SYNC=y
+CONFIG_CACHE_L2X0=y
+CONFIG_CACHE_PL310=y
+CONFIG_ARM_L1_CACHE_SHIFT=5
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+CONFIG_CPU_HAS_PMU=y
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+CONFIG_ARM_ERRATA_742230=y
+CONFIG_ARM_ERRATA_742231=y
+CONFIG_PL310_ERRATA_588369=y
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_PL310_ERRATA_727915=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_PL310_ERRATA_753970=y
+CONFIG_ARM_ERRATA_754322=y
+# CONFIG_ARM_ERRATA_754327 is not set
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_PL310_ERRATA_769419=y
+CONFIG_ARM_GIC=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_SMP=y
+# CONFIG_SMP_ON_UP is not set
+CONFIG_ARM_CPU_TOPOLOGY=y
+# CONFIG_SCHED_MC is not set
+# CONFIG_SCHED_SMT is not set
+CONFIG_HAVE_ARM_SCU=y
+CONFIG_HAVE_ARM_TWD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_NR_CPUS=2
+# CONFIG_HOTPLUG_CPU is not set
+CONFIG_LOCAL_TIMERS=y
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_HAVE_ARCH_PFN_VALID=y
+# CONFIG_HIGHMEM is not set
+CONFIG_HW_PERF_EVENTS=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_COMPACTION is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_CLEANCACHE is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+
+#
+# Boot options
+#
+# CONFIG_USE_OF is not set
+CONFIG_ZBOOT_ROM_TEXT=0x00608000
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_ZBOOT_ROM=y
+CONFIG_CMDLINE=""
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+
+#
+# CPU Power Management
+#
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_SUSPEND is not set
+# CONFIG_PM_RUNTIME is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_ARM_CPU_SUSPEND is not set
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+CONFIG_MTD_RAM=y
+CONFIG_MTD_ROM=y
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_DOCG3 is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_MISC_DEVICES is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_ELANTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_TRACE_SINK is not set
+CONFIG_DEVKMEM=y
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+CONFIG_SERIAL_8250_RSA=y
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_RAMOOPS is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_MUX is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_SMBUS=y
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=m
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_ALGOPCA=m
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+CONFIG_I2C_COMCERTO=y
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_PCA_PLATFORM=m
+# CONFIG_I2C_PXA_PCI is not set
+CONFIG_I2C_SIMTEC=m
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+CONFIG_I2C_PARPORT_LIGHT=m
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_SPI is not set
+CONFIG_SPI_MSPD=y
+CONFIG_COMCERTO_SPI=y
+
+#
+# Miscellaneous I2C Chip support
+#
+CONFIG_EEPROM_AT=y
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+
+#
+# Enable Device Drivers -> PPS to see the PTP clock options.
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB=y
+# CONFIG_SSB_DEBUG is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+CONFIG_DRM=m
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+# CONFIG_FB is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=y
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+
+#
+# Display device support
+#
+CONFIG_DISPLAY_SUPPORT=m
+
+#
+# Display hardware drivers
+#
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+
+#
+# Virtio drivers
+#
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_MMIO is not set
+# CONFIG_STAGING is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_IOMMU_SUPPORT=y
+# CONFIG_VIRT_DRIVERS is not set
+# CONFIG_PM_DEVFREQ is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_FSNOTIFY is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=1
+# CONFIG_JFFS2_FS_WRITEBUFFER is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_LOGFS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_PSTORE is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=2048
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_LOCKUP_DETECTOR is not set
+# CONFIG_HARDLOCKUP_DETECTOR is not set
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_LKDTM is not set
+# CONFIG_FAULT_INJECTION is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_EVENT_POWER_TRACING_DEPRECATED=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+# CONFIG_CRYPTO is not set
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_CRC8 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_XZ_DEC is not set
+# CONFIG_XZ_DEC_BCJ is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+# CONFIG_AVERAGE is not set
+# CONFIG_CORDIC is not set
diff --git a/arch/arm/configs/c2klv_defconfig b/arch/arm/configs/c2klv_defconfig
new file mode 100644
index 0000000..79e3d9a
--- /dev/null
+++ b/arch/arm/configs/c2klv_defconfig
@@ -0,0 +1,924 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/arm 3.2.2 Kernel Configuration
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_HAVE_IRQ_WORK=y
+CONFIG_IRQ_WORK=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_FHANDLE is not set
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_HAVE_SPARSE_IRQ=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_IRQ_DOMAIN=y
+# CONFIG_SPARSE_IRQ is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_TREE_RCU=y
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_RCU_TRACE is not set
+CONFIG_RCU_FANOUT=32
+# CONFIG_RCU_FANOUT_EXACT is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+# CONFIG_CGROUP_FREEZER is not set
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_PROC_PID_CPUSET=y
+CONFIG_CGROUP_CPUACCT=y
+# CONFIG_RESOURCE_COUNTERS is not set
+# CONFIG_CGROUP_PERF is not set
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_BLK_CGROUP is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+# CONFIG_SCHED_AUTOGROUP is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_RELAY=y
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EXPERT is not set
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+# CONFIG_EMBEDDED is not set
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_PERF_COUNTERS is not set
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_PROFILING is not set
+CONFIG_TRACEPOINTS=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_USE_GENERIC_SMP_HELPERS=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_STOP_MACHINE=y
+CONFIG_BLOCK=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_INLINE_SPIN_TRYLOCK is not set
+# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK is not set
+# CONFIG_INLINE_SPIN_LOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
+CONFIG_INLINE_SPIN_UNLOCK=y
+# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
+CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
+# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_READ_TRYLOCK is not set
+# CONFIG_INLINE_READ_LOCK is not set
+# CONFIG_INLINE_READ_LOCK_BH is not set
+# CONFIG_INLINE_READ_LOCK_IRQ is not set
+# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
+CONFIG_INLINE_READ_UNLOCK=y
+# CONFIG_INLINE_READ_UNLOCK_BH is not set
+CONFIG_INLINE_READ_UNLOCK_IRQ=y
+# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_WRITE_TRYLOCK is not set
+# CONFIG_INLINE_WRITE_LOCK is not set
+# CONFIG_INLINE_WRITE_LOCK_BH is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
+CONFIG_INLINE_WRITE_UNLOCK=y
+# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
+CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
+# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+CONFIG_MMU=y
+# CONFIG_ARCH_INTEGRATOR is not set
+CONFIG_ARCH_COMCERTO=y
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_BCMRING is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CNS3XXX is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_PRIMA2 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_MXS is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_PICOXCELL is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_EXYNOS is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_TCC_926 is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_ARCH_VT8500 is not set
+# CONFIG_ARCH_ZYNQ is not set
+# CONFIG_ARCH_SUPPORTS_BIG_ENDIAN is not set
+
+#
+# System MMU
+#
+
+#
+# Comcerto Implementation Options
+#
+CONFIG_ARCH_M86XXX=y
+# CONFIG_C2K_EVM is not set
+# CONFIG_C2K_ASIC is not set
+CONFIG_RTSM_C2K=y
+CONFIG_MTD_COMCERTO_NOR=y
+CONFIG_COMCERTO_TDM_CLOCK=y
+CONFIG_COMCERTO_NUM_PCIES=2
+CONFIG_COMCERTO_FP=y
+# CONFIG_COMCERTO_UART0_SUPPORT is not set
+CONFIG_COMCERTO_UART1_SUPPORT=y
+CONFIG_COMCERTO_USB0_SUPPORT=y
+# CONFIG_COMCERTO_USB1_SUPPORT is not set
+CONFIG_COMCERTO_IPSEC_SUPPORT=y
+CONFIG_COMCERTO_SPI_SUPPORT=y
+CONFIG_COMCERTO_FAST_SPI_SUPPORT=y
+CONFIG_COMCERTO_I2C_SUPPORT=y
+CONFIG_COMCERTO_DW_DMA_SUPPORT=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+CONFIG_SWP_EMULATE=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_OUTER_CACHE=y
+CONFIG_OUTER_CACHE_SYNC=y
+CONFIG_CACHE_L2X0=y
+CONFIG_CACHE_PL310=y
+CONFIG_ARM_L1_CACHE_SHIFT=5
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+CONFIG_CPU_HAS_PMU=y
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+# CONFIG_ARM_ERRATA_742230 is not set
+# CONFIG_ARM_ERRATA_742231 is not set
+CONFIG_PL310_ERRATA_588369=y
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_PL310_ERRATA_727915=y
+# CONFIG_ARM_ERRATA_743622 is not set
+# CONFIG_ARM_ERRATA_751472 is not set
+CONFIG_PL310_ERRATA_753970=y
+# CONFIG_ARM_ERRATA_754322 is not set
+# CONFIG_ARM_ERRATA_754327 is not set
+# CONFIG_ARM_ERRATA_764369 is not set
+CONFIG_PL310_ERRATA_769419=y
+CONFIG_ARM_GIC=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_SMP=y
+CONFIG_SMP_ON_UP=y
+CONFIG_ARM_CPU_TOPOLOGY=y
+# CONFIG_SCHED_MC is not set
+# CONFIG_SCHED_SMT is not set
+CONFIG_HAVE_ARM_SCU=y
+CONFIG_HAVE_ARM_TWD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_NR_CPUS=2
+# CONFIG_HOTPLUG_CPU is not set
+CONFIG_LOCAL_TIMERS=y
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+# CONFIG_THUMB2_KERNEL is not set
+# CONFIG_AEABI is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_HAVE_ARCH_PFN_VALID=y
+# CONFIG_HIGHMEM is not set
+# CONFIG_HW_PERF_EVENTS is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_COMPACTION is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_CLEANCACHE is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+
+#
+# Boot options
+#
+# CONFIG_USE_OF is not set
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_CMDLINE=""
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_AUTO_ZRELADDR is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_FPE_NWFPE is not set
+# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+
+#
+# Power management options
+#
+# CONFIG_SUSPEND is not set
+# CONFIG_PM_RUNTIME is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_ARM_CPU_SUSPEND is not set
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_MISC_DEVICES is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_ELANTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_TRACE_SINK is not set
+CONFIG_DEVKMEM=y
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+CONFIG_SERIAL_8250_RSA=y
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_RAMOOPS is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_MUX is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_SMBUS=y
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=m
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_ALGOPCA=m
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_PCA_PLATFORM=m
+# CONFIG_I2C_PXA_PCI is not set
+CONFIG_I2C_SIMTEC=m
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+CONFIG_I2C_PARPORT_LIGHT=m
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_SPI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+
+#
+# Enable Device Drivers -> PPS to see the PTP clock options.
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB=y
+# CONFIG_SSB_DEBUG is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+CONFIG_DRM=m
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+# CONFIG_FB is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=y
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+
+#
+# Display device support
+#
+CONFIG_DISPLAY_SUPPORT=m
+
+#
+# Display hardware drivers
+#
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+
+#
+# Virtio drivers
+#
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_MMIO is not set
+# CONFIG_STAGING is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_IOMMU_SUPPORT=y
+# CONFIG_VIRT_DRIVERS is not set
+# CONFIG_PM_DEVFREQ is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_FSNOTIFY is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+# CONFIG_MISC_FILESYSTEMS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=2048
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_LOCKUP_DETECTOR is not set
+# CONFIG_HARDLOCKUP_DETECTOR is not set
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_LKDTM is not set
+# CONFIG_FAULT_INJECTION is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_EVENT_POWER_TRACING_DEPRECATED=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+# CONFIG_CRYPTO is not set
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_CRC8 is not set
+# CONFIG_XZ_DEC is not set
+# CONFIG_XZ_DEC_BCJ is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_GENERIC_ATOMIC64=y
+# CONFIG_AVERAGE is not set
+# CONFIG_CORDIC is not set
diff --git a/arch/arm/configs/c2kmfcnevm_defconfig b/arch/arm/configs/c2kmfcnevm_defconfig
new file mode 100644
index 0000000..2e12bbf
--- /dev/null
+++ b/arch/arm/configs/c2kmfcnevm_defconfig
@@ -0,0 +1,1047 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/arm 3.2.2 Kernel Configuration
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
+CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_HAVE_IRQ_WORK=y
+CONFIG_IRQ_WORK=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_FHANDLE is not set
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_HAVE_SPARSE_IRQ=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_IRQ_DOMAIN=y
+# CONFIG_SPARSE_IRQ is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_TREE_RCU=y
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_RCU_TRACE is not set
+CONFIG_RCU_FANOUT=32
+# CONFIG_RCU_FANOUT_EXACT is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+# CONFIG_CGROUP_FREEZER is not set
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_PROC_PID_CPUSET=y
+CONFIG_CGROUP_CPUACCT=y
+# CONFIG_RESOURCE_COUNTERS is not set
+# CONFIG_CGROUP_PERF is not set
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_BLK_CGROUP is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+# CONFIG_SCHED_AUTOGROUP is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_RELAY=y
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EXPERT is not set
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+# CONFIG_EMBEDDED is not set
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_PERF_COUNTERS is not set
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_PROFILING is not set
+CONFIG_TRACEPOINTS=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_USE_GENERIC_SMP_HELPERS=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_STOP_MACHINE=y
+CONFIG_BLOCK=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_INLINE_SPIN_TRYLOCK is not set
+# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK is not set
+# CONFIG_INLINE_SPIN_LOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
+CONFIG_INLINE_SPIN_UNLOCK=y
+# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
+CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
+# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_READ_TRYLOCK is not set
+# CONFIG_INLINE_READ_LOCK is not set
+# CONFIG_INLINE_READ_LOCK_BH is not set
+# CONFIG_INLINE_READ_LOCK_IRQ is not set
+# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
+CONFIG_INLINE_READ_UNLOCK=y
+# CONFIG_INLINE_READ_UNLOCK_BH is not set
+CONFIG_INLINE_READ_UNLOCK_IRQ=y
+# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_WRITE_TRYLOCK is not set
+# CONFIG_INLINE_WRITE_LOCK is not set
+# CONFIG_INLINE_WRITE_LOCK_BH is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
+CONFIG_INLINE_WRITE_UNLOCK=y
+# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
+CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
+# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+CONFIG_MMU=y
+# CONFIG_ARCH_INTEGRATOR is not set
+CONFIG_ARCH_COMCERTO=y
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_BCMRING is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CNS3XXX is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_PRIMA2 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_MXS is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_PICOXCELL is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_EXYNOS is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_TCC_926 is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_ARCH_VT8500 is not set
+# CONFIG_ARCH_ZYNQ is not set
+# CONFIG_ARCH_SUPPORTS_BIG_ENDIAN is not set
+
+#
+# System MMU
+#
+
+#
+# Comcerto Implementation Options
+#
+CONFIG_ARCH_M86XXX=y
+# CONFIG_C2K_EVM is not set
+CONFIG_C2K_MFCN_EVM=y
+# CONFIG_C2K_ASIC is not set
+# CONFIG_RTSM_C2K is not set
+CONFIG_MTD_COMCERTO_NOR=y
+CONFIG_COMCERTO_TDM_CLOCK=y
+# CONFIG_PCI is not set
+CONFIG_COMCERTO_NUM_PCIES=2
+CONFIG_COMCERTO_FP=y
+# CONFIG_COMCERTO_UART0_SUPPORT is not set
+CONFIG_COMCERTO_UART1_SUPPORT=y
+CONFIG_COMCERTO_USB0_SUPPORT=y
+# CONFIG_COMCERTO_USB1_SUPPORT is not set
+CONFIG_COMCERTO_IPSEC_SUPPORT=y
+CONFIG_COMCERTO_SPI_SUPPORT=y
+CONFIG_COMCERTO_FAST_SPI_SUPPORT=y
+CONFIG_COMCERTO_I2C_SUPPORT=y
+CONFIG_COMCERTO_DW_DMA_SUPPORT=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+# CONFIG_SWP_EMULATE is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_OUTER_CACHE=y
+CONFIG_OUTER_CACHE_SYNC=y
+CONFIG_CACHE_L2X0=y
+CONFIG_CACHE_PL310=y
+CONFIG_ARM_L1_CACHE_SHIFT=5
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+CONFIG_CPU_HAS_PMU=y
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+CONFIG_ARM_ERRATA_742230=y
+CONFIG_ARM_ERRATA_742231=y
+CONFIG_PL310_ERRATA_588369=y
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_PL310_ERRATA_727915=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_PL310_ERRATA_753970=y
+CONFIG_ARM_ERRATA_754322=y
+# CONFIG_ARM_ERRATA_754327 is not set
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_PL310_ERRATA_769419=y
+CONFIG_ARM_GIC=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_SMP=y
+# CONFIG_SMP_ON_UP is not set
+CONFIG_ARM_CPU_TOPOLOGY=y
+# CONFIG_SCHED_MC is not set
+# CONFIG_SCHED_SMT is not set
+CONFIG_HAVE_ARM_SCU=y
+CONFIG_HAVE_ARM_TWD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_NR_CPUS=2
+# CONFIG_HOTPLUG_CPU is not set
+CONFIG_LOCAL_TIMERS=y
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_HAVE_ARCH_PFN_VALID=y
+# CONFIG_HIGHMEM is not set
+CONFIG_HW_PERF_EVENTS=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_COMPACTION is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_CLEANCACHE is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+
+#
+# Boot options
+#
+# CONFIG_USE_OF is not set
+CONFIG_ZBOOT_ROM_TEXT=0x00608000
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_ZBOOT_ROM=y
+CONFIG_CMDLINE=""
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+
+#
+# CPU Power Management
+#
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_SUSPEND is not set
+# CONFIG_PM_RUNTIME is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_ARM_CPU_SUSPEND is not set
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+CONFIG_MTD_RAM=y
+CONFIG_MTD_ROM=y
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_DOCG3 is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_MISC_DEVICES is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_ELANTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_TRACE_SINK is not set
+CONFIG_DEVKMEM=y
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+CONFIG_SERIAL_8250_RSA=y
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_RAMOOPS is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_MUX is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_SMBUS=y
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=m
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_ALGOPCA=m
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+CONFIG_I2C_COMCERTO=y
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_PCA_PLATFORM=m
+# CONFIG_I2C_PXA_PCI is not set
+CONFIG_I2C_SIMTEC=m
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+CONFIG_I2C_PARPORT_LIGHT=m
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_SPI is not set
+CONFIG_SPI_MSPD=y
+CONFIG_COMCERTO_SPI=y
+
+#
+# Miscellaneous I2C Chip support
+#
+CONFIG_EEPROM_AT=y
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+
+#
+# Enable Device Drivers -> PPS to see the PTP clock options.
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB=y
+# CONFIG_SSB_DEBUG is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+CONFIG_DRM=m
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+# CONFIG_FB is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=y
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+
+#
+# Display device support
+#
+CONFIG_DISPLAY_SUPPORT=m
+
+#
+# Display hardware drivers
+#
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+
+#
+# Virtio drivers
+#
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_MMIO is not set
+# CONFIG_STAGING is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_IOMMU_SUPPORT=y
+# CONFIG_VIRT_DRIVERS is not set
+# CONFIG_PM_DEVFREQ is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_FSNOTIFY is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=1
+# CONFIG_JFFS2_FS_WRITEBUFFER is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_LOGFS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_PSTORE is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=2048
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_LOCKUP_DETECTOR is not set
+# CONFIG_HARDLOCKUP_DETECTOR is not set
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_LKDTM is not set
+# CONFIG_FAULT_INJECTION is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_EVENT_POWER_TRACING_DEPRECATED=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+# CONFIG_CRYPTO is not set
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_CRC8 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_XZ_DEC is not set
+# CONFIG_XZ_DEC_BCJ is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+# CONFIG_AVERAGE is not set
+# CONFIG_CORDIC is not set
diff --git a/arch/arm/configs/c2krtsm_defconfig b/arch/arm/configs/c2krtsm_defconfig
new file mode 100644
index 0000000..987f396
--- /dev/null
+++ b/arch/arm/configs/c2krtsm_defconfig
@@ -0,0 +1,1034 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/arm 3.2.2 Kernel Configuration
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
+CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_HAVE_IRQ_WORK=y
+CONFIG_IRQ_WORK=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_FHANDLE is not set
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_HAVE_SPARSE_IRQ=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_IRQ_DOMAIN=y
+# CONFIG_SPARSE_IRQ is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_TREE_RCU=y
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_RCU_TRACE is not set
+CONFIG_RCU_FANOUT=32
+# CONFIG_RCU_FANOUT_EXACT is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+# CONFIG_CGROUP_FREEZER is not set
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_PROC_PID_CPUSET=y
+CONFIG_CGROUP_CPUACCT=y
+# CONFIG_RESOURCE_COUNTERS is not set
+# CONFIG_CGROUP_PERF is not set
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_BLK_CGROUP is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+# CONFIG_SCHED_AUTOGROUP is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_RELAY=y
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EXPERT is not set
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+# CONFIG_EMBEDDED is not set
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_PERF_COUNTERS is not set
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_PROFILING is not set
+CONFIG_TRACEPOINTS=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_USE_GENERIC_SMP_HELPERS=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_STOP_MACHINE=y
+CONFIG_BLOCK=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_INLINE_SPIN_TRYLOCK is not set
+# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK is not set
+# CONFIG_INLINE_SPIN_LOCK_BH is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
+# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
+CONFIG_INLINE_SPIN_UNLOCK=y
+# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
+CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
+# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_READ_TRYLOCK is not set
+# CONFIG_INLINE_READ_LOCK is not set
+# CONFIG_INLINE_READ_LOCK_BH is not set
+# CONFIG_INLINE_READ_LOCK_IRQ is not set
+# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
+CONFIG_INLINE_READ_UNLOCK=y
+# CONFIG_INLINE_READ_UNLOCK_BH is not set
+CONFIG_INLINE_READ_UNLOCK_IRQ=y
+# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
+# CONFIG_INLINE_WRITE_TRYLOCK is not set
+# CONFIG_INLINE_WRITE_LOCK is not set
+# CONFIG_INLINE_WRITE_LOCK_BH is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
+# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
+CONFIG_INLINE_WRITE_UNLOCK=y
+# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
+CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
+# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+CONFIG_MMU=y
+# CONFIG_ARCH_INTEGRATOR is not set
+CONFIG_ARCH_COMCERTO=y
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_BCMRING is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CNS3XXX is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_PRIMA2 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_MXS is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_PICOXCELL is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_EXYNOS is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_TCC_926 is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_ARCH_VT8500 is not set
+# CONFIG_ARCH_ZYNQ is not set
+# CONFIG_ARCH_SUPPORTS_BIG_ENDIAN is not set
+
+#
+# System MMU
+#
+
+#
+# Comcerto Implementation Options
+#
+CONFIG_ARCH_M86XXX=y
+# CONFIG_C2K_EVM is not set
+# CONFIG_C2K_ASIC is not set
+CONFIG_RTSM_C2K=y
+CONFIG_MTD_COMCERTO_NOR=y
+CONFIG_COMCERTO_TDM_CLOCK=y
+CONFIG_COMCERTO_NUM_PCIES=2
+CONFIG_COMCERTO_FP=y
+# CONFIG_COMCERTO_UART0_SUPPORT is not set
+CONFIG_COMCERTO_UART1_SUPPORT=y
+CONFIG_COMCERTO_USB0_SUPPORT=y
+# CONFIG_COMCERTO_USB1_SUPPORT is not set
+CONFIG_COMCERTO_IPSEC_SUPPORT=y
+CONFIG_COMCERTO_SPI_SUPPORT=y
+CONFIG_COMCERTO_FAST_SPI_SUPPORT=y
+CONFIG_COMCERTO_I2C_SUPPORT=y
+CONFIG_COMCERTO_DW_DMA_SUPPORT=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+# CONFIG_SWP_EMULATE is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_OUTER_CACHE=y
+CONFIG_OUTER_CACHE_SYNC=y
+CONFIG_CACHE_L2X0=y
+CONFIG_CACHE_PL310=y
+CONFIG_ARM_L1_CACHE_SHIFT=5
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+CONFIG_CPU_HAS_PMU=y
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+CONFIG_ARM_ERRATA_742230=y
+CONFIG_ARM_ERRATA_742231=y
+CONFIG_PL310_ERRATA_588369=y
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_PL310_ERRATA_727915=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_PL310_ERRATA_753970=y
+CONFIG_ARM_ERRATA_754322=y
+# CONFIG_ARM_ERRATA_754327 is not set
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_PL310_ERRATA_769419=y
+CONFIG_ARM_GIC=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_SMP=y
+# CONFIG_SMP_ON_UP is not set
+CONFIG_ARM_CPU_TOPOLOGY=y
+# CONFIG_SCHED_MC is not set
+# CONFIG_SCHED_SMT is not set
+CONFIG_HAVE_ARM_SCU=y
+CONFIG_HAVE_ARM_TWD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_NR_CPUS=2
+# CONFIG_HOTPLUG_CPU is not set
+CONFIG_LOCAL_TIMERS=y
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_HAVE_ARCH_PFN_VALID=y
+# CONFIG_HIGHMEM is not set
+# CONFIG_HW_PERF_EVENTS is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_COMPACTION is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_CLEANCACHE is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+
+#
+# Boot options
+#
+# CONFIG_USE_OF is not set
+CONFIG_ZBOOT_ROM_TEXT=0x00608000
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_ZBOOT_ROM=y
+CONFIG_CMDLINE=""
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+
+#
+# CPU Power Management
+#
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_SUSPEND is not set
+# CONFIG_PM_RUNTIME is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_ARM_CPU_SUSPEND is not set
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+CONFIG_MTD_RAM=y
+CONFIG_MTD_ROM=y
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_DOCG3 is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_MISC_DEVICES is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_ELANTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_TRACE_SINK is not set
+CONFIG_DEVKMEM=y
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+CONFIG_SERIAL_8250_RSA=y
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_RAMOOPS is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_MUX is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_SMBUS=y
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=m
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_ALGOPCA=m
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_PCA_PLATFORM=m
+# CONFIG_I2C_PXA_PCI is not set
+CONFIG_I2C_SIMTEC=m
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+CONFIG_I2C_PARPORT_LIGHT=m
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_SPI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+
+#
+# Enable Device Drivers -> PPS to see the PTP clock options.
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB=y
+# CONFIG_SSB_DEBUG is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+CONFIG_DRM=m
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+# CONFIG_FB is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=y
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+
+#
+# Display device support
+#
+CONFIG_DISPLAY_SUPPORT=m
+
+#
+# Display hardware drivers
+#
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+
+#
+# Virtio drivers
+#
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_MMIO is not set
+# CONFIG_STAGING is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_IOMMU_SUPPORT=y
+# CONFIG_VIRT_DRIVERS is not set
+# CONFIG_PM_DEVFREQ is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_FSNOTIFY is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=1
+# CONFIG_JFFS2_FS_WRITEBUFFER is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_LOGFS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_PSTORE is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=2048
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_LOCKUP_DETECTOR is not set
+# CONFIG_HARDLOCKUP_DETECTOR is not set
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_LKDTM is not set
+# CONFIG_FAULT_INJECTION is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_EVENT_POWER_TRACING_DEPRECATED=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+# CONFIG_CRYPTO is not set
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_CRC8 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_XZ_DEC is not set
+# CONFIG_XZ_DEC_BCJ is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+# CONFIG_AVERAGE is not set
+# CONFIG_CORDIC is not set
diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h
index 86976d0..293fabf 100644
--- a/arch/arm/include/asm/atomic.h
+++ b/arch/arm/include/asm/atomic.h
@@ -29,6 +29,27 @@
#if __LINUX_ARM_ARCH__ >= 6
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+
+int comcerto_atomic_add(int i, atomic_t *v);
+int comcerto_atomic_cmpxchg(atomic_t *v, int old, int new);
+void comcerto_atomic_clear_mask(unsigned long mask, unsigned long *addr);
+
+
+struct virtual_zone {
+ void *start;
+ void *end;
+};
+
+extern struct virtual_zone arm_dma_zone;
+
+static inline bool is_dma_zone_virtual_address(void *addr)
+{
+ return ((addr < arm_dma_zone.end) && (addr >= arm_dma_zone.start));
+}
+
+#endif
+
/*
* ARMv6 UP and SMP safe atomic ops. We use load exclusive and
* store exclusive to ensure that these are atomic. We may loop
@@ -39,6 +60,13 @@
unsigned long tmp;
int result;
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if (unlikely(is_dma_zone_virtual_address(v))) {
+ comcerto_atomic_add(i, v);
+ return;
+ }
+#endif
+
__asm__ __volatile__("@ atomic_add\n"
"1: ldrex %0, [%3]\n"
" add %0, %0, %4\n"
@@ -55,6 +83,11 @@
unsigned long tmp;
int result;
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if (unlikely(is_dma_zone_virtual_address(v)))
+ return comcerto_atomic_add(i, v);
+#endif
+
smp_mb();
__asm__ __volatile__("@ atomic_add_return\n"
@@ -77,6 +110,13 @@
unsigned long tmp;
int result;
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if (unlikely(is_dma_zone_virtual_address(v))) {
+ comcerto_atomic_add(-i, v);
+ return;
+ }
+#endif
+
__asm__ __volatile__("@ atomic_sub\n"
"1: ldrex %0, [%3]\n"
" sub %0, %0, %4\n"
@@ -93,6 +133,11 @@
unsigned long tmp;
int result;
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if (unlikely(is_dma_zone_virtual_address(v)))
+ return comcerto_atomic_add(-i, v);
+#endif
+
smp_mb();
__asm__ __volatile__("@ atomic_sub_return\n"
@@ -114,6 +159,11 @@
{
unsigned long oldval, res;
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if (unlikely(is_dma_zone_virtual_address(ptr)))
+ return comcerto_atomic_cmpxchg(ptr, old, new);
+#endif
+
smp_mb();
do {
@@ -136,6 +186,13 @@
{
unsigned long tmp, tmp2;
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if (unlikely(is_dma_zone_virtual_address(addr))) {
+ atomic_clear_mask(mask, addr);
+ return;
+ }
+#endif
+
__asm__ __volatile__("@ atomic_clear_mask\n"
"1: ldrex %0, [%3]\n"
" bic %0, %0, %4\n"
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h
index 0e9ce8d..7f02c54 100644
--- a/arch/arm/include/asm/elf.h
+++ b/arch/arm/include/asm/elf.h
@@ -109,7 +109,7 @@
#define ELF_CORE_COPY_TASK_REGS dump_task_regs
#define CORE_DUMP_USE_REGSET
-#define ELF_EXEC_PAGESIZE 4096
+#define ELF_EXEC_PAGESIZE (PAGE_SIZE)
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
use of this is to invoke "./ld.so someprog" to test out a new version of
diff --git a/arch/arm/include/asm/entry-macro-multi.S b/arch/arm/include/asm/entry-macro-multi.S
index 88d6181..f6546a9 100644
--- a/arch/arm/include/asm/entry-macro-multi.S
+++ b/arch/arm/include/asm/entry-macro-multi.S
@@ -4,8 +4,8 @@
* Interrupt handling. Preserves r7, r8, r9
*/
.macro arch_irq_handler_default
- get_irqnr_preamble r6, lr
-1: get_irqnr_and_base r0, r2, r6, lr
+ get_irqnr_preamble r6, r3, lr
+1: get_irqnr_and_base r0, r2, r6, r3, lr
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h
index bbae919..ac9fa8a 100644
--- a/arch/arm/include/asm/fixmap.h
+++ b/arch/arm/include/asm/fixmap.h
@@ -13,7 +13,7 @@
* 0xfffe0000 and 0xfffeffff.
*/
-#define FIXADDR_START 0xfff00000UL
+#define FIXADDR_START 0xfff30000UL
#define FIXADDR_TOP 0xfffe0000UL
#define FIXADDR_SIZE (FIXADDR_TOP - FIXADDR_START)
diff --git a/arch/arm/include/asm/glue-proc.h b/arch/arm/include/asm/glue-proc.h
index e2be7f1..6e94826 100644
--- a/arch/arm/include/asm/glue-proc.h
+++ b/arch/arm/include/asm/glue-proc.h
@@ -256,6 +256,7 @@
#define cpu_dcache_clean_area __glue(CPU_NAME,_dcache_clean_area)
#define cpu_do_switch_mm __glue(CPU_NAME,_switch_mm)
#define cpu_set_pte_ext __glue(CPU_NAME,_set_pte_ext)
+#define cpu_uncache_pte_ext __glue(CPU_NAME,_uncache_pte_ext)
#define cpu_suspend_size __glue(CPU_NAME,_suspend_size)
#define cpu_do_suspend __glue(CPU_NAME,_do_suspend)
#define cpu_do_resume __glue(CPU_NAME,_do_resume)
diff --git a/arch/arm/include/asm/hardware/entry-macro-gic.S b/arch/arm/include/asm/hardware/entry-macro-gic.S
index 74ebc80..56daa10 100644
--- a/arch/arm/include/asm/hardware/entry-macro-gic.S
+++ b/arch/arm/include/asm/hardware/entry-macro-gic.S
@@ -11,7 +11,7 @@
#include <asm/hardware/gic.h>
#ifndef HAVE_GET_IRQNR_PREAMBLE
- .macro get_irqnr_preamble, base, tmp
+ .macro get_irqnr_preamble, base, pending_intr, tmp
ldr \base, =gic_cpu_base_addr
ldr \base, [\base]
.endm
@@ -32,17 +32,61 @@
* valid range for an IRQ (30-1020 inclusive).
*/
- .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
+ .macro get_irqnr_and_base, irqnr, irqstat, base, pending_intr, tmp
+
+#ifdef CONFIG_COMCERTO_MSP
+ ldr \irqstat, [\base, #GIC_CPU_HIGHPRI]
+ bic \irqnr, \irqstat, #0x1c00
+ cmp \irqnr, #33
+ cmpne \irqnr, #66
+ cmpne \irqnr, #87
+ cmpeq \irqnr, \irqnr
+ bne 1001f
+
+ mov \irqnr, \irqnr /* breakpoint here */
+
+ beq 1002f
+
+1001:
+#endif /* CONFIG_COMCERTO_MSP */
ldr \irqstat, [\base, #GIC_CPU_INTACK]
/* bits 12-10 = src CPU, 9-0 = int # */
- ldr \tmp, =1021
bic \irqnr, \irqstat, #0x1c00
+
+#ifdef CONFIG_COMCERTO_MSP
+ cmp \irqnr, #33
+ streq \irqstat, [\base, #GIC_CPU_EOI]
+ ldreq \tmp, =0x2 //2 on bit1
+ ldreq \pending_intr, =COMCERTO_DISTR_INT_SET_PENDING_OFFSET_4
+ streq \tmp, [\pending_intr]
+ beq 1002f
+
+ cmpne \irqnr, #66
+ streq \irqstat, [\base, #GIC_CPU_EOI]
+ ldreq \tmp, =0x4 //4 on bit 2
+ ldreq \pending_intr, =COMCERTO_DISTR_INT_SET_PENDING_OFFSET_8
+ streq \tmp, [\pending_intr]
+ beq 1002f
+
+ cmpne \irqnr, #87
+ streq \irqstat, [\base, #GIC_CPU_EOI]
+ ldreq \tmp, =0x800000 //0x800000 on bit 23
+ ldreq \pending_intr, =COMCERTO_DISTR_INT_SET_PENDING_OFFSET_8
+ streq \tmp, [\pending_intr]
+ beq 1002f
+#endif /* CONFIG_COMCERTO_MSP */
+ ldr \tmp, =1021
cmp \irqnr, #15
cmpcc \irqnr, \irqnr
cmpne \irqnr, \tmp
cmpcs \irqnr, \irqnr
+
+#ifdef CONFIG_COMCERTO_MSP
+1002:
+#endif /* CONFIG_COMCERTO_MSP */
+
.endm
/* We assume that irqstat (the raw value of the IRQ acknowledge
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 3e91f22..7177ea1 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -22,6 +22,7 @@
#define GIC_DIST_CTRL 0x000
#define GIC_DIST_CTR 0x004
+#define GIC_DIST_SECURITY_BIT 0x080
#define GIC_DIST_ENABLE_SET 0x100
#define GIC_DIST_ENABLE_CLEAR 0x180
#define GIC_DIST_PENDING_SET 0x200
diff --git a/arch/arm/include/asm/kexec.h b/arch/arm/include/asm/kexec.h
index c2b9b4b..ddd7fbc 100644
--- a/arch/arm/include/asm/kexec.h
+++ b/arch/arm/include/asm/kexec.h
@@ -10,7 +10,7 @@
/* Maximum address we can use for the control code buffer */
#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)
-#define KEXEC_CONTROL_PAGE_SIZE 4096
+#define KEXEC_CONTROL_PAGE_SIZE (PAGE_SIZE)
#define KEXEC_ARCH KEXEC_ARCH_ARM
diff --git a/arch/arm/include/asm/mach/map.h b/arch/arm/include/asm/mach/map.h
index b36f365..520d5b6 100644
--- a/arch/arm/include/asm/mach/map.h
+++ b/arch/arm/include/asm/mach/map.h
@@ -30,6 +30,8 @@
#define MT_MEMORY_DTCM 12
#define MT_MEMORY_ITCM 13
#define MT_MEMORY_SO 14
+#define MT_MSP 15
+#define MT_MSP_NCNB 16
#ifdef CONFIG_MMU
extern void iotable_init(struct map_desc *, int);
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index a8997d7..a8941af 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -35,28 +35,31 @@
* TASK_SIZE - the maximum size of a user space task.
* TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area
*/
+#include <asm/pgtable-2level.h>
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)
#define TASK_SIZE (UL(CONFIG_PAGE_OFFSET) - UL(0x01000000))
#define TASK_UNMAPPED_BASE (UL(CONFIG_PAGE_OFFSET) / 3)
-
+#else
+#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)
+#define TASK_SIZE ((UL(CONFIG_PAGE_OFFSET) - UL(0x01000000)) & ~(UL((1 << PMD_SHIFT)-1))) // Must be aligned on PMD size (kernel/user space can share same PMD)
+#define TASK_UNMAPPED_BASE (UL(CONFIG_PAGE_OFFSET) / 3)
+#endif
/*
* The maximum size of a 26-bit user space task.
*/
#define TASK_SIZE_26 UL(0x04000000)
+#ifndef CONFIG_COMCERTO_ZONE_DMA_NCNB
/*
* The module space lives between the addresses given by TASK_SIZE
* and PAGE_OFFSET - it must be within 32MB of the kernel text.
*/
#ifndef CONFIG_THUMB2_KERNEL
-#define MODULES_VADDR (PAGE_OFFSET - 16*1024*1024)
+#define MODULES_VADDR (PAGE_OFFSET - 16*1024*1024)
#else
/* smaller range for Thumb-2 symbols relocation (2^24)*/
-#define MODULES_VADDR (PAGE_OFFSET - 8*1024*1024)
-#endif
-
-#if TASK_SIZE > MODULES_VADDR
-#error Top of user space clashes with start of module space
+#define MODULES_VADDR (PAGE_OFFSET - 8*1024*1024)
#endif
/*
@@ -68,6 +71,22 @@
#define MODULES_END (PAGE_OFFSET)
#endif
+#else
+/* Move module space into the hole reserved for MSP/PFE so we can have a bigger DMA zone */
+#define MODULES_END ((COMCERTO_DDR_SHARED_BASE + COMCERTO_DDR_SHARED_SIZE - PLAT_PHYS_OFFSET + PAGE_OFFSET) & PMD_MASK) // convert SHARED_END to virt and align on lower PMD boundary
+
+#ifndef CONFIG_THUMB2_KERNEL
+#define MODULES_VADDR (MODULES_END - 16*1024*1024)
+#else
+#define MODULES_VADDR (MODULES_END - 10*1024*1024) // Relocations will be guaranteed to work as long as kernel size is less than 6MB
+#endif
+
+#endif
+
+#if TASK_SIZE > MODULES_VADDR
+#error Top of user space clashes with start of module space
+#endif
+
/*
* The XIP kernel gets mapped at the bottom of the module vm area.
* Since we use sections to map it, this macro replaces the physical address
diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h
index ca94653..0cb69e04 100644
--- a/arch/arm/include/asm/page.h
+++ b/arch/arm/include/asm/page.h
@@ -11,7 +11,11 @@
#define _ASMARM_PAGE_H
/* PAGE_SHIFT determines the page size */
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#define PAGE_SHIFT 12
+#else
+#define PAGE_SHIFT 16
+#endif
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
diff --git a/arch/arm/include/asm/param.h b/arch/arm/include/asm/param.h
index 8b24bf94..88367aa 100644
--- a/arch/arm/include/asm/param.h
+++ b/arch/arm/include/asm/param.h
@@ -18,7 +18,11 @@
# define HZ 100
#endif
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#define EXEC_PAGESIZE 4096
+#else
+#define EXEC_PAGESIZE 65536
+#endif
#ifndef NOGROUP
#define NOGROUP (-1)
diff --git a/arch/arm/include/asm/pgalloc.h b/arch/arm/include/asm/pgalloc.h
index 3e08fd3..1a9413a 100644
--- a/arch/arm/include/asm/pgalloc.h
+++ b/arch/arm/include/asm/pgalloc.h
@@ -108,8 +108,17 @@
pmdval_t prot)
{
pmdval_t pmdval = (pte + PTE_HWTABLE_OFF) | prot;
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
pmdp[0] = __pmd(pmdval);
pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));
+#else
+ int i, off = 0;
+ for (i = 0; i < LINKED_PMDS; i++) {
+ pmdp[i] = __pmd(pmdval + off);
+ off += 1024; // Each PMD points to a 1kB 2nd-level table
+ }
+
+#endif
flush_pmd_entry(pmdp);
}
diff --git a/arch/arm/include/asm/pgtable-2level-hwdef.h b/arch/arm/include/asm/pgtable-2level-hwdef.h
index 5cfba15..01bbb91 100644
--- a/arch/arm/include/asm/pgtable-2level-hwdef.h
+++ b/arch/arm/include/asm/pgtable-2level-hwdef.h
@@ -65,7 +65,13 @@
/*
* - extended small page/tiny page
*/
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#define PTE_EXT_XN (_AT(pteval_t, 1) << 0) /* v6 */
+#define PTE_EXT_TEX(x) (_AT(pteval_t, (x)) << 6) /* v5 */
+#else
+#define PTE_EXT_XN (_AT(pteval_t, 1) << 15) /* v6 */
+#define PTE_EXT_TEX(x) (_AT(pteval_t, (x)) << 12) /* v5 */
+#endif
#define PTE_EXT_AP_MASK (_AT(pteval_t, 3) << 4)
#define PTE_EXT_AP0 (_AT(pteval_t, 1) << 4)
#define PTE_EXT_AP1 (_AT(pteval_t, 2) << 4)
@@ -73,7 +79,6 @@
#define PTE_EXT_AP_UNO_SRW (PTE_EXT_AP0)
#define PTE_EXT_AP_URO_SRW (PTE_EXT_AP1)
#define PTE_EXT_AP_URW_SRW (PTE_EXT_AP1|PTE_EXT_AP0)
-#define PTE_EXT_TEX(x) (_AT(pteval_t, (x)) << 6) /* v5 */
#define PTE_EXT_APX (_AT(pteval_t, 1) << 9) /* v6 */
#define PTE_EXT_COHERENT (_AT(pteval_t, 1) << 9) /* XScale3 */
#define PTE_EXT_SHARED (_AT(pteval_t, 1) << 10) /* v6 */
diff --git a/arch/arm/include/asm/pgtable-2level-types.h b/arch/arm/include/asm/pgtable-2level-types.h
index 66cb5b0..ad48bf6 100644
--- a/arch/arm/include/asm/pgtable-2level-types.h
+++ b/arch/arm/include/asm/pgtable-2level-types.h
@@ -24,12 +24,17 @@
typedef u32 pteval_t;
typedef u32 pmdval_t;
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#undef STRICT_MM_TYPECHECKS
+#else
+#define STRICT_MM_TYPECHECKS 1
+#endif
#ifdef STRICT_MM_TYPECHECKS
/*
* These are used to make use of C type-checking..
*/
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
typedef struct { pteval_t pte; } pte_t;
typedef struct { pmdval_t pmd; } pmd_t;
typedef struct { pmdval_t pgd[2]; } pgd_t;
@@ -45,6 +50,23 @@
#define __pgprot(x) ((pgprot_t) { (x) } )
#else
+#include <asm/pgtable-2level.h>
+typedef struct { pteval_t pte[16]; } pte_t;
+typedef struct { pmdval_t pmd; } pmd_t;
+typedef struct { pmdval_t pgd[LINKED_PMDS]; } pgd_t;
+typedef struct { pteval_t pgprot; } pgprot_t;
+
+#define pte_val(x) ((x).pte[0])
+#define pmd_val(x) ((x).pmd)
+#define pgd_val(x) ((x).pgd[0])
+#define pgprot_val(x) ((x).pgprot)
+
+#define __pte(x) ((pte_t) { {(x)} } )
+#define __pmd(x) ((pmd_t) { (x) } )
+#define __pgprot(x) ((pgprot_t) { (x) } )
+#endif
+
+#else
/*
* .. while these make it easier on the compiler
*/
diff --git a/arch/arm/include/asm/pgtable-2level.h b/arch/arm/include/asm/pgtable-2level.h
index 470457e..5feb89d 100644
--- a/arch/arm/include/asm/pgtable-2level.h
+++ b/arch/arm/include/asm/pgtable-2level.h
@@ -28,6 +28,15 @@
* which contain the state information Linux needs. We, therefore, end up
* with 512 entries in the "PTE" level.
*
+ * 64k pages support (Mindspeed COMCERTO):
+ * We cheat even more and tell Linux that we have 256 entries in the first
+ * level, each of which is 64 bytes (16 hardware pointers). The 2nd level
+ * contains 16 hardware PTE tables, or 4096 hardware entries. However,
+ * since 64kB pages are done by duplicating 4kB entries, there will only by
+ * 256 entries in the Linux "PTE" level (and the PTE entry will be larger).
+ * All defines are now also derived from the LINKED_PMDS_SHIFT macro, which
+ * determines how many PMDs point into a single 2nd-level table.
+ *
* This leads to the page tables having the following layout:
*
* pgd pte
@@ -68,6 +77,7 @@
* until either the TLB entry is evicted under pressure, or a context
* switch which changes the user space mapping occurs.
*/
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#define PTRS_PER_PTE 512
#define PTRS_PER_PMD 1
#define PTRS_PER_PGD 2048
@@ -82,6 +92,21 @@
*/
#define PMD_SHIFT 21
#define PGDIR_SHIFT 21
+#else
+#define LINKED_PMDS_SHIFT 4
+#define LINKED_PMDS (1 << LINKED_PMDS_SHIFT) /* number of PMDs pointing to the same 2nd-level page */
+#define PTRS_PER_PGD (4096 / LINKED_PMDS) /* one pgdir table contains 4096 entries */
+#define PGDIR_SHIFT (20 + LINKED_PMDS_SHIFT) /* one pgdir entry can map 1MB (2^20) */
+#define PMD_SHIFT (PGDIR_SHIFT)
+#define PTET_SIZE_SHIFT 6 /* a HW PTE entry is 16*4bytes */
+#define PTE_HWTABLE_PTRS (1 << (10 + LINKED_PMDS_SHIFT - PTET_SIZE_SHIFT)) /* one HW PTE table is 1kB (2^10) */
+
+#define PTRS_PER_PTE (PTE_HWTABLE_PTRS)
+#define PTRS_PER_PMD 1
+
+#define PTE_HWTABLE_OFF 32768 //(PTRS_PER_PTE * sizeof(pte_t))
+#define PTE_HWTABLE_SIZE (1 << (10 + LINKED_PMDS_SHIFT))
+#endif
#define PMD_SIZE (1UL << PMD_SHIFT)
#define PMD_MASK (~(PMD_SIZE-1))
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index 9451dce..ef333db 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -183,6 +183,7 @@
#define pmd_present(pmd) (pmd_val(pmd))
#define pmd_bad(pmd) (pmd_val(pmd) & 2)
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#define copy_pmd(pmdpd,pmdps) \
do { \
pmdpd[0] = pmdps[0]; \
@@ -196,10 +197,29 @@
pmdp[1] = __pmd(0); \
clean_pmd_entry(pmdp); \
} while (0)
+#else
+#define copy_pmd(pmdpd,pmdps) \
+ do { \
+ int i; \
+ for(i = 0; i < LINKED_PMDS; i++) \
+ pmdpd[i] = pmdps[i]; \
+ flush_pmd_entry(pmdpd); \
+ } while (0)
+#define pmd_clear(pmdp) \
+ do { \
+ int i; \
+ for(i = 0; i < LINKED_PMDS; i++) \
+ pmdp[i] = __pmd(0); \
+ clean_pmd_entry(pmdp); \
+ } while (0)
+
+#endif
+
+#define PMD_PAGE_ADDR_MASK (~((1 << 10) - 1))
static inline pte_t *pmd_page_vaddr(pmd_t pmd)
{
- return __va(pmd_val(pmd) & PHYS_MASK & (s32)PAGE_MASK);
+ return __va((pmd_val(pmd) & PHYS_MASK & (s32)PMD_PAGE_ADDR_MASK) - PTE_HWTABLE_OFF);
}
#define pmd_page(pmd) pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK))
@@ -229,8 +249,17 @@
#define pte_page(pte) pfn_to_page(pte_pfn(pte))
#define mk_pte(page,prot) pfn_pte(page_to_pfn(page), prot)
-#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
-#define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0)
+#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte_val(pte),ext)
+#define uncache_pte_ext(ptep) cpu_uncache_pte_ext(ptep)
+#define pte_clear(mm,addr,ptep) do {__sync_outer_cache(ptep, __pte(0)); set_pte_ext(ptep, __pte(0), 0); } while (0)
+
+#if !defined(CONFIG_L2X0_INSTRUCTION_ONLY)
+static inline void __sync_outer_cache(pte_t *ptep, pte_t pteval)
+{
+}
+#else
+extern void __sync_outer_cache(pte_t *ptep, pte_t pteval);
+#endif
#if __LINUX_ARM_ARCH__ < 6
static inline void __sync_icache_dcache(pte_t pteval)
@@ -243,6 +272,8 @@
static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pteval)
{
+ __sync_outer_cache(ptep, pteval);
+
if (addr >= TASK_SIZE)
set_pte_ext(ptep, pteval, 0);
else {
@@ -303,7 +334,7 @@
#define __swp_entry(type,offset) ((swp_entry_t) { ((type) << __SWP_TYPE_SHIFT) | ((offset) << __SWP_OFFSET_SHIFT) })
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
-#define __swp_entry_to_pte(swp) ((pte_t) { (swp).val })
+#define __swp_entry_to_pte(swp) ((pte_t) { { (swp).val } })
/*
* It is an error for the kernel to have more swap files than we can
diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h
index 9e92cb20..bbb1d96 100644
--- a/arch/arm/include/asm/proc-fns.h
+++ b/arch/arm/include/asm/proc-fns.h
@@ -65,7 +65,7 @@
* Set a possibly extended PTE. Non-extended PTEs should
* ignore 'ext'.
*/
- void (*set_pte_ext)(pte_t *ptep, pte_t pte, unsigned int ext);
+ void (*set_pte_ext)(pte_t *ptep, pteval_t pte, unsigned int ext);
/* Suspend/resume */
unsigned int suspend_size;
@@ -79,7 +79,8 @@
extern int cpu_do_idle(void);
extern void cpu_dcache_clean_area(void *, int);
extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
-extern void cpu_set_pte_ext(pte_t *ptep, pte_t pte, unsigned int ext);
+extern void cpu_set_pte_ext(pte_t *ptep, pteval_t pte, unsigned int ext);
+extern void cpu_uncache_pte_ext(pte_t *ptep);
extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
/* These three are private to arch/arm/kernel/suspend.c */
@@ -92,6 +93,7 @@
#define cpu_do_idle processor._do_idle
#define cpu_dcache_clean_area processor.dcache_clean_area
#define cpu_set_pte_ext processor.set_pte_ext
+#define cpu_uncache_pte_ext processor.uncache_pte_ext
#define cpu_do_switch_mm processor.switch_mm
/* These three are private to arch/arm/kernel/suspend.c */
diff --git a/arch/arm/include/asm/shmparam.h b/arch/arm/include/asm/shmparam.h
index a5223b3..73cdb5a 100644
--- a/arch/arm/include/asm/shmparam.h
+++ b/arch/arm/include/asm/shmparam.h
@@ -6,7 +6,12 @@
* or page size, whichever is greater since the cache aliases
* every size/ways bytes.
*/
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#define SHMLBA (4 * PAGE_SIZE) /* attach addr a multiple of this */
+#else
+#define SHMLBA (PAGE_SIZE) /* attach addr a multiple of this */
+#endif
+
/*
* Enforce SHMLBA in shmat
diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h
index ef9ffba9..d289c78 100644
--- a/arch/arm/include/asm/smp_twd.h
+++ b/arch/arm/include/asm/smp_twd.h
@@ -24,5 +24,8 @@
void twd_timer_setup(struct clock_event_device *);
void twd_timer_stop(struct clock_event_device *);
+#ifdef CONFIG_LOCAL_TIMERS
+int twd_timer_ack(void);
+#endif
#endif
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 7b5cc8d..7d6a8ac 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -15,8 +15,13 @@
#include <linux/compiler.h>
#include <asm/fpstate.h>
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#define THREAD_SIZE_ORDER 1
#define THREAD_SIZE 8192
+#else
+#define THREAD_SIZE_ORDER 0
+#define THREAD_SIZE 65536
+#endif
#define THREAD_START_SP (THREAD_SIZE - 8)
#ifndef __ASSEMBLY__
diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h
index 02b2f82..49ff231 100644
--- a/arch/arm/include/asm/tlbflush.h
+++ b/arch/arm/include/asm/tlbflush.h
@@ -471,6 +471,7 @@
* these operations. This is typically used when we are removing
* PMD entries.
*/
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
static inline void flush_pmd_entry(void *pmd)
{
const unsigned int __tlb_flag = __cpu_tlb_flags;
@@ -499,6 +500,39 @@
asm("mcr p15, 1, %0, c15, c9, 1 @ L2 flush_pmd"
: : "r" (pmd) : "cc");
}
+#else
+static inline void flush_pmd_entry(void *pmd)
+{
+ const unsigned int __tlb_flag = __cpu_tlb_flags;
+ char *p = (char *)pmd;
+
+ if (tlb_flag(TLB_DCLEAN)) {
+ while (p < ((char *)pmd + (LINKED_PMDS * sizeof(u32)))) { // A PMD contains LINKED_PMDS pointers to the 2nd-level table
+ asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pmd"
+ : : "r" (p) : "cc");
+ p += 32; //Next cache line
+ }
+ }
+
+ if (tlb_flag(TLB_WB))
+ dsb();
+}
+
+static inline void clean_pmd_entry(void *pmd)
+{
+ const unsigned int __tlb_flag = __cpu_tlb_flags;
+ char *p = (char *)pmd;
+
+ if (tlb_flag(TLB_DCLEAN)) {
+ while (p < ((char *)pmd + (LINKED_PMDS * sizeof(u32)))) { // A PMD contains LINKED_PMDS pointers to the 2nd-level table
+ asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pmd"
+ : : "r" (p) : "cc");
+ p += 32; //Next cache line
+ }
+ }
+}
+
+#endif
#undef tlb_flag
#undef always_tlb_flags
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index b145f16..ece0996 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -242,6 +242,19 @@
b 1b
#endif
+__und_fault:
+ @ Correct the PC such that it is pointing at the instruction
+ @ which caused the fault. If the faulting instruction was ARM
+ @ the PC will be pointing at the next instruction, and have to
+ @ subtract 4. Otherwise, it is Thumb, and the PC will be
+ @ pointing at the second half of the Thumb instruction. We
+ @ have to subtract 2.
+ ldr r2, [r0, #S_PC]
+ sub r2, r2, r1
+ str r2, [r0, #S_PC]
+ b do_undefinstr
+ENDPROC(__und_fault)
+
.align 5
__und_svc:
#ifdef CONFIG_KPROBES
@@ -259,25 +272,32 @@
@
@ r0 - instruction
@
-#ifndef CONFIG_THUMB2_KERNEL
+#ifndef CONFIG_THUMB2_KERNEL
ldr r0, [r4, #-4]
#else
+ mov r1, #2
ldrh r0, [r4, #-2] @ Thumb instruction at LR - 2
cmp r0, #0xe800 @ 32-bit instruction if xx >= 0
- ldrhhs r9, [r4] @ bottom 16 bits
- orrhs r0, r9, r0, lsl #16
+ blo __und_svc_fault
+ ldrh r9, [r4] @ bottom 16 bits
+ add r4, r4, #2
+ str r4, [sp, #S_PC]
+ orr r0, r9, r0, lsl #16
#endif
- adr r9, BSYM(1f)
+ adr r9, BSYM(__und_svc_finish)
mov r2, r4
bl call_fpe
+ mov r1, #4 @ PC correction to apply
+__und_svc_fault:
mov r0, sp @ struct pt_regs *regs
- bl do_undefinstr
+ bl __und_fault
@
@ IRQs off again before pulling preserved data off the stack
@
-1: disable_irq_notrace
+__und_svc_finish:
+ disable_irq_notrace
@
@ restore SPSR and restart the instruction
@@ -421,25 +441,33 @@
mov r2, r4
mov r3, r5
+ @ r2 = regs->ARM_pc, which is either 2 or 4 bytes ahead of the
+ @ faulting instruction depending on Thumb mode.
+ @ r3 = regs->ARM_cpsr
@
- @ fall through to the emulation code, which returns using r9 if
- @ it has emulated the instruction, or the more conventional lr
- @ if we are to treat this as a real undefined instruction
- @
- @ r0 - instruction
+ @ The emulation code returns using r9 if it has emulated the
+ @ instruction, or the more conventional lr if we are to treat
+ @ this as a real undefined instruction
@
adr r9, BSYM(ret_from_exception)
- adr lr, BSYM(__und_usr_unknown)
+
tst r3, #PSR_T_BIT @ Thumb mode?
- itet eq @ explicit IT needed for the 1f label
- subeq r4, r2, #4 @ ARM instr at LR - 4
- subne r4, r2, #2 @ Thumb instr at LR - 2
-1: ldreqt r0, [r4]
+ bne __und_usr_thumb
+ sub r4, r2, #4 @ ARM instr at LR - 4
+1: ldrt r0, [r4]
#ifdef CONFIG_CPU_ENDIAN_BE8
- reveq r0, r0 @ little endian instruction
+ rev r0, r0 @ little endian instruction
#endif
- beq call_fpe
+ @ r0 = 32-bit ARM instruction which caused the exception
+ @ r2 = PC value for the following instruction (:= regs->ARM_pc)
+ @ r4 = PC value for the faulting instruction
+ @ lr = 32-bit undefined instruction function
+ adr lr, BSYM(__und_usr_fault_32)
+ b call_fpe
+
+__und_usr_thumb:
@ Thumb instruction
+ sub r4, r2, #2 @ First half of thumb instr at LR - 2
#if CONFIG_ARM_THUMB && __LINUX_ARM_ARCH__ >= 6 && CONFIG_CPU_V7
/*
* Thumb-2 instruction handling. Note that because pre-v6 and >= v6 platforms
@@ -453,7 +481,7 @@
ldr r5, .LCcpu_architecture
ldr r5, [r5]
cmp r5, #CPU_ARCH_ARMv7
- blo __und_usr_unknown
+ blo __und_usr_fault_16 @ 16bit undefined instruction
/*
* The following code won't get run unless the running CPU really is v7, so
* coding round the lack of ldrht on older arches is pointless. Temporarily
@@ -461,15 +489,18 @@
*/
.arch armv6t2
#endif
-2:
- ARM( ldrht r5, [r4], #2 )
- THUMB( ldrht r5, [r4] )
- THUMB( add r4, r4, #2 )
+2: ldrht r5, [r4]
cmp r5, #0xe800 @ 32bit instruction if xx != 0
- blo __und_usr_unknown
-3: ldrht r0, [r4]
+ blo __und_usr_fault_16 @ 16bit undefined instruction
+3: ldrht r0, [r2]
add r2, r2, #2 @ r2 is PC + 2, make it PC + 4
+ str r2, [sp, #S_PC] @ it's a 2x16bit instr, update
orr r0, r0, r5, lsl #16
+ adr lr, BSYM(__und_usr_fault_32)
+ @ r0 = the two 16-bit Thumb instructions which caused the exception
+ @ r2 = PC value for the following Thumb instruction (:= regs->ARM_pc)
+ @ r4 = PC value for the first 16-bit Thumb instruction
+ @ lr = 32bit undefined instruction function
#if __LINUX_ARM_ARCH__ < 7
/* If the target arch was overridden, change it back: */
@@ -480,17 +511,13 @@
#endif
#endif /* __LINUX_ARM_ARCH__ < 7 */
#else /* !(CONFIG_ARM_THUMB && __LINUX_ARM_ARCH__ >= 6 && CONFIG_CPU_V7) */
- b __und_usr_unknown
+ b __und_usr_fault_16
#endif
- UNWIND(.fnend )
+ UNWIND(.fnend)
ENDPROC(__und_usr)
- @
- @ fallthrough to call_fpe
- @
-
/*
- * The out of line fixup for the ldrt above.
+ * The out of line fixup for the ldrt instructions above.
*/
.pushsection .fixup, "ax"
4: mov pc, r9
@@ -521,11 +548,12 @@
* NEON handler code.
*
* Emulators may wish to make use of the following registers:
- * r0 = instruction opcode.
- * r2 = PC+4
+ * r0 = instruction opcode (32-bit ARM or two 16-bit Thumb)
+ * r2 = PC value to resume execution after successful emulation
* r9 = normal "successful" return address
- * r10 = this threads thread_info structure.
+ * r10 = this threads thread_info structure
* lr = unrecognised instruction return address
+ * IRQs disabled, FIQs enabled.
*/
@
@ Fall-through from Thumb-2 __und_usr
@@ -660,12 +688,17 @@
mov pc, lr
ENDPROC(no_fp)
-__und_usr_unknown:
- enable_irq
+__und_usr_fault_32:
+ mov r1, #4
+ b 1f
+__und_usr_fault_16:
+ mov r1, #2
+1: enable_irq
mov r0, sp
adr lr, BSYM(ret_from_exception)
- b do_undefinstr
-ENDPROC(__und_usr_unknown)
+ b __und_fault
+ENDPROC(__und_usr_fault_32)
+ENDPROC(__und_usr_fault_16)
.align 5
__pabt_usr:
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index b2a27b6..aacae19 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -14,7 +14,7 @@
#include <asm/unwind.h>
#include "entry-header.S"
-
+#include <asm/page.h>
.align 5
/*
@@ -583,6 +583,7 @@
*/
sys_mmap2:
#if PAGE_SHIFT > 12
+#define PGOFF_MASK ((1 << (PAGE_SHIFT - 12)) - 1)
tst r5, #PGOFF_MASK
moveq r5, r5, lsr #PAGE_SHIFT - 12
streq r5, [sp, #4]
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
index 9a8531e..39b4bd6 100644
--- a/arch/arm/kernel/entry-header.S
+++ b/arch/arm/kernel/entry-header.S
@@ -108,11 +108,17 @@
movs pc, lr @ return & move spsr_svc into cpsr
.endm
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
.macro get_thread_info, rd
mov \rd, sp, lsr #13
mov \rd, \rd, lsl #13
.endm
-
+#else
+ .macro get_thread_info, rd
+ mov \rd, sp, lsr #16
+ mov \rd, \rd, lsl #16
+ .endm
+#endif
@
@ 32-bit wide "mov pc, reg"
@
@@ -148,11 +154,18 @@
movs pc, lr @ return & move spsr_svc into cpsr
.endm
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
.macro get_thread_info, rd
mov \rd, sp
lsr \rd, \rd, #13
mov \rd, \rd, lsl #13
.endm
+#else
+ .macro get_thread_info, rd
+ mov \rd, sp, lsr #16
+ mov \rd, \rd, lsl #16
+ .endm
+#endif
@
@ 32-bit wide "mov pc, reg"
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 08c82fd..eeb4d84 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -44,9 +44,17 @@
.globl swapper_pg_dir
.equ swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE
-
+/*
+* Mindspeed:
+* Need to break the function in case text offset is too big
+* this is the case when using zone_dma
+* There is probably a more elegant way to to that
+* original code:
+* add \rd, \phys, #TEXT_OFFSET - PG_DIR_SIZE
+*/
.macro pgtbl, rd, phys
- add \rd, \phys, #TEXT_OFFSET - PG_DIR_SIZE
+ ldr \rd, =TEXT_OFFSET - PG_DIR_SIZE
+ add \rd, \phys, \rd
.endm
#ifdef CONFIG_XIP_KERNEL
diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S
index 020e99c..0a589b7 100644
--- a/arch/arm/kernel/sleep.S
+++ b/arch/arm/kernel/sleep.S
@@ -5,6 +5,13 @@
#include <asm/glue-cache.h>
#include <asm/glue-proc.h>
#include <asm/system.h>
+
+#ifdef CONFIG_ARCH_M86XXX
+/*
+ * IRAM Location to restore the banked registers
+ */
+#define SCRATCHPAD_CPU_CONTEXT_LOC_DBG (0xf0802570)
+#endif
.text
/*
@@ -64,10 +71,66 @@
ENDPROC(cpu_resume_mmu)
cpu_resume_after_mmu:
bl cpu_init @ restore the und/abt/irq banked regs
+
+#ifdef CONFIG_ARCH_M86XXX
+ /*
+ * This code is added to restore the banked registers
+ * This restore has to be kept here as it has to happen
+ * after MMU int.
+ */
+
+ /* Restore cpsr and spsr */
+ ldr r7, scratchpad_cpu_context_loc_dbg
+
+ /* Restoring all the banked registers */
+ mrs r0, cpsr
+
+ /* Restore the Undef mode reisters */
+ bic r1, r0, #0x1f
+ orr r1, r1, #0x1b
+ msr cpsr_c, r1
+ ARM ( ldmia r7!, {r13-r14} )
+ ldmia r7!, {r6}
+ msr spsr, r6
+
+ /* Restore the Abort mode reisters */
+ bic r1, r0, #0x1f
+ orr r1, r1, #0x17
+ msr cpsr_c, r1
+ ARM ( ldmia r7!, {r13-r14} )
+ ldmia r7!, {r6}
+ msr spsr, r6
+
+ /* Restore the IRQ mode reisters */
+ bic r1, r0, #0x1f
+ orr r1, r1, #0x12
+ msr cpsr_c, r1
+ ARM ( ldmia r7!, {r13-r14} )
+ ldmia r7!, {r6}
+ msr spsr, r6
+
+ /* Restore the FIQ mode reisters */
+ bic r1, r0, #0x1f
+ orr r1, r1, #0x11
+ msr cpsr_c, r1
+ ARM ( ldmia r7!, {r8-r14} )
+ THUMB ( ldmia r7!, {r8-r12} )
+ ldmia r7!, {r6}
+ msr spsr, r6
+
+ /* Return to the original mode */
+ msr cpsr_c, r0
+#endif
mov r0, #0 @ return zero on success
ldmfd sp!, {r4 - r11, pc}
ENDPROC(cpu_resume_after_mmu)
+
+#ifdef CONFIG_ARCH_M86XXX
+scratchpad_cpu_context_loc_dbg:
+ .word SCRATCHPAD_CPU_CONTEXT_LOC_DBG
+#endif
+
/*
* Note: Yes, part of the following code is located into the .data section.
* This is to allow sleep_save_sp to be accessed with a relative load
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 1d1710e..0f7a254 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -345,7 +345,10 @@
* now.
*/
local_irq_enable();
+
+#ifndef CONFIG_COMCERTO_MSP
local_fiq_enable();
+#endif /* !CONFIG_COMCERTO_MSP */
/*
* OK, it's off to the idle thread for us
diff --git a/arch/arm/kernel/smp_scu.c b/arch/arm/kernel/smp_scu.c
index 8f5dd79..ff84772 100644
--- a/arch/arm/kernel/smp_scu.c
+++ b/arch/arm/kernel/smp_scu.c
@@ -52,6 +52,10 @@
if (scu_ctrl & 1)
return;
+#ifdef CONFIG_SCU_SPECULATIVE_LINE_FILLS
+ scu_ctrl |= (1 << 3);
+#endif
+
scu_ctrl |= 1;
__raw_writel(scu_ctrl, scu_base + SCU_CTRL);
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 160cb16..8380bd1 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -362,18 +362,10 @@
asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
{
- unsigned int correction = thumb_mode(regs) ? 2 : 4;
unsigned int instr;
siginfo_t info;
void __user *pc;
- /*
- * According to the ARM ARM, PC is 2 or 4 bytes ahead,
- * depending whether we're in Thumb mode or not.
- * Correct this offset.
- */
- regs->ARM_pc -= correction;
-
pc = (void __user *)instruction_pointer(regs);
if (processor_mode(regs) == SVC_MODE) {
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index cf73a7f..7c69deb 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -42,6 +42,7 @@
lib-$(CONFIG_ARCH_RPC) += ecard.o io-acorn.o floppydma.o
lib-$(CONFIG_ARCH_SHARK) += io-shark.o
+lib-$(CONFIG_COMCERTO_ZONE_DMA_NCNB) += atomic.o
$(obj)/csumpartialcopy.o: $(obj)/csumpartialcopygeneric.S
$(obj)/csumpartialcopyuser.o: $(obj)/csumpartialcopygeneric.S
diff --git a/arch/arm/lib/atomic.c b/arch/arm/lib/atomic.c
new file mode 100644
index 0000000..b460d00
--- /dev/null
+++ b/arch/arm/lib/atomic.c
@@ -0,0 +1,48 @@
+#include <linux/spinlock.h>
+#include <linux/export.h>
+#include <asm/atomic.h>
+
+static DEFINE_RAW_SPINLOCK(atomic_lock);
+
+struct virtual_zone arm_dma_zone; /* initialized in arch/arm/mm/mmu.c::map_lowmem() */
+EXPORT_SYMBOL(arm_dma_zone);
+
+int comcerto_atomic_add(int i, atomic_t *v)
+{
+ unsigned long flags;
+ int result;
+
+ raw_spin_lock_irqsave(&atomic_lock, flags);
+ v->counter += i;
+ result = v->counter;
+ raw_spin_unlock_irqrestore(&atomic_lock, flags);
+
+ return result;
+}
+EXPORT_SYMBOL(comcerto_atomic_add);
+
+int comcerto_atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+ unsigned long flags;
+ int result;
+
+ raw_spin_lock_irqsave(&atomic_lock, flags);
+ result = v->counter;
+ if (likely(result == old))
+ v->counter = new;
+ raw_spin_unlock_irqrestore(&atomic_lock, flags);
+
+ return result;
+}
+EXPORT_SYMBOL(comcerto_atomic_cmpxchg);
+
+void comcerto_atomic_clear_mask(unsigned long mask, unsigned long *addr)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&atomic_lock, flags);
+ *addr &= ~mask;
+ raw_spin_unlock_irqrestore(&atomic_lock, flags);
+}
+EXPORT_SYMBOL(comcerto_atomic_clear_mask);
+
diff --git a/arch/arm/lib/copy_page.S b/arch/arm/lib/copy_page.S
index 6ee2f67..eb0f586 100644
--- a/arch/arm/lib/copy_page.S
+++ b/arch/arm/lib/copy_page.S
@@ -28,7 +28,7 @@
stmfd sp!, {r4, lr} @ 2
PLD( pld [r1, #0] )
PLD( pld [r1, #L1_CACHE_BYTES] )
- mov r2, #COPY_COUNT @ 1
+ ldr r2, =COPY_COUNT @ 1
ldmia r1!, {r3, r4, ip, lr} @ 4+1
1: PLD( pld [r1, #2 * L1_CACHE_BYTES])
PLD( pld [r1, #3 * L1_CACHE_BYTES])
diff --git a/arch/arm/mach-comcerto/Kconfig b/arch/arm/mach-comcerto/Kconfig
new file mode 100644
index 0000000..3735479
--- /dev/null
+++ b/arch/arm/mach-comcerto/Kconfig
@@ -0,0 +1,289 @@
+if ARCH_COMCERTO
+
+menu "Comcerto Implementation Options"
+
+config ARCH_SUPPORTS_BIG_ENDIAN
+ bool "Architecture supports Big Endian"
+ default n
+
+choice
+ prompt "Comcerto System Type"
+ default CONFIG_ARCH_M86XXX
+
+config ARCH_M86XXX
+ bool "M86xxx"
+ select CPU_V7
+ select ARM_GIC
+ select ARM_ERRATA_743622
+ select ARM_ERRATA_751472
+ select ARM_ERRATA_754322
+ select ARM_ERRATA_764369 if SMP
+ select MTD_COMCERTO_NOR
+ select COMCERTO_UART1_SUPPORT
+ select COMCERTO_PFE_UART_SUPPORT
+ select COMCERTO_USB2_SUPPORT
+ select COMCERTO_USB3_SUPPORT
+ select COMCERTO_USB3_INTERNAL_CLK
+ select COMCERTO_IPSEC_SUPPORT
+ select COMCERTO_I2C_SUPPORT
+ select GENERIC_TIME
+ select GENERIC_CLOCKEVENTS
+ select HAVE_SCHED_CLOCK
+ select COMCERTO_DW_DMA_SUPPORT
+ select CLKDEV_LOOKUP
+ select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_OPP
+ select MIGHT_HAVE_PCI if !RTSM_C2K
+ select PCI_MSI if PCI
+ help
+ Say Y here if you intend to run this kernel with a Comcerto2000 device.
+
+endchoice
+
+
+
+choice
+ prompt "Comcerto Board type"
+ default RTSM_C2K
+
+config C2K_EVM
+ bool "EVM"
+ depends on ARCH_M86XXX
+ help
+ Say Y here if you intend to run this kernel with a C2K EVM.
+
+config C2K_MFCN_EVM
+ bool "MFCN EVM"
+ depends on ARCH_M86XXX
+ help
+ Say Y here if you intend to run this kernel with a C2K MFCN EVM.
+
+config C2K_ASIC
+ bool "ASIC"
+ depends on ARCH_M86XXX
+ select COMCERTO_UART0_SUPPORT
+ help
+ Say Y here if you intend to run this kernel with a C2K ASIC.
+
+config RTSM_C2K
+ bool "RTSM"
+ depends on ARCH_M86XXX
+ help
+ Say Y here if you intend to run this kernel with a C2K RTSM.
+
+endchoice
+
+
+config MTD_COMCERTO_NOR
+ bool
+ default y
+
+config COMCERTO_TDM_CLOCK
+ bool "Comcerto device TDM clock and frame sync control through sysfs"
+ default y
+ help
+ Say Y if you intend to use the Comcerto TDM and be able to change
+ different parameters through sysfs.
+
+config COMCERTO_CSYS_TPI_CLOCK
+ bool "Comcerto device TPI and CSYS clock control "
+ default n
+ help
+ Say Y if you intend to use the Comcerto TPI (Cortex A9 JTAG interface) and CSYS clock (Cortex A9 CoreSight module).
+
+config COMCERTO_PCIE_OCC_CLOCK
+ bool "Comcerto device PCIE OCC clock control "
+ default n
+ help
+ Say Y if you intend to use the Comcerto PCIE OCC clock.
+
+config COMCERTO_SATA_OCC_CLOCK
+ bool "Comcerto device SATA OCC clock control "
+ default n
+ help
+ Say Y if you intend to use the Comcerto SATA OCC clock.
+
+config COMCERTO_SGMII_OCC_CLOCK
+ bool "Comcerto device SGMII OCC clock control "
+ default n
+ help
+ Say Y if you intend to use the Comcerto SGMII OCC clock.
+
+config COMCERTO_MEMBUF
+ bool "Comcerto memory buffer driver"
+ default n
+ help
+ Say Y if you intend to use the memory buffer driver. This driver helps
+ Linux user space applications setup a physical scatter buffer that can be used
+ directly by the PFE. Currently the PFE hold tone generation feature requires
+ this driver to be enabled.
+
+config COMCERTO_NUM_PCIES
+ int "Number of PCIe controllers to be enabled (0-2)"
+ range 0 2
+ depends on ARCH_M86XXX
+ default "2"
+
+config COMCERTO_FP
+ bool "Comcerto Fast Path Support"
+ depends on ARCH_M86XXX
+ default y
+
+config COMCERTO_UART0_SUPPORT
+ bool
+ default n
+
+config COMCERTO_UART1_SUPPORT
+ bool
+ default y
+
+config COMCERTO_USB2_SUPPORT
+ bool
+ default n
+
+config COMCERTO_USB3_SUPPORT
+ bool
+ default n
+
+config COMCERTO_IPSEC_SUPPORT
+ bool
+ default n
+
+config COMCERTO_EPAVIS
+ bool "DPI EPAVIS content inspection driver"
+ default n
+
+config COMCERTO_ELP_SUPPORT
+ bool "Elliptic EPN1802 SPAcc/PKA crypto driver"
+ default y
+
+config COMCERTO_I2C_SUPPORT
+ bool
+ default n
+
+config COMCERTO_DW_DMA_SUPPORT
+ bool
+ default n
+
+config COMCERTO_DDR_ECC_SUPPORT
+ bool "Fix for the Comcerto DDR ECC Support problem"
+ default n
+ help
+ There is a limitation with Denali DDRC Controller when ECC is enabled. It expects that the
+ burst writes of greater than or equal to 16 bytes must start and end at a 16-byte boundary.
+ Saying Y for this option will make the dma memory area to be non-writeable, apart from being
+ non-cacheable so that single write should happen instead of burst.
+
+config COMCERTO_MSP
+ bool "Mindspeed Comcerto VoIP"
+ depends on ARCH_M86XXX
+ select FIQ
+ default y
+
+config COMCERTO_SATA
+ bool "Comcerto SATA"
+ select ATA
+ select ATA_VERBOSE_ERROR
+ select SATA_AHCI_PLATFORM
+ select BLK_DEV_SD
+ select LBDAF
+
+config COMCERTO_IMPROVED_SPLICE
+ bool "Comcerto improved splice call"
+ default n
+ help
+ Say Y if you intend to use the Comcerto improved splice call. When writing data to a file on an
+ ext4 partition, re-organize the splice code to gather all the memcpy calls in one place, instead
+ of allocating pages and releasing pipe buffers one by one. This improves performance and makes it
+ possible to use a DMA engine with scatter-gather in-place of mempcy.
+
+config COMCERTO_SPLICE_USE_MDMA
+ bool "Comcerto splice using MDMA"
+ default n
+ depends on COMCERTO_IMPROVED_SPLICE
+ select COMCERTO_DMA_BASIC
+ help
+ Say Y if you intend to use the Comcerto MDMA engine to replace memcpy calls inside the improved
+ splice call.
+
+config COMCERTO_ZONE_DMA_NCNB
+ bool "Comcerto Zone DMA"
+ select ZONE_DMA
+ default n
+
+config COMCERTO_CUSTOM_SKB_LAYOUT
+ bool "Mindspeed customized skb layout, to reduce NCNB access overhead, when NCNB buffer is used in skb"
+ depends on COMCERTO_ZONE_DMA_NCNB
+ default n
+
+config DSPG_DECT_CSS
+ bool "DSPG DECT CSS"
+ default n
+
+config COMCERTO_USB3_INTERNAL_CLK
+ bool "Comcerto internal clock for USB3"
+ default n
+
+config COMCERTO_AHCI_PROF
+ bool "Comcerto AHCI profiling"
+ bool
+ default n
+
+config COMCERTO_SPLICE_PROF
+ bool "Comcerto splice profiling"
+ depends on COMCERTO_IMPROVED_SPLICE
+ default n
+
+config COMCERTO_MDMA_PROF
+ bool "Comcerto MDMA profiling"
+ depends on COMCERTO_DMA_BASIC
+ default n
+
+config COMCERTO_64K_PAGES
+ bool "Comcerto 64kB pages"
+ default n
+ help
+ Say Y to enable 64kB pages instead of the default 4kB. Memory usage will be increased, but
+ per-page operations will be reduced, and data transfers will become more efficient.
+
+config COMCERTO_CC_OPTIMIZE_O3
+ bool "Compile the kernel with -O3"
+ default n
+ depends on !CC_OPTIMIZE_FOR_SIZE
+ help
+ Compile the kernel with -O3 instead of -O2 option. Resulting kernel can be a bit faster, but
+ it will also be bigger.
+
+config COMCERTO_HW_KERNEL_PAGETABLE
+ bool "Use the 2nd HW page table for kernel memory space"
+ select VMSPLIT_2G
+ default n
+ help
+ By default, Linux uses only one page table at a time, and switches page tables when switching
+ between processes. Say Y to enable the 2nd hardware page table, which will be used to map kernel
+ addresses.
+ Note that because of the way the ARM MMU determines which page table to use, this option can
+ only be enabled with a 2G/2G userspace/kernel memory split.
+
+config COMCERTO_UNCACHED_DMA
+ bool "Make pages uncacheable when doing DMA operations"
+ depends on COMCERTO_HW_KERNEL_PAGETABLE
+ default n
+ help
+ When transferring ownership of a page to a device through the DMA API, make the page uncacheable
+ for the duration of the transfer. This makes the 2nd cache invalidate (that was needed because
+ of the possibility of speculative accesses by the A9 cores) unnecessary, which can increase
+ performance during heavy IO workloads. The page tables will be more complex though, resulting in
+ more TLB misses when a DMA transfer is going on.
+
+config COMCERTO_SPLICE_READ_NOCONTIG
+ bool "Use find_get_pages in splice read code"
+ default n
+ help
+ The splice read code normally calls find_get_pages_contig to retrieve pages mapping the requested
+ portion of the file to read. However, using find_get_pages works just as well, as the splice code
+ is able to handle both situations.
+ Say Y to use find_get_pages instead of find_get_pages_contig, which seems to yield a significant
+ performance improvement in Samba read tests.
+endmenu
+endif
diff --git a/arch/arm/mach-comcerto/Makefile b/arch/arm/mach-comcerto/Makefile
new file mode 100644
index 0000000..9f5cfa0
--- /dev/null
+++ b/arch/arm/mach-comcerto/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-y := gpio.o time.o dma.o clock.o reset.o pm.o sleep.o
+obj-m :=
+obj-n :=
+obj- :=
+
+obj-$(CONFIG_ARCH_M86XXX) += comcerto-2000.o
+obj-$(CONFIG_ARCH_M86XXX) += comcerto-vwd.o
+
+obj-$(CONFIG_ARCH_M86XXX) += serdes-c2000.o
+
+ifeq ($(CONFIG_PCI),y)
+ obj-$(CONFIG_ARCH_M86XXX) += pcie-c2000.o
+endif
+
+obj-$(CONFIG_COMCERTO_MEMBUF) += membuf.o
+obj-$(CONFIG_COMCERTO_TDM_CLOCK) += sysfstdm.o
+obj-$(CONFIG_RTSM_C2K) += board-c2krtsm.o
+obj-$(CONFIG_C2K_EVM) += board-c2kevm.o
+obj-$(CONFIG_C2K_MFCN_EVM) += board-c2kmfcnevm.o
+obj-$(CONFIG_C2K_ASIC) += board-c2kasic.o
+obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+obj-$(CONFIG_COMCERTO_MSP) += msp/
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+obj-$(CONFIG_COMCERTO_CSYS_TPI_CLOCK) += comcerto-tpicsys.o
diff --git a/arch/arm/mach-comcerto/Makefile.boot b/arch/arm/mach-comcerto/Makefile.boot
new file mode 100644
index 0000000..133025b
--- /dev/null
+++ b/arch/arm/mach-comcerto/Makefile.boot
@@ -0,0 +1,8 @@
+ifeq ($(CONFIG_ARCH_M86XXX),y)
+ifeq ($(CONFIG_COMCERTO_ZONE_DMA_NCNB),y)
+ zreladdr-y := 0x04008000
+else
+ zreladdr-y := 0x00008000
+endif
+ params_phys-y := 0x00000100
+endif
diff --git a/arch/arm/mach-comcerto/board-c2kasic.c b/arch/arm/mach-comcerto/board-c2kasic.c
new file mode 100644
index 0000000..9097b28
--- /dev/null
+++ b/arch/arm/mach-comcerto/board-c2kasic.c
@@ -0,0 +1,757 @@
+/*
+ * arch/arm/mach-comcerto/board-c2kasic.c
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <linux/memblock.h>
+#include <linux/phy.h>
+
+#include <linux/mtd/mtd.h>
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+#include <linux/mtd/nand.h>
+#endif
+#include <linux/mtd/partitions.h>
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+#include <linux/spi/spi.h>
+#endif
+
+#include <asm/sizes.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+
+#include <asm/mach/flash.h>
+#include <asm/mach/arch.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/dma.h>
+#include <linux/dw_dmac.h>
+
+#include <linux/clockchips.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/smp_twd.h>
+#include <asm/localtimer.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/time.h>
+#include <mach/gpio.h>
+
+extern void platform_reserve(void);
+extern void device_map_io (void);
+extern void device_irq_init(void);
+extern void device_init(void);
+extern void mac_addr_init(struct comcerto_pfe_platform_data *);
+extern struct sys_timer comcerto_timer;
+
+static void __init board_gpio_init(void)
+{
+#ifdef CONFIG_COMCERTO_PFE_UART_SUPPORT
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG) & ~PFE_UART_GPIO) | PFE_UART_BUS, COMCERTO_GPIO_PIN_SELECT_REG);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= PFE_UART_GPIO_PIN; /* GPIOs 12 & 13 are used for PFE_UART */
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+ /* enable SPI pins */
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_MUX_GPIO_1)) | (SPI_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+ writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_MUX_GPIO_2)) | (SPI_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= SPI_MUX_GPIO_1_PIN; /* GPIOs 18,19, 21,22, 30,31 are used for SPI*/
+ c2k_gpio_pin_stat.c2k_gpio_pins_32_63 |= SPI_MUX_GPIO_2_PIN; /* GPIO 32 is used for SPI*/
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ /* enable SPI pins */
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_2_MUX_GPIO_1)) | (SPI_2_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+ writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_2_MUX_GPIO_2)) | (SPI_2_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= SPI_2_MUX_GPIO_1_PIN;
+ c2k_gpio_pin_stat.c2k_gpio_pins_32_63 |= SPI_2_MUX_GPIO_2_PIN;
+#endif
+
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~I2C_GPIO) | I2C_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= I2C_GPIO_PIN;
+#endif
+
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NAND_GPIO) | NAND_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= NAND_GPIO_PIN;
+#endif
+
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NOR_GPIO) | NOR_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= NOR_GPIO_PIN;
+#endif
+
+ /* TDM bus configuration */
+ /* Fix for C2KASIC RevA. Correct/lower TDM bus strength to avoid noise distorting voice with si3227 */
+ /* TODO: Check on RevB. */
+ /* Setting TDM_CK strangth [7:6] equal X3 */
+ #define X1 0
+ #define X2 2
+ #define X3 1
+ #define X4 3
+ writel((X3 << 6) |(readl(COMCERTO_GPIO_PAD_CONFIG0) & ~(0x3 << 6)), COMCERTO_GPIO_PAD_CONFIG0);
+}
+
+
+/* --------------------------------------------------------------------
+ * NOR device
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+
+static struct resource comcerto_nor_resources[] = {
+ {
+ .start = NORFLASH_MEMORY_PHY1,
+ .end = NORFLASH_MEMORY_PHY1 + SZ_128M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct flash_platform_data comcerto_nor_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+};
+
+static struct platform_device comcerto_nor = {
+ .name = "comcertoflash",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(comcerto_nor_resources),
+ .resource = comcerto_nor_resources,
+ .dev = {
+ .platform_data = &comcerto_nor_data,
+ },
+};
+#endif
+
+static struct resource rtc_res[] = {
+ {
+ .start = COMCERTO_APB_RTC_BASE,
+ .end = COMCERTO_APB_RTC_BASE + SZ_32 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_RTC_ALM,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = IRQ_RTC_PRI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device rtc_dev = {
+ .name = "c2k-rtc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(rtc_res),
+ .resource = rtc_res,
+};
+
+/* --------------------------------------------------------------------
+ * DMAC controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+static struct resource dw_dmac_resource[] = {
+ {
+ .start = DW_DMA_DMAC_BASEADDR,
+ .end = DW_DMA_DMAC_BASEADDR + 0x2C0,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_DMAC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct dw_dma_platform_data dw_dmac_data = {
+ .nr_channels = 8,
+};
+
+static u64 dw_dmac_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device dw_dmac_device = {
+ .name = "dw_dmac",
+ .id = 0,
+ .dev = {
+ .dma_mask = &dw_dmac_dma_mask,
+ .platform_data = &dw_dmac_data,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = dw_dmac_resource,
+ .num_resources = ARRAY_SIZE(dw_dmac_resource),
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * NAND device
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+static struct resource comcerto_nand_resources[] = {
+ {
+ .start = COMCERTO_NAND_FIO_ADDR,
+ .end = COMCERTO_NAND_FIO_ADDR + COMCERTO_NAND_IO_SZ - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device comcerto_nand = {
+ .name = "comcertonand",
+ .id = -1,
+ .dev = {
+ .platform_data = NULL,
+ },
+ .resource = comcerto_nand_resources,
+ .num_resources = ARRAY_SIZE(comcerto_nand_resources),
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * SPI bus controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+
+#define CLK_NAME 10
+struct spi_controller_pdata {
+ int use_dma;
+ int num_chipselects;
+ int bus_num;
+ u32 max_freq;
+ char clk_name[CLK_NAME];
+};
+
+struct spi_platform_data {
+ int type;
+ int dummy;
+};
+
+struct spi_controller_data {
+ u8 poll_mode; /* 0 for contoller polling mode */
+ u8 type; /* SPI/SSP/Micrwire */
+ u8 enable_dma;
+ void (*cs_control)(u32 command);
+};
+
+struct spi_platform_data spi_pdata = {
+ .type = 0,
+ .dummy = 0,
+};
+
+struct spi_controller_data spi_ctrl_data = {
+ .poll_mode = 1,
+};
+
+static struct spi_board_info comcerto_spi_board_info[] = {
+ {
+ /* FIXME: for chipselect-0 */
+ .modalias = "comcerto_spi1",
+ .chip_select = 0,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+ {
+ /* FIXME: for chipselect-1 */
+ .modalias = "proslic",
+ .max_speed_hz = 4*1000*1000,
+ .chip_select = 1,
+ .mode = SPI_MODE_3,
+ .bus_num = 0,
+ .irq = -1,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+ {
+ .modalias = "comcerto_spi3",
+ .chip_select = 2,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+
+#if 0 //MSIF
+
+ {
+ .modalias = "proslic",
+ .max_speed_hz = 2*1000*1000,
+ .chip_select = 3,
+ .mode = SPI_MODE_1,
+ .bus_num = 0,
+ .irq = -1,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+#else
+ {
+ .modalias = "legerity",
+ .chip_select = 3,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+#endif
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+struct spi_controller_pdata fast_spi_pdata = {
+ .use_dma = 0,
+ .num_chipselects = 2,
+ .bus_num = 1,
+ .max_freq = 60 * 1000 * 1000,
+ .clk_name = "DUS",
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI2_MSPD_HIGH_SPEED)
+static struct resource comcerto_fast_spi_resource[] = {
+ {
+ .start = COMCERTO_AXI_SPI_BASE,
+ .end = COMCERTO_AXI_SPI_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_SPI,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device comcerto_fast_spi = {
+ .name = "comcerto_spi",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(comcerto_fast_spi_resource),
+ .resource = comcerto_fast_spi_resource,
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ .dev = {
+ .platform_data = &fast_spi_pdata,
+ },
+#endif
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+struct spi_controller_pdata ls_spi_pdata = {
+ .use_dma = 0,
+ .num_chipselects = 4,
+ .bus_num = 0,
+ .max_freq = 20 * 1000 * 1000,
+ .clk_name = "spi_i2c",
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+static struct resource comcerto_spi_resource[] = {
+ {
+ .start = COMCERTO_APB_SPI_BASE,
+ .end = COMCERTO_APB_SPI_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_SPI_LS,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device comcerto_spi = {
+ .name = "comcerto_spi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(comcerto_spi_resource),
+ .resource = comcerto_spi_resource,
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+ .dev = {
+ .platform_data = &ls_spi_pdata,
+ },
+#endif
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * I2C bus controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+static struct resource comcerto_i2c_resources[] = {
+ {
+ .start = COMCERTO_APB_I2C_BASE,
+ .end = COMCERTO_APB_I2C_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_i2c = {
+ .name = "comcerto_i2c",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_i2c_resources),
+ .resource = comcerto_i2c_resources,
+};
+#endif
+
+/* --------------------------------------------------------------------
+* Watchdog
+* -------------------------------------------------------------------- */
+#ifdef CONFIG_MPCORE_WATCHDOG
+static struct resource comcerto_a9wd_resources[] = {
+ {
+ .start = COMCERTO_TWD_BASE,
+ .end = COMCERTO_TWD_BASE + 0xFF,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "mpcore_wdt",
+ .start = IRQ_LOCALWDOG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_a9wd = {
+ .name = "mpcore_wdt",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_a9wd_resources),
+ .resource = comcerto_a9wd_resources,
+};
+#endif
+
+#ifdef CONFIG_COMCERTO_WATCHDOG
+static struct resource comcerto_wdt_resources[] = {
+ {
+ .start = COMCERTO_APB_TIMER_BASE + 0xD0,
+ .end = COMCERTO_APB_TIMER_BASE + 0xD8,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device comcerto_wdt = {
+ .name = "comcerto_wdt",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_wdt_resources),
+ .resource = comcerto_wdt_resources,
+};
+#endif
+
+#if defined(CONFIG_COMCERTO_ELP_SUPPORT)
+/* --------------------------------------------------------------------
+ * IPsec
+ * -------------------------------------------------------------------- */
+static struct resource comcerto_elp_resources[] = {
+ {
+ .name = "elp",
+ .start = COMCERTO_AXI_SPACC_PDU_BASE,
+ .end = COMCERTO_AXI_SPACC_PDU_BASE + SZ_16M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "irq_spacc",
+ .start = IRQ_SPACC,
+ .end = IRQ_SPACC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 comcerto_elp_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_elp_device = {
+ .name = "Elliptic-EPN1802",
+ .id = 0,
+ .num_resources = 2,
+ .resource = comcerto_elp_resources,
+ .dev = {
+ .dma_mask = &comcerto_elp_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+#endif
+
+
+static struct comcerto_tdm_data comcerto_tdm_pdata = {
+ .fsoutput = 1, /* Generic Pad Control and Version ID Register[2] */
+ .fspolarity = 0, /* 28 FSYNC_FALL(RISE)_EDGE */
+ .fshwidth = 1, /* High_Phase_Width[10:0] */
+ .fslwidth = 0xFF, /* Low_Phase_Width[10:0] */
+ .clockhz = 2048000, /* INC_VALUE[29:0] According to the desired TDM clock output
+ frequency, this field should be configured */
+ .clockout = 1, /* 0 -> set bit 21, clear bit 20 in COMCERTO_GPIO_IOCTRL_REG
+ (software control, clock input)
+ 1 -> set bit 21 and 20 in COMCERTO_GPIO_IOCTRL_REG
+ (software control, clock output)
+ 2 -> clear bit 21 in COMCERTO_GPIO_IOCTRL_REG (hardware control) */
+ .tdmmux = 0x1, /* TDM interface Muxing:0x0 - TDM block, 0x1 - ZDS block,
+ 0x2 - GPIO[63:60] signals and 0x3 - MSIF block is selected */
+#if 0
+ /* FIX ME - Need correct values for TDM_DR, TDM_DX, TDM_FS and TDM_CK */
+ .tdmck = 0x3F,
+ .tdmfs = 0x3F,
+ .tdmdx = 0x3F,
+ .tdmdr = 0x3F,
+#endif
+};
+
+static struct platform_device comcerto_tdm_device = {
+ .name = "comcerto-tdm",
+ .id = 0,
+ .dev.platform_data = &comcerto_tdm_pdata,
+ .num_resources = 0,
+ .resource = NULL,
+};
+
+#if defined(CONFIG_DSPG_DECT_CSS)
+#define CSS_ITCM_BASE COMCERTO_AXI_DECT_BASE
+#define CSS_ITCM_SIZE (SZ_1M)
+
+#define CSS_DTCM_BASE (CSS_ITCM_BASE + CSS_ITCM_SIZE)
+#define CSS_DTCM_SIZE (SZ_1M)
+
+static struct resource comcerto_css_resources[] = {
+ {
+ .name = "itcm",
+ .start = CSS_ITCM_BASE,
+ .end = CSS_ITCM_BASE + CSS_ITCM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "dtcm",
+ .start = CSS_DTCM_BASE,
+ .end = CSS_DTCM_BASE + CSS_DTCM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device comcerto_css_device = {
+ .name = "css",
+ .id = 0,
+ .dev = {
+ .platform_data = 0,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(comcerto_css_resources),
+ .resource = comcerto_css_resources,
+};
+#endif
+
+static struct resource comcerto_pfe_resources[] = {
+ {
+ .name = "apb",
+ .start = COMCERTO_APB_PFE_BASE,
+ .end = COMCERTO_APB_PFE_BASE + COMCERTO_APB_PFE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "axi",
+ .start = COMCERTO_AXI_PFE_BASE,
+ .end = COMCERTO_AXI_PFE_BASE + COMCERTO_AXI_PFE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "ddr",
+ .start = COMCERTO_PFE_DDR_BASE,
+ .end = COMCERTO_PFE_DDR_BASE + COMCERTO_PFE_DDR_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "iram",
+ .start = COMCERTO_PFE_IRAM_BASE,
+ .end = COMCERTO_PFE_IRAM_BASE + COMCERTO_PFE_IRAM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "ipsec",
+ .start = COMCERTO_AXI_IPSEC_BASE,
+ .end = COMCERTO_AXI_IPSEC_BASE + COMCERTO_AXI_IPSEC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "hif",
+ .start = IRQ_PFE_HIF,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct comcerto_pfe_platform_data comcerto_pfe_pdata = {
+ .comcerto_eth_pdata[0] = {
+ .name = GEM0_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_PHY_RGMII_ADD_DELAY,
+ .bus_id = 0,
+ .phy_id = 4,
+ .gem_id = 0,
+ .mac_addr = (u8[])GEM0_MAC,
+ },
+
+ .comcerto_eth_pdata[1] = {
+ .name = GEM1_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_PHY_RGMII_ADD_DELAY,
+ .bus_id = 0,
+ .phy_id = 5,
+ .gem_id = 1,
+ .mac_addr = (u8[])GEM1_MAC,
+ },
+
+ .comcerto_eth_pdata[2] = {
+ .name = GEM2_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_PHY_RGMII_ADD_DELAY,
+ .bus_id = 0,
+ .phy_id = 6,
+ .gem_id = 2,
+ .mac_addr = (u8[])GEM2_MAC,
+ },
+
+ /**
+ * There is a single mdio bus coming out of C2K. And that's the one
+ * connected to GEM0. All PHY's, switchs will be connected to the same
+ * bus using different addresses. Typically .bus_id is always 0, only
+ * .phy_id will change in the different comcerto_eth_pdata[] structures above.
+ */
+ .comcerto_mdio_pdata[0] = {
+ .enabled = 1,
+ .phy_mask = 0xFFFFFF8F,
+ .mdc_div = 96,
+ .irq = {
+ [4] = PHY_POLL,
+ [5] = PHY_POLL,
+ [6] = PHY_POLL,
+ },
+ },
+};
+
+static u64 comcerto_pfe_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_pfe_device = {
+ .name = "pfe",
+ .id = 0,
+ .dev = {
+ .platform_data = &comcerto_pfe_pdata,
+ .dma_mask = &comcerto_pfe_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(comcerto_pfe_resources),
+ .resource = comcerto_pfe_resources,
+};
+
+static struct platform_device *comcerto_devices[] __initdata = {
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+ &comcerto_nand,
+#endif
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+ &comcerto_nor,
+#endif
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+ &comcerto_i2c,
+#endif
+
+#if defined (CONFIG_MPCORE_WATCHDOG)
+ &comcerto_a9wd,
+#endif
+
+#if defined(CONFIG_COMCERTO_WATCHDOG)
+ &comcerto_wdt,
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI2_MSPD_HIGH_SPEED)
+ &comcerto_fast_spi,
+#endif
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+ &comcerto_spi,
+#endif
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+ &dw_dmac_device,
+#endif
+ &comcerto_tdm_device,
+ &comcerto_pfe_device,
+ &rtc_dev,
+#if defined(CONFIG_DSPG_DECT_CSS)
+ &comcerto_css_device,
+#endif
+#if defined(CONFIG_COMCERTO_ELP_SUPPORT)
+ &comcerto_elp_device,
+#endif
+};
+
+
+/************************************************************************
+ * Expansion bus
+ *
+ ************************************************************************/
+/* This variable is used by comcerto-2000.c to initialize the expansion bus */
+int comcerto_exp_values[5][7]= {
+ /* ENABLE, BASE, SEG_SZ, CFG, TMG1, TMG2, TMG3 */
+ {1, (EXP_BUS_REG_BASE_CS0 >> 12), ((EXP_BUS_REG_BASE_CS0 + EXP_CS0_SEG_SIZE - 1) >> 12), EXP_MEM_BUS_SIZE_16, 0x1A1A401F, 0x06060A04, 0x00000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS1 >> 12), ((EXP_BUS_REG_BASE_CS1 + EXP_CS1_SEG_SIZE - 1) >> 12), EXP_RDY_EN|EXP_MEM_BUS_SIZE_32, 0x1A1A401F, 0x06060A04, 0x00000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS2 >> 12), ((EXP_BUS_REG_BASE_CS2 + EXP_CS2_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS3 >> 12), ((EXP_BUS_REG_BASE_CS3 + EXP_CS3_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002}, /*BT8370*/
+ {0, (EXP_BUS_REG_BASE_CS4 >> 12), ((EXP_BUS_REG_BASE_CS4 + EXP_CS4_SEG_SIZE - 1) >> 12), EXP_NAND_MODE|EXP_MEM_BUS_SIZE_8, 0x1A1A401F, 0x06060A04, 0x00000002}, /* NAND: TODO Values to check */
+};
+
+/************************************************************************
+ * Machine definition
+ *
+ ************************************************************************/
+static void __init platform_map_io(void)
+{
+ device_map_io();
+}
+
+static void __init platform_irq_init(void)
+{
+ device_irq_init();
+}
+
+static void __init platform_init(void)
+{
+ device_init();
+ board_gpio_init();
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI_MSPD_LOW_SPEED)
+ spi_register_board_info(comcerto_spi_board_info, ARRAY_SIZE(comcerto_spi_board_info));
+#endif
+ mac_addr_init(&comcerto_pfe_pdata);
+
+ platform_add_devices(comcerto_devices, ARRAY_SIZE(comcerto_devices));
+}
+
+MACHINE_START(COMCERTO, "Comcerto 2000 ASIC")
+ /* Mindspeed Technologies Inc. */
+ .atag_offset = COMCERTO_AXI_DDR_BASE + 0x100,
+ .reserve = platform_reserve,
+ .map_io = platform_map_io,
+ .init_irq = platform_irq_init,
+ .init_machine = platform_init,
+ .timer = &comcerto_timer,
+#ifdef CONFIG_ZONE_DMA
+ .dma_zone_size = SZ_32M + 3*SZ_4M, /* Up to start of memory reserved for PFE+MSP */
+#endif
+MACHINE_END
diff --git a/arch/arm/mach-comcerto/board-c2kevm.c b/arch/arm/mach-comcerto/board-c2kevm.c
new file mode 100644
index 0000000..d944636
--- /dev/null
+++ b/arch/arm/mach-comcerto/board-c2kevm.c
@@ -0,0 +1,743 @@
+/*
+ * arch/arm/mach-comcerto/board-c2kevm.c
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <linux/memblock.h>
+#include <linux/phy.h>
+
+#include <linux/mtd/mtd.h>
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+#include <linux/mtd/nand.h>
+#endif
+#include <linux/mtd/partitions.h>
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+#include <linux/spi/spi.h>
+#endif
+
+#include <asm/sizes.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+
+#include <asm/mach/flash.h>
+#include <asm/mach/arch.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/dma.h>
+#include <linux/dw_dmac.h>
+
+#include <linux/clockchips.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/smp_twd.h>
+#include <asm/localtimer.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/time.h>
+#include <mach/gpio.h>
+
+
+extern void platform_reserve(void);
+extern void device_map_io (void);
+extern void device_irq_init(void);
+extern void device_init(void);
+extern void mac_addr_init(struct comcerto_pfe_platform_data *);
+extern struct sys_timer comcerto_timer;
+
+static void __init board_gpio_init(void)
+{
+#ifdef CONFIG_COMCERTO_PFE_UART_SUPPORT
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG) & ~PFE_UART_GPIO) | PFE_UART_BUS, COMCERTO_GPIO_PIN_SELECT_REG);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= PFE_UART_GPIO_PIN; /* GPIOs 12 & 13 are used for PFE_UART */
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+ /* enable SPI pins */
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_MUX_GPIO_1)) | (SPI_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+ writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_MUX_GPIO_2)) | (SPI_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= SPI_MUX_GPIO_1_PIN; /* GPIOs 18,19, 21,22, 30,31 are used for SPI*/
+ c2k_gpio_pin_stat.c2k_gpio_pins_32_63 |= SPI_MUX_GPIO_2_PIN; /* GPIO 32 is used for SPI*/
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ /* enable SPI pins */
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_2_MUX_GPIO_1)) | (SPI_2_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+ writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_2_MUX_GPIO_2)) | (SPI_2_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= SPI_2_MUX_GPIO_1_PIN;
+ c2k_gpio_pin_stat.c2k_gpio_pins_32_63 |= SPI_2_MUX_GPIO_2_PIN;
+#endif
+
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~I2C_GPIO) | I2C_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= I2C_GPIO_PIN;
+#endif
+
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NAND_GPIO) | NAND_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= NAND_GPIO_PIN;
+#endif
+
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NOR_GPIO) | NOR_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= NOR_GPIO_PIN;
+#endif
+}
+
+/* --------------------------------------------------------------------
+ * NOR device
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+
+static struct resource comcerto_nor_resources[] = {
+ {
+ .start = NORFLASH_MEMORY_PHY1,
+ .end = NORFLASH_MEMORY_PHY1 + SZ_64M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct flash_platform_data comcerto_nor_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+};
+
+static struct platform_device comcerto_nor = {
+ .name = "comcertoflash",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(comcerto_nor_resources),
+ .resource = comcerto_nor_resources,
+ .dev = {
+ .platform_data = &comcerto_nor_data,
+ },
+};
+#endif
+
+static struct resource rtc_res[] = {
+ {
+ .start = COMCERTO_APB_RTC_BASE,
+ .end = COMCERTO_APB_RTC_BASE + SZ_32 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_RTC_ALM,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = IRQ_RTC_PRI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device rtc_dev = {
+ .name = "c2k-rtc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(rtc_res),
+ .resource = rtc_res,
+};
+
+/* --------------------------------------------------------------------
+ * DMAC controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+static struct resource dw_dmac_resource[] = {
+ {
+ .start = DW_DMA_DMAC_BASEADDR,
+ .end = DW_DMA_DMAC_BASEADDR + 0x2C0,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_DMAC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct dw_dma_platform_data dw_dmac_data = {
+ .nr_channels = 8,
+};
+
+static u64 dw_dmac_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device dw_dmac_device = {
+ .name = "dw_dmac",
+ .id = 0,
+ .dev = {
+ .dma_mask = &dw_dmac_dma_mask,
+ .platform_data = &dw_dmac_data,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = dw_dmac_resource,
+ .num_resources = ARRAY_SIZE(dw_dmac_resource),
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * NAND device
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+static struct resource comcerto_nand_resources[] = {
+ {
+ .start = COMCERTO_NAND_FIO_ADDR,
+ .end = COMCERTO_NAND_FIO_ADDR + COMCERTO_NAND_IO_SZ - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device comcerto_nand = {
+ .name = "comcertonand",
+ .id = -1,
+ .dev = {
+ .platform_data = NULL,
+ },
+ .resource = comcerto_nand_resources,
+ .num_resources = ARRAY_SIZE(comcerto_nand_resources),
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * SPI bus controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+
+#define CLK_NAME 10
+struct spi_controller_pdata {
+ int use_dma;
+ int num_chipselects;
+ int bus_num;
+ u32 max_freq;
+ char clk_name[CLK_NAME];
+};
+
+struct spi_platform_data {
+ int type;
+ int dummy;
+};
+
+struct spi_controller_data {
+ u8 poll_mode; /* 0 for contoller polling mode */
+ u8 type; /* SPI/SSP/Micrwire */
+ u8 enable_dma;
+ void (*cs_control)(u32 command);
+};
+
+struct spi_platform_data spi_pdata = {
+ .type = 0,
+ .dummy = 0,
+};
+
+struct spi_controller_data spi_ctrl_data = {
+ .poll_mode = 1,
+};
+
+static struct spi_board_info comcerto_spi_board_info[] = {
+ {
+ /* FIXME: for chipselect-0 */
+ .modalias = "s25fl256s0",
+ .chip_select = 0,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+ {
+ /* FIXME: for chipselect-1 */
+ .modalias = "proslic",
+ .max_speed_hz = 4*1000*1000,
+ .chip_select = 1,
+ .mode = SPI_MODE_3,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+ {
+ .modalias = "comcerto_spi3",
+ .chip_select = 2,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+
+#if 0 //MSIF
+
+ {
+ .modalias = "proslic",
+ .max_speed_hz = 2*1000*1000,
+ .chip_select = 3,
+ .mode = SPI_MODE_1,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+#else
+ {
+ .modalias = "legerity",
+ .chip_select = 3,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+#endif
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+struct spi_controller_pdata fast_spi_pdata = {
+ .use_dma = 0,
+ .num_chipselects = 2,
+ .bus_num = 1,
+ .max_freq = 60 * 1000 * 1000,
+ .clk_name = "DUS",
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI2_MSPD_HIGH_SPEED)
+static struct resource comcerto_fast_spi_resource[] = {
+ {
+ .start = COMCERTO_AXI_SPI_BASE,
+ .end = COMCERTO_AXI_SPI_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_SPI,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device comcerto_fast_spi = {
+ .name = "comcerto_spi",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(comcerto_fast_spi_resource),
+ .resource = comcerto_fast_spi_resource,
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ .dev = {
+ .platform_data = &fast_spi_pdata,
+ },
+#endif
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+struct spi_controller_pdata ls_spi_pdata = {
+ .use_dma = 0,
+ .num_chipselects = 4,
+ .bus_num = 0,
+ .max_freq = 20 * 1000 * 1000,
+ .clk_name = "spi_i2c",
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+static struct resource comcerto_spi_resource[] = {
+ {
+ .start = COMCERTO_APB_SPI_BASE,
+ .end = COMCERTO_APB_SPI_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_SPI_LS,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device comcerto_spi = {
+ .name = "comcerto_spi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(comcerto_spi_resource),
+ .resource = comcerto_spi_resource,
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+ .dev = {
+ .platform_data = &ls_spi_pdata,
+ },
+#endif
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * I2C bus controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+static struct resource comcerto_i2c_resources[] = {
+ {
+ .start = COMCERTO_APB_I2C_BASE,
+ .end = COMCERTO_APB_I2C_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_i2c = {
+ .name = "comcerto_i2c",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_i2c_resources),
+ .resource = comcerto_i2c_resources,
+};
+#endif
+
+/* --------------------------------------------------------------------
+* Watchdog
+* -------------------------------------------------------------------- */
+#ifdef CONFIG_MPCORE_WATCHDOG
+static struct resource comcerto_a9wd_resources[] = {
+ {
+ .start = COMCERTO_TWD_BASE,
+ .end = COMCERTO_TWD_BASE + 0xFF,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "mpcore_wdt",
+ .start = IRQ_LOCALWDOG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_a9wd = {
+ .name = "mpcore_wdt",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_a9wd_resources),
+ .resource = comcerto_a9wd_resources,
+};
+#endif
+
+#ifdef CONFIG_COMCERTO_WATCHDOG
+static struct resource comcerto_wdt_resources[] = {
+ {
+ .start = COMCERTO_APB_TIMER_BASE + 0xD0,
+ .end = COMCERTO_APB_TIMER_BASE + 0xD8,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device comcerto_wdt = {
+ .name = "comcerto_wdt",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_wdt_resources),
+ .resource = comcerto_wdt_resources,
+};
+#endif
+
+#if defined(CONFIG_COMCERTO_ELP_SUPPORT)
+/* --------------------------------------------------------------------
+ * IPsec
+ * -------------------------------------------------------------------- */
+static struct resource comcerto_elp_resources[] = {
+ {
+ .name = "elp",
+ .start = COMCERTO_AXI_SPACC_PDU_BASE,
+ .end = COMCERTO_AXI_SPACC_PDU_BASE + SZ_16M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "irq_spacc",
+ .start = IRQ_SPACC,
+ .end = IRQ_SPACC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 comcerto_elp_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_elp_device = {
+ .name = "Elliptic-EPN1802",
+ .id = 0,
+ .num_resources = 2,
+ .resource = comcerto_elp_resources,
+ .dev = {
+ .dma_mask = &comcerto_elp_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+#endif
+
+static struct comcerto_tdm_data comcerto_tdm_pdata = {
+ .fsoutput = 1, /* Generic Pad Control and Version ID Register[2] */
+ .fspolarity = 0, /* 28 FSYNC_FALL(RISE)_EDGE */
+ .fshwidth = 1, /* High_Phase_Width[10:0] */
+ .fslwidth = 0xFF, /* Low_Phase_Width[10:0] */
+ .clockhz = 2048000, /* INC_VALUE[29:0] According to the desired TDM clock output \
+ frequency, this field should be configured */
+ .clockout = 1, /* 0 -> set bit 21, clear bit 20 in COMCERTO_GPIO_IOCTRL_REG
+ (software control, clock input)
+ 1 -> set bit 21 and 20 in COMCERTO_GPIO_IOCTRL_REG
+ (software control, clock output)
+ 2 -> clear bit 21 in COMCERTO_GPIO_IOCTRL_REG (hardware control) */
+ .tdmmux = 0x1, /* TDM interface Muxing:0x0 - TDM block, 0x1 - ZDS block,
+ 0x2 - GPIO[63:60] signals and 0x3 - MSIF block is selected */
+#if 0
+ /* FIX ME - Need correct values for TDM_DR, TDM_DX, TDM_FS and TDM_CK */
+ .tdmck = 0x3F,
+ .tdmfs = 0x3F,
+ .tdmdx = 0x3F,
+ .tdmdr = 0x3F,
+#endif
+};
+
+static struct platform_device comcerto_tdm_device = {
+ .name = "comcerto-tdm",
+ .id = 0,
+ .dev.platform_data = &comcerto_tdm_pdata,
+ .num_resources = 0,
+ .resource = NULL,
+};
+
+#if defined(CONFIG_DSPG_DECT_CSS)
+#define CSS_ITCM_BASE COMCERTO_AXI_DECT_BASE
+#define CSS_ITCM_SIZE (SZ_1M)
+
+#define CSS_DTCM_BASE (CSS_ITCM_BASE + CSS_ITCM_SIZE)
+#define CSS_DTCM_SIZE (SZ_1M)
+
+static struct resource comcerto_css_resources[] = {
+ {
+ .name = "itcm",
+ .start = CSS_ITCM_BASE,
+ .end = CSS_ITCM_BASE + CSS_ITCM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "dtcm",
+ .start = CSS_DTCM_BASE,
+ .end = CSS_DTCM_BASE + CSS_DTCM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device comcerto_css_device = {
+ .name = "css",
+ .id = 0,
+ .dev = {
+ .platform_data = 0,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(comcerto_css_resources),
+ .resource = comcerto_css_resources,
+};
+#endif
+
+static struct resource comcerto_pfe_resources[] = {
+ {
+ .name = "apb",
+ .start = COMCERTO_APB_PFE_BASE,
+ .end = COMCERTO_APB_PFE_BASE + COMCERTO_APB_PFE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "axi",
+ .start = COMCERTO_AXI_PFE_BASE,
+ .end = COMCERTO_AXI_PFE_BASE + COMCERTO_AXI_PFE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "ddr",
+ .start = COMCERTO_PFE_DDR_BASE,
+ .end = COMCERTO_PFE_DDR_BASE + COMCERTO_PFE_DDR_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "iram",
+ .start = COMCERTO_PFE_IRAM_BASE,
+ .end = COMCERTO_PFE_IRAM_BASE + COMCERTO_PFE_IRAM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "ipsec",
+ .start = COMCERTO_AXI_IPSEC_BASE,
+ .end = COMCERTO_AXI_IPSEC_BASE + COMCERTO_AXI_IPSEC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+
+ {
+ .name = "hif",
+ .start = IRQ_PFE_HIF,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct comcerto_pfe_platform_data comcerto_pfe_pdata = {
+ .comcerto_eth_pdata[0] = {
+ .name = GEM0_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_PHY_RGMII_ADD_DELAY,
+ .bus_id = 0,
+ .phy_id = 4,
+ .gem_id = 0,
+ .mac_addr = (u8[])GEM0_MAC,
+ },
+
+ .comcerto_eth_pdata[1] = {
+ .name = GEM1_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_NO_PHY,
+ .gem_id = 1,
+ .mac_addr = (u8[])GEM1_MAC,
+ },
+
+ .comcerto_eth_pdata[2] = {
+ .name = GEM2_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_NO_PHY,
+ .gem_id = 2,
+ .mac_addr = (u8[])GEM2_MAC,
+ },
+
+ /**
+ * There is a single mdio bus coming out of C2K. And that's the one
+ * connected to GEM0. All PHY's, switchs will be connected to the same
+ * bus using different addresses. Typically .bus_id is always 0, only
+ * .phy_id will change in the different comcerto_eth_pdata[] structures above.
+ */
+ .comcerto_mdio_pdata[0] = {
+ .enabled = 1,
+ .phy_mask = 0xFFFFFFEF,
+ .mdc_div = 96,
+ .irq = {
+ [4] = PHY_POLL,
+ },
+ },
+};
+
+static u64 comcerto_pfe_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_pfe_device = {
+ .name = "pfe",
+ .id = 0,
+ .dev = {
+ .platform_data = &comcerto_pfe_pdata,
+ .dma_mask = &comcerto_pfe_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(comcerto_pfe_resources),
+ .resource = comcerto_pfe_resources,
+};
+
+static struct platform_device *comcerto_devices[] __initdata = {
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+ &comcerto_nand,
+#endif
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+ &comcerto_nor,
+#endif
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+ &comcerto_i2c,
+#endif
+
+#if defined (CONFIG_MPCORE_WATCHDOG)
+ &comcerto_a9wd,
+#endif
+
+#if defined(CONFIG_COMCERTO_WATCHDOG)
+ &comcerto_wdt,
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI2_MSPD_HIGH_SPEED)
+ &comcerto_fast_spi,
+#endif
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+ &comcerto_spi,
+#endif
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+ &dw_dmac_device,
+#endif
+ &comcerto_tdm_device,
+ &comcerto_pfe_device,
+ &rtc_dev,
+#if defined(CONFIG_DSPG_DECT_CSS)
+ &comcerto_css_device,
+#endif
+#if defined(CONFIG_COMCERTO_ELP_SUPPORT)
+ &comcerto_elp_device,
+#endif
+};
+
+
+/************************************************************************
+ * Expansion bus
+ *
+ ************************************************************************/
+/* This variable is used by comcerto-2000.c to initialize the expansion bus */
+int comcerto_exp_values[5][7]= {
+ /* ENABLE, BASE, SEG_SZ, CFG, TMG1, TMG2, TMG3 */
+ {1, (EXP_BUS_REG_BASE_CS0 >> 12), ((EXP_BUS_REG_BASE_CS0 + EXP_CS0_SEG_SIZE - 1) >> 12), EXP_MEM_BUS_SIZE_16, 0x03034007, 0x04040502, 0x00000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS1 >> 12), ((EXP_BUS_REG_BASE_CS1 + EXP_CS1_SEG_SIZE - 1) >> 12), EXP_RDY_EN|EXP_MEM_BUS_SIZE_32, 0x1A1A401F, 0x06060A04, 0x00000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS2 >> 12), ((EXP_BUS_REG_BASE_CS2 + EXP_CS2_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS3 >> 12), ((EXP_BUS_REG_BASE_CS3 + EXP_CS3_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002}, /*BT8370*/
+ {1, (EXP_BUS_REG_BASE_CS4 >> 12), ((EXP_BUS_REG_BASE_CS4 + EXP_CS4_SEG_SIZE - 1) >> 12), EXP_NAND_MODE|EXP_MEM_BUS_SIZE_8, 0x00000001, 0x01010001, 0x00000002}, /* NAND: TODO Values to check */
+};
+
+/************************************************************************
+ * Machine definition
+ *
+ ************************************************************************/
+static void __init platform_map_io(void)
+{
+ device_map_io();
+}
+
+static void __init platform_irq_init(void)
+{
+ device_irq_init();
+}
+
+static void __init platform_init(void)
+{
+ device_init();
+ board_gpio_init();
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ spi_register_board_info(comcerto_spi_board_info, ARRAY_SIZE(comcerto_spi_board_info));
+#endif
+ mac_addr_init(&comcerto_pfe_pdata);
+
+ platform_add_devices(comcerto_devices, ARRAY_SIZE(comcerto_devices));
+}
+
+MACHINE_START(COMCERTO, "Comcerto 2000 EVM")
+ /* Mindspeed Technologies Inc. */
+ .atag_offset = COMCERTO_AXI_DDR_BASE + 0x100,
+ .reserve = platform_reserve,
+ .map_io = platform_map_io,
+ .init_irq = platform_irq_init,
+ .init_machine = platform_init,
+ .timer = &comcerto_timer,
+#ifdef CONFIG_ZONE_DMA
+ .dma_zone_size = SZ_32M + 3*SZ_4M,
+#endif
+MACHINE_END
diff --git a/arch/arm/mach-comcerto/board-c2kmfcnevm.c b/arch/arm/mach-comcerto/board-c2kmfcnevm.c
new file mode 100644
index 0000000..c14cf44
--- /dev/null
+++ b/arch/arm/mach-comcerto/board-c2kmfcnevm.c
@@ -0,0 +1,743 @@
+/*
+ * arch/arm/mach-comcerto/board-c2kmfcnevm.c
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <linux/memblock.h>
+#include <linux/phy.h>
+
+#include <linux/mtd/mtd.h>
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+#include <linux/mtd/nand.h>
+#endif
+#include <linux/mtd/partitions.h>
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+#include <linux/spi/spi.h>
+#endif
+
+#include <asm/sizes.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+
+#include <asm/mach/flash.h>
+#include <asm/mach/arch.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/dma.h>
+#include <linux/dw_dmac.h>
+
+#include <linux/clockchips.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/smp_twd.h>
+#include <asm/localtimer.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/time.h>
+#include <mach/gpio.h>
+
+
+extern void platform_reserve(void);
+extern void device_map_io (void);
+extern void device_irq_init(void);
+extern void device_init(void);
+extern void mac_addr_init(struct comcerto_pfe_platform_data *);
+extern struct sys_timer comcerto_timer;
+
+static void __init board_gpio_init(void)
+{
+#ifdef CONFIG_COMCERTO_PFE_UART_SUPPORT
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG) & ~PFE_UART_GPIO) | PFE_UART_BUS, COMCERTO_GPIO_PIN_SELECT_REG);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= PFE_UART_GPIO_PIN; /* GPIOs 12 & 13 are used for PFE_UART */
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+ /* enable SPI pins */
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_MUX_GPIO_1)) | (SPI_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+ writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_MUX_GPIO_2)) | (SPI_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= SPI_MUX_GPIO_1_PIN; /* GPIOs 18,19, 21,22, 30,31 are used for SPI*/
+ c2k_gpio_pin_stat.c2k_gpio_pins_32_63 |= SPI_MUX_GPIO_2_PIN; /* GPIO 32 is used for SPI*/
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ /* enable SPI pins */
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_2_MUX_GPIO_1)) | (SPI_2_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+ writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_2_MUX_GPIO_2)) | (SPI_2_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= SPI_2_MUX_GPIO_1_PIN;
+ c2k_gpio_pin_stat.c2k_gpio_pins_32_63 |= SPI_2_MUX_GPIO_2_PIN;
+#endif
+
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~I2C_GPIO) | I2C_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= I2C_GPIO_PIN;
+#endif
+
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NAND_GPIO) | NAND_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= NAND_GPIO_PIN;
+#endif
+
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NOR_GPIO) | NOR_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= NOR_GPIO_PIN;
+#endif
+}
+
+/* --------------------------------------------------------------------
+ * NOR device
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+
+static struct resource comcerto_nor_resources[] = {
+ {
+ .start = NORFLASH_MEMORY_PHY1,
+ .end = NORFLASH_MEMORY_PHY1 + SZ_64M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct flash_platform_data comcerto_nor_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+};
+
+static struct platform_device comcerto_nor = {
+ .name = "comcertoflash",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(comcerto_nor_resources),
+ .resource = comcerto_nor_resources,
+ .dev = {
+ .platform_data = &comcerto_nor_data,
+ },
+};
+#endif
+
+static struct resource rtc_res[] = {
+ {
+ .start = COMCERTO_APB_RTC_BASE,
+ .end = COMCERTO_APB_RTC_BASE + SZ_32 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_RTC_ALM,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = IRQ_RTC_PRI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device rtc_dev = {
+ .name = "c2k-rtc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(rtc_res),
+ .resource = rtc_res,
+};
+
+/* --------------------------------------------------------------------
+ * DMAC controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+static struct resource dw_dmac_resource[] = {
+ {
+ .start = DW_DMA_DMAC_BASEADDR,
+ .end = DW_DMA_DMAC_BASEADDR + 0x2C0,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_DMAC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct dw_dma_platform_data dw_dmac_data = {
+ .nr_channels = 8,
+};
+
+static u64 dw_dmac_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device dw_dmac_device = {
+ .name = "dw_dmac",
+ .id = 0,
+ .dev = {
+ .dma_mask = &dw_dmac_dma_mask,
+ .platform_data = &dw_dmac_data,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = dw_dmac_resource,
+ .num_resources = ARRAY_SIZE(dw_dmac_resource),
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * NAND device
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+static struct resource comcerto_nand_resources[] = {
+ {
+ .start = COMCERTO_NAND_FIO_ADDR,
+ .end = COMCERTO_NAND_FIO_ADDR + COMCERTO_NAND_IO_SZ - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device comcerto_nand = {
+ .name = "comcertonand",
+ .id = -1,
+ .dev = {
+ .platform_data = NULL,
+ },
+ .resource = comcerto_nand_resources,
+ .num_resources = ARRAY_SIZE(comcerto_nand_resources),
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * SPI bus controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+
+#define CLK_NAME 10
+struct spi_controller_pdata {
+ int use_dma;
+ int num_chipselects;
+ int bus_num;
+ u32 max_freq;
+ char clk_name[CLK_NAME];
+};
+
+struct spi_platform_data {
+ int type;
+ int dummy;
+};
+
+struct spi_controller_data {
+ u8 poll_mode; /* 0 for contoller polling mode */
+ u8 type; /* SPI/SSP/Micrwire */
+ u8 enable_dma;
+ void (*cs_control)(u32 command);
+};
+
+struct spi_platform_data spi_pdata = {
+ .type = 0,
+ .dummy = 0,
+};
+
+struct spi_controller_data spi_ctrl_data = {
+ .poll_mode = 1,
+};
+
+static struct spi_board_info comcerto_spi_board_info[] = {
+ {
+ /* FIXME: for chipselect-0 */
+ .modalias = "s25fl256s0",
+ .chip_select = 0,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+ {
+ /* FIXME: for chipselect-1 */
+ .modalias = "proslic",
+ .max_speed_hz = 4*1000*1000,
+ .chip_select = 1,
+ .mode = SPI_MODE_3,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+ {
+ .modalias = "comcerto_spi3",
+ .chip_select = 2,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+
+#if 0 //MSIF
+
+ {
+ .modalias = "proslic",
+ .max_speed_hz = 2*1000*1000,
+ .chip_select = 3,
+ .mode = SPI_MODE_1,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+#else
+ {
+ .modalias = "legerity",
+ .chip_select = 3,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ .controller_data = &spi_ctrl_data,
+ },
+#endif
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+struct spi_controller_pdata fast_spi_pdata = {
+ .use_dma = 0,
+ .num_chipselects = 2,
+ .bus_num = 1,
+ .max_freq = 60 * 1000 * 1000,
+ .clk_name = "DUS",
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI2_MSPD_HIGH_SPEED)
+static struct resource comcerto_fast_spi_resource[] = {
+ {
+ .start = COMCERTO_AXI_SPI_BASE,
+ .end = COMCERTO_AXI_SPI_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_SPI,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device comcerto_fast_spi = {
+ .name = "comcerto_spi",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(comcerto_fast_spi_resource),
+ .resource = comcerto_fast_spi_resource,
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ .dev = {
+ .platform_data = &fast_spi_pdata,
+ },
+#endif
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+struct spi_controller_pdata ls_spi_pdata = {
+ .use_dma = 0,
+ .num_chipselects = 4,
+ .bus_num = 0,
+ .max_freq = 20 * 1000 * 1000,
+ .clk_name = "spi_i2c",
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+static struct resource comcerto_spi_resource[] = {
+ {
+ .start = COMCERTO_APB_SPI_BASE,
+ .end = COMCERTO_APB_SPI_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_SPI_LS,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device comcerto_spi = {
+ .name = "comcerto_spi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(comcerto_spi_resource),
+ .resource = comcerto_spi_resource,
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+ .dev = {
+ .platform_data = &ls_spi_pdata,
+ },
+#endif
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * I2C bus controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+static struct resource comcerto_i2c_resources[] = {
+ {
+ .start = COMCERTO_APB_I2C_BASE,
+ .end = COMCERTO_APB_I2C_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_i2c = {
+ .name = "comcerto_i2c",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_i2c_resources),
+ .resource = comcerto_i2c_resources,
+};
+#endif
+
+/* --------------------------------------------------------------------
+* Watchdog
+* -------------------------------------------------------------------- */
+#ifdef CONFIG_MPCORE_WATCHDOG
+static struct resource comcerto_a9wd_resources[] = {
+ {
+ .start = COMCERTO_TWD_BASE,
+ .end = COMCERTO_TWD_BASE + 0xFF,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "mpcore_wdt",
+ .start = IRQ_LOCALWDOG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_a9wd = {
+ .name = "mpcore_wdt",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_a9wd_resources),
+ .resource = comcerto_a9wd_resources,
+};
+#endif
+
+#ifdef CONFIG_COMCERTO_WATCHDOG
+static struct resource comcerto_wdt_resources[] = {
+ {
+ .start = COMCERTO_APB_TIMER_BASE + 0xD0,
+ .end = COMCERTO_APB_TIMER_BASE + 0xD8,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device comcerto_wdt = {
+ .name = "comcerto_wdt",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_wdt_resources),
+ .resource = comcerto_wdt_resources,
+};
+#endif
+
+#if defined(CONFIG_COMCERTO_ELP_SUPPORT)
+/* --------------------------------------------------------------------
+ * IPsec
+ * -------------------------------------------------------------------- */
+static struct resource comcerto_elp_resources[] = {
+ {
+ .name = "elp",
+ .start = COMCERTO_AXI_SPACC_PDU_BASE,
+ .end = COMCERTO_AXI_SPACC_PDU_BASE + SZ_16M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "irq_spacc",
+ .start = IRQ_SPACC,
+ .end = IRQ_SPACC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 comcerto_elp_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_elp_device = {
+ .name = "Elliptic-EPN1802",
+ .id = 0,
+ .num_resources = 2,
+ .resource = comcerto_elp_resources,
+ .dev = {
+ .dma_mask = &comcerto_elp_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+#endif
+
+static struct comcerto_tdm_data comcerto_tdm_pdata = {
+ .fsoutput = 1, /* Generic Pad Control and Version ID Register[2] */
+ .fspolarity = 0, /* 28 FSYNC_FALL(RISE)_EDGE */
+ .fshwidth = 1, /* High_Phase_Width[10:0] */
+ .fslwidth = 0xFF, /* Low_Phase_Width[10:0] */
+ .clockhz = 2048000, /* INC_VALUE[29:0] According to the desired TDM clock output \
+ frequency, this field should be configured */
+ .clockout = 1, /* 0 -> set bit 21, clear bit 20 in COMCERTO_GPIO_IOCTRL_REG
+ (software control, clock input)
+ 1 -> set bit 21 and 20 in COMCERTO_GPIO_IOCTRL_REG
+ (software control, clock output)
+ 2 -> clear bit 21 in COMCERTO_GPIO_IOCTRL_REG (hardware control) */
+ .tdmmux = 0x1, /* TDM interface Muxing:0x0 - TDM block, 0x1 - ZDS block,
+ 0x2 - GPIO[63:60] signals and 0x3 - MSIF block is selected */
+#if 0
+ /* FIX ME - Need correct values for TDM_DR, TDM_DX, TDM_FS and TDM_CK */
+ .tdmck = 0x3F,
+ .tdmfs = 0x3F,
+ .tdmdx = 0x3F,
+ .tdmdr = 0x3F,
+#endif
+};
+
+static struct platform_device comcerto_tdm_device = {
+ .name = "comcerto-tdm",
+ .id = 0,
+ .dev.platform_data = &comcerto_tdm_pdata,
+ .num_resources = 0,
+ .resource = NULL,
+};
+
+#if defined(CONFIG_DSPG_DECT_CSS)
+#define CSS_ITCM_BASE COMCERTO_AXI_DECT_BASE
+#define CSS_ITCM_SIZE (SZ_1M)
+
+#define CSS_DTCM_BASE (CSS_ITCM_BASE + CSS_ITCM_SIZE)
+#define CSS_DTCM_SIZE (SZ_1M)
+
+static struct resource comcerto_css_resources[] = {
+ {
+ .name = "itcm",
+ .start = CSS_ITCM_BASE,
+ .end = CSS_ITCM_BASE + CSS_ITCM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "dtcm",
+ .start = CSS_DTCM_BASE,
+ .end = CSS_DTCM_BASE + CSS_DTCM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device comcerto_css_device = {
+ .name = "css",
+ .id = 0,
+ .dev = {
+ .platform_data = 0,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(comcerto_css_resources),
+ .resource = comcerto_css_resources,
+};
+#endif
+
+static struct resource comcerto_pfe_resources[] = {
+ {
+ .name = "apb",
+ .start = COMCERTO_APB_PFE_BASE,
+ .end = COMCERTO_APB_PFE_BASE + COMCERTO_APB_PFE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "axi",
+ .start = COMCERTO_AXI_PFE_BASE,
+ .end = COMCERTO_AXI_PFE_BASE + COMCERTO_AXI_PFE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "ddr",
+ .start = COMCERTO_PFE_DDR_BASE,
+ .end = COMCERTO_PFE_DDR_BASE + COMCERTO_PFE_DDR_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "iram",
+ .start = COMCERTO_PFE_IRAM_BASE,
+ .end = COMCERTO_PFE_IRAM_BASE + COMCERTO_PFE_IRAM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "ipsec",
+ .start = COMCERTO_AXI_IPSEC_BASE,
+ .end = COMCERTO_AXI_IPSEC_BASE + COMCERTO_AXI_IPSEC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+
+ {
+ .name = "hif",
+ .start = IRQ_PFE_HIF,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct comcerto_pfe_platform_data comcerto_pfe_pdata = {
+ .comcerto_eth_pdata[0] = {
+ .name = GEM0_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_PHY_RGMII_ADD_DELAY,
+ .bus_id = 0,
+ .phy_id = 4,
+ .gem_id = 0,
+ .mac_addr = (u8[])GEM0_MAC,
+ },
+
+ .comcerto_eth_pdata[1] = {
+ .name = GEM1_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_NO_PHY,
+ .gem_id = 1,
+ .mac_addr = (u8[])GEM1_MAC,
+ },
+
+ .comcerto_eth_pdata[2] = {
+ .name = GEM2_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_NO_PHY,
+ .gem_id = 2,
+ .mac_addr = (u8[])GEM2_MAC,
+ },
+
+ /**
+ * There is a single mdio bus coming out of C2K. And that's the one
+ * connected to GEM0. All PHY's, switchs will be connected to the same
+ * bus using different addresses. Typically .bus_id is always 0, only
+ * .phy_id will change in the different comcerto_eth_pdata[] structures above.
+ */
+ .comcerto_mdio_pdata[0] = {
+ .enabled = 1,
+ .phy_mask = 0xFFFFFFEF,
+ .mdc_div = 96,
+ .irq = {
+ [4] = PHY_POLL,
+ },
+ },
+};
+
+static u64 comcerto_pfe_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_pfe_device = {
+ .name = "pfe",
+ .id = 0,
+ .dev = {
+ .platform_data = &comcerto_pfe_pdata,
+ .dma_mask = &comcerto_pfe_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(comcerto_pfe_resources),
+ .resource = comcerto_pfe_resources,
+};
+
+static struct platform_device *comcerto_devices[] __initdata = {
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+ &comcerto_nand,
+#endif
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+ &comcerto_nor,
+#endif
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+ &comcerto_i2c,
+#endif
+
+#if defined (CONFIG_MPCORE_WATCHDOG)
+ &comcerto_a9wd,
+#endif
+
+#if defined(CONFIG_COMCERTO_WATCHDOG)
+ &comcerto_wdt,
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI2_MSPD_HIGH_SPEED)
+ &comcerto_fast_spi,
+#endif
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+ &comcerto_spi,
+#endif
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+ &dw_dmac_device,
+#endif
+ &comcerto_tdm_device,
+ &comcerto_pfe_device,
+ &rtc_dev,
+#if defined(CONFIG_DSPG_DECT_CSS)
+ &comcerto_css_device,
+#endif
+#if defined(CONFIG_COMCERTO_ELP_SUPPORT)
+ &comcerto_elp_device,
+#endif
+};
+
+
+/************************************************************************
+ * Expansion bus
+ *
+ ************************************************************************/
+/* This variable is used by comcerto-2000.c to initialize the expansion bus */
+int comcerto_exp_values[5][7]= {
+ /* ENABLE, BASE, SEG_SZ, CFG, TMG1, TMG2, TMG3 */
+ {1, (EXP_BUS_REG_BASE_CS0 >> 12), ((EXP_BUS_REG_BASE_CS0 + EXP_CS0_SEG_SIZE - 1) >> 12), EXP_MEM_BUS_SIZE_16, 0x03034007, 0x04040502, 0x00000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS1 >> 12), ((EXP_BUS_REG_BASE_CS1 + EXP_CS1_SEG_SIZE - 1) >> 12), EXP_RDY_EN|EXP_MEM_BUS_SIZE_32, 0x1A1A401F, 0x06060A04, 0x00000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS2 >> 12), ((EXP_BUS_REG_BASE_CS2 + EXP_CS2_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS3 >> 12), ((EXP_BUS_REG_BASE_CS3 + EXP_CS3_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002}, /*BT8370*/
+ {1, (EXP_BUS_REG_BASE_CS4 >> 12), ((EXP_BUS_REG_BASE_CS4 + EXP_CS4_SEG_SIZE - 1) >> 12), EXP_NAND_MODE|EXP_MEM_BUS_SIZE_8, 0x00000001, 0x01010001, 0x00000002}, /* NAND: TODO Values to check */
+};
+
+/************************************************************************
+ * Machine definition
+ *
+ ************************************************************************/
+static void __init platform_map_io(void)
+{
+ device_map_io();
+}
+
+static void __init platform_irq_init(void)
+{
+ device_irq_init();
+}
+
+static void __init platform_init(void)
+{
+ device_init();
+ board_gpio_init();
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ spi_register_board_info(comcerto_spi_board_info, ARRAY_SIZE(comcerto_spi_board_info));
+#endif
+ mac_addr_init(&comcerto_pfe_pdata);
+
+ platform_add_devices(comcerto_devices, ARRAY_SIZE(comcerto_devices));
+}
+
+MACHINE_START(COMCERTO, "Comcerto 2000 MFCN EVM")
+ /* Mindspeed Technologies Inc. */
+ .atag_offset = COMCERTO_AXI_DDR_BASE + 0x100,
+ .reserve = platform_reserve,
+ .map_io = platform_map_io,
+ .init_irq = platform_irq_init,
+ .init_machine = platform_init,
+ .timer = &comcerto_timer,
+#ifdef CONFIG_ZONE_DMA
+ .dma_zone_size = SZ_32M + 3*SZ_4M,
+#endif
+MACHINE_END
diff --git a/arch/arm/mach-comcerto/board-c2krtsm.c b/arch/arm/mach-comcerto/board-c2krtsm.c
new file mode 100644
index 0000000..8fad46a
--- /dev/null
+++ b/arch/arm/mach-comcerto/board-c2krtsm.c
@@ -0,0 +1,516 @@
+/*
+ * arch/arm/mach-comcerto/board-c2krtsm.c
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <linux/phy.h>
+
+#include <linux/mtd/mtd.h>
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+#include <linux/mtd/nand.h>
+#endif
+#include <linux/mtd/partitions.h>
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+#include <linux/spi/spi.h>
+#endif
+
+#include <asm/sizes.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+
+#include <asm/mach/flash.h>
+#include <asm/mach/arch.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/dma.h>
+#include <linux/dw_dmac.h>
+
+#include <linux/clockchips.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/smp_twd.h>
+#include <asm/localtimer.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/time.h>
+
+
+
+extern void platform_reserve(void);
+extern void device_map_io (void);
+extern void device_irq_init(void);
+extern void device_init(void);
+extern struct sys_timer comcerto_timer;
+
+static void __init board_gpio_init(void)
+{
+#ifdef CONFIG_COMCERTO_PFE_UART_SUPPORT
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG) & ~PFE_UART_GPIO) | PFE_UART_BUS, COMCERTO_GPIO_PIN_SELECT_REG);
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+ /* enable SPI pins */
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_MUX_GPIO_1)) | (SPI_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+ writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_MUX_GPIO_2)) | (SPI_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ /* enable SPI pins */
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~(SPI_2_MUX_GPIO_1)) | (SPI_2_MUX_BUS_1), COMCERTO_GPIO_PIN_SELECT_REG1);
+ writel((readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~(SPI_2_MUX_GPIO_2)) | (SPI_2_MUX_BUS_2), COMCERTO_GPIO_63_32_PIN_SELECT);
+#endif
+
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~I2C_GPIO) | I2C_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+#endif
+
+#if defined(CONFIG_MTD_NAND_COMCERTO) || defined(CONFIG_MTD_NAND_COMCERTO_MODULE)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NAND_GPIO) | NAND_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+#endif
+
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG1) & ~NOR_GPIO) | NOR_BUS, COMCERTO_GPIO_PIN_SELECT_REG1);
+#endif
+}
+
+
+
+/* --------------------------------------------------------------------
+ * NOR device
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+
+static struct resource comcerto_nor_resources[] = {
+ {
+ .start = NORFLASH_MEMORY_PHY1,
+ .end = NORFLASH_MEMORY_PHY1 + SZ_64M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct flash_platform_data comcerto_nor_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+};
+
+static struct platform_device comcerto_nor = {
+ .name = "comcertoflash",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(comcerto_nor_resources),
+ .resource = comcerto_nor_resources,
+ .dev = {
+ .platform_data = &comcerto_nor_data,
+ },
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * DMAC controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+static struct resource dw_dmac_resource[] = {
+ {
+ .start = DW_DMA_DMAC_BASEADDR,
+ .end = DW_DMA_DMAC_BASEADDR + 0x2C0,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_DMAC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct dw_dma_platform_data dw_dmac_data = {
+ .nr_channels = 8,
+};
+
+static u64 dw_dmac_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device dw_dmac_device = {
+ .name = "dw_dmac",
+ .id = 0,
+ .dev = {
+ .dma_mask = &dw_dmac_dma_mask,
+ .platform_data = &dw_dmac_data,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = dw_dmac_resource,
+ .num_resources = ARRAY_SIZE(dw_dmac_resource),
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * SPI bus controller [FIXME]
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+struct spi_controller_pdata {
+ int use_dma;
+ int num_chipselects;
+ int bus_num;
+ u32 max_freq;
+};
+
+struct spi_platform_data {
+ int type;
+ int dummy;
+};
+
+struct spi_platform_data spi_pdata = {
+ .type = 0,
+ .dummy = 0,
+};
+
+static struct spi_board_info comcerto_spi_board_info[] = {
+ {
+ /* FIXME: for chipselect-0 */
+ .modalias = "comcerto_spi1",
+ .chip_select = 0,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ },
+
+ {
+ /* FIXME: for chipselect-1 */
+ .modalias = "comcerto_spi2",
+ .chip_select = 1,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ },
+
+ {
+ .modalias = "comcerto_spi3",
+ .chip_select = 2,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ },
+
+ {
+ .modalias = "legerity",
+ .chip_select = 3,
+ .max_speed_hz = 4*1000*1000,
+ .bus_num = 0,
+ .irq = -1,
+ .mode = SPI_MODE_3,
+ .platform_data = &spi_pdata,
+ },
+
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+struct spi_controller_pdata fast_spi_pdata = {
+ .use_dma = 1,
+ .num_chipselects = 2,
+ .bus_num = 1,
+ .max_freq = 60 * 1000 * 1000,
+};
+
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED) || defined(CONFIG_SPI2_MSPD_HIGH_SPEED)
+static struct resource comcerto_fast_spi_resource[] = {
+ {
+ .start = COMCERTO_AXI_SPI_BASE,
+ .end = COMCERTO_AXI_SPI_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_SPI,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device comcerto_fast_spi = {
+ .name = "comcerto_spi",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(comcerto_fast_spi_resource),
+ .resource = comcerto_fast_spi_resource,
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ .dev = {
+ .platform_data = &fast_spi_pdata,
+ },
+#endif
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+struct spi_controller_pdata ls_spi_pdata = {
+ .use_dma = 0,
+ .num_chipselects = 4,
+ .bus_num = 0,
+ .max_freq = 20 * 1000 * 1000,
+};
+#endif
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+static struct resource comcerto_spi_resource[] = {
+ {
+ .start = COMCERTO_APB_SPI_BASE,
+ .end = COMCERTO_APB_SPI_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_SPI_LS,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device comcerto_spi = {
+ .name = "comcerto_spi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(comcerto_spi_resource),
+ .resource = comcerto_spi_resource,
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED)
+ .dev = {
+ .platform_data = &ls_spi_pdata,
+ },
+#endif
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * I2C bus controller
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+static struct resource comcerto_i2c_resources[] = {
+ {
+ .start = COMCERTO_APB_I2C_BASE,
+ .end = COMCERTO_APB_I2C_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_i2c = {
+ .name = "comcerto_i2c",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(comcerto_i2c_resources),
+ .resource = comcerto_i2c_resources,
+};
+#endif
+
+
+static struct comcerto_tdm_data comcerto_tdm_pdata = {
+ .fsoutput = 1, /* Generic Pad Control and Version ID Register[2] */
+ .fspolarity = 0, /* 28 FSYNC_FALL(RISE)_EDGE */
+ .fshwidth = 1, /* High_Phase_Width[10:0] */
+ .fslwidth = 0xFF, /* Low_Phase_Width[10:0] */
+ .clockhz = 2048000, /* INC_VALUE[29:0] According to the desired TDM clock output frequency, this field should be configured */
+ .clockout = 1, /* 0 -> set bit 21, clear bit 20 in COMCERTO_GPIO_IOCTRL_REG
+ (software control, clock input)
+ 1 -> set bit 21 and 20 in COMCERTO_GPIO_IOCTRL_REG
+ (software control, clock output)
+ 2 -> clear bit 21 in COMCERTO_GPIO_IOCTRL_REG (hardware control) */
+ .tdmmux = 0x1, /* TDM interface Muxing:0x0 - TDM block, 0x1 - ZDS block,
+ 0x2 - GPIO[63:60] signals and 0x3 - MSIF block is selected */
+#if 0
+/* FIX ME - Need correct values for TDM_DR, TDM_DX, TDM_FS and TDM_CK */
+ .tdmck = 0x3F,
+ .tdmfs = 0x3F,
+ .tdmdx = 0x3F,
+ .tdmdr = 0x3F,
+#endif
+};
+
+static struct platform_device comcerto_tdm_device = {
+ .name = "comcerto-tdm",
+ .id = 0,
+ .dev.platform_data = &comcerto_tdm_pdata,
+ .num_resources = 0,
+ .resource = NULL,
+};
+
+static struct resource comcerto_pfe_resources[] = {
+ {
+ .name = "apb",
+ .start = COMCERTO_APB_PFE_BASE,
+ .end = COMCERTO_APB_PFE_BASE + COMCERTO_APB_PFE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "axi",
+ .start = COMCERTO_AXI_PFE_BASE,
+ .end = COMCERTO_AXI_PFE_BASE + COMCERTO_AXI_PFE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "ddr",
+ .start = COMCERTO_PFE_DDR_BASE,
+ .end = COMCERTO_PFE_DDR_BASE + COMCERTO_PFE_DDR_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "iram",
+ .start = COMCERTO_PFE_IRAM_BASE,
+ .end = COMCERTO_PFE_IRAM_BASE + COMCERTO_PFE_IRAM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "hif",
+ .start = IRQ_PFE_HIF,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct comcerto_pfe_platform_data comcerto_pfe_pdata = {
+ .comcerto_eth_pdata[0] = {
+ .name = GEM0_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_PHY_RGMII_ADD_DELAY,
+ .bus_id = 0,
+ .phy_id = 0,
+ .gem_id = 0,
+ .mac_addr = (u8[])GEM0_MAC,
+ },
+
+ .comcerto_eth_pdata[1] = {
+ .name = GEM1_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_NO_PHY,
+ .gem_id = 1,
+ .mac_addr = (u8[])GEM1_MAC,
+ },
+
+ .comcerto_eth_pdata[2] = {
+ .name = GEM2_ITF_NAME,
+ .device_flags = CONFIG_COMCERTO_GEMAC,
+ .mii_config = CONFIG_COMCERTO_USE_RGMII,
+ .gemac_mode = GEMAC_SW_CONF | GEMAC_SW_FULL_DUPLEX | GEMAC_SW_SPEED_1G,
+ .phy_flags = GEMAC_NO_PHY,
+ .gem_id = 2,
+ .mac_addr = (u8[])GEM2_MAC,
+ },
+
+ /**
+ * There is a single mdio bus coming out of C2K. And that's the one
+ * connected to GEM0. All PHY's, switchs will be connected to the same
+ * bus using different addresses. Typically .bus_id is always 0, only
+ * .phy_id will change in the different comcerto_eth_pdata[] structures above.
+ */
+ .comcerto_mdio_pdata[0] = {
+ .enabled = 1,
+ .phy_mask = 0xFFFFFFFE,
+ .mdc_div = 96,
+ .irq = {
+ [0] = PHY_POLL,
+ },
+ },
+};
+
+static u64 comcerto_pfe_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_pfe_device = {
+ .name = "pfe",
+ .id = 0,
+ .dev = {
+ .platform_data = &comcerto_pfe_pdata,
+ .dma_mask = &comcerto_pfe_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(comcerto_pfe_resources),
+ .resource = comcerto_pfe_resources,
+};
+
+static struct platform_device *comcerto_devices[] __initdata = {
+#if defined(CONFIG_MTD_COMCERTO_NOR)
+ &comcerto_nor,
+#endif
+#if defined(CONFIG_COMCERTO_I2C_SUPPORT)
+ &comcerto_i2c,
+#endif
+#if defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ &comcerto_fast_spi,
+#endif
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI2_MSPD_LOW_SPEED)
+ &comcerto_spi,
+#endif
+#if defined(CONFIG_COMCERTO_DW_DMA_SUPPORT)
+ &dw_dmac_device,
+#endif
+ &comcerto_tdm_device,
+ &comcerto_pfe_device,
+ };
+
+
+/************************************************************************
+ * Expansion bus
+ *
+ ************************************************************************/
+/* This variable is used by comcerto-1000.c to initialize the expansion bus */
+int comcerto_exp_values[5][7]= {
+ /* ENABLE, BASE, SEG_SZ, CFG, TMG1, TMG2, TMG3 */
+ {1, (EXP_BUS_REG_BASE_CS0 >> 12), ((EXP_BUS_REG_BASE_CS0 + EXP_CS0_SEG_SIZE - 1) >> 12), EXP_MEM_BUS_SIZE_16, 0x1A1A401F, 0x06060A04, 0x00000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS1 >> 12), ((EXP_BUS_REG_BASE_CS1 + EXP_CS1_SEG_SIZE - 1) >> 12), EXP_RDY_EN|EXP_MEM_BUS_SIZE_32, 0x1A1A401F, 0x06060A04, 0x00000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS2 >> 12), ((EXP_BUS_REG_BASE_CS2 + EXP_CS2_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002}, /*TODO Values to check*/
+ {0, (EXP_BUS_REG_BASE_CS3 >> 12), ((EXP_BUS_REG_BASE_CS3 + EXP_CS3_SEG_SIZE - 1) >> 12), EXP_STRB_MODE|EXP_ALE_MODE|EXP_MEM_BUS_SIZE_8, 0x1A10201A, 0x03080403, 0x0000002}, /*BT8370*/
+ {0, (EXP_BUS_REG_BASE_CS4 >> 12), ((EXP_BUS_REG_BASE_CS4 + EXP_CS4_SEG_SIZE - 1) >> 12), EXP_NAND_MODE|EXP_MEM_BUS_SIZE_8, 0x1A1A401F, 0x06060A04, 0x00000002}, /* NAND: TODO Values to check */
+};
+
+/************************************************************************
+ * Machine definition
+ *
+ ************************************************************************/
+static void __init platform_map_io(void)
+{
+ device_map_io();
+}
+
+static void __init platform_irq_init(void)
+{
+ device_irq_init();
+}
+
+static void __init platform_init(void)
+{
+ device_init();
+ board_gpio_init();
+
+#if defined(CONFIG_SPI_MSPD_LOW_SPEED) || defined(CONFIG_SPI_MSPD_HIGH_SPEED)
+ spi_register_board_info(comcerto_spi_board_info, ARRAY_SIZE(comcerto_spi_board_info));
+#endif
+ platform_add_devices(comcerto_devices, ARRAY_SIZE(comcerto_devices));
+}
+
+MACHINE_START(COMCERTO, "Comcerto 2000 RTSM")
+ /* Mindspeed Technologies Inc. */
+ .atag_offset = COMCERTO_AXI_DDR_BASE + 0x100,
+ .reserve = platform_reserve,
+ .map_io = platform_map_io,
+ .init_irq = platform_irq_init,
+ .init_machine = platform_init,
+ .timer = &comcerto_timer,
+MACHINE_END
diff --git a/arch/arm/mach-comcerto/clock.c b/arch/arm/mach-comcerto/clock.c
new file mode 100644
index 0000000..7ef79a9
--- /dev/null
+++ b/arch/arm/mach-comcerto/clock.c
@@ -0,0 +1,1023 @@
+/*
+ * linux/arch/arm/mach-comcerto/clock.c
+ *
+ * Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clkdev.h>
+#include <linux/printk.h>
+#include <linux/list.h>
+#include <mach/comcerto-2000/clock.h>
+
+static DEFINE_SPINLOCK(clock_lock);
+
+/* Forward declaration */
+static void HAL_set_clk_divider(unsigned long ,u32 , u32);
+static unsigned long HAL_get_clk_freq(u32, u32);
+
+/* API clk_put clk_get
+ * Declared in "include/linux/clk.h", include this
+ * in correspoding driver.
+ * Defined in drivers/clk/clkdev.c, will
+ * use the above.
+ */
+
+/* @ struct clk *
+ * API:local_clk_disable
+ * Call the device level disable.
+ * This will decrease the usecount following to disabling
+ * the clock.
+*/
+static void local_clk_disable(struct clk *clk)
+{
+ u32 val;
+ if (clk->usecount == 0){
+ pr_warning("Warning : %s Clock is already disabled \n",clk->name);
+ return;
+ }
+
+ /* Decrement the usecount */
+ clk->usecount--;
+
+ if (!clk->usecount){
+ /* Take care of parent disable , if present for the clock.
+ * Disable the parent , if no other clock is using.
+ */
+ if (clk->parent)
+ local_clk_disable(clk->parent);
+
+ /* Apply the Clock register write here */
+ if (clk->enable_reg){
+ val = readl(clk->enable_reg);
+ val &= ~clk->enable_mask;
+ writel(val , clk->enable_reg);
+ }
+ }
+}
+
+/* @ struct clk *
+ * API:clk_disable used to disable clock for
+ * the devices.
+ * This API will be available to outside
+ * ( for all device driver).
+ * Call of this API should be followed by clk_get
+ * clk_disable->clk_put.
+*/
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ local_clk_disable(clk);
+ spin_unlock_irqrestore(&clock_lock, flags);
+}
+
+EXPORT_SYMBOL(clk_disable);
+
+/* @ struct clk *
+ * API : Internal call for clk_disable_unused for
+ * unused devices.
+*/
+static void __clk_disable_unused(struct clk *clk)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ /* Apply the Clock register write here */
+ if (clk->enable_reg){
+ val = readl(clk->enable_reg);
+ val &= ~clk->enable_mask;
+ writel(val , clk->enable_reg);
+ }
+ spin_unlock_irqrestore(&clock_lock, flags);
+}
+
+/* @ struct clk *
+ * API:local_clk_enable
+ * Call the device level enable.
+ * This will increase the usecount.
+*/
+static int local_clk_enable(struct clk *clk)
+{
+ int ret = 0;
+ u32 val;
+
+ if (clk->usecount == 0) {
+ /* Check for parent clock .Enable the parent clock,if available*/
+ if (clk->parent){
+ ret = local_clk_enable(clk->parent);
+ if (ret)
+ return -EINVAL;
+ }
+ if (clk->enable_reg){
+ val = readl(clk->enable_reg);
+ val |= clk->enable_mask;
+ writel(val , clk->enable_reg);
+ ret=0;
+ }
+ }
+ /* Increment the use count */
+ clk->usecount++;
+ return ret;
+}
+
+/* @ struct clk *
+ * API:clk_enable used to enable clock for
+ * the devices.
+ * This API will be available to outside
+ * ( for all device driver).
+ * Call of this API should be followed by clk_get
+ * clk_get -> clk_enable.
+*/
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret=0;
+
+ if (!clk)
+ return -EINVAL;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ ret = local_clk_enable(clk);
+ spin_unlock_irqrestore(&clock_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+
+
+/* @ struct clk *
+ * API:clk_get_rate used to get clock rate value
+ * for the devices. This API will be available to
+ * outside ( for all device driver).
+*/
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (clk->get_rate)
+ return clk->get_rate(clk);
+ else
+ return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+/* @ struct clk * , unsigned long
+ * API:local_set_rate used to set clock rate value
+ * for the devices.
+*/
+
+static int local_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret = -EINVAL;
+ if (clk->set_rate) {
+ ret = clk->set_rate(clk, rate);
+ }
+ return ret;
+}
+
+/* @ struct clk * , unsigned long
+ * API:clk_set_rate used to set clock rate value
+ * for the devices. This API will be available to
+ * outside ( for all device driver).
+*/
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long actual_rate;
+ int ret = -EINVAL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ actual_rate=clk->rate;
+
+ if (actual_rate != rate ){
+ ret = local_set_rate(clk, rate);
+ }
+ else{ /*configured rate is same as desired rate*/
+ pr_debug("Current rate value of clock source (%s) is same as desired rate value (%ld)\n",clk->name,clk->rate);
+ spin_unlock_irqrestore(&clock_lock, flags);
+ return 0;
+ }
+
+ if ( ret == -EINVAL ){
+ pr_debug("Cannot change clock:(%s) ,not supporting set_rate,set to previous value (%ld)\n",clk->name,clk->rate);
+ }else{
+ pr_debug("Changed clock:(%s) rate ,rate value is (%ld)\n",clk->name,clk->rate);
+ }
+ spin_unlock_irqrestore(&clock_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+/* @ struct clk *
+ * API:clk_get_parent used to parent clock source.
+ * This API will be available to
+ * outside ( for all device driver).
+*/
+struct clk *clk_get_parent(struct clk *clk)
+{
+ return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+/* @ struct clk * , unsigned long
+ * API:clk_set_parent used to set to a different
+ * parent clock source.
+ * This API will be available to
+ * outside ( for all device driver).
+*/
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ unsigned long flags;
+
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ /* Only set parent once and if none is already set. */
+ if (clk->parent)
+ return -EINVAL;
+
+ /* Cannot change parent on enabled clock */
+ if ( WARN_ON(clk->usecount))
+ return -EBUSY;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ /* Set the parent in clk strusture */
+ clk->parent = parent;
+ spin_unlock_irqrestore(&clock_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+/*
+ * CLKGEN Divider registers
+ * The divider bypass bit in several configuration registers
+ * can only be written (if you read back you get zero).
+ *
+ * The Bug is in registers: 0x84 (A9DP_CLKDIV_CNTRL), 0x84 + 16 (L2CC_CLKDIV_CNTRL), 0x84 + 32
+ * (TPI_CLKDIV_CNTRL), etc... until PLL_ADDR_SPACE at 0x1C0.
+ */
+
+/*
+ * Barebox uses IRAM to mirror the clock divider registers
+ * Linux will relocate this mirror from IRAM to DDR to free up IRAM.
+ */
+#define IRAM_CLK_REG_MIRROR (0x8300FC00 - COMCERTO_AXI_IRAM_BASE + IRAM_MEMORY_VADDR)
+#define CLK_REG_DIV_BUG_BASE AXI_CLK_DIV_CNTRL
+#define CLK_REG_DIV_BUG_SIZE (PLL0_M_LSB - AXI_CLK_DIV_CNTRL)
+
+static u8 clk_div_backup_table [CLK_REG_DIV_BUG_SIZE];
+#define read_clk_div_bypass_backup(reg) readl(reg - CLK_REG_DIV_BUG_BASE + clk_div_backup_table)
+#define write_clk_div_bypass_backup(val, reg) writel(val, reg - CLK_REG_DIV_BUG_BASE + clk_div_backup_table)
+
+void HAL_clk_div_backup_relocate_table (void)
+{
+ memcpy (clk_div_backup_table, (void*) IRAM_CLK_REG_MIRROR, CLK_REG_DIV_BUG_SIZE);
+}
+EXPORT_SYMBOL(HAL_clk_div_backup_relocate_table);
+
+/*
+ * Get the reference clock after reading bootstrap
+ */
+unsigned long HAL_get_ref_clk (void)
+{
+ unsigned long clock_freq = 0;
+ unsigned int boot_strap, tmp;
+
+ boot_strap = readl(COMCERTO_GPIO_SYSTEM_CONFIG);
+ tmp = (boot_strap & GPIO_SYS_PLL_REF_CLK_MASK) >> GPIO_SYS_PLL_REF_CLK_SHIFT;
+
+ if ( USB_XTAL_REF_CLK == tmp )
+ {
+ if ( boot_strap & GPIO_USB_OSC_PAD_MASK )
+ clock_freq = REF_CLK_24MHZ;
+ else
+ clock_freq = REF_CLK_48MHZ;
+ }
+ else if ( SERDES_XTAL_REF_CLK == tmp )
+ {
+ if ( boot_strap & GPIO_SERDES_OSC_PAD_MASK )
+ clock_freq = REF_CLK_24MHZ;
+ else
+ clock_freq = REF_CLK_48MHZ;
+ }
+
+ return clock_freq;
+}
+EXPORT_SYMBOL(HAL_get_ref_clk);
+
+static unsigned long HAL_get_pll_freq(int pll_no)
+{
+ u32 p;
+ u32 od;
+ u32 m;
+ u32 k;
+ u32 s;
+ unsigned long pll_clk = 0;
+ unsigned long ref_clk = HAL_get_ref_clk();
+ unsigned long pll_div = 0;
+
+ if (pll_no < PLL3)
+ {
+ //get NF, NR and OD values
+ switch (pll_no)
+ {
+ case PLL0:
+ m = readl(PLL0_M_LSB) & 0xff;
+ m |= (readl(PLL0_M_MSB) & 0x3) << 8;
+ p = readl(PLL0_P) & 0x3f;
+ s = readl(PLL0_S) & 0x7;
+ od = (1 << s); // 2^s;
+ pll_div = readl(PLL0_DIV_CNTRL);
+ break;
+
+ case PLL1:
+ m = readl(PLL1_M_LSB) & 0xff;
+ m |= (readl(PLL1_M_MSB) & 0x3) << 8;
+ p = readl(PLL1_P) & 0x3f;
+ s = readl(PLL1_S) & 0x7;
+ od = (1 << s);
+ pll_div = readl(PLL1_DIV_CNTRL);
+ break;
+
+ case PLL2:
+ m = readl(PLL2_M_LSB) & 0xff;
+ m |= (readl(PLL2_M_MSB) & 0x3) << 8;
+ p = readl(PLL2_P) & 0x3f;
+ s = readl(PLL2_S) & 0x7;
+ od = (1 << s);
+ pll_div = readl(PLL2_DIV_CNTRL);
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+
+ /* Ref Clock divided by 1000000. It should be displayed in MHz. */
+ pll_clk = ((ref_clk / 1000000) * m) / p / od ;
+
+ if (pll_div & CLK_DIV_BYPASS)
+ pll_div = 1;
+ else
+ pll_div = pll_div & 0x1F;
+
+ if (pll_div > 1)
+ pll_clk = pll_clk/pll_div;
+
+ }
+ else if (pll_no == PLL3)
+ {
+ m = readl(PLL3_M_LSB) & 0xff;
+ m |= (readl(PLL3_M_MSB) & 0x3) << 8;
+ p = readl(PLL3_P) & 0x3f;
+ s = readl(PLL3_S) & 0x7;
+ k = readl(PLL3_K_LSB) & 0xff;
+ k |= (readl(PLL3_K_MSB) & 0xf) << 8;
+ od = (1 << s);
+ pll_clk = (((ref_clk / 1000000) * (m * 1024 + k)) / p / od + 1023) / 1024;
+ }
+
+ return (pll_clk * 1000000); /* convert into Hz and return it */
+}
+
+static void HAL_set_clk_divider(unsigned long rate,u32 ctrl_reg, u32 div_reg)
+{
+ u32 pll_src;
+ u32 val;
+ int divider;
+ unsigned long pll_rate;
+
+ /* Get PLL Source */
+ pll_src = readl(ctrl_reg);
+ pll_src = (pll_src >> CLK_PLL_SRC_SHIFT) & CLK_PLL_SRC_MASK;
+
+ /* Get PLL Freq */
+ pll_rate = HAL_get_pll_freq(pll_src);
+
+ /* Get The Divider value For Clock */
+ divider = pll_rate/rate;
+
+ if ( divider == 1){
+ write_clk_div_bypass_backup(CLK_DIV_BYPASS,div_reg);
+ /* Enable the Bypass bit in Hw reg (clk_div_bypass in div_reg) */
+ val = readl(div_reg);
+ val |= CLK_DIV_BYPASS;
+ writel(val , div_reg);
+ }
+ else
+ {
+ write_clk_div_bypass_backup(0,div_reg);
+ /* Write to the divider reg */
+ val = readl(div_reg);
+ val &= ~0x1f;
+ val |= divider;
+ writel(val, div_reg);
+ /* Clear the Bypass bit in Hw reg (clk_div_bypass in div_reg) */
+ val = readl(div_reg);
+ val &= ~CLK_DIV_BYPASS;
+ writel(val , div_reg);
+ }
+}
+
+static int HAL_get_clock_pll_source(u32 ctrl_reg)
+{
+ int pll_src;
+
+ /* Get PLL source */
+ pll_src = readl(ctrl_reg);
+ pll_src = (pll_src >> CLK_PLL_SRC_SHIFT) & CLK_PLL_SRC_MASK;
+
+ return pll_src;
+
+}
+
+static void HAL_set_clock_pll_source(u32 ctrl_reg,int pll_src){
+
+ switch(pll_src)
+ {
+ case PLL0:
+ writel(readl(ctrl_reg) | (1 << 0) , ctrl_reg);
+ break;
+ case PLL1:
+ writel(readl(ctrl_reg) | (1 << 1) , ctrl_reg);
+ break;
+ case PLL3:
+ writel(readl(ctrl_reg) | (1 << 3), ctrl_reg);
+ break;
+ case PLL2:
+ default:
+ writel(readl(ctrl_reg) | (1 << 2), ctrl_reg);
+ break;
+ }
+
+}
+
+static unsigned long HAL_get_clk_freq(u32 ctrl_reg, u32 div_reg)
+{
+ u32 pll_src;
+ u32 clk_div;
+ unsigned long clk_out;
+ unsigned long pll_clk;
+ int bypass = 0;
+
+ /* Get PLL Source */
+ pll_src = readl(ctrl_reg);
+ pll_src = (pll_src >> CLK_PLL_SRC_SHIFT) & CLK_PLL_SRC_MASK;
+
+ /* Get clock divider bypass value from IRAM Clock Divider registers mirror location */
+ clk_div = read_clk_div_bypass_backup(div_reg);
+
+ if (clk_div & CLK_DIV_BYPASS)
+ bypass = 1;
+ else
+ {
+ clk_div = readl(div_reg);
+ clk_div &= 0x1f;
+ }
+
+ pll_clk = HAL_get_pll_freq(pll_src);
+
+ if (bypass)
+ clk_out = pll_clk;
+ else
+ clk_out = pll_clk / clk_div;
+
+ return clk_out;
+}
+
+/* Get rate API callback */
+static unsigned long HAL_get_clk(struct clk *clk)
+{
+ return HAL_get_clk_freq(clk->clkgen_reg, clk->div_reg);
+}
+
+/* Get rate API callback for those clocks having Parent */
+static unsigned long HAL_get_parent_clk(struct clk *clk)
+{
+ return clk_get_rate(clk->parent);
+}
+/* Specific Get rate API callback for arm peripherals */
+static unsigned long HAL_get_arm_peri_clk(struct clk *clk)
+{
+ return clk_get_rate(clk->parent)/4 ;
+}
+
+/* Set rate APIs callbacks */
+static int HAL_set_clk(struct clk *clk,unsigned long rate)
+{
+ struct clk *clk_parent;
+ /* Get the parent clock */
+ clk_parent=clk_get_parent(clk);
+ if ( clk_parent || rate <= clk_parent->rate){
+ /* Set the divider value to reg */
+ HAL_set_clk_divider(rate,clk->clkgen_reg,clk->div_reg);
+ /* Set the rate value to clk structure */
+ clk->rate=rate;
+ return 0;
+ }else
+ return -EINVAL;
+}
+
+/* Initization of clk structure for individual device */
+
+static struct clk clk_pll0 = {
+ .name = "pll0",
+};
+
+static struct clk clk_pll1 = {
+ .name = "pll1",
+};
+
+static struct clk clk_pll2 = {
+ .name = "pll2",
+};
+
+static struct clk clk_pll3 = {
+ .name = "pll3",
+};
+
+
+
+static struct clk gemtx_clk = {
+ .name = "gemtx",
+ .enable_reg = GEMTX_CLK_CNTRL,
+ .clkgen_reg = GEMTX_CLK_CNTRL,
+ .div_reg = GEMTX_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk ddr_clk = {
+ .name = "ddr",
+ .enable_reg = DDR_CLK_CNTRL,
+ .clkgen_reg = DDR_CLK_CNTRL,
+ .div_reg = DDR_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+};
+
+static struct clk arm_clk = {
+ .name = "arm",
+ .enable_reg = A9DP_CLK_CNTRL,
+ .clkgen_reg = A9DP_CLK_CNTRL,
+ .div_reg = A9DP_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk l2cc_clk = {
+ .name = "l2cc",
+ .enable_reg = L2CC_CLK_CNTRL,
+ .clkgen_reg = L2CC_CLK_CNTRL,
+ .div_reg = L2CC_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk axi_clk = {
+ .name = "axi",
+ .enable_reg = AXI_CLK_CNTRL_0,
+ .clkgen_reg = AXI_CLK_CNTRL_0,
+ .div_reg = AXI_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk uart_clk = { /* Legacy UART */
+ .parent = &axi_clk,
+ .name = "uart",
+ .enable_reg = AXI_CLK_CNTRL_1,
+ .enable_mask = CLK_DOMAIN_UART_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk ipsec_eape_clk = {
+ .name = "ipsec_eape",
+ .enable_reg = IPSEC_CLK_CNTRL,
+ .clkgen_reg = IPSEC_CLK_CNTRL,
+ .div_reg = IPSEC_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+};
+
+static struct clk ipsec_spacc_clk = { /* IPSEC spacc clock for Elliptic EPN1802*/
+ .parent = &axi_clk,
+ .name = "ipsec_spacc",
+ .enable_reg = AXI_CLK_CNTRL_1,
+ .enable_mask = CLK_DOMAIN_IPSEC_SPACC_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk dpi_cie_clk = { /* DPI cie clock*/
+ .parent = &axi_clk,
+ .name = "dpi_cie",
+ .enable_reg = AXI_CLK_CNTRL_0,
+ .enable_mask = CLK_DOMAIN_DPI_CIE_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk dpi_decomp_clk = { /* DPI decomp clock*/
+ .parent = &axi_clk,
+ .name = "dpi_decomp",
+ .enable_reg = AXI_CLK_CNTRL_0,
+ .enable_mask = CLK_DOMAIN_DPI_DECOMP_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk DUS_clk = { /* DMA,FAST-UART and SMI clock */
+ .parent = &axi_clk,
+ .name = "DUS",
+ .enable_reg = AXI_CLK_CNTRL_1,
+ .enable_mask = CLK_DOMAIN_DUS_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk arm_peri_clk = {
+ .parent = &arm_clk,
+ .name = "arm_peri",
+ .enable_reg = A9DP_MPU_CLK_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_arm_peri_clk,
+};
+
+static struct clk hfe_core_clk = {
+ .name = "hfe_core",
+ .enable_reg = PFE_CLK_CNTRL,
+ .clkgen_reg = PFE_CLK_CNTRL,
+ .div_reg = PFE_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk spi_i2c_clk = {
+ .parent = &axi_clk,
+ .name = "spi_i2c",
+ .enable_reg = AXI_CLK_CNTRL_1,
+ .enable_mask = CLK_DOMAIN_SPI_I2C_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk tdmNTG_clk = {
+ .name = "ntgref",
+ .enable_reg = TDMNTG_REF_CLK_CNTRL,
+ .clkgen_reg = TDMNTG_REF_CLK_CNTRL,
+ .div_reg = TDMNTG_REF_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk pcie0_clk = {
+ .parent = &axi_clk,
+ .name = "pcie0",
+ .enable_reg = AXI_CLK_CNTRL_2,
+ .enable_mask = CLK_DOMAIN_PCIE0_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk pcie1_clk = {
+ .parent = &axi_clk,
+ .name = "pcie1",
+ .enable_reg = AXI_CLK_CNTRL_2,
+ .enable_mask = CLK_DOMAIN_PCIE1_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk usb0_clk = {
+ .parent = &axi_clk,
+ .name = "usb0",
+ .enable_reg = AXI_CLK_CNTRL_2,
+ .enable_mask = CLK_DOMAIN_USB0_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk usb1_clk = {
+ .parent = &axi_clk,
+ .name = "usb1",
+ .enable_reg = AXI_CLK_CNTRL_2,
+ .enable_mask = CLK_DOMAIN_USB1_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+
+static struct clk sata_clk = { /* SATA AXI clock */
+ .parent = &axi_clk,
+ .name = "sata",
+ .enable_reg = AXI_CLK_CNTRL_2,
+ .enable_mask = CLK_DOMAIN_SATA_MASK,
+ .get_rate = HAL_get_parent_clk,
+};
+static struct clk sata_oob_clk = {
+ .name = "sata_oob", /* SATA PMU alive clock */
+ .enable_reg = SATA_OOB_CLK_CNTRL,
+ .clkgen_reg = SATA_OOB_CLK_CNTRL,
+ .div_reg = SATA_OOB_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk sata_pmu_clk = {
+ .name = "sata_pmu", /* SATA core clock control */
+ .enable_reg = SATA_PMU_CLK_CNTRL,
+ .clkgen_reg = SATA_PMU_CLK_CNTRL,
+ .div_reg = SATA_PMU_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk ext_phy0_clk = {
+ .name = "ext_phy0",
+ .enable_reg = EXTPHY0_CLK_CNTRL,
+ .clkgen_reg = EXTPHY0_CLK_CNTRL,
+ .div_reg = EXTPHY0_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk ext_phy1_clk = {
+ .name = "ext_phy1",
+ .enable_reg = EXTPHY1_CLK_CNTRL,
+ .clkgen_reg = EXTPHY1_CLK_CNTRL,
+ .div_reg = EXTPHY1_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk ext_phy2_clk = {
+ .name = "ext_phy2",
+ .enable_reg = EXTPHY2_CLK_CNTRL,
+ .clkgen_reg = EXTPHY2_CLK_CNTRL,
+ .div_reg = EXTPHY2_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk tpi_clk = {
+ .name = "tpi",
+ .enable_reg = TPI_CLK_CNTRL,
+ .clkgen_reg = TPI_CLK_CNTRL,
+ .div_reg = TPI_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk csys_clk = {
+ .name = "csys",
+ .enable_reg = CSYS_CLK_CNTRL,
+ .clkgen_reg = CSYS_CLK_CNTRL,
+ .div_reg = CSYS_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk tsuntg_clk = {
+ .name = "tsuntg",
+ .enable_reg = TSUNTG_REF_CLK_CNTRL,
+ .clkgen_reg = TSUNTG_REF_CLK_CNTRL,
+ .div_reg = TSUNTG_REF_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk sata_occ_clk = {
+ .name = "sata_occ",
+ .enable_reg = SATA_OCC_CLK_CNTRL,
+ .clkgen_reg = SATA_OCC_CLK_CNTRL,
+ .div_reg = SATA_OCC_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk pcie_occ_clk = {
+ .name = "pcie_occ",
+ .enable_reg = PCIE_OCC_CLK_CNTRL,
+ .clkgen_reg = PCIE_OCC_CLK_CNTRL,
+ .div_reg = PCIE_OCC_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+static struct clk sgmii_occ_clk = {
+ .name = "sgmii_occ",
+ .enable_reg = SGMII_OCC_CLK_CNTRL,
+ .clkgen_reg = SGMII_OCC_CLK_CNTRL,
+ .div_reg = SGMII_OCC_CLK_DIV_CNTRL,
+ .enable_mask = CLK_DOMAIN_MASK,
+ .get_rate = HAL_get_clk,
+ .set_rate = HAL_set_clk,
+};
+
+/* These clocks are visible outside this module
+ * and can be initialized , this list could be expanded
+ * according to new device support.
+ */
+static struct clk *c2k_clks[] __initdata = {
+ &clk_pll0,
+ &clk_pll1,
+ &clk_pll2,
+ &clk_pll3,
+ &uart_clk,
+ &DUS_clk,
+ &gemtx_clk,
+ &ipsec_eape_clk,
+ &ipsec_spacc_clk,
+ &dpi_cie_clk,
+ &dpi_decomp_clk,
+ &ddr_clk,
+ &arm_clk,
+ &l2cc_clk,
+ &axi_clk,
+ &arm_peri_clk,
+ &hfe_core_clk,
+ &spi_i2c_clk,
+ &tdmNTG_clk,
+ &pcie0_clk,
+ &pcie1_clk,
+ &usb0_clk,
+ &usb1_clk,
+ &sata_clk,
+ &sata_oob_clk,
+ &sata_pmu_clk,
+ &ext_phy0_clk,
+ &ext_phy1_clk,
+ &ext_phy2_clk,
+ &tpi_clk,
+ &csys_clk,
+ &tsuntg_clk,
+ &sata_occ_clk,
+ &pcie_occ_clk,
+ &sgmii_occ_clk,
+};
+
+static struct clk_lookup c2k_clksreg[] = {
+ { .clk = &clk_pll0, .con_id = "pll0"},
+ { .clk = &clk_pll1, .con_id = "pll1"},
+ { .clk = &clk_pll2, .con_id = "pll2"},
+ { .clk = &clk_pll3, .con_id = "pll3"},
+ { .clk = &uart_clk, .con_id = "uart"},
+ { .clk = &DUS_clk, .con_id = "DUS"},
+ { .clk = &gemtx_clk, .con_id = "gemtx"},
+ { .clk = &ipsec_eape_clk, .con_id = "ipsec_eape"},
+ { .clk = &ipsec_spacc_clk, .con_id = "ipsec_spacc"},
+ { .clk = &dpi_cie_clk, .con_id = "dpi_cie"},
+ { .clk = &dpi_decomp_clk, .con_id = "dpi_decomp"},
+ { .clk = &ddr_clk, .con_id = "ddr"},
+ { .clk = &arm_clk, .con_id = "arm"},
+ { .clk = &l2cc_clk, .con_id = "l2cc"},
+ { .clk = &axi_clk, .con_id = "axi"},
+ { .clk = &arm_peri_clk, .con_id = "arm_peri"},
+ { .clk = &hfe_core_clk, .con_id = "hfe_core"},
+ { .clk = &spi_i2c_clk, .con_id = "spi_i2c"},
+ { .clk = &tdmNTG_clk, .con_id = "ntgref"},
+ { .clk = &pcie0_clk, .con_id = "pcie0"},
+ { .clk = &pcie1_clk, .con_id = "pcie1"},
+ { .clk = &usb0_clk, .con_id = "usb0"},
+ { .clk = &usb1_clk, .con_id = "usb1"},
+ { .clk = &sata_clk, .con_id = "sata"},
+ { .clk = &sata_oob_clk, .con_id = "sata_oob"},
+ { .clk = &sata_pmu_clk, .con_id = "sata_pmu"},
+ { .clk = &ext_phy0_clk, .con_id = "ext_phy0"},
+ { .clk = &ext_phy1_clk, .con_id = "ext_phy1"},
+ { .clk = &ext_phy2_clk, .con_id = "ext_phy2"},
+ { .clk = &tpi_clk, .con_id = "tpi"},
+ { .clk = &csys_clk, .con_id = "csys"},
+ { .clk = &tsuntg_clk, .con_id = "tsuntg"},
+ { .clk = &sata_occ_clk, .con_id = "sata_occ"},
+ { .clk = &pcie_occ_clk, .con_id = "pcie_occ"},
+ { .clk = &sgmii_occ_clk, .con_id = "sgmii_occ"},
+};
+
+/* Initilize all the available clocks */
+int clk_init(void){
+ struct clk **clkp;
+ int pll_no;
+
+ spin_lock_init(&clock_lock);
+
+ /* Determine the barebox configured pll0,pll1,pll2,pll3 rate value */
+ clk_pll0.rate = HAL_get_pll_freq(PLL0);
+ clk_pll1.rate = HAL_get_pll_freq(PLL1);
+ clk_pll2.rate = HAL_get_pll_freq(PLL2);
+ clk_pll3.rate = HAL_get_pll_freq(PLL3);
+
+ /* Set the NTG ref clock to PLL src (gemtx PLL source)
+ * Currently it is not set from barebox,set here.
+ */
+ pll_no = HAL_get_clock_pll_source(gemtx_clk.clkgen_reg);
+ HAL_set_clock_pll_source(tdmNTG_clk.clkgen_reg,pll_no);
+
+ pr_info("PLL0 running at %ld MHz, PLL1 at %ld MHz PLL2 running at %ld MHz PLL3 running at %ld MHz\n",
+ clk_pll0.rate/1000000, clk_pll1.rate/1000000, clk_pll2.rate/1000000, clk_pll3.rate/1000000);
+ /* Initilization of Clocks present in C2k device */
+ for (clkp = c2k_clks; clkp < c2k_clks + ARRAY_SIZE(c2k_clks);clkp++) {
+ struct clk *clk = *clkp;
+ /* Setting the parent clock in the init
+ * Only to those who can be configured with PLL source
+ */
+ if (!clk->parent){
+ if (clk->clkgen_reg){ /* This check is for not to set parent for PLLS */
+ pll_no = HAL_get_clock_pll_source(clk->clkgen_reg);
+ switch (pll_no)
+ {
+ case PLL0:
+ clk_set_parent(clk,&clk_pll0);
+ break;
+ case PLL1:
+ clk_set_parent(clk,&clk_pll1);
+ break;
+ case PLL2:
+ clk_set_parent(clk,&clk_pll2);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ /* Get The Device Clock Rate Values */
+ if ( clk->get_rate)
+ clk->rate=clk->get_rate(clk);
+ pr_debug("%s: clock %s, rate %ld\n",__func__, clk->name, clk->rate);
+ }
+
+ /* Creation of Clock Device Tree */
+ clkdev_add_table(c2k_clksreg, ARRAY_SIZE(c2k_clksreg));
+ return 0;
+
+}
+EXPORT_SYMBOL(clk_init);
+
+/*
+ * Disable any unused clocks left on by the bootloader(Barebox)
+ */
+static int __init clk_disable_unused(void)
+{
+ struct clk **clkp;
+
+ for (clkp = c2k_clks; clkp < c2k_clks + ARRAY_SIZE(c2k_clks);clkp++) {
+ struct clk *clk = *clkp;
+ if (clk->usecount > 0)
+ continue;
+
+ /* FIXME Currently there is no clock FW support for the
+ * following clocks in corresponding driver . So skipping these
+ * clocks from by default disable state.
+ * All other unused clocks will be disabled.
+ */
+ if ( strcmp(clk->name, "hfe_core") &&
+ strcmp(clk->name, "ipsec_eape") &&
+ strcmp(clk->name, "pll3") &&
+ strcmp(clk->name, "arm_peri") )
+ {
+ pr_info("Clocks: disabled unused %s\n", clk->name);
+ __clk_disable_unused(clk);
+ }
+ }
+ return 0;
+}
+late_initcall(clk_disable_unused);
diff --git a/arch/arm/mach-comcerto/comcerto-2000.c b/arch/arm/mach-comcerto/comcerto-2000.c
new file mode 100644
index 0000000..b5b0cb7
--- /dev/null
+++ b/arch/arm/mach-comcerto/comcerto-2000.c
@@ -0,0 +1,1027 @@
+/*
+ * linux/arch/arm/mach-comcerto/comcerto-2000.c
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/memblock.h>
+
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <mach/irqs.h>
+#include <linux/delay.h>
+#include <asm/pmu.h>
+#include <mach/dma.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/smp.h>
+#include <linux/uio_driver.h>
+
+#include <asm/mach-types.h>
+#include <asm/smp_twd.h>
+#include <asm/localtimer.h>
+
+#include <asm/mach/time.h>
+#include <asm/hardware/gic.h>
+#include <mach/reset.h>
+
+#include <mach/hardware.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <mach/comcerto-2000.h>
+#include <linux/ahci_platform.h>
+#include <mach/serdes-c2000.h>
+#include <linux/clockchips.h>
+#include <linux/clk.h>
+#include <mach/comcerto-2000/clock.h>
+#include <mach/comcerto-2000/pm.h>
+#include <mach/gpio.h>
+
+struct c2k_gpio_pin_stat_info c2k_gpio_pin_stat =
+{
+ .c2k_gpio_pins_0_31 = 0x0,
+ .c2k_gpio_pins_32_63 = 0x0,
+};
+
+#ifdef CONFIG_CACHE_L2X0
+void __iomem *l2cache_base;
+#endif
+
+/***********************************************************
+ * Virtual address Mapping *
+ * *
+ ***********************************************************/
+
+static struct map_desc comcerto_io_desc[] __initdata =
+{
+#ifdef CONFIG_COMCERTO_MSP
+ {
+ .virtual = COMCERTO_MSP_VADDR,
+ .pfn = __phys_to_pfn(COMCERTO_MSP_DDR_BASE),
+ .length = COMCERTO_MSP_DDR_SIZE_CB,
+ .type = MT_MSP
+ },
+ {
+ .virtual = (COMCERTO_MSP_VADDR + COMCERTO_MSP_DDR_SIZE_CB),
+ .pfn = __phys_to_pfn(COMCERTO_MSP_DDR_BASE + COMCERTO_MSP_DDR_SIZE_CB),
+ .length = COMCERTO_MSP_DDR_SIZE_NCNB,
+ .type = MT_MSP_NCNB
+ },
+ {
+ .virtual = COMCERTO_PFE_VADDR,
+ .pfn = __phys_to_pfn(COMCERTO_PFE_DDR_BASE),
+ .length = COMCERTO_PFE_DDR_SIZE,
+ .type = MT_DEVICE
+ },
+ {
+ .virtual = COMCERTO_PFE_AXI_VADDR,
+ .pfn = __phys_to_pfn(COMCERTO_AXI_PFE_BASE),
+ .length = COMCERTO_AXI_PFE_SIZE,
+ .type = MT_DEVICE
+ },
+#endif /* CONFIG_COMCERTO_MSP */
+#if defined(CONFIG_PCI)
+ {
+ .virtual = COMCERTO_AXI_PCIe0_VADDR_BASE,
+ .pfn = __phys_to_pfn(COMCERTO_AXI_PCIe0_BASE),
+ .length = SZ_16M,
+ .type = MT_DEVICE
+ },
+ {
+ .virtual = COMCERTO_AXI_PCIe1_VADDR_BASE,
+ .pfn = __phys_to_pfn(COMCERTO_AXI_PCIe1_BASE),
+ .length = SZ_16M,
+ .type = MT_DEVICE
+ },
+#endif
+ {
+ .virtual = COMCERTO_SCU_VADDR,
+ .pfn = __phys_to_pfn(COMCERTO_SCU_BASE),
+ .length = SZ_128K,
+ .type = MT_DEVICE
+ },
+ {
+ .virtual = IRAM_MEMORY_VADDR,
+ .pfn = __phys_to_pfn(COMCERTO_AXI_IRAM_BASE),
+ .length = IRAM_MEMORY_SIZE,
+ .type = MT_DEVICE
+ },
+ {
+ .virtual = COMCERTO_APB_VADDR,
+ .pfn = __phys_to_pfn(COMCERTO_AXI_APB_BASE),
+ .length = COMCERTO_APB_SIZE,
+ .type = MT_DEVICE
+ },
+ {
+ .virtual = COMCERTO_AXI_UART_SPI_VADDR,
+ .pfn = __phys_to_pfn(COMCERTO_AXI_UART_SPI_BASE),
+ .length = COMCERTO_AXI_UART_SPI_SIZE,
+ .type = MT_DEVICE
+ },
+ {
+ .virtual = COMCERTO_DECT_VADDR,
+ .pfn = __phys_to_pfn(COMCERTO_AXI_DECT_BASE),
+ .length = 2 * SZ_1M,
+ .type = MT_DEVICE
+ },
+ {
+ .virtual = COMCERTO_SEMA_VADDR,
+ .pfn = __phys_to_pfn(COMCERTO_AXI_SEMA_BASE),
+ .length = SZ_16M,
+ .type = MT_DEVICE
+ },
+};
+
+#if defined(CONFIG_COMCERTO_64K_PAGES)
+#define PFE_DMA_SIZE (4 * SZ_1M)
+#else
+#define PFE_DMA_SIZE (16 * SZ_1M)
+#endif
+
+#define DSPG_DECT_CSS_DMA_SIZE (10 * SZ_1M)
+
+void __init device_map_io(void)
+{
+ unsigned long size = PFE_DMA_SIZE;
+
+#if defined(CONFIG_DSPG_DECT_CSS)
+ size += DSPG_DECT_CSS_DMA_SIZE;
+#endif
+
+ iotable_init(comcerto_io_desc, ARRAY_SIZE(comcerto_io_desc));
+
+ /* Increase consistent DMA zone size */
+ init_consistent_dma_size(size);
+}
+
+
+void __init device_irq_init(void)
+{
+ gic_init(0, SGI_IRQ(1), (void *)COMCERTO_GIC_DIST_VADDR, (void *)COMCERTO_GIC_CPU_VADDR);
+}
+
+/************************************************************************
+ * GPIO
+ ************************************************************************/
+static __init void gpio_init(void)
+{
+
+#if defined(CONFIG_COMCERTO_UART1_SUPPORT)
+ writel(readl(COMCERTO_GPIO_MISC_PIN_SELECT) & ~0x3, COMCERTO_GPIO_MISC_PIN_SELECT);
+#endif
+
+#if defined(CONFIG_COMCERTO_UART0_SUPPORT)
+ writel((readl(COMCERTO_GPIO_PIN_SELECT_REG) & ~UART0_GPIO) | UART0_BUS, COMCERTO_GPIO_PIN_SELECT_REG);
+ c2k_gpio_pin_stat.c2k_gpio_pins_0_31 |= UART0_GPIO_PIN; /* GPIOs 8 to 11 are used for UART0 */
+#endif
+
+ /*
+ * Configure each GPIO to be Output or Input
+ * When Input, Configure to be Input, IRQ
+ * When Input IRQ, Configure to be IRQ Rising Edge or IRQ falling Edge
+ */
+
+ /*[FIXME]: GPIO Output, others are input*/
+ __raw_writel(__raw_readl(COMCERTO_GPIO_OE_REG) | COMCERTO_OUTPUT_GPIO, COMCERTO_GPIO_OE_REG);
+
+ /*[FIXME]: GPIO IRQ Configuration */
+ __raw_writel(COMCERTO_IRQ_RISING_EDGE_GPIO, COMCERTO_GPIO_INT_CFG_REG);
+
+#if !defined(CONFIG_C2K_MFCN_EVM)
+ /* [FIXME]: Need to have proper defines for enabling the GPIO irq */
+ __raw_writel(__raw_readl(COMCERTO_GPIO_OE_REG) | (0x1 << 5), COMCERTO_GPIO_OE_REG); // enable GPIO5 (SLIC_RESET_n) as output
+ __raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) | (0x1 << 5), COMCERTO_GPIO_OUTPUT_REG); // clear reset
+ udelay(15);
+ __raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) & ~(0x1 << 5), COMCERTO_GPIO_OUTPUT_REG); // put in reset
+ udelay(15);
+ __raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) | (0x1 << 5), COMCERTO_GPIO_OUTPUT_REG); // clear reset after some time
+#endif
+ __raw_writel(0x4, COMCERTO_GPIO_INT_CFG_REG); /* si3227 is falling edge interrupt(gpio1) */
+
+
+ /* [FIXME]: Are pins GPIO or pins used by another block*/
+ //__raw_writel(COMCERTO_GPIO_PIN_USAGE, COMCERTO_GPIO_IOCTRL_REG);
+}
+
+/************************************************************************
+ * Expansion Bus
+ ************************************************************************/
+
+/*This variable is provided by the board file*/
+extern int comcerto_exp_values[5][7];
+
+static __init void exp_bus_init(void)
+{
+ int cs;
+ u32 cs_enable;
+ unsigned int axi_clk, clk_div;
+ struct clk *clk_axi;
+
+ /*First, Reset the Expansion block*/
+ __raw_writel(0x1, COMCERTO_EXP_SW_RST_R);
+
+ while (readl(COMCERTO_EXP_SW_RST_R) & 0x1) ;
+
+ /* Clock divider configuration, get the AXI clock first
+ * AXI clock will be used for refernce count , as exp bus
+ * also have a dependancy with AXI.
+ */
+ clk_axi = clk_get(NULL,"axi");
+
+ if (IS_ERR(clk_axi)) {
+ pr_err("comcerto_Device_init: Unable to obtain axi clock: %ld\n",PTR_ERR(clk_axi));
+ }
+
+ /*Enable the AXI clock */
+ if (clk_enable(clk_axi)){
+ pr_err("%s: Unable to enable axi clock:\n",__func__);
+ }
+
+ /* Get the AXI clock rate in HZ */
+ axi_clk = clk_get_rate(clk_axi);
+ /* Round divider up */
+ clk_div = (axi_clk + COMCERTO_EXPCLK - 1) / COMCERTO_EXPCLK;
+
+ __raw_writel(clk_div, COMCERTO_EXP_CLOCK_DIV_R);
+
+ cs_enable = 0;
+ for (cs = 0; cs < 5; cs++) {
+ /*configure only enabled CS */
+ {
+ if (comcerto_exp_values[cs][0] == 1)
+ cs_enable |= EXP_CSx_EN(cs);
+
+ /*mode configuration*/
+ __raw_writel(comcerto_exp_values[cs][3], COMCERTO_EXP_CSx_CFG_R(cs));
+
+ /*Chip select Base configuration (start of address space)*/
+ __raw_writel(comcerto_exp_values[cs][1], COMCERTO_EXP_CSx_BASE_R(cs));
+
+ /*Chip select Segment size configuration (end of address space)*/
+ __raw_writel(comcerto_exp_values[cs][2], COMCERTO_EXP_CSx_SEG_R(cs));
+
+ /*Chip select timing configuration*/
+ /* [FIXME] : Using default timing values */
+ __raw_writel(comcerto_exp_values[cs][4], COMCERTO_EXP_CSx_TMG1_R(cs));
+ __raw_writel(comcerto_exp_values[cs][5], COMCERTO_EXP_CSx_TMG2_R(cs));
+ __raw_writel(comcerto_exp_values[cs][6], COMCERTO_EXP_CSx_TMG3_R(cs));
+ }
+ }
+
+ /*Chip Select activation*/
+ __raw_writel(EXP_CLK_EN | cs_enable, COMCERTO_EXP_CS_EN_R);
+}
+
+static u32 armv7_aux_ctrl_read(void)
+{
+ u32 val;
+
+ asm volatile ("mrc p15, 0, %0, c1, c0, 1" : "=r"(val));
+
+ printk(KERN_INFO "ARMv7 AUX CTRL(%d): %#x\n", smp_processor_id(), val);
+
+ return val;
+}
+
+static void armv7_aux_ctrl_write(u32 val)
+{
+ printk(KERN_INFO "ARMv7 AUX CTRL(%d): %#x\n", smp_processor_id(), val);
+ asm volatile ("mcr p15, 0, %0, c1, c0, 1" : : "r"(val));
+}
+
+static void armv7_aux_ctrl_setup(void *info)
+{
+ u32 aux_ctrl;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ aux_ctrl = armv7_aux_ctrl_read();
+
+#ifdef CONFIG_PL310_FULL_LINE_OF_ZERO
+ aux_ctrl |= (1 << 3);
+#endif
+
+#ifdef CONFIG_PL310_EXCLUSIVE_CACHE
+ aux_ctrl |= (1 << 7);
+#endif
+
+ armv7_aux_ctrl_write(aux_ctrl);
+
+ local_irq_restore(flags);
+}
+
+#ifdef CONFIG_CACHE_L2X0
+void l2x0_latency(u32 tag_ram_setup_lat, u32 tag_ram_rd_lat, u32 tag_ram_wr_lat, u32 data_ram_setup_lat, u32 data_ram_rd_lat, u32 data_ram_wr_lat)
+{
+ u32 val;
+
+ val = ((tag_ram_wr_lat - 1) << COMCERTO_L2CC_WR_LAT_SHIFT) | ((tag_ram_rd_lat - 1) << COMCERTO_L2CC_RD_LAT_SHIFT) | (tag_ram_setup_lat - 1);
+ writel(val, l2cache_base + L2X0_TAG_LATENCY_CTRL);
+
+ val = ((data_ram_wr_lat - 1) << COMCERTO_L2CC_WR_LAT_SHIFT) | ((data_ram_rd_lat - 1) << COMCERTO_L2CC_RD_LAT_SHIFT) | (data_ram_setup_lat - 1);
+ writel(val, l2cache_base + L2X0_DATA_LATENCY_CTRL);
+}
+
+void comcerto_l2cc_init(void)
+{
+ unsigned int aux_val, aux_mask;
+ unsigned int associativity, waysize;
+#ifdef CONFIG_L2X0_INSTRUCTION_ONLY
+ int i;
+#endif
+
+
+ l2cache_base = (void *)COMCERTO_L310_VADDR;
+ BUG_ON(!l2cache_base);
+
+ /* Set Latency of L2CC to minimum (i.e. 1 cycle) */
+ l2x0_latency(1, 1, 1, 1, 1, 1);
+
+ /* Set L2 address filtering, use L2CC M1 port for DDR accesses */
+ writel(0x80000000, l2cache_base + L2X0_ADDR_FILTER_END);
+ writel(0x00000000 | L2X0_ADDR_FILTER_EN, l2cache_base + L2X0_ADDR_FILTER_START);
+
+ associativity = (COMCERTO_L2CC_ASSOCIATIVITY_8WAY << COMCERTO_L2CC_ASSOCIATIVITY_SHIFT) & COMCERTO_L2CC_ASSOCIATIVITY_MASK;
+ waysize = (COMCERTO_L2CC_ASSOCIATIVITY_32KB << COMCERTO_L2CC_WAYSIZE_SHIFT) & COMCERTO_L2CC_WAYSIZE_MASK;
+ aux_val = associativity | waysize;
+ aux_mask = (COMCERTO_L2CC_ASSOCIATIVITY_MASK | COMCERTO_L2CC_WAYSIZE_MASK);
+
+ /* Shareable attribute override enable */
+ /* This prevents the cache from changing "normal memory/non-cacheable" accesses to
+ "normal memory/cacheable/writethrough no read/write allocate"*/
+ aux_val |= (1 << 22);
+ aux_mask |= (1 << 22);
+
+ /* Write allocate override, no write allocate */
+ aux_val |= (1 << 23);
+ aux_mask |= (3 << 23);
+
+#ifdef CONFIG_PL310_FULL_LINE_OF_ZERO
+ aux_val |= (1 << 0);
+ aux_mask |= (1 << 0);
+#endif
+
+#ifdef CONFIG_PL310_EARLY_WRITE_RESPONSE
+ aux_val |= (1 << 30);
+ aux_mask |= (1 << 30);
+#endif
+
+#ifdef CONFIG_PL310_STORE_BUFFER_DEVICE_LIMITATION
+ aux_val |= (1 << 11);
+ aux_mask |= (1 << 11);
+#endif
+
+#ifdef CONFIG_PL310_INSTRUCTION_PREFETCH
+ aux_val |= (1 << 29);
+ aux_mask |= (1 << 29);
+#endif
+
+#ifdef CONFIG_PL310_DATA_PREFETCH
+ aux_val |= (1 << 28);
+ aux_mask |= (1 << 28);
+#endif
+
+
+#ifdef CONFIG_PL310_EXCLUSIVE_CACHE
+ aux_val |= (1 << 12);
+ aux_mask |= (1 << 12);
+#endif
+
+ /* L2 8-way associativity with 32KB way size */
+ l2x0_init(l2cache_base, aux_val, aux_mask);
+
+#ifdef CONFIG_L2X0_INSTRUCTION_ONLY
+ for (i = 0; i < 8; i++)
+ writel_relaxed(0xffff, l2cache_base + L2X0_LOCKDOWN_WAY_D_BASE + i * L2X0_LOCKDOWN_STRIDE);
+
+ outer_flush_all();
+#endif
+
+ armv7_aux_ctrl_setup(NULL);
+
+ smp_call_function(armv7_aux_ctrl_setup, NULL, 1);
+}
+#endif
+
+#if defined(CONFIG_COMCERTO_SATA)
+
+#define SERDES_PHY1 1
+#define SERDES_PHY2 2
+
+static int comcerto_ahci_init(struct device *dev, void __iomem *mmio)
+{
+ struct serdes_regs_s *p_sata_phy_reg_file;
+ int serdes_regs_size;
+ u32 val;
+ int ref_clk_24;
+
+ /* Move SATA controller to DDRC2 port */
+ writel(readl(COMCERTO_GPIO_FABRIC_CTRL_REG) | 0x2, COMCERTO_GPIO_FABRIC_CTRL_REG);
+
+ val = readl(COMCERTO_GPIO_SYSTEM_CONFIG);
+ ref_clk_24 = val & (BIT_5_MSK|BIT_7_MSK);
+
+ if(ref_clk_24)
+ {
+ p_sata_phy_reg_file = &sata_phy_reg_file_24[0];
+ serdes_regs_size = sizeof(sata_phy_reg_file_24);
+ printk(KERN_INFO "SATA Serdes: 24Mhz ref clk\n");
+ }
+ else
+ {
+ p_sata_phy_reg_file = &sata_phy_reg_file_48[0];
+ serdes_regs_size = sizeof(sata_phy_reg_file_48);
+ printk(KERN_INFO "SATA Serdes: 48Mhz ref clk\n");
+ }
+
+ //Take SATA AXI domain out of reset
+ c2000_block_reset(COMPONENT_AXI_SATA,0);
+ //Bring SATA PMU and OOB out of reset
+ c2000_block_reset(COMPONENT_SATA_PMU,0);
+ c2000_block_reset(COMPONENT_SATA_OOB,0);
+
+ if ( (val & BOOT_SERDES1_CNF_SATA0) || (!(val & BOOT_SERDES2_CNF_SATA1)))
+ {
+ if (val & BOOT_SERDES1_CNF_SATA0)
+ {
+ //Bring Serdes1 out of reset
+ c2000_block_reset(COMPONENT_SERDES1,0);
+ //Bring SATA0 out of reset
+ c2000_block_reset(COMPONENT_SERDES_SATA0,0);
+
+ /* Serdes Initialization. */
+ if( serdes_phy_init(SERDES_PHY1, p_sata_phy_reg_file,
+ serdes_regs_size / sizeof(serdes_regs_t),
+ SD_DEV_TYPE_SATA) )
+ {
+ printk(KERN_ERR "%s: Failed to initialize serdes1 !!\n", __func__);
+ return -1;
+ }
+
+ }
+
+ if (!(val & BOOT_SERDES2_CNF_SATA1))
+ {
+ //Bring Serdes2 out of reset
+ c2000_block_reset(COMPONENT_SERDES2,0);
+ //Bring SATA1 out of reset
+ c2000_block_reset(COMPONENT_SERDES_SATA1,0);
+
+ /* Serdes Initialization. */
+ if( serdes_phy_init(SERDES_PHY2, p_sata_phy_reg_file,
+ serdes_regs_size / sizeof(serdes_regs_t),
+ SD_DEV_TYPE_SATA) )
+ {
+ printk(KERN_ERR "%s: Failed to initialize serdes2 !!\n", __func__);
+ return -1;
+ }
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_COMCERTO_UART0_SUPPORT) || defined(CONFIG_COMCERTO_UART1_SUPPORT)
+#define UART_DWC_USR 0x1F
+static int fastuart_handle_irq(struct uart_port *p)
+{
+ unsigned int iir = p->serial_in(p, UART_IIR);
+ unsigned int dummy;
+ if (serial8250_handle_irq(p, iir)) {
+ return 1;
+ } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
+ /* Clear the USR */
+ dummy = p->serial_in(p, UART_DWC_USR);
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+static struct resource comcerto_pmu_resources[] = {
+ {
+ .start = IRQ_A9_PMU0,
+ .end = IRQ_A9_PMU0,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = IRQ_A9_PMU1,
+ .end = IRQ_A9_PMU1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_pmu = {
+ .name = "arm-pmu",
+ .id = ARM_PMU_DEVICE_CPU,
+ .num_resources = ARRAY_SIZE(comcerto_pmu_resources),
+ .resource = comcerto_pmu_resources,
+};
+
+/* --------------------------------------------------------------------
+ * Serial interface
+ * -------------------------------------------------------------------- */
+#if defined(CONFIG_COMCERTO_UART0_SUPPORT) || defined(CONFIG_COMCERTO_UART1_SUPPORT)
+static struct plat_serial8250_port comcerto_uart_data[] = {
+#ifdef CONFIG_COMCERTO_UART1_SUPPORT
+ {
+ .mapbase = COMCERTO_AXI_UART1_BASE,
+ .membase = (void *)COMCERTO_AXI_UART1_VADDR,
+ .irq = IRQ_UART1,
+ .handle_irq = fastuart_handle_irq,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ },
+#endif
+#ifdef CONFIG_COMCERTO_UART0_SUPPORT
+ {
+ .mapbase = COMCERTO_AXI_UART0_BASE,
+ .membase = (void *)COMCERTO_AXI_UART0_VADDR,
+ .irq = IRQ_UART0,
+ .handle_irq = fastuart_handle_irq,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ },
+#endif
+ {
+ .flags = 0,
+ },
+};
+
+static struct platform_device comcerto_uart = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = comcerto_uart_data,
+ },
+};
+#endif
+
+#ifdef CONFIG_COMCERTO_MSP
+static struct resource comcerto_ved_resources[] = {
+ {
+ .name = "voip",
+ .start = COMCERTO_MSP_DDR_BASE,
+ .end = COMCERTO_MSP_DDR_BASE + COMCERTO_MSP_DDR_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "irq",
+ .start = IRQ_PTP0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_ved = {
+ .name = "ved",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(comcerto_ved_resources),
+ .resource = comcerto_ved_resources,
+};
+#endif /* CONFIG_COMCERTO_MSP */
+
+
+/* --------------------------------------------------------------------
+ * USB 3.0 Host
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_COMCERTO_USB3_SUPPORT)
+static struct resource comcerto_usb3_resource[] = {
+ [0] = {
+ .start = COMCERTO_AXI_USB3P0_BASE,
+ .end = COMCERTO_AXI_USB3P0_BASE + SZ_8M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_USB3,
+ .end = IRQ_USB3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 comcerto_usb3_dmamask = 0xfffff000;
+
+struct platform_device comcerto_device_usb3 = {
+ .name = "xhci-hcd",
+ .id = -1,
+ .resource = comcerto_usb3_resource,
+ .num_resources = ARRAY_SIZE(comcerto_usb3_resource),
+ .dev = {
+ .dma_mask = &comcerto_usb3_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * USB 2.0 Host
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_COMCERTO_USB2_SUPPORT)
+
+static u64 comcerto_dwc_otg_dmamask = 0xFFFFFFFF /*DMA_BIT_MASK(32)*/;
+
+static struct resource comcerto_dwc_otg_resources[] = {
+ {
+ .start = COMCERTO_AXI_USB2P0_BASE,
+ .end = COMCERTO_AXI_USB2P0_BASE + 0xFFFFFF,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_USB2,
+ .end = IRQ_USB2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device comcerto_dwc_otg_device = {
+ .name = "dwc_otg",
+ .resource = comcerto_dwc_otg_resources,
+ .num_resources = ARRAY_SIZE(comcerto_dwc_otg_resources),
+ .dev = {
+ .platform_data = NULL,
+ .dma_mask = &comcerto_dwc_otg_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ }
+};
+#endif
+
+#if defined(CONFIG_COMCERTO_SATA)
+static struct ahci_platform_data comcerto_ahci_pdata = {
+ .init = comcerto_ahci_init,
+};
+
+static struct resource comcerto_ahci_resource[] = {
+ [0] = {
+ .start = COMCERTO_AXI_SATA_BASE,
+ .end = COMCERTO_AXI_SATA_BASE + SZ_64K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SATA,
+ .end = IRQ_SATA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 comcerto_ahci_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device comcerto_device_ahci = {
+ .name = "ahci",
+ .id = -1,
+ .resource = comcerto_ahci_resource,
+ .num_resources = ARRAY_SIZE(comcerto_ahci_resource),
+ .dev = {
+ .platform_data = &comcerto_ahci_pdata,
+ .dma_mask = &comcerto_ahci_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+#endif
+
+/* --------------------------------------------------------------------
+ * XOR Engine
+ * -------------------------------------------------------------------- */
+
+static struct resource comcerto_xor_resource[] = {
+ {
+ .name = "xor base address",
+ .start = COMCERTO_APB_MDMA_BASE,
+ .end = COMCERTO_APB_MDMA_BASE + COMCERTO_APB_MDMA_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "IO2M IRQ",
+ .start = IRQ_MDMA_IO2M,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 comcerto_xor_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_xor_device = {
+ .name = "comcerto_xor",
+ .id = 0,
+ .dev = {
+ .dma_mask = &comcerto_xor_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(comcerto_xor_resource),
+ .resource = comcerto_xor_resource,
+};
+
+/* --------------------------------------------------------------------
+ * Basic C2K MDMA Engine
+ * -------------------------------------------------------------------- */
+
+static struct resource comcerto_dma_resource[] = {
+ {
+ .name = "c2k mdma base address",
+ .start = COMCERTO_APB_MDMA_BASE,
+ .end = COMCERTO_APB_MDMA_BASE + COMCERTO_APB_MDMA_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "IO2M IRQ",
+ .start = IRQ_MDMA_IO2M,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 comcerto_dma_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device comcerto_dma_device = {
+ .name = "comcerto_dma",
+ .id = 0,
+ .dev = {
+ .dma_mask = &comcerto_dma_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(comcerto_dma_resource),
+ .resource = comcerto_dma_resource,
+};
+
+#if defined(CONFIG_COMCERTO_EPAVIS)
+static u64 comcerto_epavis_cie_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device comcerto_device_epavis_cie = {
+ .name = "lc_cie",
+ .id = -1,
+ .dev = {
+ .platform_data = NULL,
+ .dma_mask = &comcerto_epavis_cie_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+static u64 comcerto_epavis_decomp_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device comcerto_device_epavis_decomp = {
+ .name = "lc_decomp",
+ .id = -1,
+ .dev = {
+ .platform_data = NULL,
+ .dma_mask = &comcerto_epavis_decomp_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+#endif
+
+#if defined(CONFIG_COMCERTO_CSYS_TPI_CLOCK)
+struct platform_device comcerto_device_tpi_csys_clk = {
+ .name = "tpi_csys",
+ .id = -1,
+ .dev = {
+ .platform_data = NULL,
+ },
+};
+#endif
+
+int usb3_clk_internal = 1;
+static int __init get_usb3_clk_mode(char *str)
+{
+ if (!strcmp(str, "no"))
+ usb3_clk_internal = 0;
+
+ return 1;
+}
+
+__setup("usb3_internal_clk=", get_usb3_clk_mode);
+
+int is_mac_zero(u8 *buf)
+{
+ unsigned long dm;
+ for (dm = 0; dm < 6; dm++){
+ if ((*(buf+dm)) != 0)
+ return 1;
+ }
+ return 0;
+}
+
+static int __init mac_addr_atoi(u8 mac_addr[], char *mac_addr_str)
+{
+ int i, j, k;
+ int str_incr_cnt = 0;
+
+ if (*mac_addr_str == ',') {
+ *mac_addr_str++;
+ str_incr_cnt++;
+ return str_incr_cnt;
+ }
+
+ for (i = 0; i < 6; i++) {
+
+ j = hex_to_bin(*mac_addr_str++);
+ str_incr_cnt++;
+ if (j == -1)
+ return str_incr_cnt;
+
+ k = hex_to_bin(*mac_addr_str++);
+ str_incr_cnt++;
+ if (k == -1)
+ return str_incr_cnt;
+
+ mac_addr_str++;
+ str_incr_cnt++;
+ mac_addr[i] = (j << 4) + k;
+ }
+
+ return str_incr_cnt;
+}
+
+u8 c2k_mac_addr[3][14];
+
+static void __init mac_addr_setup(char *str)
+{
+ int str_incr_cnt = 0;
+
+ if (*str++ != '=' || !*str) /* No mac addr specified */
+ return;
+
+ str_incr_cnt = mac_addr_atoi(c2k_mac_addr[0], str);
+
+ str += str_incr_cnt;
+
+ str_incr_cnt = mac_addr_atoi(c2k_mac_addr[1], str);
+
+ str += str_incr_cnt;
+
+ mac_addr_atoi(c2k_mac_addr[2], str);
+}
+__setup("mac_addr", mac_addr_setup);
+
+void __init mac_addr_init(struct comcerto_pfe_platform_data * comcerto_pfe_data_ptr)
+{
+ u8 gem_port_id;
+
+ for (gem_port_id = 0; gem_port_id < NUM_GEMAC_SUPPORT; gem_port_id++) {
+ if (is_mac_zero(c2k_mac_addr[gem_port_id])) /* If mac is non-zero */
+ comcerto_pfe_data_ptr->comcerto_eth_pdata[gem_port_id].mac_addr = c2k_mac_addr[gem_port_id];
+ }
+
+}
+
+static struct platform_device *comcerto_common_devices[] __initdata = {
+#if defined(CONFIG_COMCERTO_UART0_SUPPORT) || defined(CONFIG_COMCERTO_UART1_SUPPORT)
+ &comcerto_uart,
+#endif
+#ifdef CONFIG_COMCERTO_MSP
+ &comcerto_ved,
+#endif /* CONFIG_COMCERTO_MSP */
+ &comcerto_pmu,
+
+#if defined(CONFIG_COMCERTO_USB3_SUPPORT)
+ &comcerto_device_usb3,
+#endif
+
+#if defined(CONFIG_COMCERTO_USB2_SUPPORT)
+ &comcerto_dwc_otg_device,
+#endif
+
+#if defined(CONFIG_COMCERTO_SATA)
+ &comcerto_device_ahci,
+#endif
+// &comcerto_xor_device,
+ &comcerto_dma_device,
+#if defined(CONFIG_COMCERTO_EPAVIS)
+ &comcerto_device_epavis_cie,
+ &comcerto_device_epavis_decomp,
+#endif
+#if defined(CONFIG_COMCERTO_CSYS_TPI_CLOCK)
+ &comcerto_device_tpi_csys_clk,
+#endif
+};
+
+void __init device_init(void)
+{
+ /* Default value for the bit mask */
+ unsigned int default_host_utilpe_shared_bitmask = ~(USB2p0_IRQ|WOL_IRQ);
+ struct clk *axi_clk,*ddr_clk,*arm_clk,*l2cc_clk;
+ HAL_clk_div_backup_relocate_table ();
+ system_rev = (readl(COMCERTO_GPIO_DEVICE_ID_REG) >> 24) & 0xf;
+
+ /* Initialize the reset driver here */
+ reset_init();
+
+ /* Enable the AXI,DDR,A9 susbsystem clock
+ * this is just for s/w use count house keeping.
+ * These clocks will never be disabled from bootloader while
+ * booting. In fact we are keeping this because for these clocks ,
+ * we dont have any driver to do a clk_enable. so doing it here.
+ */
+
+ /* Get the AXI clk structure */
+ axi_clk = clk_get(NULL,"axi");
+ if (IS_ERR(axi_clk)) {
+ pr_err("%s: Unable to obtain AXI clock: %ld\n",__func__,PTR_ERR(axi_clk));
+ /* System cannot proceed from here */
+ BUG();
+ }
+ /* Enable the AXI clk */
+ if (clk_enable(axi_clk)){
+ pr_err("%s: Unable to enable AXI clock:\n",__func__);
+ /* System cannot proceed from here */
+ BUG();
+ }
+
+ /* Get the DDR clock structure */
+ ddr_clk = clk_get(NULL,"ddr");
+ if (IS_ERR(ddr_clk)) {
+ pr_err("%s: Unable to obtain DDR clock: %ld\n",__func__,PTR_ERR(ddr_clk));
+ /* System cannot proceed from here */
+ BUG();
+ }
+ /* Enable the DDR clk */
+ if (clk_enable(ddr_clk)){
+ pr_err("%s: Unable to enable DDR clock:\n",__func__);
+ /* System cannot proceed from here */
+ BUG();
+ }
+
+ /* Get the CPU(A9) clock */
+ arm_clk = clk_get(NULL,"arm");
+ if (IS_ERR(arm_clk)) {
+ pr_err("%s: Unable to obtain A9(arm) clock: %ld\n",__func__,PTR_ERR(arm_clk));
+ /* System cannot proceed from here */
+ BUG();
+ }
+ /* Enable the ARM clk */
+ if (clk_enable(arm_clk)){
+ pr_err("%s: Unable to enable A9(arm) clock:\n",__func__);
+ /* System cannot proceed from here */
+ BUG();
+ }
+
+ /* Get the L2CC clock */
+ l2cc_clk = clk_get(NULL,"l2cc");
+ if (IS_ERR(l2cc_clk)) {
+ pr_err("%s: Unable to obtain L2CC clock: %ld\n",__func__,PTR_ERR(l2cc_clk));
+ /* L2CC initilization cannot proceed from here */
+ BUG();
+ }
+
+ /* Enable the L2CC clk */
+ if (clk_enable(l2cc_clk)){
+ pr_err("%s: Unable to enable L2CC clock:\n",__func__);
+ /* L2CC initilization cannot proceed from here */
+ BUG();
+ }
+
+#ifdef CONFIG_CACHE_L2X0
+ comcerto_l2cc_init();
+#endif
+
+ exp_bus_init();
+
+ gpio_init();
+
+#ifdef CONFIG_COMCERTO_TDM_CLOCK
+ // [FIXME] Take TDM out of reset
+ //writel(readl(COMCERTO_BLOCK_RESET_REG) | TDM_RST, COMCERTO_BLOCK_RESET_REG);
+#endif
+ /* Default bit mask is applied here , which will be passed to Util-Pe*/
+ c2k_pm_bitmask_store(default_host_utilpe_shared_bitmask);
+
+ platform_add_devices(comcerto_common_devices, ARRAY_SIZE(comcerto_common_devices));
+}
+
+void __init platform_reserve(void)
+{
+ /* Allocate DDR block used by PFE/MSP, the base address is fixed so that util-pe code can
+ be linked at a fixed address */
+ if (memblock_reserve(COMCERTO_DDR_SHARED_BASE, COMCERTO_DDR_SHARED_SIZE) < 0)
+ BUG();
+
+ if (memblock_free(COMCERTO_DDR_SHARED_BASE, COMCERTO_DDR_SHARED_SIZE) < 0)
+ BUG();
+
+ if (memblock_remove(COMCERTO_DDR_SHARED_BASE, COMCERTO_DDR_SHARED_SIZE) < 0)
+ BUG();
+}
diff --git a/arch/arm/mach-comcerto/comcerto-tpicsys.c b/arch/arm/mach-comcerto/comcerto-tpicsys.c
new file mode 100644
index 0000000..e11e4f8
--- /dev/null
+++ b/arch/arm/mach-comcerto/comcerto-tpicsys.c
@@ -0,0 +1,118 @@
+/*
+ * linux/arch/arm/mach-comcerto/comcerto-tpicsys.c
+ *
+ * Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+struct clk *clk_tpi,*clk_csys;
+
+static int tpi_csys_probe(struct platform_device *pdev)
+{
+ int err=0;
+ /* Get the refrence to TPI clk structure */
+ clk_tpi = clk_get(NULL,"tpi");
+
+ /* Error Handling , if no TPI clock reference: return error */
+ if (IS_ERR(clk_tpi)) {
+ pr_err("%s: Unable to obtain clock: %ld\n",__func__,PTR_ERR(clk_tpi));
+ err = PTR_ERR(clk_tpi);
+ goto err_tpi_get;
+ }
+
+ /* Enable the TPI clock ,required for cortex A9 JTAG */
+ err = clk_enable(clk_tpi);
+ if (err){
+ pr_err("%s: TPI clock failed to enable:\n",__func__);
+ goto err_tpi_enable;
+ }
+
+ /* Get the CSYS clk structure ,required ofr cortex A9 coresight*/
+ clk_csys = clk_get(NULL,"csys");
+
+ /* Error Handling , if no CSYS clock reference: return error
+ * Disable the TPI clock enabled before.
+ */
+ if (IS_ERR(clk_csys)) {
+ pr_err("%s: Unable to obtain clock: %ld\n",__func__,PTR_ERR(clk_csys));
+ err = PTR_ERR(clk_csys);
+ goto err_csys_get;
+ }
+
+ /*Enable the CSYS clock */
+ err = clk_enable(clk_csys);
+ if (err){
+ pr_err("%s: CSYS clock failed to enable:\n",__func__);
+ /* Disable the CSYS(A9 coresight clock also */
+ goto err_csys_enable;
+ }
+
+ return 0;
+
+err_csys_enable:
+ clk_put(clk_csys);
+err_csys_get:
+ clk_disable(clk_tpi);
+err_tpi_enable:
+ clk_put(clk_tpi);
+err_tpi_get:
+ return err;
+}
+
+static int tpi_csys_remove(struct platform_device *pdev)
+{
+
+ /* Disable the TPI/CSYS clock */
+ clk_disable(clk_tpi);
+ clk_put(clk_tpi);
+
+ clk_disable(clk_csys);
+ clk_put(clk_csys);
+
+ return 0;
+}
+
+
+/* Structure for Device Driver */
+static struct platform_driver tpi_csys_platform_driver = {
+ .probe = tpi_csys_probe,
+ .remove = tpi_csys_remove,
+ .driver = {
+ .name = "tpi_csys",
+ },
+};
+
+static int comcerto_tpi_csys_init(void)
+{
+ return platform_driver_register(&tpi_csys_platform_driver);
+}
+
+static void comcerto_tpi_csys_exit(void)
+{
+ platform_driver_unregister(&tpi_csys_platform_driver);
+}
+
+module_init(comcerto_tpi_csys_init);
+module_exit(comcerto_tpi_csys_exit);
+
+MODULE_DESCRIPTION("Comcerto TPI/CSYS");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-comcerto/comcerto-vwd.c b/arch/arm/mach-comcerto/comcerto-vwd.c
new file mode 100644
index 0000000..c5431d9
--- /dev/null
+++ b/arch/arm/mach-comcerto/comcerto-vwd.c
@@ -0,0 +1,55 @@
+/*
+ * linux/arch/arm/mach-comcerto/comcerto-vwd.c
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+
+
+static int comcerto_wifi_rx_dummy_hdlr(int (*hdlr)(struct sk_buff *skb));
+static int (*vwd_rx_hdlr)(struct sk_buff *) = comcerto_wifi_rx_dummy_hdlr;
+
+static int comcerto_wifi_rx_dummy_hdlr(int (*hdlr)(struct sk_buff *skb))
+{
+ return -1;
+}
+
+int comcerto_wifi_rx_fastpath_register(int (*hdlr)(struct sk_buff *skb))
+{
+ printk(KERN_INFO "%s:%d VWD Tx function registered\n", __func__, __LINE__ );
+ vwd_rx_hdlr = hdlr;
+
+ return 0;
+}
+
+void comcerto_wifi_rx_fastpath_unregister(void)
+{
+ printk(KERN_INFO "%s:%d VWD Tx function unregistered\n", __func__, __LINE__ );
+ vwd_rx_hdlr = comcerto_wifi_rx_dummy_hdlr;
+
+ return;
+}
+
+int comcerto_wifi_rx_fastpath(struct sk_buff *skb)
+{
+ return vwd_rx_hdlr(skb);
+
+}
+
+EXPORT_SYMBOL(comcerto_wifi_rx_fastpath_register);
+EXPORT_SYMBOL(comcerto_wifi_rx_fastpath_unregister);
+EXPORT_SYMBOL(comcerto_wifi_rx_fastpath);
diff --git a/arch/arm/mach-comcerto/dma.c b/arch/arm/mach-comcerto/dma.c
new file mode 100644
index 0000000..9911c9b
--- /dev/null
+++ b/arch/arm/mach-comcerto/dma.c
@@ -0,0 +1,214 @@
+/*
+;=======================================================================
+; Filename : dma.c
+; Purpose : DW DMA APIs for Fast SPI and Fast UART.
+;======================================================================
+*/
+
+#include <mach/serial.h>
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <asm/io.h>
+#include <linux/module.h>
+
+
+
+
+/*
+ ********************************************
+ * dma_ssi_xfer_cmplete_chk ()
+ *
+ * Check for the complete of DMA data xfer on given channal no.
+ *
+ ********************************************
+ */
+retcode dma_xfer_cmplete_chk(unsigned int ch_no)
+{
+ unsigned int channel_status = 0xF;
+
+ while(channel_status != 0x0)
+ {
+ channel_status = readl(DW_DMA_DMAC_CH_EN_REG);
+ channel_status = channel_status & ((1 << ch_no) & 0xFF);
+ }
+
+ return RETCODE_OK;
+}
+
+EXPORT_SYMBOL(dma_xfer_cmplete_chk);
+
+/*
+ ****************************************
+ * dma_configure ()
+ *
+ * Single-block Transfer -- without write-back of control
+ * and status information enabled at the end of the single-block transfer
+ ****************************************
+ */
+void dma_configure(unsigned int source_add, unsigned int target_add, unsigned int data_len, unsigned int ch_no)
+{
+ unsigned int dma_channel = (1 << ch_no) & 0xFF ;
+ unsigned int ch_reg_multiplier = 0;
+ unsigned int ch_ctrl_l = 0;
+ unsigned int ch_cfg_l = 0;
+ unsigned int ch_cfg_h = 0;
+
+ /* DMA enable */
+ writel(DMA_GLOBAL_ENABLE, DW_DMA_DMAC_DMA_CFG_REG);
+
+ /* verify channel is not busy, done with last transaction */
+ dma_xfer_cmplete_chk(ch_no);
+
+ /* Configure DMA handshaking */
+
+ /* Writing a 1 to the Last Destination Transaction Request Register initiates a transaction. */
+ writel(((dma_channel << DMA_REG_WE_SHIFT) | (0x0 & 0xFF)), DW_DMA_DMAC_LST_DST_REG);
+
+/* The type of transaction, single or burst, depends on the state of the corresponding channel bit
+ * in the Single Source/Destination Transaction Request register.
+ */
+ writel(((dma_channel << DMA_REG_WE_SHIFT) | (0x0 & 0xFF)), DW_DMA_DMAC_SGL_REQ_DST_REG);
+
+ /* Clear Interrupts on the channal */
+
+ /* Clear for IntTfr Interrupt */
+ writel(dma_channel, DW_DMA_DMAC_CLEAR_TFR);
+ /* Clear for IntBlock Interrupt */
+ writel(dma_channel, DW_DMA_DMAC_CLEAR_BLK);
+ /* Clear for IntSrcTran Interrupts */
+ writel(dma_channel, DW_DMA_DMAC_CLEAR_SRC_TRAN);
+ /* Clear for IntDstTran Interrupt */
+ writel(dma_channel, DW_DMA_DMAC_CLEAR_DST_TRAN);
+ /* Clear for IntErr Interrupt */
+ writel(dma_channel, DW_DMA_DMAC_CLEAR_ERR);
+
+ /* Set up Interrupt Mask registers */
+
+ /* Mask for IntTfr Interrupt */
+ writel(((dma_channel << DMA_REG_WE_SHIFT) | (0x0 & 0xFF)), DW_DMA_DMAC_MASK_TFR);
+ /* Mask for IntBlock Interrupt */
+ writel(((dma_channel << DMA_REG_WE_SHIFT) | dma_channel), DW_DMA_DMAC_MASK_BLOCK);
+ /* Mask for IntSrcTran Interrupt */
+ writel(((dma_channel << DMA_REG_WE_SHIFT) | dma_channel), DW_DMA_DMAC_MASK_SRC_TRAN);
+ /* Mask for IntDstTran Interrupt */
+ writel(((dma_channel << DMA_REG_WE_SHIFT) | dma_channel), DW_DMA_DMAC_MASK_DST_TRAN);
+ /* Mask for IntErr Interrupt */
+ writel(((dma_channel << DMA_REG_WE_SHIFT) | (0x0 & 0xFF)), DW_DMA_DMAC_MASK_ERR);
+
+ /* configure channel specific registers */
+ if( ch_no != 0 )
+ {
+ ch_reg_multiplier = (DMA_CHANNEL_REG_COUNT << 3) ;
+ ch_reg_multiplier = ch_no * ch_reg_multiplier;
+ }
+
+ /* configure : Source Address Register for Channel */
+
+/* The starting source address is programmed before the DMA channel is enabled. While the DMA transfer is in
+ * progress, this register is updated to reflect the source address of the current AHB transfer.
+ */
+ /* SAR Address must be alligned to DMA_CTL_SRC_TR_WIDTH boundry */
+ writel(source_add, (DMA_CHANNEL_REG_SAR_BASE + ch_reg_multiplier));
+
+ /* configure : Destination Address Register for Channel */
+
+/* The starting destination address is programmed before the DMA channel is enabled. While the DMA transfer is in
+ * progress, this register is updated to reflect the destination address of the current AHB transfer.
+ */
+ /* DAR Address must be alligned to DMA_CTL_DST_TR_WIDTH boundry */
+ writel(target_add, (DMA_CHANNEL_REG_DAR_BASE + ch_reg_multiplier));
+
+
+ /* configure : Control Register for Channel [32-63] */
+/* The number programmed into BLOCK_TS indicates the total number of single transactions
+ * to perform for every block transfer; a single transaction is mapped to a single AMBA beat.
+ */
+ writel(data_len, (DMA_CHANNEL_REG_CTL_BASE + ch_reg_multiplier + ch_no));
+
+ /* configure : Control Register for Channel [0-31] */
+ ch_ctrl_l = (((DMA_CTL_INT_EN & DMA_CTL_INT_EN_MASK) << DMA_CTL_INT_EN_SHIFT) |
+ ((DMA_CTL_DST_TR_WIDTH & DMA_CTL_DST_TR_WIDTH_MASK) << DMA_CTL_DST_TR_WIDTH_SHIFT) |
+ ((DMA_CTL_SRC_TR_WIDTH & DMA_CTL_SRC_TR_WIDTH_MASK) << DMA_CTL_SRC_TR_WIDTH_SHIFT) |
+ ((DMA_CTL_DINC & DMA_CTL_DINC_MASK) << DMA_CTL_DINC_SHIFT) |
+ ((DMA_CTL_SINC & DMA_CTL_SINC_MASK) << DMA_CTL_SINC_SHIFT) |
+ ((DMA_CTL_DEST_MSIZE & DMA_CTL_DEST_MSIZE_MASK) << DMA_CTL_DEST_MSIZE_SHIFT) |
+ ((DMA_CTL_SRC_MSIZE & DMA_CTL_SRC_MSIZE_MASK) << DMA_CTL_SRC_MSIZE_SHIFT) |
+ ((DMA_CTL_SRC_GATHER_EN & DMA_CTL_SRC_GATHER_EN_MASK) << DMA_CTL_SRC_GATHER_EN_SHIFT) |
+ ((DMA_CTL_DST_SCATTER_EN & DMA_CTL_DST_SCATTER_EN_MASK) << DMA_CTL_DST_SCATTER_EN_SHIFT) |
+ ((DMA_CTL_TT_FC & DMA_CTL_TT_FC_MASK) << DMA_CTL_TT_FC_SHIFT) |
+ ((DMA_CTL_DMS & DMA_CTL_DMS_MASK) << DMA_CTL_DMS_SHIFT) |
+ ((DMA_CTL_SMS & DMA_CTL_SMS_MASK) << DMA_CTL_SMS_SHIFT) |
+ ((DMA_CTL_LLP_DST_EN & DMA_CTL_LLP_DST_EN_MASK) << DMA_CTL_LLP_DST_EN_SHIFT) |
+ ((DMA_CTL_LLP_SRC_EN & DMA_CTL_LLP_SRC_EN_MASK) << DMA_CTL_LLP_SRC_EN_SHIFT));
+
+ writel(ch_ctrl_l, (DMA_CHANNEL_REG_CTL_BASE + ch_reg_multiplier));
+
+ /* configure : Linked List Pointer Register for Channel */
+ writel(0x0, (DMA_CHANNEL_REG_LLP_BASE + ch_reg_multiplier));
+
+ /* configure : Configuration Register for Channel [32-63] */
+ ch_cfg_h = (((DMA_CFG_FCMODE & DMA_CFG_FCMODE_MASK) << DMA_CFG_FCMODE_SHIFT) |
+ ((DMA_CFG_FIFO_MODE & DMA_CFG_FIFO_MODE_MASK) << DMA_CFG_FIFO_MODE_SHIFT) |
+ ((DMA_CFG_PROTCTL & DMA_CFG_PROTCTL_MASK) << DMA_CFG_PROTCTL_SHIFT) |
+ ((DMA_CFG_DS_UPD_EN & DMA_CFG_DS_UPD_EN_MASK) << DMA_CFG_DS_UPD_EN_SHIFT) |
+ ((DMA_CFG_SS_UPD_EN & DMA_CFG_SS_UPD_EN_MASK) << DMA_CFG_SS_UPD_EN_SHIFT) |
+ ((ch_no /*DMA_CFG_SRC_PER*/ & DMA_CFG_SRC_PER_MASK) << DMA_CFG_SRC_PER_SHIFT) |
+ ((DMA_CFG_DEST_PER & DMA_CFG_DEST_PER_MASK) << DMA_CFG_DEST_PER_SHIFT));
+
+ writel(ch_cfg_h, (DMA_CHANNEL_REG_CFG_BASE + ch_reg_multiplier + ch_no));
+
+ /* configure : Configuration Register for Channel [0-31] */
+ ch_cfg_l = (((ch_no & DMA_CFG_CH_PRIOR_MASK) << DMA_CFG_CH_PRIOR_SHIFT) |
+ ((DMA_CFG_CH_SUSP & DMA_CFG_CH_SUSP_MASK) << DMA_CFG_CH_SUSP_SHIFT) |
+ ((DMA_CFG_FIFO_EMPTY & DMA_CFG_FIFO_EMPTY_MASK) << DMA_CFG_FIFO_EMPTY_SHIFT) |
+ ((DMA_CFG_HS_SEL_DST & DMA_CFG_HS_SEL_DST_MASK) << DMA_CFG_HS_SEL_DST_SHIFT) |
+ ((DMA_CFG_HS_SEL_SRC & DMA_CFG_HS_SEL_SRC_MASK) << DMA_CFG_HS_SEL_SRC_SHIFT) |
+ ((DMA_CFG_LOCK_CH_L & DMA_CFG_LOCK_CH_L_MASK) << DMA_CFG_LOCK_CH_L_SHIFT) |
+ ((DMA_CFG_LOCK_B_L & DMA_CFG_LOCK_B_L_MASK) << DMA_CFG_LOCK_B_L_SHIFT) |
+ ((DMA_CFG_LOCK_CH & DMA_CFG_LOCK_CH_MASK) << DMA_CFG_LOCK_CH_SHIFT) |
+ ((DMA_CFG_LOCK_B & DMA_CFG_LOCK_B_MASK) << DMA_CFG_LOCK_B_SHIFT) |
+ ((DMA_CFG_DST_HS_POL & DMA_CFG_DST_HS_POL_MASK) << DMA_CFG_DST_HS_POL_SHIFT) |
+ ((DMA_CFG_SRC_HS_POL & DMA_CFG_SRC_HS_POL_MASK) << DMA_CFG_SRC_HS_POL_SHIFT) |
+ ((DMA_CFG_MAX_ABRST & DMA_CFG_MAX_ABRST_MASK) << DMA_CFG_MAX_ABRST_SHIFT) |
+ ((DMA_CFG_RELOAD_SRC & DMA_CFG_RELOAD_SRC_MASK) << DMA_CFG_RELOAD_SRC_SHIFT) |
+ ((DMA_CFG_RELOAD_DST & DMA_CFG_RELOAD_DST_MASK) << DMA_CFG_RELOAD_DST_SHIFT));
+
+ writel(ch_cfg_l, (DMA_CHANNEL_REG_CFG_BASE + ch_reg_multiplier));
+
+ /* Enable the DMA channel */
+ writel(((dma_channel << DMA_REG_WE_SHIFT) | dma_channel), DW_DMA_DMAC_CH_EN_REG);
+
+ return ;
+}
+
+EXPORT_SYMBOL(dma_configure);
+
+int fast_uart_write(unsigned int len, const char *str)
+{
+ unsigned int dma_len;
+
+ while (len)
+ {
+ if (len > DMA_XFER_DLEN)
+ dma_len = DMA_XFER_DLEN;
+ else
+ dma_len = len;
+
+ while ((readl(DW_DMA_UART1_BASEADDR + UART_LSR) & LSR_TEMT) == 0) ;
+
+ dma_configure((unsigned int) str, DW_DMA_UART1_BASEADDR + UART_THR, dma_len, DMA_CHANNEL_1);
+
+ /* Wait for completion of DMA xfer (till channel is disabled by HW) */
+ if (RETCODE_OK != dma_xfer_cmplete_chk(DMA_CHANNEL_1))
+ return RETCODE_ERROR;
+
+ len -= dma_len;
+
+ str += dma_len;
+ }
+
+ return RETCODE_OK;
+}
+
+EXPORT_SYMBOL(fast_uart_write);
+
diff --git a/arch/arm/mach-comcerto/gpio.c b/arch/arm/mach-comcerto/gpio.c
new file mode 100644
index 0000000..6051dae
--- /dev/null
+++ b/arch/arm/mach-comcerto/gpio.c
@@ -0,0 +1,106 @@
+/*
+ * linux/arch/arm/mach-comcerto/gpio.c
+ *
+ * Copyright (C) 2006 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* [FIXME] */
+#if 0
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <linux/kernel_stat.h>
+
+#if !defined(CONFIG_ARCH_M83XXX)
+
+/* The GPIO IRQ block generates interrupts only on rising/falling edges of the GPIO pin signal.
+ * To avoid loosing interrupts or having spurious interrupts care must be taken.
+ * The general strategy is to loop and poll the GPIO pin to make sure no interrupts are missed.
+ * The GPIO IRQ must be acked inside the loop at each iteration. If it was acked
+ * before the loop there would be a race condition(1) where we exit comcerto_handle_gpio_level_irq() with
+ * the GPIO IRQ set, even if the source was already handled. If it was acked after the loop
+ * there would be a race condition(2) where we ack a GPIO IRQ but the source is not yet handled.
+ * The GPIO IRQ must be acked after all the driver handlers have been called (after handle_simple_irq())
+ * to also avoid the race mentioned in (1) above.
+ */
+
+extern int noirqdebug;
+extern int redirect_hardirq(struct irq_desc *desc);
+
+void comcerto_handle_gpio_level_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct irqaction *action;
+ irqreturn_t action_ret;
+ const unsigned int cpu = smp_processor_id();
+ u32 pending;
+
+ spin_lock(&desc->lock);
+
+ /*
+ * Mask IRQ.
+ */
+ desc->chip->mask(irq);
+
+ do {
+ if (unlikely(desc->status & IRQ_INPROGRESS))
+ goto out_unlock;
+ desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
+ kstat_cpu(cpu).irqs[irq]++;
+
+ action = desc->action;
+ if (unlikely(!action || (desc->status & IRQ_DISABLED))) {
+ desc->status |= IRQ_PENDING;
+ goto out_unlock;
+ }
+
+ desc->status |= IRQ_INPROGRESS;
+ /*
+ * hardirq redirection to the irqd process context:
+ */
+ if (redirect_hardirq(desc))
+ goto out_unlock;
+ desc->status &= ~IRQ_PENDING;
+ spin_unlock(&desc->lock);
+
+ action_ret = handle_IRQ_event(irq, action);
+ if (!noirqdebug)
+ note_interrupt(irq, desc, action_ret);
+
+ spin_lock(&desc->lock);
+ desc->status &= ~IRQ_INPROGRESS;
+
+ /*
+ * Ack IRQ.
+ */
+ desc->chip->ack(irq);
+ /*
+ * Source interrupts are usually active low
+ */
+ pending = comcerto_gpio_read(1 << ((irq - 1) & 0x1f)) ? 0 : 1;
+
+ } while (pending && !(desc->status & IRQ_DISABLED));
+
+ if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
+ desc->chip->unmask(irq);
+
+out_unlock:
+ spin_unlock(&desc->lock);
+}
+#endif
+#endif
diff --git a/arch/arm/mach-comcerto/headsmp.S b/arch/arm/mach-comcerto/headsmp.S
new file mode 100644
index 0000000..101cc49
--- /dev/null
+++ b/arch/arm/mach-comcerto/headsmp.S
@@ -0,0 +1,93 @@
+/*
+ * arch/arm/mach-transcede/headsmp.S
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ * Copyright (c) 2003 ARM Limited
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#if defined(CONFIG_HOTPLUG_CPU)
+ __CPUINIT
+#else
+ __INIT
+#endif
+
+/*
+ * Realview specific entry point for secondary CPUs. This provides
+ * a "holding pen" into which all secondary cores are held until we're
+ * ready for them to initialise.
+ */
+ENTRY(v7_invalidate_l1)
+ mov r0, #0
+ mcr p15, 2, r0, c0, c0, 0
+ mrc p15, 1, r0, c0, c0, 0
+
+ ldr r1, =0x7fff
+ and r2, r1, r0, lsr #13
+
+ ldr r1, =0x3ff
+
+ and r3, r1, r0, lsr #3 @ NumWays - 1
+ add r2, r2, #1 @ NumSets
+
+ and r0, r0, #0x7
+ add r0, r0, #4 @ SetShift
+
+ clz r1, r3 @ WayShift
+ add r4, r3, #1 @ NumWays
+1: sub r2, r2, #1 @ NumSets--
+ mov r3, r4 @ Temp = NumWays
+2: subs r3, r3, #1 @ Temp--
+ mov r5, r3, lsl r1
+ mov r6, r2, lsl r0
+ orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
+ mcr p15, 0, r5, c7, c6, 2
+ bgt 2b
+ cmp r2, #0
+ bgt 1b
+ dsb
+ isb
+ mov pc, lr
+ENDPROC(v7_invalidate_l1)
+
+
+ENTRY(comcerto_secondary_startup)
+ mov r0, #0xd3
+ msr cpsr_fsxc, r0
+ bl v7_invalidate_l1
+ mrc p15, 0, r0, c0, c0, 5
+ and r0, r0, #15
+ adr r4, 1f
+ ldmia r4, {r5, r6}
+ sub r4, r4, r5
+ add r6, r6, r4
+pen: ldr r7, [r6]
+ cmp r7, r0
+ bne pen
+
+ /*
+ * we've been released from the holding pen: secondary_stack
+ * should now contain the SVC stack for this core
+ */
+ b secondary_startup
+
+1: .long .
+ .long pen_release
+
diff --git a/arch/arm/mach-comcerto/hotplug.c b/arch/arm/mach-comcerto/hotplug.c
new file mode 100644
index 0000000..fed8465
--- /dev/null
+++ b/arch/arm/mach-comcerto/hotplug.c
@@ -0,0 +1,87 @@
+/*
+ * comcerto SMP cpu-hotplug support
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, LTD.
+ * Author:
+ * Satyabrata sahu <satyabrat.sahu@mindspeed.com>
+ *
+ * Platform file needed for the comcerto A9 SMP system . This file is based on arm
+ * realview smp platform.
+ * Copyright (c) 2002 ARM Limited.
+
+ * CPU-1 shutdown and reset . while makeing online , CPU-1 will be again in
+ * out of reset mode.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+
+#include <asm/cacheflush.h>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/kthread.h> // for threads
+#include <linux/sched.h> // for task_struct
+#include <linux/time.h> // for using jiffies
+#include <linux/timer.h>
+
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+
+
+extern volatile int pen_release;
+
+extern cpu1_hotplug;
+extern u32 cpu1_hotplug_done;
+
+int platform_cpu_kill(unsigned int cpu)
+{
+ return 1;
+}
+
+/*
+ * platform-specific code to shutdown a CPU
+ * Called with IRQs disabled
+ */
+void platform_cpu_die(unsigned int cpu)
+{
+ /* Flush all cache */
+ flush_cache_all();
+ dsb();
+
+ /*
+ * we're ready for shutdown now, so do it
+ */
+
+ /* Entering to LOW power state.
+ * Go to Low power ,Configure the CPU to reset mode.
+ */
+
+ if(cpu) {
+ /* Put A9 CPU-1 to reset */
+ cpu1_hotplug_done = 1;
+ wake_up(&cpu1_hotplug);
+ while(1);
+ }
+}
+
+int platform_cpu_disable(unsigned int cpu)
+{
+ /*
+ * we don't allow CPU 0 to be shutdown (it is still too special
+ * e.g. clock tick interrupts)
+ */
+
+ if (!cpu)
+ pr_info("We are not allowing the CPU(%d) to shutdown. \n",cpu);
+
+ return cpu == 0 ? -EPERM : 0;
+}
diff --git a/arch/arm/mach-comcerto/include/mach/board-c2kasic.h b/arch/arm/mach-comcerto/include/mach/board-c2kasic.h
new file mode 100644
index 0000000..c56c3c5
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/board-c2kasic.h
@@ -0,0 +1,55 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/board-c2kasic.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __BOARD_C2KASIC_H__
+#define __BOARD_C2KASIC_H__
+
+#include <mach/hardware.h>
+
+ /***********************************
+ * Expansion bus configuration
+ ***********************************/
+
+ #define COMCERTO_EXPCLK 50000000 /* 50MHz */
+
+ /***********************************
+ * GPIO
+ ***********************************/
+ #define COMCERTO_OUTPUT_GPIO (COMCERTO_NAND_CE)
+ #define COMCERTO_IRQ_RISING_EDGE_GPIO 0 // [FIXME]
+ #define COMCERTO_IRQ_FALLING_EDGE_GPIO (GPIO_2 | GPIO_0) // [FIXME]
+ #define COMCERTO_IRQ_LEVEL_GPIO GPIO_2 // [FIXME]
+ /*Are pins used either as GPIO or as pins for others IP blocks*/
+ #define COMCERTO_GPIO_PIN_USAGE (SPI_BUS) // [FIXME]
+
+ /***********************************
+ * EEPROM
+ ***********************************/
+
+ /***********************************
+ * NOR
+ ***********************************/
+ #define NORFLASH_MEMORY_PHY1 EXP_CS0_AXI_BASEADDR
+
+ /***********************************
+ * NAND
+ ***********************************/
+ #define COMCERTO_EXP_CS4_SEG_SZ 1
+
+ #define COMCERTO_NAND_FIO_ADDR EXP_CS4_AXI_BASEADDR
+ #define COMCERTO_NAND_BR 0x20000000 /* BR is on GPIO_29 */
+ #define COMCERTO_NAND_CE 0x10000000 /* CE is on GPIO_28 */
+ #define COMCERTO_NAND_IO_SZ ((COMCERTO_EXP_CS4_SEG_SZ << 12) +0x1000)
+
+ /***********************************
+ * SLIC
+ ***********************************/
+ #define COMCERTO_SLIC_GPIO_IRQ IRQ_G1
+
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/board-c2kevm.h b/arch/arm/mach-comcerto/include/mach/board-c2kevm.h
new file mode 100644
index 0000000..1e2d48f
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/board-c2kevm.h
@@ -0,0 +1,54 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/board-c2kevm.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __BOARD_C2KEVM_H__
+#define __BOARD_C2KEVM_H__
+
+#include <mach/hardware.h>
+
+ /***********************************
+ * Expansion bus configuration
+ ***********************************/
+
+ #define COMCERTO_EXPCLK 50000000 /* 50MHz */
+
+ /***********************************
+ * GPIO
+ ***********************************/
+ #define COMCERTO_OUTPUT_GPIO (COMCERTO_NAND_CE)
+ #define COMCERTO_IRQ_RISING_EDGE_GPIO 0 // [FIXME]
+ #define COMCERTO_IRQ_FALLING_EDGE_GPIO (GPIO_2 | GPIO_0) // [FIXME]
+ #define COMCERTO_IRQ_LEVEL_GPIO GPIO_2 // [FIXME]
+ /*Are pins used either as GPIO or as pins for others IP blocks*/
+ #define COMCERTO_GPIO_PIN_USAGE (SPI_BUS) // [FIXME]
+
+ /***********************************
+ * EEPROM
+ ***********************************/
+
+ /***********************************
+ * NOR
+ ***********************************/
+ #define NORFLASH_MEMORY_PHY1 EXP_CS0_AXI_BASEADDR
+
+ /***********************************
+ * NAND
+ ***********************************/
+ #define COMCERTO_EXP_CS4_SEG_SZ 1
+
+ #define COMCERTO_NAND_FIO_ADDR EXP_CS4_AXI_BASEADDR
+ #define COMCERTO_NAND_BR 0x20000000 /* BR is on GPIO_29 */
+ #define COMCERTO_NAND_CE 0x10000000 /* CE is on GPIO_28 */
+ #define COMCERTO_NAND_IO_SZ ((COMCERTO_EXP_CS4_SEG_SZ << 12) +0x1000)
+
+ /***********************************
+ * SLIC
+ ***********************************/
+ #define COMCERTO_SLIC_GPIO_IRQ IRQ_G2
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/board-c2kmfcnevm.h b/arch/arm/mach-comcerto/include/mach/board-c2kmfcnevm.h
new file mode 100644
index 0000000..2c52c91
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/board-c2kmfcnevm.h
@@ -0,0 +1,54 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/board-c2kmfcnevm.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __BOARD_C2KMFCNEVM_H__
+#define __BOARD_C2KMFCNEVM_H__
+
+#include <mach/hardware.h>
+
+ /***********************************
+ * Expansion bus configuration
+ ***********************************/
+
+ #define COMCERTO_EXPCLK 50000000 /* 50MHz */
+
+ /***********************************
+ * GPIO
+ ***********************************/
+ #define COMCERTO_OUTPUT_GPIO (COMCERTO_NAND_CE)
+ #define COMCERTO_IRQ_RISING_EDGE_GPIO 0 // [FIXME]
+ #define COMCERTO_IRQ_FALLING_EDGE_GPIO (GPIO_2 | GPIO_0) // [FIXME]
+ #define COMCERTO_IRQ_LEVEL_GPIO GPIO_2 // [FIXME]
+ /*Are pins used either as GPIO or as pins for others IP blocks*/
+ #define COMCERTO_GPIO_PIN_USAGE (SPI_BUS) // [FIXME]
+
+ /***********************************
+ * EEPROM
+ ***********************************/
+
+ /***********************************
+ * NOR
+ ***********************************/
+ #define NORFLASH_MEMORY_PHY1 EXP_CS0_AXI_BASEADDR
+
+ /***********************************
+ * NAND
+ ***********************************/
+ #define COMCERTO_EXP_CS4_SEG_SZ 1
+
+ #define COMCERTO_NAND_FIO_ADDR EXP_CS4_AXI_BASEADDR
+ #define COMCERTO_NAND_BR 0x20000000 /* BR is on GPIO_29 */
+ #define COMCERTO_NAND_CE 0x10000000 /* CE is on GPIO_28 */
+ #define COMCERTO_NAND_IO_SZ ((COMCERTO_EXP_CS4_SEG_SZ << 12) +0x1000)
+
+ /***********************************
+ * SLIC
+ ***********************************/
+ #define COMCERTO_SLIC_GPIO_IRQ IRQ_G2
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/board-c2krtsm.h b/arch/arm/mach-comcerto/include/mach/board-c2krtsm.h
new file mode 100644
index 0000000..b1541a4
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/board-c2krtsm.h
@@ -0,0 +1,56 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/board-c2krtsm.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __BOARD_C2KRTSM_H__
+#define __BOARD_C2KRTSM_H__
+
+#include <mach/hardware.h>
+
+ /***********************************
+ * Expansion bus configuration
+ ***********************************/
+
+ #define COMCERTO_EXPCLK 50000000 /* 50MHz */
+
+ /***********************************
+ * GPIO
+ ***********************************/
+ #define COMCERTO_OUTPUT_GPIO (COMCERTO_NAND_CE | COMCERTO_NAND_ALE | COMCERTO_NAND_CLE) // [FIXME]
+ #define COMCERTO_IRQ_RISING_EDGE_GPIO 0 // [FIXME]
+ #define COMCERTO_IRQ_FALLING_EDGE_GPIO (GPIO_2 | GPIO_0) // [FIXME]
+ #define COMCERTO_IRQ_LEVEL_GPIO GPIO_2 // [FIXME]
+ /*Are pins used either as GPIO or as pins for others IP blocks*/
+ #define COMCERTO_GPIO_PIN_USAGE (SPI_BUS) // [FIXME]
+
+ /***********************************
+ * EEPROM
+ ***********************************/
+
+ /***********************************
+ * NOR
+ ***********************************/
+ #define NORFLASH_MEMORY_PHY1 EXP_CS0_AXI_BASEADDR
+
+ /***********************************
+ * NAND
+ ***********************************/
+ #define COMCERTO_EXP_CS4_SEG_SZ 1
+
+ #define COMCERTO_NAND_FIO_ADDR COMCERTO_EXP_CS4_BASE
+ #define COMCERTO_NAND_BR GPIO_6
+ #define COMCERTO_NAND_CE GPIO_29
+ #define COMCERTO_NAND_ALE GPIO_30
+ #define COMCERTO_NAND_CLE GPIO_31
+ #define COMCERTO_NAND_IO_SZ ((COMCERTO_EXP_CS4_SEG_SZ << 12) +0x1000)
+
+ /***********************************
+ * SLIC
+ ***********************************/
+ #define COMCERTO_SLIC_GPIO_IRQ IRQ_G0
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/c2k_dma.h b/arch/arm/mach-comcerto/include/mach/c2k_dma.h
new file mode 100644
index 0000000..942ac7f
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/c2k_dma.h
@@ -0,0 +1,241 @@
+#ifndef C2K_DMA_H_
+#define C2K_DMA_H_
+
+#ifndef __ASSEMBLY__
+
+#include <linux/mm_types.h>
+#include <linux/time.h>
+
+// Mem to IO Control
+#define M2IO_START (1 << 0)
+#define M2IO_FLENEN (1 << 1)
+#define M2IO_FCOM (1 << 2)
+#define M2IO_DONOSTOP (1 << 3)
+#define M2IO_DONOSTA (1 << 4)
+
+// IO to Mem: DMA Control
+#define IO2M_IRQFRDYN (1 << 0)
+#define IO2M_IRQFLST (1 << 1)
+#define IO2M_IRQFDON (1 << 2)
+#define IO2M_IRQFLSH (1 << 3)
+#define IO2M_IRQFLEN (1 << 4)
+#define IO2M_IRQFTHLD (1 << 5)
+
+// IRQ Status Register
+#define IRQ_IRQFRDYN (1 << 0)
+#define IRQ_IRQFLST (1 << 1)
+#define IRQ_IRQFDON (1 << 2)
+#define IRQ_IRQFLSH (1 << 3)
+#define IRQ_IRQFLEN (1 << 4)
+#define IRQ_IRQFTHLD (1 << 5)
+#define IRQ_IRQFCTRL (1 << 6)
+
+// Inbound Frame and Buffer Descriptor Programming
+
+// BControl
+#define BLAST (1 << 16)
+#define BFIX (1 << 17)
+
+// Block Size
+#define XOR_BLOCK_SIZE_256 0
+#define XOR_BLOCK_SIZE_512 1
+#define XOR_BLOCK_SIZE_1024 2
+#define XOR_BLOCK_SIZE_2048 3
+#define XOR_BLOCK_SIZE_4096 4
+
+#define COMCERTO_XOR_MAX_SRC 6
+
+#define MDMA_INBOUND_BUF_DESC 256
+#define MDMA_OUTBOUND_BUF_DESC 256
+
+#define XOR_INBOUND_BUF_DESC 6
+#define XOR_OUTBOUND_BUF_DESC 2
+
+/* FLEN => Maximum no. of fdescs mdma can process at a time is 4k-1 */
+/* Need to verify if these many can be created in aram_pool. Don't know whether someone else use iram_pool */
+//#define XOR_FDESC_COUNT 256
+
+#define MDMA_MAX_BUF_SIZE 0xffff
+#define MDMA_SPLIT_BUF_SIZE 0x8000 /* half a page with 64kB pages */
+
+/* AxCACHE[3:0], bit[3]= WA, bit[2]= RA, bit[1]= C, bit[0]= B */
+#define BUFFERABLE (1 << 0)
+#define CACHEABLE (1 << 1)
+#define READ_ALLOC (1 << 2)
+#define WRITE_ALLOC (1 << 3)
+
+/* AxUSER[0] = coherent(1)/non_coherent(0) */
+
+/* AxUSER[4:1] */
+#define STRONGLY_ORDERED 0x0
+#define DEVICE 0x1
+#define NORMAL_NONCACHEABLE 0x3
+#define WRITETHROUGH 0x6
+#define WRITEBACK 0x7
+#define WRITEBACK_WA 0xf
+
+/* AxPROT[2:0], bit[2]= instruction(1)/data(0), bit[1]= non-secure(1)/secure(0) , bit[0] = privilege(1)/normal(0) */
+#define PRIVILEGE (1 << 0)
+#define NON_SECURE (1 << 1)
+#define INSTRUCTION (1 << 2)
+
+
+#define AWUSER_COHERENT(x) ((((x) << 1) | 1) << 19)
+#define AWPROT(x) ((x) << 16)
+#define AWCACHE(x) ((x) << 12)
+
+#define ARUSER_COHERENT(x) ((((x) << 1) | 1) << 7)
+#define ARPROT(x) ((x) << 4)
+#define ARCACHE(x) ((x) << 0)
+
+enum mdma_transaction_type {
+ MDMA_MEMCPY,
+ MDMA_XOR,
+ MDMA_XOR_VAL,
+};
+
+struct comcerto_mdma_buffer_desc {
+ u32 bpointer;
+ u32 bcontrol;
+}__attribute__ ((aligned(8)));
+
+struct comcerto_memcpy_inbound_fdesc {
+ u32 next_desc;
+ u32 fcontrol;
+ u32 fstatus0;
+ u32 fstatus1;
+ struct comcerto_mdma_buffer_desc bdesc[MDMA_INBOUND_BUF_DESC];
+}__attribute__ ((aligned(16)));
+
+struct comcerto_memcpy_outbound_fdesc {
+ u32 next_desc;
+ u32 fcontrol;
+ u32 fstatus0;
+ u32 fstatus1;
+ struct comcerto_mdma_buffer_desc bdesc[MDMA_OUTBOUND_BUF_DESC];
+}__attribute__ ((aligned(16)));
+
+struct comcerto_xor_inbound_fdesc {
+ u32 next_desc;
+ u32 fcontrol;
+ u32 fstatus0;
+ u32 fstatus1;
+ struct comcerto_mdma_buffer_desc bdesc[XOR_INBOUND_BUF_DESC];
+}__attribute__ ((aligned(16)));
+
+struct comcerto_xor_outbound_fdesc {
+ u32 next_desc;
+ u32 fcontrol;
+ u32 fstatus0;
+ u32 fstatus1;
+ struct comcerto_mdma_buffer_desc bdesc[XOR_OUTBOUND_BUF_DESC];
+}__attribute__ ((aligned(16)));
+
+struct comcerto_dma_buf {
+ dma_addr_t phys_addr;
+ unsigned int len;
+ unsigned int split;
+};
+
+struct comcerto_dma_sg {
+ unsigned int input_idx;
+ unsigned int output_idx;
+ dma_addr_t high_phys_addr;
+ dma_addr_t low_phys_addr;
+#if defined(CONFIG_COMCERTO_MDMA_PROF)
+ struct timeval start;
+ struct timeval end;
+#endif
+ struct comcerto_dma_buf in_bdesc[MDMA_INBOUND_BUF_DESC];
+ struct comcerto_dma_buf out_bdesc[MDMA_OUTBOUND_BUF_DESC];
+};
+
+struct mdma_xor_struct {
+ int transaction_type;
+ int xor_block_size;
+ int xor_src_cnt;
+ dma_addr_t **xor_srcs;
+ dma_addr_t *xor_dest;
+};
+
+
+extern struct comcerto_memcpy_inbound_fdesc *mdma_in_desc;
+extern struct comcerto_memcpy_outbound_fdesc *mdma_out_desc;
+
+extern struct comcerto_xor_inbound_fdesc *xor_in_fdesc[];
+extern struct comcerto_xor_outbound_fdesc *xor_out_fdesc[];
+
+
+static inline void comcerto_dma_set_in_bdesc(u32 idx, u32 addr, u32 ctrl)
+{
+ mdma_in_desc->bdesc[idx].bpointer = addr;
+ mdma_in_desc->bdesc[idx].bcontrol = ctrl;
+}
+
+static inline void comcerto_dma_set_out_bdesc(u32 idx, u32 addr, u32 ctrl)
+{
+ mdma_out_desc->bdesc[idx].bpointer = addr;
+ mdma_out_desc->bdesc[idx].bcontrol = ctrl;
+}
+
+static inline void comcerto_dma_in_bdesc_ctrl_update(u32 idx, u32 ctrl)
+{
+ mdma_in_desc->bdesc[idx].bcontrol |= ctrl;
+}
+
+static inline void comcerto_dma_out_bdesc_ctrl_update(u32 idx, u32 ctrl)
+{
+ mdma_out_desc->bdesc[idx].bcontrol |= ctrl;
+}
+
+/****************** XOR functions ********************/
+static inline void mdma_xor_set_in_bdesc(u32 xor_cbuf_wr_cntr, u32 idx, u32 addr, u32 ctrl)
+{
+ xor_in_fdesc[xor_cbuf_wr_cntr]->bdesc[idx].bpointer = addr;
+ xor_in_fdesc[xor_cbuf_wr_cntr]->bdesc[idx].bcontrol = ctrl;
+}
+
+static inline void mdma_xor_set_out_bdesc(u32 xor_cbuf_wr_cntr, u32 idx, u32 addr, u32 ctrl)
+{
+ xor_out_fdesc[xor_cbuf_wr_cntr]->bdesc[idx].bpointer = addr;
+ xor_out_fdesc[xor_cbuf_wr_cntr]->bdesc[idx].bcontrol = ctrl;
+}
+
+static inline void mdma_xor_in_bdesc_ctrl_update(u32 xor_cbuf_wr_cntr,u32 idx, u32 ctrl)
+{
+ xor_in_fdesc[xor_cbuf_wr_cntr]->bdesc[idx].bcontrol |= ctrl;
+}
+
+static inline void mdma_xor_out_bdesc_ctrl_update(u32 xor_cbuf_wr_cntr,u32 idx, u32 ctrl)
+{
+ xor_out_fdesc[xor_cbuf_wr_cntr]->bdesc[idx].bcontrol |= ctrl;
+}
+
+/****************** XOR functions end ********************/
+
+extern void comcerto_dma_get(void);
+extern void comcerto_dma_put(void);
+extern void comcerto_dma_set_in_bdesc(u32 idx, u32 addr, u32 ctrl);
+extern void comcerto_dma_set_out_bdesc(u32 idx, u32 addr, u32 ctrl);
+extern void comcerto_dma_start(void);
+extern void comcerto_dma_wait(void);
+extern void comcerto_do_mdma_xor(unsigned int src_count, unsigned int bytes, dma_addr_t dest, dma_addr_t *srcs);
+extern void comcerto_do_mdma_memcpy(void);
+
+int comcerto_dma_sg_add_input(struct comcerto_dma_sg *sg, void *p, unsigned int len, int use_acp);
+int comcerto_dma_sg_add_output(struct comcerto_dma_sg *sg, void *p, unsigned int len, int use_acp);
+void comcerto_dma_sg_setup(struct comcerto_dma_sg *sg, unsigned int len);
+void comcerto_dma_sg_cleanup(struct comcerto_dma_sg *sg, unsigned int len);
+
+static inline void comcerto_dma_sg_init(struct comcerto_dma_sg *sg)
+{
+ sg->input_idx = 0;
+ sg->output_idx = 0;
+ sg->high_phys_addr = 0x0;
+ sg->low_phys_addr = 0xffffffff;
+}
+
+#endif
+
+#endif /* C2K_DMA_H_ */
+
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000.h
new file mode 100644
index 0000000..4a27d04
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000.h
@@ -0,0 +1,250 @@
+/*
+ * arch/arm/arch-comcerto/include/mach/comcerto-2000.h
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#error "Do not include this directly, instead #include <asm/hardware.h>"
+#endif
+
+#ifndef __ASM_ARCH_COMCERTO2000_H__
+#define __ASM_ARCH_COMCERTO2000_H__
+
+
+/*
+ * System Clock Frequencies
+ */
+#ifdef CONFIG_RTSM_C2K
+#define COMCERTO_DEFAULTAXICLK (600000000/3) /* Hz */
+#else
+#define COMCERTO_DEFAULTAXICLK 200000000 /* Hz */
+#endif
+
+
+/*
+ * SCU registers
+ */
+#define COMCERTO_SCU_BASE 0xFFF00000
+#define COMCERTO_GIC_CPU_BASE (COMCERTO_SCU_BASE + 0x100)
+#define COMCERTO_TWD_BASE (COMCERTO_SCU_BASE + 0x600)
+#define COMCERTO_TWD_PERCPU_BASE (COMCERTO_SCU_BASE + 0x700)
+#define COMCERTO_GIC_DIST_BASE (COMCERTO_SCU_BASE + 0x1000)
+#define COMCERTO_L310_BASE (COMCERTO_SCU_BASE + 0x10000)
+
+#define COMCERTO_L2CC_ASSOCIATIVITY_SHIFT 16
+#define COMCERTO_L2CC_ASSOCIATIVITY_MASK 0x00010000
+#define COMCERTO_L2CC_ASSOCIATIVITY_8WAY 0x0
+#define COMCERTO_L2CC_ASSOCIATIVITY_16WAY 0x1
+
+#define COMCERTO_L2CC_WAYSIZE_SHIFT 17
+#define COMCERTO_L2CC_WAYSIZE_MASK 0x000E0000
+#define COMCERTO_L2CC_ASSOCIATIVITY_32KB 0x2
+
+#define COMCERTO_L2CC_WR_LAT_SHIFT 8
+#define COMCERTO_L2CC_RD_LAT_SHIFT 4
+
+
+/*
+ * Physical address of IO on APB Bus
+ */
+
+#define COMCERTO_APB_TDM_BASE 0x90400000
+#define COMCERTO_APB_USBPHY_SERDES_STAT_BASE 0x90410000
+#define COMCERTO_APB_TDMA_BASE 0x90420000
+#define COMCERTO_APB_TIMER_BASE 0x90450000
+#define COMCERTO_APB_PCI_SATA_USB_CTRL_BASE 0x90460000
+#define COMCERTO_APB_GPIO_BASE 0x90470000
+#define COMCERTO_APB_UART0_BASE 0x90490000
+#define COMCERTO_APB_SPI_BASE 0x90498000
+#define COMCERTO_APB_I2C_BASE 0x9049C000
+#define COMCERTO_APB_USB3_BASE 0x904A0000
+#define COMCERTO_APB_CLK_BASE 0x904B0000
+#define COMCERTO_APB_RTC_BASE 0x904E0000
+#define COMCERTO_APB_OTP_BASE 0x904F0000
+#define COMCERTO_APB_PFE_BASE 0x90500000
+#define COMCERTO_APB_SERDES_CONF_BASE 0x90590000
+#define COMCERTO_APB_EXPBUS_BASE 0x905A0000
+#define COMCERTO_APB_DDRPHY_BASE 0x905B0000
+#define COMCERTO_APB_TDMA2_BASE 0x905D0000
+#define COMCERTO_APB_MDMA_BASE 0x905E0000
+#define COMCERTO_APB_A9CS_BASE 0x90600000
+
+
+/*
+ * Physical address on AXI Bus
+ */
+#define COMCERTO_AXI_HIGHMEMDDR_BASE 0xFFFF0000
+#define COMCERTO_AXI_DDR_BASE 0x00000000
+#define COMCERTO_AXI_ACP_BASE 0x80000000 /* 48MB */
+#define COMCERTO_AXI_IRAM_BASE 0x83000000
+#define COMCERTO_AXI_IBR_BASE 0x90000000
+#define COMCERTO_AXI_APB_BASE 0x90400000 /* 12MB */
+#define COMCERTO_AXI_SEMA_BASE 0x91000000 /* 16MB */
+#define COMCERTO_AXI_USB2P0_BASE 0x92000000
+#define COMCERTO_AXI_TRUSTZONE_BASE 0x93000000
+#define COMCERTO_AXI_DPI0_BASE 0x94000000 /* 16MB */
+#define COMCERTO_AXI_DPI1_BASE 0x95000000 /* 16MB */
+#define COMCERTO_AXI_UART_SPI_BASE 0x96000000 /* 16MB */
+#define COMCERTO_AXI_UART0_BASE (COMCERTO_AXI_UART_SPI_BASE + 0x00300000)
+#define COMCERTO_AXI_UART1_BASE (COMCERTO_AXI_UART_SPI_BASE + 0x00400000)
+#define COMCERTO_AXI_SPI_BASE (COMCERTO_AXI_UART_SPI_BASE + 0x00500000)
+#define COMCERTO_AXI_DDRCONFIG_BASE 0x97000000
+#define COMCERTO_AXI_PCIe1_BASE 0x99000000
+#define COMCERTO_AXI_PCIe0_BASE 0x98000000
+#define COMCERTO_AXI_IPSEC_BASE 0x9A000000
+#define COMCERTO_AXI_SPACC_PDU_BASE 0x9B000000 /* 16MB */
+#define COMCERTO_AXI_PFE_BASE 0x9C000000 /* 16MB */
+#define COMCERTO_AXI_SATA_BASE 0x9D000000 /* 16MB */
+#define COMCERTO_AXI_DECT_BASE 0x9E000000 /* 16MB */
+#define COMCERTO_AXI_USB3P0_BASE 0x9F000000 /* 16MB */
+#define COMCERTO_AXI_PCIe0_SLAVE_BASE 0xA0000000
+#define COMCERTO_AXI_PCIe1_SLAVE_BASE 0xB0000000
+#define COMCERTO_AXI_EXP_BASE 0xC0000000
+#define COMCERTO_AXI_EXP_ECC_BASE 0xCFFF0000 /* 64KB */
+
+#define COMCERTO_AXI_ACP_SIZE (1 << 24)
+
+#define COMCERTO_DDR_SHARED_BASE (COMCERTO_AXI_DDR_BASE + 0x2C00000)
+#define COMCERTO_DDR_SHARED_SIZE (SZ_16M + SZ_4M)
+
+/* MSP memory map */
+#define COMCERTO_MSP_DDR_BASE COMCERTO_DDR_SHARED_BASE
+#define COMCERTO_MSP_DDR_SIZE_CB (SZ_1M * 5)
+#define COMCERTO_MSP_DDR_SIZE_NCNB (SZ_1M * 3)
+#define COMCERTO_MSP_DDR_SIZE (COMCERTO_MSP_DDR_SIZE_CB + COMCERTO_MSP_DDR_SIZE_NCNB)
+
+/* PFE memory map */
+#define COMCERTO_APB_PFE_SIZE SZ_64K
+#define COMCERTO_AXI_PFE_SIZE SZ_16M
+#define COMCERTO_AXI_IPSEC_SIZE SZ_16M
+#define COMCERTO_PFE_DDR_BASE (COMCERTO_DDR_SHARED_BASE + SZ_8M)
+#define COMCERTO_PFE_DDR_SIZE (SZ_8M + SZ_4M)
+#define COMCERTO_PFE_IRAM_BASE (COMCERTO_AXI_IRAM_BASE + 0x0000)
+#define COMCERTO_PFE_IRAM_SIZE SZ_8K
+
+#define IO_SPACE_LIMIT 0
+
+/* MDMA memory map */
+#define COMCERTO_APB_MDMA_SIZE SZ_1K+SZ_4
+
+/*
+ * Virtual address mapping
+ */
+#define COMCERTO_MSP_VADDR 0xf0000000
+#define COMCERTO_PFE_VADDR 0xfa000000
+#define COMCERTO_PFE_AXI_VADDR 0xfc000000
+#define COMCERTO_SCU_VADDR COMCERTO_SCU_BASE
+#define COMCERTO_GIC_CPU_VADDR (COMCERTO_SCU_VADDR + 0x100)
+#define COMCERTO_GIC_GLOBAL_TIMER_VADDR (COMCERTO_SCU_VADDR + 0x200)
+#define COMCERTO_TWD_VADDR (COMCERTO_SCU_VADDR + 0x600)
+#define COMCERTO_GIC_DIST_VADDR (COMCERTO_SCU_VADDR + 0x1000)
+#define COMCERTO_L310_VADDR (COMCERTO_SCU_VADDR + 0x10000)
+
+#define COMCERTO_DISTR_INT_SET_PENDING (COMCERTO_GIC_DIST_VADDR + 0x200)
+#define COMCERTO_DISTR_INT_SET_PENDING_OFFSET_4 (COMCERTO_DISTR_INT_SET_PENDING + 0x4)
+#define COMCERTO_DISTR_INT_SET_PENDING_OFFSET_8 (COMCERTO_DISTR_INT_SET_PENDING + 0x8)
+
+#define IRAM_MEMORY_VADDR 0xf0800000
+#define COMCERTO_APB_VADDR 0xf0900000 /* VA of IO on APB bus */
+#define COMCERTO_APB_SIZE 0x00C00000
+#define COMCERTO_AXI_UART_SPI_VADDR 0xF1600000
+#define COMCERTO_AXI_UART_SPI_SIZE 0x01000000
+#define COMCERTO_AXI_DMA_VADDR (COMCERTO_AXI_UART_SPI_VADDR+0x00000000)
+#define COMCERTO_AXI_UART0_VADDR (COMCERTO_AXI_UART_SPI_VADDR+0x00300000)
+#define COMCERTO_AXI_UART1_VADDR (COMCERTO_AXI_UART_SPI_VADDR+0x00400000)
+#define COMCERTO_AXI_SSI_VADDR (COMCERTO_AXI_UART_SPI_VADDR+0x00500000)
+
+#define COMCERTO_SEMA_VADDR 0xf4000000
+#define COMCERTO_AXI_PCIe0_VADDR_BASE 0xf5000000
+#define COMCERTO_AXI_PCIe1_VADDR_BASE 0xf6000000
+#define COMCERTO_DECT_VADDR 0xf7000000
+
+/*
+ * Reference Clock Option in Boot Strap Register
+ *
+ * BIT [9:8] System PLL Refclk Select
+ * '00' - USB XTAL
+ * '01' - Serdes #0 Refclk
+ * '10' - Serdes #1 Refclk
+ * '11' - Serdes XTAL
+ *
+ * BIT[7] Serdes OSC PAD - Reference clock frequency selection bootstrap (inverted value of SF1)
+ * '0' - 30MHz ~ 50MHz
+ * '1' - 15MHz ~ 30MHz
+ * Note: SF0 is tied to 1 in GPIO block and might later on be controlled during DFT
+ *
+ * BIT[5] USB/Sys PLL OSC PAD - Reference clock frequency selection bootstrap (inverted value of SF1)
+ *'0' - 30MHz ~ 50MHz
+ * '1' - 15MHz ~ 30MHz
+ * Note: SF0 is tied to 1 in GPIO block and might later on be controlled during DFT
+ *
+ */
+
+#define GPIO_SYS_PLL_REF_CLK_MASK (0x00000100 | 0x00000200)
+#define GPIO_SYS_PLL_REF_CLK_SHIFT 8
+
+#define USB_XTAL_REF_CLK 0
+#define SERDES_0_REF_CLK 1
+#define SERDES_2_REF_CLK 2
+#define SERDES_XTAL_REF_CLK 3
+
+#define GPIO_SERDES_OSC_PAD_MASK (0x00000080)
+#define GPIO_SERDES_OSC_PAD_SHIFT 7
+
+#define GPIO_USB_OSC_PAD_MASK (0x00000020)
+#define GPIO_USB_OSC_PAD_SHIFT 5
+
+#define REF_CLK_24MHZ 24000000 /* 24 MHz */
+#define REF_CLK_48MHZ 48000000 /* 48 MHz */
+
+
+/* USB 2.0 */
+#define USB2_PHY_BASE APB_VADDR(COMCERTO_APB_PCI_SATA_USB_CTRL_BASE)
+
+/* USB 3.0 */
+#define USB3_PHY_BASE APB_VADDR(COMCERTO_APB_USB3_BASE)
+
+#define USBPHY_SERDES_STAT_BASE APB_VADDR(COMCERTO_APB_USBPHY_SERDES_STAT_BASE)
+
+/* Includes */
+#include <mach/comcerto-2000/clk-rst.h>
+#include <mach/comcerto-2000/timer.h>
+#include <mach/comcerto-2000/gpio.h>
+#include <mach/comcerto-2000/exp-bus.h>
+#include <mach/comcerto-2000/pcie.h>
+#include <mach/comcerto-2000/serdes.h>
+#include <mach/comcerto-2000/memory.h>
+#include <mach/c2k_dma.h>
+
+#define comcerto_timer4_set(hbound) __raw_writel((hbound), COMCERTO_TIMER4_HIGH_BOUND)
+
+#define comcerto_timer4_get() __raw_readl(COMCERTO_TIMER4_CURRENT_COUNT)
+
+
+#define comcerto_timer5_set(lbound, hbound, ctrl) do { \
+ __raw_writel((ctrl) & 0x1, COMCERTO_TIMER5_CTRL); \
+ __raw_writel((lbound), COMCERTO_TIMER5_LOW_BOUND); \
+ __raw_writel((hbound), COMCERTO_TIMER5_HIGH_BOUND); \
+ } while(0)
+
+#define comcerto_timer5_get() __raw_readl(COMCERTO_TIMER5_CURRENT_COUNT)
+
+/* Number of gemacs supported in comcerto 2000 */
+#define NUM_GEMAC_SUPPORT 3
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/clk-rst.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/clk-rst.h
new file mode 100644
index 0000000..631ea98
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/clk-rst.h
@@ -0,0 +1,252 @@
+/*
+ * linux/arch/arm/mach-comcerto/include/mach/comcerto-2000/clk-rst.h
+ *
+ * Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CLK_H__
+#define __CLK_H__
+
+/*
+ * Clock Reset Registers
+ */
+#define DEVICE_RST_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x000)
+#define SERDES_RST_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x004)
+#define PCIe_SATA_RST_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x008)
+#define USB_RST_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x00C)
+#define GNRL_DEVICE_CNFG_0 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x010)
+#define GNRL_DEVICE_CNFG_1 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x014)
+#define GNRL_DEVICE_STATUS APB_VADDR(COMCERTO_APB_CLK_BASE + 0x018)
+#define A9DP_PWR_STAT APB_VADDR(COMCERTO_APB_CLK_BASE + 0x028)
+#define A9DP_PWR_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x02C)
+#define GNRL_CLK_CNTRL_0 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x030)
+#define GNRL_CLK_CNTRL_1 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x034)
+#define PLLS_GLOBAL_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x038)
+#define AXI_CLK_CNTRL_0 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x040)
+#define AXI_CLK_CNTRL_1 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x044)
+#define AXI_CLK_CNTRL_2 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x048)
+#define AXI_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x04C)
+#define AXI_RESET_0 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x050)
+#define AXI_RESET_1 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x054)
+#define AXI_RESET_2 APB_VADDR(COMCERTO_APB_CLK_BASE + 0x058)
+#define A9DP_MPU_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x68)
+#define A9DP_MPU_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x6C)
+#define A9DP_MPU_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x70)
+#define A9DP_CPU_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x74)
+#define A9DP_CPU_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x78)
+#define A9DP_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x80)
+#define A9DP_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x84)
+#define A9DP_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x88)
+#define L2CC_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x090)
+#define L2CC_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x094)
+#define L2CC_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x098)
+#define TPI_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0A0)
+#define TPI_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0A4)
+#define TPI_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0A8)
+#define CSYS_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0B0)
+#define CSYS_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0B4)
+#define CSYS_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0B8)
+#define EXTPHY0_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0C0)
+#define EXTPHY0_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0C4)
+#define EXTPHY0_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0C8)
+#define EXTPHY1_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0D0)
+#define EXTPHY1_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0D4)
+#define EXTPHY1_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0D8)
+#define EXTPHY2_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0E0)
+#define EXTPHY2_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0E4)
+#define EXTPHY2_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0E8)
+#define DDR_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0F0)
+#define DDR_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0F4)
+#define DDR_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x0F8)
+#define PFE_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x100)
+#define PFE_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x104)
+#define PFE_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x108)
+#define IPSEC_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x110)
+#define IPSEC_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x114)
+#define IPSEC_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x118)
+#define DECT_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x120)
+#define DECT_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x124)
+#define DECT_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x128)
+#define GEMTX_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x130)
+#define GEMTX_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x134)
+#define GEMTX_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x138)
+#define TDMNTG_REF_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x140)
+#define TDMNTG_REF_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x144)
+#define TDMNTG_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x148)
+#define TDM_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x14C)
+#define TSUNTG_REF_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x150)
+#define TSUNTG_REF_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x154)
+#define TSUNTG_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x158)
+#define SATA_PMU_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x160)
+#define SATA_PMU_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x164)
+#define SATA_PMU_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x168)
+#define SATA_OOB_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x170)
+#define SATA_OOB_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x174)
+#define SATA_OOB_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x178)
+#define SATA_OCC_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x180)
+#define SATA_OCC_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x184)
+#define SATA_OCC_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x188)
+#define PCIE_OCC_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x190)
+#define PCIE_OCC_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x194)
+#define PCIE_OCC_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x198)
+#define SGMII_OCC_CLK_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1A0)
+#define SGMII_OCC_CLK_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1A4)
+#define SGMII_OCC_RESET APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1A8)
+#define PLL0_M_LSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1C0)
+#define PLL0_M_MSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1C4)
+#define PLL0_P APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1C8)
+#define PLL0_S APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1CC)
+#define PLL0_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1D0)
+#define PLL0_TEST APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1D4)
+#define PLL0_STATUS APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1D8)
+#define PLL0_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1DC)
+#define PLL1_M_LSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1E0)
+#define PLL1_M_MSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1E4)
+#define PLL1_P APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1E8)
+#define PLL1_S APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1EC)
+#define PLL1_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1F0)
+#define PLL1_TEST APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1F4)
+#define PLL1_STATUS APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1F8)
+#define PLL1_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x1FC)
+#define PLL2_M_LSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x200)
+#define PLL2_M_MSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x204)
+#define PLL2_P APB_VADDR(COMCERTO_APB_CLK_BASE + 0x208)
+#define PLL2_S APB_VADDR(COMCERTO_APB_CLK_BASE + 0x20C)
+#define PLL2_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x210)
+#define PLL2_TEST APB_VADDR(COMCERTO_APB_CLK_BASE + 0x214)
+#define PLL2_STATUS APB_VADDR(COMCERTO_APB_CLK_BASE + 0x218)
+#define PLL2_DIV_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x21C)
+#define PLL3_M_LSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x220)
+#define PLL3_M_MSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x224)
+#define PLL3_P APB_VADDR(COMCERTO_APB_CLK_BASE + 0x228)
+#define PLL3_S APB_VADDR(COMCERTO_APB_CLK_BASE + 0x22c)
+#define PLL3_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x230)
+#define PLL3_TEST APB_VADDR(COMCERTO_APB_CLK_BASE + 0x234)
+#define PLL3_STATUS APB_VADDR(COMCERTO_APB_CLK_BASE + 0x238)
+#define PLL3_DITHER_CNTRL APB_VADDR(COMCERTO_APB_CLK_BASE + 0x23C)
+#define PLL3_K_LSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x240)
+#define PLL3_K_MSB APB_VADDR(COMCERTO_APB_CLK_BASE + 0x244)
+#define PLL3_MFR APB_VADDR(COMCERTO_APB_CLK_BASE + 0x248)
+#define PLL3_MRR APB_VADDR(COMCERTO_APB_CLK_BASE + 0x24C)
+
+#define PLL0 0
+#define PLL1 1
+#define PLL2 2
+#define PLL3 3
+
+/* Device Reset Control Register (DEVICE_RST_CNTRL)*/
+
+#define CLK_DIV_RESTART (1 << 7)
+#define WD_STATUS_CLR (1 << 6)
+#define AXI_WD_RST_EN (1 << 5)
+#define DEBUG_RST (1 << 4)
+#define CLKRST_SCLR_RST (1 << 3)
+#define FUNC_SCLR_RST (1 << 2)
+#define GLB_SCLR_RST (1 << 1)
+#define PWR_ON_SOFT_RST (1 << 0)
+
+/* General Device Configuration Status Register (GNRL_DEVICE_STATUS) */
+#define CPU1_WD_RST_ACTIVATED (1 << 2)
+#define CPU0_WD_RST_ACTIVATED (1 << 1)
+#define AXI_WD_RST_ACTIVATED (1 << 0)
+
+/* Clock Control Register */
+#define CLOCK_DOMAIN_ENABLE 1
+#define CLOCK_DOMAIN_ENABLE_SHIFT 0
+
+#define CLOCK_SOURCE_PLL0 0
+#define CLOCK_SOURCE_PLL1 1
+#define CLOCK_SOURCE_PLL2 2
+#define CLOCK_SOURCE_PLL3 3
+#define CLOCK_SOURCE_PLL4 4
+
+#define CLOCK_SOURCE_SHIFT 1
+
+#define CLK_DIV_BYPASS (1 << 7)
+#define CLK_DIV_RATIO_MASK 0x1f
+
+#define PLL_VSEL (1 << 6)
+#define PLL_LOCK_EN (1 << 5)
+#define PLL_BYPASS (1 << 4)
+#define PLL_RESET (1 << 0)
+
+#define PLL_LOCK (1 << 0)
+
+#define CLK_PLL_SRC_MASK 0x7
+#define CLK_PLL_SRC_SHIFT 1
+#define CLK_A9DP_PERI_DIV_BYPASS (1 << 3)
+
+
+/* TDMNTG */
+#define TDMNTG_ADDR_SPACE_BASEADDR APB_VADDR(COMCERTO_APB_CLK_BASE + 0x280)
+
+#define TDM_NTG_CLK_CTRL (TDMNTG_ADDR_SPACE_BASEADDR + 0x00)
+#define TDM_NTG_INCR (TDMNTG_ADDR_SPACE_BASEADDR + 0x04)
+#define TDM_FSYNC_GEN_CTRL (TDMNTG_ADDR_SPACE_BASEADDR + 0x08)
+#define TDM_FSYNC_LOW (TDMNTG_ADDR_SPACE_BASEADDR + 0x0C)
+#define TDM_FSYNC_HIGH (TDMNTG_ADDR_SPACE_BASEADDR + 0x10)
+
+#define FSYNC_FALL_EDGE (1 << 1)
+#define NTG_DIV_RST_N (1 << 5)
+#define NTG_EN (1 << 0)
+
+
+/* TSUNTG */
+#define TSUNTG_ADDR_SPACE_BASEADDR APB_VADDR(COMCERTO_APB_CLK_BASE + 0x2C0)
+
+/* USB 3.0 */
+#define USB1_UTMI_RST (1 << 5)
+#define USB1_PHY_RST (1 << 4)
+#define USB1_AXI_RST (1 << 4)
+
+/* USB 2.0 */
+#define USB0_PHY_RST (1 << 0)
+#define USB0_UTMI_RST (1 << 1)
+#define USB0_AXI_RST (1 << 3)
+
+/* CPU */
+#define CPU0_RST (1 << 0)
+#define NEON0_RST (1 << 1)
+#define CPU1_RST (1 << 2)
+#define NEON1_RST (1 << 3)
+
+#define CPU0_CLK_ENABLE (1 << 0)
+#define NEON0_CLK_ENABLE (1 << 1)
+#define CPU1_CLK_ENABLE (1 << 2)
+#define NEON1_CLK_ENABLE (1 << 3)
+
+#define GLOBAL_CLK_ENABLE (1 << 0)
+
+#define CLAMP_CORE0 (1 << 4)
+#define CLAMP_CORE1 (1 << 6)
+#define CORE_PWRDWN0 (1 << 5)
+#define CORE_PWRDWN1 (1 << 7)
+#define MP_PWRDWN (1 << 0)
+
+#define SCPRE (1 << 4)
+#define SCALL (1 << 5)
+#define ISO_EN (1 << 6)
+
+#ifndef __ASSEMBLY__
+
+/* Function Declaration */
+extern void HAL_clk_div_backup_relocate_table (void);
+extern unsigned long HAL_get_ref_clk (void);
+#endif
+
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/clock.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/clock.h
new file mode 100644
index 0000000..e29fb2f
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/clock.h
@@ -0,0 +1,41 @@
+/*
+ * arch/arm/mach-comcerto/clock.h
+ *
+ * Clock control driver for Comcerto C2K device - internal header file
+ *
+ * This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#ifndef __ARCH_ARM_C2K_CLOCK_H__
+#define __ARCH_ARM_C2K_CLOCK_H__
+
+#define CLK_DOMAIN_MASK (1<<0)
+#define CLK_DOMAIN_SPI_I2C_MASK (1<<5)
+#define CLK_DOMAIN_TDMNTG_MASK (1<<4)
+#define CLK_DOMAIN_UART_MASK (1<<6)
+#define CLK_DOMAIN_PCIE0_MASK (1<<0)
+#define CLK_DOMAIN_PCIE1_MASK (1<<1)
+#define CLK_DOMAIN_IPSEC_SPACC_MASK (1<<2)
+#define CLK_DOMAIN_DPI_CIE_MASK (1<<5)
+#define CLK_DOMAIN_DPI_DECOMP_MASK (1<<6)
+#define CLK_DOMAIN_USB0_MASK (1<<3)
+#define CLK_DOMAIN_USB1_MASK (1<<4)
+#define CLK_DOMAIN_DUS_MASK (1<<0)
+#define CLK_DOMAIN_SATA_MASK (1<<2)
+
+struct clk {
+ struct clk *parent;
+ const char *name;
+ unsigned long rate; /* Default rate value in HZ */
+ int usecount;
+ u32 enable_reg;
+ u32 clkgen_reg;
+ u32 div_reg;
+ u32 enable_mask;
+ unsigned long (*get_rate)(struct clk *);
+ int (*set_rate) (struct clk *, unsigned long rate);
+};
+extern int clk_init(void);
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/exp-bus.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/exp-bus.h
new file mode 100644
index 0000000..9ca39a1
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/exp-bus.h
@@ -0,0 +1,102 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/comcerto-2000/exp-bus.h
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __COMCERTO_EXP_BUS_H__
+#define __COMCERTO_EXP_BUS_H__
+
+
+/***** Registers address *****/
+
+#define COMCERTO_EXP_SW_RST_R APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x000)
+#define COMCERTO_EXP_CS_EN_R APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x004)
+#define COMCERTO_EXP_CSx_BASE_R(x) APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x008 + 0x4 * (x))
+#define COMCERTO_EXP_CSx_SEG_R(x) APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x01C + 0x4 * (x))
+#define COMCERTO_EXP_CSx_CFG_R(x) APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x030 + 0x4 * (x))
+#define COMCERTO_EXP_CSx_TMG1_R(x) APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x044 + 0x4 * (x))
+#define COMCERTO_EXP_CSx_TMG2_R(x) APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x058 + 0x4 * (x))
+#define COMCERTO_EXP_CSx_TMG3_R(x) APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x06C + 0x4 * (x))
+#define COMCERTO_EXP_CLOCK_DIV_R APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x080)
+#define COMCERTO_EXP_MFSM_R APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x100)
+#define COMCERTO_EXP_CSFSM_R APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x104)
+#define COMCERTO_EXP_WRSM_R APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x108)
+#define COMCERTO_EXP_RDSM_R APB_VADDR(COMCERTO_APB_EXPBUS_BASE + 0x10C)
+
+
+/***** Masks *****/
+
+/* EXP_SWRST_R register*/
+#define EXP_SW_RST 0x00000001
+
+/* EXP_CS_EN_R register*/
+#define EXP_CS4_EN 0x00000020
+#define EXP_CS3_EN 0x00000010
+#define EXP_CS2_EN 0x00000008
+#define EXP_CS1_EN 0x00000004
+#define EXP_CS0_EN 0x00000002
+#define EXP_CSx_EN(x) (1 << ((x) + 1))
+#define EXP_CLK_EN 0x00000001
+
+/* EXP_CSx_CFG_R register*/
+#define EXP_RDY_EDG 0x00000800
+#define EXP_RDY_EN 0x00000400
+#define EXP_NAND_MODE 0x00000200
+#define EXP_DM_MODE 0x00000100
+#define EXP_STRB_MODE 0x00000080
+#define EXP_ALE_MODE 0x00000040
+#define EXP_RE_CMD_LVL 0x00000020
+#define EXP_WE_CMD_LVL 0x00000010
+#define EXP_CS_LEVEL 0x00000008
+#define EXP_MEM_BUS_SIZE 0x00000006
+#define EXP_MEM_BUS_SIZE_32 0x00000004
+#define EXP_MEM_BUS_SIZE_16 0x00000002
+#define EXP_MEM_BUS_SIZE_8 0x00000000
+
+
+
+#define EXP_CS0_SEG_SIZE SZ_128M
+#define EXP_CS1_SEG_SIZE SZ_1M
+#define EXP_CS2_SEG_SIZE SZ_1M
+#define EXP_CS3_SEG_SIZE SZ_1M
+#define EXP_CS4_SEG_SIZE SZ_1M
+
+#define EXP_CS0_AXI_BASEADDR COMCERTO_AXI_EXP_BASE
+#define EXP_CS1_AXI_BASEADDR (EXP_CS0_AXI_BASEADDR + EXP_CS0_SEG_SIZE)
+#define EXP_CS2_AXI_BASEADDR (EXP_CS1_AXI_BASEADDR + EXP_CS1_SEG_SIZE)
+#define EXP_CS3_AXI_BASEADDR (EXP_CS2_AXI_BASEADDR + EXP_CS2_SEG_SIZE)
+#define EXP_CS4_AXI_BASEADDR (EXP_CS3_AXI_BASEADDR + EXP_CS3_SEG_SIZE)
+
+#define EXP_BUS_REG_BASE_CS0 (EXP_CS0_AXI_BASEADDR & ~0xf0000000)
+#define EXP_BUS_REG_BASE_CS1 (EXP_CS1_AXI_BASEADDR & ~0xf0000000)
+#define EXP_BUS_REG_BASE_CS2 (EXP_CS2_AXI_BASEADDR & ~0xf0000000)
+#define EXP_BUS_REG_BASE_CS3 (EXP_CS3_AXI_BASEADDR & ~0xf0000000)
+#define EXP_BUS_REG_BASE_CS4 (EXP_CS4_AXI_BASEADDR & ~0xf0000000)
+
+/* EXP_CSx_TMG1_R register */
+/* EXP_CSx_TMG2_R register */
+/* EXP_CSx_TMG3_R register */
+
+/* EXP_CLOCK_DIV_R register */
+
+/* EXP_MFSM_R register*/
+/* EXP_CSFSM_R register*/
+/* EXP_WRFSM_R register*/
+/* EXP_RDFSM_R register*/
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/gpio.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/gpio.h
new file mode 100644
index 0000000..764b176
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/gpio.h
@@ -0,0 +1,315 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/comcerto-2000/gpio.h
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __COMCERTO_GPIO_H__
+#define __COMCERTO_GPIO_H__
+
+/***** GPIO *****/
+#define COMCERTO_GPIO_OUTPUT_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x00)
+#define COMCERTO_GPIO_OE_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x04)
+#define COMCERTO_GPIO_INT_CFG_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x08)
+#define COMCERTO_GPIO_INPUT_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x10)
+#define COMCERTO_GPIO_APB_WS APB_VADDR(OMCERTO_APB_GPIO_BASE + 0x14)
+#define COMCERTO_GPIO_SYSTEM_CONFIG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x1C)
+#define COMCERTO_GPIO_TDM_MUX APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x28)
+#define COMCERTO_GPIO_MISC_CTRL APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x34)
+#define COMCERTO_GPIO_DDR_AXI_CTRL APB_VADDR(OMCERTO_APB_GPIO_BASE + 0x38)
+#define COMCERTO_GPIO_DDRC_STATUS APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x3C)
+#define COMCERTO_GPIO_BOOTSTRAP_STATUS APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x40)
+#define COMCERTO_GPIO_BOOTSTRAP_OVERRIDE APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x44)
+
+
+#define COMCERTO_GPIO_DEVICE_ID_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x50)
+#define COMCERTO_GPIO_PIN_SELECT_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x58)
+#define COMCERTO_GPIO_PIN_SELECT_REG1 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x5C)
+#define COMCERTO_GPIO_MISC_PIN_SELECT APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x60)
+
+#define COMCERTO_GPIO_FABRIC_CTRL_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x6C)
+#define COMCERTO_GPIO_A9_AUTH_CTRL_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x70)
+#define COMCERTO_GPIO_A9_ACP_CONF_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x74)
+#define COMCERTO_GPIO_PCIE_CLK_OUT_CTRL_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x80)
+#define COMCERTO_GPIO_INTR_CLR_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x98)
+#define COMCERTO_GPIO_INTR_SET_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x9C)
+#define COMCERTO_GPIO_INTR_MASK_REG APB_VADDR(COMCERTO_APB_GPIO_BASE + 0xA0)
+#define COMCERTO_GPIO_CSS_DECT_SYS_CFG0 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0xB0)
+#define COMCERTO_GPIO_CSS_DECT_SYS_CFG1 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0xB4)
+#define COMCERTO_GPIO_CSS_DECT_CTRL APB_VADDR(COMCERTO_APB_GPIO_BASE + 0xB8)
+#define COMCERTO_GPIO_63_32_PIN_OUTPUT APB_VADDR(COMCERTO_APB_GPIO_BASE + 0xD0)
+#define COMCERTO_GPIO_63_32_PIN_OUTPUT_EN APB_VADDR(COMCERTO_APB_GPIO_BASE + 0xD4)
+#define COMCERTO_GPIO_63_32_PIN_INPUT APB_VADDR(COMCERTO_APB_GPIO_BASE + 0xD8)
+#define COMCERTO_GPIO_63_32_PIN_SELECT APB_VADDR(COMCERTO_APB_GPIO_BASE + 0xDC)
+
+#define COMCERTO_GPIO_PAD_CONFIG0 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x100)
+#define COMCERTO_GPIO_PAD_CONFIG1 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x104)
+#define COMCERTO_GPIO_PAD_CONFIG2 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x108)
+#define COMCERTO_GPIO_PAD_CONFIG3 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x10C)
+#define COMCERTO_GPIO_PAD_CONFIG4 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x110)
+#define COMCERTO_GPIO_PAD_CONFIG5 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x114)
+#define COMCERTO_GPIO_PAD_CONFIG6 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x118)
+#define COMCERTO_GPIO_PAD_CONFIG7 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x11C)
+#define COMCERTO_GPIO_PAD_CONFIG8 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x120)
+#define COMCERTO_GPIO_PAD_CONFIG9 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x124)
+#define COMCERTO_GPIO_PAD_CONFIG10 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x128)
+#define COMCERTO_GPIO_PAD_CONFIG11 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x12C)
+#define COMCERTO_GPIO_PAD_CONFIG12 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x130)
+#define COMCERTO_GPIO_PAD_CONFIG13 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x134)
+#define COMCERTO_GPIO_PAD_CONFIG14 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x138)
+
+#define COMCERTO_GPIO_PMU_INTR_CLR APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x160)
+#define COMCERTO_GPIO_PMU_INTR_SET APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x164)
+#define COMCERTO_GPIO_PMU_INTR_MASK0 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x168)
+#define COMCERTO_GPIO_PMU_INTR_MASK1 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x16C)
+
+#define COMCERTO_GPIO_MEM_EMA_CONF0 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x1A4)
+#define COMCERTO_GPIO_MEM_EMA_CONF1 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x1A8)
+#define COMCERTO_GPIO_MEM_EMA_CONF2 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x1A8)
+#define COMCERTO_GPIO_MEM_EMA_CONF3 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x1AC)
+#define COMCERTO_GPIO_MEM_EMA_CONF4 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x1B0)
+#define COMCERTO_GPIO_MEM_EMA_CONF5 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x1B4)
+#define COMCERTO_GPIO_MEM_EMA_CONF6 APB_VADDR(COMCERTO_APB_GPIO_BASE + 0x1B8)
+
+#define GPIO_0 0x00000003
+#define GPIO_1 0x0000000C
+#define GPIO_2 0x00000030
+#define GPIO_3 0x000000C0
+#define GPIO_4 0x00000300
+#define GPIO_5 0x00000C00
+#define GPIO_6 0x00003000
+#define GPIO_7 0x0000C000
+#define GPIO_8 0x00030000
+#define GPIO_9 0x000C0000
+#define GPIO_10 0x00300000
+#define GPIO_11 0x00C00000
+#define GPIO_12 0x03000000
+#define GPIO_13 0x0C000000
+#define GPIO_14 0x30000000
+#define GPIO_15 0xC0000000
+#define GPIO_16 0x00000003
+#define GPIO_17 0x0000000C
+#define GPIO_18 0x00000030
+#define GPIO_19 0x000000C0
+#define GPIO_20 0x00000300
+#define GPIO_21 0x00000C00
+#define GPIO_22 0x00003000
+#define GPIO_23 0x0000C000
+#define GPIO_24 0x00030000
+#define GPIO_25 0x000C0000
+#define GPIO_26 0x00300000
+#define GPIO_27 0x00C00000
+#define GPIO_28 0x03000000
+#define GPIO_29 0x0C000000
+#define GPIO_30 0x30000000
+#define GPIO_31 0xC0000000
+
+#define GPIO_0_S 0x00000001
+#define GPIO_1_S 0x00000002
+#define GPIO_2_S 0x00000004
+
+/* GPIO Pin Number and Description */
+#define GPIO_PIN_NUM_0 0
+#define GPIO_PIN_NUM_1 1
+#define GPIO_PIN_NUM_2 2
+#define GPIO_PIN_NUM_3 3
+#define GPIO_PIN_NUM_4 4
+#define GPIO_PIN_NUM_5 5
+
+#define GPIO_PIN_DESC_0 "gpio-0"
+#define GPIO_PIN_DESC_1 "gpio-1"
+#define GPIO_PIN_DESC_2 "gpio-2"
+#define GPIO_PIN_DESC_3 "gpio-3"
+#define GPIO_PIN_DESC_4 "gpio-4"
+#define GPIO_PIN_DESC_5 "gpio-5"
+
+#define GPIO_SET_0 0
+#define GPIO_SET_1 1
+
+
+/* GPIO Pin Mask */
+#define GPIO_PIN_0 (0x1 << 0)
+#define GPIO_PIN_1 (0x1 << 1)
+#define GPIO_PIN_2 (0x1 << 2)
+#define GPIO_PIN_3 (0x1 << 3)
+#define GPIO_PIN_4 (0x1 << 4)
+#define GPIO_PIN_5 (0x1 << 5)
+#define GPIO_PIN_6 (0x1 << 6)
+#define GPIO_PIN_7 (0x1 << 7)
+#define GPIO_PIN_8 (0x1 << 8)
+#define GPIO_PIN_9 (0x1 << 9)
+#define GPIO_PIN_10 (0x1 << 10)
+#define GPIO_PIN_11 (0x1 << 11)
+#define GPIO_PIN_12 (0x1 << 12)
+#define GPIO_PIN_13 (0x1 << 13)
+#define GPIO_PIN_14 (0x1 << 14)
+#define GPIO_PIN_15 (0x1 << 15)
+#define GPIO_PIN_16 (0x1 << 16)
+#define GPIO_PIN_17 (0x1 << 17)
+#define GPIO_PIN_18 (0x1 << 18)
+#define GPIO_PIN_19 (0x1 << 19)
+#define GPIO_PIN_20 (0x1 << 20)
+#define GPIO_PIN_21 (0x1 << 21)
+#define GPIO_PIN_22 (0x1 << 22)
+#define GPIO_PIN_23 (0x1 << 23)
+#define GPIO_PIN_24 (0x1 << 24)
+#define GPIO_PIN_25 (0x1 << 25)
+#define GPIO_PIN_26 (0x1 << 26)
+#define GPIO_PIN_27 (0x1 << 27)
+#define GPIO_PIN_28 (0x1 << 28)
+#define GPIO_PIN_29 (0x1 << 29)
+#define GPIO_PIN_30 (0x1 << 30)
+#define GPIO_PIN_31 (0x1 << 31)
+
+#define GPIO_PIN_32 (0x1 << (32 - 32))
+#define GPIO_PIN_33 (0x1 << (33 - 32))
+#define GPIO_PIN_34 (0x1 << (34 - 32))
+#define GPIO_PIN_35 (0x1 << (35 - 32))
+#define GPIO_PIN_36 (0x1 << (36 - 32))
+#define GPIO_PIN_37 (0x1 << (37 - 32))
+#define GPIO_PIN_38 (0x1 << (38 - 32))
+#define GPIO_PIN_39 (0x1 << (39 - 32))
+#define GPIO_PIN_40 (0x1 << (40 - 32))
+#define GPIO_PIN_41 (0x1 << (41 - 32))
+#define GPIO_PIN_42 (0x1 << (42 - 32))
+#define GPIO_PIN_43 (0x1 << (43 - 32))
+#define GPIO_PIN_44 (0x1 << (44 - 32))
+#define GPIO_PIN_45 (0x1 << (45 - 32))
+#define GPIO_PIN_46 (0x1 << (46 - 32))
+#define GPIO_PIN_47 (0x1 << (47 - 32))
+#define GPIO_PIN_48 (0x1 << (48 - 32))
+#define GPIO_PIN_49 (0x1 << (49 - 32))
+#define GPIO_PIN_50 (0x1 << (50 - 32))
+#define GPIO_PIN_51 (0x1 << (51 - 32))
+#define GPIO_PIN_52 (0x1 << (52 - 32))
+#define GPIO_PIN_53 (0x1 << (53 - 32))
+#define GPIO_PIN_54 (0x1 << (54 - 32))
+#define GPIO_PIN_55 (0x1 << (55 - 32))
+#define GPIO_PIN_56 (0x1 << (56 - 32))
+#define GPIO_PIN_57 (0x1 << (57 - 32))
+#define GPIO_PIN_58 (0x1 << (58 - 32))
+#define GPIO_PIN_59 (0x1 << (59 - 32))
+#define GPIO_PIN_60 (0x1 << (60 - 32))
+#define GPIO_PIN_61 (0x1 << (61 - 32))
+#define GPIO_PIN_62 (0x1 << (62 - 32))
+#define GPIO_PIN_63 (0x1 << (63 - 32))
+
+/* GPIO Pin Select Pins */
+
+#define GPIO4_PWM0 (0x1 << 8)
+#define GPIO5_PWM1 (0x1 << 10)
+#define GPIO6_PWM2 (0x1 << 12)
+#define GPIO6_SATA0_ACT_LED (0x2 << 12)
+#define GPIO7_PWM3 (0x1 << 14)
+#define GPIO7_SATA0_CP_POD (0x2 << 14)
+#define GPIO8_UART0_RX (0x2 << 16)
+#define GPIO9_UART0_TX (0x2 << 18)
+#define GPIO10_UART0_RTS_N (0x2 << 20)
+#define GPIO10_DECT0 (0x3 << 20)
+#define GPIO11_UART0_CTS_N (0x2 << 22)
+#define GPIO11_DECT2 (0x3 << 22)
+#define GPIO12_PWM4 (0x1 << 24)
+#define GPIO12_PFE_UART_RX (0x2 << 24)
+#define GPIO12_DECT3 (0x3 << 24)
+#define GPIO13_PWM5 (0x1 << 26)
+#define GPIO13_PFE_UART_TX (0x2 << 26)
+#define GPIO13_DECT4 (0x3 << 26)
+#define GPIO14_SATA1_ACT_LED (0x1 << 28)
+#define GPIO14_TIM_EVENT0 (0x2 << 28)
+#define GPIO14_DECT5 (0x3 << 28)
+#define GPIO15_SATA1_CP_POD (0x1 << 30)
+#define GPIO15_TIM_EVENT1 (0x2 << 30)
+#define GPIO15_RTCOSC (0x3 << 30)
+#define GPIO16_I2C_SCL (0x0 << 0)
+#define GPIO17_I2C_SDA (0x0 << 2)
+#define GPIO18_SPI_SS0_N (0x0 << 4)
+#define GPIO19_SPI_SS1_N (0x0 << 6)
+#define GPIO20_SPI2_SS1_N (0x0 << 8)
+#define GPIO21_SPI_SS2_N (0x0 << 10)
+#define GPIO22_SPI_SS3_N (0x0 << 12)
+#define GPIO23_EXP_CS2 (0x0 << 14)
+#define GPIO24_EXP_CS3 (0x0 << 16)
+#define GPIO25_EXP_ALE (0x0 << 18)
+#define GPIO26_EXP_RDY (0x0 << 20)
+#define GPIO27_TM_EXT_RESET (0x0 << 22)
+#define GPIO28_EXP_NAND_CS (0x0 << 24)
+#define GPIO29_EXP_NAND_RDY (0x0 << 26)
+#define GPIO30_SPI_TXD (0x0 << 28)
+#define GPIO31_SPI_SCLK (0x0 << 30)
+#define GPIO32_SPI_RXD (0x0 << 0)
+#define GPIO33_SPI_2_RXD (0x0 << 1)
+#define GPIO34_SPI_2_SS0_N (0x0 << 2)
+
+#define BOOT_SERDES1_CNF_SATA0 (1 << 11)
+#define BOOT_SERDES2_CNF_SATA1 (1 << 12)
+
+ /* 0000 0000 0000 0000 0000 0011 0000 0000 */
+#define SPI_2_MUX_GPIO_1 (GPIO_4)
+#define SPI_2_MUX_BUS_1 (GPIO20_SPI2_SS1_N)
+#define SPI_2_MUX_GPIO_1_PIN (GPIO_PIN_20)
+
+ /* 0000 0000 0000 0000 0000 0000 0000 0110 */
+#define SPI_2_MUX_GPIO_2 (GPIO_1_S | GPIO_2_S)
+#define SPI_2_MUX_BUS_2 (GPIO34_SPI_2_SS0_N | GPIO33_SPI_2_RXD)
+#define SPI_2_MUX_GPIO_2_PIN (GPIO_PIN_34 | GPIO_PIN_33)
+
+ /* 1111 0000 0000 0000 0011 1100 1111 0000 */
+#define SPI_MUX_GPIO_1 (GPIO_30 | GPIO_31 | GPIO_18 | GPIO_19 | GPIO_21 | GPIO_22)
+#define SPI_MUX_BUS_1 (GPIO31_SPI_SCLK | GPIO30_SPI_TXD | GPIO22_SPI_SS3_N | GPIO21_SPI_SS2_N \
+ | GPIO19_SPI_SS1_N | GPIO18_SPI_SS0_N)
+#define SPI_MUX_GPIO_1_PIN (GPIO_PIN_30 | GPIO_PIN_31 | GPIO_PIN_18 | GPIO_PIN_19 | GPIO_PIN_21 | GPIO_PIN_22)
+
+ /* 0000 0000 0000 0000 0000 0000 0000 0001 */
+#define SPI_MUX_GPIO_2 (GPIO_0_S)
+#define SPI_MUX_BUS_2 (GPIO32_SPI_RXD)
+#define SPI_MUX_GPIO_2_PIN (GPIO_PIN_32)
+
+#define UART0_GPIO (GPIO_8 | GPIO_9 | GPIO_10 | GPIO_11)
+#define UART0_BUS (GPIO8_UART0_RX | GPIO9_UART0_TX | GPIO10_UART0_RTS_N | GPIO11_UART0_CTS_N)
+#define UART0_GPIO_PIN (GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11)
+
+#define PFE_UART_GPIO (GPIO_12 | GPIO_13)
+#define PFE_UART_BUS (GPIO12_PFE_UART_RX | GPIO13_PFE_UART_TX)
+#define PFE_UART_GPIO_PIN (GPIO_PIN_12 | GPIO_PIN_13)
+
+#define I2C_GPIO (GPIO_16 | GPIO_17)
+#define I2C_BUS (GPIO16_I2C_SCL | GPIO17_I2C_SDA)
+#define I2C_GPIO_PIN (GPIO_PIN_16 | GPIO_PIN_17)
+
+#define NAND_GPIO (GPIO_28 | GPIO_29 )
+#define NAND_BUS (GPIO28_EXP_NAND_CS | GPIO29_EXP_NAND_RDY)
+#define NAND_GPIO_PIN (GPIO_PIN_28 | GPIO_PIN_29)
+
+#define NOR_GPIO (GPIO_25 | GPIO_26)
+#define NOR_BUS (GPIO25_EXP_ALE | GPIO26_EXP_RDY)
+#define NOR_GPIO_PIN (GPIO_PIN_25 | GPIO_PIN_26)
+
+#define C2K_GPIO_NR_GPIOS 64
+
+#define ARCH_NR_GPIOS C2K_GPIO_NR_GPIOS
+
+#ifndef __ASSEMBLY__
+/* This is a temporary bit mask for avoiding usage of reserved gpio pins. */
+struct c2k_gpio_pin_stat_info {
+ uint32_t c2k_gpio_pins_0_31;
+ uint32_t c2k_gpio_pins_32_63;
+};
+
+extern struct c2k_gpio_pin_stat_info c2k_gpio_pin_stat;
+#endif
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/i2c.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/i2c.h
new file mode 100644
index 0000000..38bf9bd
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/i2c.h
@@ -0,0 +1,58 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/comcerto1000/i2c.h
+ *
+ * Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __ASM_ARCH_COMCERTO1000_I2C_H
+#define __ASM_ARCH_COMCERTO1000_I2C_H
+
+#define COMCERTO_I2C_ADDR (0x00*4)
+#define COMCERTO_I2C_DATA (0x01*4)
+#define COMCERTO_I2C_CNTR (0x02*4)
+#define COMCERTO_I2C_STAT (0x03*4)
+#define COMCERTO_I2C_CCRFS (0x03*4)
+#define COMCERTO_I2C_XADDR (0x04*4)
+#define COMCERTO_I2C_CCRH (0x05*4)
+#define COMCERTO_I2C_RESET (0x07*4)
+
+/* CNTR - Control register bits */
+#define CNTR_IEN (1<<7)
+#define CNTR_ENAB (1<<6)
+#define CNTR_STA (1<<5)
+#define CNTR_STP (1<<4)
+#define CNTR_IFLG (1<<3)
+#define CNTR_AAK (1<<2)
+
+/* STAT - Status codes */
+#define STAT_BUS_ERROR 0x00 /* Bus error in master mode only */
+#define STAT_START 0x08 /* Start condition transmitted */
+#define STAT_START_REPEATED 0x10 /* Repeated Start condition transmited */
+#define STAT_ADDR_WR_ACK 0x18 /* Address + Write bit transmitted, ACK received */
+#define STAT_ADDR_WR_NACK 0x20 /* Address + Write bit transmitted, NACK received */
+#define STAT_DATA_WR_ACK 0x28 /* Data byte transmitted in master mode , ACK received */
+#define STAT_DATA_WR_NACK 0x30 /* Data byte transmitted in master mode , NACK received */
+#define STAT_ARBIT_LOST 0x38 /* Arbitration lost in address or data byte */
+#define STAT_ADDR_RD_ACK 0x40 /* Address + Read bit transmitted, ACK received */
+#define STAT_ADDR_RD_NACK 0x48 /* Address + Read bit transmitted, NACK received */
+#define STAT_DATA_RD_ACK 0x50 /* Data byte received in master mode, ACK transmitted */
+#define STAT_DATA_RD_NACK 0x58 /* Data byte received in master mode, NACK transmitted*/
+#define STAT_ARBIT_LOST_ADDR 0x68 /* Arbitration lost in address */
+#define STAT_GENERAL_CALL 0x70 /* General Call, ACK transmitted */
+#define STAT_NO_RELEVANT_INFO 0xF8 /* No relevant status information, IFLF=0 */
+
+#endif /* __ASM_ARCH_COMCERTO1000_I2C_H */
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/io.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/io.h
new file mode 100644
index 0000000..deb36f9
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/io.h
@@ -0,0 +1,77 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/comcerto-1000/io.h
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_COMCERTO1000_IO_H
+#define __ASM_ARCH_COMCERTO1000_IO_H
+
+#include <asm/io.h>
+#include <mach/hardware.h>
+
+#if !defined(CONFIG_PCI)
+
+#define __io(a) ((void __iomem *)(a))
+#define __mem_pci(a) (a)
+
+#else
+
+#define __mem_pci(a) (a)
+
+/* IO ports are not supported */
+#define outb(v,p) __readwrite_bug("outb")
+#define outw(v,p) __readwrite_bug("outw")
+#define outl(v,p) __readwrite_bug("outl")
+
+#define inb(p) (__readwrite_bug("inb"), 0)
+#define inw(p) (__readwrite_bug("inw"), 0)
+#define inl(p) (__readwrite_bug("inl"), 0)
+
+#define outsb(p,d,l) __readwrite_bug("outsb")
+#define outsw(p,d,l) __readwrite_bug("outsw")
+#define outsl(p,d,l) __readwrite_bug("outsl")
+
+#define insb(p,d,l) (__readwrite_bug("insb"), 0)
+#define insw(p,d,l) (__readwrite_bug("insw"), 0)
+#define insl(p,d,l) (__readwrite_bug("insl"), 0)
+
+/*
+ * io{read,write}{8,16,32} macros
+ */
+
+#define ioread8(p) ({ unsigned int __v = __raw_readb(p); __v; })
+#define ioread16(p) ({ unsigned int __v = le16_to_cpu(__raw_readw(p)); __v; })
+#define ioread32(p) ({ unsigned int __v = le32_to_cpu(__raw_readl(p)); __v; })
+
+#define iowrite8(v,p) __raw_writeb(v, p)
+#define iowrite16(v,p) __raw_writew(cpu_to_le16(v), p)
+#define iowrite32(v,p) __raw_writel(cpu_to_le32(v), p)
+
+#define ioread8_rep(p,d,c) __raw_readsb(p,d,c)
+#define ioread16_rep(p,d,c) __raw_readsw(p,d,c)
+#define ioread32_rep(p,d,c) __raw_readsl(p,d,c)
+
+#define iowrite8_rep(p,s,c) __raw_writesb(p,s,c)
+#define iowrite16_rep(p,s,c) __raw_writesw(p,s,c)
+#define iowrite32_rep(p,s,c) __raw_writesl(p,s,c)
+
+#define ioport_map(c,s) (__readwrite_bug("ioport_map"), NULL)
+#define ioport_unmap(addr) __readwrite_bug("ioport_unmap")
+
+#endif /* !defined(CONFIG_PCI) */
+
+#endif /* __ASM_ARCH_COMCERTO1000_IO_H */
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/irqs.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/irqs.h
new file mode 100644
index 0000000..625af66
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/irqs.h
@@ -0,0 +1,121 @@
+/*
+ * arch/arm/arch-comcerto/include/mach/comcerto-2000/irqs.h
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __COMCERTO_IRQS_H__
+#define __COMCERTO_IRQS_H__
+
+#define FIQ_START (0)
+
+#define SGI_IRQ(x) (0 + (x)) /* ID0 - ID15 */
+#define PPI_IRQ(x) (27 + (x)) /* ID27 - ID31 */
+#define LSPI_IRQ(x) (32 + (x)) /* ID32 - ID62 */
+#define SPI_IRQ(x) (63 + (x)) /* ID63 - ID1020 */
+
+#define IRQ_LOCALTIMER PPI_IRQ(2)
+#define IRQ_LOCALWDOG PPI_IRQ(3)
+
+#define IRQ_PTP0 LSPI_IRQ(0)
+#define IRQ_PTP1 LSPI_IRQ(1)
+#define IRQ_PTP2 LSPI_IRQ(2)
+#define IRQ_PTP3 LSPI_IRQ(3)
+#define IRQ_PFE_HIF LSPI_IRQ(4)
+#define IRQ_PFE_HIFNCPY LSPI_IRQ(5)
+#define IRQ_PFE_GPT LSPI_IRQ(6)
+#define IRQ_FABRIC LSPI_IRQ(7)
+#define IRQ_A9_L1_PAR LSPI_IRQ(8)
+#define IRQ_A9_PMU0 LSPI_IRQ(9)
+#define IRQ_A9_PMU1 LSPI_IRQ(10)
+#define IRQ_L2 LSPI_IRQ(11)
+#define IRQ_EAPE LSPI_IRQ(12)
+#define IRQ_SPACC LSPI_IRQ(13)
+#define IRQ_CIE LSPI_IRQ(14)
+#define IRQ_DEC LSPI_IRQ(15)
+#define IRQ_SATA LSPI_IRQ(16)
+#define IRQ_CSS0 LSPI_IRQ(17)
+#define IRQ_CSS1 LSPI_IRQ(18)
+#define IRQ_CSS2 LSPI_IRQ(19)
+#define IRQ_CSS3 LSPI_IRQ(20)
+#define IRQ_USB2 LSPI_IRQ(21)
+#define IRQ_USB3 LSPI_IRQ(22)
+#define IRQ_SATA_MSI LSPI_IRQ(23)
+#define IRQ_CSS LSPI_IRQ(24)
+#define IRQ_DMAC LSPI_IRQ(25)
+#define IRQ_UART0 LSPI_IRQ(26)
+#define IRQ_UART1 LSPI_IRQ(27)
+#define IRQ_UART_S2 LSPI_IRQ(27)
+#define IRQ_SPI LSPI_IRQ(28)
+#define IRQ_SPI_LS LSPI_IRQ(29)
+#define IRQ_I2C LSPI_IRQ(30)
+
+#define IRQ_PFE_UPE SPI_IRQ(0)
+#define IRQ_DDRC SPI_IRQ(1)
+#define IRQ_TDMA_TX SPI_IRQ(2)
+#define IRQ_TDMA_RX SPI_IRQ(3)
+#define IRQ_TDMA_AHBERR SPI_IRQ(4)
+#define IRQ_PFE_UPE_TIMER SPI_IRQ(5)
+#define IRQ_TDMA SPI_IRQ(6)
+#define IRQ_SLIC SPI_IRQ(7)
+#define IRQ_MDMA_M2IO SPI_IRQ(8)
+#define IRQ_MDMA_IO2M SPI_IRQ(9)
+#define IRQ_MDMA_AXIW SPI_IRQ(10)
+#define IRQ_MDMA_AXIR SPI_IRQ(11)
+#define IRQ_PCIe0 SPI_IRQ(12)
+#define IRQ_PCIe1 SPI_IRQ(13)
+#define IRQ_G0 SPI_IRQ(14)
+#define IRQ_G1 SPI_IRQ(15)
+#define IRQ_G2 SPI_IRQ(16)
+#define IRQ_G3 SPI_IRQ(17)
+#define IRQ_G4 SPI_IRQ(18)
+#define IRQ_G5 SPI_IRQ(19)
+#define IRQ_G6 SPI_IRQ(20)
+#define IRQ_G7 SPI_IRQ(21)
+#define IRQ_WOL SPI_IRQ(22)
+#define IRQ_USB3_PME SPI_IRQ(23)
+#define IRQ_TIMER0 SPI_IRQ(24)
+#define IRQ_TIMER1 SPI_IRQ(25)
+#define IRQ_TIMER2 SPI_IRQ(26)
+#define IRQ_TIMER3 SPI_IRQ(27)
+#define IRQ_TIMER4 SPI_IRQ(28)
+#define IRQ_TIMER5 SPI_IRQ(29)
+#define IRQ_TIMER6 SPI_IRQ(30)
+#define IRQ_RTC_ALM SPI_IRQ(31)
+#define IRQ_RTC_PRI SPI_IRQ(32)
+
+
+/* Software decoded interrupts used by PCIE */
+#define COMCERTO_IRQ_MAX SPI_IRQ(33)
+
+/* PCIE MSI virtual irqs */
+#define PCIE_NUM_MSI_IRQS 32
+#define PCIE0_MSI_INT_BASE (COMCERTO_IRQ_MAX + 0)
+#define PCIE0_MSI_INT_END (PCIE0_MSI_INT_BASE + PCIE_NUM_MSI_IRQS)
+#define PCIE1_MSI_INT_BASE (PCIE0_MSI_INT_END + 0)
+#define PCIE1_MSI_INT_END (PCIE1_MSI_INT_BASE + PCIE_NUM_MSI_IRQS)
+
+#define PCIE_NUM_INTX_IRQS 4
+#define PCIE0_INTX_BASE (PCIE1_MSI_INT_END + 0)
+#define PCIE0_INTX_END (PCIE0_INTX_BASE + PCIE_NUM_INTX_IRQS)
+#define PCIE1_INTX_BASE (PCIE0_INTX_END + 0)
+#define PCIE1_INTX_END (PCIE1_INTX_BASE + PCIE_NUM_INTX_IRQS)
+
+#define VIRQ_END PCIE1_INTX_END
+#define NR_IRQS 256
+
+#endif
+
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/memory.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/memory.h
new file mode 100644
index 0000000..8253848
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/memory.h
@@ -0,0 +1,38 @@
+/*
+ * arch/arm/arch-comcerto/include/mach/comcerto-2000/memory.h
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef __MEMORY_H__
+#define __MEMORY_H__
+
+ /* Physical addresses of memories */
+ #define IRAM_MEMORY_SIZE SZ_64K
+
+/*
+ * Virtual view <-> DMA view memory address translations
+ * virt_to_bus: Used to translate the virtual address to an
+ * address suitable to be passed to set_dma_addr
+ * bus_to_virt: Used to convert an address for DMA operations
+ * to an address that the kernel can use.
+ */
+
+#define aram_to_virt(p) (void*)(((unsigned long)p - COMCERTO_AXI_IRAM_BASE) + IRAM_MEMORY_VADDR)
+#define virt_to_aram(v) (((unsigned long)v - IRAM_MEMORY_VADDR) + COMCERTO_AXI_IRAM_BASE)
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/pcie.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/pcie.h
new file mode 100644
index 0000000..019596a
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/pcie.h
@@ -0,0 +1,175 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/comcerto-2000/pcie.h
+ *
+ * Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __COMCERTO_PCIE_H__
+#define __COMCERTO_PCIE_H__
+
+/* CFG0 Register definitions */
+#define DWC_CFG0_DEV_TYPE_MASK 0x0F
+#define CFG0_DEV_TYPE_EP 0x00
+#define CFG0_DEV_TYPE_LEP 0x01
+#define CFG0_DEV_TYPE_RC 0x04
+#define CFG0_DEV_TYPE_UP_SW 0x05
+#define CFG0_DEV_TYPE_DWN_SW 0x06
+
+/* CFG1,2 Register definitions */
+/*ID definitions of ARMISC*/
+#define AXI_OP_TYPE_ID_MASK 0x0000001F
+#define AXI_OP_TYPE_ID_BIT 0
+#define AXI_OP_BCM_ID 0x00000020
+#define AXI_OP_EP_ID 0x00000040
+#define AXI_OP_TD_ID 0x00000080
+#define AXI_OP_ATTRIBUTE_ID_MASK 0x00000300
+#define AXI_OP_ATTRIBUTE_ID_BIT 8
+#define AXI_OP_TC_ID_MASK 0x00001C00
+#define AXI_OP_TC_ID_BIT 10
+#define AXI_OP_MSG_CODE_ID_MASK 0x001FE000
+#define AXI_OP_MSG_CODE_ID_BIT 13
+#define AXI_OP_DBI_ACCESS_ID 0x00200000
+#define AXI_OP_TYPE_MASK 0x1F
+#define AXI_OP_TYPE_MEM_RDRW 0
+#define AXI_OP_TYPE_MEM_RDRW_LOCKED 1
+#define AXI_OP_TYPE_IO_RDRW 2
+#define AXI_OP_TYPE_CONFIG_RDRW_TYPE0 4
+#define AXI_OP_TYPE_CONFIG_RDRW_TYPE1 5
+#define AXI_OP_TYPE_MSG_REQ 16
+#define AXI_OP_TYPE_COMPLETION 10
+#define AXI_OP_TYPE_COMPLETION_LOCKED 11
+#define AXI_OP_TYPE_DBI_ELBI_ENABLE 1
+
+
+
+/* CFG5 Register definitions */
+#define CFG5_APP_INIT_RST 0x01
+#define CFG5_LTSSM_ENABLE 0x02
+#define CFG5_APP_RDY_L23 0x04
+#define CFG5_LINK_DOWN_RST 0x200
+
+
+/* STS0 Register definitions */
+#define STS0_LINK_REQ_RST_NOT 0x0001
+#define STS0_XMLH_LINK_UP 0x8000
+#define STS0_RDLH_LINK_UP 0x10000
+
+/* INTR_STS and INTR_EN Register definitions */
+#define INTR_CTRL_INTA_ASSERT 0x0001
+#define INTR_CTRL_INTA_DEASSERT 0x0002
+#define INTR_CTRL_INTB_ASSERT 0x0004
+#define INTR_CTRL_INTB_DEASSERT 0x0008
+#define INTR_CTRL_INTC_ASSERT 0x0010
+#define INTR_CTRL_INTC_DEASSERT 0x0020
+#define INTR_CTRL_INTD_ASSERT 0x0040
+#define INTR_CTRL_INTD_DEASSERT 0x0080
+#define INTR_CTRL_AER 0x0100
+#define INTR_CTRL_PME 0x0200
+#define INTR_CTRL_HP 0x0400
+#define INTR_CTRL_LINK_AUTO_BW 0x0800
+#define INTR_CTRL_MSI 0x1000
+
+/* synopsis specific PCIE configuration registers*/
+
+/* Port Logic Registers */
+#define PCIE_ALRT_REG 0x700
+#define PCIE_AFL0L1_REG 0x70C
+#define PCIE_SYMNUM_REG 0x718
+#define PCIE_G2CTRL_REG 0x80C
+
+#define PCIE_CAP_BASE 0x70
+#define PCIE_LCAP_REG (PCIE_CAP_BASE + 0x0C)
+#define PCIE_LCNT2_REG (PCIE_CAP_BASE + 0x30)
+
+ /* MSI interface registers */
+#define PCIE_MSI_ADDR_LO 0x820 /* 32 bits */
+#define PCIE_MSI_ADDR_HI 0x824 /* 32 bits */
+#define PCIE_MSI_INTR0_ENABLE 0x828 /* 32 bits */
+#define PCIE_MSI_INTR0_MASK 0x82C /* 32 bits */
+#define PCIE_MSI_INTR0_STATUS 0x830 /* 32 bits */
+
+/* iATU interface registers */
+#define PCIE_iATU_VIEW_PORT 0x900 /* 32 bits */
+#define PCIE_iATU_CTRL1 0x904 /* 32 bits */
+#define PCIE_iATU_CTRL2 0x908 /* 32 bits */
+#define PCIE_iATU_SRC_LOW 0x90C /* 32 bits */
+#define PCIE_iATU_SRC_HIGH 0x910 /* 32 bits */
+#define PCIE_iATU_LIMIT 0x914 /* 32 bits */
+#define PCIE_iATU_TRGT_LOW 0x918 /* 32 bits */
+#define PCIE_iATU_TRGT_HIGH 0x91C /* 32 bits */
+
+/*******************************************************/
+/* BAR MASK Registers (uses dbi_cs2) */
+/*******************************************************/
+#define PCIE_BAR0_MASK_REG 0x100010
+
+/* iATU viewport register definitions */
+#define iATU_VIEW_PORT_ID_MASK 0x0F
+#define iATU_VIEW_PORT_ID_BIT 0
+#define iATU_VIEW_PORT_IN_BOUND 0X80000000
+
+/* iATU control1 register definitions */
+#define iATU_CTRL1_TYPE_MASK 0x001F
+#define iATU_CTRL1_TYPE_BIT 0
+#define iATU_CTRL1_TC_MASK 0x00E0
+#define iATU_CTRL1_TC_BIT 5
+#define iATU_CTRL1_TD 0x100
+
+
+/* iATU control2 register definitions */
+#define iATU_CTRL2_ID_EN 0x80000000
+#define iATU_CTRL2_IB_MEM_BAR_MATCH 0x40000000
+#define iATU_CTRL2_IB_CFG0_ACCEPT 0x40000000
+#define iATU_CTRL2_IB_MATCH_BAR0 0x00000000
+#define iATU_CTRL2_IB_MATCH_BAR1 0x00000100
+#define iATU_CTRL2_IB_MATCH_BAR2 0x00000200
+#define iATU_CTRL2_IB_MATCH_BAR3 0x00000300
+#define iATU_CTRL2_IB_MATCH_BAR4 0x00000400
+#define iATU_CTRL2_IB_MATCH_BAR5 0x00000500
+#define iATU_CTRL2_IB_MATCH_ROM 0x00000600
+
+#define iATU_ENTRY_MAX 8
+#define iATU_ENTRY_MEM 0
+#define iATU_ENTRY_IO 1
+#define iATU_ENTRY_CNF0 2
+#define iATU_ENTRY_CNF1 3
+#define iATU_ENTRY_MSG 4
+
+/** 254MB,s used for slave memory mapped address space remaining 2MB
+ * is used for IO, CFG0/1 and MSG transalation. This value cann't be
+ * increased.
+ */
+#define COMCERTO_PCIe_MEM_SIZE (254 * 1024 *1024)
+#define COMCERTO_PCIe_MSG_SIZE (1 * 1024 * 1024)
+#define COMCERTO_PCIe_IO_SIZE (64 * 1024)
+#define COMCERTO_PCIe_CFG0_SIZE (64 * 1024)
+#define COMCERTO_PCIe_CFG1_SIZE (64 * 1024)
+
+/* Sum of all these space can maximum be 256MB */
+#define iATU_MEM_SIZE (COMCERTO_PCIe_MEM_SIZE)
+#define iATU_MSG_SIZE (COMCERTO_PCIe_MSG_SIZE)
+#define iATU_IO_SIZE (COMCERTO_PCIe_IO_SIZE)
+#define iATU_CFG0_SIZE (COMCERTO_PCIe_CFG0_SIZE)
+#define iATU_CFG1_SIZE (COMCERTO_PCIe_CFG1_SIZE)
+
+#define iATU_GET_MEM_BASE( _base ) (_base + 0)
+#define iATU_GET_MSG_BASE( _base ) ( iATU_GET_MEM_BASE ( _base ) + iATU_MEM_SIZE)
+#define iATU_GET_IO_BASE( _base ) ( iATU_GET_MSG_BASE( _base ) + iATU_MSG_SIZE)
+#define iATU_GET_CFG0_BASE( _base ) ( iATU_GET_IO_BASE( _base ) + iATU_IO_SIZE)
+#define iATU_GET_CFG1_BASE( _base ) ( iATU_GET_CFG0_BASE( _base ) + iATU_CFG0_SIZE)
+
+#endif /* __COMCERTO_PCIE_H__ */
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/pm.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/pm.h
new file mode 100644
index 0000000..80f2102
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/pm.h
@@ -0,0 +1,72 @@
+/*
+ * pm.h
+ *
+ * Power managemen driver for Comcerto C2K device - internal header file
+ *
+ * This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#ifndef __ARCH_ARM_C2K_PM_H__
+#define __ARCH_ARM_C2K_PM_H__
+
+/* PMU Interrup masks */
+#define GPIO0_IRQ (1 << 0)
+#define GPIO1_IRQ (1 << 1)
+#define GPIO2_IRQ (1 << 2)
+#define GPIO3_IRQ (1 << 3)
+
+#define GPIO4_IRQ (1 << 4)
+#define GPIO5_IRQ (1 << 5)
+#define GPIO6_IRQ (1 << 6)
+#define GPIO7_IRQ (1 << 7)
+
+#define TIMER0_IRQ (1 << 8)
+#define TIMER1_IRQ (1 << 9)
+#define TIMER2_IRQ (1 << 10)
+#define TIMER3_IRQ (1 << 11)
+
+#define ZDS_MSIF_IRQ (1 << 12)
+#define RTC_ALM_IRQ (1 << 13)
+#define RTC_PRI_IRQ (1 << 14)
+#define PCIe0_IRQ (1 << 15)
+
+#define PCIe1_IRQ (1 << 16)
+#define SATA_IRQ (1 << 17)
+#define SATA_MSI_IRQ (1 << 18)
+#define USB2p0_IRQ (1 << 19)
+
+#define USB3p0_IRQ (1 << 20)
+#define HFE_0_IRQ (1 << 21)
+#define WOL_IRQ (1 << 22)
+#define CSS_IRQ (1 << 23)
+
+#define DUS_DMAC_IRQ (1 << 24)
+#define DUS_UART0_IRQ (1 << 25)
+#define DUS_UART0UARTS2_IRQ (1 << 26)
+#define HFE_1_IRQ (1 << 27)
+
+#define USB3p0_PM (1 << 28)
+#define PTP0_IRQ (1 << 29)
+#define PTP1_IRQ (1 << 30)
+#define PTP2_IRQ (1 << 31)
+
+#define JUMP_TO_RESUME_1 0xe3a00020 /* mov r0, #32 */
+#define JUMP_TO_RESUME_2 0xe590f000 /* ldr pc, [r0] */
+
+/*
+ * Two Bytes are used as shared memory between Host and UtilPE
+ * One for installing the Suspend Event and Return Resume location
+ * 2nd to pass the bitmask
+ */
+#define HOST_UTILPE_SHARED_ADDRESS_OFF 0x2400 /* This is offset into the iRAM */
+#define HOST_UTILPE_SHARED_ADDRESS (IRAM_MEMORY_VADDR+HOST_UTILPE_SHARED_ADDRESS_OFF)
+
+/* Global Variable for Shared Util-PE interrupt Mask */
+extern unsigned host_utilpe_shared_pmu_bitmask;
+
+void c2k_pm_bitmask_store(unsigned int);
+unsigned int c2k_pm_bitmask_show(void);
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/serdes.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/serdes.h
new file mode 100644
index 0000000..0fa0ffe
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/serdes.h
@@ -0,0 +1,61 @@
+
+/*
+ * Copyright (C) Mindspeed Technologies, Inc. 2011. All rights reserved.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * @file serdes.h
+ * @brief this header file will contain all required data structure
+ * and function definitions for Snowbush SerDes PHY interface.
+ * @date 10/02/2011
+ */
+
+#ifndef __COMCERTO_SERDES_H__
+#define __COMCERTO_SERDES_H__
+
+/* SER-DES Address space */
+#define SD_COMMON_CMU 0x000
+#define SD_LANE0 0x200
+#define SD_LANE1 0x400
+#define SD_LANE2 0x600
+#define SD_LANE3 0x800
+#define SD_COMMON_LANE 0xA00
+
+#define SD_DEV_TYPE_PCIE 0
+#define SD_DEV_TYPE_SATA 1
+#define SD_DEV_TYPE_SGMII 2
+
+
+#define COMCERTO_APB_SERDES0_BASE APB_VADDR(COMCERTO_APB_SERDES_CONF_BASE)
+#define COMCERTO_APB_SERDES1_BASE (COMCERTO_APB_SERDES0_BASE + 0x4000)
+#define COMCERTO_APB_SERDES2_BASE (COMCERTO_APB_SERDES0_BASE + 0x8000)
+
+#define COMCERTO_DWC1_CFG_BASE APB_VADDR(COMCERTO_APB_USBPHY_SERDES_STAT_BASE)
+#define COMCERTO_DWC1_SERDES_CFG_BASE COMCERTO_DWC1_CFG_BASE + 0x2C
+#define SD_PHY_STS_REG_OFST 0x00
+#define SD_PHY_CTRL1_REG_OFST 0x04
+#define SD_PHY_CTRL2_REG_OFST 0x08
+#define SD_PHY_CTRL3_REG_OFST 0x0C
+
+
+#define COMCERTO_SERDES_REG( _num, _ofst) ((COMCERTO_APB_SERDES0_BASE + (0x4000 * _num)) + _ofst)
+
+#define COMCERTO_SERDES_DWC_CFG_REG( _num, _ofst) ((COMCERTO_DWC1_SERDES_CFG_BASE + (_num <<4)) + _ofst)
+
+#endif //__COMCERTO_SERDES_H__
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/spi.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/spi.h
new file mode 100644
index 0000000..ce61974
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/spi.h
@@ -0,0 +1,71 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/comcerto1000/spi.h
+ *
+ * Copyright (C) 2004,2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_COMCERTO1000_SPI_H
+#define __ASM_ARCH_COMCERTO1000_SPI_H
+
+#define COMCERTO_SPI_FIFO_DEPTH 8
+
+/* SPI core registers */
+#define COMCERTO_SPI_CTRLR0 0x00
+#define COMCERTO_SPI_CTRLR1 0x04
+#define COMCERTO_SPI_SSIENR 0x08
+#define COMCERTO_SPI_MWCR 0x0C
+#define COMCERTO_SPI_SER 0x10
+#define COMCERTO_SPI_BAUDR 0x14
+#define COMCERTO_SPI_TXFTLR 0x18
+#define COMCERTO_SPI_RXFTLR 0x1C
+#define COMCERTO_SPI_TXFLR 0x20
+#define COMCERTO_SPI_RXFLR 0x24
+#define COMCERTO_SPI_SR 0x28
+#define COMCERTO_SPI_IMR 0x2C
+#define COMCERTO_SPI_ISR 0x30
+#define COMCERTO_SPI_RISR 0x34
+#define COMCERTO_SPI_TXOICR 0x38
+#define COMCERTO_SPI_RXOICR 0x3C
+#define COMCERTO_SPI_RXUICR 0x40
+#define COMCERTO_SPI_MSTICR 0x44
+#define COMCERTO_SPI_ICR 0x48
+#define COMCERTO_SPI_IDR 0x58
+#define COMCERTO_SPI_DR 0x60
+
+/* CTRLR0 - control register 0 bits/masks (incomplete) */
+#define SPI_CTRLR0_DFS_MASK (15<<0) /* Data frame size mask */
+#define SPI_CTRLR0_SCPOL (1<<7) /* Serial clock polarity */
+#define SPI_CTRLR0_SCPH (1<<6) /* Serial clock phase */
+#define SPI_CTRLR0_SRL (1<<11) /* Shift register loop */
+
+/* SR - status register bits */
+#define SPI_SR_BUSY (1<<0) /* SSI busy flag, serial transfer in progress */
+#define SPI_SR_TFNF (1<<1) /* Transmit FIFO not full */
+#define SPI_SR_TFE (1<<2) /* Transmit FIFO empty */
+#define SPI_SR_RFNE (1<<3) /* Receive FIFO not empty */
+#define SPI_SR_RFF (1<<4) /* Receive FIFO full */
+#define SPI_SR_TXE (1<<5) /* Transmission error */
+#define SPI_SR_DCOL (1<<6) /* Data collision error */
+
+/* IMR, ISR, RISR - interrupt mask/status bits */
+#define SPI_INT_TXEI (1<<0) /* Transmit FIFO empty interrupt status */
+#define SPI_INT_TXOI (1<<1) /* Transmit FIFO overflow interrupt status */
+#define SPI_INT_RXUI (1<<2) /* Receive FIFO underflow interrupt status */
+#define SPI_INT_RXOI (1<<3) /* Receive FIFO overflow interrupt status */
+#define SPI_INT_RXFI (1<<4) /* Receive FIFO full interrupt status */
+#define SPI_INT_MSTI (1<<5) /* Multi-Master contention interrupt status */
+
+#endif /* __ASM_ARCH_COMCERTO1000_SPI_H */
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/timer.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/timer.h
new file mode 100644
index 0000000..99396fe
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/timer.h
@@ -0,0 +1,106 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/comcerto-1000/timer.h
+ *
+ * Copyright (C) 2004-2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __TIMER_H__
+#define __TIMER_H__
+
+
+/* Comcerto Timers */
+#define COMCERTO_TIMER0_HIGH_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x00)
+#define COMCERTO_TIMER0_CURRENT_COUNT APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x04)
+#define COMCERTO_TIMER1_HIGH_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x08)
+#define COMCERTO_TIMER1_CURRENT_COUNT APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x0C)
+#define COMCERTO_TIMER2_LOW_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x10)
+#define COMCERTO_TIMER2_HIGH_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x14)
+#define COMCERTO_TIMER2_CTRL APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x18)
+#define COMCERTO_TIMER2_CURRENT_COUNT APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x1C)
+#define COMCERTO_TIMER3_LOW_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x20)
+#define COMCERTO_TIMER3_HIGH_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x24)
+#define COMCERTO_TIMER3_CTRL APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x28)
+#define COMCERTO_TIMER3_CURRENT_COUNT APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x2C)
+#define COMCERTO_TIMER4_HIGH_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x30)
+#define COMCERTO_TIMER4_CURRENT_COUNT APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x34)
+#define COMCERTO_TIMER5_LOW_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x38)
+#define COMCERTO_TIMER5_HIGH_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x3C)
+#define COMCERTO_TIMER5_CURRENT_COUNT APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x40)
+#define COMCERTO_TIMER5_CTRL APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x44)
+#define COMCERTO_TIMER_IRQ_MASK APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x48)
+#define COMCERTO_TIMER_STATUS APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x50)
+#define COMCERTO_TIMER_STATUS_CLR APB_VADDR(COMCERTO_APB_TIMER_BASE + 0x50)
+#define COMCERTO_TIMER_WDT_HIGH_BOUND APB_VADDR(COMCERTO_APB_TIMER_BASE + 0xD0)
+#define COMCERTO_TIMER_WDT_CONTROL APB_VADDR(COMCERTO_APB_TIMER_BASE + 0xD4)
+#define COMCERTO_TIMER_WDT_CURRENT_COUNT APB_VADDR(COMCERTO_APB_TIMER_BASE + 0xD8)
+
+#define COMCERTO_TIMER_WDT_CONTROL_TIMER_ENABLE (1 << 0)
+
+/*COMCERTO_TIMER_IRQ_MASK*/
+#define COMCERTO_TIMER0 0x01
+#define COMCERTO_TIMER1 0x02
+#define COMCERTO_TIMER2 0x04
+#define COMCERTO_TIMER3 0x08
+#define COMCERTO_TIMER4 0x10
+#define COMCERTO_TIMER5 0x20
+#define COMCERTO_ALL 0xFF
+#define COMCERTO_TIMER_CSP (COMCERTO_TIMER1 | COMCERTO_TIMER2 | COMCERTO_TIMER3 | COMCERTO_TIMER4 | COMCERTO_TIMER5)
+
+/*
+ * TIMERS
+ */
+
+
+/*Hardware Timer API*/
+#define COMCERTO_TIMER_RUN_ONCE (1 << 0)
+#define __comcerto_timer_enable(t) __raw_writel(__raw_readl(COMCERTO_TIMER_IRQ_MASK) | (1 << (t)), COMCERTO_TIMER_IRQ_MASK)
+#define __comcerto_timer_disable(t) __raw_writel(__raw_readl(COMCERTO_TIMER_IRQ_MASK) & ~(1 << (t)), COMCERTO_TIMER_IRQ_MASK)
+#define comcerto_timer_ack(t) __raw_writel(1 << (t), COMCERTO_TIMER_STATUS_CLR)
+
+#define comcerto_timer0_set(hbound) __raw_writel((hbound), COMCERTO_TIMER0_HIGH_BOUND)
+#define comcerto_timer0_get() __raw_readl(COMCERTO_TIMER0_CURRENT_COUNT)
+
+#define comcerto_timer1_set(hbound) __raw_writel((hbound) & 0x3FFFFFFF, COMCERTO_TIMER1_HIGH_BOUND)
+#define comcerto_timer1_get() __raw_readl(COMCERTO_TIMER1_CURRENT_COUNT)
+
+#define comcerto_timer2_set(lbound, hbound, ctrl) do {\
+ __raw_writel((ctrl) & 0x1, COMCERTO_TIMER2_CTRL); \
+ __raw_writel((lbound), COMCERTO_TIMER2_LOW_BOUND); \
+ __raw_writel((hbound), COMCERTO_TIMER2_HIGH_BOUND); \
+ } while (0)
+
+#define comcerto_timer2_get() __raw_readl(COMCERTO_TIMER2_CURRENT_COUNT)
+
+
+#define comcerto_timer3_set(lbound, hbound, ctrl) do { \
+ __raw_writel((ctrl) & 0x1, COMCERTO_TIMER3_CTRL); \
+ __raw_writel((lbound), COMCERTO_TIMER3_LOW_BOUND); \
+ __raw_writel((hbound), COMCERTO_TIMER3_HIGH_BOUND); \
+ } while(0)
+
+#define comcerto_timer3_get() __raw_readl(COMCERTO_TIMER3_CURRENT_COUNT)
+
+#ifndef __ASSEMBLY__
+struct comcerto_timer {
+ unsigned long timeout;
+ void (*func) (unsigned long data);
+ unsigned long data;
+ unsigned char flags;
+ unsigned long thw;
+};
+#endif
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-2000/wdt.h b/arch/arm/mach-comcerto/include/mach/comcerto-2000/wdt.h
new file mode 100644
index 0000000..6b78dbd
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-2000/wdt.h
@@ -0,0 +1,24 @@
+/*
+ * linux/include/asm/arch/comcerto1000/wdt.h
+ *
+ * Copyright (C) 2004,2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_COMCERTO1000_WDT_H
+#define __ASM_ARCH_COMCERTO1000_WDT_H
+
+
+#endif /* __ASM_ARCH_COMCERTO1000_WDT_H */
diff --git a/arch/arm/mach-comcerto/include/mach/comcerto-common.h b/arch/arm/mach-comcerto/include/mach/comcerto-common.h
new file mode 100644
index 0000000..6a14993
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/comcerto-common.h
@@ -0,0 +1,171 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/comcerto-common.h
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#error "Do not include this directly, instead #include <asm/hardware.h>"
+#endif
+
+#ifndef __ASM_COMCERTO_COMMON_H__
+#define __ASM_COMCERTO_COMMON_H__
+
+#include <asm/types.h>
+
+#define APB_VADDR(x) ((x) - COMCERTO_AXI_APB_BASE + COMCERTO_APB_VADDR) /* macro to get virtual address of IO on APB Bus from Physical address*/
+#define AXI_VADDR(x) ((x) - COMCERTO_AXI_SPI_BASE + COMCERTO_AXI_SSI_VADDR) /* macro to get virtual address of IO on AXI Bus from Physical address*/
+
+#define BIT_0_MSK 0x00000001
+#define BIT_1_MSK 0x00000002
+#define BIT_2_MSK 0x00000004
+#define BIT_3_MSK 0x00000008
+#define BIT_4_MSK 0x00000010
+#define BIT_5_MSK 0x00000020
+#define BIT_6_MSK 0x00000040
+#define BIT_7_MSK 0x00000080
+#define BIT_8_MSK 0x00000100
+#define BIT_9_MSK 0x00000200
+#define BIT_10_MSK 0x00000400
+#define BIT_11_MSK 0x00000800
+#define BIT_12_MSK 0x00001000
+#define BIT_13_MSK 0x00002000
+#define BIT_14_MSK 0x00004000
+#define BIT_15_MSK 0x00008000
+#define BIT_16_MSK 0x00010000
+#define BIT_17_MSK 0x00020000
+#define BIT_18_MSK 0x00040000
+#define BIT_19_MSK 0x00080000
+#define BIT_20_MSK 0x00100000
+#define BIT_21_MSK 0x00200000
+#define BIT_22_MSK 0x00400000
+#define BIT_23_MSK 0x00800000
+#define BIT_24_MSK 0x01000000
+#define BIT_25_MSK 0x02000000
+#define BIT_26_MSK 0x04000000
+#define BIT_27_MSK 0x08000000
+#define BIT_28_MSK 0x10000000
+#define BIT_29_MSK 0x20000000
+#define BIT_30_MSK 0x40000000
+#define BIT_31_MSK 0x80000000
+
+/*
+ * GPIO
+ */
+/* Set gpio pins specified by gpiomask to be outputs */
+#define comcerto_gpio_enable_output(gpiomask) __raw_writel(__raw_readl(COMCERTO_GPIO_OE_REG) | (gpiomask), COMCERTO_GPIO_OE_REG)
+
+/* Set output pins specified by gpiomask to low */
+#define comcerto_gpio_set_0(gpiomask) __raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) & ~(gpiomask), COMCERTO_GPIO_OUTPUT_REG)
+
+/* Set output pins specified by gpiomask to high */
+#define comcerto_gpio_set_1(gpiomask) __raw_writel(__raw_readl(COMCERTO_GPIO_OUTPUT_REG) | (gpiomask), COMCERTO_GPIO_OUTPUT_REG)
+
+/* Read status of input pins specified by gpiomask */
+#define comcerto_gpio_read(gpiomask) (__raw_readl(COMCERTO_GPIO_INPUT_REG) & (gpiomask))
+
+
+#ifndef __ASSEMBLY__
+#define CONFIG_COMCERTO_GEMAC 1
+
+#define CONFIG_COMCERTO_USE_MII 1
+#define CONFIG_COMCERTO_USE_RMII 2
+#define CONFIG_COMCERTO_USE_GMII 4
+#define CONFIG_COMCERTO_USE_RGMII 8
+#define CONFIG_COMCERTO_USE_SGMII 0x10
+
+#define GEMAC_SW_CONF (1 << 8) | (1 << 11) // GEMAC configured by SW
+#define GEMAC_PHY_CONF 0 // GEMAC configured by phy lines (not for MII/GMII)
+#define GEMAC_SW_FULL_DUPLEX (1 << 9)
+#define GEMAC_SW_SPEED_10M (0 << 12)
+#define GEMAC_SW_SPEED_100M (1 << 12)
+#define GEMAC_SW_SPEED_1G (2 << 12)
+
+#define GEMAC_NO_PHY (1 << 0) // set if no phy connected to MAC (ex ethernet switch). In this case use MAC fixed configuration
+#define GEMAC_PHY_RGMII_ADD_DELAY (1 << 1)
+
+/* gemac to interface name assignment */
+#define GEM0_ITF_NAME "eth0"
+#define GEM1_ITF_NAME "eth2"
+#define GEM2_ITF_NAME "eth3"
+
+#define GEM0_MAC { 0x00, 0xED, 0xCD, 0xEF, 0xAA, 0xCC }
+#define GEM1_MAC { 0x00, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E }
+#define GEM2_MAC { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }
+
+struct comcerto_eth_platform_data {
+ /* device specific information */
+ u32 device_flags;
+ char name[16];
+
+
+ /* board specific information */
+ u32 mii_config;
+ u32 gemac_mode;
+ u32 phy_flags;
+ u32 gem_id;
+ u32 bus_id;
+ u32 phy_id;
+ u8 *mac_addr;
+};
+
+struct comcerto_mdio_platform_data {
+ int enabled;
+ int irq[32];
+ u32 phy_mask;
+ int mdc_div;
+};
+
+struct comcerto_pfe_platform_data
+{
+ struct comcerto_eth_platform_data comcerto_eth_pdata[3];
+ struct comcerto_mdio_platform_data comcerto_mdio_pdata[3];
+};
+
+struct comcerto_tdm_data {
+ u8 fsoutput; /* Generic Pad Control and Version ID Register[2] */
+ u8 fspolarity; /* 28 FSYNC_FALL(RISE)_EDGE */
+ u16 fshwidth; /* High_Phase_Width[10:0] */
+ u16 fslwidth; /* Low_Phase_Width[26:16]] */
+ u32 clockhz; /* INC_VALUE[29:0] According to the desired TDM clock output frequency, this field should be configured */
+ u8 clockout; /* IO Control Register[21] hardware or software control selection IO Control Register[20] pads are input (output) */
+ u8 tdmmux;
+#if 0
+ u32 tdmck;
+ u32 tdmfs;
+ u32 tdmdx;
+ u32 tdmdr;
+#endif
+};
+
+/* L210 cache controller value
+ // Configure Aux:
+ // [11:9]=[8:6]=[2:0]=001 RAM LAT = 2 cycles
+ // [5:3]=000 Data write latency is 1
+ // [12]=0 WRAP access is enabled
+ // [16:13]=1000 8-way cache
+ // [19:17]=001 16KB way
+ // [20]=1 Event bus is enabled
+ // [21]=1 Parity is enabled
+ // [22]=0 Shared accesses treated as noncacheable
+ // [23]=0 HPROT is used
+ // [24]=1 Abort generation of exclusive access disabled
+*/
+#define L210_AUX_CTRL_REG 0x01330241
+
+#endif
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/debug-macro.S b/arch/arm/mach-comcerto/include/mach/debug-macro.S
new file mode 100644
index 0000000..c1efb9b
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/debug-macro.S
@@ -0,0 +1,53 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/debug-macro.S
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define UART_LSR 0x14
+
+
+ .macro addruart, rx, rv, tmp
+ moveq \rx, #0x96000000 @ physical base address
+ @movne \rv, #0xf1600000 @ virtual base
+ #ifdef CONFIG_COMCERTO_UART1_SUPPORT
+ orr \rx, \rx, #0x00400000
+ orr \rv, \rv, #0x00400000
+ #elif CONFIG_COMCERTO_UART0_SUPPORT
+ orr \rx, \rx, #0x00300000
+ orr \rv, \rv, #0x00300000
+ #else
+ #error no uart defined
+ #endif
+ .endm
+
+ .macro senduart,rd,rx
+ strb \rd, [\rx, #0x00]
+ .endm
+
+ .macro waituart,rd,rx
+1001: ldrb \rd, [\rx, #UART_LSR]
+ tst \rd, #0x20 @ wait for THRE
+ beq 1001b
+ .endm
+
+ .macro busyuart,rd,rx
+1001: ldrb \rd, [\rx, #UART_LSR]
+ and \rd, \rd, #0x60
+ teq \rd, #0x60 @ wait for TEMT and THRE
+ bne 1001b
+ .endm
diff --git a/arch/arm/mach-comcerto/include/mach/debug.h b/arch/arm/mach-comcerto/include/mach/debug.h
new file mode 100644
index 0000000..0cc2eae
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/debug.h
@@ -0,0 +1,66 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/debug.h
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#include <linux/kernel.h>
+
+/* debug messages: to disable comment the next line */
+#define DEBUG_MSG
+
+/* several debug levels: 1 enables, 0 disables */
+
+#define DEBUG_ALWAYS 1
+
+/* prints general information */
+#define DEBUG_INFO 1
+
+/* prints timing information */
+#define DEBUG_TIMING 0
+
+#define DEBUG_DISPLAY 1
+
+#define VED_INIT_FUNC 1
+#define VED_RX_FUNC 0
+#define VED_TX_FUNC 0
+#define VED_STATE 0
+#define SMI_PART 0
+
+#define SKB_POOL_ERR 1
+#define SKB_POOL_INIT 0
+#define SKB_POOL_FUNC 0
+
+#define MSP_ERR 1
+#define MSP_INIT 0
+#define MSP_FUNC 0
+
+
+/* add other debug messages types here */
+
+/* the debug macro */
+#ifdef DEBUG_MSG
+#define info(fmt, args...) printk(KERN_INFO __FILE__ ": " fmt "\n" , ## args)
+#define PDEBUG(type, fmt, args...) do {if(type) info ("%d: " fmt, __LINE__ , ## args);} while(0)
+#else
+#define PDEBUG(type, fmt, args...) do{} while(0)
+#endif
+
+#endif /* _DEBUG_H */
diff --git a/arch/arm/mach-comcerto/include/mach/dma.h b/arch/arm/mach-comcerto/include/mach/dma.h
new file mode 100644
index 0000000..cc08f34
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/dma.h
@@ -0,0 +1,552 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/dma.h
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_DMA_H
+#define __ASM_ARCH_DMA_H
+
+
+
+typedef long I32;
+typedef char I8;
+typedef unsigned char U8;
+typedef unsigned short U16;
+typedef unsigned long U32;
+typedef volatile unsigned char V8;
+typedef volatile unsigned short V16;
+typedef volatile unsigned long V32;
+typedef unsigned int BOOL;
+typedef U32 (*CKSUMFUNC)(void);
+typedef U32 (*PLUGINPROC)(void * );
+
+typedef enum _retcode
+{
+ RETCODE_OK = 0,
+ RETCODE_ERROR,
+ RETCODE_BAD_PARAMETER,
+ RETCODE_MISMATCH, /* Security violation. */
+ RETCODE_INVALID_FUSE_SETTING, /* Security violation. */
+ RETCODE_INVALID_IMAGE_HEADER, /* Security violation. */
+ RETCODE_RSA_ERROR, /* Security Violation. */
+ RETCODE_NO_VALID_COPY,
+ RETCODE_HDCP_KEY_LOAD_FAILED,
+ RETCODE_FUSE_PROGRAM_FAILED,
+ RETCODE_OTP_ERR,
+ RETCODE_FAILED,
+ RETCODE_BOOTDEV_UART,
+ RETCODE_BOOTDEV_FLASH,
+ RETCODE_BOOTDEV_INVALID,
+ RETCODE_FLASH_DEVICE_NOT_SUPPORTED,
+ RETCODE_FLASH_BAD_BLOCK,
+ RETCODE_FLASH_UNCORRECTABLE_ERROR,
+ RETCODE_UARTBOOT_ERROR,
+ RETCODE_NOT_SUPPORTED,
+ RETCODE_INVALID_BOOT_DEV,
+ RETCODE_SECURE_BOOT_ENABLE,
+ RETCODE_SECURE_BOOT_DISABLE,
+ RETCODE_YMODEM_HDR_ERROR,
+ RETCODE_YMODEM_PKT_ERROR,
+ RETCODE_YMODEM_DONE_ERROR,
+ RETCODE_PAD_ERROR,
+ RETCODE_ECC_ERR_CORRECTED,
+ RETCODE_ECC_ERR_UNCORRECTABLE,
+ RETCODE_ECC_DATA_ERROR,
+ RETCODE_NAND_BBT_OUT_OF_BOUND
+} retcode;
+
+
+
+/*
+ * DW DMA (HS SPI and UART) Block
+ */
+
+#define DW_DMA_DMAC_BASEADDR (COMCERTO_AXI_UART_SPI_BASE+0x00000000)
+#define DW_DMA_UART0_BASEADDR (COMCERTO_AXI_UART_SPI_BASE+0x00300000)
+#define DW_DMA_UART1_BASEADDR (COMCERTO_AXI_UART_SPI_BASE+0x00400000)
+#define DW_DMA_SSI_BASEADDR (COMCERTO_AXI_UART_SPI_BASE+0x00500000)
+
+
+/*
+ * DW DMA DMAC
+ */
+
+/* Channel Registers */
+#define DW_DMA_DMAC_SAR0 (DW_DMA_DMAC_BASEADDR+0x000)
+#define DW_DMA_DMAC_DAR0 (DW_DMA_DMAC_BASEADDR+0x008)
+#define DW_DMA_DMAC_LLP0 (DW_DMA_DMAC_BASEADDR+0x010)
+#define DW_DMA_DMAC_CTL0 (DW_DMA_DMAC_BASEADDR+0x018)
+#define DW_DMA_DMAC_SSTAT0 (DW_DMA_DMAC_BASEADDR+0x020)
+#define DW_DMA_DMAC_DSTAT0 (DW_DMA_DMAC_BASEADDR+0x028)
+#define DW_DMA_DMAC_SSTATAR0 (DW_DMA_DMAC_BASEADDR+0x030)
+#define DW_DMA_DMAC_DSTATAR0 (DW_DMA_DMAC_BASEADDR+0x038)
+#define DW_DMA_DMAC_CFG0 (DW_DMA_DMAC_BASEADDR+0x040)
+#define DW_DMA_DMAC_SGR0 (DW_DMA_DMAC_BASEADDR+0x048)
+#define DW_DMA_DMAC_DSR0 (DW_DMA_DMAC_BASEADDR+0x050)
+
+#define DW_DMA_DMAC_SAR1 (DW_DMA_DMAC_BASEADDR+0x058)
+#define DW_DMA_DMAC_DAR1 (DW_DMA_DMAC_BASEADDR+0x060)
+#define DW_DMA_DMAC_LLP1 (DW_DMA_DMAC_BASEADDR+0x068)
+#define DW_DMA_DMAC_CTL1 (DW_DMA_DMAC_BASEADDR+0x070)
+#define DW_DMA_DMAC_SSTAT1 (DW_DMA_DMAC_BASEADDR+0x078)
+#define DW_DMA_DMAC_DSTAT1 (DW_DMA_DMAC_BASEADDR+0x080)
+#define DW_DMA_DMAC_SSTATAR1 (DW_DMA_DMAC_BASEADDR+0x088)
+#define DW_DMA_DMAC_DSTATAR1 (DW_DMA_DMAC_BASEADDR+0x090)
+#define DW_DMA_DMAC_CFG1 (DW_DMA_DMAC_BASEADDR+0x098)
+#define DW_DMA_DMAC_SGR1 (DW_DMA_DMAC_BASEADDR+0x0A0)
+#define DW_DMA_DMAC_DSR1 (DW_DMA_DMAC_BASEADDR+0x0A8)
+
+#define DW_DMA_DMAC_SAR2 (DW_DMA_DMAC_BASEADDR+0x0B0)
+#define DW_DMA_DMAC_DAR2 (DW_DMA_DMAC_BASEADDR+0x0B8)
+#define DW_DMA_DMAC_LLP2 (DW_DMA_DMAC_BASEADDR+0x0C0)
+#define DW_DMA_DMAC_CTL2 (DW_DMA_DMAC_BASEADDR+0x0C8)
+#define DW_DMA_DMAC_SSTAT2 (DW_DMA_DMAC_BASEADDR+0x0D0)
+#define DW_DMA_DMAC_DSTAT2 (DW_DMA_DMAC_BASEADDR+0x0D8)
+#define DW_DMA_DMAC_SSTATAR2 (DW_DMA_DMAC_BASEADDR+0x0E0)
+#define DW_DMA_DMAC_DSTATAR2 (DW_DMA_DMAC_BASEADDR+0x0E8)
+#define DW_DMA_DMAC_CFG2 (DW_DMA_DMAC_BASEADDR+0x0F0)
+#define DW_DMA_DMAC_SGR2 (DW_DMA_DMAC_BASEADDR+0x0F8)
+#define DW_DMA_DMAC_DSR2 (DW_DMA_DMAC_BASEADDR+0x100)
+
+#define DW_DMA_DMAC_SAR3 (DW_DMA_DMAC_BASEADDR+0x108)
+#define DW_DMA_DMAC_DAR3 (DW_DMA_DMAC_BASEADDR+0x110)
+#define DW_DMA_DMAC_LLP3 (DW_DMA_DMAC_BASEADDR+0x118)
+#define DW_DMA_DMAC_CTL3 (DW_DMA_DMAC_BASEADDR+0x120)
+#define DW_DMA_DMAC_SSTAT3 (DW_DMA_DMAC_BASEADDR+0x128)
+#define DW_DMA_DMAC_DSTAT3 (DW_DMA_DMAC_BASEADDR+0x130)
+#define DW_DMA_DMAC_SSTATAR3 (DW_DMA_DMAC_BASEADDR+0x138)
+#define DW_DMA_DMAC_DSTATAR3 (DW_DMA_DMAC_BASEADDR+0x140)
+#define DW_DMA_DMAC_CFG3 (DW_DMA_DMAC_BASEADDR+0x148)
+#define DW_DMA_DMAC_SGR3 (DW_DMA_DMAC_BASEADDR+0x150)
+#define DW_DMA_DMAC_DSR3 (DW_DMA_DMAC_BASEADDR+0x158)
+
+#define DW_DMA_DMAC_SAR4 (DW_DMA_DMAC_BASEADDR+0x160)
+#define DW_DMA_DMAC_DAR4 (DW_DMA_DMAC_BASEADDR+0x168)
+#define DW_DMA_DMAC_LLP4 (DW_DMA_DMAC_BASEADDR+0x170)
+#define DW_DMA_DMAC_CTL4 (DW_DMA_DMAC_BASEADDR+0x178)
+#define DW_DMA_DMAC_SSTAT4 (DW_DMA_DMAC_BASEADDR+0x180)
+#define DW_DMA_DMAC_DSTAT4 (DW_DMA_DMAC_BASEADDR+0x188)
+#define DW_DMA_DMAC_SSTATAR4 (DW_DMA_DMAC_BASEADDR+0x190)
+#define DW_DMA_DMAC_DSTATAR4 (DW_DMA_DMAC_BASEADDR+0x198)
+#define DW_DMA_DMAC_CFG4 (DW_DMA_DMAC_BASEADDR+0x1A0)
+#define DW_DMA_DMAC_SGR4 (DW_DMA_DMAC_BASEADDR+0x1A8)
+#define DW_DMA_DMAC_DSR4 (DW_DMA_DMAC_BASEADDR+0x1B0)
+
+#define DW_DMA_DMAC_SAR5 (DW_DMA_DMAC_BASEADDR+0x1B8)
+#define DW_DMA_DMAC_DAR5 (DW_DMA_DMAC_BASEADDR+0x1C0)
+#define DW_DMA_DMAC_LLP5 (DW_DMA_DMAC_BASEADDR+0x1C8)
+#define DW_DMA_DMAC_CTL5 (DW_DMA_DMAC_BASEADDR+0x1D0)
+#define DW_DMA_DMAC_SSTAT5 (DW_DMA_DMAC_BASEADDR+0x1D8)
+#define DW_DMA_DMAC_DSTAT5 (DW_DMA_DMAC_BASEADDR+0x1E0)
+#define DW_DMA_DMAC_SSTATAR5 (DW_DMA_DMAC_BASEADDR+0x1E8)
+#define DW_DMA_DMAC_DSTATAR5 (DW_DMA_DMAC_BASEADDR+0x1F0)
+#define DW_DMA_DMAC_CFG5 (DW_DMA_DMAC_BASEADDR+0x1F8)
+#define DW_DMA_DMAC_SGR5 (DW_DMA_DMAC_BASEADDR+0x200)
+#define DW_DMA_DMAC_DSR5 (DW_DMA_DMAC_BASEADDR+0x208)
+
+#define DW_DMA_DMAC_SAR6 (DW_DMA_DMAC_BASEADDR+0x210)
+#define DW_DMA_DMAC_DAR6 (DW_DMA_DMAC_BASEADDR+0x218)
+#define DW_DMA_DMAC_LLP6 (DW_DMA_DMAC_BASEADDR+0x220)
+#define DW_DMA_DMAC_CTL6 (DW_DMA_DMAC_BASEADDR+0x228)
+#define DW_DMA_DMAC_SSTAT6 (DW_DMA_DMAC_BASEADDR+0x230)
+#define DW_DMA_DMAC_DSTAT6 (DW_DMA_DMAC_BASEADDR+0x238)
+#define DW_DMA_DMAC_SSTATAR6 (DW_DMA_DMAC_BASEADDR+0x240)
+#define DW_DMA_DMAC_DSTATAR6 (DW_DMA_DMAC_BASEADDR+0x248)
+#define DW_DMA_DMAC_CFG6 (DW_DMA_DMAC_BASEADDR+0x250)
+#define DW_DMA_DMAC_SGR6 (DW_DMA_DMAC_BASEADDR+0x258)
+#define DW_DMA_DMAC_DSR6 (DW_DMA_DMAC_BASEADDR+0x260)
+
+#define DW_DMA_DMAC_SAR7 (DW_DMA_DMAC_BASEADDR+0x268)
+#define DW_DMA_DMAC_DAR7 (DW_DMA_DMAC_BASEADDR+0x270)
+#define DW_DMA_DMAC_LLP7 (DW_DMA_DMAC_BASEADDR+0x278)
+#define DW_DMA_DMAC_CTL7 (DW_DMA_DMAC_BASEADDR+0x280)
+#define DW_DMA_DMAC_SSTAT7 (DW_DMA_DMAC_BASEADDR+0x288)
+#define DW_DMA_DMAC_DSTAT7 (DW_DMA_DMAC_BASEADDR+0x290)
+#define DW_DMA_DMAC_SSTATAR7 (DW_DMA_DMAC_BASEADDR+0x298)
+#define DW_DMA_DMAC_DSTATAR7 (DW_DMA_DMAC_BASEADDR+0x2A0)
+#define DW_DMA_DMAC_CFG7 (DW_DMA_DMAC_BASEADDR+0x2A8)
+#define DW_DMA_DMAC_SGR7 (DW_DMA_DMAC_BASEADDR+0x2B0)
+#define DW_DMA_DMAC_DSR7 (DW_DMA_DMAC_BASEADDR+0x2B8)
+
+/* Interrupt Registers */
+#define DW_DMA_DMAC_RAW_TFR (DW_DMA_DMAC_BASEADDR+0x2C0)
+#define DW_DMA_DMAC_RAW_BLOCK (DW_DMA_DMAC_BASEADDR+0x2C8)
+#define DW_DMA_DMAC_RAW_SRC_TRAN (DW_DMA_DMAC_BASEADDR+0x2D0)
+#define DW_DMA_DMAC_RAW_DST_TRAN (DW_DMA_DMAC_BASEADDR+0x2D8)
+#define DW_DMA_DMAC_RAW_ERR (DW_DMA_DMAC_BASEADDR+0x2E0)
+#define DW_DMA_DMAC_STATUS_TFR (DW_DMA_DMAC_BASEADDR+0x2E8)
+#define DW_DMA_DMAC_STATUS_BLK (DW_DMA_DMAC_BASEADDR+0x2F0)
+#define DW_DMA_DMAC_STATUS_SRC_TRAN (DW_DMA_DMAC_BASEADDR+0x2F8)
+#define DW_DMA_DMAC_STATUS_DST_TRAN (DW_DMA_DMAC_BASEADDR+0x300)
+#define DW_DMA_DMAC_STATUS_ERR (DW_DMA_DMAC_BASEADDR+0x308)
+#define DW_DMA_DMAC_MASK_TFR (DW_DMA_DMAC_BASEADDR+0x310)
+#define DW_DMA_DMAC_MASK_BLOCK (DW_DMA_DMAC_BASEADDR+0x318)
+#define DW_DMA_DMAC_MASK_SRC_TRAN (DW_DMA_DMAC_BASEADDR+0x320)
+#define DW_DMA_DMAC_MASK_DST_TRAN (DW_DMA_DMAC_BASEADDR+0x328)
+#define DW_DMA_DMAC_MASK_ERR (DW_DMA_DMAC_BASEADDR+0x330)
+#define DW_DMA_DMAC_CLEAR_TFR (DW_DMA_DMAC_BASEADDR+0x338)
+#define DW_DMA_DMAC_CLEAR_BLK (DW_DMA_DMAC_BASEADDR+0x340)
+#define DW_DMA_DMAC_CLEAR_SRC_TRAN (DW_DMA_DMAC_BASEADDR+0x348)
+#define DW_DMA_DMAC_CLEAR_DST_TRAN (DW_DMA_DMAC_BASEADDR+0x350)
+#define DW_DMA_DMAC_CLEAR_ERR (DW_DMA_DMAC_BASEADDR+0x358)
+#define DW_DMA_DMAC_STATUS_INT (DW_DMA_DMAC_BASEADDR+0x360)
+
+/* Software Handshaking Registers */
+#define DW_DMA_DMAC_REQ_SRC_REG (DW_DMA_DMAC_BASEADDR+0x368)
+#define DW_DMA_DMAC_REQ_DST_REG (DW_DMA_DMAC_BASEADDR+0x370)
+#define DW_DMA_DMAC_SGL_REQ_SRC_REG (DW_DMA_DMAC_BASEADDR+0x378)
+#define DW_DMA_DMAC_SGL_REQ_DST_REG (DW_DMA_DMAC_BASEADDR+0x380)
+#define DW_DMA_DMAC_LST_SRC_REG (DW_DMA_DMAC_BASEADDR+0x388)
+#define DW_DMA_DMAC_LST_DST_REG (DW_DMA_DMAC_BASEADDR+0x390)
+
+/* Miscellaneous Registers */
+#define DW_DMA_DMAC_DMA_CFG_REG (DW_DMA_DMAC_BASEADDR+0x398)
+#define DW_DMA_DMAC_CH_EN_REG (DW_DMA_DMAC_BASEADDR+0x3A0)
+#define DW_DMA_DMAC_DMA_LD_REG (DW_DMA_DMAC_BASEADDR+0x3A8)
+#define DW_DMA_DMAC_DMA_TEST_REG (DW_DMA_DMAC_BASEADDR+0x3B0)
+#define DW_DMA_DMAC_DMA_COMP_PA_RAMS_6 (DW_DMA_DMAC_BASEADDR+0x3C8)
+#define DW_DMA_DMAC_DMA_COMP_PA_RAMS_5 (DW_DMA_DMAC_BASEADDR+0x3D0)
+#define DW_DMA_DMAC_DMA_COMP_PA_RAMS_4 (DW_DMA_DMAC_BASEADDR+0x3D8)
+#define DW_DMA_DMAC_DMA_COMP_PA_RAMS_3 (DW_DMA_DMAC_BASEADDR+0x3E0)
+#define DW_DMA_DMAC_DMA_COMP_PA_RAMS_2 (DW_DMA_DMAC_BASEADDR+0x3E8)
+#define DW_DMA_DMAC_DMA_COMP_PA_RAMS_1 (DW_DMA_DMAC_BASEADDR+0x3F0)
+#define DW_DMA_DMAC_DMA_COMP_ID_REG (DW_DMA_DMAC_BASEADDR+0x3F8)
+
+
+
+/*
+ * DW DMA UART0
+ */
+
+#define DW_DMA_UART0_RBR (DW_DMA_UART0_BASEADDR+0x00)
+#define DW_DMA_UART0_THR (DW_DMA_UART0_BASEADDR+0x00)
+#define DW_DMA_UART0_DLL (DW_DMA_UART0_BASEADDR+0x00)
+#define DW_DMA_UART0_DLH (DW_DMA_UART0_BASEADDR+0x04)
+#define DW_DMA_UART0_IER (DW_DMA_UART0_BASEADDR+0x04)
+#define DW_DMA_UART0_IIR (DW_DMA_UART0_BASEADDR+0x08)
+#define DW_DMA_UART0_FCR (DW_DMA_UART0_BASEADDR+0x08)
+#define DW_DMA_UART0_LCR (DW_DMA_UART0_BASEADDR+0x0C)
+#define DW_DMA_UART0_MCR (DW_DMA_UART0_BASEADDR+0x10)
+#define DW_DMA_UART0_LSR (DW_DMA_UART0_BASEADDR+0x14)
+#define DW_DMA_UART0_MSR (DW_DMA_UART0_BASEADDR+0x18)
+#define DW_DMA_UART0_SCR (DW_DMA_UART0_BASEADDR+0x1C)
+#define DW_DMA_UART0_LPDLL (DW_DMA_UART0_BASEADDR+0x20)
+#define DW_DMA_UART0_LPDLH (DW_DMA_UART0_BASEADDR+0x24)
+#define DW_DMA_UART0_SRBR (DW_DMA_UART0_BASEADDR+0x30) /* Shadow Receive Buffer Register (0x30 to 0x6C) */
+#define DW_DMA_UART0_STHR (DW_DMA_UART0_BASEADDR+0x30) /* Shadow Transmit Holding Register (0x30 to 0x6C) */
+#define DW_DMA_UART0_FAR (DW_DMA_UART0_BASEADDR+0x40)
+#define DW_DMA_UART0_TFR (DW_DMA_UART0_BASEADDR+0x70)
+#define DW_DMA_UART0_RFW (DW_DMA_UART0_BASEADDR+0x74)
+#define DW_DMA_UART0_USR (DW_DMA_UART0_BASEADDR+0x78)
+#define DW_DMA_UART0_TFL (DW_DMA_UART0_BASEADDR+0x7C)
+#define DW_DMA_UART0_RFL (DW_DMA_UART0_BASEADDR+0x80)
+#define DW_DMA_UART0_SRR (DW_DMA_UART0_BASEADDR+0x84)
+#define DW_DMA_UART0_SRTS (DW_DMA_UART0_BASEADDR+0x88)
+#define DW_DMA_UART0_DR (DW_DMA_UART0_BASEADDR+0x8C)
+#define DW_DMA_UART0_SBCR (DW_DMA_UART0_BASEADDR+0x90)
+#define DW_DMA_UART0_SDMAM (DW_DMA_UART0_BASEADDR+0x94)
+#define DW_DMA_UART0_SFE (DW_DMA_UART0_BASEADDR+0x98)
+#define DW_DMA_UART0_SRT (DW_DMA_UART0_BASEADDR+0x9C)
+#define DW_DMA_UART0_STET (DW_DMA_UART0_BASEADDR+0xA0)
+#define DW_DMA_UART0_HTX (DW_DMA_UART0_BASEADDR+0xA4)
+#define DW_DMA_UART0_DMASA (DW_DMA_UART0_BASEADDR+0xA8)
+#define DW_DMA_UART0_CPR (DW_DMA_UART0_BASEADDR+0xF4)
+#define DW_DMA_UART0_UCV (DW_DMA_UART0_BASEADDR+0xF8)
+#define DW_DMA_UART0_CTR (DW_DMA_UART0_BASEADDR+0xFC)
+
+
+
+
+/*
+ * DW DMA UART1
+ */
+
+#define DW_DMA_UART1_RBR (DW_DMA_UART1_BASEADDR+0x00)
+#define DW_DMA_UART1_THR (DW_DMA_UART1_BASEADDR+0x00)
+#define DW_DMA_UART1_DLL (DW_DMA_UART1_BASEADDR+0x00)
+#define DW_DMA_UART1_DLH (DW_DMA_UART1_BASEADDR+0x04)
+#define DW_DMA_UART1_IER (DW_DMA_UART1_BASEADDR+0x04)
+#define DW_DMA_UART1_IIR (DW_DMA_UART1_BASEADDR+0x08)
+#define DW_DMA_UART1_FCR (DW_DMA_UART1_BASEADDR+0x08)
+#define DW_DMA_UART1_LCR (DW_DMA_UART1_BASEADDR+0x0C)
+#define DW_DMA_UART1_MCR (DW_DMA_UART1_BASEADDR+0x10)
+#define DW_DMA_UART1_LSR (DW_DMA_UART1_BASEADDR+0x14)
+#define DW_DMA_UART1_MSR (DW_DMA_UART1_BASEADDR+0x18)
+#define DW_DMA_UART1_SCR (DW_DMA_UART1_BASEADDR+0x1C)
+#define DW_DMA_UART1_LPDLL (DW_DMA_UART1_BASEADDR+0x20)
+#define DW_DMA_UART1_LPDLH (DW_DMA_UART1_BASEADDR+0x24)
+#define DW_DMA_UART1_SRBR (DW_DMA_UART1_BASEADDR+0x30) /* Shadow Receive Buffer Register (0x30 to 0x6C) */
+#define DW_DMA_UART1_STHR (DW_DMA_UART1_BASEADDR+0x30) /* Shadow Transmit Holding Register (0x30 to 0x6C) */
+#define DW_DMA_UART1_FAR (DW_DMA_UART1_BASEADDR+0x40)
+#define DW_DMA_UART1_TFR (DW_DMA_UART1_BASEADDR+0x70)
+#define DW_DMA_UART1_RFW (DW_DMA_UART1_BASEADDR+0x74)
+#define DW_DMA_UART1_USR (DW_DMA_UART1_BASEADDR+0x78)
+#define DW_DMA_UART1_TFL (DW_DMA_UART1_BASEADDR+0x7C)
+#define DW_DMA_UART1_RFL (DW_DMA_UART1_BASEADDR+0x80)
+#define DW_DMA_UART1_SRR (DW_DMA_UART1_BASEADDR+0x84)
+#define DW_DMA_UART1_SRTS (DW_DMA_UART1_BASEADDR+0x88)
+#define DW_DMA_UART1_DR (DW_DMA_UART1_BASEADDR+0x8C)
+#define DW_DMA_UART1_SBCR (DW_DMA_UART1_BASEADDR+0x90)
+#define DW_DMA_UART1_SDMAM (DW_DMA_UART1_BASEADDR+0x94)
+#define DW_DMA_UART1_SFE (DW_DMA_UART1_BASEADDR+0x98)
+#define DW_DMA_UART1_SRT (DW_DMA_UART1_BASEADDR+0x9C)
+#define DW_DMA_UART1_STET (DW_DMA_UART1_BASEADDR+0xA0)
+#define DW_DMA_UART1_HTX (DW_DMA_UART1_BASEADDR+0xA4)
+#define DW_DMA_UART1_DMASA (DW_DMA_UART1_BASEADDR+0xA8)
+#define DW_DMA_UART1_CPR (DW_DMA_UART1_BASEADDR+0xF4)
+#define DW_DMA_UART1_UCV (DW_DMA_UART1_BASEADDR+0xF8)
+#define DW_DMA_UART1_CTR (DW_DMA_UART1_BASEADDR+0xFC)
+
+
+
+
+/* DMA Channels */
+#define DMA_CHANNEL_0 0x0
+#define DMA_CHANNEL_1 0x1
+#define DMA_CHANNEL_2 0x2
+#define DMA_CHANNEL_3 0x3
+#define DMA_CHANNEL_4 0x4
+#define DMA_CHANNEL_5 0x5
+#define DMA_CHANNEL_6 0x6
+#define DMA_CHANNEL_7 0x7
+
+/* DMA Channel mask */
+#define DMA_CHANNEL_0_MASK 0x00000001
+#define DMA_CHANNEL_1_MASK 0x00000002
+#define DMA_CHANNEL_2_MASK 0x00000004
+#define DMA_CHANNEL_3_MASK 0x00000008
+#define DMA_CHANNEL_4_MASK 0x00000010
+#define DMA_CHANNEL_5_MASK 0x00000020
+#define DMA_CHANNEL_6_MASK 0x00000040
+#define DMA_CHANNEL_7_MASK 0x00000080
+
+#define DMA_GLOBAL_ENABLE 0x1
+#define DMA_REG_WE_SHIFT 0x8
+
+
+#define DMA_CHANNEL_REG_SAR_BASE DW_DMA_DMAC_SAR0
+#define DMA_CHANNEL_REG_DAR_BASE DW_DMA_DMAC_DAR0
+#define DMA_CHANNEL_REG_LLP_BASE DW_DMA_DMAC_LLP0
+#define DMA_CHANNEL_REG_CTL_BASE DW_DMA_DMAC_CTL0
+#define DMA_CHANNEL_REG_SSTAT_BASE DW_DMA_DMAC_SSTAT0
+#define DMA_CHANNEL_REG_DSTAT_BASE DW_DMA_DMAC_DSTAT0
+#define DMA_CHANNEL_REG_SSTATAR_BASE DW_DMA_DMAC_SSTATAR0
+#define DMA_CHANNEL_REG_DSTATAR_BASE DW_DMA_DMAC_DSTATAR0
+#define DMA_CHANNEL_REG_CFG_BASE DW_DMA_DMAC_CFG0
+#define DMA_CHANNEL_REG_SGR_BASE DW_DMA_DMAC_SGR0
+#define DMA_CHANNEL_REG_DSR_BASE DW_DMA_DMAC_DSR0
+
+
+#define DMA_CHANNEL_REG_COUNT 11
+#define DMA_CHANNEL_REG_SIZE 8
+
+
+/* DMA Control Register for Channel [0-31] */
+
+/*
+ * Single-block Transfer -- without write-back
+ * of control and status information enabled at
+ * the end of the single-block transfer
+ */
+
+#define DMA_CTL_INT_EN 0x1
+#define DMA_CTL_INT_EN_MASK 0x1
+#define DMA_CTL_INT_EN_SHIFT 0
+
+#define DMA_CTL_DST_TR_WIDTH 0x0 /* 8 bit */
+#define DMA_CTL_DST_TR_WIDTH_MASK 0x7
+#define DMA_CTL_DST_TR_WIDTH_SHIFT 1
+
+#define DMA_CTL_SRC_TR_WIDTH 0x0 /* 8 bit */
+#define DMA_CTL_SRC_TR_WIDTH_MASK 0x7
+#define DMA_CTL_SRC_TR_WIDTH_SHIFT 4
+
+#define DMA_CTL_DINC 0x0 /* Incriment */
+#define DMA_CTL_DINC_MASK 0x3
+#define DMA_CTL_DINC_SHIFT 7
+
+#define DMA_CTL_SINC 0x2 /* No Change */
+#define DMA_CTL_SINC_MASK 0x3
+#define DMA_CTL_SINC_SHIFT 9
+
+#define DMA_CTL_DEST_MSIZE 0x3 //0x1 /* Number of data items to be transferred = 4 */
+#define DMA_CTL_DEST_MSIZE_MASK 0x7
+#define DMA_CTL_DEST_MSIZE_SHIFT 11
+
+#define DMA_CTL_SRC_MSIZE 0x3 //0x1 /* Number of data items to be transferred = 4 */
+#define DMA_CTL_SRC_MSIZE_MASK 0x7
+#define DMA_CTL_SRC_MSIZE_SHIFT 14
+
+#define DMA_CTL_SRC_GATHER_EN 0x0 /* Disabled */
+#define DMA_CTL_SRC_GATHER_EN_MASK 0x1
+#define DMA_CTL_SRC_GATHER_EN_SHIFT 17
+
+#define DMA_CTL_DST_SCATTER_EN 0x0 /* Disabled */
+#define DMA_CTL_DST_SCATTER_EN_MASK 0x1
+#define DMA_CTL_DST_SCATTER_EN_SHIFT 18
+
+#define DMA_CTL_TT_FC 0x2 /* Peripheral to Memory, Flow Controller -- DW_ahb_dmac */
+#define DMA_CTL_TT_FC_MASK 0x7
+#define DMA_CTL_TT_FC_SHIFT 20
+
+#define DMA_CTL_DMS 0x0 /* ?????? AHB master 1 */
+#define DMA_CTL_DMS_MASK 0x3
+#define DMA_CTL_DMS_SHIFT 23
+
+#define DMA_CTL_SMS 0x1 /* ?????? AHB master 2 */
+#define DMA_CTL_SMS_MASK 0x3
+#define DMA_CTL_SMS_SHIFT 25
+
+#define DMA_CTL_LLP_DST_EN 0x0
+#define DMA_CTL_LLP_DST_EN_MASK 0x1
+#define DMA_CTL_LLP_DST_EN_SHIFT 27
+
+#define DMA_CTL_LLP_SRC_EN 0x0
+#define DMA_CTL_LLP_SRC_EN_MASK 0x1
+#define DMA_CTL_LLP_SRC_EN_SHIFT 28
+
+
+/* DMA Control Register for Channel [32-63] */
+/*
+ * Single-block Transfer -- without write-back
+ * of control and status information enabled at
+ * the end of the single-block transfer
+ */
+
+/* DMA_CTL_BLOCK_TS
+ * When the DW_ahb_dmac is the flow controller, the user writes this
+ * field before the channel is enabled in order to indicate the block size.
+ *
+ * The number programmed into BLOCK_TS indicates the total number
+ * of single transactions to perform for every block transfer.
+ *
+ * The width of the single transaction is determined by
+ * DMA_CTL_SRC_TR_WIDTH.
+ */
+#define DMA_CTL_BLOCK_TS SPI_NDF
+#define DMA_CTL_BLOCK_TS_MASK 0XFFF
+#define DMA_CTL_BLOCK_TS_SHIFT 0
+
+#define DMA_CTL_DONE 0x0
+#define DMA_CTL_DONE_MASK 0x1
+#define DMA_CTL_DONE_SHIFT 12
+
+
+
+/* DMA Config Register for Channel [0-31] */
+/*
+ * Single-block Transfer -- without write-back
+ * of control and status information enabled at
+ * the end of the single-block transfer
+ */
+
+#define DMA_CFG_RELOAD_DST 0x0 /* Disabled -- No Multi Block xfer */
+#define DMA_CFG_RELOAD_DST_MASK 0x1
+#define DMA_CFG_RELOAD_DST_SHIFT 31
+
+#define DMA_CFG_RELOAD_SRC 0x0 /* Disabled -- No Multi Block xfer */
+#define DMA_CFG_RELOAD_SRC_MASK 0x1
+#define DMA_CFG_RELOAD_SRC_SHIFT 30
+
+#define DMA_CFG_MAX_ABRST 0x0 /* max AMBA burst length -- not limited by software */
+#define DMA_CFG_MAX_ABRST_MASK 0x3FF
+#define DMA_CFG_MAX_ABRST_SHIFT 20
+
+#define DMA_CFG_SRC_HS_POL 0x0 /* Active high */
+#define DMA_CFG_SRC_HS_POL_MASK 0x1
+#define DMA_CFG_SRC_HS_POL_SHIFT 19
+
+#define DMA_CFG_DST_HS_POL 0x0 /* Active high */
+#define DMA_CFG_DST_HS_POL_MASK 0x1
+#define DMA_CFG_DST_HS_POL_SHIFT 18
+
+#define DMA_CFG_LOCK_B 0x0 /* No Locked DMA Transfers */
+#define DMA_CFG_LOCK_B_MASK 0x1
+#define DMA_CFG_LOCK_B_SHIFT 17
+
+#define DMA_CFG_LOCK_CH 0x0 /* No exclusive Access to the channel */
+#define DMA_CFG_LOCK_CH_MASK 0x1
+#define DMA_CFG_LOCK_CH_SHIFT 16
+
+#define DMA_CFG_LOCK_B_L 0x0
+#define DMA_CFG_LOCK_B_L_MASK 0x3
+#define DMA_CFG_LOCK_B_L_SHIFT 14
+
+#define DMA_CFG_LOCK_CH_L 0x0
+#define DMA_CFG_LOCK_CH_L_MASK 0x3
+#define DMA_CFG_LOCK_CH_L_SHIFT 12
+
+#define DMA_CFG_HS_SEL_SRC 0x0 /* Hardware handshaking */
+#define DMA_CFG_HS_SEL_SRC_MASK 0x1
+#define DMA_CFG_HS_SEL_SRC_SHIFT 11
+
+#define DMA_CFG_HS_SEL_DST 0x1 /* S/w handshaking */
+#define DMA_CFG_HS_SEL_DST_MASK 0x1
+#define DMA_CFG_HS_SEL_DST_SHIFT 10
+
+#define DMA_CFG_FIFO_EMPTY 0x1 /* Channel FIFO empty */
+#define DMA_CFG_FIFO_EMPTY_MASK 0x1
+#define DMA_CFG_FIFO_EMPTY_SHIFT 9
+
+#define DMA_CFG_CH_SUSP 0x0 /* Not suspended */
+#define DMA_CFG_CH_SUSP_MASK 0x1
+#define DMA_CFG_CH_SUSP_SHIFT 8
+
+#define DMA_CFG_CH_PRIOR 0x0 /* Should be kept equal to channel no */
+#define DMA_CFG_CH_PRIOR_MASK 0x7
+#define DMA_CFG_CH_PRIOR_SHIFT 5
+
+
+/* DMA Config Register for Channel [32-63] */
+/*
+ * Single-block Transfer -- without write-back
+ * of control and status information enabled at
+ * the end of the single-block transfer
+ */
+
+#define DMA_CFG_DEST_PER 0x0
+#define DMA_CFG_DEST_PER_MASK 0xF
+#define DMA_CFG_DEST_PER_SHIFT 11
+
+#define DMA_CFG_SRC_PER 0x4
+#define DMA_CFG_SRC_PER_MASK 0xF
+#define DMA_CFG_SRC_PER_SHIFT 7
+
+#define DMA_CFG_SS_UPD_EN 0x0 /* Disabled */
+#define DMA_CFG_SS_UPD_EN_MASK 0x1
+#define DMA_CFG_SS_UPD_EN_SHIFT 6
+
+#define DMA_CFG_DS_UPD_EN 0x0 /* Disabled */
+#define DMA_CFG_DS_UPD_EN_MASK 0x1
+#define DMA_CFG_DS_UPD_EN_SHIFT 5
+
+#define DMA_CFG_PROTCTL 0x1
+#define DMA_CFG_PROTCTL_MASK 0x7
+#define DMA_CFG_PROTCTL_SHIFT 2
+
+#define DMA_CFG_FIFO_MODE 0x0 /* Single AHB Space of the specified transfer width */
+#define DMA_CFG_FIFO_MODE_MASK 0x1
+#define DMA_CFG_FIFO_MODE_SHIFT 1
+
+#define DMA_CFG_FCMODE 0x0
+#define DMA_CFG_FCMODE_MASK 0x1
+#define DMA_CFG_FCMODE_SHIFT 0
+
+#define DMA_XFER_DLEN 16
+
+#endif /* _ASM_ARCH_DMA_H */
+
diff --git a/arch/arm/mach-comcerto/include/mach/ecc.h b/arch/arm/mach-comcerto/include/mach/ecc.h
new file mode 100644
index 0000000..f82a611
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/ecc.h
@@ -0,0 +1,126 @@
+#ifndef _ECC_H
+#define _ECC_H
+
+/* ECC Register Set */
+/********************/
+/* ECC Control Registers */
+#define ECC_SHIFT_EN_CFG (0x0)
+#define ECC_GEN_CFG (0x4)
+#define ECC_TAG_CFG (0x8)
+#define ECC_INIT_CFG (0xC)
+#define ECC_PRTY_OUT_SEL_CFG (0x10)
+#define ECC_POLY_START_CFG (0x14)
+#define ECC_CS_SEL_CFG (0x18)
+/* ECC Status Registers */
+#define ECC_IDLE_STAT (0x1C)
+#define ECC_POLY_STAT (0x20)
+#define ECC_CORR_STAT (0x24)
+#define ECC_CORR_DONE_STAT (0x28)
+#define ECC_CORR_DATA_STAT (0x2C)
+
+#if 0
+#define ECC_SHIFT_EN_CFG (COMCERTO_AXI_EXP_ECC_BASE)
+#define ECC_GEN_CFG (COMCERTO_AXI_EXP_ECC_BASE + 0x4)
+#define ECC_TAG_CFG (COMCERTO_AXI_EXP_ECC_BASE + 0x8)
+#define ECC_INIT_CFG (COMCERTO_AXI_EXP_ECC_BASE + 0xC)
+#define ECC_PRTY_OUT_SEL_CFG (COMCERTO_AXI_EXP_ECC_BASE + 0x10)
+#define ECC_POLY_START_CFG (COMCERTO_AXI_EXP_ECC_BASE + 0x14)
+#define ECC_CS_SEL_CFG (COMCERTO_AXI_EXP_ECC_BASE + 0x18)
+/* ECC Status Registers */
+#define ECC_IDLE_STAT (COMCERTO_AXI_EXP_ECC_BASE + 0x1C)
+#define ECC_POLY_STAT (COMCERTO_AXI_EXP_ECC_BASE + 0x20)
+#define ECC_CORR_STAT (COMCERTO_AXI_EXP_ECC_BASE + 0x24)
+#define ECC_CORR_DONE_STAT (COMCERTO_AXI_EXP_ECC_BASE + 0x28)
+#define ECC_CORR_DATA_STAT (COMCERTO_AXI_EXP_ECC_BASE + 0x2C)
+#endif
+
+/* ECC general configuration register parameters */
+#define HAMM_MODE BIT_28_MSK
+#define BCH_MODE (~ HAMM_MODE)
+
+#define PRTY_MODE_MASK BIT_24_MSK
+#define PRTY_CALC PRTY_MODE_MASK
+#define SYNDROME_CALC (~ PRTY_CALC)
+
+#define ECC_LVL_MASK 0x3F0000
+#define ECC_LVL_SHIFT 16
+
+#define BLK_SIZE_MASK 0x7FF
+
+#define ECC_LVL_2 0x2
+#define ECC_LVL_4 0x4
+#define ECC_LVL_6 0x6
+#define ECC_LVL_8 0x8
+#define ECC_LVL_10 0xA
+#define ECC_LVL_12 0xC
+#define ECC_LVL_14 0xE
+#define ECC_LVL_16 0x10
+#define ECC_LVL_18 0x12
+#define ECC_LVL_20 0x14
+#define ECC_LVL_22 0x16
+#define ECC_LVL_24 0x18
+#define ECC_LVL_26 0x1A
+#define ECC_LVL_28 0x1C
+#define ECC_LVL_30 0x1E
+#define ECC_LVL_32 0x20
+
+#if defined (CONFIG_NAND_COMCERTO_ECC_24_HW_BCH)
+ #define ECC_LVL_VAL ECC_LVL_24 /* ECC Level 24 is used */
+#elif defined (CONFIG_NAND_COMCERTO_ECC_8_HW_BCH)
+ #define ECC_LVL_VAL ECC_LVL_8 /* ECC Level 8 is used */
+#endif
+
+/* Block size used in Bytes*/
+#define ECC_BLOCK_SIZE_512 512
+#define ECC_BLOCK_SIZE_1024 1024
+
+/* Maximum value of ECC Block size is 2k-(1+14*ECC_LVL/8) Bytes */
+#define ECC_BLOCK_SIZE ECC_BLOCK_SIZE_1024
+#define ECC_BLOCK_SIZE_SHIFT ECC_BLOCK_SIZE_1024_SHIFT
+
+#define ECC_CS4_SEL 0x10
+#define ECC_CS3_SEL 0x08
+#define ECC_CS2_SEL 0x04
+#define ECC_CS1_SEL 0x02
+#define ECC_CS0_SEL 0x01
+
+#define ECC_INIT 0x1
+#define ECC_SHIFT_ENABLE 0x1
+#define ECC_SHIFT_DISABLE 0x0
+#define ECC_PARITY_OUT_EN 0x1
+#define ECC_PARITY_OUT_DISABLE 0x0
+#define ECC_UNCORR_ERR_HAMM 0x4
+
+/* Polynomial Start Configuration (ECC_POLY_START_CFG) */
+#define ECC_POLY_START (1 << 0)
+
+/* Idle Status (ECC_IDLE_STAT) */
+#define ECC_IDLE (1 << 0)
+
+/* Polynomial Status (ECC_POLY_STAT) */
+#define ECC_CORR_REQ (1 << 0)
+#define ECC_ERASED_PAGE (1 << 1)
+#define ECC_UNCORR_ERR_HAMM (1 << 2)
+
+/* Correction Status (ECC_CORR_STAT) */
+#define ECC_TAG_MASK 0xFFFF
+#define ECC_NUM_ERR_MASK 0x3F
+#define ECC_NUM_ERR_SHIFT 16
+#define ECC_UNCORR (1 << 24)
+
+/* Correction Done Status (ECC_CORR_DONE_STAT) */
+#define ECC_DONE (1 << 0)
+
+/* Correction Data Status (ECC_CORR_DATA_STAT), BCH Mode */
+#define ECC_BCH_MASK 0xFFFF
+#define ECC_BCH_INDEX_MASK 0x7FF
+#define ECC_BCH_INDEX_SHIFT 16
+#define ECC_BCH_VALID (1 << 31)
+
+/* Correction Data Status (ECC_CORR_DATA_STAT), Hamming Mode */
+#define ECC_HAMM_MASK 0xF
+#define ECC_HAMM_INDEX_MASK 0x1FF
+#define ECC_HAMM_INDEX_SHIFT 16
+#define ECC_HAMM_VALID (1 << 31)
+
+#endif /*_ECC_H */
diff --git a/arch/arm/mach-comcerto/include/mach/entry-macro.S b/arch/arm/mach-comcerto/include/mach/entry-macro.S
new file mode 100644
index 0000000..e718efa
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/entry-macro.S
@@ -0,0 +1,29 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/entry-macro.S
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <mach/hardware.h>
+#include <asm/hardware/gic.h>
+#include <asm/hardware/entry-macro-gic.S>
+
+ .macro disable_fiq
+ .endm
+
+ .macro arch_ret_to_user, tmp1, tmp2
+ .endm
diff --git a/arch/arm/mach-comcerto/include/mach/gpio.h b/arch/arm/mach-comcerto/include/mach/gpio.h
new file mode 100644
index 0000000..e0bfe4d
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/gpio.h
@@ -0,0 +1,2 @@
+
+#include "comcerto-2000/gpio.h"
diff --git a/arch/arm/mach-comcerto/include/mach/hardware.h b/arch/arm/mach-comcerto/include/mach/hardware.h
new file mode 100644
index 0000000..3099117
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/hardware.h
@@ -0,0 +1,50 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/hardware.h
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H
+
+#include <linux/types.h>
+
+ /***** Device *****/
+ #if defined(CONFIG_ARCH_M86XXX)
+ #include <mach/comcerto-2000.h>
+ #else
+ #error "mach/hardware.h : Unknown architecture"
+ #endif
+
+
+ /***** Board *****/
+ #if defined(CONFIG_C2K_ASIC)
+ #include <mach/board-c2kasic.h>
+ #elif defined(CONFIG_C2K_EVM)
+ #include <mach/board-c2kevm.h>
+ #elif defined(CONFIG_C2K_MFCN_EVM)
+ #include <mach/board-c2kmfcnevm.h>
+ #elif defined(CONFIG_RTSM_C2K)
+ #include <mach/board-c2krtsm.h>
+
+ #else
+ #error "mach/board_XXX.h : Unknown board"
+ #endif
+
+ #include <mach/comcerto-common.h>
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/i2c.h b/arch/arm/mach-comcerto/include/mach/i2c.h
new file mode 100644
index 0000000..15ce3cd
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/i2c.h
@@ -0,0 +1,32 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/i2c.h
+ *
+ * Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __ASM_ARCH_I2C_H
+#define __ASM_ARCH_I2C_H
+
+
+#if defined(CONFIG_ARCH_M86XXX)
+ #include <mach/comcerto-2000/i2c.h>
+#else
+ #error "mach/comcerto-2000/i2c.h : Unknown architecture"
+#endif
+
+#endif /* __ASM_ARCH_I2C_H */
diff --git a/arch/arm/mach-comcerto/include/mach/io.h b/arch/arm/mach-comcerto/include/mach/io.h
new file mode 100644
index 0000000..80e7dc6
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/io.h
@@ -0,0 +1,30 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/io.h
+ *
+ * Copyright (C) 2004,2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_IO_H
+#define __ASM_ARCH_IO_H
+
+
+#if defined(CONFIG_ARCH_M86XXX)
+ #include <mach/comcerto-2000/io.h>
+#else
+ #error "Unsupported CPU"
+#endif
+
+#endif /* __ASM_ARCH_IO_H */
diff --git a/arch/arm/mach-comcerto/include/mach/irqs.h b/arch/arm/mach-comcerto/include/mach/irqs.h
new file mode 100644
index 0000000..154edd8
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/irqs.h
@@ -0,0 +1,21 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/irqs.h
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __ASM_ARCH_IRQS_H
+#define __ASM_ARCH_IRQS_H
+
+#if defined(CONFIG_ARCH_M86XXX)
+ #include <mach/comcerto-2000/irqs.h>
+#else
+ #error "Unsupported CPU"
+#endif
+
+#endif /* __ASM_ARCH_IRQS_H */
diff --git a/arch/arm/mach-comcerto/include/mach/memory.h b/arch/arm/mach-comcerto/include/mach/memory.h
new file mode 100644
index 0000000..82265a4
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/memory.h
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+ #include <mach/hardware.h>
+
+ #define PLAT_PHYS_OFFSET COMCERTO_AXI_DDR_BASE
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/msp_ioctl.h b/arch/arm/mach-comcerto/include/mach/msp_ioctl.h
new file mode 100644
index 0000000..b7231fd
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/msp_ioctl.h
@@ -0,0 +1,56 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/msp_ioctl.h
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MSP_IOCTL_H
+#define _MSP_IOCTL_H
+
+struct MSP_IOCTL_IMAGE
+{
+ void *buf;
+ unsigned long len;
+};
+
+struct MSP_IOCTL_MEM_DUMP
+{
+ void *buf;
+ unsigned long addr;
+ unsigned long len;
+};
+
+#ifdef __KERNEL__
+int msp_ioctl_from_csp(unsigned int cmd, unsigned long arg);
+
+/* Only valid when called from the CSP */
+#define MSP_IOCTL_RESET_MSP_LOAD_FROM_KERNELBUF _IOR(MSP_IOC_TYPE, 5, struct MSP_IOCTL_IMAGE)
+#define MSP_IOCTL_RESET_MSP_DUMP_TO_KERNELBUF _IOWR(MSP_IOC_TYPE, 6, struct MSP_IOCTL_IMAGE)
+
+#endif
+
+#define MSP_IOC_TYPE 'm'
+#define MSP_IOCTL_DISPLAY_MSP_FROM_NORFLASH _IO(MSP_IOC_TYPE, 1)
+#define MSP_IOCTL_RESET_MSP_LOAD_FROM_NORFLASH _IO(MSP_IOC_TYPE, 2)
+#define MSP_IOCTL_RESET_MSP_LOAD_FROM_BUF _IOR(MSP_IOC_TYPE, 3, struct MSP_IOCTL_IMAGE)
+#define MSP_IOCTL_RESET_MSP_DUMP_TO_BUF _IOR(MSP_IOC_TYPE, 4, struct MSP_IOCTL_MEM_DUMP)
+
+#define MSP_DEVICE_NAME "/dev/msp"
+
+#define MSP_DEVICE_MAJOR_NUM 237 /* this could be whatever you want, as long as it does not conflict with other modules */
+
+#endif /* _MSP_IOCTL_H */
diff --git a/arch/arm/mach-comcerto/include/mach/pcie-c2000.h b/arch/arm/mach-comcerto/include/mach/pcie-c2000.h
new file mode 100644
index 0000000..9110d4f
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/pcie-c2000.h
@@ -0,0 +1,97 @@
+
+/*
+ * linux/arch/arm/mach-comcerto/pcie-comcerto2000.h
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef __PCIE_C2000__
+#define __PCIE_C2000__
+
+#define NUM_PCIE_PORTS CONFIG_COMCERTO_NUM_PCIES
+#define MAX_PCIE_PORTS 2
+
+struct pcie_port {
+ u8 port;
+ u8 root_bus_nr;
+ u16 reset;
+ unsigned long base;
+ unsigned long remote_mem_baseaddr;
+ unsigned long app_base;
+ void __iomem *va_app_base;
+ void __iomem *va_dbi_base;
+ unsigned long cfg0_base;
+ void __iomem *va_cfg0_base;
+ unsigned long cfg1_base;
+ void __iomem *va_cfg1_base;
+ unsigned int cfg0_prev_taddr;
+ unsigned int cfg1_prev_taddr;
+ unsigned int cmd_reg_val;
+ spinlock_t conf_lock;
+ spinlock_t intr_lock;
+ spinlock_t msi_map_lock;
+ char mem_space_name[16];
+ char io_space_name[16];
+ int port_mode;
+ int link_state;
+ int intx_base;
+ int msi_base;
+ int irq;
+ dma_addr_t msi_mbox_handle;
+ void *msi_mbox_baseaddr;
+ struct pcie_app_reg *app_regs;
+ struct resource res[2];
+ struct clk *ref_clock;
+#if defined(CONFIG_COMCERTO_PCIE_OCC_CLOCK)
+ struct clk *occ_clock;
+#endif
+};
+
+#define PCIE_PORT_MODE_NONE -1
+#define PCIE_PORT_MODE_EP CFG0_DEV_TYPE_EP
+#define PCIE_PORT_MODE_RC CFG0_DEV_TYPE_RC
+
+/* PCIe PM related defines */
+#define PCI_CAP_PM 0x40
+
+
+/* The following register definitions are as per "DWC_regs_rev04.doc" document */
+
+struct pcie_app_reg {
+ u32 cfg0;
+ u32 cfg1;
+ u32 cfg2;
+ u32 cfg3;
+ u32 cfg4;
+ u32 cfg5;
+ u32 cfg6;
+ u32 sts0;
+ u32 sts1;
+ u32 sts2;
+ u32 sts3;
+ u32 pwr_cfg_bdgt_data;
+ u32 pwr_cfg_bdgt_fn;
+ u32 radm_sts;
+ u32 pwr_sts_bdgt;
+ u32 intr_sts;
+ u32 intr_en;
+ u32 intr_msi_sts;
+ u32 intr_msi_en;
+};
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/reset.h b/arch/arm/mach-comcerto/include/mach/reset.h
new file mode 100644
index 0000000..16045ea
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/reset.h
@@ -0,0 +1,130 @@
+/*
+ * linux/arch/arm/mach-comcerto/include/mach/comcerto-2000/reset.h
+ *
+ * Header file for block reset for all the devices availble in the
+ * c2000 device.
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __ARCH_COMCERTO2000_RESET_H
+#define __ARCH_COMCERTO2000_RESET_H
+
+#define RTC_AXI_RESET_BIT (1<<7)
+#define I2CSPI_AXI_RESET_BIT (1<<5)
+#define DUS_AXI_RESET_BIT (1<<0)
+#define TDM_AXI_RESET_BIT (1<<4)
+#define PFE_SYS_AXI_RESET_BIT (1<<3)
+#define IPSEC_SPACC_AXI_RESET_BIT (1<<2)
+#define IPSEC_EAPE_AXI_RESET_BIT (1<<1)
+#define DPI_CIE_AXI_RESET_BIT (1<<5)
+#define DPI_DECOMP_AXI_RESET_BIT (1<<6)
+#define USB1_AXI_RESET_BIT (1<<4)
+#define USB1_PHY_RESET_BIT (1<<4)
+#define USB0_AXI_RESET_BIT (1<<3)
+#define USB0_PHY_RESET_BIT (1<<0)
+#define SATA_AXI_RESET_BIT (1<<2)
+#define SATA_PMU_RESET_BIT (1<<0)
+#define SATA_OOB_RESET_BIT (1<<0)
+#define PCIE1_AXI_RESET_BIT (1<<1)
+#define PCIE0_AXI_RESET_BIT (1<<0)
+#define PFE_CORE_RESET_BIT (1<<0)
+#define IPSEC_EAPE_CORE_RESET_BIT (1<<0)
+#define GEMTX_RESET_BIT (1<<0)
+#define L2CC_RESET_BIT (1<<0)
+#define DECT_RESET_BIT (1<<0)
+#define DDR_CNTRL_RESET_BIT (1<<1)
+#define DDR_PHY_RESET_BIT (1<<0)
+#define SERDES0_RESET_BIT (1<<0)
+#define SERDES1_RESET_BIT (1<<1)
+#define SERDES2_RESET_BIT (1<<2)
+#define SERDES_PCIE0_RESET_BIT ((1<<0)|(1<<1))
+#define SERDES_PCIE1_RESET_BIT ((1<<2)|(1<<3))
+#define SERDES_SATA0_RESET_BIT ((1<<4)|(1<<5))
+#define SERDES_SATA1_RESET_BIT ((1<<6)|(1<<7))
+#define SGMII_RESET_BIT (1<<0)
+#define USB0_UTMI_RESET_BIT (1<<1)
+#define USB1_UTMI_RESET_BIT (1<<5)
+#define TDMNTG_RESET_BIT (1<<0)
+
+/* C2000 device blocks which are to be put
+ * in reset.
+ */
+typedef enum {
+ COMPONENT_AXI_RTC=0,
+ COMPONENT_AXI_LEGACY_SPI,
+ COMPONENT_AXI_I2C,
+ COMPONENT_AXI_DMA,
+ COMPONENT_AXI_FAST_UART,
+ COMPONENT_AXI_FAST_SPI,
+ COMPONENT_AXI_TDM,
+ COMPONENT_PFE_SYS,
+ COMPONENT_AXI_IPSEC_EAPE,
+ COMPONENT_AXI_IPSEC_SPACC,
+ COMPONENT_AXI_DPI_CIE,
+ COMPONENT_AXI_DPI_DECOMP,
+ COMPONENT_AXI_USB1 , /* USB controller1, AXI Clock Domain reset control */
+ COMPONENT_UTMI_USB1, /* USB controller1,UTMI Clock Domain reset control*/
+ COMPONENT_USB1_PHY, /* USB PHY1 Reset control */
+ COMPONENT_AXI_USB0, /* USB controller0, AXI Clock Domain reset control*/
+ COMPONENT_UTMI_USB0, /* USB controller0. UTMI Clock Domain reset control */
+ COMPONENT_USB0_PHY, /* USB PHY0 Reset Control */
+ COMPONENT_AXI_SATA, /* SATA controller AXI Clock Domain Control Both for SATA 0/1*/
+ COMPONENT_SERDES_SATA0, /* SATA serdes Controller0 TX,Core Logic and RX clock domain control*/
+ COMPONENT_SERDES_SATA1, /* SATA serdes Controller1 TX,Core Logic and RX clock domain control*/
+ COMPONENT_AXI_PCIE1, /* PCIE Controller1,AXI Clock Domain reset control*/
+ COMPONENT_SERDES_PCIE1, /* PCIE serdes Controller1 Striky register and power register*/
+ COMPONENT_AXI_PCIE0, /* PCIE Controller0,AXI Clock Domain reset control*/
+ COMPONENT_SERDES_PCIE0, /* PCIE Controller1,TX,Core Logic and RX clock domain control*/
+ COMPONENT_PFE_CORE,
+ COMPONENT_IPSEC_EAPE_CORE,
+ COMPONENT_GEMTX,
+ COMPONENT_L2CC,
+ COMPONENT_DECT,
+ COMPONENT_DDR_CNTLR,
+ COMPONENT_DDR_PHY,
+ COMPONENT_SERDES0,
+ COMPONENT_SERDES1,
+ COMPONENT_SERDES2,
+ COMPONENT_SGMII,
+ COMPONENT_SATA_PMU,
+ COMPONENT_SATA_OOB,
+ COMPONENT_TDMNTG,
+}C2000_RESET_COMPONENT;
+
+extern void comcerto_rst_cntrl_set(unsigned int dev_rst_cntrl_bit);
+extern void c2000_block_reset(int block,int state);
+extern void reset_init(void);
+
+#if defined(CONFIG_C2K_MFCN_EVM)
+/* C2000 device blocks which are to be put
+ * in out of reset(GPIO).
+ */
+typedef enum {
+ COMPONENT_ATHEROS_SWITCH=0,
+ COMPONENT_SLIC,
+ COMPONENT_PCIE0,
+ COMPONENT_PCIE1,
+ COMPONENT_USB_HUB,
+ COMPONENT_EXP_DAUGTHER_CARD,
+ COMPONENT_RGMII0,
+ COMPONENT_RGMII1
+}C2000_GEN2_GPIO_RESET_COMPONENT;
+
+void GPIO_reset_external_device(int block,int state);
+#endif
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/sema.h b/arch/arm/mach-comcerto/include/mach/sema.h
new file mode 100644
index 0000000..dcecc12
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/sema.h
@@ -0,0 +1,128 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/sema.h
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARCH_SEMA_H__
+#define __ASM_ARCH_SEMA_H__
+
+#ifdef CONFIG_COMCERTO_MSP
+
+#define COMCERTO_SEMA_COUNT (64)
+
+enum {
+ COMCERTO_SEMA_MSP,
+};
+
+/* get status and disable FIQ & IRQ */
+static inline unsigned long arch_local_frq_save(void)
+{
+ unsigned long flags;
+
+ asm volatile (
+ "mrs %0, cpsr @ arch_local_frq_save\n"
+ "cpsid if"
+ : "=r" (flags) : : "memory", "cc");
+
+ return flags;
+}
+
+#define local_frq_save() arch_local_frq_save()
+#define local_frq_restore(flags) local_irq_restore(flags)
+
+/**
+ * comcerto_sema_lock()
+ * Spin to lock. Always succeeds.
+ *
+ * In order to acquire the semaphore, the processor needs to read the
+ * semaphore value. If zero, the processor acquired the semaphore and
+ * HW will set it to one, to mark that the semaphore is taken, if one
+ * is read, the semaphore is already taken by another processor and
+ * the current processor needs to keep polling the semaphore until
+ * value of zero is received.
+ *
+ * C2K has COMCERTO_SEMA_COUNT=64, 1-bit semaphores.
+ */
+static inline void comcerto_sema_lock(volatile unsigned long lock_id)
+{
+ volatile unsigned long *p = (unsigned long *)COMCERTO_SEMA_VADDR;
+ unsigned long x;
+
+ if (lock_id >= COMCERTO_SEMA_COUNT) {
+ BUG();
+ }
+
+ p += lock_id;
+ x = *p;
+
+ if (x) {
+ while (*p) {
+ /* wait for lock */
+ ;
+ }
+ }
+}
+
+/**
+ * comcerto_sema_unlock()
+ * Unlock. Always succeeds.
+ *
+ * After acquiring a semaphore the processor needs to write zero to it
+ * in order to release it.
+ */
+static inline void comcerto_sema_unlock(volatile unsigned long lock_id)
+{
+ volatile unsigned long *p = (unsigned long *)COMCERTO_SEMA_VADDR;
+
+ if (lock_id >= COMCERTO_SEMA_COUNT) {
+ BUG();
+ }
+
+ p += lock_id;
+ *p = 0;
+}
+
+static inline unsigned long msp_lock_frqsave(void)
+{
+ unsigned long flags;
+
+ flags = local_frq_save();
+ comcerto_sema_lock(COMCERTO_SEMA_MSP);
+
+ return flags;
+}
+
+static inline void msp_unlock_frqrestore(unsigned long flags)
+{
+ comcerto_sema_unlock(COMCERTO_SEMA_MSP);
+ local_frq_restore(flags);
+}
+
+#else /* !CONFIG_COMCERTO_MSP */
+static inline unsigned long msp_lock_frqsave(void)
+{
+ return 0;
+}
+
+static inline void msp_unlock_frqrestore(unsigned long flags)
+{
+ return;
+}
+#endif /* CONFIG_COMCERTO_MSP */
+
+#endif /* __ASM_ARCH_SEMA_H__ */
diff --git a/arch/arm/mach-comcerto/include/mach/serdes-c2000.h b/arch/arm/mach-comcerto/include/mach/serdes-c2000.h
new file mode 100644
index 0000000..121af86
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/serdes-c2000.h
@@ -0,0 +1,1993 @@
+
+/*
+ * Copyright (C) Mindspeed Technologies, Inc. 2011. All rights reserved.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * @file serdes.h
+ * @brief this header file will contain all required data structure
+ * and function definitions for Snowbush SerDes PHY interface.
+ * @date 10/02/2011
+ */
+/* SER-DES Address space */
+
+
+typedef struct serdes_regs_s
+{
+ unsigned short ofst;
+ unsigned short val;
+}serdes_regs_t;
+
+int serdes_phy_init(int phy_num, struct serdes_regs_s *regs, int size, int type);
+void serdes_phy_reset(int phy_num);
+
+ /* Initialize values of the Snowbush PHY (Serdes1) for SATA0 */
+
+/* 48MHz internal ref clock configuration */
+static struct serdes_regs_s sata_phy_reg_file_48[] =
+ {
+ { 0x000 << 2, 0x06},
+ { 0x001 << 2, 0x00},
+ { 0x002 << 2, 0x88},
+ { 0x003 << 2, 0x00},
+ { 0x004 << 2, 0x84},
+ { 0x005 << 2, 0x10},
+ { 0x006 << 2, 0x20},
+ { 0x007 << 2, 0x00},
+ { 0x008 << 2, 0x00},
+ { 0x009 << 2, 0x00},
+ { 0x00A << 2, 0x00},
+ { 0x00B << 2, 0x00},
+ { 0x00C << 2, 0x00},
+ { 0x00D << 2, 0x00},
+ { 0x00E << 2, 0x00},
+ { 0x00F << 2, 0x00},
+ { 0x010 << 2, 0x00},
+ { 0x011 << 2, 0x00},
+ { 0x012 << 2, 0x00},
+ { 0x013 << 2, 0x00},
+ { 0x014 << 2, 0x00},
+ { 0x015 << 2, 0x00},
+ { 0x016 << 2, 0x00},
+ { 0x017 << 2, 0x00},
+ { 0x018 << 2, 0x00},
+ { 0x019 << 2, 0x00},
+ { 0x01A << 2, 0x00},
+ { 0x01B << 2, 0x00},
+ { 0x01C << 2, 0x00},
+ { 0x01D << 2, 0x00},
+ { 0x01E << 2, 0x00},
+ { 0x01F << 2, 0x00},
+ { 0x020 << 2, 0x00},
+ { 0x021 << 2, 0x00},
+ { 0x022 << 2, 0xa0},
+ { 0x023 << 2, 0x62},
+ { 0x024 << 2, 0x00},
+ { 0x025 << 2, 0x00},
+ { 0x026 << 2, 0x00},
+ { 0x027 << 2, 0x00},
+ { 0x028 << 2, 0x00},
+ { 0x029 << 2, 0x00},
+ { 0x02A << 2, 0x00},
+ { 0x02B << 2, 0x00},
+ { 0x02C << 2, 0x00},
+ { 0x02D << 2, 0x00},
+ { 0x02E << 2, 0x04},
+ { 0x02F << 2, 0x50},
+ { 0x030 << 2, 0x70},
+ { 0x031 << 2, 0x02},
+ { 0x032 << 2, 0x25},
+ { 0x033 << 2, 0x40},
+ { 0x034 << 2, 0x01},
+ { 0x035 << 2, 0x40},
+ { 0x036 << 2, 0x00},
+ { 0x037 << 2, 0x00},
+ { 0x038 << 2, 0x00},
+ { 0x039 << 2, 0x00},
+ { 0x03A << 2, 0x00},
+ { 0x03B << 2, 0x00},
+ { 0x03C << 2, 0x00},
+ { 0x03D << 2, 0x00},
+ { 0x03E << 2, 0x00},
+ { 0x03F << 2, 0x00},
+ { 0x040 << 2, 0x00},
+ { 0x041 << 2, 0x00},
+ { 0x042 << 2, 0x00},
+ { 0x043 << 2, 0x00},
+ { 0x044 << 2, 0x00},
+ { 0x045 << 2, 0x00},
+ { 0x046 << 2, 0x00},
+ { 0x047 << 2, 0x00},
+ { 0x048 << 2, 0x00},
+ { 0x049 << 2, 0x00},
+ { 0x04A << 2, 0x00},
+ { 0x04B << 2, 0x00},
+ { 0x04C << 2, 0x00},
+ { 0x04D << 2, 0x00},
+ { 0x04E << 2, 0x00},
+ { 0x04F << 2, 0x00},
+ { 0x050 << 2, 0x00},
+ { 0x051 << 2, 0x00},
+ { 0x052 << 2, 0x00},
+ { 0x053 << 2, 0x00},
+ { 0x054 << 2, 0x00},
+ { 0x055 << 2, 0x00},
+ { 0x056 << 2, 0x00},
+ { 0x057 << 2, 0x00},
+ { 0x058 << 2, 0x00},
+ { 0x059 << 2, 0x00},
+ { 0x05A << 2, 0x00},
+ { 0x05B << 2, 0x00},
+ { 0x05C << 2, 0x00},
+ { 0x05D << 2, 0x00},
+ { 0x05E << 2, 0x00},
+ { 0x05F << 2, 0x00},
+ { 0x060 << 2, 0x00},
+ { 0x061 << 2, 0x2e},
+ { 0x062 << 2, 0x08},
+ { 0x063 << 2, 0x5e},
+ { 0x064 << 2, 0x00},
+ { 0x065 << 2, 0x42},
+ { 0x066 << 2, 0xd1},
+ { 0x067 << 2, 0xa0},
+ { 0x068 << 2, 0x28},
+ { 0x069 << 2, 0x78},
+ { 0x06A << 2, 0xcc},
+ { 0x06B << 2, 0xc1},
+ { 0x06C << 2, 0x4e},
+ { 0x06D << 2, 0x03},
+ { 0x06E << 2, 0x00},
+ { 0x06F << 2, 0x00},
+ { 0x070 << 2, 0x00},
+ { 0x071 << 2, 0x00},
+ { 0x072 << 2, 0x00},
+ { 0x200 << 2, 0x02},
+ { 0x201 << 2, 0x00},
+ { 0x202 << 2, 0x00},
+ { 0x203 << 2, 0x00},
+ { 0x204 << 2, 0x00},
+ { 0x205 << 2, 0x10},
+ { 0x206 << 2, 0x84},
+ { 0x207 << 2, 0x3C},
+ { 0x208 << 2, 0xe0},
+ { 0x210 << 2, 0x23},
+ { 0x211 << 2, 0x00},
+ { 0x212 << 2, 0x40},
+ { 0x213 << 2, 0x05},
+ { 0x214 << 2, 0x20},/* */
+ { 0x215 << 2, 0x17},
+ { 0x216 << 2, 0x00},
+ { 0x217 << 2, 0x68},
+ { 0x218 << 2, 0xe2},
+ { 0x219 << 2, 0x1e},
+ { 0x21A << 2, 0x18},
+ { 0x21B << 2, 0x0d},
+ { 0x21C << 2, 0x0c},
+ { 0x21D << 2, 0x00},
+ { 0x21E << 2, 0x00},
+ { 0x21F << 2, 0x00},
+ { 0x220 << 2, 0x00},
+ { 0x221 << 2, 0x00},
+ { 0x222 << 2, 0x00},
+ { 0x223 << 2, 0x00},
+ { 0x224 << 2, 0x00},
+ { 0x225 << 2, 0x00},
+ { 0x226 << 2, 0x00},
+ { 0x227 << 2, 0x00},
+ { 0x228 << 2, 0x00},
+ { 0x229 << 2, 0x00},
+ { 0x22A << 2, 0x00},
+ { 0x22B << 2, 0x00},
+ { 0x22C << 2, 0x00},
+ { 0x22D << 2, 0x00},
+ { 0x22E << 2, 0x00},
+ { 0x22F << 2, 0x00},
+ { 0x230 << 2, 0x00},
+ { 0x231 << 2, 0x00},
+ { 0x232 << 2, 0x00},
+ { 0x233 << 2, 0x00},
+ { 0x234 << 2, 0x00},
+ { 0x235 << 2, 0x00},
+ { 0x236 << 2, 0x00},
+ { 0x237 << 2, 0x00},
+ { 0x238 << 2, 0x00},
+ { 0x239 << 2, 0x00},
+ { 0x23A << 2, 0x00},
+ { 0x23B << 2, 0x00},
+ { 0x23C << 2, 0x00},
+ { 0x23D << 2, 0x00},
+ { 0x23E << 2, 0x00},
+ { 0x23F << 2, 0x00},
+ { 0x240 << 2, 0x00},
+ { 0x241 << 2, 0x00},
+ { 0x242 << 2, 0x00},
+ { 0x243 << 2, 0x00},
+ { 0x244 << 2, 0x00},
+ { 0x245 << 2, 0x00},
+ { 0x246 << 2, 0x00},
+ { 0x247 << 2, 0x00},
+ { 0x248 << 2, 0x00},
+ { 0x249 << 2, 0x00},
+ { 0x24A << 2, 0x00},
+ { 0x24B << 2, 0x00},
+ { 0x24C << 2, 0x00},
+ { 0x24D << 2, 0x00},
+ { 0x24E << 2, 0x00},
+ { 0x24F << 2, 0x00},
+ { 0x250 << 2, 0x60},
+ { 0x251 << 2, 0x0f},
+ { 0xA00 << 2, 0x00},
+ { 0xA01 << 2, 0x20},
+ { 0xA02 << 2, 0x00},
+ { 0xA03 << 2, 0x40},
+ { 0xA04 << 2, 0x24},
+ { 0xA05 << 2, 0xae},
+ { 0xA06 << 2, 0x19},
+ { 0xA07 << 2, 0x49},
+ { 0xA08 << 2, 0x04},
+ { 0xA09 << 2, 0xc3},
+ { 0xA0A << 2, 0x2a},
+ { 0xA0B << 2, 0xc6},
+ { 0xA0C << 2, 0x01},
+ { 0xA0D << 2, 0x03},
+ { 0xA0E << 2, 0x28},
+ { 0xA0F << 2, 0x98},
+ { 0xA10 << 2, 0x19},
+ { 0xA11 << 2, 0x00},
+ { 0xA12 << 2, 0x00},
+ { 0xA13 << 2, 0x80},
+ { 0xA14 << 2, 0xf0},
+ { 0xA15 << 2, 0xd0},
+ { 0xA16 << 2, 0x00},
+ { 0xA17 << 2, 0x00},
+ { 0xA30 << 2, 0x00},
+ { 0xA31 << 2, 0x00},
+ { 0xA32 << 2, 0x00},
+ { 0xA33 << 2, 0x00},
+ { 0xA34 << 2, 0x00},
+ { 0xA35 << 2, 0x00},
+ { 0xA36 << 2, 0x00},
+ { 0xA37 << 2, 0x00},
+ { 0xA38 << 2, 0x00},
+ { 0xA39 << 2, 0xa0},
+ { 0xA3A << 2, 0xa0},
+ { 0xA3B << 2, 0xa0},
+ { 0xA3C << 2, 0xa0},
+ { 0xA3D << 2, 0xa0},
+ { 0xA3E << 2, 0xa0},
+ { 0xA3F << 2, 0xa0},
+ { 0xA40 << 2, 0x62},
+ { 0xA41 << 2, 0x00},
+ { 0xA42 << 2, 0x80},
+ { 0xA43 << 2, 0x58},
+ { 0xA44 << 2, 0x00},
+ { 0xA45 << 2, 0x44},
+ { 0xA46 << 2, 0x5c},
+ { 0xA47 << 2, 0x86},
+ { 0xA48 << 2, 0x8d},
+ { 0xA49 << 2, 0xd0},
+ { 0xA4A << 2, 0x09},
+ { 0xA4B << 2, 0x90},
+ { 0xA4C << 2, 0x07},
+ { 0xA4D << 2, 0x40},
+ { 0xA4E << 2, 0x00},
+ { 0xA4F << 2, 0x00},
+ { 0xA50 << 2, 0x00},
+ { 0xA51 << 2, 0x20},
+ { 0xA52 << 2, 0x32},
+ { 0xA53 << 2, 0x00},
+ { 0xA54 << 2, 0x00},
+ { 0xA55 << 2, 0x00},
+ { 0xA56 << 2, 0x00},
+ { 0xA57 << 2, 0x00},
+ { 0xA58 << 2, 0x00},
+ { 0xA59 << 2, 0x00},
+ { 0xA5A << 2, 0x00},
+ { 0xA5B << 2, 0x00},
+ { 0xA5C << 2, 0x00},
+ { 0xA5D << 2, 0x00},
+ { 0xA5E << 2, 0x00},
+ { 0xA5F << 2, 0x00},
+ { 0xA60 << 2, 0x00},
+ { 0xA61 << 2, 0x00},
+ { 0xA62 << 2, 0x00},
+ { 0xA63 << 2, 0x00},
+ { 0xA64 << 2, 0x00},
+ { 0xA65 << 2, 0x00},
+ { 0xA66 << 2, 0x00},
+ { 0xA67 << 2, 0x00},
+ { 0xA68 << 2, 0x00},
+ { 0xA69 << 2, 0x00},
+ { 0xA6A << 2, 0x00},
+ { 0xA6B << 2, 0x00},
+ { 0xA6C << 2, 0x00},
+ { 0xA6D << 2, 0x00},
+ { 0xA6E << 2, 0x00},
+ { 0xA6F << 2, 0x00},
+ { 0xA70 << 2, 0x00},
+ { 0xA71 << 2, 0x00},
+ { 0xA72 << 2, 0x00},
+ { 0xA73 << 2, 0x00},
+ { 0xA74 << 2, 0x00},
+ { 0xA75 << 2, 0x00},
+ { 0xA76 << 2, 0x00},
+ { 0xA77 << 2, 0x00},
+ { 0xA78 << 2, 0x00},
+ { 0xA79 << 2, 0x00},
+ { 0xA7A << 2, 0x00},
+ { 0xA7B << 2, 0x00},
+ { 0xA7C << 2, 0x00},
+ { 0xA7D << 2, 0x00},
+ { 0xA7E << 2, 0x00},
+ { 0xA7F << 2, 0xd8},
+ { 0xA80 << 2, 0x1a},
+ { 0xA81 << 2, 0xff},
+ { 0xA82 << 2, 0x11},
+ { 0xA83 << 2, 0x00},
+ { 0xA84 << 2, 0x00},
+ { 0xA85 << 2, 0x00},
+ { 0xA86 << 2, 0x00},
+ { 0xA87 << 2, 0xf0},
+ { 0xA88 << 2, 0xff},
+ { 0xA89 << 2, 0xff},
+ { 0xA8A << 2, 0xff},
+ { 0xA8B << 2, 0xff},
+ { 0xA8C << 2, 0x1c},
+ { 0xA8D << 2, 0xc2},
+ { 0xA8E << 2, 0xc3},
+ { 0xA8F << 2, 0x3f},
+ { 0xA90 << 2, 0x0a},
+ { 0xA91 << 2, 0x00},
+ { 0xA92 << 2, 0x00},
+ { 0xA93 << 2, 0x00},
+ { 0xA94 << 2, 0x00},
+ { 0xA95 << 2, 0x00},
+ { 0xA96 << 2, 0xf8},
+ { 0x000 << 2, 0x07}
+ };
+
+/* 24MHz internal ref clock configuration */
+static struct serdes_regs_s sata_phy_reg_file_24[] =
+ {
+ { 0x000 << 2, 0x06},
+ { 0x001 << 2, 0x00},
+ { 0x002 << 2, 0x88},
+ { 0x003 << 2, 0x00},
+ { 0x004 << 2, 0x7b},
+ { 0x005 << 2, 0xc9},
+ { 0x006 << 2, 0x03},
+ { 0x007 << 2, 0x00},
+ { 0x008 << 2, 0x00},
+ { 0x009 << 2, 0x00},
+ { 0x00A << 2, 0x00},
+ { 0x00B << 2, 0x00},
+ { 0x00C << 2, 0x00},
+ { 0x00D << 2, 0x00},
+ { 0x00E << 2, 0x00},
+ { 0x00F << 2, 0x00},
+ { 0x010 << 2, 0x00},
+ { 0x011 << 2, 0x00},
+ { 0x012 << 2, 0x00},
+ { 0x013 << 2, 0x00},
+ { 0x014 << 2, 0x00},
+ { 0x015 << 2, 0x00},
+ { 0x016 << 2, 0x00},
+ { 0x017 << 2, 0x00},
+ { 0x018 << 2, 0x00},
+ { 0x019 << 2, 0x00},
+ { 0x01A << 2, 0x00},
+ { 0x01B << 2, 0x00},
+ { 0x01C << 2, 0x00},
+ { 0x01D << 2, 0x00},
+ { 0x01E << 2, 0x00},
+ { 0x01F << 2, 0x00},
+ { 0x020 << 2, 0x00},
+ { 0x021 << 2, 0x00},
+ { 0x022 << 2, 0xa0},
+ { 0x023 << 2, 0x54},
+ { 0x024 << 2, 0x00},
+ { 0x025 << 2, 0x00},
+ { 0x026 << 2, 0x00},
+ { 0x027 << 2, 0x00},
+ { 0x028 << 2, 0x00},
+ { 0x029 << 2, 0x00},
+ { 0x02A << 2, 0x00},
+ { 0x02B << 2, 0x00},
+ { 0x02C << 2, 0x00},
+ { 0x02D << 2, 0x00},
+ { 0x02E << 2, 0x04},
+ { 0x02F << 2, 0x50},
+ { 0x030 << 2, 0x70},
+ { 0x031 << 2, 0x02},
+ { 0x032 << 2, 0x25},
+ { 0x033 << 2, 0x40},
+ { 0x034 << 2, 0x01},
+ { 0x035 << 2, 0x40},
+ { 0x036 << 2, 0x00},
+ { 0x037 << 2, 0x00},
+ { 0x038 << 2, 0x00},
+ { 0x039 << 2, 0x00},
+ { 0x03A << 2, 0x00},
+ { 0x03B << 2, 0x00},
+ { 0x03C << 2, 0x00},
+ { 0x03D << 2, 0x00},
+ { 0x03E << 2, 0x00},
+ { 0x03F << 2, 0x00},
+ { 0x040 << 2, 0x00},
+ { 0x041 << 2, 0x00},
+ { 0x042 << 2, 0x00},
+ { 0x043 << 2, 0x00},
+ { 0x044 << 2, 0x00},
+ { 0x045 << 2, 0x00},
+ { 0x046 << 2, 0x00},
+ { 0x047 << 2, 0x00},
+ { 0x048 << 2, 0x00},
+ { 0x049 << 2, 0x00},
+ { 0x04A << 2, 0x00},
+ { 0x04B << 2, 0x00},
+ { 0x04C << 2, 0x00},
+ { 0x04D << 2, 0x00},
+ { 0x04E << 2, 0x00},
+ { 0x04F << 2, 0x00},
+ { 0x050 << 2, 0x00},
+ { 0x051 << 2, 0x00},
+ { 0x052 << 2, 0x00},
+ { 0x053 << 2, 0x00},
+ { 0x054 << 2, 0x00},
+ { 0x055 << 2, 0x00},
+ { 0x056 << 2, 0x00},
+ { 0x057 << 2, 0x00},
+ { 0x058 << 2, 0x00},
+ { 0x059 << 2, 0x00},
+ { 0x05A << 2, 0x00},
+ { 0x05B << 2, 0x00},
+ { 0x05C << 2, 0x00},
+ { 0x05D << 2, 0x00},
+ { 0x05E << 2, 0x00},
+ { 0x05F << 2, 0x00},
+ { 0x060 << 2, 0x00},
+ //{ 0x061 << 2, 0x2e}, //for Rev-A0 device
+ { 0x061 << 2, 0x2e}, //for Rev-A1 device
+ { 0x062 << 2, 0x00},
+ { 0x063 << 2, 0x5e},
+ { 0x064 << 2, 0x00},
+ { 0x065 << 2, 0x42},
+ { 0x066 << 2, 0xd1},
+ { 0x067 << 2, 0x20},
+ { 0x068 << 2, 0x28},
+ { 0x069 << 2, 0x78},
+ { 0x06A << 2, 0x2c},
+ { 0x06B << 2, 0xb9},
+ { 0x06C << 2, 0x5e},
+ { 0x06D << 2, 0x03},
+ { 0x06E << 2, 0x00},
+ { 0x06F << 2, 0x00},
+ { 0x070 << 2, 0x00},
+ { 0x071 << 2, 0x00},
+ { 0x072 << 2, 0x00},
+ { 0x200 << 2, 0x02},
+ { 0x201 << 2, 0x00},
+ { 0x202 << 2, 0x00},
+ { 0x203 << 2, 0x00},
+ { 0x204 << 2, 0x00},
+ { 0x205 << 2, 0x10},
+ { 0x206 << 2, 0x84},
+ { 0x207 << 2, 0x3c},
+ { 0x208 << 2, 0xe0},
+ { 0x210 << 2, 0x23},
+ { 0x211 << 2, 0x00},
+ { 0x212 << 2, 0x40},
+ { 0x213 << 2, 0x05},
+ { 0x214 << 2, 0xd0},/* */
+ { 0x215 << 2, 0x17},
+ { 0x216 << 2, 0x00},
+ { 0x217 << 2, 0x68},
+ { 0x218 << 2, 0xf2},
+ { 0x219 << 2, 0x1e},
+ { 0x21A << 2, 0x18},
+ { 0x21B << 2, 0x0d},
+ { 0x21C << 2, 0x0c},
+ { 0x21D << 2, 0x00},
+ { 0x21E << 2, 0x00},
+ { 0x21F << 2, 0x00},
+ { 0x220 << 2, 0x00},
+ { 0x221 << 2, 0x00},
+ { 0x222 << 2, 0x00},
+ { 0x223 << 2, 0x00},
+ { 0x224 << 2, 0x00},
+ { 0x225 << 2, 0x00},
+ { 0x226 << 2, 0x00},
+ { 0x227 << 2, 0x00},
+ { 0x228 << 2, 0x00},
+ { 0x229 << 2, 0x00},
+ { 0x22A << 2, 0x00},
+ { 0x22B << 2, 0x00},
+ { 0x22C << 2, 0x00},
+ { 0x22D << 2, 0x00},
+ { 0x22E << 2, 0x00},
+ { 0x22F << 2, 0x00},
+ { 0x230 << 2, 0x00},
+ { 0x231 << 2, 0x00},
+ { 0x232 << 2, 0x00},
+ { 0x233 << 2, 0x00},
+ { 0x234 << 2, 0x00},
+ { 0x235 << 2, 0x00},
+ { 0x236 << 2, 0x00},
+ { 0x237 << 2, 0x00},
+ { 0x238 << 2, 0x00},
+ { 0x239 << 2, 0x00},
+ { 0x23A << 2, 0x00},
+ { 0x23B << 2, 0x00},
+ { 0x23C << 2, 0x00},
+ { 0x23D << 2, 0x00},
+ { 0x23E << 2, 0x00},
+ { 0x23F << 2, 0x00},
+ { 0x240 << 2, 0x00},
+ { 0x241 << 2, 0x00},
+ { 0x242 << 2, 0x00},
+ { 0x243 << 2, 0x00},
+ { 0x244 << 2, 0x00},
+ { 0x245 << 2, 0x00},
+ { 0x246 << 2, 0x00},
+ { 0x247 << 2, 0x00},
+ { 0x248 << 2, 0x00},
+ { 0x249 << 2, 0x00},
+ { 0x24A << 2, 0x00},
+ { 0x24B << 2, 0x00},
+ { 0x24C << 2, 0x00},
+ { 0x24D << 2, 0x00},
+ { 0x24E << 2, 0x00},
+ { 0x24F << 2, 0x00},
+ { 0x250 << 2, 0x60},
+ { 0x251 << 2, 0x0f},
+ { 0xA00 << 2, 0x00},
+ { 0xA01 << 2, 0x20},
+ { 0xA02 << 2, 0x00},
+ { 0xA03 << 2, 0x40},
+ { 0xA04 << 2, 0x24},
+ { 0xA05 << 2, 0xae},
+ { 0xA06 << 2, 0x19},
+ { 0xA07 << 2, 0x49},
+ { 0xA08 << 2, 0x04},
+ { 0xA09 << 2, 0x83},
+ { 0xA0A << 2, 0x4b},
+ { 0xA0B << 2, 0xc5},
+ { 0xA0C << 2, 0x01},
+ { 0xA0D << 2, 0x03},
+ { 0xA0E << 2, 0x28},
+ { 0xA0F << 2, 0x98},
+ { 0xA10 << 2, 0x19},
+ { 0xA11 << 2, 0x00},
+ { 0xA12 << 2, 0x00},
+ { 0xA13 << 2, 0x80},
+ { 0xA14 << 2, 0xf0},
+ { 0xA15 << 2, 0xd0},
+ { 0xA16 << 2, 0x00},
+ { 0xA17 << 2, 0x00},
+ { 0xA30 << 2, 0x00},
+ { 0xA31 << 2, 0x00},
+ { 0xA32 << 2, 0x00},
+ { 0xA33 << 2, 0x00},
+ { 0xA34 << 2, 0x00},
+ { 0xA35 << 2, 0x00},
+ { 0xA36 << 2, 0x00},
+ { 0xA37 << 2, 0x00},
+ { 0xA38 << 2, 0x00},
+ { 0xA39 << 2, 0xa0},
+ { 0xA3A << 2, 0xa0},
+ { 0xA3B << 2, 0xa0},
+ { 0xA3C << 2, 0xa0},
+ { 0xA3D << 2, 0xa0},
+ { 0xA3E << 2, 0xa0},
+ { 0xA3F << 2, 0x54},
+ { 0xA40 << 2, 0x62},
+ { 0xA41 << 2, 0x00},
+ { 0xA42 << 2, 0x80},
+ { 0xA43 << 2, 0x58},
+ { 0xA44 << 2, 0x00},
+ { 0xA45 << 2, 0x44},
+ { 0xA46 << 2, 0x5c},
+ { 0xA47 << 2, 0x86},
+ { 0xA48 << 2, 0x8d},
+ { 0xA49 << 2, 0xd0},
+ { 0xA4A << 2, 0x09},
+ { 0xA4B << 2, 0x90},
+ { 0xA4C << 2, 0x07},
+ { 0xA4D << 2, 0x40},
+ { 0xA4E << 2, 0x00},
+ { 0xA4F << 2, 0x00},
+ { 0xA50 << 2, 0x00},
+ { 0xA51 << 2, 0x20},
+ { 0xA52 << 2, 0x32},
+ { 0xA53 << 2, 0x00},
+ { 0xA54 << 2, 0x00},
+ { 0xA55 << 2, 0x00},
+ { 0xA56 << 2, 0x00},
+ { 0xA57 << 2, 0x00},
+ { 0xA58 << 2, 0x00},
+ { 0xA59 << 2, 0x00},
+ { 0xA5A << 2, 0x00},
+ { 0xA5B << 2, 0x00},
+ { 0xA5C << 2, 0x00},
+ { 0xA5D << 2, 0x00},
+ { 0xA5E << 2, 0x00},
+ { 0xA5F << 2, 0x00},
+ { 0xA60 << 2, 0x00},
+ { 0xA61 << 2, 0x00},
+ { 0xA62 << 2, 0x00},
+ { 0xA63 << 2, 0x00},
+ { 0xA64 << 2, 0x00},
+ { 0xA65 << 2, 0x00},
+ { 0xA66 << 2, 0x00},
+ { 0xA67 << 2, 0x00},
+ { 0xA68 << 2, 0x00},
+ { 0xA69 << 2, 0x00},
+ { 0xA6A << 2, 0x00},
+ { 0xA6B << 2, 0x00},
+ { 0xA6C << 2, 0x00},
+ { 0xA6D << 2, 0x00},
+ { 0xA6E << 2, 0x00},
+ { 0xA6F << 2, 0x00},
+ { 0xA70 << 2, 0x00},
+ { 0xA71 << 2, 0x00},
+ { 0xA72 << 2, 0x00},
+ { 0xA73 << 2, 0x00},
+ { 0xA74 << 2, 0x00},
+ { 0xA75 << 2, 0x00},
+ { 0xA76 << 2, 0x00},
+ { 0xA77 << 2, 0x00},
+ { 0xA78 << 2, 0x00},
+ { 0xA79 << 2, 0x00},
+ { 0xA7A << 2, 0x00},
+ { 0xA7B << 2, 0x00},
+ { 0xA7C << 2, 0x00},
+ { 0xA7D << 2, 0x00},
+ { 0xA7E << 2, 0x00},
+ { 0xA7F << 2, 0xd8},
+ { 0xA80 << 2, 0x1a},
+ { 0xA81 << 2, 0xff},
+ { 0xA82 << 2, 0x11},
+ { 0xA83 << 2, 0x00},
+ { 0xA84 << 2, 0x00},
+ { 0xA85 << 2, 0x00},
+ { 0xA86 << 2, 0x00},
+ { 0xA87 << 2, 0xf0},
+ { 0xA88 << 2, 0xff},
+ { 0xA89 << 2, 0xff},
+ { 0xA8A << 2, 0xff},
+ { 0xA8B << 2, 0xff},
+ { 0xA8C << 2, 0x1c},
+ { 0xA8D << 2, 0xc2},
+ { 0xA8E << 2, 0xc3},
+ { 0xA8F << 2, 0x3f},
+ { 0xA90 << 2, 0x0a},
+ { 0xA91 << 2, 0x00},
+ { 0xA92 << 2, 0x00},
+ { 0xA93 << 2, 0x00},
+ { 0xA94 << 2, 0x00},
+ { 0xA95 << 2, 0x00},
+ { 0xA96 << 2, 0xf8},
+ { 0x000 << 2, 0x07}
+ };
+
+#if 0
+/* This is 60Mhz external ref clock configuration which is not used on the board currently */
+static struct serdes_regs_s sata0_phy_reg_file[] =
+ {
+ { 0x000 << 2, 0x06},
+ { 0x001 << 2, 0x00},
+ { 0x002 << 2, 0x80},
+ { 0x003 << 2, 0x00},
+ { 0x004 << 2, 0x00},
+ { 0x005 << 2, 0x00},
+ { 0x006 << 2, 0x00},
+ { 0x007 << 2, 0x00},
+ { 0x008 << 2, 0x00},
+ { 0x009 << 2, 0x00},
+ { 0x00A << 2, 0x00},
+ { 0x00B << 2, 0x00},
+ { 0x00C << 2, 0x00},
+ { 0x00D << 2, 0x00},
+ { 0x00E << 2, 0x00},
+ { 0x00F << 2, 0x00},
+ { 0x010 << 2, 0x00},
+ { 0x011 << 2, 0x00},
+ { 0x012 << 2, 0x00},
+ { 0x013 << 2, 0x00},
+ { 0x014 << 2, 0x00},
+ { 0x015 << 2, 0x00},
+ { 0x016 << 2, 0x00},
+ { 0x017 << 2, 0x00},
+ { 0x018 << 2, 0x00},
+ { 0x019 << 2, 0x00},
+ { 0x01A << 2, 0x00},
+ { 0x01B << 2, 0x00},
+ { 0x01C << 2, 0x00},
+ { 0x01D << 2, 0x00},
+ { 0x01E << 2, 0x00},
+ { 0x01F << 2, 0x00},
+ { 0x020 << 2, 0x00},
+ { 0x021 << 2, 0x00},
+ { 0x022 << 2, 0xa0},
+ { 0x023 << 2, 0x64},
+ { 0x024 << 2, 0x00},
+ { 0x025 << 2, 0x00},
+ { 0x026 << 2, 0x00},
+ { 0x027 << 2, 0x00},
+ { 0x028 << 2, 0x00},
+ { 0x029 << 2, 0x00},
+ { 0x02A << 2, 0x00},
+ { 0x02B << 2, 0x00},
+ { 0x02C << 2, 0x00},
+ { 0x02D << 2, 0x00},
+ { 0x02E << 2, 0x04},
+ { 0x02F << 2, 0x50},
+ { 0x030 << 2, 0x70},
+ { 0x031 << 2, 0x02},
+ { 0x032 << 2, 0x25},
+ { 0x033 << 2, 0x40},
+ { 0x034 << 2, 0x01},
+ { 0x035 << 2, 0x40},
+ { 0x036 << 2, 0x00},
+ { 0x037 << 2, 0x00},
+ { 0x038 << 2, 0x00},
+ { 0x039 << 2, 0x00},
+ { 0x03A << 2, 0x00},
+ { 0x03B << 2, 0x00},
+ { 0x03C << 2, 0x00},
+ { 0x03D << 2, 0x00},
+ { 0x03E << 2, 0x00},
+ { 0x03F << 2, 0x00},
+ { 0x040 << 2, 0x00},
+ { 0x041 << 2, 0x00},
+ { 0x042 << 2, 0x00},
+ { 0x043 << 2, 0x00},
+ { 0x044 << 2, 0x00},
+ { 0x045 << 2, 0x00},
+ { 0x046 << 2, 0x00},
+ { 0x047 << 2, 0x00},
+ { 0x048 << 2, 0x00},
+ { 0x049 << 2, 0x00},
+ { 0x04A << 2, 0x00},
+ { 0x04B << 2, 0x00},
+ { 0x04C << 2, 0x00},
+ { 0x04D << 2, 0x00},
+ { 0x04E << 2, 0x00},
+ { 0x04F << 2, 0x00},
+ { 0x050 << 2, 0x00},
+ { 0x051 << 2, 0x00},
+ { 0x052 << 2, 0x00},
+ { 0x053 << 2, 0x00},
+ { 0x054 << 2, 0x00},
+ { 0x055 << 2, 0x00},
+ { 0x056 << 2, 0x00},
+ { 0x057 << 2, 0x00},
+ { 0x058 << 2, 0x00},
+ { 0x059 << 2, 0x00},
+ { 0x05A << 2, 0x00},
+ { 0x05B << 2, 0x00},
+ { 0x05C << 2, 0x00},
+ { 0x05D << 2, 0x00},
+ { 0x05E << 2, 0x00},
+ { 0x05F << 2, 0x00},
+ { 0x060 << 2, 0x00},
+ { 0x061 << 2, 0x2e},
+ { 0x062 << 2, 0x00},
+ { 0x063 << 2, 0x5e},
+ { 0x064 << 2, 0x00},
+ { 0x065 << 2, 0x42},
+ { 0x066 << 2, 0xd1},
+ { 0x067 << 2, 0xa0},
+ { 0x068 << 2, 0x28},
+ { 0x069 << 2, 0x78},
+ { 0x06A << 2, 0x44},
+ { 0x06B << 2, 0xce},
+ { 0x06C << 2, 0x0b},
+ { 0x06D << 2, 0x00},
+ { 0x06E << 2, 0x00},
+ { 0x06F << 2, 0x00},
+ { 0x070 << 2, 0x00},
+ { 0x071 << 2, 0x00},
+ { 0x072 << 2, 0x00},
+ { 0x200 << 2, 0x02},
+ { 0x201 << 2, 0x00},
+ { 0x202 << 2, 0x00},
+ { 0x203 << 2, 0x00},
+ { 0x204 << 2, 0x00},
+ { 0x205 << 2, 0x10},
+ { 0x206 << 2, 0x84},
+ { 0x207 << 2, 0x04},
+ { 0x208 << 2, 0xe0},
+ { 0x210 << 2, 0x23},
+ { 0x211 << 2, 0x00},
+ { 0x212 << 2, 0x00},
+ { 0x213 << 2, 0x05},
+ { 0x214 << 2, 0x38},
+ { 0x215 << 2, 0x04},
+ { 0x216 << 2, 0x00},
+ { 0x217 << 2, 0x68},
+ { 0x218 << 2, 0xf2},
+ { 0x219 << 2, 0x1e},
+ { 0x21A << 2, 0x18},
+ { 0x21B << 2, 0x0d},
+ { 0x21C << 2, 0x0d},
+ { 0x21D << 2, 0x00},
+ { 0x21E << 2, 0x00},
+ { 0x21F << 2, 0x00},
+ { 0x220 << 2, 0x00},
+ { 0x221 << 2, 0x00},
+ { 0x222 << 2, 0x00},
+ { 0x223 << 2, 0x00},
+ { 0x224 << 2, 0x00},
+ { 0x225 << 2, 0x00},
+ { 0x226 << 2, 0x00},
+ { 0x227 << 2, 0x00},
+ { 0x228 << 2, 0x00},
+ { 0x229 << 2, 0x00},
+ { 0x22A << 2, 0x00},
+ { 0x22B << 2, 0x00},
+ { 0x22C << 2, 0x00},
+ { 0x22D << 2, 0x00},
+ { 0x22E << 2, 0x00},
+ { 0x22F << 2, 0x00},
+ { 0x230 << 2, 0x00},
+ { 0x231 << 2, 0x00},
+ { 0x232 << 2, 0x00},
+ { 0x233 << 2, 0x00},
+ { 0x234 << 2, 0x00},
+ { 0x235 << 2, 0x00},
+ { 0x236 << 2, 0x00},
+ { 0x237 << 2, 0x00},
+ { 0x238 << 2, 0x00},
+ { 0x239 << 2, 0x00},
+ { 0x23A << 2, 0x00},
+ { 0x23B << 2, 0x00},
+ { 0x23C << 2, 0x00},
+ { 0x23D << 2, 0x00},
+ { 0x23E << 2, 0x00},
+ { 0x23F << 2, 0x00},
+ { 0x240 << 2, 0x00},
+ { 0x241 << 2, 0x00},
+ { 0x242 << 2, 0x00},
+ { 0x243 << 2, 0x00},
+ { 0x244 << 2, 0x00},
+ { 0x245 << 2, 0x00},
+ { 0x246 << 2, 0x00},
+ { 0x247 << 2, 0x00},
+ { 0x248 << 2, 0x00},
+ { 0x249 << 2, 0x00},
+ { 0x24A << 2, 0x00},
+ { 0x24B << 2, 0x00},
+ { 0x24C << 2, 0x00},
+ { 0x24D << 2, 0x00},
+ { 0x24E << 2, 0x00},
+ { 0x24F << 2, 0x00},
+ { 0x250 << 2, 0x60},
+ { 0x251 << 2, 0x0f},
+ { 0xA00 << 2, 0x00},
+ { 0xA01 << 2, 0x20},
+ { 0xA02 << 2, 0x00},
+ { 0xA03 << 2, 0x40},
+ { 0xA04 << 2, 0x24},
+ { 0xA05 << 2, 0xae},
+ { 0xA06 << 2, 0x19},
+ { 0xA07 << 2, 0x49},
+ { 0xA08 << 2, 0x04},
+ { 0xA09 << 2, 0x23},
+ { 0xA0A << 2, 0x48},
+ { 0xA0B << 2, 0xc6},
+ { 0xA0C << 2, 0x01},
+ { 0xA0D << 2, 0x03},
+ { 0xA0E << 2, 0x28},
+ { 0xA0F << 2, 0x98},
+ { 0xA10 << 2, 0x19},
+ { 0xA11 << 2, 0x00},
+ { 0xA12 << 2, 0x00},
+ { 0xA13 << 2, 0x80},
+ { 0xA14 << 2, 0xf0},
+ { 0xA15 << 2, 0xd0},
+ { 0xA16 << 2, 0x00},
+ { 0xA17 << 2, 0x00},
+ { 0xA30 << 2, 0x00},
+ { 0xA31 << 2, 0x00},
+ { 0xA32 << 2, 0x00},
+ { 0xA33 << 2, 0x00},
+ { 0xA34 << 2, 0x00},
+ { 0xA35 << 2, 0x00},
+ { 0xA36 << 2, 0x00},
+ { 0xA37 << 2, 0x00},
+ { 0xA38 << 2, 0x00},
+ { 0xA39 << 2, 0xa0},
+ { 0xA3A << 2, 0xa0},
+ { 0xA3B << 2, 0xa0},
+ { 0xA3C << 2, 0xa0},
+ { 0xA3D << 2, 0xa0},
+ { 0xA3E << 2, 0xa0},
+ { 0xA3F << 2, 0xa0},
+ { 0xA40 << 2, 0x64},
+ { 0xA41 << 2, 0x00},
+ { 0xA42 << 2, 0x80},
+ { 0xA43 << 2, 0x58},
+ { 0xA44 << 2, 0x00},
+ { 0xA45 << 2, 0x44},
+ { 0xA46 << 2, 0x5c},
+ { 0xA47 << 2, 0x86},
+ { 0xA48 << 2, 0x8d},
+ { 0xA49 << 2, 0xd0},
+ { 0xA4A << 2, 0x09},
+ { 0xA4B << 2, 0x90},
+ { 0xA4C << 2, 0x07},
+ { 0xA4D << 2, 0x40},
+ { 0xA4E << 2, 0x00},
+ { 0xA4F << 2, 0x00},
+ { 0xA50 << 2, 0x00},
+ { 0xA51 << 2, 0x20},
+ { 0xA52 << 2, 0x32},
+ { 0xA53 << 2, 0x00},
+ { 0xA54 << 2, 0x00},
+ { 0xA55 << 2, 0x00},
+ { 0xA56 << 2, 0x00},
+ { 0xA57 << 2, 0x00},
+ { 0xA58 << 2, 0x00},
+ { 0xA59 << 2, 0x00},
+ { 0xA5A << 2, 0x00},
+ { 0xA5B << 2, 0x00},
+ { 0xA5C << 2, 0x00},
+ { 0xA5D << 2, 0x00},
+ { 0xA5E << 2, 0x00},
+ { 0xA5F << 2, 0x00},
+ { 0xA60 << 2, 0x00},
+ { 0xA61 << 2, 0x00},
+ { 0xA62 << 2, 0x00},
+ { 0xA63 << 2, 0x00},
+ { 0xA64 << 2, 0x00},
+ { 0xA65 << 2, 0x00},
+ { 0xA66 << 2, 0x00},
+ { 0xA67 << 2, 0x00},
+ { 0xA68 << 2, 0x00},
+ { 0xA69 << 2, 0x00},
+ { 0xA6A << 2, 0x00},
+ { 0xA6B << 2, 0x00},
+ { 0xA6C << 2, 0x00},
+ { 0xA6D << 2, 0x00},
+ { 0xA6E << 2, 0x00},
+ { 0xA6F << 2, 0x00},
+ { 0xA70 << 2, 0x00},
+ { 0xA71 << 2, 0x00},
+ { 0xA72 << 2, 0x00},
+ { 0xA73 << 2, 0x00},
+ { 0xA74 << 2, 0x00},
+ { 0xA75 << 2, 0x00},
+ { 0xA76 << 2, 0x00},
+ { 0xA77 << 2, 0x00},
+ { 0xA78 << 2, 0x00},
+ { 0xA79 << 2, 0x00},
+ { 0xA7A << 2, 0x00},
+ { 0xA7B << 2, 0x00},
+ { 0xA7C << 2, 0x00},
+ { 0xA7D << 2, 0x00},
+ { 0xA7E << 2, 0x00},
+ { 0xA7F << 2, 0xd8},
+ { 0xA80 << 2, 0x1a},
+ { 0xA81 << 2, 0xff},
+ { 0xA82 << 2, 0x11},
+ { 0xA83 << 2, 0x00},
+ { 0xA84 << 2, 0x00},
+ { 0xA85 << 2, 0x00},
+ { 0xA86 << 2, 0x00},
+ { 0xA87 << 2, 0xf0},
+ { 0xA88 << 2, 0xff},
+ { 0xA89 << 2, 0xff},
+ { 0xA8A << 2, 0xff},
+ { 0xA8B << 2, 0xff},
+ { 0xA8C << 2, 0x1c},
+ { 0xA8D << 2, 0xc2},
+ { 0xA8E << 2, 0xc3},
+ { 0xA8F << 2, 0x3f},
+ { 0xA90 << 2, 0x0a},
+ { 0xA91 << 2, 0x00},
+ { 0xA92 << 2, 0x00},
+ { 0xA93 << 2, 0x00},
+ { 0xA94 << 2, 0x00},
+ { 0xA95 << 2, 0x00},
+ { 0xA96 << 2, 0xf8},
+ { 0x000 << 2, 0x07}
+ };
+#endif
+
+static struct serdes_regs_s pcie_phy_reg_file_48[] =
+
+/* PCIe SERDES is using internal clock */
+ {
+ /* Common CMU block */
+ { 0x000 << 2, 0x06},
+ { 0x001 << 2, 0x00},
+ { 0x002 << 2, 0x09}, //
+ { 0x003 << 2, 0x00},
+ { 0x004 << 2, 0x60}, //
+ { 0x005 << 2, 0x09}, //
+ { 0x006 << 2, 0x0e}, //
+ { 0x007 << 2, 0x00},
+ { 0x008 << 2, 0x00},
+ { 0x009 << 2, 0x00},
+ { 0x00A << 2, 0x00},
+ { 0x00B << 2, 0x00},
+ { 0x00C << 2, 0x00},
+ { 0x00D << 2, 0x00},
+ { 0x00E << 2, 0x00},
+ { 0x00F << 2, 0x00},
+ { 0x010 << 2, 0x00},
+ { 0x011 << 2, 0x00},
+ { 0x012 << 2, 0x00},
+ { 0x013 << 2, 0x00},
+ { 0x014 << 2, 0x00},
+ { 0x015 << 2, 0x00},
+ { 0x016 << 2, 0x00},
+ { 0x017 << 2, 0x00},
+ { 0x018 << 2, 0x00},
+ { 0x019 << 2, 0x00},
+ { 0x01A << 2, 0x00},
+ { 0x01B << 2, 0x00},
+ { 0x01C << 2, 0x00},
+ { 0x01D << 2, 0x00},
+ { 0x01E << 2, 0x00},
+ { 0x01F << 2, 0x00},
+ { 0x020 << 2, 0x00},
+ { 0x021 << 2, 0x00},
+ { 0x022 << 2, 0xa0},
+ { 0x023 << 2, 0x6c}, //
+ { 0x024 << 2, 0x00},
+ { 0x025 << 2, 0x00},
+ { 0x026 << 2, 0x00},
+ { 0x027 << 2, 0x00},
+ { 0x028 << 2, 0x00},
+ { 0x029 << 2, 0x00},
+ { 0x02A << 2, 0x00},
+ { 0x02B << 2, 0x00},
+ { 0x02C << 2, 0x00},
+ { 0x02D << 2, 0x00},
+ { 0x02E << 2, 0x04},
+ { 0x02F << 2, 0x50},
+ { 0x030 << 2, 0x70},
+ { 0x031 << 2, 0x02},
+ { 0x032 << 2, 0x25},
+ { 0x033 << 2, 0x40},
+ { 0x034 << 2, 0x01},
+ { 0x035 << 2, 0x40},
+ { 0x036 << 2, 0x00},
+ { 0x037 << 2, 0x00},
+ { 0x038 << 2, 0x00},
+ { 0x039 << 2, 0x00},
+ { 0x03A << 2, 0x00},
+ { 0x03B << 2, 0x00},
+ { 0x03C << 2, 0x00},
+ { 0x03D << 2, 0x00},
+ { 0x03E << 2, 0x00},
+ { 0x03F << 2, 0x00},
+ { 0x040 << 2, 0x00},
+ { 0x041 << 2, 0x00},
+ { 0x042 << 2, 0x00},
+ { 0x043 << 2, 0x00},
+ { 0x044 << 2, 0x00},
+ { 0x045 << 2, 0x00},
+ { 0x046 << 2, 0x00},
+ { 0x047 << 2, 0x00},
+ { 0x048 << 2, 0x00},
+ { 0x049 << 2, 0x00},
+ { 0x04A << 2, 0x00},
+ { 0x04B << 2, 0x00},
+ { 0x04C << 2, 0x00},
+ { 0x04D << 2, 0x00},
+ { 0x04E << 2, 0x00},
+ { 0x04F << 2, 0x00},
+ { 0x050 << 2, 0x00},
+ { 0x051 << 2, 0x00},
+ { 0x052 << 2, 0x00},
+ { 0x053 << 2, 0x00},
+ { 0x054 << 2, 0x00},
+ { 0x055 << 2, 0x00},
+ { 0x056 << 2, 0x00},
+ { 0x057 << 2, 0x00},
+ { 0x058 << 2, 0x00},
+ { 0x059 << 2, 0x00},
+ { 0x05A << 2, 0x00},
+ { 0x05B << 2, 0x00},
+ { 0x05C << 2, 0x00},
+ { 0x05D << 2, 0x00},
+ { 0x05E << 2, 0x00},
+ { 0x05F << 2, 0x00},
+ { 0x060 << 2, 0x00},
+ { 0x061 << 2, 0x2e},
+ { 0x062 << 2, 0x08}, //
+ { 0x063 << 2, 0x5e},
+ { 0x064 << 2, 0x00},
+ { 0x065 << 2, 0x42},
+ { 0x066 << 2, 0xd1},
+ { 0x067 << 2, 0x90}, //
+ { 0x068 << 2, 0x08},
+ { 0x069 << 2, 0x90}, //
+ { 0x06A << 2, 0x2c}, //
+ { 0x06B << 2, 0x32}, //
+ { 0x06C << 2, 0x59}, //
+ { 0x06D << 2, 0x03}, //
+ { 0x06E << 2, 0x00},
+ { 0x06F << 2, 0x00},
+ { 0x070 << 2, 0x00},
+ { 0x071 << 2, 0x00},
+ { 0x072 << 2, 0x00},
+ /* Lane0 Block */
+ { 0x200 << 2, 0x00},
+ { 0x201 << 2, 0x00},
+ { 0x202 << 2, 0x00},
+ { 0x203 << 2, 0x00},
+ { 0x204 << 2, 0x00},
+ { 0x205 << 2, 0x10},
+ { 0x206 << 2, 0x84},
+ { 0x207 << 2, 0x04},
+ { 0x208 << 2, 0xe0},
+ { 0x210 << 2, 0x23},
+ { 0x211 << 2, 0x00},
+ { 0x212 << 2, 0x00},
+ { 0x213 << 2, 0x04}, //
+ { 0x214 << 2, 0xc0}, //
+ { 0x215 << 2, 0x18}, //
+ { 0x216 << 2, 0x00},
+ { 0x217 << 2, 0x68},
+ { 0x218 << 2, 0xa2},
+ { 0x219 << 2, 0x1e},
+ { 0x21A << 2, 0x18},
+ { 0x21B << 2, 0x0d},
+ { 0x21C << 2, 0x0d},
+ { 0x21D << 2, 0x00},
+ { 0x21E << 2, 0x00},
+ { 0x21F << 2, 0x00},
+ { 0x220 << 2, 0x00},
+ { 0x221 << 2, 0x00},
+ { 0x222 << 2, 0x00},
+ { 0x223 << 2, 0x00},
+ { 0x224 << 2, 0x00},
+ { 0x225 << 2, 0x00},
+ { 0x226 << 2, 0x00},
+ { 0x227 << 2, 0x00},
+ { 0x228 << 2, 0x00},
+ { 0x229 << 2, 0x00},
+ { 0x22A << 2, 0x00},
+ { 0x22B << 2, 0x00},
+ { 0x22C << 2, 0x00},
+ { 0x22D << 2, 0x00},
+ { 0x22E << 2, 0x00},
+ { 0x22F << 2, 0x00},
+ { 0x230 << 2, 0x00},
+ { 0x231 << 2, 0x00},
+ { 0x232 << 2, 0x00},
+ { 0x233 << 2, 0x00},
+ { 0x234 << 2, 0x00},
+ { 0x235 << 2, 0x00},
+ { 0x236 << 2, 0x00},
+ { 0x237 << 2, 0x00},
+ { 0x238 << 2, 0x00},
+ { 0x239 << 2, 0x00},
+ { 0x23A << 2, 0x00},
+ { 0x23B << 2, 0x00},
+ { 0x23C << 2, 0x00},
+ { 0x23D << 2, 0x00},
+ { 0x23E << 2, 0x00},
+ { 0x23F << 2, 0x00},
+ { 0x240 << 2, 0x00},
+ { 0x241 << 2, 0x00},
+ { 0x242 << 2, 0x00},
+ { 0x243 << 2, 0x00},
+ { 0x244 << 2, 0x00},
+ { 0x245 << 2, 0x00},
+ { 0x246 << 2, 0x00},
+ { 0x247 << 2, 0x00},
+ { 0x248 << 2, 0x00},
+ { 0x249 << 2, 0x00},
+ { 0x24A << 2, 0x00},
+ { 0x24B << 2, 0x00},
+ { 0x24C << 2, 0x00},
+ { 0x24D << 2, 0x00},
+ { 0x24E << 2, 0x00},
+ { 0x24F << 2, 0x00},
+ { 0x250 << 2, 0x60},
+ { 0x251 << 2, 0x0f},
+ /* Common Lane Block */
+ { 0xA00 << 2, 0xc0},
+ { 0xA01 << 2, 0x90},
+ { 0xA02 << 2, 0x02},
+ { 0xA03 << 2, 0x40},
+ { 0xA04 << 2, 0x3c},
+ { 0xA05 << 2, 0x00},
+ { 0xA06 << 2, 0x00},
+ { 0xA07 << 2, 0x00},
+ { 0xA08 << 2, 0x00},
+ { 0xA09 << 2, 0xc3}, //
+ { 0xA0A << 2, 0xca}, //
+ { 0xA0B << 2, 0xc6},
+ { 0xA0C << 2, 0x01},
+ { 0xA0D << 2, 0x03},
+ { 0xA0E << 2, 0x28},
+ { 0xA0F << 2, 0x98},
+ { 0xA10 << 2, 0x19},
+ { 0xA11 << 2, 0x28},
+ { 0xA12 << 2, 0x78},
+ { 0xA13 << 2, 0xe1},
+ { 0xA14 << 2, 0xf0},
+ { 0xA15 << 2, 0x10},
+ { 0xA16 << 2, 0xf4},
+ { 0xA17 << 2, 0x00},
+ { 0xA30 << 2, 0x00},
+ { 0xA31 << 2, 0x00},
+ { 0xA32 << 2, 0x00},
+ { 0xA33 << 2, 0x00},
+ { 0xA34 << 2, 0x00},
+ { 0xA35 << 2, 0x00},
+ { 0xA36 << 2, 0x00},
+ { 0xA37 << 2, 0x00},
+ { 0xA38 << 2, 0x00},
+ { 0xA39 << 2, 0xa0},
+ { 0xA3A << 2, 0xa0},
+ { 0xA3B << 2, 0xa0},
+ { 0xA3C << 2, 0xa0},
+ { 0xA3D << 2, 0xa0},
+ { 0xA3E << 2, 0xa0},
+ { 0xA3F << 2, 0xa0},
+ { 0xA40 << 2, 0x6c}, //
+ { 0xA41 << 2, 0x00},
+ { 0xA42 << 2, 0xc0},
+ { 0xA43 << 2, 0x9f},
+ { 0xA44 << 2, 0x01},
+ { 0xA45 << 2, 0x00},
+ { 0xA46 << 2, 0x00},
+ { 0xA47 << 2, 0x00},
+ { 0xA48 << 2, 0x00},
+ { 0xA49 << 2, 0x00},
+ { 0xA4A << 2, 0x00},
+ { 0xA4B << 2, 0x00},
+ { 0xA4C << 2, 0x30},
+ { 0xA4D << 2, 0x41},
+ { 0xA4E << 2, 0x7e},
+ { 0xA4F << 2, 0xd0},
+ { 0xA50 << 2, 0xcc},
+ { 0xA51 << 2, 0x85},
+ { 0xA52 << 2, 0x52},
+ { 0xA53 << 2, 0x93},
+ { 0xA54 << 2, 0xe0},
+ { 0xA55 << 2, 0x49},
+ { 0xA56 << 2, 0xdd},
+ { 0xA57 << 2, 0xb0},
+ { 0xA58 << 2, 0x0b},
+ { 0xA59 << 2, 0x02},
+ { 0xA5A << 2, 0x00},
+ { 0xA5B << 2, 0x00},
+ { 0xA5C << 2, 0x00},
+ { 0xA5D << 2, 0x00},
+ { 0xA5E << 2, 0x00},
+ { 0xA5F << 2, 0x00},
+ { 0xA60 << 2, 0x00},
+ { 0xA61 << 2, 0x00},
+ { 0xA62 << 2, 0x00},
+ { 0xA63 << 2, 0x00},
+ { 0xA64 << 2, 0x00},
+ { 0xA65 << 2, 0x00},
+ { 0xA66 << 2, 0x00},
+ { 0xA67 << 2, 0x00},
+ { 0xA68 << 2, 0x00},
+ { 0xA69 << 2, 0x00},
+ { 0xA6A << 2, 0x00},
+ { 0xA6B << 2, 0x00},
+ { 0xA6C << 2, 0x00},
+ { 0xA6D << 2, 0x00},
+ { 0xA6E << 2, 0x00},
+ { 0xA6F << 2, 0x00},
+ { 0xA70 << 2, 0x00},
+ { 0xA71 << 2, 0x00},
+ { 0xA72 << 2, 0x00},
+ { 0xA73 << 2, 0x00},
+ { 0xA74 << 2, 0x00},
+ { 0xA75 << 2, 0x00},
+ { 0xA76 << 2, 0x00},
+ { 0xA77 << 2, 0x00},
+ { 0xA78 << 2, 0x00},
+ { 0xA79 << 2, 0x00},
+ { 0xA7A << 2, 0x00},
+ { 0xA7B << 2, 0x00},
+ { 0xA7C << 2, 0x00},
+ { 0xA7D << 2, 0x00},
+ { 0xA7E << 2, 0x00},
+ { 0xA7F << 2, 0xd8},
+ { 0xA80 << 2, 0x1a},
+ { 0xA81 << 2, 0xff},
+ { 0xA82 << 2, 0x01},
+ { 0xA83 << 2, 0x00},
+ { 0xA84 << 2, 0x00},
+ { 0xA85 << 2, 0x00},
+ { 0xA86 << 2, 0x00},
+ { 0xA87 << 2, 0xf0},
+ { 0xA88 << 2, 0xff},
+ { 0xA89 << 2, 0xff},
+ { 0xA8A << 2, 0xff},
+ { 0xA8B << 2, 0xff},
+ { 0xA8C << 2, 0x1c},
+ { 0xA8D << 2, 0xc2},
+ { 0xA8E << 2, 0xc3},
+ { 0xA8F << 2, 0x3f},
+ { 0xA90 << 2, 0x0a},
+ { 0xA91 << 2, 0x00},
+ { 0xA92 << 2, 0x00},
+ { 0xA93 << 2, 0x00},
+ { 0xA94 << 2, 0x00},
+ { 0xA95 << 2, 0x00},
+ { 0xA96 << 2, 0xf8},
+ { 0x000 << 2, 0x07}
+ };
+
+static struct serdes_regs_s pcie_phy_reg_file_100[] =
+ {
+ /* Common CMU block */
+ { 0x000 << 2, 0x06},
+ { 0x001 << 2, 0x00},
+ { 0x002 << 2, 0x09}, //
+ { 0x003 << 2, 0x00},
+ { 0x004 << 2, 0x00},
+ { 0x005 << 2, 0x00},
+ { 0x006 << 2, 0x00},
+ { 0x007 << 2, 0x00},
+ { 0x008 << 2, 0x00},
+ { 0x009 << 2, 0x00},
+ { 0x00A << 2, 0x00},
+ { 0x00B << 2, 0x00},
+ { 0x00C << 2, 0x00},
+ { 0x00D << 2, 0x00},
+ { 0x00E << 2, 0x00},
+ { 0x00F << 2, 0x00},
+ { 0x010 << 2, 0x00},
+ { 0x011 << 2, 0x00},
+ { 0x012 << 2, 0x00},
+ { 0x013 << 2, 0x00},
+ { 0x014 << 2, 0x00},
+ { 0x015 << 2, 0x00},
+ { 0x016 << 2, 0x00},
+ { 0x017 << 2, 0x00},
+ { 0x018 << 2, 0x00},
+ { 0x019 << 2, 0x00},
+ { 0x01A << 2, 0x00},
+ { 0x01B << 2, 0x00},
+ { 0x01C << 2, 0x00},
+ { 0x01D << 2, 0x00},
+ { 0x01E << 2, 0x00},
+ { 0x01F << 2, 0x00},
+ { 0x020 << 2, 0x00},
+ { 0x021 << 2, 0x00},
+ { 0x022 << 2, 0xa0},
+ { 0x023 << 2, 0x64},
+ { 0x024 << 2, 0x00},
+ { 0x025 << 2, 0x00},
+ { 0x026 << 2, 0x00},
+ { 0x027 << 2, 0x00},
+ { 0x028 << 2, 0x00},
+ { 0x029 << 2, 0x00},
+ { 0x02A << 2, 0x00},
+ { 0x02B << 2, 0x00},
+ { 0x02C << 2, 0x00},
+ { 0x02D << 2, 0x00},
+ { 0x02E << 2, 0x04},
+ { 0x02F << 2, 0x50},
+ { 0x030 << 2, 0x70},
+ { 0x031 << 2, 0x02},
+ { 0x032 << 2, 0x25},
+ { 0x033 << 2, 0x40},
+ { 0x034 << 2, 0x01},
+ { 0x035 << 2, 0x40},
+ { 0x036 << 2, 0x00},
+ { 0x037 << 2, 0x00},
+ { 0x038 << 2, 0x00},
+ { 0x039 << 2, 0x00},
+ { 0x03A << 2, 0x00},
+ { 0x03B << 2, 0x00},
+ { 0x03C << 2, 0x00},
+ { 0x03D << 2, 0x00},
+ { 0x03E << 2, 0x00},
+ { 0x03F << 2, 0x00},
+ { 0x040 << 2, 0x00},
+ { 0x041 << 2, 0x00},
+ { 0x042 << 2, 0x00},
+ { 0x043 << 2, 0x00},
+ { 0x044 << 2, 0x00},
+ { 0x045 << 2, 0x00},
+ { 0x046 << 2, 0x00},
+ { 0x047 << 2, 0x00},
+ { 0x048 << 2, 0x00},
+ { 0x049 << 2, 0x00},
+ { 0x04A << 2, 0x00},
+ { 0x04B << 2, 0x00},
+ { 0x04C << 2, 0x00},
+ { 0x04D << 2, 0x00},
+ { 0x04E << 2, 0x00},
+ { 0x04F << 2, 0x00},
+ { 0x050 << 2, 0x00},
+ { 0x051 << 2, 0x00},
+ { 0x052 << 2, 0x00},
+ { 0x053 << 2, 0x00},
+ { 0x054 << 2, 0x00},
+ { 0x055 << 2, 0x00},
+ { 0x056 << 2, 0x00},
+ { 0x057 << 2, 0x00},
+ { 0x058 << 2, 0x00},
+ { 0x059 << 2, 0x00},
+ { 0x05A << 2, 0x00},
+ { 0x05B << 2, 0x00},
+ { 0x05C << 2, 0x00},
+ { 0x05D << 2, 0x00},
+ { 0x05E << 2, 0x00},
+ { 0x05F << 2, 0x00},
+ { 0x060 << 2, 0x00},
+ { 0x061 << 2, 0x2e},//for Rev1 modified in code
+ { 0x062 << 2, 0x00},
+ { 0x063 << 2, 0x5e},
+ { 0x064 << 2, 0x00},
+ { 0x065 << 2, 0x42},
+ { 0x066 << 2, 0xd1},
+ { 0x067 << 2, 0x90},
+ { 0x068 << 2, 0x08},
+ { 0x069 << 2, 0x50},
+ { 0x06A << 2, 0x44},
+ { 0x06B << 2, 0xce},
+ { 0x06C << 2, 0x0b},
+ { 0x06D << 2, 0x00},
+ { 0x06E << 2, 0x00},
+ { 0x06F << 2, 0x00},
+ { 0x070 << 2, 0x00},
+ { 0x071 << 2, 0x00},
+ { 0x072 << 2, 0x00},
+ /* Lane0 Block */
+ { 0x200 << 2, 0x00},
+ { 0x201 << 2, 0x00},
+ { 0x202 << 2, 0x00},
+ { 0x203 << 2, 0x00},
+ { 0x204 << 2, 0x00},
+ { 0x205 << 2, 0x10},
+ { 0x206 << 2, 0x04},
+ { 0x207 << 2, 0x18},
+ { 0x208 << 2, 0xe0},
+ { 0x210 << 2, 0x23},
+ { 0x211 << 2, 0x00},
+ { 0x212 << 2, 0x00},
+ { 0x213 << 2, 0x03},
+ { 0x214 << 2, 0x3C},
+ { 0x215 << 2, 0x04},
+ { 0x216 << 2, 0x00},
+ { 0x217 << 2, 0x68},
+ { 0x218 << 2, 0xa2},
+ { 0x219 << 2, 0x1e},
+ { 0x21A << 2, 0x18},
+ { 0x21B << 2, 0x0d},
+ { 0x21C << 2, 0x0d},
+ { 0x21D << 2, 0x00},
+ { 0x21E << 2, 0x00},
+ { 0x21F << 2, 0x00},
+ { 0x220 << 2, 0x00},
+ { 0x221 << 2, 0x00},
+ { 0x222 << 2, 0x00},
+ { 0x223 << 2, 0x00},
+ { 0x224 << 2, 0x00},
+ { 0x225 << 2, 0x00},
+ { 0x226 << 2, 0x00},
+ { 0x227 << 2, 0x00},
+ { 0x228 << 2, 0x00},
+ { 0x229 << 2, 0x00},
+ { 0x22A << 2, 0x00},
+ { 0x22B << 2, 0x00},
+ { 0x22C << 2, 0x00},
+ { 0x22D << 2, 0x00},
+ { 0x22E << 2, 0x00},
+ { 0x22F << 2, 0x00},
+ { 0x230 << 2, 0x00},
+ { 0x231 << 2, 0x00},
+ { 0x232 << 2, 0x00},
+ { 0x233 << 2, 0x00},
+ { 0x234 << 2, 0x00},
+ { 0x235 << 2, 0x00},
+ { 0x236 << 2, 0x00},
+ { 0x237 << 2, 0x00},
+ { 0x238 << 2, 0x00},
+ { 0x239 << 2, 0x00},
+ { 0x23A << 2, 0x00},
+ { 0x23B << 2, 0x00},
+ { 0x23C << 2, 0x00},
+ { 0x23D << 2, 0x00},
+ { 0x23E << 2, 0x00},
+ { 0x23F << 2, 0x00},
+ { 0x240 << 2, 0x00},
+ { 0x241 << 2, 0x00},
+ { 0x242 << 2, 0x00},
+ { 0x243 << 2, 0x00},
+ { 0x244 << 2, 0x00},
+ { 0x245 << 2, 0x00},
+ { 0x246 << 2, 0x00},
+ { 0x247 << 2, 0x00},
+ { 0x248 << 2, 0x00},
+ { 0x249 << 2, 0x00},
+ { 0x24A << 2, 0x00},
+ { 0x24B << 2, 0x00},
+ { 0x24C << 2, 0x00},
+ { 0x24D << 2, 0x00},
+ { 0x24E << 2, 0x00},
+ { 0x24F << 2, 0x00},
+ { 0x250 << 2, 0xf6},
+ { 0x251 << 2, 0x03},
+ /* Common Lane Block */
+ { 0xA00 << 2, 0xc0},
+ { 0xA01 << 2, 0x90},
+ { 0xA02 << 2, 0x02},
+ { 0xA03 << 2, 0x40},
+ { 0xA04 << 2, 0x3c},
+ { 0xA05 << 2, 0x00},
+ { 0xA06 << 2, 0x00},
+ { 0xA07 << 2, 0x00},
+ { 0xA08 << 2, 0x00},
+ { 0xA09 << 2, 0x63}, //
+ { 0xA0A << 2, 0x49}, //
+ { 0xA0B << 2, 0xc6},
+ { 0xA0C << 2, 0x01},
+ { 0xA0D << 2, 0x03},
+ { 0xA0E << 2, 0x28},
+ { 0xA0F << 2, 0x98},
+ { 0xA10 << 2, 0x19},
+ { 0xA11 << 2, 0x28},
+ { 0xA12 << 2, 0x78},
+ { 0xA13 << 2, 0xe1},
+ { 0xA14 << 2, 0xf0},
+ { 0xA15 << 2, 0x10},
+ { 0xA16 << 2, 0xf4},
+ { 0xA17 << 2, 0x00},
+ { 0xA30 << 2, 0x00},
+ { 0xA31 << 2, 0x00},
+ { 0xA32 << 2, 0x00},
+ { 0xA33 << 2, 0x00},
+ { 0xA34 << 2, 0x00},
+ { 0xA35 << 2, 0x00},
+ { 0xA36 << 2, 0x00},
+ { 0xA37 << 2, 0x00},
+ { 0xA38 << 2, 0x00},
+ { 0xA39 << 2, 0xa0},
+ { 0xA3A << 2, 0xa0},
+ { 0xA3B << 2, 0xa0},
+ { 0xA3C << 2, 0xa0},
+ { 0xA3D << 2, 0xa0},
+ { 0xA3E << 2, 0xa0},
+ { 0xA3F << 2, 0xa0},
+ { 0xA40 << 2, 0x64},
+ { 0xA41 << 2, 0x00},
+ { 0xA42 << 2, 0xc0},
+ { 0xA43 << 2, 0x9f},
+ { 0xA44 << 2, 0x01},
+ { 0xA45 << 2, 0x00},
+ { 0xA46 << 2, 0x00},
+ { 0xA47 << 2, 0x00},
+ { 0xA48 << 2, 0x00},
+ { 0xA49 << 2, 0x00},
+ { 0xA4A << 2, 0x00},
+ { 0xA4B << 2, 0x00},
+ { 0xA4C << 2, 0x30},
+ { 0xA4D << 2, 0x41},
+ { 0xA4E << 2, 0x7e},
+ { 0xA4F << 2, 0xd0},
+ { 0xA50 << 2, 0xcc},
+ { 0xA51 << 2, 0x85},
+ { 0xA52 << 2, 0x52},
+ { 0xA53 << 2, 0x93},
+ { 0xA54 << 2, 0xe0},
+ { 0xA55 << 2, 0x49},
+ { 0xA56 << 2, 0xdd},
+ { 0xA57 << 2, 0xb0},
+ { 0xA58 << 2, 0x0b},
+ { 0xA59 << 2, 0x02},
+ { 0xA5A << 2, 0x00},
+ { 0xA5B << 2, 0x00},
+ { 0xA5C << 2, 0x00},
+ { 0xA5D << 2, 0x00},
+ { 0xA5E << 2, 0x00},
+ { 0xA5F << 2, 0x00},
+ { 0xA60 << 2, 0x00},
+ { 0xA61 << 2, 0x00},
+ { 0xA62 << 2, 0x00},
+ { 0xA63 << 2, 0x00},
+ { 0xA64 << 2, 0x00},
+ { 0xA65 << 2, 0x00},
+ { 0xA66 << 2, 0x00},
+ { 0xA67 << 2, 0x00},
+ { 0xA68 << 2, 0x00},
+ { 0xA69 << 2, 0x00},
+ { 0xA6A << 2, 0x00},
+ { 0xA6B << 2, 0x00},
+ { 0xA6C << 2, 0x00},
+ { 0xA6D << 2, 0x00},
+ { 0xA6E << 2, 0x00},
+ { 0xA6F << 2, 0x00},
+ { 0xA70 << 2, 0x00},
+ { 0xA71 << 2, 0x00},
+ { 0xA72 << 2, 0x00},
+ { 0xA73 << 2, 0x00},
+ { 0xA74 << 2, 0x00},
+ { 0xA75 << 2, 0x00},
+ { 0xA76 << 2, 0x00},
+ { 0xA77 << 2, 0x00},
+ { 0xA78 << 2, 0x00},
+ { 0xA79 << 2, 0x00},
+ { 0xA7A << 2, 0x00},
+ { 0xA7B << 2, 0x00},
+ { 0xA7C << 2, 0x00},
+ { 0xA7D << 2, 0x00},
+ { 0xA7E << 2, 0x00},
+ { 0xA7F << 2, 0xd8},
+ { 0xA80 << 2, 0x1a},
+ { 0xA81 << 2, 0xff},
+ { 0xA82 << 2, 0x01},
+ { 0xA83 << 2, 0x00},
+ { 0xA84 << 2, 0x00},
+ { 0xA85 << 2, 0x00},
+ { 0xA86 << 2, 0x00},
+ { 0xA87 << 2, 0xf0},
+ { 0xA88 << 2, 0xff},
+ { 0xA89 << 2, 0xff},
+ { 0xA8A << 2, 0xff},
+ { 0xA8B << 2, 0xff},
+ { 0xA8C << 2, 0x1c},
+ { 0xA8D << 2, 0xc2},
+ { 0xA8E << 2, 0xc3},
+ { 0xA8F << 2, 0x3f},
+ { 0xA90 << 2, 0x0a},
+ { 0xA91 << 2, 0x00},
+ { 0xA92 << 2, 0x00},
+ { 0xA93 << 2, 0x00},
+ { 0xA94 << 2, 0x00},
+ { 0xA95 << 2, 0x00},
+ { 0xA96 << 2, 0xf8},
+ { 0x000 << 2, 0x07}
+ };
+
+
+static struct serdes_regs_s pcie_phy_reg_file_24[] =
+/* PCIe SERDES is using internal clock */
+ {
+ /* Common CMU block */
+ { 0x000 << 2, 0x06},
+ { 0x001 << 2, 0x00},
+ { 0x002 << 2, 0x09}, //
+ { 0x003 << 2, 0x00},
+ { 0x004 << 2, 0x60}, //
+ { 0x005 << 2, 0x09}, //
+ { 0x006 << 2, 0x0e}, //
+ { 0x007 << 2, 0x00},
+ { 0x008 << 2, 0x00},
+ { 0x009 << 2, 0x00},
+ { 0x00A << 2, 0x00},
+ { 0x00B << 2, 0x00},
+ { 0x00C << 2, 0x00},
+ { 0x00D << 2, 0x00},
+ { 0x00E << 2, 0x00},
+ { 0x00F << 2, 0x00},
+ { 0x010 << 2, 0x00},
+ { 0x011 << 2, 0x00},
+ { 0x012 << 2, 0x00},
+ { 0x013 << 2, 0x00},
+ { 0x014 << 2, 0x00},
+ { 0x015 << 2, 0x00},
+ { 0x016 << 2, 0x00},
+ { 0x017 << 2, 0x00},
+ { 0x018 << 2, 0x00},
+ { 0x019 << 2, 0x00},
+ { 0x01A << 2, 0x00},
+ { 0x01B << 2, 0x00},
+ { 0x01C << 2, 0x00},
+ { 0x01D << 2, 0x00},
+ { 0x01E << 2, 0x00},
+ { 0x01F << 2, 0x00},
+ { 0x020 << 2, 0x00},
+ { 0x021 << 2, 0x00},
+ { 0x022 << 2, 0xa0},
+ { 0x023 << 2, 0x68}, //
+ { 0x024 << 2, 0x00},
+ { 0x025 << 2, 0x00},
+ { 0x026 << 2, 0x00},
+ { 0x027 << 2, 0x00},
+ { 0x028 << 2, 0x00},
+ { 0x029 << 2, 0x00},
+ { 0x02A << 2, 0x00},
+ { 0x02B << 2, 0x00},
+ { 0x02C << 2, 0x00},
+ { 0x02D << 2, 0x00},
+ { 0x02E << 2, 0x04},
+ { 0x02F << 2, 0x50},
+ { 0x030 << 2, 0x70},
+ { 0x031 << 2, 0x02},
+ { 0x032 << 2, 0x25},
+ { 0x033 << 2, 0x40},
+ { 0x034 << 2, 0x01},
+ { 0x035 << 2, 0x40},
+ { 0x036 << 2, 0x00},
+ { 0x037 << 2, 0x00},
+ { 0x038 << 2, 0x00},
+ { 0x039 << 2, 0x00},
+ { 0x03A << 2, 0x00},
+ { 0x03B << 2, 0x00},
+ { 0x03C << 2, 0x00},
+ { 0x03D << 2, 0x00},
+ { 0x03E << 2, 0x00},
+ { 0x03F << 2, 0x00},
+ { 0x040 << 2, 0x00},
+ { 0x041 << 2, 0x00},
+ { 0x042 << 2, 0x00},
+ { 0x043 << 2, 0x00},
+ { 0x044 << 2, 0x00},
+ { 0x045 << 2, 0x00},
+ { 0x046 << 2, 0x00},
+ { 0x047 << 2, 0x00},
+ { 0x048 << 2, 0x00},
+ { 0x049 << 2, 0x00},
+ { 0x04A << 2, 0x00},
+ { 0x04B << 2, 0x00},
+ { 0x04C << 2, 0x00},
+ { 0x04D << 2, 0x00},
+ { 0x04E << 2, 0x00},
+ { 0x04F << 2, 0x00},
+ { 0x050 << 2, 0x00},
+ { 0x051 << 2, 0x00},
+ { 0x052 << 2, 0x00},
+ { 0x053 << 2, 0x00},
+ { 0x054 << 2, 0x00},
+ { 0x055 << 2, 0x00},
+ { 0x056 << 2, 0x00},
+ { 0x057 << 2, 0x00},
+ { 0x058 << 2, 0x00},
+ { 0x059 << 2, 0x00},
+ { 0x05A << 2, 0x00},
+ { 0x05B << 2, 0x00},
+ { 0x05C << 2, 0x00},
+ { 0x05D << 2, 0x00},
+ { 0x05E << 2, 0x00},
+ { 0x05F << 2, 0x00},
+ { 0x060 << 2, 0x00},
+ { 0x061 << 2, 0x2e}, //for Rev-A0 device
+ { 0x062 << 2, 0x00}, //
+ { 0x063 << 2, 0x5e},
+ { 0x064 << 2, 0x00},
+ { 0x065 << 2, 0x42},
+ { 0x066 << 2, 0x91},
+ { 0x067 << 2, 0x10}, //
+ { 0x068 << 2, 0x48},
+ { 0x069 << 2, 0x90}, //
+ { 0x06A << 2, 0x0c}, //
+ { 0x06B << 2, 0x4c}, //
+ { 0x06C << 2, 0x73}, //
+ { 0x06D << 2, 0x03}, //
+ { 0x06E << 2, 0x00},
+ { 0x06F << 2, 0x00},
+ { 0x070 << 2, 0x00},
+ { 0x071 << 2, 0x00},
+ { 0x072 << 2, 0x00},
+ /* Lane0 Block */
+ { 0x200 << 2, 0x00},
+ { 0x201 << 2, 0x00},
+ { 0x202 << 2, 0x00},
+ { 0x203 << 2, 0x00},
+ { 0x204 << 2, 0x00},
+ { 0x205 << 2, 0x10},
+ { 0x206 << 2, 0x04},
+ { 0x207 << 2, 0x18},
+ { 0x208 << 2, 0xe0},
+ { 0x210 << 2, 0x23},
+ { 0x211 << 2, 0x00},
+ { 0x212 << 2, 0x00},
+ { 0x213 << 2, 0x04}, //
+ { 0x214 << 2, 0x38}, //
+ { 0x215 << 2, 0x10}, //
+ { 0x216 << 2, 0x00},
+ { 0x217 << 2, 0x68},
+ { 0x218 << 2, 0xa2},
+ { 0x219 << 2, 0x1e},
+ { 0x21A << 2, 0x18},
+ { 0x21B << 2, 0x0d},
+ { 0x21C << 2, 0x0c},
+ { 0x21D << 2, 0x00},
+ { 0x21E << 2, 0x00},
+ { 0x21F << 2, 0x00},
+ { 0x220 << 2, 0x00},
+ { 0x221 << 2, 0x00},
+ { 0x222 << 2, 0x00},
+ { 0x223 << 2, 0x00},
+ { 0x224 << 2, 0x00},
+ { 0x225 << 2, 0x00},
+ { 0x226 << 2, 0x00},
+ { 0x227 << 2, 0x00},
+ { 0x228 << 2, 0x00},
+ { 0x229 << 2, 0x00},
+ { 0x22A << 2, 0x00},
+ { 0x22B << 2, 0x00},
+ { 0x22C << 2, 0x00},
+ { 0x22D << 2, 0x00},
+ { 0x22E << 2, 0x00},
+ { 0x22F << 2, 0x00},
+ { 0x230 << 2, 0x00},
+ { 0x231 << 2, 0x00},
+ { 0x232 << 2, 0x00},
+ { 0x233 << 2, 0x00},
+ { 0x234 << 2, 0x00},
+ { 0x235 << 2, 0x00},
+ { 0x236 << 2, 0x00},
+ { 0x237 << 2, 0x00},
+ { 0x238 << 2, 0x00},
+ { 0x239 << 2, 0x00},
+ { 0x23A << 2, 0x00},
+ { 0x23B << 2, 0x00},
+ { 0x23C << 2, 0x00},
+ { 0x23D << 2, 0x00},
+ { 0x23E << 2, 0x00},
+ { 0x23F << 2, 0x00},
+ { 0x240 << 2, 0x00},
+ { 0x241 << 2, 0x00},
+ { 0x242 << 2, 0x00},
+ { 0x243 << 2, 0x00},
+ { 0x244 << 2, 0x00},
+ { 0x245 << 2, 0x00},
+ { 0x246 << 2, 0x00},
+ { 0x247 << 2, 0x00},
+ { 0x248 << 2, 0x00},
+ { 0x249 << 2, 0x00},
+ { 0x24A << 2, 0x00},
+ { 0x24B << 2, 0x00},
+ { 0x24C << 2, 0x00},
+ { 0x24D << 2, 0x00},
+ { 0x24E << 2, 0x00},
+ { 0x24F << 2, 0x00},
+ { 0x250 << 2, 0xf6},
+ { 0x251 << 2, 0x03},
+ /* Common Lane Block */
+ { 0xA00 << 2, 0xc0},
+ { 0xA01 << 2, 0x90},
+ { 0xA02 << 2, 0x02},
+ { 0xA03 << 2, 0x40},
+ { 0xA04 << 2, 0x3c},
+ { 0xA05 << 2, 0x00},
+ { 0xA06 << 2, 0x00},
+ { 0xA07 << 2, 0x00},
+ { 0xA08 << 2, 0x00},
+ { 0xA09 << 2, 0x83}, //
+ { 0xA0A << 2, 0x8b}, //
+ { 0xA0B << 2, 0xc6},
+ { 0xA0C << 2, 0x01},
+ { 0xA0D << 2, 0x03},
+ { 0xA0E << 2, 0x28},
+ { 0xA0F << 2, 0x98},
+ { 0xA10 << 2, 0x19},
+ { 0xA11 << 2, 0x28},
+ { 0xA12 << 2, 0x78},
+ { 0xA13 << 2, 0xe1},
+ { 0xA14 << 2, 0xf0},
+ { 0xA15 << 2, 0x10},
+ { 0xA16 << 2, 0xf4},
+ { 0xA17 << 2, 0x00},
+ { 0xA30 << 2, 0x00},
+ { 0xA31 << 2, 0x00},
+ { 0xA32 << 2, 0x00},
+ { 0xA33 << 2, 0x00},
+ { 0xA34 << 2, 0x00},
+ { 0xA35 << 2, 0x00},
+ { 0xA36 << 2, 0x00},
+ { 0xA37 << 2, 0x00},
+ { 0xA38 << 2, 0x00},
+ { 0xA39 << 2, 0xa0},
+ { 0xA3A << 2, 0xa0},
+ { 0xA3B << 2, 0xa0},
+ { 0xA3C << 2, 0xa0},
+ { 0xA3D << 2, 0xa0},
+ { 0xA3E << 2, 0xa0},
+ { 0xA3F << 2, 0xa0},
+ { 0xA40 << 2, 0x68}, //
+ { 0xA41 << 2, 0x00},
+ { 0xA42 << 2, 0xc0},
+ { 0xA43 << 2, 0x9f},
+ { 0xA44 << 2, 0x01},
+ { 0xA45 << 2, 0x00},
+ { 0xA46 << 2, 0x00},
+ { 0xA47 << 2, 0x00},
+ { 0xA48 << 2, 0x00},
+ { 0xA49 << 2, 0x00},
+ { 0xA4A << 2, 0x00},
+ { 0xA4B << 2, 0x00},
+ { 0xA4C << 2, 0x30},
+ { 0xA4D << 2, 0x41},
+ { 0xA4E << 2, 0x7e},
+ { 0xA4F << 2, 0xd0},
+ { 0xA50 << 2, 0xcc},
+ { 0xA51 << 2, 0x85},
+ { 0xA52 << 2, 0x52},
+ { 0xA53 << 2, 0x93},
+ { 0xA54 << 2, 0xe0},
+ { 0xA55 << 2, 0x49},
+ { 0xA56 << 2, 0xdd},
+ { 0xA57 << 2, 0xb0},
+ { 0xA58 << 2, 0x0b},
+ { 0xA59 << 2, 0x02},
+ { 0xA5A << 2, 0x00},
+ { 0xA5B << 2, 0x00},
+ { 0xA5C << 2, 0x00},
+ { 0xA5D << 2, 0x00},
+ { 0xA5E << 2, 0x00},
+ { 0xA5F << 2, 0x00},
+ { 0xA60 << 2, 0x00},
+ { 0xA61 << 2, 0x00},
+ { 0xA62 << 2, 0x00},
+ { 0xA63 << 2, 0x00},
+ { 0xA64 << 2, 0x00},
+ { 0xA65 << 2, 0x00},
+ { 0xA66 << 2, 0x00},
+ { 0xA67 << 2, 0x00},
+ { 0xA68 << 2, 0x00},
+ { 0xA69 << 2, 0x00},
+ { 0xA6A << 2, 0x00},
+ { 0xA6B << 2, 0x00},
+ { 0xA6C << 2, 0x00},
+ { 0xA6D << 2, 0x00},
+ { 0xA6E << 2, 0x00},
+ { 0xA6F << 2, 0x00},
+ { 0xA70 << 2, 0x00},
+ { 0xA71 << 2, 0x00},
+ { 0xA72 << 2, 0x00},
+ { 0xA73 << 2, 0x00},
+ { 0xA74 << 2, 0x00},
+ { 0xA75 << 2, 0x00},
+ { 0xA76 << 2, 0x00},
+ { 0xA77 << 2, 0x00},
+ { 0xA78 << 2, 0x00},
+ { 0xA79 << 2, 0x00},
+ { 0xA7A << 2, 0x00},
+ { 0xA7B << 2, 0x00},
+ { 0xA7C << 2, 0x00},
+ { 0xA7D << 2, 0x00},
+ { 0xA7E << 2, 0x00},
+ { 0xA7F << 2, 0xd8},
+ { 0xA80 << 2, 0x1a},
+ { 0xA81 << 2, 0xff},
+ { 0xA82 << 2, 0x01},
+ { 0xA83 << 2, 0x00},
+ { 0xA84 << 2, 0x00},
+ { 0xA85 << 2, 0x00},
+ { 0xA86 << 2, 0x00},
+ { 0xA87 << 2, 0xf0},
+ { 0xA88 << 2, 0xff},
+ { 0xA89 << 2, 0xff},
+ { 0xA8A << 2, 0xff},
+ { 0xA8B << 2, 0xff},
+ { 0xA8C << 2, 0x1c},
+ { 0xA8D << 2, 0xc2},
+ { 0xA8E << 2, 0xc3},
+ { 0xA8F << 2, 0x3f},
+ { 0xA90 << 2, 0x0a},
+ { 0xA91 << 2, 0x00},
+ { 0xA92 << 2, 0x00},
+ { 0xA93 << 2, 0x00},
+ { 0xA94 << 2, 0x00},
+ { 0xA95 << 2, 0x00},
+ { 0xA96 << 2, 0xf8},
+ { 0x000 << 2, 0x07}
+ };
+
+
diff --git a/arch/arm/mach-comcerto/include/mach/serial.h b/arch/arm/mach-comcerto/include/mach/serial.h
new file mode 100644
index 0000000..1fea3f0
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/serial.h
@@ -0,0 +1,64 @@
+/* UART */
+#define LCR_CHAR_LEN_5 0x00 /* default */
+#define LCR_CHAR_LEN_6 0x01
+#define LCR_CHAR_LEN_7 0x02
+#define LCR_CHAR_LEN_8 0x03
+#define LCR_ONE_STOP 0x00 /* One stop bit! - default */
+#define LCR_TWO_STOP 0x04 /* Two stop bit! */
+#define LCR_PEN 0x08 /* Parity Enable */
+#define LCR_PARITY_NONE 0x00
+#define LCR_EPS 0x10 /* Even Parity Select */
+#define LCR_PS 0x20 /* Enable Parity Stuff */
+#define LCR_SBRK 0x40 /* Start Break */
+#define LCR_PSB 0x80 /* Parity Stuff Bit */
+#define LCR_DLAB 0x80 /* UART 16550 Divisor Latch Assess */
+
+#define LSR_FIFOE (1 << 7) /* FIFO Error Status */
+#define LSR_TEMT (1 << 6) /* Transmitter Empty */
+#define LSR_TDRQ (1 << 5) /* Transmit Data Request */
+#define LSR_BI (1 << 4) /* Break Interrupt */
+#define LSR_FE (1 << 3) /* Framing Error */
+#define LSR_PE (1 << 2) /* Parity Error */
+#define LSR_OE (1 << 1) /* Overrun Error */
+#define LSR_DR (1 << 0) /* Data Ready */
+
+#define IER_DMAE (1 << 7) /* DMA Requests Enable */
+#define IER_UUE (1 << 6) /* UART Unit Enable */
+#define IER_NRZE (1 << 5) /* NRZ coding Enable */
+#define IER_RTIOE (1 << 4) /* Receiver Time Out Interrupt Enable */
+#define IER_MIE (1 << 3) /* Modem Interrupt Enable */
+#define IER_RLSE (1 << 2) /* Receiver Line Status Interrupt Enable */
+#define IER_TIE (1 << 1) /* Transmit Data request Interrupt Enable */
+#define IER_RAVIE (1 << 0) /* Receiver Data Available Interrupt Enable */
+
+#define IIR_FIFOES1 (1 << 7) /* FIFO Mode Enable Status */
+#define IIR_FIFOES0 (1 << 6) /* FIFO Mode Enable Status */
+#define IIR_TOD (1 << 3) /* Time Out Detected */
+#define IIR_IID2 (1 << 2) /* Interrupt Source Encoded */
+#define IIR_IID1 (1 << 1) /* Interrupt Source Encoded */
+#define IIR_IP (1 << 0) /* Interrupt Pending (active low) */
+
+/* UART 16550 FIFO Control Register */
+#define FCR_FIFOEN 0x01
+#define FCR_RCVRRES 0x02
+#define FCR_XMITRES 0x04
+
+/* Interrupt Enable Register */
+/* UART 16550 */
+#define IER_RXTH 0x01 /* Enable Received Data Available Interrupt */
+#define IER_TXTH 0x02 /* Enable Transmitter Empty Interrupt */
+
+#define UART_THR 0x00
+#define UART_RBR 0x00
+#define UART_DLL 0x00
+#define UART_IER 0x04
+#define UART_DLH 0x04
+#define UART_IIR 0x08
+#define UART_FCR 0x08
+#define UART_LCR 0x0C
+#define UART_MCR 0x10
+#define UART_LSR 0x14
+#define UART_MSR 0x18
+#define UART_SCR 0x1C
+
+
diff --git a/arch/arm/mach-comcerto/include/mach/spi.h b/arch/arm/mach-comcerto/include/mach/spi.h
new file mode 100644
index 0000000..7b303dd
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/spi.h
@@ -0,0 +1,21 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/irqs.h
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __ASM_ARCH_SPI_H
+#define __ASM_ARCH_SPI_H
+
+#if defined(CONFIG_ARCH_M86XX)
+ #include <mach/comcerto-2000/spi.h>
+#else
+ #error "Unsupported CPU"
+#endif
+
+#endif /* __ASM_ARCH_SPI_H */
diff --git a/arch/arm/mach-comcerto/include/mach/system.h b/arch/arm/mach-comcerto/include/mach/system.h
new file mode 100644
index 0000000..150b46c
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/system.h
@@ -0,0 +1,42 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/system.h
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H
+
+#include <mach/hardware.h>
+#include <mach/io.h>
+#include <mach/comcerto-2000/clk-rst.h>
+#include <mach/reset.h>
+
+static inline void arch_idle(void)
+{
+ /*
+ * This should do all the clock switching
+ * and wait for interrupt tricks
+ */
+ cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+ comcerto_rst_cntrl_set(PWR_ON_SOFT_RST);
+}
+
+#endif /* __ASM_ARCH_SYSTEM_H */
diff --git a/arch/arm/mach-comcerto/include/mach/timex.h b/arch/arm/mach-comcerto/include/mach/timex.h
new file mode 100644
index 0000000..35ba9b3
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/timex.h
@@ -0,0 +1,28 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/timex.h
+ *
+ * Copyright (C) 2006 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARCH_TIMEX_H__
+#define __ASM_ARCH_TIMEX_H__
+
+ #include <mach/hardware.h>
+
+ #define CLOCK_TICK_RATE COMCERTO_DEFAULTAXICLK
+
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/uncompress.h b/arch/arm/mach-comcerto/include/mach/uncompress.h
new file mode 100644
index 0000000..f3f0c4b
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/uncompress.h
@@ -0,0 +1,52 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/uncompress.h
+ *
+ * Copyright (C) 2004,2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_UNCOMPRESS_H
+#define __ASM_ARCH_UNCOMPRESS_H
+#include <asm/byteorder.h>
+
+#ifdef CONFIG_COMCERTO_UART1_SUPPORT
+ #define UART_DR (*(volatile unsigned long *)0x96400000)
+ #define UART_LSR (*(volatile unsigned long *)0x96400014)
+#elif CONFIG_COMCERTO_UART0_SUPPORT
+ #define UART_DR (*(volatile unsigned long *)0x96300000)
+ #define UART_LSR (*(volatile unsigned long *)0x96300014)
+#else
+ #error no uart configured
+#endif
+
+static inline void putc(int c)
+{
+ while (!( __cpu_to_le32(UART_LSR) & 0x20))
+ barrier();
+ UART_DR = __cpu_to_le32(c);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+
+#define arch_decomp_wdog()
+
+#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-comcerto/include/mach/vmalloc.h b/arch/arm/mach-comcerto/include/mach/vmalloc.h
new file mode 100644
index 0000000..daed580
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/vmalloc.h
@@ -0,0 +1,26 @@
+/*
+ * arch/arm/mach-comcerto/include/mach/vmalloc.h
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#if defined(CONFIG_ARCH_M86XXX)
+#define VMALLOC_END (0xF0000000)
+#else
+#error "Unsupported CPU"
+#endif
diff --git a/arch/arm/mach-comcerto/include/mach/wdt.h b/arch/arm/mach-comcerto/include/mach/wdt.h
new file mode 100644
index 0000000..7090906
--- /dev/null
+++ b/arch/arm/mach-comcerto/include/mach/wdt.h
@@ -0,0 +1,22 @@
+/*
+ * linux/include/asm-arm/arch-comcerto/irqs.h
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __ASM_ARCH_WDT_H
+#define __ASM_ARCH_WDT_H
+
+
+#if defined(CONFIG_ARCH_M86XXX)
+ #include <mach/comcerto-2000/wdt.h>
+#else
+ #error "mach/comcerto-2000/wdt.h : Unknown architecture"
+#endif
+
+#endif /* __ASM_ARCH_WDT_H */
diff --git a/arch/arm/mach-comcerto/membuf.c b/arch/arm/mach-comcerto/membuf.c
new file mode 100644
index 0000000..7cd3494
--- /dev/null
+++ b/arch/arm/mach-comcerto/membuf.c
@@ -0,0 +1,411 @@
+/*
+ * linux/arch/arm/mach-comcerto/membuf.c
+ *
+ * Copyright (C) 2010 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
+#include <asm/uaccess.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+#define sg_page(sg) ((sg)->page)
+
+#define sg_virt(sg) (page_address(sg_page(sg)) + (sg)->offset)
+#endif
+
+#define MAX_BUFFERS 48
+
+#define MEMBUF_MINOR 0
+#define MEMBUF_MINOR_COUNT 1
+#define MEMBUF_DEV_COUNT 1
+
+#define MEMBUF_DEFAULT_PG_ORDER 6
+
+#define MEMBUF_DRV_NAME "membuf"
+
+
+#define MEMBUF_GET_SCATTER _IOR('m', 1, struct usr_scatter_list)
+
+struct usr_scatter_list
+{
+ u8 entries;
+ u8 pg_order[MAX_BUFFERS];
+ u32 addr[MAX_BUFFERS];
+};
+
+struct membuf
+{
+ struct scatterlist sg[MAX_BUFFERS];
+ int sg_nr;
+
+ int size; /* Total memory in use for the buffer data */
+ int real_size; /* Total memory allocated for the scatter list */
+
+ int pg_order;
+
+ char mapped;
+};
+
+static struct membuf_dev
+{
+ struct cdev dev;
+ int devno;
+} membuf_dev;
+
+
+static int membuf_map(struct membuf *buf)
+{
+ int rc;
+
+ if (buf->mapped)
+ {
+ printk(KERN_ERR "%s: buffer already mapped\n", __func__);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = dma_map_sg(NULL, buf->sg, buf->sg_nr, DMA_TO_DEVICE);
+ if (rc != buf->sg_nr)
+ printk(KERN_ERR "%s: dma_map_sg() failed\n", __func__);
+ else
+ buf->mapped = 1;
+
+out:
+ return rc;
+}
+
+static void membuf_unmap(struct membuf *buf)
+{
+ if (!buf->mapped)
+ return;
+
+ dma_unmap_sg(NULL, buf->sg, buf->sg_nr, DMA_TO_DEVICE);
+
+ buf->mapped = 0;
+}
+
+static void membuf_free(struct membuf *buf)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for (i = 0, sg = buf->sg; i < buf->sg_nr; i++, sg++) {
+ __free_pages(sg_page(sg), get_order(sg->length));
+ }
+
+ buf->sg_nr = 0;
+ buf->size = 0;
+ buf->real_size = 0;
+ buf->pg_order = MEMBUF_DEFAULT_PG_ORDER;
+}
+
+static void membuf_sync(struct membuf *buf)
+{
+ dma_sync_sg_for_device(NULL, buf->sg, buf->sg_nr, DMA_TO_DEVICE);
+}
+
+static int membuf_expand(struct membuf *buf, int size)
+{
+ struct scatterlist *sg;
+ void *page;
+ int len;
+
+ membuf_unmap(buf);
+
+ while (size > 0)
+ {
+ if (buf->sg_nr >= MAX_BUFFERS) {
+ printk(KERN_ERR "%s: too many scatter entries(%d) failed\n", __func__, buf->sg_nr);
+ return -ENOMEM;
+ }
+
+ retry:
+ page = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN, buf->pg_order);
+ if (!page) {
+ if (buf->pg_order-- > 1)
+ goto retry;
+
+ printk(KERN_ERR "%s: __get_free_pages(%d) failed\n", __func__, buf->pg_order);
+ return -ENOMEM;
+ }
+
+ sg = &buf->sg[buf->sg_nr];
+
+ len = PAGE_SIZE * (1 << buf->pg_order);
+
+ sg_set_buf(sg, page, len);
+
+ buf->sg_nr++;
+
+ buf->real_size += len;
+
+ if (size > len)
+ buf->size += len;
+ else
+ buf->size += size;
+
+ size -= len;
+ }
+
+ return 0;
+}
+
+
+static int get_sg_index(struct membuf *buf, int *offset)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for (i = 0, sg = buf->sg; i < buf->sg_nr; i++, sg++) {
+ if (*offset < sg->length)
+ break;
+
+ *offset -= sg->length;
+ }
+
+// printk(KERN_INFO "%d %d\n", i, *offset);
+
+ return i;
+}
+
+
+static ssize_t membuf_write(struct file *file, const char __user *data, size_t count, loff_t *off)
+{
+ struct membuf *buf = (struct membuf *)file->private_data;
+ struct scatterlist *sg;
+ int sg_offset, sg_i;
+ int count_now;
+ int offset = 0;
+ int rc;
+
+// printk(KERN_INFO "%d\n", *off);
+
+ if ((*off + count) > buf->real_size) {
+
+ rc = membuf_expand(buf, (*off + count) - buf->real_size);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: membuf_expand() failed\n", __func__);
+ goto out;
+ }
+ }
+
+ sg_offset = *off;
+ sg_i = get_sg_index(buf, &sg_offset);
+ sg = &buf->sg[sg_i];
+
+ while (count) {
+
+ count_now = min(sg->length - sg_offset, count);
+
+ if (copy_from_user(sg_virt(sg) + sg_offset, data + offset, count_now)) {
+ printk(KERN_ERR "%s: copy_from_user() failed\n", __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ count -= count_now;
+ offset += count_now;
+ sg++;
+ sg_offset = 0;
+ }
+
+ *off += offset;
+ rc = offset;
+
+out:
+ return rc;
+}
+
+static loff_t membuf_llseek(struct file *file, loff_t offset, int origin)
+{
+ struct membuf *buf = (struct membuf *)file->private_data;
+ loff_t new_offset;
+ int rc;
+
+ switch (origin) {
+ case 0: /* SEEK_SET */
+ new_offset = offset;
+ break;
+
+ case 1: /* SEEK_CUR */
+ new_offset = file->f_pos + offset;
+ break;
+
+ case 2: /* SEEK_END */
+ new_offset = buf->size + offset;
+ break;
+
+ default:
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if (new_offset > buf->real_size) {
+ rc = membuf_expand(buf, new_offset - buf->real_size);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: membuf_expand() failed\n", __func__);
+ goto err;
+ }
+ }
+
+ file->f_pos = new_offset;
+
+ return new_offset;
+
+err:
+ return rc;
+}
+
+
+static long membuf_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct membuf *buf = (struct membuf *)file->private_data;
+ struct usr_scatter_list usr_sg;
+ struct scatterlist *sg;
+ int i;
+ int rc = 0;
+
+ switch (cmd) {
+ case MEMBUF_GET_SCATTER:
+
+ if (!buf->mapped)
+ {
+ rc = membuf_map(buf);
+ if (rc < 0)
+ break;
+ }
+
+ membuf_sync(buf);
+
+ usr_sg.entries = 0;
+ for (i = 0, sg = buf->sg; i < buf->sg_nr; i++, sg++) {
+ usr_sg.addr[usr_sg.entries] = sg_dma_address(sg);
+ usr_sg.pg_order[usr_sg.entries] = get_order(sg_dma_len(sg));
+
+/*
+ printk(KERN_INFO "%d %x %x %x %x %d\n", i, sg_virt(sg), sg_phys(sg),
+ sg_dma_address(sg), usr_sg.pg_order[usr_sg.entries],
+ sg->length);
+*/
+ usr_sg.entries++;
+ }
+
+ if (copy_to_user((void __user *)arg, &usr_sg, sizeof(struct usr_scatter_list))) {
+ printk(KERN_ERR "%s: copy_to_user() failed\n", __func__);
+ rc = -EFAULT;
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "%s: unknown ioctl(%d)\n", __func__, cmd);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+
+
+static int membuf_open(struct inode *in, struct file *file)
+{
+ struct membuf *buf;
+
+ buf = kzalloc(sizeof(struct membuf), GFP_KERNEL);
+ if (!buf) {
+ printk(KERN_ERR "%s: kmalloc() failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ buf->pg_order = MEMBUF_DEFAULT_PG_ORDER;
+
+ file->private_data = buf;
+
+ return 0;
+}
+
+
+static int membuf_release(struct inode *in, struct file *file)
+{
+ struct membuf *buf = (struct membuf *)file->private_data;
+
+ membuf_unmap(buf);
+ membuf_free(buf);
+
+ kfree(buf);
+
+ return 0;
+}
+
+
+static const struct file_operations membuf_fops = {
+ .owner = THIS_MODULE,
+ .write = membuf_write,
+ .llseek = membuf_llseek,
+ .unlocked_ioctl = membuf_ioctl,
+ .open = membuf_open,
+ .release = membuf_release,
+};
+
+static int __init membuf_init(void)
+{
+ int rc = 0;
+
+ rc = alloc_chrdev_region(&membuf_dev.devno, MEMBUF_MINOR, MEMBUF_MINOR_COUNT, MEMBUF_DRV_NAME);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: alloc_chrdev_region() failed\n", __func__);
+ goto err0;
+ }
+
+ cdev_init(&membuf_dev.dev, &membuf_fops);
+ membuf_dev.dev.owner = THIS_MODULE;
+
+ rc = cdev_add (&membuf_dev.dev, membuf_dev.devno, MEMBUF_DEV_COUNT);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: cdev_add() failed\n", __func__);
+ goto err1;
+ }
+
+ printk(KERN_INFO "%s: created membuf device(%d, %d)\n", __func__, MAJOR(membuf_dev.devno), MINOR(membuf_dev.devno));
+
+ return 0;
+
+err1:
+ unregister_chrdev_region(membuf_dev.devno, MEMBUF_DEV_COUNT);
+
+err0:
+ return rc;
+}
+
+static void __exit membuf_exit(void)
+{
+ cdev_del(&membuf_dev.dev);
+
+ unregister_chrdev_region(membuf_dev.devno, MEMBUF_DEV_COUNT);
+}
+
+
+module_init(membuf_init);
+module_exit(membuf_exit);
diff --git a/arch/arm/mach-comcerto/msp/Makefile b/arch/arm/mach-comcerto/msp/Makefile
new file mode 100644
index 0000000..c5e1af0
--- /dev/null
+++ b/arch/arm/mach-comcerto/msp/Makefile
@@ -0,0 +1,2 @@
+# Makefile Comcerto network firmware drivers
+obj-y += ved.o smi.o msp.o
diff --git a/arch/arm/mach-comcerto/msp/msp.c b/arch/arm/mach-comcerto/msp/msp.c
new file mode 100644
index 0000000..6a6affd
--- /dev/null
+++ b/arch/arm/mach-comcerto/msp/msp.c
@@ -0,0 +1,508 @@
+/*
+ * arch/arm/mach-comcerto/msp/msp.c
+ *
+ * Copyright (C) 2004,2008,2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <asm/fiq.h>
+#include <linux/platform_device.h>
+
+#include "ved.h"
+#include "msp.h"
+
+#define MSP_READY_DELAY (8000UL) /* msec */
+#define MSP_PROC_SECTION "msp_proc"
+#define VOIP_ENTRY_SECTION "voip_entries"
+
+#define MAGIC_LEN (0x24)
+#define MAGIC_OFF (0x20)
+
+#define MAX_SAVED_ALERT (20)
+
+#define COMCERTO_ATTR_SHOW(_name) \
+ static ssize_t comcerto_show_##_name(struct device *dev, struct device_attribute *attr, char *buf); \
+ static DEVICE_ATTR(_name, 0444, comcerto_show_##_name, NULL)
+
+#define COMCERTO_ATTR_SET(_name) \
+ static ssize_t comcerto_set_##_name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); \
+ static DEVICE_ATTR(_name, 0200, NULL, comcerto_set_##_name)
+
+#define COMCERTO_ATTR(_name) \
+ static ssize_t comcerto_show_##_name(struct device *dev, struct device_attribute *attr, char *buf); \
+ static ssize_t comcerto_set_##_name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); \
+ static DEVICE_ATTR(_name, 0644, comcerto_show_##_name, comcerto_set_##_name)
+
+COMCERTO_ATTR_SHOW(msp_info);
+COMCERTO_ATTR_SHOW(alert_info);
+COMCERTO_ATTR_SHOW(abi_rev);
+
+struct voip_fiq_code {
+ u32 len;
+ u8 data[MAGIC_LEN]; /* use hardcoded instruction, see below */
+};
+
+static int voip_fiq_op(void *p, int release);
+
+static struct fiq_handler voip_fiq_handler = {
+ .name = "Comcerto VoIPoFIQ",
+ .fiq_op = voip_fiq_op,
+ .dev_id = NULL
+};
+
+static void (*voip_entry)(void); /* VoIP code entry point */
+static void (*voip_exit)(void); /* VoIP code exit point */
+
+static void msp_poll(unsigned long arg);
+void update_pfe_status_for_MSP(void);
+static volatile struct msp_info * global_msp_info;
+
+struct pfe_info {
+ unsigned long buf_baseaddr;
+ unsigned long cbus_baseaddr;
+ void *owner;
+};
+
+struct tpfe_status {
+ unsigned long pfe_state;
+ unsigned long pfe_virt;
+ unsigned long axi_virt;
+ void *owner;
+} pfe_status;
+
+/* PFE inform MSP it is started and its parameters*/
+int msp_register_pfe(struct pfe_info *pfe_sync_info)
+{
+ /* save PFE status */
+ pfe_status.pfe_state = 1;
+ pfe_status.pfe_virt = pfe_sync_info->buf_baseaddr;
+ pfe_status.axi_virt = pfe_sync_info->cbus_baseaddr;
+ pfe_status.owner = pfe_sync_info->owner;
+
+ if (global_msp_info) {
+ /* msp is running, lock PFE, update PFE shared memory for MSP */
+ try_module_get(pfe_status.owner);
+ update_pfe_status_for_MSP();
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(msp_register_pfe);
+
+void update_pfe_status_for_MSP(void)
+{
+ global_msp_info->pfe_ready = pfe_status.pfe_state;
+ global_msp_info->pfe_virt = pfe_status.pfe_virt;
+ global_msp_info->pfe_phys = COMCERTO_PFE_DDR_BASE;
+ global_msp_info->axi_virt = pfe_status.axi_virt;
+ global_msp_info->axi_phys = COMCERTO_AXI_EXP_BASE;
+ global_msp_info->msp_virt = COMCERTO_MSP_VADDR;
+ global_msp_info->msp_phys = COMCERTO_MSP_DDR_BASE;
+}
+
+
+/* PFE inform MSP it is stopped */
+void msp_unregister_pfe(void)
+{
+ /* save PFE status */
+ pfe_status.pfe_state = 0;
+
+ if (global_msp_info) {
+ update_pfe_status_for_MSP();
+ }
+}
+EXPORT_SYMBOL(msp_unregister_pfe);
+
+static int voip_fiq_op(void *p, int release)
+{
+ return 0;
+}
+
+/**
+ * load_elf
+ *
+ *
+ */
+static int load_elf(struct _code_info *code_info)
+{
+ Elf32_Ehdr *this_elf_header = (Elf32_Ehdr *)(code_info->code);
+ Elf32_Half number_of_section = this_elf_header->e_shnum;
+ /* pointer to the section header */
+ Elf32_Shdr *this_section_headers = (Elf32_Shdr *)(code_info->code + this_elf_header->e_shoff);
+ Elf32_Off string_section_offset = this_section_headers[this_elf_header->e_shstrndx].sh_offset;
+ const char *section_name = NULL;
+ int rc = 0;
+ int i = 0;
+
+ if (!number_of_section) {
+ printk(KERN_ERR "error loading elf: number of section is zero\n");
+ rc = -1;
+
+ goto out;
+ }
+
+ /* parse all sections */
+ for (i = 0; i < number_of_section; i++) {
+ section_name = code_info->code + string_section_offset + this_section_headers[i].sh_name;
+
+ if (!strncmp(section_name, MSP_PROC_SECTION, strlen(MSP_PROC_SECTION))) {
+ code_info->proc_addr = this_section_headers[i].sh_addr;
+ }
+
+ if (!strncmp(section_name, VOIP_ENTRY_SECTION, strlen(VOIP_ENTRY_SECTION))) {
+ code_info->sym_addr = this_section_headers[i].sh_addr;
+ }
+
+ /* retrieve the section name from the ELF buffer */
+
+ if ((this_section_headers[i].sh_flags != SHF_MIPS_ADDR)
+ && this_section_headers[i].sh_flags
+ && strncmp(section_name, "CHECKSUM", 8))
+ {
+ /* retrieve the section name from the ELF buffer */
+
+ if ((this_section_headers[i].sh_type & 3) == SHT_PROGBITS) {
+ memcpy(
+ (void *)this_section_headers[i].sh_addr,
+ (void*)(code_info->code + this_section_headers[i].sh_offset),
+ this_section_headers[i].sh_size);
+ }
+ }
+ }
+
+ if (code_info->proc_addr && code_info->sym_addr) {
+ code_info->program_addr = this_elf_header->e_entry;
+ } else {
+ printk(KERN_ERR "error loading elf: could not find %s or %s\n", MSP_PROC_SECTION, VOIP_ENTRY_SECTION);
+
+ rc = -1;
+ }
+
+out:
+ return rc;
+}
+
+static int msp_ready(struct comcerto_msp *msp, unsigned long timeout)
+{
+ /* timeout is passed in ms, set it in jiffies */
+ timeout = jiffies + (timeout * HZ) / 1000;
+
+ while (!msp->info || !msp->info->ready) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+
+ if (time_after(jiffies, timeout) && (!msp->info || !msp->info->ready)) {
+ printk(KERN_ERR "error: could not receive ack from VoIP\n");
+ msp->info = NULL;
+
+ goto err;
+ }
+ }
+
+ return 1;
+
+
+err:
+ return 0;
+}
+
+
+static int msp_start(struct comcerto_msp *msp)
+{
+ msp->state = MSP_STARTING;
+ msp->alert_seen = 0;
+
+ if (!msp_ready(msp, MSP_READY_DELAY)) {
+ printk(KERN_ERR "VoIP starting failed\n");
+
+ goto err;
+ }
+
+ printk(KERN_INFO "VoIP has been started successfully\n");
+
+ /* start heart beat timer */
+ init_timer(&msp->timer_expire);
+ msp->timer_expire.function = msp_poll;
+ msp->timer_expire.expires = jiffies + 1 * HZ; /* 1sec */
+ msp->timer_expire.data = (unsigned long)msp;
+ add_timer(&msp->timer_expire);
+ msp->state = MSP_RUNNING;
+ global_msp_info = msp->info;
+
+ if (pfe_status.pfe_state == 1) {
+ try_module_get(pfe_status.owner);
+ update_pfe_status_for_MSP();
+ }
+
+ return 0;
+
+
+err:
+ return -1;
+}
+
+static void msp_alert(struct comcerto_msp *msp)
+{
+ struct alert_type *alert = NULL;
+ int ialert = 0;
+
+ if (!msp->info) {
+ return;
+ }
+
+ while (msp->info->alert_number > msp->alert_seen) {
+ if (msp->alert_seen >= MAX_SAVED_ALERT) {
+ ialert = MAX_SAVED_ALERT - 1;
+ } else {
+ ialert = msp->alert_seen;
+ }
+
+ alert = (struct alert_type *)(msp->info->save_alert) + ialert;
+ msp->alert_seen++;
+
+ printk(KERN_ERR "VoIP alert: current number / total received = %lu/%lu\n", msp->alert_seen, msp->info->alert_number);
+ printk(KERN_ERR "\ttype: 0x%02x\n", alert->type & 0xff);
+ printk(KERN_ERR "\tchannel: %hu\n", alert->channel);
+ printk(KERN_ERR "\tlink register: 0x%08x\n", alert->abort_lr);
+ printk(KERN_ERR "\tunique ID: 0x%04x\n", alert->unique_id);
+ printk(KERN_ERR "\taction: 0x%04x\n", alert->action);
+ printk(KERN_ERR "\tlocaltime: 0x%08x\n", alert->localtime);
+ printk(KERN_ERR "\tval1: 0x%08x\n", alert->val1);
+ printk(KERN_ERR "\tval2: 0x%08x\n", alert->val2);
+ }
+}
+
+static void msp_poll(unsigned long arg)
+{
+ struct comcerto_msp *msp = (struct comcerto_msp *)arg;
+
+ switch (msp->state) {
+ case MSP_RESETTING:
+ case MSP_RESET:
+ case MSP_STARTING:
+ case MSP_CRASHED:
+ break;
+ case MSP_RUNNING: {
+ /* check for alerts */
+ msp_alert(msp);
+
+ if (!msp->info->heartbeat) {
+ printk(KERN_ERR "VoIP heart beat failure\n");
+ msp->state = MSP_CRASHED;
+ }
+
+ msp->info->heartbeat = 0;
+ } break;
+ }
+
+ msp->timer_expire.expires = jiffies + 1 * HZ; /* 1sec */
+ add_timer(&msp->timer_expire);
+}
+
+int comcerto_download_to_msp(struct comcerto_msp *msp)
+{
+ struct _code_info *code_info = &msp->code_info;
+ struct voip_sym *voip_sym;
+ struct voip_fiq_code voip_fiq;
+ void (*rtxc_handler)(void); /* FIQ handler code entry point */
+ int rc = 0;
+
+ if (load_elf(code_info)) {
+ printk(KERN_ERR "VoIP download failed\n");
+ rc = -EINVAL;
+
+ goto err;
+ }
+
+ /* save msp info pointer */
+ msp->info = (struct msp_info *)msp->code_info.proc_addr;
+ voip_sym = (struct voip_sym *)msp->code_info.sym_addr;
+
+ rtxc_handler = voip_sym->rtxc_handler;
+ voip_entry = voip_sym->voip_entry;
+ voip_exit = voip_sym->voip_exit;
+
+ if (!voip_entry || !voip_exit || !rtxc_handler) {
+ printk(KERN_ERR "failed extract symbol\n");
+ rc = -EINVAL;
+
+ goto err;
+ }
+
+ if (claim_fiq(&voip_fiq_handler)) {
+ printk(KERN_ERR "could not claim FIQ\n");
+ rc = -EINVAL;
+
+ goto err;
+ } else {
+ /* copy code to IVT */
+ unsigned int code = 0xe59ff018; /* LDR pc, [pc, #0x20]; here 0x20 == MAGIC_OFF, do not change it */
+
+ voip_fiq.len = MAGIC_LEN;
+ memcpy(voip_fiq.data + MAGIC_OFF, &rtxc_handler, sizeof(rtxc_handler));
+ memcpy(voip_fiq.data, &code, sizeof(code));
+
+ /* copy code to IVT */
+ set_fiq_handler(&voip_fiq.data[0], voip_fiq.len);
+ }
+
+err:
+ return rc;
+}
+
+int comcerto_start_msp(struct comcerto_msp *msp)
+{
+
+#ifdef CONFIG_SMP
+ struct cpumask in_mask;
+ int cpu = 0;
+ int pid = 0;
+
+ /* start voip on cpu where FIQ handler is registered */
+ cpumask_set_cpu(cpu, &in_mask);
+ sched_setaffinity(pid, &in_mask);
+#endif /* CONFIG_SMP */
+
+ /* give control to MSP */
+ voip_entry();
+
+ return msp_start(msp);
+}
+
+void comcerto_stop_msp(struct comcerto_msp *msp)
+{
+ /* stop MSP gently */
+ voip_exit();
+
+ printk(KERN_INFO "VoIP has been stopped\n");
+
+ release_fiq(&voip_fiq_handler);
+
+ msp->state = MSP_RESET;
+ del_timer(&msp->timer_expire);
+ msp->info = NULL;
+
+ if (global_msp_info && (pfe_status.pfe_state == 1)) {
+ module_put(pfe_status.owner);
+ }
+
+ global_msp_info = 0;
+}
+
+static ssize_t comcerto_show_msp_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ved_priv *priv = netdev_priv(ndev);
+ struct comcerto_msp *msp = &priv->msp;
+ int len = 0;
+
+ if (!msp->info) {
+ return sprintf(buf + len, "no VoIP info available\n");
+ }
+
+ len += sprintf(buf + len, "ABI version: %lu\n", msp->info->abi_rev);
+ len += sprintf(buf + len, "VoIP version: %s\n", msp->info->msp_version);
+ len += sprintf(buf + len, "DSP version: %s\n", msp->info->spu_version);
+ len += sprintf(buf + len, "VoIP freq: %d Mhz\n", msp->info->ARMfreq);
+ len += sprintf(buf + len, "AMBA bus freq: %d MHz\n", msp->info->AMBAfreq);
+ len += sprintf(buf + len, "Alert number: %lu\n", msp->alert_seen);
+
+ return len;
+}
+
+static ssize_t comcerto_show_alert_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ved_priv *priv = netdev_priv(ndev);
+ struct comcerto_msp *msp = &priv->msp;
+ struct alert_type *alert;
+ unsigned long ialert = 0;
+ int len = 0;
+
+ if ( !msp->info || !msp->alert_seen ) {
+ return sprintf(buf + len, "no VoIP alert info available\n");
+ }
+
+ if (msp->alert_seen > MAX_SAVED_ALERT) {
+ ialert = MAX_SAVED_ALERT;
+ } else {
+ ialert = msp->alert_seen;
+ }
+
+ for (; ialert > 0; ialert--) {
+ alert = (struct alert_type *)(msp->info->save_alert) + ialert - 1;
+
+ len += sprintf(buf + len, "alert No: %lu\n", ((ialert == MAX_SAVED_ALERT) ? msp->alert_seen : ialert));
+ len += sprintf(buf + len, "\ttype: 0x%02X\n", (alert->type & 0xff));
+ len += sprintf(buf + len, "\tchannel: %hu\n", alert->channel);
+ len += sprintf(buf + len, "\tunique ID: 0x%04X\n", alert->unique_id);
+ len += sprintf(buf + len, "\taction: 0x%04X\n", alert->action);
+ len += sprintf(buf + len, "\tlink register: 0x%08X\n", alert->abort_lr);
+ len += sprintf(buf + len, "\tlocaltime: 0x%08X\n", alert->localtime);
+ len += sprintf(buf + len, "\tval1: 0x%08X\n", alert->val1);
+ len += sprintf(buf + len, "\tval2: 0x%08X\n", alert->val2);
+
+ /* Make sure we are not going out of buffer. Number to add is
+ empirical, so, change buf above - change number below */
+ if (len + 175 > PAGE_SIZE) {
+ break;
+ }
+ }
+
+ return len;
+}
+
+static ssize_t comcerto_show_abi_rev(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ved_priv *priv = netdev_priv(ndev);
+ struct comcerto_msp *msp = &priv->msp;
+ int len = 0;
+
+ if (!msp->info) {
+ return sprintf(buf + len, "no VoIP info available\n");
+ }
+
+ len += sprintf(buf + len, "%lu\n", msp->info->abi_rev);
+
+ return len;
+}
+
+int msp_init_sysfs(struct platform_device *pdev)
+{
+ if (device_create_file(&pdev->dev, &dev_attr_msp_info)
+ || device_create_file(&pdev->dev, &dev_attr_alert_info)
+ || device_create_file(&pdev->dev, &dev_attr_abi_rev))
+ {
+ printk(KERN_ERR "failed to create VoIP sysfs files\n");
+
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-comcerto/msp/msp.h b/arch/arm/mach-comcerto/msp/msp.h
new file mode 100644
index 0000000..4f1b1f2
--- /dev/null
+++ b/arch/arm/mach-comcerto/msp/msp.h
@@ -0,0 +1,143 @@
+/*
+ * arch/arm/mach-comcerto/msp/msp.h
+ *
+ * Copyright (C) 2006,2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _COMCERTO_MSP_H
+#define _COMCERTO_MSP_H
+
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/elf.h>
+#include <linux/netdevice.h>
+#include <asm/pgtable.h>
+#include <mach/irqs.h>
+
+#define SHF_MIPS_ADDR (0x40000000)
+
+enum msp_state {
+ MSP_RESETTING = 0,
+ MSP_RESET,
+ MSP_STARTING,
+ MSP_RUNNING,
+ MSP_CRASHED
+};
+
+struct msp_stack_frame {
+ unsigned int r0;
+ unsigned int r1;
+ unsigned int r2;
+ unsigned int r3;
+ unsigned int r4;
+ unsigned int r5;
+ unsigned int r6;
+ unsigned int r7;
+ unsigned int r8;
+ unsigned int r9;
+ unsigned int r10;
+ unsigned int r11;
+ unsigned int r12;
+ unsigned int sp;
+ unsigned int lr;
+ unsigned int pc;
+};
+
+struct alert_type {
+ unsigned short type;
+ unsigned short channel;
+ unsigned short unique_id;
+ unsigned short action;
+ unsigned int abort_lr;
+ unsigned int val1;
+ unsigned int val2;
+ unsigned int localtime;
+};
+
+struct _code_info {
+ const unsigned char *code;
+ unsigned long size;
+
+ unsigned long checksum_program_addr;
+ unsigned long program_addr;
+ unsigned long proc_addr;
+ unsigned long sym_addr;
+ unsigned long checksum;
+};
+
+struct msp_info {
+ unsigned long abi_rev;
+ volatile unsigned long lock;
+ unsigned long device;
+ unsigned long revision;
+ char msp_version[32];
+ char spu_version[16];
+ /* */
+ unsigned long CSPtoMSPQueuePhyaddr;
+ unsigned long MSPtoCSPQueuePhyaddr;
+ unsigned long SMRXCSPhyaddr;
+ unsigned long SMTXCSPhyaddr;
+ unsigned long SPDRV_ACP_MSP_Phyaddr;
+ /* */
+ unsigned long ERAMsize;
+ unsigned long ARAMsize;
+ unsigned long IRAMsize;
+ unsigned short ARMfreq;
+ unsigned short AMBAfreq;
+ unsigned short SPUfreq;
+ unsigned long voip_ipaddr;
+ volatile unsigned long IPoffload;
+ volatile unsigned long ready;
+ volatile unsigned long heartbeat;
+ volatile unsigned long alert_number;
+ volatile unsigned long alert_frame;
+ volatile unsigned long save_alert;
+ unsigned long pfe_ready;
+ unsigned long pfe_virt;
+ unsigned long pfe_phys;
+ unsigned long axi_virt;
+ unsigned long axi_phys;
+ unsigned long msp_virt;
+ unsigned long msp_phys;
+};
+
+struct voip_sym {
+ void (*rtxc_handler)(void);
+ void (*voip_entry)(void);
+ void (*voip_exit)(void);
+ void (*voip_crash)(void *);
+};
+
+struct comcerto_msp {
+ unsigned long alert_seen;
+ unsigned long last_tick;
+ int state;
+
+ volatile struct msp_info *info;
+ struct _code_info code_info;
+ struct sk_buff_head msp_list;
+ struct timer_list timer_expire;
+ struct ctl_table_header *sysctl_header;
+ void* __iomem vectors_base;
+};
+
+int comcerto_download_to_msp(struct comcerto_msp *msp);
+int comcerto_start_msp(struct comcerto_msp *msp);
+void comcerto_stop_msp(struct comcerto_msp *msp);
+int msp_init_sysfs(struct platform_device *pdev);
+
+#endif /* _COMCERTO_MSP_H */
diff --git a/arch/arm/mach-comcerto/msp/smi.c b/arch/arm/mach-comcerto/msp/smi.c
new file mode 100644
index 0000000..bb3822f
--- /dev/null
+++ b/arch/arm/mach-comcerto/msp/smi.c
@@ -0,0 +1,67 @@
+/*
+ * arch/arm/mach-comcerto/msp/smi.h
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+#include "smi.h"
+#include "msp.h"
+
+void *smi_alloc_part(struct fastpart *fp)
+{
+ unsigned long flags = 0;
+ void *vaddr = NULL;
+
+ flags = msp_lock_frqsave();
+
+ if (fp->freeblk) {
+ vaddr = fp->freeblk;
+ fp->freeblk = (u32 * volatile)__raw_readl(vaddr);
+ }
+
+ msp_unlock_frqrestore(flags);
+
+ return vaddr;
+}
+
+void smi_free_part(struct fastpart *fp, void *vaddr)
+{
+ unsigned long flags = 0;
+
+ flags = msp_lock_frqsave();
+
+ __raw_writel((unsigned long)fp->freeblk, (unsigned long)vaddr);
+ fp->freeblk = vaddr;
+
+ msp_unlock_frqrestore(flags);
+}
+
+struct fastqueue *smi_queue_init(struct smiqueue *psmiq, unsigned long addr, void (*gen_msp_irq)(void))
+{
+ struct fastqueue *fpq = (struct fastqueue *)addr;
+
+ if (fpq) {
+ psmiq->fpq = fpq;
+ psmiq->storage = fpq->storage;
+ psmiq->gen_msp_irq = gen_msp_irq;
+ }
+
+ return fpq;
+}
diff --git a/arch/arm/mach-comcerto/msp/smi.h b/arch/arm/mach-comcerto/msp/smi.h
new file mode 100644
index 0000000..c45b2a6
--- /dev/null
+++ b/arch/arm/mach-comcerto/msp/smi.h
@@ -0,0 +1,150 @@
+/*
+ * arch/arm/mach-comcerto/msp/smi.h
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _COMCERTO_SMI_H
+#define _COMCERTO_SMI_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/sema.h>
+
+#ifndef COMCERTO_IRQ_SET
+#define DISTR_INT_SET_PENDING (COMCERTO_GIC_DIST_BASE + 0x200)
+#define DISTR_CLEAR_PENDING (COMCERTO_GIC_DIST_BASE + 0x280)
+#define CPU_END_OF_INT (COMCERTO_GIC_CPU_BASE + 0x10)
+#define comcerto_softirq_set(irq) \
+ (*((volatile unsigned long *)DISTR_INT_SET_PENDING + ((irq) >> 5)) = 1 << ((irq) & 0x1f))
+#define comcerto_softirq_check(irq) \
+ ((*((volatile unsigned long *)DISTR_INT_SET_PENDING + ((irq) >> 5)) >> ((irq) & 0x1f)) & 0x01)
+#define comcerto_irq_ack(irq) \
+ (* ((volatile unsigned long *) DISTR_CLEAR_PENDING + ((irq) >> 5)) = 1 << ((irq) % 32))
+
+//#define comcerto_irq_ack(irq) (*(volatile unsigned long *)(CPU_END_OF_INT) = irq)
+
+#endif /* C2000 */
+
+/* shared structure (MSP uses the same type) */
+struct fastqueue {
+ u32 *storage;
+ u32 sema;
+ volatile u16 lock;
+ volatile u16 get;
+ volatile u16 put;
+ u16 size;
+};
+
+/* private structure (CSP only) */
+struct smiqueue {
+ u32 *storage; /* virtual address of fpq->storage */
+ struct fastqueue *fpq; /* queue pointer */
+ void (*gen_msp_irq)(void); /* CSP to MSP generate interrupt function */
+};
+
+struct fastpart {
+ volatile u8 lock;
+ u8 reserved1;
+ u32 * volatile freeblk;
+ u32 *storage;
+ u32 blksz;
+ u32 blkcnt;
+ u32 *end_of_storage;
+ u16 reserved2;
+ u16 reserved3;
+ u16 freecnt;
+};
+
+void *smi_alloc_part(struct fastpart *fp);
+void smi_free_part(struct fastpart *fp, void *v);
+
+
+struct fastqueue *smi_queue_init(struct smiqueue *psmiq, unsigned long addr, void (*gen_msp_irq)(void));
+
+static inline void smi_gen_msp_irq(void)
+{
+ comcerto_softirq_set(IRQ_PTP1);
+}
+
+static inline int smi_enqueue(struct smiqueue *psmiq, void *vaddr)
+{
+ struct fastqueue *fpq = psmiq->fpq;
+ unsigned long flags = 0;
+ u16 put = 0;
+ int rc = 0;
+
+ flags = msp_lock_frqsave();
+
+ put = fpq->put;
+
+ if (++put >= fpq->size) {
+ put = 0;
+ }
+
+ if (put != fpq->get) {
+ psmiq->storage[fpq->put] = (u32)vaddr;
+ fpq->put = put;
+
+ if (psmiq->gen_msp_irq) {
+ psmiq->gen_msp_irq();
+ }
+
+ rc = 1;
+ }
+
+ msp_unlock_frqrestore(flags);
+
+ return rc;
+}
+
+static inline void *smi_dequeue(struct smiqueue *psmiq)
+{
+ struct fastqueue *fpq = psmiq->fpq;
+ unsigned long flags = 0;
+ u32 get = 0;
+ void *vaddr = NULL;
+
+ flags = msp_lock_frqsave();
+
+ get = fpq->get;
+
+ if (fpq->put != get) {
+ vaddr = (void *)(psmiq->storage[get++]);
+
+ if (get == fpq->size) {
+ get = 0;
+ }
+
+ fpq->get = get;
+ }
+
+ msp_unlock_frqrestore(flags);
+
+ return vaddr;
+}
+
+static inline int smi_is_queue_empty(struct fastqueue *fpq)
+{
+ return (fpq->get == fpq->put);
+}
+
+#endif /* _COMCERTO_SMI_H */
diff --git a/arch/arm/mach-comcerto/msp/ved.c b/arch/arm/mach-comcerto/msp/ved.c
new file mode 100644
index 0000000..f9fc70f
--- /dev/null
+++ b/arch/arm/mach-comcerto/msp/ved.c
@@ -0,0 +1,557 @@
+/*
+ * arch/arm/mach-comcerto/msp/ved.c
+ *
+ * Copyright (C) 2006 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/interrupt.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+
+#include "ved.h"
+
+static char ctrl_mac[6] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE };
+
+#define VED_POLL_WEIGHT (32)
+#define IP_SEC_HEADROOM (40)
+#define PKT_BUF_SZ (1540 + IP_SEC_HEADROOM) /* size of each rx buffer */
+
+static int ved_xmit(struct sk_buff *skb, struct net_device *dev);
+static struct net_device_stats *ved_get_stats(struct net_device *dev);
+static irqreturn_t ved_interrupt(int irq, void *dev_id);
+static int ved_open(struct net_device *dev);
+static int ved_release(struct net_device *dev);
+static int ved_rebuild_header(struct sk_buff *skb);
+static int ved_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+ const void *daddr, const void *saddr, unsigned int len);
+static int ved_write_packet(struct sk_buff *skb, struct net_device *dev);
+static int ved_read_packet(struct net_device *dev, struct FDesc * ThisFdesc);
+static int ved_poll(struct napi_struct *napi, int budget);
+static int start_ved(struct net_device *dev);
+static int stop_ved(struct net_device *dev);
+
+
+static int ved_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ved_priv *priv = (struct ved_priv *)netdev_priv(dev);
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rc = ved_write_packet(skb, dev);
+
+ if (rc) {
+ priv->stats.tx_dropped++;
+ } else {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies; /* save the timestamp */
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static inline void ved_ack_msp(void)
+{
+ comcerto_softirq_set(IRQ_PTP1);
+}
+
+static irqreturn_t ved_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct ved_priv *priv = netdev_priv(dev);
+
+ spin_lock(&priv->lock);
+
+ if (napi_schedule_prep(&priv->napi)) {
+ comcerto_softirq_set(irq);
+
+ disable_irq_nosync(irq);
+ __napi_schedule(&priv->napi);
+ } else {
+ /* FIX by disabling interrupts */
+ /* disable_irq(irq); */
+ }
+
+ /* unlock the device */
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int ved_rebuild_header(struct sk_buff *skb)
+{
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+ struct net_device *dev = skb->dev;
+
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ memcpy(eth->h_dest, dev->dev_addr, dev->addr_len);
+ eth->h_dest[ETH_ALEN - 1] ^= 0x01; /* dest is us xor 1 */
+
+ return 0;
+}
+
+static int ved_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+ const void *daddr, const void *saddr, unsigned int len)
+{
+ struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
+
+ eth->h_proto = htons(type);
+ memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
+ memcpy(eth->h_dest, daddr ? daddr : dev->dev_addr, dev->addr_len);
+ eth->h_dest[ETH_ALEN - 1] ^= 0x01; /* dest is us xor 1 */
+
+ return dev->hard_header_len;
+}
+
+static int ved_open(struct net_device *dev)
+{
+ struct ved_priv *priv = netdev_priv(dev);
+ struct platform_device *pdev = priv->pdev;
+ const struct firmware *fw_entry;
+ const char fw_name[] = {"voip.axf"};
+ int rc = 0;
+
+ /* check if the interface is already opened */
+
+ if (priv->state == 1) {
+ /* interface is opened, nothing to be done */
+
+ return rc;
+ }
+
+ if (priv->msp.info) {
+ /* stop MSP */
+
+ del_timer(&priv->msp.timer_expire);
+ priv->msp.info = NULL;
+ priv->msp.state = MSP_RESET;
+ }
+
+ might_sleep();
+
+ memset((void *)COMCERTO_MSP_VADDR, 0x00, COMCERTO_MSP_DDR_SIZE);
+
+ if (request_firmware(&fw_entry, (char *)fw_name, &pdev->dev)) {
+ printk(KERN_ERR "could not find the VoIP firmware\n");
+ rc = -ETIMEDOUT;
+
+ return rc;
+ }
+
+ priv->msp.code_info.code = fw_entry->data;
+ priv->msp.code_info.size = fw_entry->size;
+
+ if (comcerto_download_to_msp(&priv->msp)) {
+ printk(KERN_ERR "failed download VoIP\n");
+ rc = -EREMOTEIO;
+
+ goto out;
+ }
+
+ if (comcerto_start_msp(&priv->msp)) {
+ printk(KERN_ERR "failed start VoIP\n");
+ rc = -EREMOTEIO;
+
+ goto out;
+ }
+
+ start_ved(dev);
+ napi_enable(&priv->napi);
+
+ if (request_irq(dev->irq, ved_interrupt, IRQF_SHARED, "comcerto_ved", dev)) {
+ printk(KERN_ERR "%s: failed to request irq#%d\n", dev->name, dev->irq);
+
+ comcerto_stop_msp(&priv->msp);
+ napi_disable(&priv->napi);
+ stop_ved(dev);
+
+ goto out;
+ }
+
+ netif_start_queue(dev);
+
+ priv->state = 1;
+
+
+out:
+ release_firmware(fw_entry);
+
+ return rc;
+}
+
+int ved_release(struct net_device *dev)
+{
+ struct ved_priv *priv = netdev_priv(dev);
+
+ /* do nothing if interface is already down */
+
+ if (priv->state) {
+ netif_stop_queue(dev); /* can't transmit any more */
+
+ napi_disable(&priv->napi);
+
+ /* disable irq */
+ free_irq(dev->irq, dev);
+
+ /* reset MSP device */
+ comcerto_stop_msp(&priv->msp);
+ stop_ved(dev);
+ priv->state = 0;
+ }
+
+ return 0;
+}
+
+static struct net_device_stats *ved_get_stats(struct net_device *dev)
+{
+ return &(((struct ved_priv *)netdev_priv(dev))->stats);
+}
+
+/**
+ * ved_read_packet - read and process an REALPACKET (Ethernet) frame
+ * @dev: device id
+ * @ThisFdesc: Frame descriptor to parse
+ *
+ * This function read and process received frame descriptor non pre-processed by MSP . (ethernet frames)
+ */
+static int ved_read_packet(struct net_device *dev, struct FDesc *ThisFdesc)
+{
+ struct ved_priv *priv = (struct ved_priv *)netdev_priv(dev);
+ char *data_addr;
+ int length, offset, reason;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(PKT_BUF_SZ);
+
+ if (!skb) {
+ printk(KERN_WARNING "no skb was allocated: packet is dropped\n");
+ priv->stats.rx_dropped++;
+
+ return 0;
+ }
+
+ length = ThisFdesc->Length;
+ offset = ThisFdesc->Offset;
+
+ ThisFdesc->FStatus = 0;
+ data_addr = (char *)(ThisFdesc->Payload + offset);
+
+ skb_reserve(skb, NET_IP_ALIGN);
+ memcpy(skb_put(skb, length), data_addr, length);
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+
+ if ((reason = netif_receive_skb(skb))) {
+ priv->stats.rx_dropped++;
+ }
+
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += length;
+ dev->last_rx = jiffies;
+
+ return 1;
+}
+
+static int ved_poll(struct napi_struct *napi, int budget)
+{
+
+ struct ved_priv *priv = container_of(napi, struct ved_priv, napi);
+ struct net_device *dev = priv->dev;
+ struct FDesc * ThisFdesc = NULL;
+ int done;
+ int rx_work_limit = budget;
+ int received = 0;
+
+restart_poll:
+ do {
+ comcerto_irq_ack(dev->irq);
+
+ while (!smi_is_queue_empty(priv->rx_smiq.fpq)) {
+ if (--rx_work_limit < 0) {
+ goto not_done;
+ }
+
+ ThisFdesc = smi_dequeue(&priv->rx_smiq);
+
+ done = ved_read_packet(dev, ThisFdesc);
+ smi_free_part(ThisFdesc->fpart, ThisFdesc);
+
+ if (!done) {
+ goto not_done;
+ } else {
+ received++;
+ }
+
+ break;
+ }
+ } while (comcerto_softirq_check(dev->irq));
+
+ /* we are happy/done, no more packets on ring; put us back
+ to where we can start processing interrupts again */
+ napi_complete(napi);
+ /* enable_irq(dev->irq); */
+
+ /* The last op happens after poll completion. Which means the following:
+ * 1. it can race with disabling irqs in irq handler (which are done to
+ * schedule polls)
+ * 2. it can race with dis/enabling irqs in other poll threads
+ * 3. if an irq raised after the begining of the outer beginning
+ * loop(marked in the code above), it will be immediately
+ * triggered here.
+ *
+ * Summarizing: the logic may results in some redundant irqs both
+ * due to races in masking and due to too late acking of already
+ * processed irqs. The good news: no events are ever lost.
+ */
+
+ /* Let's have a last chance to process any missing interrupts we may
+ * we may have missed while the IRQs line was masqued. Before leaving
+ * poll mode, we check if data have been posted in the queue from MSP.
+ * If true, then the IRQ line is disabled again and a new loop of polling
+ * is scheduled. This double check work arround is well known in NAPI
+ * based implementation (see documentations/NAPI_HOWTO.txt - appendix2)
+ */
+
+ if (!smi_is_queue_empty(priv->rx_smiq.fpq) && napi_reschedule(napi)) {
+ /* disable_irq(dev->irq); */
+ goto restart_poll;
+ }
+
+ enable_irq(dev->irq);
+
+ return 0;
+
+not_done:
+ if (!received) {
+ received = 1;
+ }
+
+ return 1;
+}
+
+static int ved_write_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ved_priv *priv = (struct ved_priv *)netdev_priv(dev);
+ struct FDesc *ThisFdesc;
+ u8 *buf;
+ u32 offset;
+
+ ThisFdesc = smi_alloc_part(priv->tx_smipart);
+
+ if (!ThisFdesc) {
+ dev_kfree_skb(skb);
+
+ return -ENOMEM;
+ }
+
+ offset = NET_IP_ALIGN;
+ buf = ThisFdesc->Payload + offset;
+
+ memcpy(buf, skb->data, skb->len);
+
+ ThisFdesc->Offset = offset;
+
+ ThisFdesc->Next = NULL;
+ ThisFdesc->Length = (u32)(skb->len);
+ ThisFdesc->FStatus = 0;
+ ThisFdesc->protocol = priv->default_packet_type;
+
+ /* free the original skb (local generation) */
+ dev_kfree_skb(skb);
+
+ if (!smi_enqueue(&priv->tx_smiq, ThisFdesc)) {
+ printk(KERN_WARNING "could not enqueue: queue is full: %#lx\n", (unsigned long)ThisFdesc);
+
+ smi_free_part(priv->tx_smipart, ThisFdesc);
+
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int stop_ved(struct net_device *dev)
+{
+ return 0;
+}
+
+static int start_ved(struct net_device *dev)
+{
+ struct ved_priv *priv = (struct ved_priv *)netdev_priv(dev);
+ struct msp_info *info = (struct msp_info *)priv->msp.info;
+
+ if (!info) {
+ printk(KERN_ERR "no VoIP info found\n");
+
+ return -1;
+ }
+
+ printk(KERN_INFO "Device: %lx\n", info->device);
+ printk(KERN_INFO "Revision: %lx\n", info->revision);
+ printk(KERN_INFO "CSPtoMSPQueuePhyaddr: %#lx\n", info->CSPtoMSPQueuePhyaddr);
+ printk(KERN_INFO "MSPtoCSPQueuePhyaddr: %#lx\n", info->MSPtoCSPQueuePhyaddr);
+ printk(KERN_INFO "SMRXCSPhyaddr: %#lx\n", info->SMRXCSPhyaddr);
+ printk(KERN_INFO "SMTXCSPhyaddr: %#lx\n", info->SMTXCSPhyaddr);
+
+ printk(KERN_INFO "DSP version: %s\n", info->spu_version);
+ printk(KERN_INFO "VoIP version: %s\n", info->msp_version);
+
+ priv->tx_smipart = (struct fastpart *)(info->SMTXCSPhyaddr);
+ priv->rx_smipart = (struct fastpart *)(info->SMRXCSPhyaddr);
+
+ smi_queue_init(&priv->tx_smiq, info->CSPtoMSPQueuePhyaddr, smi_gen_msp_irq);
+ smi_queue_init(&priv->rx_smiq, info->MSPtoCSPQueuePhyaddr, NULL);
+
+ ved_ack_msp();
+
+ return 0;
+}
+
+static const struct net_device_ops ctrl_netdev_ops = {
+ .ndo_open = ved_open,
+ .ndo_stop = ved_release,
+ .ndo_start_xmit = ved_xmit,
+ .ndo_get_stats = ved_get_stats,
+};
+
+static const struct header_ops ctrl_header_ops = {
+ .create = ved_header,
+ .rebuild = ved_rebuild_header,
+};
+
+static int ved_probe(struct platform_device *pdev)
+{
+ struct net_device *dev = NULL;
+ struct ved_priv *priv= NULL;
+ int rc;
+
+ /* create an ethernet device instance */
+
+ dev = alloc_etherdev(sizeof (*priv));
+
+ if (!dev) {
+ printk(KERN_ERR "gemac %d device allocation failed\n", pdev->id);
+ rc = -ENOMEM;
+
+ goto err0;
+ }
+
+ priv = netdev_priv(dev);
+
+ memset(priv, 0, sizeof(struct ved_priv));
+
+ spin_lock_init(&priv->lock);
+
+ /* init private section */
+ priv->default_packet_type = PROTID_ETH;
+ priv->state = 0; /* closed */
+
+ priv->pdev = pdev;
+ priv->dev = dev;
+ platform_set_drvdata(pdev, dev);
+
+ /* copy the station address into the dev structure */
+ memcpy(dev->dev_addr, ctrl_mac, ETH_ALEN);
+
+ if (dev_alloc_name(dev, CTRL)) {
+ printk(KERN_ERR "could not allocate net device name %s\n", CTRL);
+ rc = -EINVAL;
+
+ goto err2;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* fill in device structure with ethernet-generic values */
+ ether_setup(dev);
+
+ /* initialize the device structure */
+ dev->netdev_ops = &ctrl_netdev_ops;
+ dev->header_ops = &ctrl_header_ops;
+ dev->destructor = free_netdev;
+
+ /* fill in device structure with ethernet-generic values */
+ dev->tx_queue_len = 0;
+ dev->flags |= IFF_NOARP;
+ dev->flags &= ~IFF_MULTICAST;
+ dev->irq = platform_get_irq_byname(pdev, "irq");
+
+ netif_napi_add(dev, &priv->napi, ved_poll, VED_POLL_WEIGHT);
+
+ skb_queue_head_init(&priv->msp.msp_list);
+
+ rc = register_netdev(dev);
+
+ if (rc) {
+ printk(KERN_ERR "could not register net device\n");
+
+ goto err0;
+ }
+
+ /* create all the sysfs files */
+ msp_init_sysfs(pdev);
+
+ return 0;
+
+
+err2:
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+
+err0:
+ return rc;
+}
+
+static int ved_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ unregister_netdevice(dev);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+
+ return 0;
+}
+
+/* Structure for a device driver */
+static struct platform_driver ved_driver = {
+ .probe = ved_probe,
+ .remove = ved_remove,
+ .driver = {
+ .name = "ved",
+ },
+};
+
+static int __init ved_init(void)
+{
+ return platform_driver_register(&ved_driver);
+}
+
+static void __exit ved_exit(void)
+{
+ platform_driver_unregister(&ved_driver);
+}
+
+
+module_init(ved_init);
+module_exit(ved_exit);
diff --git a/arch/arm/mach-comcerto/msp/ved.h b/arch/arm/mach-comcerto/msp/ved.h
new file mode 100644
index 0000000..f747cc5
--- /dev/null
+++ b/arch/arm/mach-comcerto/msp/ved.h
@@ -0,0 +1,100 @@
+/*
+ * arch/arm/mach-comcerto/msp/ved.h
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _COMCERTO_VED_H
+#define _COMCERTO_VED_H
+
+#include <linux/netdevice.h>
+
+#include "smi.h"
+#include "msp.h"
+
+/* Protocol index definition */
+enum {
+ PROTID_RAW,
+ PROTID_ETH,
+ PROTID_PPP,
+ PROTID_IPV4,
+ PROTID_IPV6,
+ PROTID_CSME,
+ PROTID_ARP,
+ PROTID_VLAN,
+ PROTID_UDP,
+ PROTID_TCP,
+ PROTID_ICMP,
+ PROTID_CSM_API,
+ MAX_PROTOCOL,
+ PROTO_INVALID
+};
+
+#define MAX_BDESC_NUMBER (4)
+#define CTRL "eth1"
+
+
+/*
+ * This structure is private to each device. It is used to pass
+ * packets in and out, so there is place for a packet
+ */
+
+struct ved_priv {
+ struct net_device_stats stats;
+ struct platform_device *pdev;
+ struct net_device *dev;
+ struct napi_struct napi;
+ spinlock_t lock;
+
+ struct _SKB_POOL *skbpool; /* skb pool for MSP to CSP buffer (max MTU size) */
+
+ /* smi (share memory interface) */
+ struct fastpart *tx_smipart; /* CSP to MSP Fdesc fast part */
+ struct fastpart *rx_smipart; /* MSP to CSP Fdesc fast part */
+ struct smiqueue tx_smiq;
+ struct smiqueue rx_smiq;
+ u32 default_packet_type;
+ struct comcerto_msp msp;
+ int state;
+};
+
+struct BDesc {
+ u8 *BPtr;
+ volatile u32 BControl;
+};
+
+struct FDesc {
+ struct FDesc *Next;
+ volatile u32 System;
+ volatile u32 FStatus;
+ volatile u32 FControl;
+ struct BDesc BDesc[MAX_BDESC_NUMBER];
+
+ u16 Length;
+ u16 Offset;
+ u16 protocol;
+ u16 protocol_hdr_len;
+ struct sk_buff *skb;
+ struct fastpart *fpart;
+ struct FDesc *Tail;
+ u32 nFDesc; /* nFdesc used by MSP or local phy addr of FDESC
+ * (only used in direct EMAC control mode) */
+ void *pSFdesc; /* CSP must not touch this value */
+ u8 *Payload;
+};
+
+#endif /* _COMCERTO_VED_H */
diff --git a/arch/arm/mach-comcerto/pcie-c2000.c b/arch/arm/mach-comcerto/pcie-c2000.c
new file mode 100644
index 0000000..d4c01fb
--- /dev/null
+++ b/arch/arm/mach-comcerto/pcie-c2000.c
@@ -0,0 +1,1964 @@
+/*
+ * linux/arch/arm/mach-comcerto/pcie-c2000.c
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#if defined(CONFIG_PCI_MSI)
+#include <linux/msi.h>
+#endif
+
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/sizes.h>
+#include <asm/mach/pci.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <asm/mach/irq.h>
+#include <mach/pcie-c2000.h>
+#include <mach/serdes-c2000.h>
+#include <mach/reset.h>
+#include <mach/hardware.h>
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <mach/comcerto-2000/pm.h>
+//#define COMCERTO_PCIE_DEBUG
+
+#ifdef CONFIG_PCI_MSI
+static DECLARE_BITMAP(msi_irq_in_use[NUM_PCIE_PORTS], PCIE_NUM_MSI_IRQS);
+static int comcerto_msi_init(struct pcie_port *pp);
+#endif
+
+int pcie_external_clk = 0;
+static int __init get_pcie_clk_mode(char *str)
+{
+ if (!strcmp(str, "yes"))
+ pcie_external_clk = 1;
+
+ return 1;
+}
+
+__setup("pcie_external_clk=", get_pcie_clk_mode);
+
+int pcie_gen1_only = 0;
+static int __init get_pcie_gen_mode(char *str)
+{
+ if (!strcmp(str, "yes"))
+ pcie_gen1_only = 1;
+
+ return 1;
+}
+
+__setup("pcie_gen1_only=", get_pcie_gen_mode);
+
+
+/* DWC PCIEe configuration register offsets on APB */
+struct pcie_app_reg app_regs[MAX_PCIE_PORTS] = {
+ /* PCIe0 */
+ {
+ 0x00000000,
+ 0x00000004,
+ 0x00000008,
+ 0x0000000C,
+ 0x00000010,
+ 0x00000014,
+ 0x00000018,
+ 0x00000040,
+ 0x00000044,
+ 0x00000048,
+ 0x00000058,
+ 0x00000080,
+ 0x00000084,
+ 0x000000C0,
+ 0x000000C4,
+ 0x00000100,
+ 0x00000104,
+ 0x00000108,
+ 0x0000010C
+ },
+ /* PCIe1 */
+ {
+ 0x00000020,
+ 0x00000024,
+ 0x00000028,
+ 0x0000002C,
+ 0x00000030,
+ 0x00000034,
+ 0x00000038,
+ 0x0000004C,
+ 0x00000050,
+ 0x00000054,
+ 0x0000005C,
+ 0x00000088,
+ 0x0000008C,
+ 0x000000C8,
+ 0x000000CC,
+ 0x00000110,
+ 0x00000114,
+ 0x00000118,
+ 0x0000011C
+ }
+};
+
+/* Keeping all DDR area of 512MB accesible for inbound transaction */
+#define INBOUND_ADDR_MASK 0x1FFFFFFF
+
+
+#define PCIE_SETUP_iATU_IB_ENTRY( _pp, _view_port, _base, _limit, _ctl1, _ctl2, _target ) \
+{\
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_VIEW_PORT, 4, (u32)(_view_port|iATU_VIEW_PORT_IN_BOUND)); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL2, 4, 0); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_SRC_LOW, 4, (u32)_base); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_SRC_HIGH, 4, 0); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_LIMIT, 4, (u32)((_base)+(_limit))); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_TRGT_LOW, 4, (u32)_target); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_TRGT_HIGH, 4, (u32)0); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL1, 4, (u32)_ctl1); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL2, 4, (u32)(_ctl2 |iATU_CTRL2_ID_EN) ); \
+}
+
+#define PCIE_SETUP_iATU_OB_ENTRY( _pp, _view_port, _base, _limit, _ctl1, _ctl2, _target ) \
+{\
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_VIEW_PORT, 4, (u32)_view_port); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL2, 4, 0); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_SRC_LOW, 4, (u32)_base); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_SRC_HIGH, 4, (u32)0); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_LIMIT, 4, ((u32)((_base)+(_limit)))); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_TRGT_LOW, 4, (u32)_target); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_TRGT_HIGH, 4, (u32)0); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL1, 4, (u32)_ctl1); \
+ comcerto_dbi_write_reg(_pp, PCIE_iATU_CTRL2, 4, (u32)(_ctl2 |iATU_CTRL2_ID_EN) ); \
+}
+
+#define MAX_LINK_UP_WAIT_JIFFIES HZ /* 1 Second */
+
+static unsigned long pcie_cnf_base_addr[MAX_PCIE_PORTS] =
+ { COMCERTO_AXI_PCIe0_BASE, COMCERTO_AXI_PCIe1_BASE };
+static unsigned long pcie_remote_base_addr[MAX_PCIE_PORTS] =
+ { COMCERTO_AXI_PCIe0_SLAVE_BASE, COMCERTO_AXI_PCIe1_SLAVE_BASE };
+static int pcie_msi_base[MAX_PCIE_PORTS] =
+ { PCIE0_MSI_INT_BASE, PCIE1_MSI_INT_BASE };
+static int pcie_intx_base[MAX_PCIE_PORTS] =
+ { PCIE0_INTX_BASE, PCIE1_INTX_BASE };
+static int pcie_irqs[MAX_PCIE_PORTS] =
+ { IRQ_PCIe0, IRQ_PCIe1 };
+
+
+static struct pcie_port pcie_port[MAX_PCIE_PORTS];
+
+static int pcie_port_is_host( int nr )
+{
+ struct pcie_port *pp= &pcie_port[nr];
+
+ return ( pp->port_mode == PCIE_PORT_MODE_RC ) ? 1 : 0;
+}
+
+/**
+ * This function sets PCIe port mode.
+ */
+static void pcie_port_set_mode( int nr, int mode )
+{
+ struct pcie_port *pp= &pcie_port[nr];
+
+ writel( (readl(pp->va_app_base + pp->app_regs->cfg0) & ~0xf) | mode, pp->va_app_base + pp->app_regs->cfg0);
+}
+
+/**
+ * This function returns the given port mode.
+ * @param nr PCIe Port number.
+ */
+static int pcie_port_get_mode( int nr )
+{
+ struct pcie_port *pp= &pcie_port[nr];
+
+ return ( readl(pp->va_app_base + pp->app_regs->cfg0) &
+ DWC_CFG0_DEV_TYPE_MASK );
+}
+
+/**
+ * This function checks whether link is up or not.
+ * Returns true if link is up otherwise returns false.
+ * @param pp Pointer to PCIe Port control block.
+ */
+static int comcerto_pcie_link_up( struct pcie_port *pp )
+{
+ unsigned long deadline = jiffies + MAX_LINK_UP_WAIT_JIFFIES;
+
+ do {
+ if (readl( pp->va_app_base + pp->app_regs->sts0 ) & STS0_RDLH_LINK_UP) {
+ return 1;
+ }
+
+ cond_resched();
+ } while (!time_after_eq(jiffies, deadline));
+
+ return 0;
+}
+
+/**
+ * bus_to_port().
+ *
+ */
+static struct pcie_port *bus_to_port(int bus)
+{
+ int i;
+
+ for (i = NUM_PCIE_PORTS - 1; i >= 0; i--) {
+ int rbus = pcie_port[i].root_bus_nr;
+ if ( !pcie_port_is_host(i) )
+ continue;
+ if (rbus != -1 && rbus <= bus)
+ break;
+ }
+
+ return i >= 0 ? pcie_port + i : NULL;
+}
+
+/**
+ * This function is used to read DBI registers.
+ */
+
+static void comcerto_dbi_read_reg(struct pcie_port *pp, int where, int size,
+ u32 *val)
+{
+ u32 va_address;
+
+ va_address = (u32)pp->va_dbi_base + (where & ~0x3);
+
+ *val = readl_relaxed(va_address);
+
+ if (size == 1)
+ *val = (*val >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *val = (*val >> (8 * (where & 3))) & 0xffff;
+}
+
+/**
+ * This function is used to write into DBI registers.
+ */
+static void comcerto_dbi_write_reg(struct pcie_port *pp, int where, int size,
+ u32 val)
+{
+ u32 va_address;
+ int pos, val1, mask = 0;
+
+ va_address = (u32)pp->va_dbi_base + (where & ~0x3);
+
+ pos = (where & 0x3) << 3;
+
+ if (size == 4)
+ val1 = val;
+ else
+ {
+ if (size == 2)
+ mask = 0xffff;
+ else if (size == 1)
+ mask = 0xff;
+
+ val1 = readl_relaxed(va_address);
+ val1 = ( val1 & ~( mask << pos ) ) | ( (val & mask) << pos );
+ }
+
+ writel_relaxed(val1, va_address);
+}
+
+static inline void nop_delay(void)
+{
+ int k;
+ for(k = 0 ; k < 1000; k++)
+ nop();
+}
+
+static int comcerto_pcie_rd_conf(struct pcie_port *pp, int bus_nr,
+ u32 devfn, int where, int size, u32 *val)
+{
+ u32 address;
+ u32 target_address = (u32)(bus_nr << 24) | (PCI_SLOT(devfn) << 19) | (PCI_FUNC(devfn) << 16);
+
+ /* Initialize iATU */
+ if (bus_nr != pp->root_bus_nr) {
+
+ if (pp->cfg1_prev_taddr != target_address) {
+ /* Type1 configuration request */
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_CNF1, (u32)iATU_GET_CFG1_BASE(pp->remote_mem_baseaddr),
+ iATU_CFG1_SIZE - 1, (AXI_OP_TYPE_CONFIG_RDRW_TYPE1 & iATU_CTRL1_TYPE_MASK),
+ 0, target_address );
+ pp->cfg1_prev_taddr = target_address;
+ }
+
+ address = (u32)pp->va_cfg1_base |(where & 0xFFFC);
+ } else {
+ if (pp->cfg0_prev_taddr != target_address) {
+ /* Type0 configuration request */
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_CNF0, (u32)iATU_GET_CFG0_BASE(pp->remote_mem_baseaddr),
+ iATU_CFG0_SIZE - 1, (AXI_OP_TYPE_CONFIG_RDRW_TYPE0 & iATU_CTRL1_TYPE_MASK),
+ 0, target_address );
+ pp->cfg0_prev_taddr = target_address;
+ }
+
+ address = (u32)pp->va_cfg0_base |(where & 0xFFFC);
+ }
+
+
+ *val = readl_relaxed(address);
+
+ /* Because of the imprecise external abort the processor is not able to get the exact instruction
+ which caused the abort and hence when the abort handler tries to restore the PC to the next
+ instruction to resume it is often wrong and it results in skipping few instruction after the
+ readl_relaxed which has caused abort. So nop instructions are added after readl so that even
+ if the some instructions are missed out it will miss the nop instruction only.
+ */
+ nop_delay();
+
+ if (size == 1)
+ *val = (*val >> (8 * (where & 3))) & 0xff;
+ else if (size == 2)
+ *val = (*val >> (8 * (where & 3))) & 0xffff;
+
+#ifdef COMCERTO_PCIE_DEBUG
+ printk(KERN_DEBUG "%s: bus:%d dev:%d where:%d, size:%d addr : %x value:%x\n", __func__, bus_nr, devfn, where, size, address, *val);
+#endif
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pcie_read_conf(struct pci_bus *bus, u32 devfn, int where, int size, u32 *val)
+{
+ struct pcie_port *pp = bus_to_port(bus->number);
+ unsigned long flags;
+ int ret;
+
+
+#ifdef COMCERTO_PCIE_DEBUG
+ printk(KERN_DEBUG "%s: bus:%d dev:%d where:%d, size:%d\n", __func__, bus->number, devfn, where, size);
+#endif
+
+
+ /* Make sure that link is up.
+ * Filter device numbers, unless it's a type1 access
+ */
+ if ( (!pp->link_state)||
+ ((bus->number == pp->root_bus_nr) && (PCI_SLOT(devfn) > 0)) ) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ BUG_ON (((where & 0x3) + size) > 4);
+
+ /* Enter critical section. */
+ spin_lock_irqsave(&pp->conf_lock, flags);
+ ret = comcerto_pcie_rd_conf(pp, bus->number, devfn, where, size, val);
+ /* Exit critical section. */
+ spin_unlock_irqrestore(&pp->conf_lock, flags);
+
+ return ret;
+}
+
+static int comcerto_pcie_wr_conf(struct pcie_port *pp, int bus_nr,
+ u32 devfn, int where, int size, u32 val)
+{
+ int ret = PCIBIOS_SUCCESSFUL;
+ u32 address;
+ u32 target_address = (u32)(bus_nr << 24) | (PCI_SLOT(devfn) << 19) | (PCI_FUNC(devfn) << 16);
+
+ /* Initialize iATU */
+ if (bus_nr != pp->root_bus_nr) {
+ if (pp->cfg1_prev_taddr != target_address) {
+ /* Type1 configuration request */
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_CNF1, (u32)iATU_GET_CFG1_BASE(pp->remote_mem_baseaddr),
+ iATU_CFG1_SIZE - 1, (AXI_OP_TYPE_CONFIG_RDRW_TYPE1 & iATU_CTRL1_TYPE_MASK),
+ 0, target_address );
+ pp->cfg1_prev_taddr = target_address;
+ }
+
+ address = (u32)pp->va_cfg1_base |(where & 0xFFFC);
+ } else {
+ if (pp->cfg0_prev_taddr != target_address) {
+ /* Type0 configuration request */
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_CNF0, (u32)iATU_GET_CFG0_BASE(pp->remote_mem_baseaddr),
+ iATU_CFG0_SIZE - 1, (AXI_OP_TYPE_CONFIG_RDRW_TYPE0 & iATU_CTRL1_TYPE_MASK),
+ 0, target_address );
+ pp->cfg0_prev_taddr = target_address;
+ }
+
+ address = (u32)pp->va_cfg0_base |(where & 0xFFFC);
+ }
+
+
+#ifdef COMCERTO_PCIE_DEBUG
+ printk(KERN_DEBUG "%s: bus:%d dev:%d where:%d, size:%d addr : %x value:%x\n", __func__, bus_nr, devfn, where, size, address, val);
+#endif
+ if (size == 4)
+ writel_relaxed(val, address);
+ else if (size == 2)
+ writew_relaxed(val, address + (where & 2));
+ else if (size == 1)
+ writeb_relaxed(val, address + (where & 3));
+ else
+ ret = PCIBIOS_BAD_REGISTER_NUMBER;
+
+ return ret;
+}
+
+static int pcie_write_conf(struct pci_bus *bus, u32 devfn, int where, int size, u32 val)
+{
+ struct pcie_port *pp = bus_to_port(bus->number);
+ unsigned long flags;
+ int ret;
+
+#ifdef COMCERTO_PCIE_DEBUG
+ printk(KERN_DEBUG "%s: bus:%d dev:%d where:%d, size:%d val:%d\n", __func__, bus->number, devfn, where, size, val);
+#endif
+
+ /* Make sure that link is up.
+ * Filter device numbers, unless it's a type1 access
+ */
+ if ( (!pp->link_state)||
+ ((bus->number == pp->root_bus_nr) && (PCI_SLOT(devfn) > 0)) ) {
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ BUG_ON (((where & 0x3) + size) > 4);
+
+ /* Enter critical section. */
+ spin_lock_irqsave(&pp->conf_lock, flags);
+ ret = comcerto_pcie_wr_conf(pp, bus->number, devfn, where, size, val);
+ if (where == PCI_COMMAND)
+ pp->cmd_reg_val = val & 0xffff;
+ /* Exit critical section. */
+ spin_unlock_irqrestore(&pp->conf_lock, flags);
+
+ return ret;
+}
+
+
+
+static u8 __init comcerto_pcie_swizzle(struct pci_dev *dev, u8 *pin)
+{
+ return PCI_SLOT(dev->devfn);
+}
+
+static int __init comcerto_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct pcie_port *pp = bus_to_port(dev->bus->number);
+ int irq = (PCIE0_INTX_BASE + pp->port * PCIE_NUM_INTX_IRQS + pin - 1);
+
+ return irq;
+}
+
+static int __init comcerto_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct pcie_port *pp;
+ struct pcie_app_reg *app_reg;
+ u32 val;
+
+
+ if ((nr >= NUM_PCIE_PORTS) || !pcie_port_is_host(nr))
+ return 0;
+
+#ifdef COMCERTO_PCIE_DEBUG
+ printk(KERN_DEBUG "%s:%d nr:%d\n", __func__, __LINE__, nr);
+#endif
+
+
+ pp = &pcie_port[nr];
+
+ if (!pp->link_state)
+ return 0;
+
+ pp->root_bus_nr = sys->busnr;
+ app_reg = pp->app_regs;
+
+ /* Allocate device memory mapped and IO mapped regions */
+ snprintf(pp->mem_space_name, sizeof(pp->mem_space_name),
+ "PCIe %d MEM", pp->port);
+ pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0;
+ pp->res[0].name = pp->mem_space_name;
+ pp->res[0].start = iATU_GET_MEM_BASE(pp->remote_mem_baseaddr);
+ pp->res[0].end = pp->res[0].start + iATU_MEM_SIZE - 1;
+ pp->res[0].flags = IORESOURCE_MEM;
+
+ snprintf(pp->io_space_name, sizeof(pp->io_space_name),
+ "PCIe %d I/O", pp->port);
+ pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0;
+ pp->res[1].name = pp->io_space_name;
+ pp->res[1].start = iATU_GET_IO_BASE(pp->remote_mem_baseaddr);
+ pp->res[1].end = pp->res[1].start + iATU_IO_SIZE - 1;
+ pp->res[1].flags = IORESOURCE_IO;
+
+
+ if (request_resource(&iomem_resource, &pp->res[0]))
+ {
+ printk(KERN_ERR "%s:can't allocate PCIe Memory space", __func__);
+ return 0;
+ }
+
+ if (request_resource(&iomem_resource, &pp->res[1]))
+ {
+ printk(KERN_ERR "%s:can't allocate PCIe IO space", __func__);
+ return 0;
+ }
+
+ /* Generic PCIe unit setup.*/
+
+ /* Enable own BME. It is necessary to enable own BME to do a
+ * memory transaction on a downstream device
+ */
+ comcerto_dbi_read_reg(pp, PCI_COMMAND, 2, &val);
+ val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER
+ | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+ comcerto_dbi_write_reg(pp, PCI_COMMAND, 2, val);
+
+ /* Need to come back here*/
+
+ sys->resource[0] = &pp->res[0];
+ sys->resource[1] = &pp->res[1];
+ sys->resource[2] = NULL;
+
+ pp->cfg0_prev_taddr = 0xffffffff;
+ pp->cfg1_prev_taddr = 0xffffffff;
+
+ return 1;
+}
+
+static struct pci_ops pcie_ops = {
+ .read = pcie_read_conf,
+ .write = pcie_write_conf,
+};
+
+static struct pci_bus *__init comcerto_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ struct pci_bus *bus;
+
+ if ((nr < NUM_PCIE_PORTS) && (pcie_port_is_host(nr))) {
+
+ bus = pci_scan_bus(sys->busnr, &pcie_ops, sys);
+ } else {
+ bus = NULL;
+ BUG();
+ }
+
+ return bus;
+}
+
+static struct hw_pci comcerto_pcie __initdata = {
+ .nr_controllers = NUM_PCIE_PORTS,
+ .swizzle = comcerto_pcie_swizzle,
+ .map_irq = comcerto_pcie_map_irq,
+ .setup = comcerto_pcie_setup,
+ .scan = comcerto_pcie_scan_bus,
+};
+
+
+
+#ifdef CONFIG_PCI_MSI
+/* MSI int handler
+ */
+static void handle_msi(struct pcie_port *pp)
+{
+ unsigned long val, mask;
+ unsigned int pos, mask0;
+
+
+ val = readl_relaxed(pp->va_dbi_base + PCIE_MSI_INTR0_STATUS);
+
+continue_handle:
+
+ pos = 0;
+
+ while (val) {
+ mask0 = 1 << pos;
+
+ if (val & mask0) {
+ /* FIXME : WA for bz69520
+ * To avoid race condition during avk the interrupt disabling interrupt before
+ * Ack and enabling after Ack.
+ */
+ spin_lock(&pp->intr_lock);
+ mask = readl_relaxed(pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE);
+ writel_relaxed(mask & ~mask0, pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE);
+ writel_relaxed(mask0, pp->va_dbi_base + PCIE_MSI_INTR0_STATUS);
+ writel_relaxed(mask, pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE);
+ spin_unlock(&pp->intr_lock);
+ generic_handle_irq(pp->msi_base + pos);
+ val = val & ~mask0;
+ }
+ pos++;
+ }
+
+ val = readl_relaxed(pp->va_dbi_base + PCIE_MSI_INTR0_STATUS);
+ if(val)
+ goto continue_handle;
+
+#if 0
+ for (i = 0; i < (PCIE_NUM_MSI_IRQS >> 5); i++) {
+ val = readl_relaxed(pp->va_dbi_base + PCIE_MSI_INTR0_STATUS + (i * 12));
+ if (val) {
+ pos = 0;
+ while ((pos = find_next_bit(&val, 32, pos)) != 32) {
+ /* FIXME : WA for bz69520
+ * To avoid race condition during avk the interrupt disabling interrupt before
+ * Ack and enabling after Ack.
+ */
+ spin_lock(&pp->intr_lock);
+ mask = readl_relaxed(pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE + (i * 12));
+ writel_relaxed(mask & ~(1 << pos), pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE + (i * 12));
+ writel_relaxed((1 << pos), pp->va_dbi_base + PCIE_MSI_INTR0_STATUS + (i * 12));
+ writel_relaxed(mask & (1 << pos), pp->va_dbi_base + PCIE_MSI_INTR0_ENABLE + (i * 12));
+ spin_unlock(&pp->intr_lock);
+ generic_handle_irq(pp->msi_base + (i * 32) + pos);
+ pos++;
+ }
+ }
+ }
+#endif
+}
+#else
+static void handle_msi(struct pcie_port *pp)
+{
+}
+#endif
+
+static void comcerto_pcie_int_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct pcie_port *pp = irq_get_handler_data(irq);
+ struct pcie_app_reg *app_reg = pp->app_regs;
+ struct irq_chip *chip;
+ unsigned int status;
+ int pos, ii;
+
+ chip = irq_desc_get_chip(desc);
+ chained_irq_enter(chip, desc);
+ status = readl_relaxed(pp->va_app_base + app_reg->intr_sts);
+
+ if (status & INTR_CTRL_MSI) {
+ writel_relaxed(INTR_CTRL_MSI, (pp->va_app_base + app_reg->intr_sts));
+ status &= ~(INTR_CTRL_MSI);
+ handle_msi(pp);
+ }
+
+ for (ii = 0; (ii < PCIE_NUM_INTX_IRQS) && status; ii++) {
+ pos = ii << 1;
+
+ /* Handle INTx Assert */
+ if (status & (INTR_CTRL_INTA_ASSERT << pos)) {
+ status &= ~(INTR_CTRL_INTA_ASSERT << pos);
+ writel_relaxed((INTR_CTRL_INTA_ASSERT << pos), (pp->va_app_base + app_reg->intr_sts));
+ generic_handle_irq(pp->intx_base + ii);
+ }
+
+ /*FIXME : No need to handle DEASSERT, simply clear the interrupt */
+ status &= ~(INTR_CTRL_INTA_DEASSERT << pos);
+#if 0
+ /* Handle INTx Deasert */
+ if (status & (INTR_CTRL_INTA_DEASSERT << pos)) {
+ status &= ~(INTR_CTRL_INTA_DEASSERT << pos);
+ writel_relaxed((INTR_CTRL_INTA_DEASSERT << pos), (pp->va_app_base + app_reg->intr_sts));
+ }
+#endif
+ }
+
+ if (status) {
+ printk(KERN_INFO "%s:Unhandled interrupt %x\n", __func__, status);
+ /* FIXME: HP, AER, PME interrupts need to be handled */
+ writel_relaxed(status, (pp->va_app_base + app_reg->intr_sts));
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+
+static void pcie_nop_intx_irq(struct irq_data *data )
+{
+ return;
+}
+
+static void pcie_unmask_intx_irq( struct irq_data *data )
+{
+ struct pcie_port *pp = data->chip_data;
+
+ spin_lock(&pp->conf_lock);
+ comcerto_pcie_wr_conf(pp, pp->root_bus_nr, 0, PCI_COMMAND, 2, (pp->cmd_reg_val & ~PCI_COMMAND_INTX_DISABLE));
+ spin_unlock(&pp->conf_lock);
+}
+
+static void pcie_enable_intx_irq( struct irq_data *data )
+{
+ int irq = data->irq;
+ struct pcie_port *pp = data->chip_data;
+ int irq_offset = irq - pp->intx_base;
+ struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->app_regs;
+ unsigned long flags;
+
+ if( irq_offset >= PCIE_NUM_INTX_IRQS )
+ return;
+
+ /*
+ * In interrupt sts/mask register each interrupt has two
+ * consecutive bits, one for assert and another for dessert.
+ */
+ irq_offset = irq_offset << 1;
+
+ spin_lock_irqsave(&pp->intr_lock, flags);
+ writel_relaxed(readl_relaxed(pp->va_app_base + app_reg->intr_en) | ( INTR_CTRL_INTA_ASSERT << irq_offset),
+ (pp->va_app_base + app_reg->intr_en) );
+ spin_unlock_irqrestore(&pp->intr_lock, flags);
+}
+
+static void pcie_mask_intx_irq( struct irq_data *data )
+{
+ struct pcie_port *pp = data->chip_data;
+
+ spin_lock(&pp->conf_lock);
+ comcerto_pcie_wr_conf(pp, pp->root_bus_nr, 0, PCI_COMMAND, 2, (pp->cmd_reg_val | PCI_COMMAND_INTX_DISABLE));
+ spin_unlock(&pp->conf_lock);
+}
+
+static void pcie_disable_intx_irq( struct irq_data *data )
+{
+ int irq = data->irq;
+ struct pcie_port *pp = data->chip_data;
+ int irq_offset = irq - pp->intx_base;
+ struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->app_regs;
+ unsigned long flags;
+
+ if( irq_offset >= PCIE_NUM_INTX_IRQS )
+ return;
+
+ /*
+ * In interrupt sts/mask register each interrupt has two
+ * consecutive bits, one for assert and another for dessert.
+ */
+ irq_offset = irq_offset << 1;
+
+ spin_lock_irqsave(&pp->intr_lock, flags);
+ writel_relaxed( readl_relaxed(pp->va_app_base + app_reg->intr_en) & ~( INTR_CTRL_INTA_ASSERT << irq_offset),
+ (pp->va_app_base + app_reg->intr_en) );
+ spin_unlock_irqrestore(&pp->intr_lock, flags);
+}
+
+static struct irq_chip pcie_intx_chip = {
+ .name = "PCIe INTx",
+ .irq_ack = pcie_nop_intx_irq,
+ .irq_enable = pcie_enable_intx_irq,
+ .irq_disable = pcie_disable_intx_irq,
+ .irq_mask = pcie_mask_intx_irq,
+ .irq_unmask = pcie_unmask_intx_irq,
+};
+
+static int comcerto_pcie_intx_init(struct pcie_port *pp)
+{
+ int i, irq;
+ struct pcie_app_reg *app_reg;
+
+ /* Disable INTX interrupt*/
+ app_reg = pp->app_regs;
+
+ writel(readl(pp->va_app_base + app_reg->intr_en) &
+ ~(INTR_CTRL_INTA_ASSERT |
+ INTR_CTRL_INTB_ASSERT |
+ INTR_CTRL_INTC_ASSERT |
+ INTR_CTRL_INTD_ASSERT),
+ pp->va_app_base + app_reg->intr_en );
+
+ /* initilize INTX chip here only. MSI chip will be
+ * initilized dynamically.*/
+ irq = pp->intx_base;
+ for (i = 0; i < PCIE_NUM_INTX_IRQS; i++) {
+ irq_set_chip_data(irq + i, pp);
+ irq_set_chip_and_handler(irq + i, &pcie_intx_chip,
+ handle_level_irq);
+ set_irq_flags(irq + i, IRQF_VALID);
+ }
+
+ irq_set_handler_data(pp->irq, pp);
+ irq_set_chained_handler(pp->irq, comcerto_pcie_int_handler);
+
+ /* FIXME Added for debuging */
+ writel(readl(pp->va_app_base + app_reg->intr_en) &
+ ~(INTR_CTRL_AER | INTR_CTRL_PME |
+ INTR_CTRL_HP | INTR_CTRL_LINK_AUTO_BW ),
+ pp->va_app_base + app_reg->intr_en );
+
+
+ return 0;
+}
+
+static int comcerto_pcie_rc_int_init( struct pcie_port *pp )
+{
+
+ printk(KERN_INFO "%s\n",__func__);
+
+ comcerto_pcie_intx_init(pp);
+
+#ifdef CONFIG_PCI_MSI
+ comcerto_msi_init(pp);
+#endif
+
+ return 0;
+}
+
+#ifdef CONFIG_PCI_MSI
+static int find_valid_pos0(int port, int nvec, int pos, int *pos0)
+{
+ int flag = 1;
+ do {
+ pos = find_next_zero_bit(msi_irq_in_use[port],
+ PCIE_NUM_MSI_IRQS, pos);
+ /*if you have reached to the end then get out from here.*/
+ if (pos == PCIE_NUM_MSI_IRQS)
+ return -ENOSPC;
+ /* Check if this position is at correct offset.nvec is always a
+ * power of two. pos0 must be nvec bit alligned.
+ */
+ if (pos % nvec)
+ pos += nvec - (pos % nvec);
+ else
+ flag = 0;
+ } while (flag);
+
+ *pos0 = pos;
+ return 0;
+}
+
+#define GET_MSI_INT_REG_POS(_irq) ((_irq >> 5) * 12)
+#define GET_MSI_INT_OFST(_irq) (_irq & 0x1F)
+
+static void comcerto_msi_nop(struct irq_data *data)
+{
+ return;
+}
+
+static void comcerto_msi_unmask(struct irq_data *data)
+{
+ struct pcie_port *pp = data->chip_data;
+ int irq = data->irq - pp->msi_base;
+ int ofst, pos;
+ unsigned int val;
+ unsigned long flags;
+
+ if( irq >= PCIE_NUM_MSI_IRQS )
+ return;
+
+ pos = GET_MSI_INT_REG_POS(irq);
+ ofst = GET_MSI_INT_OFST(irq);
+#ifdef COMCERTO_PCIE_DEBUG
+ printk(KERN_DEBUG "%s: %x:%x\n",__func__, pos, ofst);
+#endif
+
+ spin_lock_irqsave(&pp->intr_lock, flags);
+ comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_MASK + pos, 4, &val);
+ val &= ~(1 << ofst);
+ comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_MASK + pos, 4, val);
+ spin_unlock_irqrestore(&pp->intr_lock, flags);
+
+ return;
+}
+
+static void comcerto_msi_mask(struct irq_data *data)
+{
+ struct pcie_port *pp = data->chip_data;
+ int irq = data->irq - pp->msi_base;
+ int ofst, pos;
+ unsigned int val;
+ unsigned long flags;
+
+ if( irq >= PCIE_NUM_MSI_IRQS )
+ return;
+
+ pos = GET_MSI_INT_REG_POS(irq);
+ ofst = GET_MSI_INT_OFST(irq);
+#ifdef COMCERTO_PCIE_DEBUG
+ printk(KERN_DEBUG "%s: %x:%x\n",__func__, pos, ofst);
+#endif
+
+ spin_lock_irqsave(&pp->intr_lock, flags);
+ comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_MASK + pos, 4, &val);
+ val |= (1 << ofst);
+ comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_MASK + pos, 4, val);
+ spin_unlock_irqrestore(&pp->intr_lock, flags);
+
+ return;
+}
+
+
+static void comcerto_msi_enable(struct irq_data *data)
+{
+ struct pcie_port *pp = data->chip_data;
+ int irq = data->irq - pp->msi_base;
+ int ofst, pos;
+ unsigned int val;
+ unsigned long flags;
+
+ if( irq >= PCIE_NUM_MSI_IRQS )
+ return;
+
+ pos = GET_MSI_INT_REG_POS(irq);
+ ofst = GET_MSI_INT_OFST(irq);
+
+#ifdef COMCERTO_PCIE_DEBUG
+ printk(KERN_DEBUG "%s: %x:%x\n",__func__, pos, ofst);
+#endif
+
+ spin_lock_irqsave(&pp->intr_lock, flags);
+ comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + pos, 4, &val);
+ val |= (1 << ofst);
+ comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + pos, 4, val);
+ spin_unlock_irqrestore(&pp->intr_lock, flags);
+
+ return;
+}
+
+
+static void comcerto_msi_disable(struct irq_data *data)
+{
+ struct pcie_port *pp = data->chip_data;
+ int irq = data->irq - pp->msi_base;
+ int ofst, pos;
+ unsigned int val;
+ unsigned long flags;
+
+ if( irq >= PCIE_NUM_MSI_IRQS )
+ return;
+
+ pos = GET_MSI_INT_REG_POS(irq);
+ ofst = GET_MSI_INT_OFST(irq);
+#ifdef COMCERTO_PCIE_DEBUG
+ printk(KERN_DEBUG "%s: %x:%x\n",__func__, pos, ofst);
+#endif
+
+ spin_lock_irqsave(&pp->intr_lock, flags);
+ comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + pos, 4, &val);
+ val &= ~(1 << ofst);
+ comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + pos, 4, val);
+ spin_unlock_irqrestore(&pp->intr_lock, flags);
+
+ return;
+}
+
+static struct irq_chip comcerto_msi_chip = {
+ .name = "PCI-MSI",
+ .irq_ack = comcerto_msi_nop,
+ .irq_enable = comcerto_msi_enable,
+ .irq_disable = comcerto_msi_disable,
+ .irq_mask = comcerto_msi_mask,
+ .irq_unmask = comcerto_msi_unmask,
+};
+
+/*
+ * Dynamic irq allocate and deallocation
+ */
+static int get_irq(int nvec, struct msi_desc *desc, int *pos)
+{
+ int irq, pos0, pos1, i;
+ struct pcie_port *pp = bus_to_port(desc->dev->bus->number);
+
+ pos0 = find_first_zero_bit(msi_irq_in_use[pp->port],
+ PCIE_NUM_MSI_IRQS);
+ if (pos0 % nvec) {
+ if (find_valid_pos0(pp->port, nvec, pos0, &pos0))
+ goto no_valid_irq;
+ }
+ if (nvec > 1) {
+ pos1 = find_next_bit(msi_irq_in_use[pp->port],
+ PCIE_NUM_MSI_IRQS, pos0);
+ /* there must be nvec number of consecutive free bits */
+ while ((pos1 - pos0) < nvec) {
+ if (find_valid_pos0(pp->port, nvec, pos1, &pos0))
+ goto no_valid_irq;
+ pos1 = find_next_bit(msi_irq_in_use[pp->port],
+ PCIE_NUM_MSI_IRQS, pos0);
+ }
+ }
+
+ irq = pp->msi_base + pos0;
+
+ if ((irq + nvec) > (pp->msi_base + PCIE_NUM_MSI_IRQS))
+ goto no_valid_irq;
+
+ i = 0;
+ while (i < nvec) {
+ set_bit(pos0 + i, msi_irq_in_use[pp->port]);
+ dynamic_irq_init(irq + i);
+ irq_set_chip_data(irq + i, pp);
+ irq_set_chip_and_handler(irq + i, &comcerto_msi_chip,
+ handle_simple_irq);
+ set_irq_flags(irq + i, IRQF_VALID);
+ i++;
+ }
+
+ irq_set_msi_desc(irq, desc);
+
+ *pos = pos0;
+ return irq;
+no_valid_irq:
+ printk(KERN_ERR "%s : MSI interrupt allocate failed\n", __func__);
+ *pos = pos0;
+ return -ENOSPC;
+}
+
+static void clean_irq(unsigned int irq)
+{
+ int res, bit, val, pos;
+ struct irq_data *data = irq_get_irq_data(irq);
+ struct pcie_port *pp = data->chip_data;
+ unsigned long flags;
+
+ if( !pp )
+ return;
+
+ pos = irq - pp->msi_base;
+
+ dynamic_irq_cleanup(irq);
+
+ spin_lock_irqsave(&pp->msi_map_lock, flags);
+ clear_bit(pos, msi_irq_in_use[pp->port]);
+
+ /* Disable corresponding interrupt on MSI interrupt
+ * controller.
+ */
+ res = (pos / 32) * 12;
+ bit = pos % 32;
+ comcerto_dbi_read_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
+ val &= ~(1 << bit);
+ comcerto_dbi_write_reg(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
+ spin_unlock_irqrestore(&pp->msi_map_lock, flags);
+
+}
+
+int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+{
+ int irq, pos;
+ struct msi_msg msg;
+ struct pcie_port *pp = bus_to_port(pdev->bus->number);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pp->msi_map_lock, flags);
+ irq = get_irq(1, desc, &pos);
+ spin_unlock_irqrestore(&pp->msi_map_lock, flags);
+
+
+ if (irq < 0)
+ return irq;
+
+ desc->msi_attrib.multiple = 0;
+
+ /* An EP will modify lower 8 bits(max) of msi data while
+ * sending any msi interrupt
+ */
+ msg.address_hi = 0x0;
+ msg.address_lo = pp->msi_mbox_handle;
+ msg.data = pos;
+ write_msi_msg(irq, &msg);
+
+ return 0;
+}
+
+void arch_teardown_msi_irq(unsigned int irq)
+{
+ clean_irq(irq);
+}
+
+static int comcerto_msi_init(struct pcie_port *pp)
+{
+ struct pcie_app_reg *app_reg = (struct pcie_app_reg *)pp->app_regs;
+
+ pp->msi_mbox_baseaddr = dma_alloc_coherent(NULL, sizeof(u32), &pp->msi_mbox_handle, GFP_KERNEL);
+ if (!pp->msi_mbox_baseaddr) {
+ printk(KERN_ERR "PCIe(%d): failed to allocate msi mailbox coherent memory\n", pp->port);
+ goto err;
+ }
+
+ comcerto_dbi_write_reg(pp, PCIE_MSI_ADDR_LO, 4, pp->msi_mbox_handle);
+ comcerto_dbi_write_reg(pp, PCIE_MSI_ADDR_HI, 4, 0);
+ /* Enbale MSI interrupt*/
+ writel(readl(pp->va_app_base + app_reg->intr_en) | INTR_CTRL_MSI,
+ pp->va_app_base + app_reg->intr_en);
+ return 0;
+
+err:
+ return -1;
+}
+#endif
+
+static void comcerto_pcie_rc_init(struct pcie_port *pp)
+{
+ struct pcie_app_reg *app_reg = pp->app_regs;
+ unsigned int val;
+
+
+ //FIXME : Bit:27 definition is not clear from document
+ // This register setting is copied from simulation code.
+ writel(readl(pp->va_app_base + app_reg->cfg0) | 0x08007FF0,
+ pp->va_app_base + app_reg->cfg0);
+
+
+ /**
+ *FIXME : Bit:15 definition is not clear from document
+ * This register setting is copied from simulation code.
+ */
+ writel(readl(pp->va_app_base + app_reg->cfg4) | 0x00008000,
+ pp->va_app_base + app_reg->cfg4);
+
+ comcerto_dbi_read_reg(pp, PCIE_AFL0L1_REG, 4, &val);
+ val &= ~(0x00FFFF00);
+ val |= 0x00F1F100;
+ comcerto_dbi_write_reg(pp, PCIE_AFL0L1_REG, 4, val);
+
+ if(pcie_gen1_only)
+ {
+ comcerto_dbi_write_reg(pp, PCIE_LCNT2_REG, 4, 0x1);
+ comcerto_dbi_write_reg(pp, PCIE_LCAP_REG, 4, 0x1);
+ }
+ else
+ {
+ comcerto_dbi_read_reg(pp, PCIE_G2CTRL_REG, 4, &val);
+ val &= ~(0xFF);
+ val |= 0xF1;
+ comcerto_dbi_write_reg(pp, PCIE_G2CTRL_REG, 4, val);
+
+ // instruct pcie to switch to gen2 after init
+ comcerto_dbi_read_reg(pp, PCIE_G2CTRL_REG, 4, &val);
+ val |= (1 << 17);
+ comcerto_dbi_write_reg(pp, PCIE_G2CTRL_REG, 4, val);
+ }
+
+ /*setup iATU for outbound translation */
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_MEM, iATU_GET_MEM_BASE(pp->remote_mem_baseaddr),
+ iATU_MEM_SIZE - 1, 0, 0, pp->remote_mem_baseaddr );
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_IO, iATU_GET_IO_BASE(pp->remote_mem_baseaddr),
+ iATU_IO_SIZE - 1, (AXI_OP_TYPE_IO_RDRW & iATU_CTRL1_TYPE_MASK),
+ 0, iATU_GET_IO_BASE(pp->remote_mem_baseaddr) );
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_MSG, iATU_GET_MSG_BASE(pp->remote_mem_baseaddr),
+ iATU_MSG_SIZE - 1, (AXI_OP_TYPE_MSG_REQ & iATU_CTRL1_TYPE_MASK),
+ 0, iATU_GET_MSG_BASE(pp->remote_mem_baseaddr) );
+ PCIE_SETUP_iATU_IB_ENTRY( pp, 0, 0,
+ INBOUND_ADDR_MASK, 0, 0, COMCERTO_AXI_DDR_BASE);
+
+}
+
+static int pcie_app_init(struct pcie_port *pp, int nr, int mode)
+{
+
+ if (nr > MAX_PCIE_PORTS) {
+ printk(KERN_ERR "%s: Invalid port\n", __func__);
+ goto err0;
+ }
+
+ if (mode != CFG0_DEV_TYPE_RC) {
+ printk(KERN_ERR "%s: Unsupported mode selected mode: %d. \n", __func__, mode);
+ goto err0;
+ }
+
+ memset(pp, 0, sizeof(struct pcie_port));
+
+ pp->port = nr;
+ pp->app_regs = &app_regs[nr];
+ pp->root_bus_nr = nr;
+ pp->base = pcie_cnf_base_addr[nr];
+ pp->app_base = (unsigned long)COMCERTO_APB_PCI_SATA_USB_CTRL_BASE;
+ pp->va_app_base = (void __iomem *)APB_VADDR(COMCERTO_APB_PCI_SATA_USB_CTRL_BASE);
+ pp->remote_mem_baseaddr = pcie_remote_base_addr[nr];
+
+ if (!nr)
+ pp->va_dbi_base = (void __iomem *) COMCERTO_AXI_PCIe0_VADDR_BASE;
+ else
+ pp->va_dbi_base = (void __iomem *) COMCERTO_AXI_PCIe1_VADDR_BASE;
+
+ pp->va_cfg0_base = (void __iomem *)
+ ioremap( iATU_GET_CFG0_BASE(pp->remote_mem_baseaddr), iATU_CFG0_SIZE);
+ if (!pp->va_cfg0_base) {
+ pr_err("error with ioremap in function %s\n", __func__);
+ goto err0;
+ }
+ pp->va_cfg1_base = (void __iomem *)
+ ioremap( iATU_GET_CFG1_BASE(pp->remote_mem_baseaddr) , iATU_CFG1_SIZE);
+ if (!pp->va_cfg1_base) {
+ pr_err("error with ioremap in function %s\n", __func__);
+ goto err1;
+ }
+
+ pp->intx_base = pcie_intx_base[nr];
+ pp->msi_base = pcie_msi_base[nr];
+ pp->irq = pcie_irqs[nr];
+ spin_lock_init(&pp->conf_lock);
+ spin_lock_init(&pp->intr_lock);
+ spin_lock_init(&pp->msi_map_lock);
+ memset(pp->res, 0, sizeof(pp->res));
+
+ /* Get the reference clock to PCIe port*/
+ switch (nr) {
+ case 0:
+ pp->ref_clock = clk_get(NULL, "pcie0");
+ break;
+ case 1:
+ pp->ref_clock = clk_get(NULL, "pcie1");
+ break;
+ default:
+ printk(KERN_ERR "%s: Invalid port\n", __func__);
+ goto err2;
+ }
+ if (IS_ERR(pp->ref_clock)) {
+ pr_err("%s: Unable to obtain pcie%d clock: %ld\n", __func__, nr, PTR_ERR(pcie_port[nr].ref_clock));
+ goto err2;
+ }
+
+ /* Enable the PCIE_OCC clock */
+#if defined(CONFIG_COMCERTO_PCIE_OCC_CLOCK)
+ pp->occ_clock = clk_get(NULL,"pcie_occ");
+ if (IS_ERR(pp->occ_clock)) {
+ pr_err("%s: Unable to obtain pcie_occ clock: %ld\n", __func__, PTR_ERR(pp->occ_clock));
+ goto err_occ_clock;
+ }
+#endif
+ pcie_port_set_mode(nr, mode);
+
+ return 0;
+
+#if defined(CONFIG_COMCERTO_PCIE_OCC_CLOCK)
+err_occ_clock:
+ clk_put(pp->occ_clock);
+#endif
+
+err2:
+ iounmap(pp->va_cfg1_base);
+err1:
+ iounmap(pp->va_cfg0_base);
+err0:
+ return -1;
+
+}
+
+#if defined(CONFIG_C2K_EVM) || defined(CONFIG_C2K_ASIC)
+#define PCIE_DEV_EXT_RESET_DEASSERT(_id) \
+ writel(readl(COMCERTO_GPIO_OUTPUT_REG) & ~(GPIO_PIN_27), COMCERTO_GPIO_OUTPUT_REG);
+
+#define PCIE_DEV_EXT_RESET_ASSERT(_id) \
+ writel(readl(COMCERTO_GPIO_OUTPUT_REG) | (GPIO_PIN_27), COMCERTO_GPIO_OUTPUT_REG);
+#else
+/* Board specific */
+#define PCIE_DEV_EXT_RESET_DEASSERT(_id)
+#define PCIE_DEV_EXT_RESET_ASSERT(_id)
+#endif
+
+static int comcerto_pcie_device_reset(struct pcie_port *pp)
+{
+ int ii;
+ struct pcie_port *l_pp = pp;
+ struct pci_dev *pci_dev = NULL;
+
+ printk(KERN_INFO "ENTER: Bringing PCIe%d device reset\n", pp->port);
+ if (!pp->link_state)
+ return -1;
+
+ /* On C2KEVM and ASIC same reset is connected to PCIe0/1.
+ * So, device might have kept in reset, using other PCIe host.
+ */
+ if (!comcerto_pcie_link_up(pp)) {
+ printk(KERN_INFO "%s : Device is already link down state\n", __func__);
+ return 0;
+ }
+
+
+ /*FIXME : Below code might be required if we want to reset pcie device, without
+ * invoking pcie device supend. If we invoke pcie device suspend it will
+ * take care of saving pcie config space.
+ */
+#if 0
+ /* Now save the PCIe device configuration space.*/
+ while((pci_dev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_dev))) {
+ if (pp->root_bus_nr == pci_dev->bus->number) {
+ pci_save_state(pci_dev);
+ }
+ }
+
+ /* On C2KEVM/ASIC, since same GPIO27 is used to reset PCIe0/PCIe1 devices,
+ * save configuration of devices on other PCIe also.
+ * This may not be applicable for other cutomer boards.
+ */
+#if defined(CONFIG_C2K_EVM) || defined(CONFIG_C2K_ASIC)
+ l_pp = &pcie_port[!pp->port];
+ pci_dev = NULL;
+
+ if (l_pp->link_state) {
+ while((pci_dev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_dev))) {
+ if (l_pp->root_bus_nr == pci_dev->bus->number) {
+ pci_save_state(pci_dev);
+ }
+ }
+ }
+#endif
+#endif
+
+ /************** Ready to issue rest *****************/
+ /* De-assert external reset (GPIO-27) */
+ PCIE_DEV_EXT_RESET_DEASSERT(pp->port);
+ printk(KERN_INFO "EXIT: Bringing PCIe%d device reset\n", pp->port);
+
+ return 0;
+
+}
+
+
+static int comcerto_pcie_device_reset_exit(struct pcie_port *pp)
+{
+ unsigned int val;
+ struct pci_dev *pci_dev;
+
+ printk(KERN_INFO "ENTER: Bringing PCIe%d device out-of-reset\n", pp->port);
+
+ if (!pp->link_state && pp->reset)
+ return -1;
+
+ /* Pull up external reset */
+ /* assert external reset (GPIO-27) */
+ PCIE_DEV_EXT_RESET_ASSERT(pp->port);
+
+ udelay(1000);
+ udelay(1000);
+ udelay(1000);
+ udelay(1000);
+ udelay(1000);
+ udelay(1000);
+ udelay(1000);
+
+ /* Restore the RC configuration */
+ comcerto_dbi_read_reg(pp, PCIE_AFL0L1_REG, 4, &val);
+ val &= ~(0x00FFFF00);
+ val |= 0x00F1F100;
+ comcerto_dbi_write_reg(pp, PCIE_AFL0L1_REG, 4, val);
+
+ if(pcie_gen1_only)
+ {
+ comcerto_dbi_write_reg(pp, PCIE_LCNT2_REG, 4, 0x1);
+ comcerto_dbi_write_reg(pp, PCIE_LCAP_REG, 4, 0x1);
+ }
+ else
+ {
+ comcerto_dbi_read_reg(pp, PCIE_G2CTRL_REG, 4, &val);
+ val &= ~(0xFF);
+ val |= 0xF1;
+ comcerto_dbi_write_reg(pp, PCIE_G2CTRL_REG, 4, val);
+ }
+
+ // instruct pcie to switch to gen2 after init
+ comcerto_dbi_read_reg(pp, PCIE_G2CTRL_REG, 4, &val);
+ val |= (1 << 17);
+ comcerto_dbi_write_reg(pp, PCIE_G2CTRL_REG, 4, val);
+
+ /*setup iATU for outbound translation */
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_MEM, iATU_GET_MEM_BASE(pp->remote_mem_baseaddr),
+ iATU_MEM_SIZE - 1, 0, 0, pp->remote_mem_baseaddr );
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_IO, iATU_GET_IO_BASE(pp->remote_mem_baseaddr),
+ iATU_IO_SIZE - 1, (AXI_OP_TYPE_IO_RDRW & iATU_CTRL1_TYPE_MASK),
+ 0, iATU_GET_IO_BASE(pp->remote_mem_baseaddr) );
+ PCIE_SETUP_iATU_OB_ENTRY( pp, iATU_ENTRY_MSG, iATU_GET_MSG_BASE(pp->remote_mem_baseaddr),
+ iATU_MSG_SIZE - 1, (AXI_OP_TYPE_MSG_REQ & iATU_CTRL1_TYPE_MASK),
+ 0, iATU_GET_MSG_BASE(pp->remote_mem_baseaddr) );
+ PCIE_SETUP_iATU_IB_ENTRY( pp, 0, 0,
+ INBOUND_ADDR_MASK, 0, 0, COMCERTO_AXI_DDR_BASE);
+
+ comcerto_dbi_write_reg(pp, PCIE_MSI_ADDR_LO, 4, pp->msi_mbox_handle);
+ comcerto_dbi_write_reg(pp, PCIE_MSI_ADDR_HI, 4, 0);
+
+ writel_relaxed(0x7, pp->va_app_base + pp->app_regs->cfg5);
+
+ /* Generic PCIe unit setup.*/
+
+ /* Enable own BME. It is necessary to enable own BME to do a
+ * memory transaction on a downstream device
+ */
+ comcerto_dbi_read_reg(pp, PCI_COMMAND, 2, &val);
+ val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER
+ | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+ comcerto_dbi_write_reg(pp, PCI_COMMAND, 2, val);
+
+ pp->cfg0_prev_taddr = 0xffffffff;
+ pp->cfg1_prev_taddr = 0xffffffff;
+
+ //udelay(1000);
+
+ if(comcerto_pcie_link_up(pp)) {
+ printk(KERN_INFO " Bringing PCIe%d device out-of-reset : Link Up\n", pp->port);
+ /*FIXME : Below code might be required if we want to bring pcie device out-of-reset,
+ * without invoking pcie device supend. If we invoke pcie device resume it will
+ * take care of restoring, saved pcie config space.
+ */
+#if 0
+ pci_dev = NULL;
+ while((pci_dev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_dev))) {
+ if (pp->root_bus_nr == pci_dev->bus->number) {
+ pci_restore_state(pci_dev);
+ }
+ }
+#endif
+ }
+
+ printk(KERN_INFO "EXIT: Bringing PCIe%d device out-of-reset\n", pp->port);
+ return 0;
+
+}
+
+static ssize_t comcerto_pcie_show_reset(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pcie_port *pp = &pcie_port[pdev->id];
+
+ return sprintf(buf, "%d\n", pp->reset);
+}
+
+static ssize_t comcerto_pcie_set_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pcie_port *pp = &pcie_port[pdev->id];
+ int reset = 0, rc;
+
+ if (!pcie_port_is_host(pdev->id) || !(pp->link_state))
+ return count;
+
+ sscanf(buf, "%d", &reset);
+
+ reset = reset ? 1:0;
+
+ if (pp->reset == reset) {
+ printk(KERN_INFO "%s: Already in same state\n", __func__);
+ return count;
+ }
+
+
+ if (reset) {
+ printk(KERN_INFO "ENTER : Putting PCIe%d device into reset\n", pdev->id);
+
+ if (!comcerto_pcie_device_reset(pp)) {
+ int ii = 10;
+
+ /* Wait for link_req_rst_not, to be de-asseted */
+ while (ii--) {
+
+ if (!(readl( pp->va_app_base + pp->app_regs->sts0 ) & STS0_LINK_REQ_RST_NOT)) {
+ printk(KERN_INFO "%s : (PCIe%d) link_req_rst_not is de-asseted\n", __func__, pp->port);
+ break;
+ }
+
+ udelay(1000);
+ }
+
+ if (ii == 10)
+ printk(KERN_WARNING "%s : (PCIe%d) link_req_rst_not is not de-asseted \n", __func__, pp->port);
+
+ pp->reset = 1;
+ /* Disable LTSSM and initiate linkdown reset */
+ writel((readl(pp->va_app_base + pp->app_regs->cfg5) &
+ ~(CFG5_LTSSM_ENABLE)) | CFG5_LINK_DOWN_RST,
+ pp->va_app_base + pp->app_regs->cfg5);
+ udelay(1000);
+ }
+
+ printk(KERN_INFO "Disabling PCIe%d Controler Clock\n", pdev->id);
+
+ if (pcie_port[pdev->id].port_mode != PCIE_PORT_MODE_NONE)
+ clk_disable(pcie_port[pdev->id].ref_clock);
+
+ printk(KERN_INFO "EXIT : Putting PCIe%d device into reset\n", pdev->id);
+ }
+ else {
+
+ printk(KERN_INFO "ENTER: Bringing PCIe%d device outof reset\n", pdev->id);
+
+ printk(KERN_INFO "Enabling PCIe%d Controler Clock\n", pdev->id);
+
+ if(pcie_port[pdev->id].port_mode != PCIE_PORT_MODE_NONE) {
+ rc = clk_enable(pcie_port[pdev->id].ref_clock);
+ if (rc)
+ pr_err("%s: PCIe%d clock enable failed\n", __func__, pdev->id);
+ }
+
+ if (!comcerto_pcie_device_reset_exit(pp)) {
+ pp->reset = 0;
+ }
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(device_reset, 0644, comcerto_pcie_show_reset, comcerto_pcie_set_reset);
+
+
+static ssize_t comcerto_pcie_serdes_pd(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pcie_port *pp = &pcie_port[pdev->id];
+ int reset = 0, rc;
+
+ sscanf(buf, "%d", &reset);
+
+ reset = reset ? 1:0;
+
+ if (reset) {
+ printk(KERN_INFO "%s: Putting Serdes to Low Power and CMU Power Off\n", __func__);
+
+ if (pdev->id)
+ writel(readl(USBPHY_SERDES_STAT_BASE+0x44) | ((0x3 << 2)|(0x1 << 7)) , USBPHY_SERDES_STAT_BASE+0x44);
+ else
+ writel(readl(USBPHY_SERDES_STAT_BASE+0x34) | ((0x3 << 2)|(0x1 << 7)) , USBPHY_SERDES_STAT_BASE+0x34);
+ } else {
+ printk(KERN_INFO "%s: Getting Serdes out of Low Power and CMU Power On\n", __func__);
+
+ if (pdev->id)
+ writel(readl(USBPHY_SERDES_STAT_BASE+0x44) & ~((0x3 << 2)|(0x1 << 7)) , USBPHY_SERDES_STAT_BASE+0x44);
+ else
+ writel(readl(USBPHY_SERDES_STAT_BASE+0x34) & ~((0x3 << 2)|(0x1 << 7)) , USBPHY_SERDES_STAT_BASE+0x34);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(serdes_pd, 0644, NULL, comcerto_pcie_serdes_pd);
+
+
+#ifdef CONFIG_PM
+static int no_irq_resume ;
+
+static int comcerto_pcie_suspend(struct device *dev)
+{
+ unsigned int val, i;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ printk(KERN_INFO "%s: pcie device %p (id = %d):\n",
+ __func__, pdev, pdev->id);
+
+ /* Check for the BitMask bit for PCIe, if it is enabled
+ * then we are not going suspend the PCIe device , as by
+ * this device , we will wake from System Resume.
+ */
+ if ( !(host_utilpe_shared_pmu_bitmask & PCIe0_IRQ) || !(host_utilpe_shared_pmu_bitmask & PCIe1_IRQ) ){
+ /* We will Just return from here */
+ return 0;
+ }
+ if (pcie_port[pdev->id].port_mode != PCIE_PORT_MODE_NONE) {
+ if (comcerto_pcie_link_up(&pcie_port[pdev->id])){
+ /* Enable PME to root Port */
+ comcerto_dbi_read_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, &val);
+ comcerto_dbi_write_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, val | PCI_PM_CTRL_STATE_MASK);
+ /* Required PM Delay */
+ for (i = 0 ; i < 40 ; i++)
+ udelay(500);
+ }
+ }
+ no_irq_resume =0 ;
+ return 0;
+}
+
+static int comcerto_pcie_resume(struct device *dev)
+{
+ unsigned int val, i;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ printk(KERN_INFO "%s: pcie device %p (id = %d)\n",
+ __func__, pdev, pdev->id);
+ /* Check for the Bit_Mask bit for PCIe, if it is enabled
+ * then we are not going suspend the PCIe device , as by
+ * this device , we will wake from System Resume.
+ */
+ if ( !(host_utilpe_shared_pmu_bitmask & PCIe0_IRQ) || !(host_utilpe_shared_pmu_bitmask & PCIe1_IRQ) ){
+
+ /* We will Just return
+ */
+ return 0;
+ }
+ if( no_irq_resume == 0)
+ {
+ if(pcie_port[pdev->id].port_mode != PCIE_PORT_MODE_NONE) {
+ if (comcerto_pcie_link_up(&pcie_port[pdev->id])){
+ /* Put In D0 State */
+ comcerto_dbi_read_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, &val);
+ comcerto_dbi_write_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, val & (~PCI_PM_CTRL_STATE_MASK));
+
+ /* Required PM Delay */
+ for (i = 0 ; i < 40 ; i++)
+ udelay(500);
+ }
+ }
+ }
+ return 0;
+}
+
+static int comcerto_pcie_noirq_resume(struct device *dev)
+{
+ int val,i;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ printk(KERN_INFO "%s: pcie device %p (id = %d)\n",
+ __func__, pdev, pdev->id);
+
+ /* Check for the Bit_Mask bit for PCIe, if it is enabled
+ * then we are not going suspend the PCIe device , as by
+ * this device , we will wake from System Resume.
+ */
+ if ( !(host_utilpe_shared_pmu_bitmask & PCIe0_IRQ) || !(host_utilpe_shared_pmu_bitmask & PCIe1_IRQ) ){
+ /* We will Just return
+ */
+ return 0;
+ }
+ if(pcie_port[pdev->id].port_mode != PCIE_PORT_MODE_NONE) {
+ if (comcerto_pcie_link_up(&pcie_port[pdev->id])){
+ /* Put In D0 State */
+ comcerto_dbi_read_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, &val);
+ comcerto_dbi_write_reg(&pcie_port[pdev->id], (PCI_CAP_PM + PCI_PM_CTRL), 4, val & (~PCI_PM_CTRL_STATE_MASK));
+ /* Required PM Delay */
+ for (i = 0 ; i < 40 ; i++)
+ udelay(500);
+ }
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops pcie_platform_pm_ops = {
+ .suspend = comcerto_pcie_suspend,
+ .resume = comcerto_pcie_resume,
+ .resume_noirq = comcerto_pcie_noirq_resume,
+};
+
+
+static struct platform_driver comcerto_pcie_driver = {
+ .driver = {
+ .name = "pcie",
+ .pm = &pcie_platform_pm_ops,
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device pcie_pwr0 = {
+ .name = "pcie",
+ .id = 0,
+};
+
+static struct platform_device pcie_pwr1 = {
+ .name = "pcie",
+ .id = 1,
+};
+#endif
+
+static void comcerto_serdes_set_polarity(struct serdes_regs_s *p_pcie_phy_reg_file, int current_polarity)
+{
+ switch(current_polarity)
+ {
+ case 0:
+ p_pcie_phy_reg_file[0x73].val = 0x0;
+ p_pcie_phy_reg_file[0x75].val = 0x0;
+ break;
+ case 1:
+ p_pcie_phy_reg_file[0x73].val = 0x8;
+ p_pcie_phy_reg_file[0x75].val = 0x2;
+ break;
+ case 2:
+ p_pcie_phy_reg_file[0x73].val = 0x0;
+ p_pcie_phy_reg_file[0x75].val = 0x2;
+ break;
+ case 3:
+ p_pcie_phy_reg_file[0x73].val = 0x8;
+ p_pcie_phy_reg_file[0x75].val = 0x0;
+ break;
+ }
+
+}
+
+static int comcerto_pcie_bsp_link_init(struct pcie_port *pp, int nr, struct serdes_regs_s *p_pcie_phy_reg_file, int serdes_reg_size, int current_polarity)
+{
+ int axi_pcie_component;
+ int pcie_component;
+ int serdes_component;
+ int rc;
+ int if_err = 1;
+
+ comcerto_serdes_set_polarity(p_pcie_phy_reg_file, current_polarity);
+
+ if(nr == 0)
+ {
+ axi_pcie_component = COMPONENT_AXI_PCIE0;
+ pcie_component = COMPONENT_SERDES_PCIE0;
+ serdes_component = COMPONENT_SERDES0;
+ }
+ else
+ {
+ axi_pcie_component = COMPONENT_AXI_PCIE1;
+ pcie_component = COMPONENT_SERDES_PCIE1;
+ serdes_component = COMPONENT_SERDES1;
+ }
+
+ //Bring serdes out of reset
+ c2000_block_reset(axi_pcie_component,0);
+ c2000_block_reset(serdes_component,0);
+
+ /* SW select for ck_soc_div_i SOC clock */
+ writel(0xFF3C, COMCERTO_SERDES_DWC_CFG_REG( nr, SD_PHY_CTRL3_REG_OFST ));
+ writel(readl(COMCERTO_SERDES_DWC_CFG_REG( nr, SD_PHY_CTRL2_REG_OFST )) & ~0x3,
+ COMCERTO_SERDES_DWC_CFG_REG( nr, SD_PHY_CTRL2_REG_OFST ));
+
+ rc = clk_enable(pp->ref_clock);
+ if (rc){
+ pr_err("%s: PCIe%d clock enable failed\n", __func__, nr);
+ goto err1;
+ }
+
+ /* Enable the PCIE_OCC clock */
+#if defined(CONFIG_COMCERTO_PCIE_OCC_CLOCK)
+ rc = clk_enable(pp->occ_clock);
+ if (rc){
+ pr_err("%s: PCIe_occ clock enable failed\n", __func__);
+ goto err_occ_clock;
+ }
+#endif
+
+ /* Serdes Initialization. */
+ if( serdes_phy_init(nr, p_pcie_phy_reg_file,
+ serdes_reg_size/ sizeof(serdes_regs_t),
+ SD_DEV_TYPE_PCIE) )
+ {
+ pp->port_mode = PCIE_PORT_MODE_NONE;
+ pr_err("%s: Failed to initialize serdes (%d)\n", __func__, nr );
+ goto err_phy_link;
+ }
+
+ mdelay(1); //After CMU locks wait for sometime
+
+#if defined(CONFIG_C2K_MFCN_EVM)
+ if(nr == 0){
+ GPIO_reset_external_device(COMPONENT_PCIE0,0);
+ }else{
+ GPIO_reset_external_device(COMPONENT_PCIE1,0);
+ }
+
+ mdelay(1);
+#endif
+ //Bring PCIe out of reset
+ c2000_block_reset(pcie_component,0);
+
+ //Hold the LTSSM in detect state
+ writel(readl(pp->va_app_base + pp->app_regs->cfg5) & ~CFG5_LTSSM_ENABLE,
+ pp->va_app_base + pp->app_regs->cfg5);
+
+ comcerto_pcie_rc_init(pp);
+
+ //Enable LTSSM to start link initialization
+ writel(readl(pp->va_app_base + pp->app_regs->cfg5) | (CFG5_APP_INIT_RST | CFG5_LTSSM_ENABLE),
+ pp->va_app_base + pp->app_regs->cfg5);
+
+ pp->link_state = comcerto_pcie_link_up( &pcie_port[nr] );
+
+ if(!pp->link_state)
+ {
+ if_err = 0;
+ goto err_phy_link;
+ }
+
+ return 0;
+
+err_phy_link:
+ clk_disable(pp->ref_clock);
+ clk_put(pp->ref_clock);
+#if defined(CONFIG_COMCERTO_PCIE_OCC_CLOCK)
+err_occ_clock:
+ clk_disable(pp->occ_clock);
+ clk_put(pp->occ_clock);
+#endif
+
+err1:
+ //Put all to reset
+ c2000_block_reset(axi_pcie_component,1);
+ c2000_block_reset(serdes_component,1);
+ c2000_block_reset(pcie_component,1);
+
+ if(if_err)
+ return -1;
+ else
+ return 0;
+
+}
+
+static int comcerto_pcie_bsp_init(struct pcie_port *pp, int nr)
+{
+ struct serdes_regs_s *p_pcie_phy_reg_file;
+ int serdes_regs_size;
+ int polarity_max = 4;
+ int polarity;
+ int ret;
+
+
+ if (nr >= NUM_PCIE_PORTS) {
+ printk("%s : Invalid PCIe port number\n", __func__);
+ goto err0;
+ }
+
+ ret = pcie_app_init(pp, nr, CFG0_DEV_TYPE_RC);
+ if(ret == -1)
+ goto err0;;
+
+ pp->port_mode = pcie_port_get_mode(nr);
+
+ if(pcie_external_clk)
+ {
+ p_pcie_phy_reg_file = &pcie_phy_reg_file_100[0];
+ serdes_regs_size = sizeof(pcie_phy_reg_file_100);
+ }
+ else
+ {
+ if(HAL_get_ref_clk() == REF_CLK_24MHZ)
+ {
+ p_pcie_phy_reg_file = &pcie_phy_reg_file_24[0];
+ serdes_regs_size = sizeof(pcie_phy_reg_file_24);
+ printk(KERN_INFO "PCIe: Ref clk 24Mhz\n");
+ }
+ else
+ {
+ p_pcie_phy_reg_file = &pcie_phy_reg_file_48[0];
+ serdes_regs_size = sizeof(pcie_phy_reg_file_48);
+ printk(KERN_INFO "PCIe: Ref clk 48Mhz\n");
+ }
+ }
+
+ if (system_rev == 1) {
+ printk(KERN_INFO "PCIe: Detected C2K RevA1 device serdes clk devider old:new=%x:%x\n",
+ p_pcie_phy_reg_file[0x61].val, 0x06);
+ p_pcie_phy_reg_file[0x61].val = 0x6;
+ }
+
+ for(polarity = 0 ; polarity < polarity_max; polarity++)
+ {
+
+ ret = comcerto_pcie_bsp_link_init(pp, nr, p_pcie_phy_reg_file, serdes_regs_size, polarity);
+ if(ret == -1)
+ goto err1;;
+
+ if (pp->link_state)
+ goto linkup;
+
+ if(!pcie_port_is_host(nr))
+ return 0; //Endpoint, so no need to change polarity
+
+ }
+
+ printk(KERN_INFO "PCIe%d: Link Up Failed \n",nr);
+
+err1:
+ iounmap(pp->va_cfg0_base);
+ iounmap(pp->va_cfg1_base);
+ pp->port_mode = PCIE_PORT_MODE_NONE;
+err0:
+ return -1;
+
+linkup:
+ printk(KERN_INFO "PCIe%d: Link Up Success \n",nr);
+ printk(KERN_INFO "PCIe%d: Polarity: %d Gen1 mode: %d External Clk: %d\n",nr, polarity, pcie_gen1_only,pcie_external_clk);
+ if (pcie_port_is_host(nr))
+ comcerto_pcie_rc_int_init(pp);
+ return 0;
+
+}
+
+static int comcerto_pcie_abort_handler(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs)
+{
+ if (fsr & (1 << 10))
+ regs->ARM_pc += 4;
+ return 0;
+}
+
+
+static int __init comcerto_pcie_init(void)
+{
+ struct pcie_port *pp;
+ int i, rc;
+ int num_pcie_port = 1;
+ struct pci_dev *pdev = NULL;
+
+
+ pp = &pcie_port[0];
+ comcerto_pcie_bsp_init(pp, 0);
+
+ if ( (NUM_PCIE_PORTS == 2) &&
+ !(readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES1_CNF_SATA0) )
+ {
+ num_pcie_port = 2;
+ pp = &pcie_port[1];
+ comcerto_pcie_bsp_init(pp, 1);
+ }
+
+ comcerto_pcie.nr_controllers = num_pcie_port;
+
+ pcibios_min_io = iATU_GET_IO_BASE(COMCERTO_AXI_PCIe0_SLAVE_BASE);
+ pcibios_min_mem = COMCERTO_AXI_PCIe0_SLAVE_BASE;
+ pci_add_flags(PCI_REASSIGN_ALL_RSRC);
+
+ hook_fault_code(16 + 6, comcerto_pcie_abort_handler, SIGBUS, 0, "imprecise external abort");
+
+ pci_common_init(&comcerto_pcie);
+
+ for ( i = 0; i < num_pcie_port; i++ )
+ {
+
+ if (!pcie_port_is_host(i) || !(pcie_port[i].link_state))
+ continue;
+
+ while((pdev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pdev))) {
+ if (pcie_port[i].root_bus_nr == pdev->bus->number) {
+ if( (rc = pcie_get_readrq(pdev)) > 512 ) {
+ printk(KERN_WARNING "PCIe%d Device rdreq size (%d) is more than supported\n", i, rc);
+ pcie_set_readrq(pdev, 512);
+ }
+ }
+ }
+ }
+
+
+#ifdef CONFIG_PM
+ platform_device_register(&pcie_pwr0);
+
+ if (device_create_file(&pcie_pwr0.dev, &dev_attr_device_reset))
+ printk(KERN_ERR "%s: Unable to create pcie0 reset sysfs entry\n", __func__);
+
+ if (device_create_file(&pcie_pwr0.dev, &dev_attr_serdes_pd))
+ printk(KERN_ERR "%s: Unable to create pcie0 serdes_pd sysfs entry\n", __func__);
+
+ if(num_pcie_port > 1) {
+ platform_device_register(&pcie_pwr1);
+
+ if (device_create_file(&pcie_pwr1.dev, &dev_attr_device_reset))
+ printk(KERN_ERR "%s: Unable to create pcie1 reset sysfs entry\n", __func__);
+
+ if (device_create_file(&pcie_pwr1.dev, &dev_attr_serdes_pd))
+ printk(KERN_ERR "%s: Unable to create pcie1 serdes_pd sysfs entry\n", __func__);
+ }
+
+ platform_driver_register(&comcerto_pcie_driver);
+#endif
+
+ return 0;
+}
+subsys_initcall(comcerto_pcie_init);
+
+
diff --git a/arch/arm/mach-comcerto/platsmp.c b/arch/arm/mach-comcerto/platsmp.c
new file mode 100644
index 0000000..7c0c727
--- /dev/null
+++ b/arch/arm/mach-comcerto/platsmp.c
@@ -0,0 +1,260 @@
+/*
+ * arch/arm/mach-comcerto/platsmp.c
+ *
+ * Copyright (C) 2011 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+
+#include <asm/cacheflush.h>
+#include <mach/hardware.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach-types.h>
+#include <asm/smp_scu.h>
+#include <asm/unified.h>
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/kthread.h> // for threads
+#include <linux/sched.h> // for task_struct
+#include <linux/time.h> // for using jiffies
+#include <linux/timer.h>
+
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/pid.h>
+
+
+extern void comcerto_secondary_startup(void);
+
+static void __iomem *scu_base_addr(void)
+{
+ return (void *)COMCERTO_SCU_VADDR;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+void __init smp_init_cpus(void)
+{
+ void __iomem *scu_base = scu_base_addr();
+ unsigned int i, ncores;
+
+ ncores = scu_base ? scu_get_core_count(scu_base) : 1;
+
+ if (ncores > NR_CPUS) {
+ printk(KERN_WARNING
+ "Comcerto: no. of cores (%d) greater than configured "
+ "maximum of %d - clipping\n",
+ ncores, NR_CPUS);
+ ncores = NR_CPUS;
+ }
+
+ for (i = 0; i < ncores; i++)
+ set_cpu_possible(i, true);
+
+ set_smp_cross_call(gic_raise_softirq);
+}
+
+#define JUMP_TO_KERNEL_START_1 0xe3a00020 /* mov r0, #32 */
+#define JUMP_TO_KERNEL_START_2 0xe590f000 /* ldr pc, [r0] */
+
+/* Creating Task for Cpu-1 Hotplug */
+struct task_struct *thread1=NULL;
+
+DECLARE_WAIT_QUEUE_HEAD(cpu1_hotplug);
+
+/*
+ * Hotplug signal from CPU Hotplug framework
+ * to invoke the Hotplug task
+ */
+u32 cpu1_hotplug_done;
+
+/* Cpu-1 Hotplug Task */
+void hotplug_cpu1_die(void)
+{
+ unsigned int reset;
+ struct cpumask in_mask;
+ int cpu = 0;
+ int pid = 0;
+
+ cpumask_set_cpu(cpu, &in_mask);
+ sched_setaffinity(pid, &in_mask);
+
+wait_for_cpu1_hotplug_done:
+
+ /* Waiting for hotplug event invoked by CPU hotplug framework */
+ wait_event(cpu1_hotplug, cpu1_hotplug_done>0);
+
+#ifdef CONFIG_NEON
+ __raw_writel((__raw_readl(A9DP_CPU_CLK_CNTRL) & ~NEON1_CLK_ENABLE), A9DP_CPU_CLK_CNTRL);
+ __raw_writel((__raw_readl(A9DP_CPU_RESET) | NEON1_RST), A9DP_CPU_RESET);
+#endif
+ __raw_writel((__raw_readl(A9DP_CPU_CLK_CNTRL) & ~CPU1_CLK_ENABLE), A9DP_CPU_CLK_CNTRL);
+ __raw_writel((__raw_readl(A9DP_PWR_CNTRL) | CLAMP_CORE1), A9DP_PWR_CNTRL);
+ __raw_writel((__raw_readl(A9DP_PWR_CNTRL) | CORE_PWRDWN1), A9DP_PWR_CNTRL);
+ __raw_writel((__raw_readl(A9DP_CPU_RESET) | CPU1_RST), A9DP_CPU_RESET);
+ __raw_writel((__raw_readl(A9DP_PWR_CNTRL) & ~CORE_PWRDWN1), A9DP_PWR_CNTRL);
+
+ cpu1_hotplug_done = 0;
+ goto wait_for_cpu1_hotplug_done;
+
+ return;
+}
+
+void __init platform_smp_prepare_cpus(unsigned int max_cpus)
+{
+ int i;
+ char our_thread[25]="cpu1_hotplug_thread";
+
+ /*
+ * Initialise the present map, which describes the set of CPUs
+ * actually populated at the present time.
+ */
+ for (i = 0; i < max_cpus; i++)
+ set_cpu_present(i, true);
+
+ scu_enable(scu_base_addr());
+
+ /* Create cpu1_hotplug_thread */
+
+ printk(" Creating cpu1_hotplug_thread....\n");
+ cpu1_hotplug_done = 0;
+ thread1 = kthread_create(hotplug_cpu1_die,NULL,our_thread);
+
+ if((thread1))
+ {
+ printk(" cpu1_hotplug_thread Created\n");
+ wake_up_process(thread1);
+ }
+
+ return 0;
+}
+
+/*
+ * control for which core is the next to come out of the secondary
+ * boot "holding pen"
+ */
+volatile int __cpuinitdata pen_release = -1;
+
+/*
+ * Write pen_release in a way that is guaranteed to be visible to all
+ * observers, irrespective of whether they're taking part in coherency
+ * or not. This is necessary for the hotplug code to work reliably.
+ */
+static void __cpuinit write_pen_release(int val)
+{
+ pen_release = val;
+ smp_wmb();
+ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+}
+
+static DEFINE_SPINLOCK(boot_lock);
+
+void __cpuinit platform_secondary_init(unsigned int cpu)
+{
+ /*
+ * if any interrupts are already enabled for the primary
+ * core (e.g. timer irq), then they will not have been enabled
+ * for us: do so
+ */
+ gic_secondary_init(0);
+
+ /*
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+ write_pen_release(-1);
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+ unsigned int *loop = (unsigned int *)phys_to_virt(0x08);
+
+ /*
+ * Install the comcerto_secondary_startup pointer at 0x20
+ * Physical Address
+ */
+ __raw_writel(BSYM(virt_to_phys(comcerto_secondary_startup)), phys_to_virt(0x20));
+ __raw_writel((unsigned int)JUMP_TO_KERNEL_START_1 , phys_to_virt(0x00));
+ __raw_writel((unsigned int)JUMP_TO_KERNEL_START_2 , phys_to_virt(0x04));
+ smp_wmb();
+ __cpuc_flush_dcache_area((void *)phys_to_virt(0x00), 0x24);
+ outer_clean_range(__pa(phys_to_virt(0x00)), __pa(phys_to_virt(0x24)));
+
+ /* Get CPU 1 out of reset */
+ __raw_writel((__raw_readl(A9DP_CPU_RESET) & ~CPU1_RST), A9DP_CPU_RESET);
+ __raw_writel((__raw_readl(A9DP_PWR_CNTRL) & ~CLAMP_CORE1), A9DP_PWR_CNTRL);
+ __raw_writel((__raw_readl(A9DP_CPU_CLK_CNTRL) | CPU1_CLK_ENABLE), A9DP_CPU_CLK_CNTRL);
+
+#ifdef CONFIG_NEON
+ /* Get NEON 1 out of reset */
+ __raw_writel((__raw_readl(A9DP_CPU_RESET) & ~NEON1_RST), A9DP_CPU_RESET);
+ __raw_writel((__raw_readl(A9DP_CPU_CLK_CNTRL) | NEON1_CLK_ENABLE), A9DP_CPU_CLK_CNTRL);
+#endif
+ /*
+ * Set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ spin_lock(&boot_lock);
+
+ /*
+ * This is really belt and braces; we hold unintended secondary
+ * CPUs in the holding pen until we're ready for them. However,
+ * since we haven't sent them a soft interrupt, they shouldn't
+ * be there.
+ */
+ write_pen_release(cpu);
+
+ /*
+ * Send the secondary CPU a soft interrupt, thereby causing
+ * the boot monitor to read the system wide flags register,
+ * and branch to the address found there.
+ */
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ smp_rmb();
+ if (pen_release == -1)
+ break;
+
+ udelay(10);
+ }
+
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ spin_unlock(&boot_lock);
+
+ return pen_release != -1 ? -ENOSYS : 0;
+}
diff --git a/arch/arm/mach-comcerto/pm.c b/arch/arm/mach-comcerto/pm.c
new file mode 100644
index 0000000..128ee9d
--- /dev/null
+++ b/arch/arm/mach-comcerto/pm.c
@@ -0,0 +1,408 @@
+/*
+ * arch/arm/mach-comcerto/pm.c
+ * C2K Power Management
+ *
+ * Copyright (C) 2012 Mindspeed
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/suspend.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/suspend.h>
+#include <asm/irq.h>
+#include <linux/atomic.h>
+#include <asm/mach/time.h>
+#include <asm/mach/irq.h>
+#include <linux/console.h>
+
+
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+
+#include <asm/cacheflush.h>
+#include <mach/hardware.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach-types.h>
+#include <asm/smp_scu.h>
+#include <mach/comcerto-2000/pm.h>
+#include <linux/gpio.h>
+
+unsigned int host_utilpe_shared_pmu_bitmask;
+
+/* Externs */
+extern void comcerto_cpu_suspend(int save_state);
+extern unsigned int * c2k_get_restore_pointer(void);
+extern void comcerto_cpu_restore (void);
+
+unsigned int c2k_pm_bitmask_show(void)
+{
+ return host_utilpe_shared_pmu_bitmask;
+}
+
+void c2k_pm_bitmask_store(unsigned int bitmask_value)
+{
+ /*
+ * Initialize the shared pmu bitmask
+ * This information can be configurable run time.
+ * Can be passed from bootloader also (Not Implimented Yet)
+ */
+ //host_utilpe_shared_pmu_bitmask = 0xFFE7FFFF;
+ host_utilpe_shared_pmu_bitmask = bitmask_value;
+
+ /* Pass the bitmask info to UtilPE */
+ *(((volatile unsigned int *)(HOST_UTILPE_SHARED_ADDRESS))+4) = host_utilpe_shared_pmu_bitmask;
+}
+
+
+static int comcerto_do_sram_idle(unsigned long save_state)
+{
+ comcerto_cpu_suspend(save_state);
+ return 0;
+}
+
+/*------------------------- L2 Cache and SCU Save Resume ----------------------------*/
+#define SCU_DATA_SIZE 32
+#define L2_DATA_SIZE 96
+
+extern void pl310_save(void);
+extern void pl310_resume(void);
+
+typedef struct
+{
+ /* 0x00 */ volatile unsigned int control;
+ /* 0x04 */ const unsigned int configuration;
+ /* 0x08 */ union
+ {
+ volatile unsigned int w;
+ volatile unsigned char b[4];
+ } power_status;
+ /* 0x0c */ volatile unsigned int invalidate_all;
+ char padding1[48];
+ /* 0x40 */ volatile unsigned int filtering_start;
+ /* 0x44 */ volatile unsigned int filtering_end;
+ char padding2[8];
+ /* 0x50 */ volatile unsigned int access_control;
+ /* 0x54 */ volatile unsigned int ns_access_control;
+} a9_scu_registers;
+
+
+void save_a9_scu(u32 *pointer, unsigned scu_address)
+{
+ a9_scu_registers *scu = (a9_scu_registers *)scu_address;
+
+ pointer[0] = scu->control;
+ pointer[1] = scu->power_status.w;
+ pointer[2] = scu->filtering_start;
+ pointer[3] = scu->filtering_end;
+ pointer[4] = scu->access_control;
+ pointer[5] = scu->ns_access_control;
+}
+
+void restore_a9_scu(u32 *pointer, unsigned scu_address)
+{
+ a9_scu_registers *scu = (a9_scu_registers *)scu_address;
+
+ scu->invalidate_all = 0xffff;
+ scu->filtering_start = pointer[2];
+ scu->filtering_end = pointer[3];
+ scu->access_control = pointer[4];
+ scu->ns_access_control = pointer[5];
+ scu->power_status.w = pointer[1];
+ scu->control = pointer[0];
+}
+
+
+struct lockdown_regs
+{
+ unsigned int d, i;
+};
+
+typedef struct
+{
+ /* 0x000 */ const unsigned cache_id;
+ /* 0x004 */ const unsigned cache_type;
+ char padding1[0x0F8];
+ /* 0x100 */ volatile unsigned control;
+ /* 0x104 */ volatile unsigned aux_control;
+ /* 0x108 */ volatile unsigned tag_ram_control;
+ /* 0x10C */ volatile unsigned data_ram_control;
+ char padding2[0x0F0];
+ /* 0x200 */ volatile unsigned ev_counter_ctrl;
+ /* 0x204 */ volatile unsigned ev_counter1_cfg;
+ /* 0x208 */ volatile unsigned ev_counter0_cfg;
+ /* 0x20C */ volatile unsigned ev_counter1;
+ /* 0x210 */ volatile unsigned ev_counter0;
+ /* 0x214 */ volatile unsigned int_mask;
+ /* 0x218 */ const volatile unsigned int_mask_status;
+ /* 0x21C */ const volatile unsigned int_raw_status;
+ /* 0x220 */ volatile unsigned int_clear;
+ char padding3[0x50C];
+ /* 0x730 */ volatile unsigned cache_sync;
+ char padding4[0x03C];
+ /* 0x770 */ volatile unsigned inv_pa;
+ char padding5[0x008];
+ /* 0x77C */ volatile unsigned inv_way;
+ char padding6[0x030];
+ /* 0x7B0 */ volatile unsigned clean_pa;
+ char padding7[0x004];
+ /* 0x7B8 */ volatile unsigned clean_index;
+ /* 0x7BC */ volatile unsigned clean_way;
+ char padding8[0x030];
+ /* 0x7F0 */ volatile unsigned clean_inv_pa;
+ char padding9[0x004];
+ /* 0x7F8 */ volatile unsigned clean_inv_index;
+ /* 0x7FC */ volatile unsigned clean_inv_way;
+ char paddinga[0x100];
+ /* 0x900 */ volatile struct lockdown_regs lockdown[8];
+ char paddingb[0x010];
+ /* 0x950 */ volatile unsigned lock_line_en;
+ /* 0x954 */ volatile unsigned unlock_way;
+ char paddingc[0x2A8];
+ /* 0xC00 */ volatile unsigned addr_filtering_start;
+ /* 0xC04 */ volatile unsigned addr_filtering_end;
+ char paddingd[0x338];
+ /* 0xF40 */ volatile unsigned debug_ctrl;
+ char paddinge[0x01C];
+ /* 0xF60 */ volatile unsigned prefetch_ctrl;
+ char paddingf[0x01C];
+ /* 0xF80 */ volatile unsigned power_ctrl;
+} pl310_registers;
+
+
+typedef struct
+{
+ unsigned int aux_control;
+ unsigned int tag_ram_control;
+ unsigned int data_ram_control;
+ unsigned int ev_counter_ctrl;
+ unsigned int ev_counter1_cfg;
+ unsigned int ev_counter0_cfg;
+ unsigned int ev_counter1;
+ unsigned int ev_counter0;
+ unsigned int int_mask;
+ unsigned int lock_line_en;
+ struct lockdown_regs lockdown[8];
+ unsigned int unlock_way;
+ unsigned int addr_filtering_start;
+ unsigned int addr_filtering_end;
+ unsigned int debug_ctrl;
+ unsigned int prefetch_ctrl;
+ unsigned int power_ctrl;
+} pl310_context;
+
+
+void save_pl310(u32 *pointer, unsigned int pl310_address)
+{
+ pl310_registers *pl310 = (pl310_registers *)pl310_address;
+ pl310_context *context = (pl310_context *)pointer;
+ int i;
+
+ /* TODO: are all these registers are present in earlier PL310 versions? */
+ context->aux_control = pl310->aux_control;
+ context->tag_ram_control = pl310->tag_ram_control;
+ context->data_ram_control = pl310->data_ram_control;
+ context->ev_counter_ctrl = pl310->ev_counter_ctrl;
+ context->ev_counter1_cfg = pl310->ev_counter1_cfg;
+ context->ev_counter0_cfg = pl310->ev_counter0_cfg;
+ context->ev_counter1 = pl310->ev_counter1;
+ context->ev_counter0 = pl310->ev_counter0;
+ context->int_mask = pl310->int_mask;
+ context->lock_line_en = pl310->lock_line_en;
+
+ for (i=0; i<8; ++i)
+ {
+ context->lockdown[i].d = pl310->lockdown[i].d;
+ context->lockdown[i].i = pl310->lockdown[i].i;
+ }
+ context->addr_filtering_start = pl310->addr_filtering_start;
+ context->addr_filtering_end = pl310->addr_filtering_end;
+ context->debug_ctrl = pl310->debug_ctrl;
+ context->prefetch_ctrl = pl310->prefetch_ctrl;
+ context->power_ctrl = pl310->power_ctrl;
+}
+
+void restore_pl310(u32 *pointer, unsigned int pl310_address)
+{
+ pl310_registers *pl310 = (pl310_registers *)pl310_address;
+ pl310_context *context = (pl310_context *)pointer;
+ int i;
+
+ /* We may need to disable the PL310 if the boot code has turned it on */
+ if (pl310->control)
+ {
+ /* Wait for the cache to be idle, then disable */
+ pl310->cache_sync = 0;
+ dsb();
+ pl310->control = 0;
+ }
+
+ /* TODO: are all these registers present in earlier PL310 versions? */
+ pl310->aux_control = context->aux_control;
+ pl310->tag_ram_control = context->tag_ram_control;
+ pl310->data_ram_control = context->data_ram_control;
+ pl310->ev_counter_ctrl = context->ev_counter_ctrl;
+ pl310->ev_counter1_cfg = context->ev_counter1_cfg;
+ pl310->ev_counter0_cfg = context->ev_counter0_cfg;
+ pl310->ev_counter1 = context->ev_counter1;
+ pl310->ev_counter0 = context->ev_counter0;
+ pl310->int_mask = context->int_mask;
+ pl310->lock_line_en = context->lock_line_en;
+ for (i=0; i<8; ++i)
+ {
+ pl310->lockdown[i].d = context->lockdown[i].d;
+ pl310->lockdown[i].i= context->lockdown[i].i;
+ }
+ pl310->addr_filtering_start = context->addr_filtering_start;
+ pl310->addr_filtering_end = context->addr_filtering_end;
+ pl310->debug_ctrl = context->debug_ctrl;
+ pl310->prefetch_ctrl = context->prefetch_ctrl;
+ pl310->power_ctrl = context->power_ctrl;
+ dsb();
+ pl310->control = 1;
+ dsb();
+}
+
+/*------------------------- L2 Cache and SCU Save Resume ----------------------------*/
+
+static void C2k_pm_suspend(void)
+{
+ /* Variable to tell what needs to be saved and restored
+ * in C2k_pm_suspend_new */
+
+ /* save_state = 0 => Nothing to save and restored */
+ /* save_state = 1 => Only L1 and logic lost */
+ /* save_state = 2 => Only L2 lost */
+ /* save_state = 3 => L1, L2 and logic lost */
+ int save_state = 3;
+ unsigned int * p0;
+
+ unsigned int scu_data[SCU_DATA_SIZE];
+ unsigned int pl310_data[L2_DATA_SIZE];
+
+
+ printk(KERN_INFO "PM: C2000 Device is trying to enter Suspend mode ...\n");
+
+ p0 = (unsigned int *) comcerto_cpu_restore;
+
+ __raw_writel(virt_to_phys((unsigned int)p0), phys_to_virt(0x20));
+ __raw_writel((unsigned int)JUMP_TO_RESUME_1 , phys_to_virt(0x00));
+ __raw_writel((unsigned int)JUMP_TO_RESUME_2 , phys_to_virt(0x04));
+ smp_wmb();
+ __cpuc_flush_dcache_area((void *)phys_to_virt(0x00), 0x24);
+ outer_clean_range(__pa(phys_to_virt(0x00)), __pa(phys_to_virt(0x24)));
+
+ printk(KERN_INFO "PM: C2000 Jump Location Installed ... -- 0x%x -- 0x%x -- 0x%x \n", (unsigned int)p0, (unsigned int)comcerto_cpu_restore, virt_to_phys((unsigned int)p0));
+
+ printk(KERN_INFO "PM: Saving SCU Context ...\n");
+ save_a9_scu(&scu_data[0], (unsigned int *)COMCERTO_SCU_VADDR);
+
+ printk(KERN_INFO "PM: Saving L2 Cache Context ...\n");
+ save_pl310(&pl310_data[0], (unsigned int *)COMCERTO_L310_VADDR);
+
+ /* Pass the bitmask information to the uTilPE */
+ *(((volatile unsigned int *)(HOST_UTILPE_SHARED_ADDRESS))+4) = host_utilpe_shared_pmu_bitmask;
+
+ printk(KERN_INFO "PM: Going to Suspend ...\n");
+
+ cpu_suspend(save_state, comcerto_do_sram_idle);
+
+ restore_a9_scu(&scu_data[0], (unsigned int *)COMCERTO_SCU_VADDR);
+ restore_pl310(&pl310_data[0], (unsigned int *)COMCERTO_L310_VADDR);
+
+ printk(KERN_INFO "PM: C2000 is re-starting from Suspend State ...\n");
+
+ return;
+}
+
+/* C2k_pm_enter
+ * @state: State we're entering.
+ */
+
+static int C2k_pm_enter(suspend_state_t state)
+{
+ switch(state)
+ {
+
+ case PM_SUSPEND_STANDBY:
+ case PM_SUSPEND_MEM:
+ C2k_pm_suspend();
+ break;
+ default:
+ return -EINVAL;
+ }
+ pr_info("PM: C2000 Leaving C2k_pm_enter \n");
+ return 0;
+}
+
+static int C2k_pm_valid_state(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_ON:
+ case PM_SUSPEND_STANDBY:
+ case PM_SUSPEND_MEM:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static suspend_state_t target_state;
+
+/*
+ * Called after processes are frozen, but before we shutdown devices.
+ */
+static int C2k_pm_begin(suspend_state_t state)
+{
+ target_state = state;
+ return 0;
+}
+
+/*
+ * Called right prior to thawing processes.
+ */
+static void C2k_pm_finish(void)
+{
+ printk(KERN_INFO "Suspend process is completed, Wait for C2000 device to resume \n");
+}
+
+
+/*
+ * Called right prior to thawing processes.
+ */
+static void C2k_pm_end(void)
+{
+ printk(KERN_INFO "Resume process is completed, C2000 device is Power on Again \n");
+ target_state = PM_SUSPEND_ON;
+}
+
+
+static const struct platform_suspend_ops C2k_pm_ops = {
+ .valid = C2k_pm_valid_state,
+ .begin = C2k_pm_begin,
+ .enter = C2k_pm_enter,
+ .finish = C2k_pm_finish,
+ .end = C2k_pm_end,
+};
+
+static int __init C2k_pm_init(void)
+{
+ printk(KERN_INFO "Power Management Mode Support For C2000: \n");
+
+ suspend_set_ops(&C2k_pm_ops);
+ return 0;
+}
+arch_initcall(C2k_pm_init);
+
diff --git a/arch/arm/mach-comcerto/reset.c b/arch/arm/mach-comcerto/reset.c
new file mode 100644
index 0000000..d87333a
--- /dev/null
+++ b/arch/arm/mach-comcerto/reset.c
@@ -0,0 +1,530 @@
+/*
+ * linux/arch/arm/mach-comcerto/reset.c
+ *
+ * driver for block reset for all the devices availble in the
+ * c2000 device.
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <mach/reset.h>
+#include <asm/io.h>
+#include <linux/spinlock.h>
+
+#include <linux/gpio.h>
+
+static char i2cspi_state[2],dus_state[3];
+spinlock_t reset_lock;
+static spinlock_t gpio_lock;
+
+
+void comcerto_rst_cntrl_set(unsigned int dev_rst_cntrl_bit)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&reset_lock, flags);
+
+ __raw_writel((dev_rst_cntrl_bit | __raw_readl(DEVICE_RST_CNTRL)), DEVICE_RST_CNTRL);
+
+ spin_unlock_irqrestore(&reset_lock,flags);
+}
+EXPORT_SYMBOL(comcerto_rst_cntrl_set);
+
+/* @ int block : Id of device block to be put in reset
+ * @ int state : State value 0->OUT-OF-RESET , 1->RESET.
+ * API for block reset to all the device blocks
+ * available for C2000 devices.
+ */
+void c2000_block_reset(int block,int state)
+{
+unsigned long flags;
+spin_lock_irqsave(&reset_lock, flags);
+
+if(state){
+ /* Code is to put the device block in RESET */
+ switch (block)
+ {
+ case COMPONENT_AXI_RTC:
+ /* Put the Timer device(AXI clock domain) in reset state */
+ writel(readl(AXI_RESET_1) | RTC_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_I2C:
+ i2cspi_state[0]=1;
+ goto i2cspi_rst;
+ case COMPONENT_AXI_LEGACY_SPI:
+ i2cspi_state[1]=1;
+ i2cspi_rst:
+ if(i2cspi_state[0] == 1 && i2cspi_state[1] == 1)
+ /* Put the I2C and LEGACY SPI(AXI clock domain) device in reset state */
+ writel(readl(AXI_RESET_1) | I2CSPI_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_DMA:
+ dus_state[0]=1;
+ goto dus_rst;
+ case COMPONENT_AXI_FAST_UART:
+ dus_state[1]=1;
+ goto dus_rst;
+ case COMPONENT_AXI_FAST_SPI:
+ dus_state[2]=1;
+ dus_rst:
+ if(dus_state[0] == 1 && dus_state[1] == 1 && dus_state[2] == 1)
+ /* Put the DUS (AXI clock domain) device in reset state */
+ writel(readl(AXI_RESET_1) | DUS_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_TDM:
+ /* Put the TDM(AXI clock domain) device in reset state */
+ writel(readl(AXI_RESET_1) | TDM_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_PFE_SYS:
+ /* Put the PFE device in reset state */
+ writel(readl(AXI_RESET_1) | PFE_SYS_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_IPSEC_EAPE:
+ /* Put the IPSEC EAPE (AXI clock domain) device in reset state */
+ writel(readl(AXI_RESET_1) | IPSEC_EAPE_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_IPSEC_SPACC:
+ /* Put the IPSEC SPACC (AXI clock domain) device in reset state */
+ writel(readl(AXI_RESET_1) | IPSEC_SPACC_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_DPI_CIE:
+ /* Put the DPI CIE (AXI clock domain) device in reset state */
+ writel(readl(AXI_RESET_0) | DPI_CIE_AXI_RESET_BIT, AXI_RESET_0);
+ break;
+ case COMPONENT_AXI_DPI_DECOMP:
+ /* Put the DPI DECOMP (AXI clock domain) device in reset state */
+ writel(readl(AXI_RESET_0) | DPI_DECOMP_AXI_RESET_BIT, AXI_RESET_0);
+ break;
+ case COMPONENT_AXI_USB0:
+ /* Put the USB0 device(AXI clock domain) in reset state */
+ writel(readl(AXI_RESET_2) | USB0_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_UTMI_USB0:
+ /* Put the USB0 device(UTMI clock domain) in reset state */
+ writel(readl(USB_RST_CNTRL) | USB0_UTMI_RESET_BIT, USB_RST_CNTRL);
+ break;
+ case COMPONENT_USB0_PHY:
+ /* Put the USB0_PHY device in reset state */
+ writel(readl(USB_RST_CNTRL) | USB0_PHY_RESET_BIT, USB_RST_CNTRL);
+ break;
+ case COMPONENT_AXI_USB1:
+ /* Put the USB1 device in reset state */
+ writel(readl(AXI_RESET_2) | USB1_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_UTMI_USB1:
+ /* Put the USB1 device(UTMI clock domain) in reset state */
+ writel(readl(USB_RST_CNTRL) | USB1_UTMI_RESET_BIT, USB_RST_CNTRL);
+ break;
+ case COMPONENT_USB1_PHY:
+ /* Put the USB1_PHY device in reset state */
+ writel(readl(USB_RST_CNTRL) | USB1_PHY_RESET_BIT, USB_RST_CNTRL);
+ break;
+ case COMPONENT_AXI_SATA:
+ /* Put the SATA device in reset state */
+ writel(readl(AXI_RESET_2) | SATA_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_AXI_PCIE0:
+ /* Put the PCIE0 device in reset state */
+ writel(readl(AXI_RESET_2) | PCIE0_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_AXI_PCIE1:
+ /* Put the PCIE1 device in reset state */
+ writel(readl(AXI_RESET_2) | PCIE1_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_PFE_CORE:
+ /* Put the PFE core device in reset state */
+ writel(readl(PFE_RESET) | PFE_CORE_RESET_BIT, PFE_RESET);
+ break;
+ case COMPONENT_IPSEC_EAPE_CORE:
+ /* Put the IPSEC EAPE core device in reset state */
+ writel(readl(IPSEC_RESET) | IPSEC_EAPE_CORE_RESET_BIT, IPSEC_RESET);
+ break;
+ case COMPONENT_GEMTX:
+ /* Put the GEMTX device in reset state */
+ writel(readl(GEMTX_RESET) | GEMTX_RESET_BIT , GEMTX_RESET);
+ break;
+ case COMPONENT_DECT:
+ /* Put the DECT device in reset state */
+ writel(readl(DECT_RESET) | DECT_RESET_BIT , DECT_RESET);
+ break;
+ case COMPONENT_DDR_CNTLR:
+ /* Put the DDR controller device in reset state */
+ writel(readl(DDR_RESET) | DDR_CNTRL_RESET_BIT , DDR_RESET);
+ break;
+ case COMPONENT_DDR_PHY:
+ /* Put the DDR PHY device in reset state */
+ writel(readl(DDR_RESET) | DDR_PHY_RESET_BIT , DDR_RESET);
+ break;
+ case COMPONENT_SERDES0:
+ /* Put SERDES0 controller in Reset state */
+ writel(readl(SERDES_RST_CNTRL) | SERDES0_RESET_BIT ,SERDES_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES_PCIE0:
+ /* Put the PCIE0 SERDES controller in Reset state */
+ writel(readl(PCIe_SATA_RST_CNTRL) | SERDES_PCIE0_RESET_BIT, PCIe_SATA_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES1:
+ /* Put SERDES1 contrller 1 in reset state */
+ writel(readl(SERDES_RST_CNTRL) | SERDES1_RESET_BIT ,SERDES_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES_PCIE1:
+ /* Put PCIE1 serdes controller in reset state */
+ writel(readl(PCIe_SATA_RST_CNTRL) | SERDES_PCIE1_RESET_BIT , PCIe_SATA_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES_SATA0:
+ /* Put SATA0 serdes controller in reset state */
+ writel(readl(PCIe_SATA_RST_CNTRL) | (SERDES_SATA0_RESET_BIT), PCIe_SATA_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES2:
+ /* Put SERDES2 contrller in reset state */
+ writel(readl(SERDES_RST_CNTRL) | SERDES2_RESET_BIT ,SERDES_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES_SATA1:
+ /* Put SATA1 serdes controller in reset state */
+ writel(readl(PCIe_SATA_RST_CNTRL) | SERDES_SATA1_RESET_BIT , PCIe_SATA_RST_CNTRL);
+ break;
+ case COMPONENT_SGMII:
+ /* Put SGMII serdes controller in reset state */
+ writel(readl(SGMII_OCC_RESET) | (SGMII_RESET_BIT), SGMII_OCC_RESET);
+ break;
+ case COMPONENT_SATA_PMU:
+ /* Put the SATA PMU(Keep Alive clock) in Reset state */
+ writel(readl(SATA_PMU_RESET) | SATA_PMU_RESET_BIT, SATA_PMU_RESET);
+ break;
+ case COMPONENT_SATA_OOB:
+ /* Put the SATA OOB in Reset state */
+ writel(readl(SATA_OOB_RESET) | SATA_OOB_RESET_BIT, SATA_OOB_RESET);
+ break;
+ case COMPONENT_TDMNTG:
+ /* Put the TDMNTG in Reset state */
+ writel(readl(TDMNTG_RESET) | TDMNTG_RESET_BIT, TDMNTG_RESET);
+ break;
+ default :
+ break;
+ }
+}else{
+ /* Code is to put the device block to Out of reset */
+ switch (block)
+ {
+ case COMPONENT_AXI_RTC:
+ /* Put the Timer device in Out-Of-Reset state */
+ writel(readl(AXI_RESET_1) & ~RTC_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_I2C:
+ i2cspi_state[0]=0;
+ goto i2cspi_outrst;
+ case COMPONENT_AXI_LEGACY_SPI:
+ i2cspi_state[1]=0;
+ i2cspi_outrst:
+ /* Put the I2C/SPI device in Out-Of-Reset state */
+ writel(readl(AXI_RESET_1) & ~I2CSPI_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_DMA:
+ dus_state[0]=0;
+ goto dus_outrst;
+ case COMPONENT_AXI_FAST_UART:
+ dus_state[1]=0;
+ goto dus_outrst;
+ case COMPONENT_AXI_FAST_SPI:
+ dus_state[2]=0;
+ dus_outrst:
+ /* Put the DUS devices in Out-Of-Reset state */
+ writel(readl(AXI_RESET_1) & ~DUS_AXI_RESET_BIT , AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_TDM:
+ /* Put the TDM device in Out-Of-Reset state */
+ writel(readl(AXI_RESET_1) & ~TDM_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_PFE_SYS:
+ /* Put the PFE System devices in Out-Of-Reset state */
+ writel(readl(AXI_RESET_1) & ~PFE_SYS_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_IPSEC_EAPE:
+ /* Put the IPSEC EAPE devices in Out-Of-Reset state */
+ writel(readl(AXI_RESET_1) & ~IPSEC_EAPE_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_IPSEC_SPACC:
+ /* Put the IPSEC SPACC devices in Out-Of-Reset state */
+ writel(readl(AXI_RESET_1) & ~IPSEC_SPACC_AXI_RESET_BIT, AXI_RESET_1);
+ break;
+ case COMPONENT_AXI_DPI_CIE:
+ /* Put the DPI CIE devices in Out-Of-Reset state */
+ writel(readl(AXI_RESET_0) & ~DPI_CIE_AXI_RESET_BIT, AXI_RESET_0);
+ break;
+ case COMPONENT_AXI_DPI_DECOMP:
+ /* Put the DPI DECOMP devices in Out-Of-Reset state */
+ writel(readl(AXI_RESET_0) & ~DPI_DECOMP_AXI_RESET_BIT, AXI_RESET_0);
+ break;
+ case COMPONENT_AXI_USB0:
+ /* Put the USB0 device in Out-Of-Reset state */
+ writel(readl(AXI_RESET_2) & ~USB0_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_UTMI_USB0:
+ /* Put the USB0 device(UTMI clock domain) in Out-Of-reset state */
+ writel(readl(USB_RST_CNTRL) & ~USB0_UTMI_RESET_BIT, USB_RST_CNTRL);
+ break;
+ case COMPONENT_USB0_PHY:
+ /* Put the USB0_PHY devices in Out-Of-Reset state */
+ writel(readl(USB_RST_CNTRL)& ~USB0_PHY_RESET_BIT, USB_RST_CNTRL);
+ break;
+ case COMPONENT_AXI_USB1:
+ /* Put the USB1 device in Out-Of-Reset state */
+ writel(readl(AXI_RESET_2) & ~USB1_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_UTMI_USB1:
+ /* Put the USB1 device(UTMI clock domain) in Out-Of-reset state */
+ writel(readl(USB_RST_CNTRL) & ~USB1_UTMI_RESET_BIT, USB_RST_CNTRL);
+ break;
+ case COMPONENT_USB1_PHY:
+ /* Put the USB1_PHY devices in Out-Of-Reset state */
+ writel(readl(USB_RST_CNTRL)& ~USB1_PHY_RESET_BIT, USB_RST_CNTRL);
+ break;
+ case COMPONENT_AXI_SATA:
+ /* Put the SATA device in Out-Of-Reset state */
+ writel(readl(AXI_RESET_2) & ~SATA_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_AXI_PCIE0:
+ /* Put the PCIE0 device in Out-Of-reset state */
+ writel(readl(AXI_RESET_2) & ~PCIE0_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_AXI_PCIE1:
+ /* Put the PCIE1 device in Out-Of-Reset state */
+ writel(readl(AXI_RESET_2) & ~PCIE1_AXI_RESET_BIT, AXI_RESET_2);
+ break;
+ case COMPONENT_PFE_CORE:
+ /* Put the PFE core device Out-Of-Reset state */
+ writel(readl(PFE_RESET) & ~PFE_CORE_RESET_BIT, PFE_RESET);
+ break;
+ case COMPONENT_IPSEC_EAPE_CORE:
+ /* Put the IPSEC EAPE core device in Out-Of-Reset state */
+ writel(readl(IPSEC_RESET) & ~IPSEC_EAPE_CORE_RESET_BIT, IPSEC_RESET);
+ break;
+ case COMPONENT_GEMTX:
+ /* Put the GEMTX device in Out-Of-Reset state */
+ writel(readl(GEMTX_RESET) & ~GEMTX_RESET_BIT , GEMTX_RESET);
+ break;
+ case COMPONENT_DECT:
+ /* Put the DECT device in Out-Of-Reset state */
+ writel(readl(DECT_RESET) & ~DECT_RESET_BIT , DECT_RESET);
+ break;
+ case COMPONENT_DDR_CNTLR:
+ /* Put the DDR controller device in Out-Of-Reset state */
+ writel(readl(DDR_RESET) & ~DDR_CNTRL_RESET_BIT , DDR_RESET);
+ break;
+ case COMPONENT_DDR_PHY:
+ /* Put the DDR PHY device in Out-Of-Reset state */
+ writel(readl(DDR_RESET) & ~DDR_PHY_RESET_BIT , DDR_RESET);
+ break;
+ case COMPONENT_SERDES0:
+ /* put the SERDES0 controller in Out-of-Reset state */
+ writel(readl(SERDES_RST_CNTRL) & ~SERDES0_RESET_BIT ,SERDES_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES_PCIE0:
+ /* Put the PCIE0 SERDES controller in Out-Of-Reset state */
+ writel(readl(PCIe_SATA_RST_CNTRL) & ~SERDES_PCIE0_RESET_BIT, PCIe_SATA_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES1:
+ /* Put SEDES1 controller in Out-Of-reset state */
+ writel(readl(SERDES_RST_CNTRL)& ~SERDES1_RESET_BIT ,SERDES_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES_PCIE1:
+ /* Put PCIE1 serdes controller in Out-Of-Reset state */
+ writel(readl(PCIe_SATA_RST_CNTRL) & ~SERDES_PCIE1_RESET_BIT , PCIe_SATA_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES_SATA0:
+ writel(readl(PCIe_SATA_RST_CNTRL) & ~SERDES_SATA0_RESET_BIT, PCIe_SATA_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES2:
+ /* Put Serdes contrller 1 in Out-Of-Reset state */
+ writel(readl(SERDES_RST_CNTRL) & ~SERDES2_RESET_BIT ,SERDES_RST_CNTRL);
+ break;
+ case COMPONENT_SERDES_SATA1:
+ /* Put SATA1 serdes controller in Out-Of-reset state */
+ writel(readl(PCIe_SATA_RST_CNTRL) & ~SERDES_SATA1_RESET_BIT , PCIe_SATA_RST_CNTRL);
+ break;
+ case COMPONENT_SGMII:
+ /* Put the SGMII controller in Out-Of-Reset state */
+ writel(readl(SGMII_OCC_RESET) & ~SGMII_RESET_BIT, SGMII_OCC_RESET);
+ break;
+ case COMPONENT_SATA_PMU:
+ /* Put the SATA PMU(KEEP ALIVE clock) in Out-Of-Reset state */
+ writel(readl(SATA_PMU_RESET) & ~SATA_PMU_RESET_BIT , SATA_PMU_RESET);
+ break;
+ case COMPONENT_SATA_OOB:
+ /* Put the SATA OOB in Out-Of-Reset state */
+ writel(readl(SATA_OOB_RESET) & ~SATA_OOB_RESET_BIT , SATA_OOB_RESET);
+ break;
+ case COMPONENT_TDMNTG:
+ /* Put the TDMNTG in Out-Of-Reset state */
+ writel(readl(TDMNTG_RESET) & ~TDMNTG_RESET_BIT, TDMNTG_RESET);
+ break;
+ default :
+ break;
+ }
+}
+spin_unlock_irqrestore(&reset_lock,flags);
+}
+EXPORT_SYMBOL(c2000_block_reset);
+
+#if defined(CONFIG_C2K_MFCN_EVM)
+void GPIO_reset_external_device(int block,int state)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* Blocks to be put in out of Reset and reset mode
+ * 0 ----> out of reset
+ * 1 ----> reset
+ */
+ switch (block){
+ case COMPONENT_ATHEROS_SWITCH:
+ if(gpio_request_one(GPIO_PIN_NUM_5, GPIOF_OUT_INIT_HIGH, GPIO_PIN_DESC_5)){
+ printk(KERN_ERR "%s:%d: Cannot request gpio for gpio-%d\n", \
+ __func__, __LINE__, GPIO_PIN_NUM_4);
+ return;
+ }
+
+ if (state){
+ gpio_set_value(GPIO_PIN_NUM_5, GPIO_SET_0);
+
+ gpio_direction_input(GPIO_PIN_NUM_5);
+ }else{
+ gpio_set_value(GPIO_PIN_NUM_5, GPIO_SET_0);
+
+ gpio_direction_output(GPIO_PIN_NUM_5, GPIO_SET_0);
+
+ gpio_set_value(GPIO_PIN_NUM_5, GPIO_SET_1);
+ }
+
+ gpio_free(GPIO_PIN_NUM_5);
+
+ break;
+
+ case COMPONENT_SLIC:
+ if(gpio_request_one(GPIO_PIN_NUM_4, GPIOF_OUT_INIT_HIGH, GPIO_PIN_DESC_4)){
+ printk(KERN_ERR "%s:%d: Cannot request gpio for gpio-%d\n", \
+ __func__, __LINE__, GPIO_PIN_NUM_4);
+ return;
+ }
+
+ if (state){
+ gpio_set_value(GPIO_PIN_NUM_4, GPIO_SET_0);
+
+ gpio_direction_input(GPIO_PIN_NUM_4);
+ }else{
+ gpio_set_value(GPIO_PIN_NUM_4, GPIO_SET_0);
+
+ gpio_direction_output(GPIO_PIN_NUM_4, GPIO_SET_0);
+
+ gpio_set_value(GPIO_PIN_NUM_4, GPIO_SET_1);
+ }
+
+ gpio_free(GPIO_PIN_NUM_4);
+
+ break;
+
+ case COMPONENT_PCIE0:
+ if (state){
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_48, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) | GPIO_PIN_48, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~GPIO_PIN_48, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }else{
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_48, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) & ~GPIO_PIN_48, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) | GPIO_PIN_48, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) | GPIO_PIN_48, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }
+ break;
+ case COMPONENT_PCIE1:
+ if (state){
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_47, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) | GPIO_PIN_47, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~GPIO_PIN_47, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }else{
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_47, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) & ~GPIO_PIN_47, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) | GPIO_PIN_47, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) | GPIO_PIN_47, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }
+ break;
+ case COMPONENT_USB_HUB:
+ if (state){
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_50, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) | GPIO_PIN_50, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~GPIO_PIN_50, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }else{
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_50, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) & ~GPIO_PIN_50, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) | GPIO_PIN_50, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) | GPIO_PIN_50, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }
+ break;
+ case COMPONENT_EXP_DAUGTHER_CARD:
+ if (state){
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_49, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) | GPIO_PIN_49, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~GPIO_PIN_49, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }else{
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_49, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) & ~GPIO_PIN_49, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) | GPIO_PIN_49, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) | GPIO_PIN_49, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }
+ break;
+ case COMPONENT_RGMII0:
+ if (state){
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_46, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) | GPIO_PIN_46, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) & ~GPIO_PIN_46, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }else{
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_46, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) & ~GPIO_PIN_46, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) | GPIO_PIN_46, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) | GPIO_PIN_46, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }
+ break;
+ case COMPONENT_RGMII1:
+ if (state){
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_45, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) | GPIO_PIN_45, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT)& ~GPIO_PIN_45, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }else{
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) & ~GPIO_PIN_45, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel( readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) & ~GPIO_PIN_45, COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_OUTPUT) | GPIO_PIN_45, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ writel(readl(COMCERTO_GPIO_63_32_PIN_SELECT) | GPIO_PIN_45, COMCERTO_GPIO_63_32_PIN_SELECT);
+ }
+ break;
+ default:
+ break;
+ }
+
+ spin_unlock_irqrestore(&gpio_lock,flags);
+}
+EXPORT_SYMBOL(GPIO_reset_external_device);
+#endif
+
+void reset_init(void){
+
+ /*Initilize the DUS ,serde0/1/2 and I2CSPI dependancy values */
+ i2cspi_state[0]=i2cspi_state[1]=dus_state[0]=dus_state[1]=dus_state[2]=0;
+
+ spin_lock_init(&reset_lock);
+
+ /* Do Any boottime Reset/Out-Of-Reset to devices if required*/
+}
diff --git a/arch/arm/mach-comcerto/serdes-c2000.c b/arch/arm/mach-comcerto/serdes-c2000.c
new file mode 100644
index 0000000..955f1cc
--- /dev/null
+++ b/arch/arm/mach-comcerto/serdes-c2000.c
@@ -0,0 +1,190 @@
+
+/*
+ * Copyright (C) Mindspeed Technologies, Inc. 2011. All rights reserved.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * @file serdes.c
+ * @brief this C file will contain all required functions to program
+ * Snowbush SerDes PHY interface.
+ * @date 10/02/2011
+ */
+
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <mach/comcerto-2000.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/sizes.h>
+#include <mach/serdes-c2000.h>
+
+#define MAX_LANE_OK_WAIT_JIFFIES (200 * HZ) / 1000 /* 200ms */
+#define MAX_CMU_OK_WAIT_JIFFIES (2000 * HZ) / 1000 /* 2 Seconds */
+
+/**
+ * This function Wait for the 'Lane OK' to be signaled by the
+ * Snowbush Serdes PHY.
+ * @param sbphy_num SerDes PHY intefrace number.
+ */
+static int wait_lane_ok(u32 sbphy_num)
+{
+ u32 rd_data = 0, masked_data = 0;
+ u32 lane_ok_dtctd_mask = 0x00001000;
+ unsigned long deadline = jiffies + MAX_LANE_OK_WAIT_JIFFIES;
+
+ /* Keep looping until you see the lane_ok_o of Serdes */
+ do
+ {
+ rd_data = readl(COMCERTO_SERDES_DWC_CFG_REG( sbphy_num, SD_PHY_STS_REG_OFST));
+
+ /* Mask lane_ok Status */
+ masked_data = rd_data & lane_ok_dtctd_mask;
+
+ if(masked_data == lane_ok_dtctd_mask) {
+ /* Lane OK Detected on Serdes Port */
+ printk(KERN_INFO "Serdes%d: Lane OK Passed\n",sbphy_num);
+ return 1;
+ }
+
+ cond_resched();
+
+ } while (!time_after_eq(jiffies, deadline));
+
+ printk(KERN_INFO "Serdes%d: Lane OK Failed\n",sbphy_num);
+ return 0;
+}
+
+
+/**
+ * This function wait for the 'CMU OK' to be signaled by the
+ * Snowbush Serdes PHY.
+ * @param sbphy_num SerDes PHY intefrace number.
+ */
+static int wait_cmu_ok(u32 sbphy_num)
+{
+ u32 rd_data = 0, masked_data = 0;
+ u32 cmu_ok_dtctd_mask = 0x00004000;
+ int CMU_Offset;
+ unsigned long deadline = jiffies + MAX_CMU_OK_WAIT_JIFFIES;
+
+ CMU_Offset = COMCERTO_SERDES_DWC_CFG_REG( sbphy_num, SD_PHY_STS_REG_OFST );
+
+
+ /* Keep looping until you see the cmu_ok_o of Serdes */
+ do
+ {
+ rd_data = readl(CMU_Offset);
+
+ /* Mask cmu_ok Status */
+ masked_data = rd_data & cmu_ok_dtctd_mask;
+
+ if(masked_data == cmu_ok_dtctd_mask) {
+ /* CMU OK Detected on Serdes Port */
+ //printk(KERN_INFO "Serdes%d: CMU OK Passed\n",sbphy_num);
+ return 1;
+ }
+
+ cond_resched();
+
+ } while (!time_after_eq(jiffies, deadline));
+
+ printk(KERN_INFO "Serdes%d: CMU OK Failed\n",sbphy_num);
+
+ return 0;
+}
+
+
+/**
+ * This function wait for the specified configured Snowbush PHY
+ * (Serdes) to issue it's CMU-OK, and it's Lane to become Ready
+ * after releasing the CMU & Lane resets.
+ * @param sbphy_num SerDes PHY intefrace number.
+ */
+static int wait_sb_cmu_lane_rdy(u32 sbphy_num, u32 type)
+{
+ u32 sd_ctl2_reg_offset;
+ u32 cmu_rst_mask = 0x00010000;
+ u32 lane_rst_mask = 0x00000040;
+ u32 tmp = 0;
+
+ sd_ctl2_reg_offset = COMCERTO_SERDES_DWC_CFG_REG( sbphy_num, SD_PHY_CTRL2_REG_OFST );
+
+ /* Releasing the CMU Reset */
+ tmp = readl(sd_ctl2_reg_offset);
+ tmp = tmp & (~cmu_rst_mask);
+ tmp = tmp | cmu_rst_mask;
+
+ writel(tmp, sd_ctl2_reg_offset );
+
+ /* Waiting for CMU OK */
+ if( !wait_cmu_ok(sbphy_num) )
+ return -1;
+
+ if ( type == SD_DEV_TYPE_PCIE )
+ writel(0xC3, COMCERTO_SERDES_REG(sbphy_num, (SD_COMMON_LANE << 2)));
+ else
+ writel(0x03, COMCERTO_SERDES_REG(sbphy_num, (SD_COMMON_LANE << 2)));
+
+ /* Releasing the Lane Reset */
+ tmp = readl(sd_ctl2_reg_offset);
+ tmp = tmp & (~lane_rst_mask);
+ tmp = tmp | lane_rst_mask;
+
+ writel(tmp, sd_ctl2_reg_offset);
+
+ /* Waiting for the Lane Ready */
+ if (type != SD_DEV_TYPE_PCIE) {
+ if( !wait_lane_ok(sbphy_num) )
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * This function initialize the Snowbush PHY (Serdes) for operation
+ * with the one of the PCIE,SATA or SGMII IP blocks, and then waiting
+ * until it issue it's CMU-OK, and it's Lane to become Ready after
+ * releasing the CMU & Lane Resets.
+ * @param phy_num SerDes PHY intefrace number.
+ * @param *regs Register file (Array of registers and coresponding
+ * values to be programmed).
+ * @param size Number of registers to be programmed.
+ */
+int serdes_phy_init(int phy_num, struct serdes_regs_s *regs, int size, int type)
+{
+ int ii;
+
+ /* Initilize serdes phy registers */
+ for( ii = 0; ii < size; ii++ )
+ writel(regs[ii].val, COMCERTO_SERDES_REG(phy_num, regs[ii].ofst));
+
+ /* Wait for the initialization of Serdes-1 Port/Lane to become Ready */
+ return wait_sb_cmu_lane_rdy(phy_num, type);
+}
+
+EXPORT_SYMBOL(serdes_phy_init);
+
diff --git a/arch/arm/mach-comcerto/sleep.S b/arch/arm/mach-comcerto/sleep.S
new file mode 100644
index 0000000..5a5c275
--- /dev/null
+++ b/arch/arm/mach-comcerto/sleep.S
@@ -0,0 +1,284 @@
+/*
+ * arch/arm/mach-comcerto/sleep.S
+ *
+ * Author: Makarand Pawagi
+ *
+ * Copyright (C) 2013 Mindspeed Technologies, Inc.
+ * Copyright (c) 2003 ARM Limited
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/memory.h>
+
+#define SCRATCHPAD_SUSPEND_INDICATOR_LOC (0xf0800000 + 0x2400)
+#define SCRATCHPAD_BASE_P (0xf0800000+0x2400+0x100) /*IRAM_MEMORY_VADDR + Offset*/
+#define SCRATCHPAD_CPU_CONTEXT_LOC_OFFSET 0x70
+#define SCRATCHPAD_CPU_CONTEXT_LOC SCRATCHPAD_BASE_P + SCRATCHPAD_CPU_CONTEXT_LOC_OFFSET
+
+
+ .text
+/* Function call to get the restore pointer for resume from OFF */
+ENTRY(c2k_get_restore_pointer)
+ stmfd sp!, {lr} @ save registers on stack
+ ARM ( adr r0, comcerto_cpu_restore )
+ bic r0, #PAGE_OFFSET
+ ldmfd sp!, {pc} @ restore regs and return
+ENTRY(get_restore_pointer_sz)
+ .word . - get_restore_pointer_sz
+
+
+
+
+/*
+ * ======================
+ * == Idle entry point ==
+ * ======================
+ */
+
+/*
+ * Forces C2K into idle state
+ *
+ * comcerto_cpu_suspend() - This bit of code saves the CPU context if needed
+ * and executes the WFI instruction. Calling WFI effectively changes the
+ * power domains states to the desired target power states.
+ */
+ .align 3
+ENTRY(comcerto_cpu_suspend)
+ stmfd sp!, {r4 - r11, lr} @ save registers on stack
+
+ /*
+ * r0 contains information about saving context:
+ * 0 - No context lost
+ * 1 - Only L1 and logic lost
+ * 2 - Only L2 lost (Even L1 is retained we clean it along with L2)
+ * 3 - Both L1 and L2 lost and logic lost
+ */
+
+ /*
+ * For OFF mode: save context and jump to WFI (comcerto_do_wfi)
+ */
+ cmp r0, #0x0 @ If no context save required,
+ beq comcerto_do_wfi @ jump to the WFI
+
+
+ /* Otherwise fall through to the save context code */
+comcerto_save_context_wfi:
+ /*
+ * jump out to kernel flush routine
+ * - reuse that code is better
+ * - it executes in a cached space so is faster than refetch per-block
+ * - should be faster and will change with kernel
+ * - 'might' have to copy address, load and jump to it
+ * Flush all data from the L1 data cache before disabling
+ * SCTLR.C bit.
+ */
+ ldr r1, kernel_flush
+ mov lr, pc
+ bx r1
+
+ /*
+ * Clear the SCTLR.C bit to prevent further data cache
+ * allocation. Clearing SCTLR.C would make all the data accesses
+ * strongly ordered and would not hit the cache.
+ */
+ ARM ( mrc p15, 0, r0, c1, c0, 0 )
+ ARM ( bic r0, r0, #(1 << 2) ) @ Disable the C bit
+ ARM ( mcr p15, 0, r0, c1, c0, 0 )
+ ARM ( isb )
+
+ /*
+ * Invalidate L1 data cache. Even though only invalidate is
+ * necessary exported flush API is used here. Doing clean
+ * on already clean cache would be almost NOP.
+ */
+ ldr r1, kernel_flush
+ blx r1
+ /*
+ * The kernel doesn't interwork: v7_flush_dcache_all in particluar will
+ * always return in Thumb state when CONFIG_THUMB2_KERNEL is enabled.
+ * This sequence switches back to ARM. Note that .align may insert a
+ * nop: bx pc needs to be word-aligned in order to work.
+ */
+ THUMB( .thumb )
+ THUMB( .align )
+ THUMB( bx pc )
+ THUMB( nop )
+ .arm
+
+ b comcerto_do_wfi
+
+/*
+ * Local variables
+ */
+kernel_flush:
+ .word v7_flush_dcache_all
+
+
+/* ===================================
+ * == WFI instruction => Enter idle ==
+ * ===================================
+ */
+
+/*
+ * Do WFI instruction
+ * Includes the resume path for non-OFF modes [TBD]
+ */
+ .align 3
+ENTRY(comcerto_do_wfi)
+
+ /* Store cpsr and spsr */
+ ldr r7, scratchpad_cpu_context_loc
+
+ /*mrs r4, cpsr
+ mrs r5, spsr
+ stmia r7!, {r4-r5}*/
+
+
+
+ /* Saving all the banked registers */
+ mrs r0, cpsr
+
+ /* Save the Undef mode reisters */
+ bic r1, r0, #0x1f
+ orr r1, r1, #0x1b
+ msr cpsr_c, r1
+ ARM ( stmia r7!, {r13-r14} )
+ mrs r13, spsr
+ stmia r7!, {r13}
+
+ /* Save the Abort mode reisters */
+ bic r1, r0, #0x1f
+ orr r1, r1, #0x17
+ msr cpsr_c, r1
+ ARM ( stmia r7!, {r13-r14} )
+ mrs r13, spsr
+ stmia r7!, {r13}
+
+ /* Save the IRQ mode reisters */
+ bic r1, r0, #0x1f
+ orr r1, r1, #0x12
+ msr cpsr_c, r1
+ ARM ( stmia r7!, {r13-r14} )
+ mrs r13, spsr
+ stmia r7!, {r13}
+
+ /* Save the FIQ mode reisters */
+ bic r1, r0, #0x1f
+ orr r1, r1, #0x11
+ msr cpsr_c, r1
+ ARM ( stmia r7!, {r8-r14} )
+ THUMB ( stmia r7!, {r8-r12} )
+ mrs r13, spsr
+ stmia r7!, {r13}
+
+ /* Return to the original mode */
+ msr cpsr_c, r0
+
+ /* We can Put DDR in self refresh mode here [TBD] */
+
+ /* Pass control to UtilPE */
+ ldr r4, scratchpad_reboot_indicator_loc
+ mov r5, #0xFF
+ str r5, [r4]
+
+ /* Data memory barrier and Data sync barrier */
+ dsb
+ dmb
+
+/*
+ * ===================================
+ * == WFI instruction => Enter idle ==
+ * ===================================
+ */
+ wfi @ wait for interrupt
+
+/*
+ * =========================================
+ * == Resume path for non-OFF modes TBD ==
+ * =========================================
+ */
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+/*
+ * ===================================
+ * TBD
+ * ===================================
+ */
+
+/*
+ * ==============================
+ * == Resume path for OFF mode ==
+ * ==============================
+ */
+
+/*
+ * The restore function is instaleed at 0x0 location
+ * of DDR.
+ */
+
+ENTRY(comcerto_cpu_restore)
+ ldr r1, l2dis_3630
+ cmp r1, #0x1 /*@ Test if L2 re-enable needed on 3630*/
+ bne skipl2reen
+ mrc p15, 0, r1, c1, c0, 1
+ orr r1, r1, #2 /*@ re-enable L2 cache*/
+ mcr p15, 0, r1, c1, c0, 1
+skipl2reen:
+
+
+ /* Now branch to the common CPU resume function */
+ blx cpu_resume
+
+ .ltorg
+
+/*
+ * Local variables
+ */
+
+l2dis_3630:
+ .word
+
+
+ENTRY(copy_words)
+ cmp r2, #0
+ beq f1
+b0:
+ ldr r3, [r1], #4
+ str r3, [r0], #4
+ subs r2, r2, #1
+ bne b0
+f1:
+ bx lr
+
+
+
+
+scratchpad_cpu_context_loc:
+ .word SCRATCHPAD_CPU_CONTEXT_LOC
+scratchpad_reboot_indicator_loc:
+ .word SCRATCHPAD_SUSPEND_INDICATOR_LOC
diff --git a/arch/arm/mach-comcerto/sysfstdm.c b/arch/arm/mach-comcerto/sysfstdm.c
new file mode 100644
index 0000000..0f50020
--- /dev/null
+++ b/arch/arm/mach-comcerto/sysfstdm.c
@@ -0,0 +1,552 @@
+ /*
+ * Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <mach/comcerto-common.h>
+#include <asm/div64.h>
+#include <mach/comcerto-2000/clk-rst.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <mach/reset.h>
+
+/* Defalult Rate value is in Hz*/
+#define TDMNTG_DEFAULT_REF_CLK 500000000
+
+/* since 'ppm' means parts-per-million */
+#define MILLION (1000000UL)
+
+/* NTG to TDM clock dividers */
+#define COMCERTO_BLOCK_TDM_DIV 1
+#define COMCERTO_BLOCK_ZDS_DIV 24
+#define COMCERTO_BLOCK_GPIO_DIV 1
+#define COMCERTO_BLOCK_MSIF_DIV 12
+static int comcerto_block_clk_div [4] = {COMCERTO_BLOCK_TDM_DIV, COMCERTO_BLOCK_ZDS_DIV, COMCERTO_BLOCK_GPIO_DIV, COMCERTO_BLOCK_MSIF_DIV};
+static int block_selected;
+static int block_sel_cnt = 0;
+static spinlock_t block_lock;
+
+/*ntgref clock*/
+static struct clk *clk_ntg_ref;
+static unsigned long tdmntg_ref_clk;
+
+static unsigned long BaseClock = 0; /* frequency is set by clock_frequency_set() */
+
+static void fsync_output_set(unsigned int fsoutput)
+{
+ if (fsoutput)
+ {
+ writel(readl(COMCERTO_GPIO_TDM_MUX) | (1 << 0), COMCERTO_GPIO_TDM_MUX);
+ writel(readl(TDM_FSYNC_GEN_CTRL) | (1 << 0), TDM_FSYNC_GEN_CTRL);
+ }
+ else
+ {
+ writel(readl(COMCERTO_GPIO_TDM_MUX) & ~(1 << 0), COMCERTO_GPIO_TDM_MUX);
+ writel(readl(TDM_FSYNC_GEN_CTRL) & ~(1 << 0), TDM_FSYNC_GEN_CTRL);
+ }
+}
+
+static void fsync_polarity_set(unsigned int fspolarity)
+{
+ /* 28 FSYNC_FALL(RISE)_EDGE */
+ if (fspolarity)
+ writel(readl(TDM_FSYNC_GEN_CTRL) | FSYNC_FALL_EDGE, TDM_FSYNC_GEN_CTRL);
+ else
+ writel(readl(TDM_FSYNC_GEN_CTRL) & ~FSYNC_FALL_EDGE, TDM_FSYNC_GEN_CTRL);
+}
+
+static void fsync_lphase_set(u32 fslwidth)
+{
+ /* Low_Phase_Width 7ff- maximum */
+ if (fslwidth > 0x7FF) {
+ printk(KERN_ERR "%s: Low Phase width value is out of range %#x > 0x7FF\n", __func__, fslwidth);
+ return;
+ }
+
+ writel(fslwidth, TDM_FSYNC_LOW);
+}
+
+static void fsync_hphase_set(u32 fshwidth)
+{
+ /* High_Phase_Width 7ff- maximum */
+ if (fshwidth > 0x7FF) {
+ printk(KERN_ERR "%s: High Phase width value is out of range %#x > 0x7FF\n", __func__, fshwidth);
+ return;
+ }
+
+ writel(fshwidth, TDM_FSYNC_HIGH);
+}
+
+static void clock_frequency_set(unsigned long clockhz)
+{
+ unsigned long long ntg_incr ;
+
+ BaseClock = clockhz;
+
+ /* Calculate NTG clock: multiply TDM base clock with block divider */
+ /* clock_frequency_set() is called with block_lock taken */
+ ntg_incr = comcerto_block_clk_div[block_selected] * clockhz;
+ /* get frequency resolution on an 32-bit accumulator */
+ ntg_incr = ntg_incr * (1ULL << 32) + tdmntg_ref_clk / 2;
+ do_div(ntg_incr, tdmntg_ref_clk);
+ printk(KERN_INFO "%s: (%d:%d) NTG INCR value is %llu\n", __func__, block_selected, comcerto_block_clk_div[block_selected], ntg_incr);
+
+ /* ntg_incr = 0x10C6F7A for 2.048 MHz */
+ /* ntg_incr = 0xC953969 for 24.576 MHz */
+ /* ntg_incr = 0x192A7371 for 49.152 MHz */
+ writel(ntg_incr, TDM_NTG_INCR);
+}
+
+static unsigned long clock_frequency_get(void)
+{
+ unsigned long long clc_data;
+
+ /* According to the desired TDM clock output frequency, this field should be configured */
+ clc_data = (readl(TDM_NTG_INCR) & 0x3FFFFFFF);/* get frequency from resolution on an 32-bit accumulator */
+ clc_data = (clc_data * tdmntg_ref_clk + (1ULL << 31)) >> 32;
+ /* Divide down the Data with TDM block divider */
+ do_div(clc_data, comcerto_block_clk_div[block_selected]); /* do_div because of 64 bit operation*/
+ return (unsigned long)(clc_data);
+}
+
+static void clock_output_set(unsigned long clockout)
+{
+ switch (clockout) {
+ case 0:
+ writel((0x2 << 12) | (readl(COMCERTO_GPIO_BOOTSTRAP_OVERRIDE) & ~(0x3 << 12)), COMCERTO_GPIO_BOOTSTRAP_OVERRIDE);
+ break;
+ case 1:
+ writel((0x3 << 12) | (readl(COMCERTO_GPIO_BOOTSTRAP_OVERRIDE) & ~(0x3 << 12)), COMCERTO_GPIO_BOOTSTRAP_OVERRIDE);
+ break;
+ case 2:
+ writel((0x0 << 12) | (readl(COMCERTO_GPIO_BOOTSTRAP_OVERRIDE) & ~(0x3 << 12)), COMCERTO_GPIO_BOOTSTRAP_OVERRIDE);
+ break;
+ default:
+ printk(KERN_ERR "%s: Unknown clock output value\n", __func__);
+ }
+}
+
+static void clock_ppm_adjust(long ppm)
+{
+ unsigned long long clc_data;
+ unsigned long freq_set = BaseClock;
+ int nsign = 0;
+
+ if (!freq_set) {
+ printk(KERN_ERR "(%s): Could not adjust frequency: you should set it before\n", __func__);
+ return;
+ }
+
+ if (ppm < 0) {
+ nsign = 1;
+ ppm = -ppm;
+ }
+
+ if (nsign && (ppm >= MILLION)) {
+ /* overflow dangerous */
+ printk(KERN_ERR "(%s): This is too much ppm: -%lu\n", __func__, (unsigned long)ppm);
+ return;
+ }
+
+ clc_data = ppm * (1ULL << 32);
+
+ do_div(clc_data, MILLION);
+
+ if (nsign) {
+ clc_data = (1ULL << 32) - clc_data;
+ } else {
+ clc_data = (1ULL << 32) + clc_data;
+ }
+
+ clc_data = clc_data * freq_set + tdmntg_ref_clk / 2; /* with rounding to nearest integer */
+ clc_data *= comcerto_block_clk_div[block_selected];
+ do_div(clc_data, tdmntg_ref_clk);
+
+ if (clc_data & ~0x3FFFFFFF) {
+ /* unaccounted bits dangerous */
+ printk(KERN_ERR "(%s): This is too much ppm: %lu\n", __func__, (unsigned long)ppm);
+ return;
+ }
+
+ writel((clc_data & 0x3FFFFFFF) | (readl(TDM_NTG_INCR) & ~0x3FFFFFFF), TDM_NTG_INCR);
+}
+
+static long clock_ppm_get(void)
+{
+ unsigned long freq_set = BaseClock;
+ unsigned long freq = clock_frequency_get();
+ unsigned long long ppm;
+
+ if (freq > freq_set) {
+ ppm = (freq - freq_set) * MILLION + (freq_set >> 1);
+ do_div(ppm, freq_set);
+ return (long)ppm;
+ } else {
+ ppm = (freq_set - freq) * MILLION + (freq_set >> 1);
+ do_div(ppm, freq_set);
+ return (-1 * (long)ppm);
+ }
+}
+
+#define TDM_CTRL_SLIC_RESET 0x80
+#define TDM_CTRL_CLK_DIV_BYPASS 0x40
+#define TDM_CTRL_DEF_DIV 0x2
+#define TDM_CTLR_RESET_BYPASS TDM_CTRL_SLIC_RESET | TDM_CTRL_CLK_DIV_BYPASS | TDM_CTRL_DEF_DIV
+
+static void tdm_mux_set(u32 tdmmux)
+{
+ block_selected = tdmmux;
+ /* TDM interface Muxing [5:4]
+ 00 TDM block is selected
+ 01 ZDS block (Zarlink) is selected
+ 10 GPIO[63:60] signals are selected
+ 11 MSIF block (SiLabs) is selected */
+
+ switch (block_selected){
+ case 0:
+ /* TDM block selected (SiLabs si3227) */
+ writel(TDM_CTLR_RESET_BYPASS, TDM_CLK_CNTRL); /* bypass TDM divider --> TDM = NTG out, keen ZDS/MSIF Slic reset */
+ writel((0x0 << 4) |(readl(COMCERTO_GPIO_MISC_PIN_SELECT) & ~(0x3 << 4)), COMCERTO_GPIO_MISC_PIN_SELECT);
+ break;
+
+ case 1:
+ /* ZDS block selected (Zarlink le88264) */
+ writel(TDM_CTRL_SLIC_RESET | COMCERTO_BLOCK_ZDS_DIV, TDM_CLK_CNTRL); /* TDM = NTG out / 24*/
+ writel((0x1 << 4) |(readl(COMCERTO_GPIO_MISC_PIN_SELECT) & ~(0x3 << 4)), COMCERTO_GPIO_MISC_PIN_SELECT);
+ writel(COMCERTO_BLOCK_ZDS_DIV, TDM_CLK_CNTRL); /* Remove out of reset */
+ break;
+
+ case 2:
+ /* GPIO[63:60] signals selected */
+ writel(TDM_CTLR_RESET_BYPASS, TDM_CLK_CNTRL); /* bypass TDM divider --> TDM = NTG out, keen ZDS/MSIF Slic reset */
+ writel((0x2 << 4) |(readl(COMCERTO_GPIO_MISC_PIN_SELECT) & ~(0x3 << 4)), COMCERTO_GPIO_MISC_PIN_SELECT);
+ break;
+
+ case 3:
+ /* MSIF block selected (SiLabs si32268) */
+ writel(TDM_CTRL_SLIC_RESET | COMCERTO_BLOCK_MSIF_DIV, TDM_CLK_CNTRL); /* TDM = NTG out / 12 */
+ writel((0x3 << 4) |(readl(COMCERTO_GPIO_MISC_PIN_SELECT) & ~(0x3 << 4)), COMCERTO_GPIO_MISC_PIN_SELECT);
+ writel(COMCERTO_BLOCK_MSIF_DIV, TDM_CLK_CNTRL); /* Remove out of reset */
+
+ /* Delay 100us after C2k TDM block has been un-reset, before SLIC is un-reset.
+ Ensures MSIF interface clock is active well before SLIC is un-reset (SiLabs spec).
+ */
+ udelay(100);
+
+ writel(0x1 << 30, COMCERTO_GPIO_63_32_PIN_OUTPUT); /* remove slic out of reset */
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Unknown TDM MUX value\n", __func__);
+ }
+}
+
+#if 0
+static void tdm_dr_set(u32 tdmdr)
+{
+ if(tdmdr > 0x3F)
+ {
+ printk(KERN_ERR "%s: TDM_DR value is out of range %#x > 0x3F\n", __func__, tdmdr);
+ return;
+ }
+
+ writel((tdmdr << 24) |(readl(COMCERTO_GPIO_PAD_CONFIG0) & ~(0x3F << 24)), COMCERTO_GPIO_PAD_CONFIG0);
+}
+
+static void tdm_dx_set(u32 tdmdx)
+{
+ if(tdmdx > 0x3F)
+ {
+ printk(KERN_ERR "%s: TDM_DX value is out of range %#x > 0x3F\n", __func__, tdmdx);
+ return;
+ }
+
+ writel((tdmdx << 18) |(readl(COMCERTO_GPIO_PAD_CONFIG0) & ~(0x3F << 18)), COMCERTO_GPIO_PAD_CONFIG0);
+}
+
+static void tdm_fs_set(u32 tdmfs)
+{
+ if(tdmfs > 0x3F)
+ {
+ printk(KERN_ERR "%s: TDM_FS value is out of range %#x > 0x3F\n", __func__, tdmfs);
+ return;
+ }
+
+ writel((tdmfs << 12) |(readl(COMCERTO_GPIO_PAD_CONFIG0) & ~(0x3F << 12)), COMCERTO_GPIO_PAD_CONFIG0);
+}
+
+static void tdm_ck_set(u32 tdmck)
+{
+ if(tdmck > 0x3F)
+ {
+ printk(KERN_ERR "%s: TDM_CK value is out of range %#x > 0x3F\n", __func__, tdmck);
+ return;
+ }
+
+ writel((tdmck << 6) |(readl(COMCERTO_GPIO_PAD_CONFIG0) & ~(0x3F << 6)), COMCERTO_GPIO_PAD_CONFIG0);
+}
+#endif
+
+int tdm_get_block(unsigned int block)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ /* 0x0 - TDM block, 0x1 - ZDS block, 0x2 - GPIO[63:60] signals and 0x3 - MSIF block */
+ printk(KERN_INFO "%s: get block %d (selected %d)\n", __func__, block, block_selected);
+ if (block > 3)
+ return -EINVAL;
+
+ spin_lock_irqsave(&block_lock, flags);
+
+ if(block == block_selected)
+ {
+ block_sel_cnt++;
+ }
+ else
+ {
+ if (block_sel_cnt)
+ {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ block_sel_cnt++;
+ /* Selected block global variable will be updated */
+ tdm_mux_set(block);
+ /* Clock to be recalculated accordingly to selected block */
+ clock_frequency_set(BaseClock);
+ }
+
+out:
+ spin_unlock_irqrestore(&block_lock,flags);
+
+ printk(KERN_INFO "%s: get block result %d\n", __func__, rc);
+ return rc;
+}
+
+void tdm_put_block(unsigned int block)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&block_lock, flags);
+
+ printk(KERN_INFO "%s: put block %d (selected %d, cnt %d)\n",
+ __func__, block, block_selected, block_sel_cnt);
+
+ if (block == block_selected)
+ {
+ if (block_sel_cnt)
+ block_sel_cnt--;
+ else
+ printk(KERN_ERR "%s: block hasn't selected yet\n", __func__);
+ }
+ else
+ printk(KERN_ERR "%s: Invalid block\n", __func__);
+
+ spin_unlock_irqrestore(&block_lock,flags);
+}
+
+static ssize_t tdm_data_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (strcmp("fs_polarity", attr->attr.name) == 0) /* FSYNC_FALL(RISE)_EDGE */
+ return sprintf(buf, "%d\n", (readl(TDM_FSYNC_GEN_CTRL) >> 1) & 0x1);
+ else if (strcmp("fs_lwidth", attr->attr.name) == 0) /* Low_Phase_Width */
+ return sprintf(buf, "%x\n", (readl(TDM_FSYNC_LOW) & 0x3FF));
+ else if (strcmp("fs_hwidth", attr->attr.name) == 0) /* High_Phase_Width */
+ return sprintf(buf, "%x\n", (readl(TDM_FSYNC_HIGH) & 0x3FF));
+ else if (strcmp("fs_output", attr->attr.name) == 0) /* Generic Pad Control and Version ID Register[2] */
+ return sprintf(buf, "%d\n", readl(COMCERTO_GPIO_TDM_MUX) & 0x1);
+ else if (strcmp("clock_ppmshift", attr->attr.name) == 0)
+ return sprintf(buf, "%ld\n", clock_ppm_get());
+ else if (strcmp("clock_output", attr->attr.name) == 0)
+ return sprintf(buf, "%d\n", (readl(COMCERTO_GPIO_SYSTEM_CONFIG) >> 3) & 0x1);
+ else if (strcmp("clock_hz", attr->attr.name) == 0)
+ return sprintf(buf, "%lu\n", clock_frequency_get());
+ else if (strcmp("tdm_mux", attr->attr.name) == 0)
+ return sprintf(buf, "%lu\n", (readl(COMCERTO_GPIO_MISC_PIN_SELECT) >> 4) & 0x3);
+ else
+ {
+ printk(KERN_ERR "%s: Unknown file attribute\n", __func__);
+ return -1;
+ }
+}
+
+static ssize_t tdm_data_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long tdm_data = simple_strtoul(buf, NULL, 0);
+
+ if (strcmp("fs_polarity", attr->attr.name) == 0)
+ fsync_polarity_set(tdm_data);
+ else if (strcmp("fs_lwidth", attr->attr.name) == 0)
+ fsync_lphase_set(tdm_data);
+ else if (strcmp("fs_hwidth", attr->attr.name) == 0)
+ fsync_hphase_set(tdm_data);
+ else if (strcmp("fs_output", attr->attr.name) == 0)
+ fsync_output_set(tdm_data);
+ else if (strcmp("clock_hz", attr->attr.name) == 0)
+ clock_frequency_set(tdm_data);
+ else if (strcmp("clock_output", attr->attr.name) == 0)
+ clock_output_set(tdm_data);
+ else if (strcmp("clock_ppmshift", attr->attr.name) == 0)
+ clock_ppm_adjust(simple_strtol(buf, NULL, 0));
+ else if (strcmp("tdm_mux", attr->attr.name) == 0) {
+ if (tdm_data != block_selected)
+ {
+ tdm_put_block(block_selected);
+ tdm_get_block(tdm_data);
+ }
+ else
+ printk(KERN_INFO "%s: TDM block %d is already selected\n", __func__, block_selected);
+ }
+ else
+ printk(KERN_ERR "%s: Unknown file attribute \n", __func__);
+
+ return count;
+}
+
+static struct device_attribute fsoutput_attr = __ATTR(fs_output, 0644, tdm_data_read, tdm_data_write);
+static struct device_attribute fspolarity_attr = __ATTR(fs_polarity, 0644, tdm_data_read, tdm_data_write);
+static struct device_attribute fshwidth_attr = __ATTR(fs_hwidth, 0644, tdm_data_read, tdm_data_write);
+static struct device_attribute fslwidth_attr = __ATTR(fs_lwidth, 0644, tdm_data_read, tdm_data_write);
+static struct device_attribute clockhz_attr = __ATTR(clock_hz, 0644, tdm_data_read, tdm_data_write);
+static struct device_attribute clockout_attr = __ATTR(clock_output, 0644, tdm_data_read, tdm_data_write);
+static struct device_attribute clockppm_attr = __ATTR(clock_ppmshift, 0644, tdm_data_read, tdm_data_write);
+static struct device_attribute tdmmux_attr = __ATTR(tdm_mux, 0644, tdm_data_read, tdm_data_write);
+
+static int comcerto_tdm_probe(struct platform_device *pdev)
+{
+ struct comcerto_tdm_data *pdata = (struct comcerto_tdm_data *)pdev->dev.platform_data;
+
+ int ret = 0;
+
+ spin_lock_init(&block_lock);
+
+ /* Get the reference to ntgref clock structure*/
+ clk_ntg_ref = clk_get(NULL,"ntgref");
+
+ /* Error Handling , if no ntgref clock reference: return error */
+ if (IS_ERR(clk_ntg_ref)) {
+ pr_err("%s: Unable to obtain ntgref clock: %ld\n",__func__,PTR_ERR(clk_ntg_ref));
+ return PTR_ERR(clk_ntg_ref);
+ }
+
+ /* Take TDM NTG out of reset*/
+ c2000_block_reset(COMPONENT_TDMNTG,0);
+
+ /*Enable the TDMNTG_REF clock*/
+ ret = clk_enable(clk_ntg_ref);
+
+ if (ret){
+ pr_err("%s: Unable to enable ntgref clock \n",__func__);
+ return ret;
+ }
+
+ /* Set the rate value to 500000000 Hz for the ref clock */
+ clk_set_rate(clk_ntg_ref,TDMNTG_DEFAULT_REF_CLK);
+
+ /*Initialize the tdmntgref clock rate value */
+ tdmntg_ref_clk = clk_get_rate(clk_ntg_ref);
+
+ writel((NTG_DIV_RST_N | NTG_EN), TDM_NTG_CLK_CTRL);
+
+ /* Inital configuration of tdm bus */
+ tdm_mux_set(pdata->tdmmux);
+ fsync_polarity_set(pdata->fspolarity);
+ fsync_lphase_set(pdata->fslwidth);
+ fsync_hphase_set(pdata->fshwidth);
+ clock_frequency_set(pdata->clockhz);
+ clock_output_set(pdata->clockout);
+ fsync_output_set(pdata->fsoutput);
+
+#if 0 // The default paramters are good
+ tdm_dr_set(pdata->tdmdr);
+ tdm_dx_set(pdata->tdmdx);
+ tdm_fs_set(pdata->tdmfs);
+ tdm_ck_set(pdata->tdmck);
+#endif
+
+ /* Creating sysfs files */
+ ret |= device_create_file(&pdev->dev, &fsoutput_attr);
+ ret |= device_create_file(&pdev->dev, &fspolarity_attr);
+ ret |= device_create_file(&pdev->dev, &fshwidth_attr);
+ ret |= device_create_file(&pdev->dev, &fslwidth_attr);
+ ret |= device_create_file(&pdev->dev, &clockhz_attr);
+ ret |= device_create_file(&pdev->dev, &clockout_attr);
+ ret |= device_create_file(&pdev->dev, &clockppm_attr);
+ ret |= device_create_file(&pdev->dev, &tdmmux_attr);
+
+ return ret;
+}
+
+static int comcerto_tdm_remove(struct platform_device *pdev)
+{
+ /* Disable the TDMNTG_REFclock */
+ clk_disable(clk_ntg_ref);
+ clk_put(clk_ntg_ref);
+
+ /* Puuting TDMNTG is reset mode */
+ c2000_block_reset(COMPONENT_TDMNTG,1);
+
+ /* Remove the Device File */
+ device_remove_file(&pdev->dev, &fsoutput_attr);
+ device_remove_file(&pdev->dev, &fspolarity_attr);
+ device_remove_file(&pdev->dev, &fshwidth_attr);
+ device_remove_file(&pdev->dev, &fslwidth_attr);
+ device_remove_file(&pdev->dev, &clockhz_attr);
+ device_remove_file(&pdev->dev, &clockout_attr);
+ device_remove_file(&pdev->dev, &clockppm_attr);
+ device_remove_file(&pdev->dev, &tdmmux_attr);
+
+ return 0;
+}
+
+/* Structure for a device driver */
+static struct platform_driver comcerto_tdm_driver = {
+ .probe = comcerto_tdm_probe,
+ .remove = comcerto_tdm_remove,
+ .driver = {
+ .name = "comcerto-tdm",
+ },
+};
+
+static int comcerto_tdm_init(void)
+{
+ int ret = 0;
+
+ ret = platform_driver_register(&comcerto_tdm_driver);
+
+ return ret;
+}
+
+static void comcerto_tdm_exit(void)
+{
+ platform_driver_unregister(&comcerto_tdm_driver);
+}
+
+module_init(comcerto_tdm_init);
+module_exit(comcerto_tdm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Comcerto TDM Driver");
+
diff --git a/arch/arm/mach-comcerto/time.c b/arch/arm/mach-comcerto/time.c
new file mode 100644
index 0000000..5ae39f5
--- /dev/null
+++ b/arch/arm/mach-comcerto/time.c
@@ -0,0 +1,603 @@
+/*
+ * linux/arch/arm/mach-comcerto/time.c
+ *
+ * Copyright (C) 2012 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/irq.h>
+
+#include <linux/export.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <asm/irq.h>
+#include <asm/smp_twd.h>
+#include <asm/sched_clock.h>
+#include <asm/localtimer.h>
+#include <asm/mach/time.h>
+#include <mach/hardware.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <mach/comcerto-2000/clock.h>
+
+/* Kernel needs a timer cadenced to 10ms */
+#define COMCERTO_KERNEL_TIMER_VALUE (COMCERTO_AHBCLK / HZ)
+#define machinecycles_to_usecs(ticks) (((ticks) * 10)/ (COMCERTO_AHBCLK/100000))
+
+/*
+ * HARDWARE TIMER
+ * Can be use by any driver for its own need
+ */
+
+#define COMCERTO_TIMER_DEBUG 1
+#define TIMER_STATUS_ENABLED (1 << 0)
+#define TIMER_STATUS_FREE (1 << 1)
+#define COMCERTO_MAX_TIMERS 6
+
+/*
+ * HARDWARE TIMER
+ * Can be use by any driver for its own need
+ */
+
+/* List of available timers */
+struct timer_hw {
+ u8 id;
+ u8 status;
+ struct comcerto_timer *t;
+};
+struct timer_hw timer_hw [COMCERTO_MAX_TIMERS] = {
+ {0, 0, NULL}, /* MSP timer */
+ {1, 0, NULL}, /* Clock event */
+ {2, 0, NULL}, /* CLock source + Sched clock */
+ {3, TIMER_STATUS_FREE, NULL},
+ {4, TIMER_STATUS_FREE, NULL},
+ {5, TIMER_STATUS_FREE, NULL}
+};
+
+static spinlock_t comcerto_timer_lock;
+
+static unsigned long COMCERTO_AHBCLK;
+
+static void comcerto_timer_disable(int t)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&comcerto_timer_lock, flags);
+ __comcerto_timer_disable(t);
+ spin_unlock_irqrestore(&comcerto_timer_lock, flags);
+}
+
+static unsigned long __comcerto_timer_get(int id)
+{
+ if (id == 1)
+ {
+ return comcerto_timer1_get();
+ }
+ else if (id == 2)
+ {
+ return comcerto_timer2_get();
+ }
+ else if (id == 3)
+ {
+ return comcerto_timer3_get();
+ }
+ else if (id == 4)
+ {
+ return comcerto_timer4_get();
+ }
+ else if (id == 5)
+ {
+ return comcerto_timer5_get();
+ }
+ else
+ return 0;
+}
+
+static void __comcerto_timer_set(int id, unsigned long count)
+{
+ if (id == 1)
+ {
+ comcerto_timer1_set(count);
+ }
+ else if (id == 2)
+ {
+ comcerto_timer2_set(0, count, 0);
+ }
+ else if (id == 3)
+ {
+ comcerto_timer3_set(0, count, 0);
+ }
+ else if (id == 4)
+ {
+ comcerto_timer4_set(count);
+ }
+ else if (id == 5)
+ {
+ comcerto_timer5_set(0, count, 0);
+ }
+}
+
+static void __timer_start(struct timer_hw *thw, unsigned long count)
+{
+ thw->status |= TIMER_STATUS_ENABLED;
+
+ __comcerto_timer_set(thw->id, count);
+
+ __comcerto_timer_enable(thw->id);
+}
+
+static void timer_start(struct timer_hw *thw, unsigned long count)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&comcerto_timer_lock, flags);
+ __timer_start(thw, count);
+ spin_unlock_irqrestore(&comcerto_timer_lock, flags);
+}
+
+static void __timer_stop(struct timer_hw *thw)
+{
+ __comcerto_timer_disable(thw->id);
+ thw->status &= ~ TIMER_STATUS_ENABLED;
+}
+
+static void timer_stop(struct timer_hw *thw)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&comcerto_timer_lock, flags);
+ __timer_stop(thw);
+ spin_unlock_irqrestore(&comcerto_timer_lock, flags);
+}
+
+static void __timer_free(struct timer_hw *thw)
+{
+ thw->status |= TIMER_STATUS_FREE;
+ thw->t->thw = (unsigned long) NULL;
+ thw->t = NULL;
+}
+
+static void timer_free(struct timer_hw *thw)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&comcerto_timer_lock, flags);
+ __timer_free(thw);
+ spin_unlock_irqrestore(&comcerto_timer_lock, flags);
+}
+
+
+static struct timer_hw *__timer_alloc(struct comcerto_timer *t)
+{
+ struct timer_hw *thw;
+ int i;
+
+ for (i = 0; i < COMCERTO_MAX_TIMERS; i++)
+ {
+ thw = &timer_hw[i];
+ if (thw->status & TIMER_STATUS_FREE)
+ {
+ thw->status &= ~ TIMER_STATUS_FREE;
+ t->thw = (unsigned long) thw;
+ thw->t = t;
+ goto found;
+ }
+ }
+
+ return NULL;
+found:
+ return thw;
+}
+
+static struct timer_hw *timer_alloc(struct comcerto_timer *t)
+{
+ struct timer_hw *thw;
+ unsigned long flags;
+
+ spin_lock_irqsave(&comcerto_timer_lock, flags);
+ thw = __timer_alloc(t);
+ spin_unlock_irqrestore(&comcerto_timer_lock, flags);
+
+ return thw;
+}
+
+int comcerto_timer_start(struct comcerto_timer *t)
+{
+ struct timer_hw *thw;
+
+ thw = (struct timer_hw *) t->thw;
+ if (!thw) {
+ thw = timer_alloc(t);
+ if (!thw) {
+ printk (KERN_ERR "Comcerto timer: unable to allocate hardware timer\n");
+ goto err;
+ }
+ }
+#ifdef COMCERTO_TIMER_DEBUG
+ if (thw->t != t) {
+ printk (KERN_ERR "Comcerto timer: timer corruption %#lx %#lx %#lx\n",
+ (unsigned long) thw, (unsigned long) thw->t, (unsigned long) t);
+
+ goto err;
+ }
+#endif /* COMCERTO_TIMER_DEBUG */
+
+ /* timeout in us */
+ timer_start(thw, (t->timeout * (COMCERTO_AHBCLK / 100000)) / 10);
+
+ return 0;
+
+ err:
+ return -1;
+}
+
+int comcerto_timer_stop(struct comcerto_timer *t)
+{
+ struct timer_hw *thw;
+
+ thw = (struct timer_hw *) t->thw;
+ if (!thw)
+ goto err;
+
+#ifdef COMCERTO_TIMER_DEBUG
+ if (thw->t != t) {
+ printk (KERN_ERR "Comcerto timer: timer corruption %#lx %#lx %#lx\n",
+ (unsigned long) thw, (unsigned long) thw->t, (unsigned long) t);
+
+ goto err;
+ }
+#endif /* COMCERTO_TIMER_DEBUG */
+
+ timer_stop(thw);
+ timer_free(thw);
+
+ return 0;
+
+ err:
+ return -1;
+}
+
+int comcerto_timer_read(struct comcerto_timer *t)
+{
+ struct timer_hw *thw;
+
+ thw = (struct timer_hw *) t->thw;
+ if (!thw)
+ goto err;
+
+#ifdef COMCERTO_TIMER_DEBUG
+ if (thw->t != t) {
+ printk (KERN_ERR "Comcerto timer: timer corruption %#lx %#lx %#lx\n",
+ (unsigned long) thw, (unsigned long) thw->t, (unsigned long) t);
+
+ goto err;
+ }
+#endif /* COMCERTO_TIMER_DEBUG */
+
+ return ((__comcerto_timer_get(thw->id) * 10) / (COMCERTO_AHBCLK / 100000));
+
+ err:
+ return -1;
+}
+
+EXPORT_SYMBOL(comcerto_timer_start);
+EXPORT_SYMBOL(comcerto_timer_stop);
+EXPORT_SYMBOL(comcerto_timer_read);
+
+int timer_hw_handler(u8 id)
+{
+ struct timer_hw *thw;
+ unsigned long flags;
+
+ thw = &timer_hw[id];
+
+ spin_lock_irqsave(&comcerto_timer_lock, flags);
+
+ if (thw->status & TIMER_STATUS_ENABLED) {
+
+ struct comcerto_timer *t = thw->t;
+
+ if (t->flags & COMCERTO_TIMER_RUN_ONCE) {
+ __timer_stop(thw);
+ __timer_free(thw);
+ }
+
+ spin_unlock_irqrestore(&comcerto_timer_lock, flags);
+
+ t->func(t->data);
+
+ } else {
+ spin_unlock_irqrestore(&comcerto_timer_lock, flags);
+ goto err;
+ }
+
+ return 0;
+
+ err:
+ return -1;
+}
+
+
+struct comcerto_clock_device
+{
+ int timer;
+ struct clock_event_device device;
+};
+
+/* This function must be called with interrupts disabled. We need to start as close from zero
+ * as possible - if we don't clear counter before setting high bound value we'll have innacurate
+ * results in ONESHOT mode.
+ */
+static int comcerto_clock_set_next_event(unsigned long evt, struct clock_event_device *dev)
+{
+ struct comcerto_clock_device *clock = container_of(dev, typeof(*clock), device);
+ unsigned long flags;
+
+ spin_lock_irqsave(&comcerto_timer_lock, flags);
+
+ /* now write correct bound and clear interrupt status.
+ Writing high bound register automatically resets count to low bound value.
+ For very small bound values it's possible that we ack the interrupt _after_ the timer has already expired,
+ this is not very serious because the interrupt will be asserted again in a very short time */
+ __comcerto_timer_set(clock->timer, evt);
+ comcerto_timer_ack(clock->timer);
+
+ /* enable interrupt for ONESHOT mode */
+ if (dev->mode == CLOCK_EVT_MODE_ONESHOT)
+ __comcerto_timer_enable(clock->timer);
+
+ spin_unlock_irqrestore(&comcerto_timer_lock, flags);
+
+ return 0;
+}
+
+static void comcerto_clock_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
+{
+ struct comcerto_clock_device *clock = container_of(dev, typeof(*clock), device);
+ unsigned long flags;
+
+ /* This timer is true PERIODIC in hardware, to emulate ONESHOT we need to keep it masked
+ * until set_next_event() call. Enable interrupt only for PERIODIC mode.
+ */
+ spin_lock_irqsave(&comcerto_timer_lock, flags);
+
+ if (mode != CLOCK_EVT_MODE_PERIODIC)
+ __comcerto_timer_disable(clock->timer);
+ else {
+ __comcerto_timer_set(clock->timer, COMCERTO_KERNEL_TIMER_VALUE);
+ __comcerto_timer_enable(clock->timer);
+ }
+
+ spin_unlock_irqrestore(&comcerto_timer_lock, flags);
+}
+
+static struct comcerto_clock_device clock =
+{
+ .timer = 1,
+ .device =
+ {
+ .name = "timer1",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 200,
+ .shift = 31,
+ .set_mode = comcerto_clock_set_mode,
+ .set_next_event = comcerto_clock_set_next_event,
+ },
+};
+
+static cycle_t comcerto_timer2_read(struct clocksource *cs)
+{
+ return comcerto_timer2_get();
+}
+
+static struct clocksource clocksource =
+{
+ .name = "timer2",
+ .rating = 200,
+ .read = comcerto_timer2_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 28,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+
+/***********************************************************
+ * KERNEL TIMER *
+ * (Functions called by the kernel) *
+ ***********************************************************/
+
+#define irq_to_timer(irq) (IRQ_TIMER0 - irq)
+#define timer_mask(timer) (1 << timer)
+
+#ifdef CONFIG_LOCAL_TIMERS
+/*
+ * Setup the local clock events for a CPU.
+ */
+int __cpuinit local_timer_setup(struct clock_event_device *evt)
+{
+ evt->irq = IRQ_LOCALTIMER;
+ twd_timer_setup(evt);
+ return 0;
+}
+#endif
+
+/*
+ * Routine to catch timer interrupts
+ */
+static irqreturn_t comcerto_timer1_interrupt(int irq, void *dev_id)
+{
+ u32 status;
+ struct comcerto_clock_device *clock = dev_id;
+ struct clock_event_device *dev = &clock->device;
+
+ status = __raw_readl(COMCERTO_TIMER_STATUS) & __raw_readl(COMCERTO_TIMER_IRQ_MASK);
+
+ /* timer1 expired */
+ if (status & timer_mask(clock->timer)) {
+ /* we need to disable interrupt to simulate ONESHOT mode,
+ do it before clearing the interrupt to avoid race */
+ if (dev->mode != CLOCK_EVT_MODE_PERIODIC)
+ comcerto_timer_disable(clock->timer);
+
+ comcerto_timer_ack(clock->timer);
+ dev->event_handler(dev);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+irqreturn_t comcerto_timerN_interrupt(int irq, void *dev_id)
+{
+ u32 status;
+ int timer = irq_to_timer(irq);
+
+ status = __raw_readl(COMCERTO_TIMER_STATUS);
+ if (status & timer_mask(timer)) {
+ comcerto_timer_ack(timer);
+ timer_hw_handler(timer);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static struct irqaction comcerto_timer1_irq = {
+ .name = "timer1",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = comcerto_timer1_interrupt,
+ .dev_id = &clock,
+};
+
+static struct irqaction comcerto_timer3_irq = {
+ .name = "timer3",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = comcerto_timerN_interrupt,
+};
+
+static struct irqaction comcerto_timer4_irq = {
+ .name = "timer4",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = comcerto_timerN_interrupt,
+};
+
+static struct irqaction comcerto_timer5_irq = {
+ .name = "timer5",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = comcerto_timerN_interrupt,
+};
+
+static DEFINE_CLOCK_DATA(cd);
+
+unsigned long long notrace sched_clock(void)
+{
+ u32 cyc = comcerto_timer2_get();
+
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
+}
+
+static void notrace comcerto_update_sched_clock(void)
+{
+ u32 cyc;
+
+ cyc = comcerto_timer2_get();
+
+ update_sched_clock(&cd, cyc, (u32)~0);
+}
+
+void comcerto_hwtimer_init(void)
+{
+ /*
+ * DO NOT MODIFY THE CONFIGURATION OF TIMER0
+ * It is used by the MSP
+ */
+
+ struct clk *clk_axi;
+
+ /* Initializing the clock structure Tree declared/defined in clock.c */
+ clk_init();
+
+ spin_lock_init(&comcerto_timer_lock);
+
+ /* Mask all the timers except timer0 */
+ comcerto_timer_disable(1);
+ comcerto_timer_disable(2);
+ comcerto_timer_disable(3);
+ comcerto_timer_disable(4);
+ comcerto_timer_disable(5);
+
+ /* Get the AXI clock , to be used for AHB clock value*/
+ clk_axi = clk_get(NULL,"axi");
+
+ if (IS_ERR(clk_axi)){
+ pr_err("%s: Unable to obtain axi clock: %ld\n",__func__,PTR_ERR(clk_axi));
+ /* System cannot proceed from here */
+ BUG();
+ }
+
+ /* Enable the AXI clock */
+ if (clk_enable(clk_axi)){
+ pr_err("%s: Unable to enable axi clock\n",__func__);
+ /* System cannot proceed from here */
+ BUG();
+ }
+
+ /* Get the AXI clock rate value , which will assigned to AHB clock value */
+ COMCERTO_AHBCLK = clk_get_rate(clk_axi);
+
+ __comcerto_timer_set(2, 0xffffffff);
+ clocksource.mult = clocksource_hz2mult(COMCERTO_AHBCLK, clocksource.shift);
+ clocksource_register(&clocksource);
+
+ init_sched_clock(&cd, comcerto_update_sched_clock, 32, COMCERTO_AHBCLK);
+
+ clock.device.mult = div_sc(COMCERTO_AHBCLK, NSEC_PER_SEC, clock.device.shift);
+ clock.device.max_delta_ns = clockevent_delta2ns(0x3fffffff, &clock.device);
+ clock.device.min_delta_ns = clockevent_delta2ns(1, &clock.device);
+ clock.device.cpumask = cpumask_of(0);
+ clockevents_register_device(&clock.device);
+
+ /* Clear all the timers except timer0 */
+ __raw_writel(COMCERTO_TIMER_CSP, COMCERTO_TIMER_STATUS);
+
+ /* Register interrupt handler for interrupt on IRQ_TIMERB*/
+ irq_set_irq_type(IRQ_TIMER1, IRQ_TYPE_EDGE_RISING);
+ irq_set_irq_type(IRQ_TIMER3, IRQ_TYPE_EDGE_RISING);
+ irq_set_irq_type(IRQ_TIMER4, IRQ_TYPE_EDGE_RISING);
+ irq_set_irq_type(IRQ_TIMER5, IRQ_TYPE_EDGE_RISING);
+ setup_irq(IRQ_TIMER1, &comcerto_timer1_irq);
+ setup_irq(IRQ_TIMER3, &comcerto_timer3_irq);
+ setup_irq(IRQ_TIMER4, &comcerto_timer4_irq);
+ setup_irq(IRQ_TIMER5, &comcerto_timer5_irq);
+}
+
+static void __init comcerto_timer_init(void)
+{
+#ifdef CONFIG_LOCAL_TIMERS
+ twd_base = (void *)COMCERTO_TWD_VADDR;
+#endif
+
+ comcerto_hwtimer_init();
+}
+
+struct sys_timer comcerto_timer = {
+ .init = comcerto_timer_init,
+};
+
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 67f75a0..ca9764e 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -753,6 +753,20 @@
Say Y here to use the predictable round-robin cache replacement
policy. Unless you specifically require this or are unsure, say N.
+config CPU_SPECULATIVE_ACCESS_DISABLED
+ bool "Disable speculative accesses"
+ depends on CPU_V7 && ARCH_COMCERTO
+ help
+ Disable speculative accesses for the processor. This may reduce overall system memory performance but
+ can allow other, potentially unsafe, optimizations (see below). If unsure, say N.
+
+config CPU_DMA_PARTIAL_INVALIDATES
+ bool "Do partial invalidates in the DMA management functions"
+ depends on CPU_V7 && ARCH_COMCERTO && CPU_SPECULATIVE_ACCESS_DISABLED
+ help
+ Say Y here to reduce the overhead of cache maintenance for DMA mapped memory zones.
+ WARNING: UNSAFE, this may lead to random memory corruption. If unsure, say N.
+
config CPU_BPREDICT_DISABLE
bool "Disable branch prediction"
depends on CPU_ARM1020 || CPU_V6 || CPU_V6K || CPU_MOHAWK || CPU_XSC3 || CPU_V7 || CPU_FA526
@@ -822,7 +836,7 @@
REALVIEW_EB_A9MP || ARCH_IMX_V6_V7 || MACH_REALVIEW_PBX || \
ARCH_NOMADIK || ARCH_OMAP4 || ARCH_EXYNOS4 || ARCH_TEGRA || \
ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_SHMOBILE || \
- ARCH_PRIMA2 || ARCH_ZYNQ || ARCH_CNS3XXX || ARCH_HIGHBANK
+ ARCH_PRIMA2 || ARCH_ZYNQ || ARCH_CNS3XXX || ARCH_HIGHBANK || ARCH_COMCERTO
default y
select OUTER_CACHE
select OUTER_CACHE_SYNC
@@ -837,6 +851,51 @@
This option enables optimisations for the PL310 cache
controller.
+config L2X0_INSTRUCTION_ONLY
+ bool "Use L2 cache for instructions only"
+ depends on CACHE_PL310
+ default n
+
+config PL310_EXCLUSIVE_CACHE
+ bool "Exclusive cache"
+ depends on CACHE_PL310
+ default n
+
+config PL310_EARLY_WRITE_RESPONSE
+ bool "Early write response"
+ depends on CACHE_PL310
+ default n
+
+config PL310_FULL_LINE_OF_ZERO
+ bool "Full line of zero"
+ depends on CACHE_PL310
+ default n
+
+config PL310_STORE_BUFFER_DEVICE_LIMITATION
+ bool "Store buffer device limitation"
+ depends on CACHE_PL310
+ default n
+
+config PL310_INSTRUCTION_PREFETCH
+ bool "Instruction prefetch"
+ depends on CACHE_PL310
+ default n
+
+config PL310_DATA_PREFETCH
+ bool "Data prefetch"
+ depends on CACHE_PL310
+ default n
+
+config PL310_DOUBLE_LINE_FILL
+ bool "Double line fill"
+ depends on CACHE_PL310
+ default n
+
+config PL310_INCR_DOUBLE_LINE_FILL
+ bool "Incremental double line fill"
+ depends on PL310_DOUBLE_LINE_FILL
+ default n
+
config CACHE_TAUROS2
bool "Enable the Tauros2 L2 cache controller"
depends on (ARCH_DOVE || ARCH_MMP || CPU_PJ4)
@@ -867,7 +926,7 @@
config ARM_DMA_MEM_BUFFERABLE
bool "Use non-cacheable memory for DMA" if (CPU_V6 || CPU_V6K) && !CPU_V7
depends on !(MACH_REALVIEW_PB1176 || REALVIEW_EB_ARM11MP || \
- MACH_REALVIEW_PB11MP)
+ MACH_REALVIEW_PB11MP || COMCERTO_DDR_ECC_SUPPORT)
default y if CPU_V6 || CPU_V6K || CPU_V7
help
Historically, the kernel has used strongly ordered mappings to
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index db7bcc0..3e504ec 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -299,8 +299,10 @@
lockregs = 1;
for (i = 0; i < lockregs; i++) {
+#ifndef CONFIG_L2X0_INSTRUCTION_ONLY
writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE +
i * L2X0_LOCKDOWN_STRIDE);
+#endif
writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE +
i * L2X0_LOCKDOWN_STRIDE);
}
@@ -311,6 +313,7 @@
__u32 aux;
__u32 cache_id;
__u32 way_size = 0;
+ __u32 prefetch = 0;
int ways;
const char *type;
@@ -330,11 +333,23 @@
else
ways = 8;
type = "L310";
+
#ifdef CONFIG_PL310_ERRATA_753970
/* Unmapped register. */
sync_reg_offset = L2X0_DUMMY_REG;
#endif
outer_cache.set_debug = pl310_set_debug;
+
+ prefetch = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL);
+
+#ifdef CONFIG_PL310_DOUBLE_LINE_FILL
+ prefetch |= (1 << 30) | (1 << 24);
+#ifdef CONFIG_PL310_INCR_DOUBLE_LINE_FILL
+ prefetch |= (1 << 23);
+#endif
+#endif
+ writel_relaxed(prefetch, l2x0_base + L2X0_PREFETCH_CTRL);
+
break;
case L2X0_CACHE_ID_PART_L210:
ways = (aux >> 13) & 0xf;
@@ -385,8 +400,8 @@
outer_cache.disable = l2x0_disable;
printk(KERN_INFO "%s cache controller enabled\n", type);
- printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n",
- ways, cache_id, aux, l2x0_size);
+ printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, PREFETCH_CTRL 0x%08x, Cache size: %d B\n",
+ ways, cache_id, aux, prefetch, l2x0_size);
}
#ifdef CONFIG_OF
diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S
index a655d3d..0c9258e 100644
--- a/arch/arm/mm/cache-v7.S
+++ b/arch/arm/mm/cache-v7.S
@@ -211,9 +211,16 @@
* isn't mapped, just try the next page.
*/
9001:
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
mov r12, r12, lsr #12
mov r12, r12, lsl #12
add r12, r12, #4096
+#else
+ mov r12, r12, lsr #16
+ mov r12, r12, lsl #16
+ add r12, r12, #65536
+
+#endif
b 3b
UNWIND(.fnend )
ENDPROC(v7_coherent_kern_range)
diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c
index 3d9a155..8205008 100644
--- a/arch/arm/mm/copypage-v6.c
+++ b/arch/arm/mm/copypage-v6.c
@@ -20,9 +20,16 @@
#include "mm.h"
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
#if SHMLBA > 16384
#error FIX ME
#endif
+#else
+#if SHMLBA > PAGE_SIZE
+#error FIX ME
+#endif
+#endif
+
#define from_address (0xffff8000)
#define to_address (0xffffc000)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 1aa664a..c89301b 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -28,6 +28,78 @@
#include <asm/mach/arch.h>
#include "mm.h"
+#if defined(CONFIG_COMCERTO_UNCACHED_DMA)
+#include <linux/hugetlb.h>
+#include <asm/pgalloc.h>
+#include <asm/mach/map.h>
+#endif
+
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+extern unsigned long arm_dma_zone_size;
+#endif
+
+#if defined(CONFIG_COMCERTO_UNCACHED_DMA)
+static pgd_t *shadow_pg_dir;
+static u16 *shadow_pmd_count;
+
+static int __init init_shadow_page_table(void)
+{
+ pmd_t *pmd, *shadow_pmd;
+ pte_t *shadow_pte, *ptep;
+ unsigned long start, addr, end, pfn;
+ const struct mem_type *mt;
+ int count;
+
+ shadow_pg_dir = (pgd_t *)__get_free_pages(GFP_KERNEL | GFP_ATOMIC, get_order(16384));
+ if (!shadow_pg_dir)
+ return -ENOMEM;
+ shadow_pmd_count = (u16 *)__get_free_pages(GFP_KERNEL | GFP_ATOMIC, get_order(sizeof(u16) * PTRS_PER_PGD));
+ if (!shadow_pmd_count)
+ goto err1;
+
+ memset(shadow_pg_dir, 0, 16384);
+ memset(shadow_pmd_count, 0, sizeof(u16) * PTRS_PER_PGD);
+
+ mt = get_mem_type(MT_MEMORY);
+ start = 0;
+ count = 0;
+ do {
+ pmd = pmd_off_k((unsigned long) start);
+ if (!pmd_none(*pmd)) {
+ if (pmd_bad(*pmd) && ((pmd_val(*pmd) & ~SECTION_MASK) == mt->prot_sect)) { // Only do it for MT_MEMORY areas
+ shadow_pmd = (pmd_t *)shadow_pg_dir + (pmd - pmd_off_k(0));
+ addr = (unsigned long)start & PMD_MASK;
+ end = addr + PMD_SIZE;
+
+ shadow_pte = (pte_t *)__get_free_page(PGALLOC_GFP | GFP_ATOMIC);
+ if (!shadow_pte)
+ goto err2;
+ pfn = __phys_to_pfn(pmd_val(*pmd) & PMD_MASK);
+ ptep = shadow_pte;
+ do {
+ set_pte_ext(ptep, pfn_pte(pfn, __pgprot(mt->prot_pte)), 0);
+ pfn++;
+ } while (ptep++, addr += PAGE_SIZE, addr != end);
+ __pmd_populate(shadow_pmd, __pa(shadow_pte), mt->prot_l1);
+ } else {
+ // Mark the shadow in use, so we never replace the already existing 2nd-level
+ shadow_pmd_count[pgd_index(start)]++;
+ }
+ }
+ } while (count++, start += PMD_SIZE, count < PTRS_PER_PGD);
+
+ return 0;
+
+err2:
+ __free_pages((struct page *)shadow_pmd_count, get_order(sizeof(u16) * PTRS_PER_PGD));
+ shadow_pmd_count = NULL;
+ //TODO: free already allocated shadow_pte tables
+err1:
+ __free_pages((struct page *)shadow_pg_dir, get_order(16384));
+ return -ENOMEM;
+}
+core_initcall(init_shadow_page_table);
+#endif
static u64 get_coherent_dma_mask(struct device *dev)
{
@@ -168,7 +240,7 @@
pte_t *pte;
int i = 0;
unsigned long base = consistent_base;
- unsigned long num_ptes = (CONSISTENT_END - base) >> PMD_SHIFT;
+ unsigned long num_ptes = (CONSISTENT_END - base + PMD_SIZE -1) >> PMD_SHIFT;
consistent_pte = kmalloc(num_ptes * sizeof(pte_t), GFP_KERNEL);
if (!consistent_pte) {
@@ -195,8 +267,9 @@
ret = -ENOMEM;
break;
}
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
WARN_ON(!pmd_none(*pmd));
-
+#endif
pte = pte_alloc_kernel(pmd, base);
if (!pte) {
printk(KERN_ERR "%s: no pte tables\n", __func__);
@@ -205,8 +278,8 @@
}
consistent_pte[i++] = pte;
- base += PMD_SIZE;
- } while (base < CONSISTENT_END);
+ base = (base + PMD_SIZE) & PMD_MASK;
+ } while ((base-1) < (CONSISTENT_END - 1));
return ret;
}
@@ -455,6 +528,217 @@
}
EXPORT_SYMBOL(dma_free_coherent);
+#if defined(CONFIG_COMCERTO_UNCACHED_DMA)
+static inline void shadow_pmd_inc(const void *kaddr, int incr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&init_mm.page_table_lock, flags);
+ shadow_pmd_count[pgd_index((unsigned long) kaddr)] += incr;
+ spin_unlock_irqrestore(&init_mm.page_table_lock, flags);
+}
+
+static inline void copy_pmd_fast(pmd_t *pmdpd, pmd_t *pmdps)
+{
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
+ pmdpd[0] = pmdps[0];
+ pmdpd[1] = pmdps[1];
+#else
+ int i;
+ for(i = 0; i < LINKED_PMDS; i++)
+ pmdpd[i] = pmdps[i];
+#endif
+}
+#endif
+
+static inline void __dmac_map_area(const void *kaddr, size_t size,
+ int dir)
+{
+#if defined(CONFIG_COMCERTO_UNCACHED_DMA)
+ pmd_t *pmd, *shadow_pmd;
+ pte_t *pte;
+ const void *kaddr_page;
+ const struct mem_type *mt;
+ unsigned int nr_pages, nr_pages_pmd;
+
+ if (!shadow_pmd_count)
+ goto op;
+
+ // For now, be safe and only uncache full pages so we don't have to handle
+ // the case of pages being DMA-mapped multiple times
+ if ((dir == DMA_FROM_DEVICE) && ((((unsigned long) kaddr|size) & ~PAGE_MASK) == 0)) {
+ mt = get_mem_type(MT_MEMORY_NONCACHED);
+ kaddr_page = kaddr;
+ pmd = pmd_off_k((unsigned long) kaddr_page);
+ shadow_pmd = (pmd_t *)shadow_pg_dir + (pmd - pmd_off_k(0));
+ nr_pages = __phys_to_pfn(size);
+
+ if (nr_pages == 1) { // Optimize for the common case
+ shadow_pmd_inc(kaddr_page, 1);
+ if (pmd_bad(*pmd)) { //No 2nd-level page table, retrieve it from the shadows
+ // Small race condition here, but at worst we'll end up copying the shadow_pmd to the actual pmd twice.
+ // For now, map the whole PMD. TODO: try and map only 1 section (1MB).
+ copy_pmd_fast(pmd, shadow_pmd);
+ }
+
+ pte = pte_offset_kernel(pmd, (int) kaddr_page);
+ uncache_pte_ext(pte);
+ flush_tlb_kernel_page((unsigned long) kaddr_page);
+ goto op;
+ }
+
+ nr_pages_pmd = __phys_to_pfn(PMD_SIZE - ((unsigned long) kaddr_page & ~PMD_MASK));
+
+ while (nr_pages) {
+
+ nr_pages_pmd = min(nr_pages, nr_pages_pmd);
+ nr_pages -= nr_pages_pmd;
+
+ shadow_pmd_inc(kaddr_page, nr_pages_pmd);
+
+ if (pmd_bad(*pmd)) { //No 2nd-level page table, retrieve it from the shadows
+ // Small race condition here, but at worst we'll end up copying the shadow_pmd to the actual pmd twice.
+ // For now, map the whole PMD. TODO: try and map only 1 section (1MB).
+ copy_pmd_fast(pmd, shadow_pmd);
+ }
+
+ pte = pte_offset_kernel(pmd, (int) kaddr_page);
+ while (nr_pages_pmd) {
+ uncache_pte_ext(pte);
+ flush_tlb_kernel_page((unsigned long) kaddr_page);
+ pte++;
+ kaddr_page += PAGE_SIZE;
+ nr_pages_pmd--;
+ }
+
+ nr_pages_pmd = PTRS_PER_PTE;
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
+ pmd += 2;
+ shadow_pmd += 2;
+#else
+ pmd += LINKED_PMDS;
+ shadow_pmd += LINKED_PMDS;
+#endif
+ }
+ }
+ op:
+#endif
+ dmac_map_area(kaddr, size, dir);
+}
+
+static inline void __dmac_unmap_area(const void *kaddr, size_t size,
+ int dir)
+{
+#if defined(CONFIG_COMCERTO_UNCACHED_DMA)
+ pmd_t *pmd;
+ pte_t *pte;
+ const struct mem_type *mt;
+ unsigned long pa;
+ const void *kaddr_page;
+ unsigned long flags;
+ unsigned int nr_pages, nr_pages_pmd, page_count;
+
+ if (!shadow_pmd_count)
+ goto op;
+
+ if ((dir == DMA_FROM_DEVICE) && ((((unsigned long) kaddr|size) & ~PAGE_MASK) == 0)) {
+ mt = get_mem_type(MT_MEMORY);
+ kaddr_page = kaddr;
+
+ pmd = pmd_off_k((unsigned long) kaddr_page);
+ pa = __virt_to_phys((unsigned long)kaddr_page & PMD_MASK);
+
+ nr_pages = __phys_to_pfn(size);
+
+ if (nr_pages == 1) { // Optimize for the common case
+ if (pmd_bad(*pmd)) // No 2nd-level page table, so page was never made non-cacheable.
+ goto op;
+ pte = pte_offset_kernel(pmd, (int) kaddr_page);
+ set_pte_ext(pte, *pte, 0);
+
+ spin_lock_irqsave(&init_mm.page_table_lock, flags);
+ shadow_pmd_count[pgd_index((unsigned long) kaddr_page)]--;
+ if (shadow_pmd_count[pgd_index((unsigned long) kaddr_page)] == 0) {
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
+ *pmd = __pmd(pa | mt->prot_sect);
+ pmd++;
+ pa += SECTION_SIZE;
+ *pmd = __pmd(pa | mt->prot_sect);
+#else
+ pmd_t *orig_pmd = pmd;
+ while (pmd < (orig_pmd + LINKED_PMDS)) {
+ *pmd = __pmd(pa | mt->prot_sect);
+ pa += SECTION_SIZE;
+ pmd++;
+ }
+#endif
+ }
+ spin_unlock_irqrestore(&init_mm.page_table_lock, flags);
+
+ flush_tlb_kernel_page((unsigned long) kaddr_page);
+ return;
+ }
+
+ nr_pages_pmd = __phys_to_pfn(PMD_SIZE - ((unsigned long) kaddr_page & ~PMD_MASK));
+
+ while (nr_pages) {
+ if (pmd_bad(*pmd)) // No 2nd-level page table, so page was never made non-cacheable.
+ goto op;
+ nr_pages_pmd = min(nr_pages, nr_pages_pmd);
+ nr_pages -= nr_pages_pmd;
+
+ pte = pte_offset_kernel(pmd, (int) kaddr_page);
+ page_count = nr_pages_pmd;
+ while (page_count) {
+ set_pte_ext(pte, *pte, 0);
+ pte++;
+ page_count--;
+ }
+
+ spin_lock_irqsave(&init_mm.page_table_lock, flags);
+ shadow_pmd_count[pgd_index((unsigned long) kaddr_page)] -= nr_pages_pmd;
+ if (shadow_pmd_count[pgd_index((unsigned long) kaddr_page)] == 0) {
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
+ *pmd = __pmd(pa | mt->prot_sect);
+ pmd++;
+ pa += SECTION_SIZE;
+ *pmd = __pmd(pa | mt->prot_sect);
+ pmd++;
+ pa += SECTION_SIZE;
+#else
+ pmd_t *orig_pmd = pmd;
+ while (pmd < (orig_pmd + LINKED_PMDS)) {
+ *pmd = __pmd(pa | mt->prot_sect);
+ pmd++;
+ pa += SECTION_SIZE;
+ }
+#endif
+ }
+ spin_unlock_irqrestore(&init_mm.page_table_lock, flags);
+
+ while (nr_pages_pmd) {
+ flush_tlb_kernel_page((unsigned long) kaddr_page);
+ kaddr_page += PAGE_SIZE;
+ nr_pages_pmd--;
+ }
+
+ nr_pages_pmd = PTRS_PER_PTE;
+ }
+
+ return;
+ }
+ op:
+#endif
+#if !defined(CONFIG_CPU_DMA_PARTIAL_INVALIDATES)
+ dmac_unmap_area(kaddr, size, dir);
+#else
+ size_t size_inv = min_t(size_t, 32, size);
+
+ dmac_unmap_area(kaddr, size_inv, dir);
+ dmac_unmap_area(kaddr + size - size_inv, size_inv, dir);
+#endif
+}
+
/*
* Make an area consistent for devices.
* Note: Drivers should NOT use this function directly, as it will break
@@ -464,35 +748,58 @@
void ___dma_single_cpu_to_dev(const void *kaddr, size_t size,
enum dma_data_direction dir)
{
- unsigned long paddr;
+ unsigned long paddr = __pa(kaddr);
BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1));
- dmac_map_area(kaddr, size, dir);
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if ((paddr + size) <= arm_dma_zone_size) {
+ if (dir != DMA_FROM_DEVICE)
+ wmb();
- paddr = __pa(kaddr);
+ return;
+ }
+#endif
+ __dmac_map_area(kaddr, size, dir);
+
+#if !defined(CONFIG_L2X0_INSTRUCTION_ONLY)
if (dir == DMA_FROM_DEVICE) {
outer_inv_range(paddr, paddr + size);
} else {
outer_clean_range(paddr, paddr + size);
}
/* FIXME: non-speculating: flush on bidirectional mappings? */
+#endif
}
EXPORT_SYMBOL(___dma_single_cpu_to_dev);
void ___dma_single_dev_to_cpu(const void *kaddr, size_t size,
enum dma_data_direction dir)
{
+ unsigned long paddr = __pa(kaddr);
+
BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1));
- /* FIXME: non-speculating: not required */
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if ((paddr + size) <= arm_dma_zone_size)
+ return;
+#endif
+
+#if !defined(CONFIG_L2X0_INSTRUCTION_ONLY)
/* don't bother invalidating if DMA to device */
if (dir != DMA_TO_DEVICE) {
- unsigned long paddr = __pa(kaddr);
+#if !defined(CONFIG_CPU_DMA_PARTIAL_INVALIDATES)
outer_inv_range(paddr, paddr + size);
- }
+#else
+ size_t size_inv = min_t(size_t, 32, size);
- dmac_unmap_area(kaddr, size, dir);
+ outer_inv_range(paddr, paddr + size_inv);
+ outer_inv_range(paddr + size - size_inv, paddr + size);
+#endif
+ }
+#endif
+
+ __dmac_unmap_area(kaddr, size, dir);
}
EXPORT_SYMBOL(___dma_single_dev_to_cpu);
@@ -534,6 +841,7 @@
vaddr = page_address(page) + offset;
op(vaddr, len, dir);
}
+
offset = 0;
page++;
left -= len;
@@ -543,16 +851,26 @@
void ___dma_page_cpu_to_dev(struct page *page, unsigned long off,
size_t size, enum dma_data_direction dir)
{
- unsigned long paddr;
+ unsigned long paddr = page_to_phys(page) + off;
- dma_cache_maint_page(page, off, size, dir, dmac_map_area);
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if ((paddr + size) <= arm_dma_zone_size) {
+ if (dir != DMA_FROM_DEVICE)
+ wmb();
- paddr = page_to_phys(page) + off;
+ return;
+ }
+#endif
+
+ dma_cache_maint_page(page, off, size, dir, __dmac_map_area);
+
+#if !defined(CONFIG_L2X0_INSTRUCTION_ONLY)
if (dir == DMA_FROM_DEVICE) {
outer_inv_range(paddr, paddr + size);
} else {
outer_clean_range(paddr, paddr + size);
}
+#endif
/* FIXME: non-speculating: flush on bidirectional mappings? */
}
EXPORT_SYMBOL(___dma_page_cpu_to_dev);
@@ -562,12 +880,25 @@
{
unsigned long paddr = page_to_phys(page) + off;
- /* FIXME: non-speculating: not required */
- /* don't bother invalidating if DMA to device */
- if (dir != DMA_TO_DEVICE)
- outer_inv_range(paddr, paddr + size);
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if ((paddr + size) <= arm_dma_zone_size)
+ return;
+#endif
- dma_cache_maint_page(page, off, size, dir, dmac_unmap_area);
+#if !defined(CONFIG_L2X0_INSTRUCTION_ONLY)
+ /* don't bother invalidating if DMA to device */
+ if (dir != DMA_TO_DEVICE) {
+#if !defined(CONFIG_CPU_DMA_PARTIAL_INVALIDATES)
+ outer_inv_range(paddr, paddr + size);
+#else
+ size_t size_inv = min_t(size_t, 32, size);
+
+ outer_inv_range(paddr, paddr + size_inv);
+ outer_inv_range(paddr + size - size_inv, paddr + size);
+#endif
+ }
+#endif
+ dma_cache_maint_page(page, off, size, dir, __dmac_unmap_area);
/*
* Mark the D-cache clean for this page to avoid extra flushing.
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index 1a8d4aa..94e7396 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -229,6 +229,26 @@
flush_dcache_mmap_unlock(mapping);
}
+#if defined(CONFIG_L2X0_INSTRUCTION_ONLY)
+
+void __sync_outer_cache(pte_t *ptep, pte_t pteval)
+{
+ if (pte_present(*ptep) && pte_exec(*ptep) && (!pte_present(pteval) || !pte_exec(pteval))) {
+ unsigned long phys = __pfn_to_phys(pte_pfn(*ptep));
+
+// printk(KERN_INFO "outer flush range: %x %x %lx-%lx\n", pte_val(*ptep), pteval, phys, phys + PAGE_SIZE);
+ outer_flush_range(phys, phys + PAGE_SIZE);
+ }
+}
+
+static void sync_outer_cache(struct page *page)
+{
+ unsigned long phys = page_to_phys(page);
+
+ outer_flush_range(phys, phys + PAGE_SIZE);
+}
+#endif
+
#if __LINUX_ARM_ARCH__ >= 6
void __sync_icache_dcache(pte_t pteval)
{
@@ -300,6 +320,10 @@
__flush_dcache_aliases(mapping, page);
else if (mapping)
__flush_icache_all();
+
+#if defined(CONFIG_L2X0_INSTRUCTION_ONLY)
+ sync_outer_cache(page);
+#endif
set_bit(PG_dcache_clean, &page->flags);
}
}
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index fbdd12e..f362cf8 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -288,7 +288,11 @@
if (arm_dma_zone_size) {
arm_adjust_dma_zone(zone_size, zhole_size,
arm_dma_zone_size >> PAGE_SHIFT);
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ arm_dma_limit = 0xffffffff;
+#else
arm_dma_limit = PHYS_OFFSET + arm_dma_zone_size - 1;
+#endif
} else
arm_dma_limit = 0xffffffff;
#endif
@@ -423,9 +427,15 @@
for (; pfn < end; pfn++) {
struct page *page = pfn_to_page(pfn);
+#if defined(CONFIG_L2X0_INSTRUCTION_ONLY)
+ unsigned long phys = page_to_phys(page);
+#endif
ClearPageReserved(page);
init_page_count(page);
__free_page(page);
+#if defined(CONFIG_L2X0_INSTRUCTION_ONLY)
+ outer_flush_range(phys, phys + PAGE_SIZE);
+#endif
pages++;
}
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index dc8c550..f452bdc 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -281,6 +281,18 @@
PMD_SECT_UNCACHED | PMD_SECT_XN,
.domain = DOMAIN_KERNEL,
},
+ [MT_MSP] = {
+ .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_CACHED | L_PTE_XN,
+ .prot_l1 = PMD_TYPE_TABLE,
+ .prot_sect = PROT_SECT_DEVICE | PMD_SECT_WB,
+ .domain = DOMAIN_IO,
+ },
+ [MT_MSP_NCNB] = {
+ .prot_pte = PROT_PTE_DEVICE | L_PTE_XN,
+ .prot_l1 = PMD_TYPE_TABLE,
+ .prot_sect = PROT_SECT_DEVICE | PMD_SECT_S,
+ .domain = DOMAIN_IO,
+ },
};
const struct mem_type *get_mem_type(unsigned int type)
@@ -572,8 +584,7 @@
if (((addr | end | phys) & ~SECTION_MASK) == 0) {
pmd_t *p = pmd;
- if (addr & SECTION_SIZE)
- pmd++;
+ pmd += (addr & (~PGDIR_MASK)) >> SECTION_SHIFT;
do {
*pmd = __pmd(phys | type->prot_sect);
@@ -1013,7 +1024,10 @@
static void __init map_lowmem(void)
{
struct memblock_region *reg;
-
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ extern unsigned long arm_dma_zone_size;
+ phys_addr_t length_ncnb = arm_dma_zone_size, length_ncnb_now = 0;
+#endif
/* Map all the lowmem memory banks. */
for_each_memblock(memory, reg) {
phys_addr_t start = reg->base;
@@ -1024,7 +1038,31 @@
end = lowmem_limit;
if (start >= end)
break;
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if (length_ncnb > 0)
+ {
+ length_ncnb_now = min(length_ncnb, end - start);
+ map.pfn = __phys_to_pfn(start);
+ map.virtual = __phys_to_virt(start);
+ map.length = length_ncnb_now;
+ map.type = MT_MEMORY_NONCACHED;
+ printk("Comcerto: zone_dma mapping size=%lx type=%lx\n", (unsigned long) map.length, (unsigned long) map.type);
+
+ if (!arm_dma_zone.start)
+ arm_dma_zone.start = __phys_to_virt(start);
+
+ create_mapping(&map);
+ start += length_ncnb_now;
+ length_ncnb -= length_ncnb_now;
+
+ arm_dma_zone.end = __phys_to_virt(start);
+
+ if (start == end)
+ continue;
+
+ }
+#endif
map.pfn = __phys_to_pfn(start);
map.virtual = __phys_to_virt(start);
map.length = end - start;
diff --git a/arch/arm/mm/pgd.c b/arch/arm/mm/pgd.c
index b2027c1..27b9016 100644
--- a/arch/arm/mm/pgd.c
+++ b/arch/arm/mm/pgd.c
@@ -27,7 +27,7 @@
pmd_t *new_pmd, *init_pmd;
pte_t *new_pte, *init_pte;
- new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 2);
+ new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, get_order(16384));
if (!new_pgd)
goto no_pgd;
@@ -74,7 +74,7 @@
no_pmd:
pud_free(mm, new_pud);
no_pud:
- free_pages((unsigned long)new_pgd, 2);
+ free_pages((unsigned long)new_pgd, get_order(16384));
no_pgd:
return NULL;
}
@@ -111,5 +111,5 @@
pgd_clear(pgd);
pud_free(mm, pud);
no_pgd:
- free_pages((unsigned long) pgd_base, 2);
+ free_pages((unsigned long) pgd_base, get_order(16384));
}
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index 785365e..5f3bd9b 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -46,7 +46,7 @@
mrc p15, 0, r0, c1, c0, 0 @ ctrl register
bic r0, r0, #0x1000 @ ...i............
bic r0, r0, #0x0006 @ .............ca.
- mcr p15, 0, r0, c1, c0, 0 @ disable caches
+# mcr p15, 0, r0, c1, c0, 0 @ disable caches
mov pc, lr
ENDPROC(cpu_v7_proc_fin)
@@ -149,7 +149,11 @@
bic r3, r1, #0x000003f0
bic r3, r3, #PTE_TYPE_MASK
orr r3, r3, r2
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
orr r3, r3, #PTE_EXT_AP0 | 2
+#else
+ orr r3, r3, #PTE_EXT_AP0 | 1
+#endif
tst r1, #1 << 4
orrne r3, r3, #PTE_EXT_TEX(1)
@@ -173,14 +177,65 @@
tstne r1, #L_PTE_PRESENT
moveq r3, #0
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
ARM( str r3, [r0, #2048]! )
THUMB( add r0, r0, #2048 )
THUMB( str r3, [r0] )
mcr p15, 0, r0, c7, c10, 1 @ flush_pte
+ #else
+ ldr r1, =32768 @ PTE_HWTABLE_OFF
+ add r1, r1, r0
+ add r2, r1, #64 @ 16*4 bytes
+ 1:
+ sub r2, r2, #4
+ str r3, [r2]!
+ cmp r1, r2
+ blo 1b
+ mcr p15, 0, r1, c7, c10, 1 @ flush_pte
+ add r1, r1, #32 @ 1 cache line, since HW PTE takes 64 bytes
+ mcr p15, 0, r1, c7, c10, 1 @ flush_pte
+ #endif
+
+
#endif
mov pc, lr
ENDPROC(cpu_v7_set_pte_ext)
+/* Make the memory uncached, but keep Linux settings as is so memory may be
+ * reset to original attributes later on.
+ */
+ENTRY(cpu_v7_uncache_pte_ext)
+#ifdef CONFIG_MMU
+#if !defined(CONFIG_COMCERTO_64K_PAGES)
+ ARM( ldr r3, [r0, #2048]! )
+ THUMB( add r0, r0, #2048 )
+ THUMB( ldr r3, [r0] )
+ bic r3, r3, #0x48 @ clear TEX[0],C bits (see below)
+ orr r3, r3, #0x4 @ set B bit (see below)
+ ARM( str r3, [r0, #2048]! )
+ THUMB( str r3, [r0] )
+ #else
+ ldr r1, =32768 @ PTE_HWTABLE_OFF
+ add r1, r1, r0
+ ldr r3, [r1]
+ add r2, r1, #64 @ 16*4 bytes
+ARM( bic r3, r3, #0x1008 ) @ clear TEX[0],C bits (see below)
+THUMB( bic r3, r3, #0x8 )
+THUMB( bic r3, r3, #0x1000 )
+ orr r3, r3, #0x4 @ set B bit (see below)
+ 1:
+ sub r2, r2, #4
+ str r3, [r2]!
+ cmp r1, r2
+ blo 1b
+ #endif
+
+
+#endif
+ mov pc, lr
+ENDPROC(cpu_v7_uncache_pte_ext)
+
+
string cpu_v7_name, "ARMv7 Processor"
.align
@@ -247,6 +302,9 @@
ALT_UP(orr r1, r1, #TTB_FLAGS_UP)
mcr p15, 0, r1, c2, c0, 0 @ TTB 0
mcr p15, 0, r7, c2, c0, 1 @ TTB 1
+#if defined(CONFIG_COMCERTO_HW_KERNEL_PAGETABLE)
+ mov ip, #1 @ set TTBRCR.N to 1, so that addresses above 2GB will use TTB1
+#endif
mcr p15, 0, ip, c2, c0, 2 @ TTB control register
mrc p15, 0, r4, c1, c0, 1 @ Read Auxiliary control register
teq r4, r9 @ Is it already set?
@@ -277,9 +335,16 @@
* - cache type register is implemented
*/
__v7_ca5mp_setup:
-__v7_ca9mp_setup:
mov r10, #(1 << 0) @ TLB ops broadcasting
b 1f
+
+__v7_ca9mp_setup:
+ mov r10, #(1 << 0) @ TLB ops broadcasting
+#if !defined(CONFIG_CPU_SPECULATIVE_ACCESS_DISABLED)
+ orr r10, #(3 << 1) @ L1 data prefetch, L2 prefetch hints enable
+#endif
+ b 1f
+
__v7_ca15mp_setup:
mov r10, #0
1:
@@ -371,6 +436,9 @@
dsb
#ifdef CONFIG_MMU
mcr p15, 0, r10, c8, c7, 0 @ invalidate I + D TLBs
+#if defined(CONFIG_COMCERTO_HW_KERNEL_PAGETABLE)
+ mov r10, #1 @ set TTBRCR.N to 1, so that addresses above 2GB will use TTB1
+#endif
mcr p15, 0, r10, c2, c0, 2 @ TTB control register
ALT_SMP(orr r4, r4, #TTB_FLAGS_SMP)
ALT_UP(orr r4, r4, #TTB_FLAGS_UP)
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index ccbe16f..3a670a5 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -212,6 +212,7 @@
onearm MACH_ONEARM ONEARM 1075
smdk2443 MACH_SMDK2443 SMDK2443 1084
fsg MACH_FSG FSG 1091
+comcerto MACH_COMCERTO COMCERTO 1094
at91sam9260ek MACH_AT91SAM9260EK AT91SAM9260EK 1099
glantank MACH_GLANTANK GLANTANK 1100
n2100 MACH_N2100 N2100 1101
diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S
index 4fa9903..cc926c9 100644
--- a/arch/arm/vfp/entry.S
+++ b/arch/arm/vfp/entry.S
@@ -7,18 +7,20 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * Basic entry code, called from the kernel's undefined instruction trap.
- * r0 = faulted instruction
- * r5 = faulted PC+4
- * r9 = successful return
- * r10 = thread_info structure
- * lr = failure return
*/
#include <asm/thread_info.h>
#include <asm/vfpmacros.h>
#include "../kernel/entry-header.S"
+@ VFP entry point.
+@
+@ r0 = instruction opcode (32-bit ARM or two 16-bit Thumb)
+@ r2 = PC value to resume execution after successful emulation
+@ r9 = normal "successful" return address
+@ r10 = this threads thread_info structure
+@ lr = unrecognised instruction return address
+@ IRQs disabled.
+@
ENTRY(do_vfp)
#ifdef CONFIG_PREEMPT
ldr r4, [r10, #TI_PREEMPT] @ get preempt count
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S
index 2d30c7f..3a0efaa 100644
--- a/arch/arm/vfp/vfphw.S
+++ b/arch/arm/vfp/vfphw.S
@@ -61,13 +61,13 @@
@ VFP hardware support entry point.
@
-@ r0 = faulted instruction
-@ r2 = faulted PC+4
-@ r9 = successful return
+@ r0 = instruction opcode (32-bit ARM or two 16-bit Thumb)
+@ r2 = PC value to resume execution after successful emulation
+@ r9 = normal "successful" return address
@ r10 = vfp_state union
@ r11 = CPU number
-@ lr = failure return
-
+@ lr = unrecognised instruction return address
+@ IRQs enabled.
ENTRY(vfp_support_entry)
DBGSTR3 "instr %08x pc %08x state %p", r0, r2, r10
@@ -161,9 +161,12 @@
@ exception before retrying branch
@ out before setting an FPEXC that
@ stops us reading stuff
- VFPFMXR FPEXC, r1 @ restore FPEXC last
- sub r2, r2, #4
- str r2, [sp, #S_PC] @ retry the instruction
+ VFPFMXR FPEXC, r1 @ Restore FPEXC last
+ sub r2, r2, #4 @ Retry current instruction - if Thumb
+ str r2, [sp, #S_PC] @ mode it's two 16-bit instructions,
+ @ else it's one 32-bit instruction, so
+ @ always subtract 4 from the following
+ @ instruction address.
#ifdef CONFIG_PREEMPT
get_thread_info r10
ldr r4, [r10, #TI_PREEMPT] @ get preempt count
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 6cca0d2..a122aed 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -923,3 +923,6 @@
source "drivers/crypto/Kconfig"
endif # if CRYPTO
+
+source "crypto/ocf/Kconfig"
+
diff --git a/crypto/Makefile b/crypto/Makefile
index 9e6eee2..3cde9f8 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -91,6 +91,8 @@
obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
+obj-$(CONFIG_OCF_OCF) += ocf/
+
#
# generic algorithms and the async_tx api
#
diff --git a/crypto/ocf/Config.in b/crypto/ocf/Config.in
new file mode 100644
index 0000000..652f76e
--- /dev/null
+++ b/crypto/ocf/Config.in
@@ -0,0 +1,38 @@
+#############################################################################
+
+mainmenu_option next_comment
+comment 'OCF Configuration'
+tristate 'OCF (Open Cryptograhic Framework)' CONFIG_OCF_OCF
+dep_mbool ' enable fips RNG checks (fips check on RNG data before use)' \
+ CONFIG_OCF_FIPS $CONFIG_OCF_OCF
+dep_mbool ' enable harvesting entropy for /dev/random' \
+ CONFIG_OCF_RANDOMHARVEST $CONFIG_OCF_OCF
+dep_tristate ' cryptodev (user space support)' \
+ CONFIG_OCF_CRYPTODEV $CONFIG_OCF_OCF
+dep_tristate ' cryptosoft (software crypto engine)' \
+ CONFIG_OCF_CRYPTOSOFT $CONFIG_OCF_OCF
+dep_tristate ' safenet (HW crypto engine)' \
+ CONFIG_OCF_SAFE $CONFIG_OCF_OCF
+dep_tristate ' IXP4xx (HW crypto engine)' \
+ CONFIG_OCF_IXP4XX $CONFIG_OCF_OCF
+dep_mbool ' Enable IXP4xx HW to perform SHA1 and MD5 hashing (very slow)' \
+ CONFIG_OCF_IXP4XX_SHA1_MD5 $CONFIG_OCF_IXP4XX
+dep_tristate ' hifn (HW crypto engine)' \
+ CONFIG_OCF_HIFN $CONFIG_OCF_OCF
+dep_tristate ' talitos (HW crypto engine)' \
+ CONFIG_OCF_TALITOS $CONFIG_OCF_OCF
+dep_tristate ' pasemi (HW crypto engine)' \
+ CONFIG_OCF_PASEMI $CONFIG_OCF_OCF
+dep_tristate ' ep80579 (HW crypto engine)' \
+ CONFIG_OCF_EP80579 $CONFIG_OCF_OCF
+dep_tristate ' Micronas c7108 (HW crypto engine)' \
+ CONFIG_OCF_C7108 $CONFIG_OCF_OCF
+dep_tristate ' uBsec BCM5365 (HW crypto engine)'
+ CONFIG_OCF_UBSEC_SSB $CONFIG_OCF_OCF
+dep_tristate ' ocfnull (does no crypto)' \
+ CONFIG_OCF_OCFNULL $CONFIG_OCF_OCF
+dep_tristate ' ocf-bench (HW crypto in-kernel benchmark)' \
+ CONFIG_OCF_BENCH $CONFIG_OCF_OCF
+endmenu
+
+#############################################################################
diff --git a/crypto/ocf/Kconfig b/crypto/ocf/Kconfig
new file mode 100644
index 0000000..8b01ca5
--- /dev/null
+++ b/crypto/ocf/Kconfig
@@ -0,0 +1,48 @@
+menu "OCF Configuration"
+
+config OCF_OCF
+ tristate "OCF (Open Cryptograhic Framework)"
+ help
+ A linux port of the OpenBSD/FreeBSD crypto framework.
+
+config OCF_RANDOMHARVEST
+ bool "crypto random --- harvest entropy for /dev/random"
+ depends on OCF_OCF
+ help
+ Includes code to harvest random numbers from devices that support it.
+
+config OCF_FIPS
+ bool "enable fips RNG checks"
+ depends on OCF_OCF && OCF_RANDOMHARVEST
+ help
+ Run all RNG provided data through a fips check before
+ adding it /dev/random's entropy pool.
+
+config OCF_CRYPTODEV
+ tristate "cryptodev (user space support)"
+ depends on OCF_OCF
+ help
+ The user space API to access crypto hardware.
+
+config OCF_CRYPTOSOFT
+ tristate "cryptosoft (software crypto engine)"
+ depends on OCF_OCF
+ help
+ A software driver for the OCF framework that uses
+ the kernel CryptoAPI.
+
+config OCF_BENCH
+ tristate "ocf-bench (HW crypto in-kernel benchmark)"
+ depends on OCF_OCF
+ help
+ A very simple encryption test for the in-kernel interface
+ of OCF. Also includes code to benchmark the IXP Access library
+ for comparison.
+
+config OCF_DM_CRYPT
+ bool "ocf-dm-crypt (HW crypto engine)"
+ depends on OCF_OCF
+ help
+ OCF support for crypto offloading of dm-crypt
+
+endmenu
diff --git a/crypto/ocf/Makefile b/crypto/ocf/Makefile
new file mode 100644
index 0000000..6b49294
--- /dev/null
+++ b/crypto/ocf/Makefile
@@ -0,0 +1,137 @@
+# for SGlinux builds
+-include $(ROOTDIR)/modules/.config
+
+OCF_OBJS = crypto.o criov.o
+
+ifdef CONFIG_OCF_RANDOMHARVEST
+ OCF_OBJS += random.o
+endif
+
+ifdef CONFIG_OCF_FIPS
+ OCF_OBJS += rndtest.o
+endif
+
+# Add in autoconf.h to get #defines for CONFIG_xxx
+AUTOCONF_H=$(ROOTDIR)/modules/autoconf.h
+ifeq ($(AUTOCONF_H), $(wildcard $(AUTOCONF_H)))
+ EXTRA_CFLAGS += -include $(AUTOCONF_H)
+ export EXTRA_CFLAGS
+endif
+
+ifndef obj
+ obj ?= .
+ _obj = subdir
+ mod-subdirs := safe hifn ixp4xx talitos ocfnull
+ export-objs += crypto.o criov.o random.o
+ list-multi += ocf.o
+ _slash :=
+else
+ _obj = obj
+ _slash := /
+endif
+
+EXTRA_CFLAGS += -I$(obj)/.
+
+obj-$(CONFIG_OCF_OCF) += ocf.o
+obj-$(CONFIG_OCF_CRYPTODEV) += cryptodev.o
+obj-$(CONFIG_OCF_CRYPTOSOFT) += cryptosoft.o
+obj-$(CONFIG_OCF_BENCH) += ocf-bench.o
+
+
+ocf-objs := $(OCF_OBJS)
+
+dummy:
+ @echo "Please consult the README for how to build OCF."
+ @echo "If you can't wait then the following should do it:"
+ @echo ""
+ @echo " make ocf_modules"
+ @echo " sudo make ocf_install"
+ @echo ""
+ @exit 1
+
+$(list-multi) dummy1: $(ocf-objs)
+ $(LD) -r -o $@ $(ocf-objs)
+
+.PHONY:
+clean:
+ rm -f *.o *.ko .*.o.flags .*.ko.cmd .*.o.cmd .*.mod.o.cmd *.mod.c
+ rm -f */*.o */*.ko */.*.o.cmd */.*.ko.cmd */.*.mod.o.cmd */*.mod.c */.*.o.flags
+ rm -f */modules.order */modules.builtin modules.order modules.builtin
+
+ifdef TOPDIR
+-include $(TOPDIR)/Rules.make
+endif
+
+#
+# targets to build easily on the current machine
+#
+
+ocf_make:
+ make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m
+ make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m CONFIG_OCF_CRYPTOSOFT=m
+ -make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m CONFIG_OCF_BENCH=m
+ -make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m CONFIG_OCF_OCFNULL=m
+ -make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m CONFIG_OCF_HIFN=m
+
+ocf_modules:
+ $(MAKE) ocf_make OCF_TARGET=modules
+
+ocf_install:
+ $(MAKE) ocf_make OCF_TARGET="modules modules_install"
+ depmod
+ mkdir -p /usr/include/crypto
+ cp cryptodev.h /usr/include/crypto/.
+
+#
+# generate full kernel patches for 2.4 and 2.6 kernels to make patching
+# your kernel easier
+#
+
+.PHONY: patch
+patch:
+ patchbase=.; \
+ [ -d $$patchbase/patches ] || patchbase=..; \
+ patch=ocf-linux-base.patch; \
+ patch24=ocf-linux-24.patch; \
+ patch26=ocf-linux-26.patch; \
+ patch3=ocf-linux-3.patch; \
+ ( \
+ find . -name Makefile; \
+ find . -name Config.in; \
+ find . -name Kconfig; \
+ find . -name README; \
+ find . -name '*.[ch]' | grep -v '.mod.c'; \
+ ) | while read t; do \
+ diff -Nau /dev/null $$t | sed 's?^+++ \./?+++ linux/crypto/ocf/?'; \
+ done > $$patch; \
+ cat $$patchbase/patches/linux-2.4.35-ocf.patch $$patch > $$patch24; \
+ cat $$patchbase/patches/linux-2.6.38-ocf.patch $$patch > $$patch26; \
+ cat $$patchbase/patches/linux-3.2.1-ocf.patch $$patch > $$patch3; \
+
+
+#
+# this target probably does nothing for anyone but me - davidm
+#
+
+.PHONY: release
+release:
+ REL=`date +%Y%m%d`; RELDIR=/tmp/ocf-linux-$$REL; \
+ CURDIR=`pwd`; \
+ rm -rf /tmp/ocf-linux-$$REL*; \
+ mkdir -p $$RELDIR/ocf; \
+ mkdir -p $$RELDIR/patches; \
+ mkdir -p $$RELDIR/crypto-tools; \
+ cp README* $$RELDIR/.; \
+ cp patches/[!C]* $$RELDIR/patches/.; \
+ cp tools/[!C]* $$RELDIR/crypto-tools/.; \
+ cp -r [!C]* Config.in $$RELDIR/ocf/.; \
+ rm -rf $$RELDIR/ocf/patches $$RELDIR/ocf/tools; \
+ rm -f $$RELDIR/ocf/README*; \
+ cp $$CURDIR/../../user/crypto-tools/[!C]* $$RELDIR/crypto-tools/.; \
+ make -C $$RELDIR/crypto-tools clean; \
+ make -C $$RELDIR/ocf clean; \
+ find $$RELDIR/ocf -name CVS | xargs rm -rf; \
+ cd $$RELDIR/..; \
+ tar cvf ocf-linux-$$REL.tar ocf-linux-$$REL; \
+ gzip -9 ocf-linux-$$REL.tar
+
diff --git a/crypto/ocf/criov.c b/crypto/ocf/criov.c
new file mode 100644
index 0000000..a8c1a8c
--- /dev/null
+++ b/crypto/ocf/criov.c
@@ -0,0 +1,215 @@
+/* $OpenBSD: criov.c,v 1.9 2002/01/29 15:48:29 jason Exp $ */
+
+/*
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2006-2010 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * Copyright (c) 1999 Theo de Raadt
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+__FBSDID("$FreeBSD: src/sys/opencrypto/criov.c,v 1.5 2006/06/04 22:15:13 pjd Exp $");
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/uio.h>
+#include <linux/skbuff.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+
+#include <uio.h>
+#include <cryptodev.h>
+
+/*
+ * This macro is only for avoiding code duplication, as we need to skip
+ * given number of bytes in the same way in three functions below.
+ */
+#define CUIO_SKIP() do { \
+ KASSERT(off >= 0, ("%s: off %d < 0", __func__, off)); \
+ KASSERT(len >= 0, ("%s: len %d < 0", __func__, len)); \
+ while (off > 0) { \
+ KASSERT(iol >= 0, ("%s: empty in skip", __func__)); \
+ if (off < iov->iov_len) \
+ break; \
+ off -= iov->iov_len; \
+ iol--; \
+ iov++; \
+ } \
+} while (0)
+
+void
+cuio_copydata(struct uio* uio, int off, int len, caddr_t cp)
+{
+ struct iovec *iov = uio->uio_iov;
+ int iol = uio->uio_iovcnt;
+ unsigned count;
+
+ CUIO_SKIP();
+ while (len > 0) {
+ KASSERT(iol >= 0, ("%s: empty", __func__));
+ count = min((int)(iov->iov_len - off), len);
+ memcpy(cp, ((caddr_t)iov->iov_base) + off, count);
+ len -= count;
+ cp += count;
+ off = 0;
+ iol--;
+ iov++;
+ }
+}
+
+void
+cuio_copyback(struct uio* uio, int off, int len, caddr_t cp)
+{
+ struct iovec *iov = uio->uio_iov;
+ int iol = uio->uio_iovcnt;
+ unsigned count;
+
+ CUIO_SKIP();
+ while (len > 0) {
+ KASSERT(iol >= 0, ("%s: empty", __func__));
+ count = min((int)(iov->iov_len - off), len);
+ memcpy(((caddr_t)iov->iov_base) + off, cp, count);
+ len -= count;
+ cp += count;
+ off = 0;
+ iol--;
+ iov++;
+ }
+}
+
+/*
+ * Return a pointer to iov/offset of location in iovec list.
+ */
+struct iovec *
+cuio_getptr(struct uio *uio, int loc, int *off)
+{
+ struct iovec *iov = uio->uio_iov;
+ int iol = uio->uio_iovcnt;
+
+ while (loc >= 0) {
+ /* Normal end of search */
+ if (loc < iov->iov_len) {
+ *off = loc;
+ return (iov);
+ }
+
+ loc -= iov->iov_len;
+ if (iol == 0) {
+ if (loc == 0) {
+ /* Point at the end of valid data */
+ *off = iov->iov_len;
+ return (iov);
+ } else
+ return (NULL);
+ } else {
+ iov++, iol--;
+ }
+ }
+
+ return (NULL);
+}
+
+EXPORT_SYMBOL(cuio_copyback);
+EXPORT_SYMBOL(cuio_copydata);
+EXPORT_SYMBOL(cuio_getptr);
+
+static void
+skb_copy_bits_back(struct sk_buff *skb, int offset, caddr_t cp, int len)
+{
+ int i;
+ if (offset < skb_headlen(skb)) {
+ memcpy(skb->data + offset, cp, min_t(int, skb_headlen(skb), len));
+ len -= skb_headlen(skb);
+ cp += skb_headlen(skb);
+ }
+ offset -= skb_headlen(skb);
+ for (i = 0; len > 0 && i < skb_shinfo(skb)->nr_frags; i++) {
+ if (offset < skb_shinfo(skb)->frags[i].size) {
+ memcpy(page_address(skb_frag_page(&skb_shinfo(skb)->frags[i])) +
+ skb_shinfo(skb)->frags[i].page_offset,
+ cp, min_t(int, skb_shinfo(skb)->frags[i].size, len));
+ len -= skb_shinfo(skb)->frags[i].size;
+ cp += skb_shinfo(skb)->frags[i].size;
+ }
+ offset -= skb_shinfo(skb)->frags[i].size;
+ }
+}
+
+void
+crypto_copyback(int flags, caddr_t buf, int off, int size, caddr_t in)
+{
+
+ if ((flags & CRYPTO_F_SKBUF) != 0)
+ skb_copy_bits_back((struct sk_buff *)buf, off, in, size);
+ else if ((flags & CRYPTO_F_IOV) != 0)
+ cuio_copyback((struct uio *)buf, off, size, in);
+ else
+ bcopy(in, buf + off, size);
+}
+
+void
+crypto_copydata(int flags, caddr_t buf, int off, int size, caddr_t out)
+{
+
+ if ((flags & CRYPTO_F_SKBUF) != 0)
+ skb_copy_bits((struct sk_buff *)buf, off, out, size);
+ else if ((flags & CRYPTO_F_IOV) != 0)
+ cuio_copydata((struct uio *)buf, off, size, out);
+ else
+ bcopy(buf + off, out, size);
+}
+
+int
+crypto_apply(int flags, caddr_t buf, int off, int len,
+ int (*f)(void *, void *, u_int), void *arg)
+{
+#if 0
+ int error;
+
+ if ((flags & CRYPTO_F_SKBUF) != 0)
+ error = XXXXXX((struct mbuf *)buf, off, len, f, arg);
+ else if ((flags & CRYPTO_F_IOV) != 0)
+ error = cuio_apply((struct uio *)buf, off, len, f, arg);
+ else
+ error = (*f)(arg, buf + off, len);
+ return (error);
+#else
+ KASSERT(0, ("crypto_apply not implemented!\n"));
+#endif
+ return 0;
+}
+
+EXPORT_SYMBOL(crypto_copyback);
+EXPORT_SYMBOL(crypto_copydata);
+EXPORT_SYMBOL(crypto_apply);
+
diff --git a/crypto/ocf/crypto.c b/crypto/ocf/crypto.c
new file mode 100644
index 0000000..7722361
--- /dev/null
+++ b/crypto/ocf/crypto.c
@@ -0,0 +1,1766 @@
+/*-
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2006-2010 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * Copyright (c) 2002-2006 Sam Leffler. All rights reserved.
+ *
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if 0
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.27 2007/03/21 03:42:51 sam Exp $");
+#endif
+
+/*
+ * Cryptographic Subsystem.
+ *
+ * This code is derived from the Openbsd Cryptographic Framework (OCF)
+ * that has the copyright shown below. Very little of the original
+ * code remains.
+ */
+/*-
+ * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
+ *
+ * This code was written by Angelos D. Keromytis in Athens, Greece, in
+ * February 2000. Network Security Technologies Inc. (NSTI) kindly
+ * supported the development of this code.
+ *
+ * Copyright (c) 2000, 2001 Angelos D. Keromytis
+ *
+ * Permission to use, copy, and modify this software with or without fee
+ * is hereby granted, provided that this entire notice is included in
+ * all source code copies of any software which is or includes a copy or
+ * modification of this software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
+ * PURPOSE.
+ *
+__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.16 2005/01/07 02:29:16 imp Exp $");
+ */
+
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4)
+#include <linux/kthread.h>
+#endif
+#include <cryptodev.h>
+
+/*
+ * keep track of whether or not we have been initialised, a big
+ * issue if we are linked into the kernel and a driver gets started before
+ * us
+ */
+static int crypto_initted = 0;
+
+/*
+ * Crypto drivers register themselves by allocating a slot in the
+ * crypto_drivers table with crypto_get_driverid() and then registering
+ * each algorithm they support with crypto_register() and crypto_kregister().
+ */
+
+/*
+ * lock on driver table
+ * we track its state as spin_is_locked does not do anything on non-SMP boxes
+ */
+static spinlock_t crypto_drivers_lock;
+static int crypto_drivers_locked; /* for non-SMP boxes */
+
+#define CRYPTO_DRIVER_LOCK() \
+ ({ \
+ spin_lock_irqsave(&crypto_drivers_lock, d_flags); \
+ crypto_drivers_locked = 1; \
+ dprintk("%s,%d: DRIVER_LOCK()\n", __FILE__, __LINE__); \
+ })
+#define CRYPTO_DRIVER_UNLOCK() \
+ ({ \
+ dprintk("%s,%d: DRIVER_UNLOCK()\n", __FILE__, __LINE__); \
+ crypto_drivers_locked = 0; \
+ spin_unlock_irqrestore(&crypto_drivers_lock, d_flags); \
+ })
+#define CRYPTO_DRIVER_ASSERT() \
+ ({ \
+ if (!crypto_drivers_locked) { \
+ dprintk("%s,%d: DRIVER_ASSERT!\n", __FILE__, __LINE__); \
+ } \
+ })
+
+/*
+ * Crypto device/driver capabilities structure.
+ *
+ * Synchronization:
+ * (d) - protected by CRYPTO_DRIVER_LOCK()
+ * (q) - protected by CRYPTO_Q_LOCK()
+ * Not tagged fields are read-only.
+ */
+struct cryptocap {
+ device_t cc_dev; /* (d) device/driver */
+ u_int32_t cc_sessions; /* (d) # of sessions */
+ u_int32_t cc_koperations; /* (d) # os asym operations */
+ /*
+ * Largest possible operator length (in bits) for each type of
+ * encryption algorithm. XXX not used
+ */
+ u_int16_t cc_max_op_len[CRYPTO_ALGORITHM_MAX + 1];
+ u_int8_t cc_alg[CRYPTO_ALGORITHM_MAX + 1];
+ u_int8_t cc_kalg[CRK_ALGORITHM_MAX + 1];
+
+ int cc_flags; /* (d) flags */
+#define CRYPTOCAP_F_CLEANUP 0x80000000 /* needs resource cleanup */
+ int cc_qblocked; /* (q) symmetric q blocked */
+ int cc_kqblocked; /* (q) asymmetric q blocked */
+
+ int cc_unqblocked; /* (q) symmetric q blocked */
+ int cc_unkqblocked; /* (q) asymmetric q blocked */
+};
+static struct cryptocap *crypto_drivers = NULL;
+static int crypto_drivers_num = 0;
+
+/*
+ * There are two queues for crypto requests; one for symmetric (e.g.
+ * cipher) operations and one for asymmetric (e.g. MOD)operations.
+ * A single mutex is used to lock access to both queues. We could
+ * have one per-queue but having one simplifies handling of block/unblock
+ * operations.
+ */
+static LIST_HEAD(crp_q); /* crypto request queue */
+static LIST_HEAD(crp_kq); /* asym request queue */
+
+static spinlock_t crypto_q_lock;
+
+int crypto_all_qblocked = 0; /* protect with Q_LOCK */
+module_param(crypto_all_qblocked, int, 0444);
+MODULE_PARM_DESC(crypto_all_qblocked, "Are all crypto queues blocked");
+
+int crypto_all_kqblocked = 0; /* protect with Q_LOCK */
+module_param(crypto_all_kqblocked, int, 0444);
+MODULE_PARM_DESC(crypto_all_kqblocked, "Are all asym crypto queues blocked");
+
+#define CRYPTO_Q_LOCK() \
+ ({ \
+ spin_lock_irqsave(&crypto_q_lock, q_flags); \
+ dprintk("%s,%d: Q_LOCK()\n", __FILE__, __LINE__); \
+ })
+#define CRYPTO_Q_UNLOCK() \
+ ({ \
+ dprintk("%s,%d: Q_UNLOCK()\n", __FILE__, __LINE__); \
+ spin_unlock_irqrestore(&crypto_q_lock, q_flags); \
+ })
+
+/*
+ * There are two queues for processing completed crypto requests; one
+ * for the symmetric and one for the asymmetric ops. We only need one
+ * but have two to avoid type futzing (cryptop vs. cryptkop). A single
+ * mutex is used to lock access to both queues. Note that this lock
+ * must be separate from the lock on request queues to insure driver
+ * callbacks don't generate lock order reversals.
+ */
+static LIST_HEAD(crp_ret_q); /* callback queues */
+static LIST_HEAD(crp_ret_kq);
+
+static spinlock_t crypto_ret_q_lock;
+#define CRYPTO_RETQ_LOCK() \
+ ({ \
+ spin_lock_irqsave(&crypto_ret_q_lock, r_flags); \
+ dprintk("%s,%d: RETQ_LOCK\n", __FILE__, __LINE__); \
+ })
+#define CRYPTO_RETQ_UNLOCK() \
+ ({ \
+ dprintk("%s,%d: RETQ_UNLOCK\n", __FILE__, __LINE__); \
+ spin_unlock_irqrestore(&crypto_ret_q_lock, r_flags); \
+ })
+#define CRYPTO_RETQ_EMPTY() (list_empty(&crp_ret_q) && list_empty(&crp_ret_kq))
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static kmem_cache_t *cryptop_zone;
+static kmem_cache_t *cryptodesc_zone;
+#else
+static struct kmem_cache *cryptop_zone;
+static struct kmem_cache *cryptodesc_zone;
+#endif
+
+#define debug crypto_debug
+int crypto_debug = 0;
+module_param(crypto_debug, int, 0644);
+MODULE_PARM_DESC(crypto_debug, "Enable debug");
+EXPORT_SYMBOL(crypto_debug);
+
+/*
+ * Maximum number of outstanding crypto requests before we start
+ * failing requests. We need this to prevent DOS when too many
+ * requests are arriving for us to keep up. Otherwise we will
+ * run the system out of memory. Since crypto is slow, we are
+ * usually the bottleneck that needs to say, enough is enough.
+ *
+ * We cannot print errors when this condition occurs, we are already too
+ * slow, printing anything will just kill us
+ */
+
+static int crypto_q_cnt = 0;
+module_param(crypto_q_cnt, int, 0444);
+MODULE_PARM_DESC(crypto_q_cnt,
+ "Current number of outstanding crypto requests");
+
+static int crypto_q_max = 1000;
+module_param(crypto_q_max, int, 0644);
+MODULE_PARM_DESC(crypto_q_max,
+ "Maximum number of outstanding crypto requests");
+
+#define bootverbose crypto_verbose
+static int crypto_verbose = 0;
+module_param(crypto_verbose, int, 0644);
+MODULE_PARM_DESC(crypto_verbose,
+ "Enable verbose crypto startup");
+
+int crypto_usercrypto = 1; /* userland may do crypto reqs */
+module_param(crypto_usercrypto, int, 0644);
+MODULE_PARM_DESC(crypto_usercrypto,
+ "Enable/disable user-mode access to crypto support");
+
+int crypto_userasymcrypto = 1; /* userland may do asym crypto reqs */
+module_param(crypto_userasymcrypto, int, 0644);
+MODULE_PARM_DESC(crypto_userasymcrypto,
+ "Enable/disable user-mode access to asymmetric crypto support");
+
+int crypto_devallowsoft = 0; /* only use hardware crypto */
+module_param(crypto_devallowsoft, int, 0644);
+MODULE_PARM_DESC(crypto_devallowsoft,
+ "Enable/disable use of software crypto support");
+
+/*
+ * This parameter controls the maximum number of crypto operations to
+ * do consecutively in the crypto kernel thread before scheduling to allow
+ * other processes to run. Without it, it is possible to get into a
+ * situation where the crypto thread never allows any other processes to run.
+ * Default to 1000 which should be less than one second.
+ */
+static int crypto_max_loopcount = 1000;
+module_param(crypto_max_loopcount, int, 0644);
+MODULE_PARM_DESC(crypto_max_loopcount,
+ "Maximum number of crypto ops to do before yielding to other processes");
+
+#ifndef CONFIG_NR_CPUS
+#define CONFIG_NR_CPUS 1
+#endif
+
+static struct task_struct *cryptoproc[CONFIG_NR_CPUS];
+static struct task_struct *cryptoretproc[CONFIG_NR_CPUS];
+static DECLARE_WAIT_QUEUE_HEAD(cryptoproc_wait);
+static DECLARE_WAIT_QUEUE_HEAD(cryptoretproc_wait);
+
+static int crypto_proc(void *arg);
+static int crypto_ret_proc(void *arg);
+static int crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint);
+static int crypto_kinvoke(struct cryptkop *krp, int flags);
+static void crypto_exit(void);
+static int crypto_init(void);
+
+static struct cryptostats cryptostats;
+
+static struct cryptocap *
+crypto_checkdriver(u_int32_t hid)
+{
+ if (crypto_drivers == NULL)
+ return NULL;
+ return (hid >= crypto_drivers_num ? NULL : &crypto_drivers[hid]);
+}
+
+/*
+ * Compare a driver's list of supported algorithms against another
+ * list; return non-zero if all algorithms are supported.
+ */
+static int
+driver_suitable(const struct cryptocap *cap, const struct cryptoini *cri)
+{
+ const struct cryptoini *cr;
+
+ /* See if all the algorithms are supported. */
+ for (cr = cri; cr; cr = cr->cri_next)
+ if (cap->cc_alg[cr->cri_alg] == 0)
+ return 0;
+ return 1;
+}
+
+
+/*
+ * Select a driver for a new session that supports the specified
+ * algorithms and, optionally, is constrained according to the flags.
+ * The algorithm we use here is pretty stupid; just use the
+ * first driver that supports all the algorithms we need. If there
+ * are multiple drivers we choose the driver with the fewest active
+ * sessions. We prefer hardware-backed drivers to software ones.
+ *
+ * XXX We need more smarts here (in real life too, but that's
+ * XXX another story altogether).
+ */
+static struct cryptocap *
+crypto_select_driver(const struct cryptoini *cri, int flags)
+{
+ struct cryptocap *cap, *best;
+ int match, hid;
+
+ CRYPTO_DRIVER_ASSERT();
+
+ /*
+ * Look first for hardware crypto devices if permitted.
+ */
+ if (flags & CRYPTOCAP_F_HARDWARE)
+ match = CRYPTOCAP_F_HARDWARE;
+ else
+ match = CRYPTOCAP_F_SOFTWARE;
+ best = NULL;
+again:
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
+ cap = &crypto_drivers[hid];
+ /*
+ * If it's not initialized, is in the process of
+ * going away, or is not appropriate (hardware
+ * or software based on match), then skip.
+ */
+ if (cap->cc_dev == NULL ||
+ (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
+ (cap->cc_flags & match) == 0)
+ continue;
+
+ /* verify all the algorithms are supported. */
+ if (driver_suitable(cap, cri)) {
+ if (best == NULL ||
+ cap->cc_sessions < best->cc_sessions)
+ best = cap;
+ }
+ }
+ if (best != NULL)
+ return best;
+ if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
+ /* sort of an Algol 68-style for loop */
+ match = CRYPTOCAP_F_SOFTWARE;
+ goto again;
+ }
+ return best;
+}
+
+/*
+ * Create a new session. The crid argument specifies a crypto
+ * driver to use or constraints on a driver to select (hardware
+ * only, software only, either). Whatever driver is selected
+ * must be capable of the requested crypto algorithms.
+ */
+int
+crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int crid)
+{
+ struct cryptocap *cap;
+ u_int32_t hid, lid;
+ int err;
+ unsigned long d_flags;
+
+ CRYPTO_DRIVER_LOCK();
+ if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+ /*
+ * Use specified driver; verify it is capable.
+ */
+ cap = crypto_checkdriver(crid);
+ if (cap != NULL && !driver_suitable(cap, cri))
+ cap = NULL;
+ } else {
+ /*
+ * No requested driver; select based on crid flags.
+ */
+ cap = crypto_select_driver(cri, crid);
+ /*
+ * if NULL then can't do everything in one session.
+ * XXX Fix this. We need to inject a "virtual" session
+ * XXX layer right about here.
+ */
+ }
+ if (cap != NULL) {
+ /* Call the driver initialization routine. */
+ hid = cap - crypto_drivers;
+ lid = hid; /* Pass the driver ID. */
+ cap->cc_sessions++;
+ CRYPTO_DRIVER_UNLOCK();
+ err = CRYPTODEV_NEWSESSION(cap->cc_dev, &lid, cri);
+ CRYPTO_DRIVER_LOCK();
+ if (err == 0) {
+ (*sid) = (cap->cc_flags & 0xff000000)
+ | (hid & 0x00ffffff);
+ (*sid) <<= 32;
+ (*sid) |= (lid & 0xffffffff);
+ } else
+ cap->cc_sessions--;
+ } else
+ err = EINVAL;
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+static void
+crypto_remove(struct cryptocap *cap)
+{
+ CRYPTO_DRIVER_ASSERT();
+ if (cap->cc_sessions == 0 && cap->cc_koperations == 0)
+ bzero(cap, sizeof(*cap));
+}
+
+/*
+ * Delete an existing session (or a reserved session on an unregistered
+ * driver).
+ */
+int
+crypto_freesession(u_int64_t sid)
+{
+ struct cryptocap *cap;
+ u_int32_t hid;
+ int err = 0;
+ unsigned long d_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ CRYPTO_DRIVER_LOCK();
+
+ if (crypto_drivers == NULL) {
+ err = EINVAL;
+ goto done;
+ }
+
+ /* Determine two IDs. */
+ hid = CRYPTO_SESID2HID(sid);
+
+ if (hid >= crypto_drivers_num) {
+ dprintk("%s - INVALID DRIVER NUM %d\n", __FUNCTION__, hid);
+ err = ENOENT;
+ goto done;
+ }
+ cap = &crypto_drivers[hid];
+
+ if (cap->cc_dev) {
+ CRYPTO_DRIVER_UNLOCK();
+ /* Call the driver cleanup routine, if available, unlocked. */
+ err = CRYPTODEV_FREESESSION(cap->cc_dev, sid);
+ CRYPTO_DRIVER_LOCK();
+ }
+
+ if (cap->cc_sessions)
+ cap->cc_sessions--;
+
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
+ crypto_remove(cap);
+
+done:
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+/*
+ * Return an unused driver id. Used by drivers prior to registering
+ * support for the algorithms they handle.
+ */
+int32_t
+crypto_get_driverid(device_t dev, int flags)
+{
+ struct cryptocap *newdrv;
+ int i;
+ unsigned long d_flags;
+
+ if ((flags & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+ printf("%s: no flags specified when registering driver\n",
+ device_get_nameunit(dev));
+ return -1;
+ }
+
+ CRYPTO_DRIVER_LOCK();
+
+ for (i = 0; i < crypto_drivers_num; i++) {
+ if (crypto_drivers[i].cc_dev == NULL &&
+ (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) == 0) {
+ break;
+ }
+ }
+
+ /* Out of entries, allocate some more. */
+ if (i == crypto_drivers_num) {
+ /* Be careful about wrap-around. */
+ if (2 * crypto_drivers_num <= crypto_drivers_num) {
+ CRYPTO_DRIVER_UNLOCK();
+ printk("crypto: driver count wraparound!\n");
+ return -1;
+ }
+
+ newdrv = kmalloc(2 * crypto_drivers_num * sizeof(struct cryptocap),
+ GFP_KERNEL);
+ if (newdrv == NULL) {
+ CRYPTO_DRIVER_UNLOCK();
+ printk("crypto: no space to expand driver table!\n");
+ return -1;
+ }
+
+ memcpy(newdrv, crypto_drivers,
+ crypto_drivers_num * sizeof(struct cryptocap));
+ memset(&newdrv[crypto_drivers_num], 0,
+ crypto_drivers_num * sizeof(struct cryptocap));
+
+ crypto_drivers_num *= 2;
+
+ kfree(crypto_drivers);
+ crypto_drivers = newdrv;
+ }
+
+ /* NB: state is zero'd on free */
+ crypto_drivers[i].cc_sessions = 1; /* Mark */
+ crypto_drivers[i].cc_dev = dev;
+ crypto_drivers[i].cc_flags = flags;
+ if (bootverbose)
+ printf("crypto: assign %s driver id %u, flags %u\n",
+ device_get_nameunit(dev), i, flags);
+
+ CRYPTO_DRIVER_UNLOCK();
+
+ return i;
+}
+
+/*
+ * Lookup a driver by name. We match against the full device
+ * name and unit, and against just the name. The latter gives
+ * us a simple widlcarding by device name. On success return the
+ * driver/hardware identifier; otherwise return -1.
+ */
+int
+crypto_find_driver(const char *match)
+{
+ int i, len = strlen(match);
+ unsigned long d_flags;
+
+ CRYPTO_DRIVER_LOCK();
+ for (i = 0; i < crypto_drivers_num; i++) {
+ device_t dev = crypto_drivers[i].cc_dev;
+ if (dev == NULL ||
+ (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP))
+ continue;
+ if (strncmp(match, device_get_nameunit(dev), len) == 0 ||
+ strncmp(match, device_get_name(dev), len) == 0)
+ break;
+ }
+ CRYPTO_DRIVER_UNLOCK();
+ return i < crypto_drivers_num ? i : -1;
+}
+
+/*
+ * Return the device_t for the specified driver or NULL
+ * if the driver identifier is invalid.
+ */
+device_t
+crypto_find_device_byhid(int hid)
+{
+ struct cryptocap *cap = crypto_checkdriver(hid);
+ return cap != NULL ? cap->cc_dev : NULL;
+}
+
+/*
+ * Return the device/driver capabilities.
+ */
+int
+crypto_getcaps(int hid)
+{
+ struct cryptocap *cap = crypto_checkdriver(hid);
+ return cap != NULL ? cap->cc_flags : 0;
+}
+
+/*
+ * Register support for a key-related algorithm. This routine
+ * is called once for each algorithm supported a driver.
+ */
+int
+crypto_kregister(u_int32_t driverid, int kalg, u_int32_t flags)
+{
+ struct cryptocap *cap;
+ int err;
+ unsigned long d_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ CRYPTO_DRIVER_LOCK();
+
+ cap = crypto_checkdriver(driverid);
+ if (cap != NULL &&
+ (CRK_ALGORITM_MIN <= kalg && kalg <= CRK_ALGORITHM_MAX)) {
+ /*
+ * XXX Do some performance testing to determine placing.
+ * XXX We probably need an auxiliary data structure that
+ * XXX describes relative performances.
+ */
+
+ cap->cc_kalg[kalg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
+ if (bootverbose)
+ printf("crypto: %s registers key alg %u flags %u\n"
+ , device_get_nameunit(cap->cc_dev)
+ , kalg
+ , flags
+ );
+ err = 0;
+ } else
+ err = EINVAL;
+
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+/*
+ * Register support for a non-key-related algorithm. This routine
+ * is called once for each such algorithm supported by a driver.
+ */
+int
+crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
+ u_int32_t flags)
+{
+ struct cryptocap *cap;
+ int err;
+ unsigned long d_flags;
+
+ dprintk("%s(id=0x%x, alg=%d, maxoplen=%d, flags=0x%x)\n", __FUNCTION__,
+ driverid, alg, maxoplen, flags);
+
+ CRYPTO_DRIVER_LOCK();
+
+ cap = crypto_checkdriver(driverid);
+ /* NB: algorithms are in the range [1..max] */
+ if (cap != NULL &&
+ (CRYPTO_ALGORITHM_MIN <= alg && alg <= CRYPTO_ALGORITHM_MAX)) {
+ /*
+ * XXX Do some performance testing to determine placing.
+ * XXX We probably need an auxiliary data structure that
+ * XXX describes relative performances.
+ */
+
+ cap->cc_alg[alg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
+ cap->cc_max_op_len[alg] = maxoplen;
+ if (bootverbose)
+ printf("crypto: %s registers alg %u flags %u maxoplen %u\n"
+ , device_get_nameunit(cap->cc_dev)
+ , alg
+ , flags
+ , maxoplen
+ );
+ cap->cc_sessions = 0; /* Unmark */
+ err = 0;
+ } else
+ err = EINVAL;
+
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+static void
+driver_finis(struct cryptocap *cap)
+{
+ u_int32_t ses, kops;
+
+ CRYPTO_DRIVER_ASSERT();
+
+ ses = cap->cc_sessions;
+ kops = cap->cc_koperations;
+ bzero(cap, sizeof(*cap));
+ if (ses != 0 || kops != 0) {
+ /*
+ * If there are pending sessions,
+ * just mark as invalid.
+ */
+ cap->cc_flags |= CRYPTOCAP_F_CLEANUP;
+ cap->cc_sessions = ses;
+ cap->cc_koperations = kops;
+ }
+}
+
+/*
+ * Unregister a crypto driver. If there are pending sessions using it,
+ * leave enough information around so that subsequent calls using those
+ * sessions will correctly detect the driver has been unregistered and
+ * reroute requests.
+ */
+int
+crypto_unregister(u_int32_t driverid, int alg)
+{
+ struct cryptocap *cap;
+ int i, err;
+ unsigned long d_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ CRYPTO_DRIVER_LOCK();
+
+ cap = crypto_checkdriver(driverid);
+ if (cap != NULL &&
+ (CRYPTO_ALGORITHM_MIN <= alg && alg <= CRYPTO_ALGORITHM_MAX) &&
+ cap->cc_alg[alg] != 0) {
+ cap->cc_alg[alg] = 0;
+ cap->cc_max_op_len[alg] = 0;
+
+ /* Was this the last algorithm ? */
+ for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
+ if (cap->cc_alg[i] != 0)
+ break;
+
+ if (i == CRYPTO_ALGORITHM_MAX + 1)
+ driver_finis(cap);
+ err = 0;
+ } else
+ err = EINVAL;
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+/*
+ * Unregister all algorithms associated with a crypto driver.
+ * If there are pending sessions using it, leave enough information
+ * around so that subsequent calls using those sessions will
+ * correctly detect the driver has been unregistered and reroute
+ * requests.
+ */
+int
+crypto_unregister_all(u_int32_t driverid)
+{
+ struct cryptocap *cap;
+ int err;
+ unsigned long d_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ CRYPTO_DRIVER_LOCK();
+ cap = crypto_checkdriver(driverid);
+ if (cap != NULL) {
+ driver_finis(cap);
+ err = 0;
+ } else
+ err = EINVAL;
+ CRYPTO_DRIVER_UNLOCK();
+
+ return err;
+}
+
+/*
+ * Clear blockage on a driver. The what parameter indicates whether
+ * the driver is now ready for cryptop's and/or cryptokop's.
+ */
+int
+crypto_unblock(u_int32_t driverid, int what)
+{
+ struct cryptocap *cap;
+ int err;
+ unsigned long q_flags;
+
+ CRYPTO_Q_LOCK();
+ cap = crypto_checkdriver(driverid);
+ if (cap != NULL) {
+ if (what & CRYPTO_SYMQ) {
+ cap->cc_qblocked = 0;
+ cap->cc_unqblocked = 0;
+ crypto_all_qblocked = 0;
+ }
+ if (what & CRYPTO_ASYMQ) {
+ cap->cc_kqblocked = 0;
+ cap->cc_unkqblocked = 0;
+ crypto_all_kqblocked = 0;
+ }
+ wake_up_interruptible(&cryptoproc_wait);
+ err = 0;
+ } else
+ err = EINVAL;
+ CRYPTO_Q_UNLOCK(); //DAVIDM should this be a driver lock
+
+ return err;
+}
+
+/*
+ * Add a crypto request to a queue, to be processed by the kernel thread.
+ */
+int
+crypto_dispatch(struct cryptop *crp)
+{
+ struct cryptocap *cap;
+ int result = -1;
+ unsigned long q_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+
+ cryptostats.cs_ops++;
+
+ CRYPTO_Q_LOCK();
+ /*if (crypto_q_cnt >= crypto_q_max) {
+ cryptostats.cs_drops++;
+ CRYPTO_Q_UNLOCK();
+ return ENOMEM;
+ }*/
+ crypto_q_cnt++;
+
+ /* make sure we are starting a fresh run on this crp. */
+ crp->crp_flags &= ~CRYPTO_F_DONE;
+ crp->crp_etype = 0;
+
+ /*
+ * Caller marked the request to be processed immediately; dispatch
+ * it directly to the driver unless the driver is currently blocked.
+ */
+ if ((crp->crp_flags & CRYPTO_F_BATCH) == 0) {
+ int hid = CRYPTO_SESID2HID(crp->crp_sid);
+ cap = crypto_checkdriver(hid);
+ /* Driver cannot disappear when there is an active session. */
+ KASSERT(cap != NULL, ("%s: Driver disappeared.", __func__));
+ if (!cap->cc_qblocked) {
+ crypto_all_qblocked = 0;
+ crypto_drivers[hid].cc_unqblocked = 1;
+ CRYPTO_Q_UNLOCK();
+ result = crypto_invoke(cap, crp, 0);
+ CRYPTO_Q_LOCK();
+ if (result == ERESTART)
+ if (crypto_drivers[hid].cc_unqblocked)
+ crypto_drivers[hid].cc_qblocked = 1;
+ crypto_drivers[hid].cc_unqblocked = 0;
+ }
+ }
+ if (result == ERESTART) {
+ /*
+ * The driver ran out of resources, mark the
+ * driver ``blocked'' for cryptop's and put
+ * the request back in the queue. It would
+ * best to put the request back where we got
+ * it but that's hard so for now we put it
+ * at the front. This should be ok; putting
+ * it at the end does not work.
+ */
+ list_add(&crp->crp_next, &crp_q);
+ cryptostats.cs_blocks++;
+ result = 0;
+ } else if (result == -1) {
+ TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
+ result = 0;
+ }
+ wake_up_interruptible(&cryptoproc_wait);
+ CRYPTO_Q_UNLOCK();
+ return result;
+}
+
+/*
+ * Add an asymetric crypto request to a queue,
+ * to be processed by the kernel thread.
+ */
+int
+crypto_kdispatch(struct cryptkop *krp)
+{
+ int error;
+ unsigned long q_flags;
+
+ cryptostats.cs_kops++;
+
+ error = crypto_kinvoke(krp, krp->krp_crid);
+ if (error == ERESTART) {
+ CRYPTO_Q_LOCK();
+ TAILQ_INSERT_TAIL(&crp_kq, krp, krp_next);
+ wake_up_interruptible(&cryptoproc_wait);
+ CRYPTO_Q_UNLOCK();
+ error = 0;
+ }
+ return error;
+}
+
+/*
+ * Verify a driver is suitable for the specified operation.
+ */
+static __inline int
+kdriver_suitable(const struct cryptocap *cap, const struct cryptkop *krp)
+{
+ return (cap->cc_kalg[krp->krp_op] & CRYPTO_ALG_FLAG_SUPPORTED) != 0;
+}
+
+/*
+ * Select a driver for an asym operation. The driver must
+ * support the necessary algorithm. The caller can constrain
+ * which device is selected with the flags parameter. The
+ * algorithm we use here is pretty stupid; just use the first
+ * driver that supports the algorithms we need. If there are
+ * multiple suitable drivers we choose the driver with the
+ * fewest active operations. We prefer hardware-backed
+ * drivers to software ones when either may be used.
+ */
+static struct cryptocap *
+crypto_select_kdriver(const struct cryptkop *krp, int flags)
+{
+ struct cryptocap *cap, *best, *blocked;
+ int match, hid;
+
+ CRYPTO_DRIVER_ASSERT();
+
+ /*
+ * Look first for hardware crypto devices if permitted.
+ */
+ if (flags & CRYPTOCAP_F_HARDWARE)
+ match = CRYPTOCAP_F_HARDWARE;
+ else
+ match = CRYPTOCAP_F_SOFTWARE;
+ best = NULL;
+ blocked = NULL;
+again:
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
+ cap = &crypto_drivers[hid];
+ /*
+ * If it's not initialized, is in the process of
+ * going away, or is not appropriate (hardware
+ * or software based on match), then skip.
+ */
+ if (cap->cc_dev == NULL ||
+ (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
+ (cap->cc_flags & match) == 0)
+ continue;
+
+ /* verify all the algorithms are supported. */
+ if (kdriver_suitable(cap, krp)) {
+ if (best == NULL ||
+ cap->cc_koperations < best->cc_koperations)
+ best = cap;
+ }
+ }
+ if (best != NULL)
+ return best;
+ if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
+ /* sort of an Algol 68-style for loop */
+ match = CRYPTOCAP_F_SOFTWARE;
+ goto again;
+ }
+ return best;
+}
+
+/*
+ * Dispatch an assymetric crypto request.
+ */
+static int
+crypto_kinvoke(struct cryptkop *krp, int crid)
+{
+ struct cryptocap *cap = NULL;
+ int error;
+ unsigned long d_flags;
+
+ KASSERT(krp != NULL, ("%s: krp == NULL", __func__));
+ KASSERT(krp->krp_callback != NULL,
+ ("%s: krp->crp_callback == NULL", __func__));
+
+ CRYPTO_DRIVER_LOCK();
+ if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+ cap = crypto_checkdriver(crid);
+ if (cap != NULL) {
+ /*
+ * Driver present, it must support the necessary
+ * algorithm and, if s/w drivers are excluded,
+ * it must be registered as hardware-backed.
+ */
+ if (!kdriver_suitable(cap, krp) ||
+ (!crypto_devallowsoft &&
+ (cap->cc_flags & CRYPTOCAP_F_HARDWARE) == 0))
+ cap = NULL;
+ }
+ } else {
+ /*
+ * No requested driver; select based on crid flags.
+ */
+ if (!crypto_devallowsoft) /* NB: disallow s/w drivers */
+ crid &= ~CRYPTOCAP_F_SOFTWARE;
+ cap = crypto_select_kdriver(krp, crid);
+ }
+ if (cap != NULL && !cap->cc_kqblocked) {
+ krp->krp_hid = cap - crypto_drivers;
+ cap->cc_koperations++;
+ CRYPTO_DRIVER_UNLOCK();
+ error = CRYPTODEV_KPROCESS(cap->cc_dev, krp, 0);
+ CRYPTO_DRIVER_LOCK();
+ if (error == ERESTART) {
+ cap->cc_koperations--;
+ CRYPTO_DRIVER_UNLOCK();
+ return (error);
+ }
+ /* return the actual device used */
+ krp->krp_crid = krp->krp_hid;
+ } else {
+ /*
+ * NB: cap is !NULL if device is blocked; in
+ * that case return ERESTART so the operation
+ * is resubmitted if possible.
+ */
+ error = (cap == NULL) ? ENODEV : ERESTART;
+ }
+ CRYPTO_DRIVER_UNLOCK();
+
+ if (error) {
+ krp->krp_status = error;
+ crypto_kdone(krp);
+ }
+ return 0;
+}
+
+
+/*
+ * Dispatch a crypto request to the appropriate crypto devices.
+ */
+static int
+crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint)
+{
+ KASSERT(crp != NULL, ("%s: crp == NULL", __func__));
+ KASSERT(crp->crp_callback != NULL,
+ ("%s: crp->crp_callback == NULL", __func__));
+ KASSERT(crp->crp_desc != NULL, ("%s: crp->crp_desc == NULL", __func__));
+
+ dprintk("%s()\n", __FUNCTION__);
+
+#ifdef CRYPTO_TIMING
+ if (crypto_timing)
+ crypto_tstat(&cryptostats.cs_invoke, &crp->crp_tstamp);
+#endif
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP) {
+ struct cryptodesc *crd;
+ u_int64_t nid;
+
+ /*
+ * Driver has unregistered; migrate the session and return
+ * an error to the caller so they'll resubmit the op.
+ *
+ * XXX: What if there are more already queued requests for this
+ * session?
+ */
+ crypto_freesession(crp->crp_sid);
+
+ for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
+ crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
+
+ /* XXX propagate flags from initial session? */
+ if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI),
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE) == 0)
+ crp->crp_sid = nid;
+
+ crp->crp_etype = EAGAIN;
+ crypto_done(crp);
+ return 0;
+ } else {
+ /*
+ * Invoke the driver to process the request.
+ */
+ return CRYPTODEV_PROCESS(cap->cc_dev, crp, hint);
+ }
+}
+
+/*
+ * Release a set of crypto descriptors.
+ */
+void
+crypto_freereq(struct cryptop *crp)
+{
+ struct cryptodesc *crd;
+
+ if (crp == NULL)
+ return;
+
+#ifdef DIAGNOSTIC
+ {
+ struct cryptop *crp2;
+ unsigned long q_flags;
+
+ CRYPTO_Q_LOCK();
+ TAILQ_FOREACH(crp2, &crp_q, crp_next) {
+ KASSERT(crp2 != crp,
+ ("Freeing cryptop from the crypto queue (%p).",
+ crp));
+ }
+ CRYPTO_Q_UNLOCK();
+ CRYPTO_RETQ_LOCK();
+ TAILQ_FOREACH(crp2, &crp_ret_q, crp_next) {
+ KASSERT(crp2 != crp,
+ ("Freeing cryptop from the return queue (%p).",
+ crp));
+ }
+ CRYPTO_RETQ_UNLOCK();
+ }
+#endif
+
+ while ((crd = crp->crp_desc) != NULL) {
+ crp->crp_desc = crd->crd_next;
+ kmem_cache_free(cryptodesc_zone, crd);
+ }
+ kmem_cache_free(cryptop_zone, crp);
+}
+
+/*
+ * Acquire a set of crypto descriptors.
+ */
+struct cryptop *
+crypto_getreq(int num)
+{
+ struct cryptodesc *crd;
+ struct cryptop *crp;
+
+ crp = kmem_cache_alloc(cryptop_zone, SLAB_ATOMIC);
+ if (crp != NULL) {
+ memset(crp, 0, sizeof(*crp));
+ INIT_LIST_HEAD(&crp->crp_next);
+ init_waitqueue_head(&crp->crp_waitq);
+ while (num--) {
+ crd = kmem_cache_alloc(cryptodesc_zone, SLAB_ATOMIC);
+ if (crd == NULL) {
+ crypto_freereq(crp);
+ return NULL;
+ }
+ memset(crd, 0, sizeof(*crd));
+ crd->crd_next = crp->crp_desc;
+ crp->crp_desc = crd;
+ }
+ }
+ return crp;
+}
+
+/*
+ * Invoke the callback on behalf of the driver.
+ */
+void
+crypto_done(struct cryptop *crp)
+{
+ unsigned long q_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if ((crp->crp_flags & CRYPTO_F_DONE) == 0) {
+ crp->crp_flags |= CRYPTO_F_DONE;
+ CRYPTO_Q_LOCK();
+ crypto_q_cnt--;
+ CRYPTO_Q_UNLOCK();
+ } else
+ printk("crypto: crypto_done op already done, flags 0x%x",
+ crp->crp_flags);
+ if (crp->crp_etype != 0)
+ cryptostats.cs_errs++;
+ /*
+ * CBIMM means unconditionally do the callback immediately;
+ * CBIFSYNC means do the callback immediately only if the
+ * operation was done synchronously. Both are used to avoid
+ * doing extraneous context switches; the latter is mostly
+ * used with the software crypto driver.
+ */
+ if ((crp->crp_flags & CRYPTO_F_CBIMM) ||
+ ((crp->crp_flags & CRYPTO_F_CBIFSYNC) &&
+ (CRYPTO_SESID2CAPS(crp->crp_sid) & CRYPTOCAP_F_SYNC))) {
+ /*
+ * Do the callback directly. This is ok when the
+ * callback routine does very little (e.g. the
+ * /dev/crypto callback method just does a wakeup).
+ */
+ crp->crp_callback(crp);
+ } else {
+ unsigned long r_flags;
+ /*
+ * Normal case; queue the callback for the thread.
+ */
+ CRYPTO_RETQ_LOCK();
+ wake_up_interruptible(&cryptoretproc_wait);/* shared wait channel */
+ TAILQ_INSERT_TAIL(&crp_ret_q, crp, crp_next);
+ CRYPTO_RETQ_UNLOCK();
+ }
+}
+
+/*
+ * Invoke the callback on behalf of the driver.
+ */
+void
+crypto_kdone(struct cryptkop *krp)
+{
+ struct cryptocap *cap;
+ unsigned long d_flags;
+
+ if ((krp->krp_flags & CRYPTO_KF_DONE) != 0)
+ printk("crypto: crypto_kdone op already done, flags 0x%x",
+ krp->krp_flags);
+ krp->krp_flags |= CRYPTO_KF_DONE;
+ if (krp->krp_status != 0)
+ cryptostats.cs_kerrs++;
+
+ CRYPTO_DRIVER_LOCK();
+ /* XXX: What if driver is loaded in the meantime? */
+ if (krp->krp_hid < crypto_drivers_num) {
+ cap = &crypto_drivers[krp->krp_hid];
+ cap->cc_koperations--;
+ KASSERT(cap->cc_koperations >= 0, ("cc_koperations < 0"));
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
+ crypto_remove(cap);
+ }
+ CRYPTO_DRIVER_UNLOCK();
+
+ /*
+ * CBIMM means unconditionally do the callback immediately;
+ * This is used to avoid doing extraneous context switches
+ */
+ if ((krp->krp_flags & CRYPTO_KF_CBIMM)) {
+ /*
+ * Do the callback directly. This is ok when the
+ * callback routine does very little (e.g. the
+ * /dev/crypto callback method just does a wakeup).
+ */
+ krp->krp_callback(krp);
+ } else {
+ unsigned long r_flags;
+ /*
+ * Normal case; queue the callback for the thread.
+ */
+ CRYPTO_RETQ_LOCK();
+ wake_up_interruptible(&cryptoretproc_wait);/* shared wait channel */
+ TAILQ_INSERT_TAIL(&crp_ret_kq, krp, krp_next);
+ CRYPTO_RETQ_UNLOCK();
+ }
+}
+
+int
+crypto_getfeat(int *featp)
+{
+ int hid, kalg, feat = 0;
+ unsigned long d_flags;
+
+ CRYPTO_DRIVER_LOCK();
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
+ const struct cryptocap *cap = &crypto_drivers[hid];
+
+ if ((cap->cc_flags & CRYPTOCAP_F_SOFTWARE) &&
+ !crypto_devallowsoft) {
+ continue;
+ }
+ for (kalg = 0; kalg < CRK_ALGORITHM_MAX; kalg++)
+ if (cap->cc_kalg[kalg] & CRYPTO_ALG_FLAG_SUPPORTED)
+ feat |= 1 << kalg;
+ }
+ CRYPTO_DRIVER_UNLOCK();
+ *featp = feat;
+ return (0);
+}
+
+/*
+ * Crypto thread, dispatches crypto requests.
+ */
+static int
+crypto_proc(void *arg)
+{
+ struct cryptop *crp, *submit;
+ struct cryptkop *krp, *krpp;
+ struct cryptocap *cap;
+ u_int32_t hid;
+ int result, hint;
+ unsigned long q_flags;
+ int loopcount = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ CRYPTO_Q_LOCK();
+ for (;;) {
+ /*
+ * we need to make sure we don't get into a busy loop with nothing
+ * to do, the two crypto_all_*blocked vars help us find out when
+ * we are all full and can do nothing on any driver or Q. If so we
+ * wait for an unblock.
+ */
+ crypto_all_qblocked = !list_empty(&crp_q);
+
+ /*
+ * Find the first element in the queue that can be
+ * processed and look-ahead to see if multiple ops
+ * are ready for the same driver.
+ */
+ submit = NULL;
+ hint = 0;
+ list_for_each_entry(crp, &crp_q, crp_next) {
+ hid = CRYPTO_SESID2HID(crp->crp_sid);
+ cap = crypto_checkdriver(hid);
+ /*
+ * Driver cannot disappear when there is an active
+ * session.
+ */
+ KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
+ __func__, __LINE__));
+ if (cap == NULL || cap->cc_dev == NULL) {
+ /* Op needs to be migrated, process it. */
+ if (submit == NULL)
+ submit = crp;
+ break;
+ }
+ if (!cap->cc_qblocked) {
+ if (submit != NULL) {
+ /*
+ * We stop on finding another op,
+ * regardless whether its for the same
+ * driver or not. We could keep
+ * searching the queue but it might be
+ * better to just use a per-driver
+ * queue instead.
+ */
+ if (CRYPTO_SESID2HID(submit->crp_sid) == hid)
+ hint = CRYPTO_HINT_MORE;
+ break;
+ } else {
+ submit = crp;
+ if ((submit->crp_flags & CRYPTO_F_BATCH) == 0)
+ break;
+ /* keep scanning for more are q'd */
+ }
+ }
+ }
+ if (submit != NULL) {
+ hid = CRYPTO_SESID2HID(submit->crp_sid);
+ crypto_all_qblocked = 0;
+ list_del(&submit->crp_next);
+ crypto_drivers[hid].cc_unqblocked = 1;
+ cap = crypto_checkdriver(hid);
+ CRYPTO_Q_UNLOCK();
+ KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
+ __func__, __LINE__));
+ result = crypto_invoke(cap, submit, hint);
+ CRYPTO_Q_LOCK();
+ if (result == ERESTART) {
+ /*
+ * The driver ran out of resources, mark the
+ * driver ``blocked'' for cryptop's and put
+ * the request back in the queue. It would
+ * best to put the request back where we got
+ * it but that's hard so for now we put it
+ * at the front. This should be ok; putting
+ * it at the end does not work.
+ */
+ /* XXX validate sid again? */
+ list_add(&submit->crp_next, &crp_q);
+ cryptostats.cs_blocks++;
+ if (crypto_drivers[hid].cc_unqblocked)
+ crypto_drivers[hid].cc_qblocked=0;
+ crypto_drivers[hid].cc_unqblocked=0;
+ }
+ crypto_drivers[hid].cc_unqblocked = 0;
+ }
+
+ crypto_all_kqblocked = !list_empty(&crp_kq);
+
+ /* As above, but for key ops */
+ krp = NULL;
+ list_for_each_entry(krpp, &crp_kq, krp_next) {
+ cap = crypto_checkdriver(krpp->krp_hid);
+ if (cap == NULL || cap->cc_dev == NULL) {
+ /*
+ * Operation needs to be migrated, invalidate
+ * the assigned device so it will reselect a
+ * new one below. Propagate the original
+ * crid selection flags if supplied.
+ */
+ krp->krp_hid = krp->krp_crid &
+ (CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE);
+ if (krp->krp_hid == 0)
+ krp->krp_hid =
+ CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE;
+ break;
+ }
+ if (!cap->cc_kqblocked) {
+ krp = krpp;
+ break;
+ }
+ }
+ if (krp != NULL) {
+ crypto_all_kqblocked = 0;
+ list_del(&krp->krp_next);
+ crypto_drivers[krp->krp_hid].cc_kqblocked = 1;
+ CRYPTO_Q_UNLOCK();
+ result = crypto_kinvoke(krp, krp->krp_hid);
+ CRYPTO_Q_LOCK();
+ if (result == ERESTART) {
+ /*
+ * The driver ran out of resources, mark the
+ * driver ``blocked'' for cryptkop's and put
+ * the request back in the queue. It would
+ * best to put the request back where we got
+ * it but that's hard so for now we put it
+ * at the front. This should be ok; putting
+ * it at the end does not work.
+ */
+ /* XXX validate sid again? */
+ list_add(&krp->krp_next, &crp_kq);
+ cryptostats.cs_kblocks++;
+ } else
+ crypto_drivers[krp->krp_hid].cc_kqblocked = 0;
+ }
+
+ if (submit == NULL && krp == NULL) {
+ /*
+ * Nothing more to be processed. Sleep until we're
+ * woken because there are more ops to process.
+ * This happens either by submission or by a driver
+ * becoming unblocked and notifying us through
+ * crypto_unblock. Note that when we wakeup we
+ * start processing each queue again from the
+ * front. It's not clear that it's important to
+ * preserve this ordering since ops may finish
+ * out of order if dispatched to different devices
+ * and some become blocked while others do not.
+ */
+ dprintk("%s - sleeping (qe=%d qb=%d kqe=%d kqb=%d)\n",
+ __FUNCTION__,
+ list_empty(&crp_q), crypto_all_qblocked,
+ list_empty(&crp_kq), crypto_all_kqblocked);
+ loopcount = 0;
+ CRYPTO_Q_UNLOCK();
+ wait_event_interruptible(cryptoproc_wait,
+ !(list_empty(&crp_q) || crypto_all_qblocked) ||
+ !(list_empty(&crp_kq) || crypto_all_kqblocked) ||
+ kthread_should_stop());
+ if (signal_pending (current)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_lock_irq(¤t->sigmask_lock);
+#endif
+ flush_signals(current);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_unlock_irq(¤t->sigmask_lock);
+#endif
+ }
+ CRYPTO_Q_LOCK();
+ dprintk("%s - awake\n", __FUNCTION__);
+ if (kthread_should_stop())
+ break;
+ cryptostats.cs_intrs++;
+ } else if (loopcount > crypto_max_loopcount) {
+ /*
+ * Give other processes a chance to run if we've
+ * been using the CPU exclusively for a while.
+ */
+ loopcount = 0;
+ CRYPTO_Q_UNLOCK();
+ schedule();
+ CRYPTO_Q_LOCK();
+ }
+ loopcount++;
+ }
+ CRYPTO_Q_UNLOCK();
+ return 0;
+}
+
+/*
+ * Crypto returns thread, does callbacks for processed crypto requests.
+ * Callbacks are done here, rather than in the crypto drivers, because
+ * callbacks typically are expensive and would slow interrupt handling.
+ */
+static int
+crypto_ret_proc(void *arg)
+{
+ struct cryptop *crpt;
+ struct cryptkop *krpt;
+ unsigned long r_flags;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ CRYPTO_RETQ_LOCK();
+ for (;;) {
+ /* Harvest return q's for completed ops */
+ crpt = NULL;
+ if (!list_empty(&crp_ret_q))
+ crpt = list_entry(crp_ret_q.next, typeof(*crpt), crp_next);
+ if (crpt != NULL)
+ list_del(&crpt->crp_next);
+
+ krpt = NULL;
+ if (!list_empty(&crp_ret_kq))
+ krpt = list_entry(crp_ret_kq.next, typeof(*krpt), krp_next);
+ if (krpt != NULL)
+ list_del(&krpt->krp_next);
+
+ if (crpt != NULL || krpt != NULL) {
+ CRYPTO_RETQ_UNLOCK();
+ /*
+ * Run callbacks unlocked.
+ */
+ if (crpt != NULL)
+ crpt->crp_callback(crpt);
+ if (krpt != NULL)
+ krpt->krp_callback(krpt);
+ CRYPTO_RETQ_LOCK();
+ } else {
+ /*
+ * Nothing more to be processed. Sleep until we're
+ * woken because there are more returns to process.
+ */
+ dprintk("%s - sleeping\n", __FUNCTION__);
+ CRYPTO_RETQ_UNLOCK();
+ wait_event_interruptible(cryptoretproc_wait,
+ !list_empty(&crp_ret_q) ||
+ !list_empty(&crp_ret_kq) ||
+ kthread_should_stop());
+ if (signal_pending (current)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_lock_irq(¤t->sigmask_lock);
+#endif
+ flush_signals(current);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_unlock_irq(¤t->sigmask_lock);
+#endif
+ }
+ CRYPTO_RETQ_LOCK();
+ dprintk("%s - awake\n", __FUNCTION__);
+ if (kthread_should_stop()) {
+ dprintk("%s - EXITING!\n", __FUNCTION__);
+ break;
+ }
+ cryptostats.cs_rets++;
+ }
+ }
+ CRYPTO_RETQ_UNLOCK();
+ return 0;
+}
+
+
+#if 0 /* should put this into /proc or something */
+static void
+db_show_drivers(void)
+{
+ int hid;
+
+ db_printf("%12s %4s %4s %8s %2s %2s\n"
+ , "Device"
+ , "Ses"
+ , "Kops"
+ , "Flags"
+ , "QB"
+ , "KB"
+ );
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
+ const struct cryptocap *cap = &crypto_drivers[hid];
+ if (cap->cc_dev == NULL)
+ continue;
+ db_printf("%-12s %4u %4u %08x %2u %2u\n"
+ , device_get_nameunit(cap->cc_dev)
+ , cap->cc_sessions
+ , cap->cc_koperations
+ , cap->cc_flags
+ , cap->cc_qblocked
+ , cap->cc_kqblocked
+ );
+ }
+}
+
+DB_SHOW_COMMAND(crypto, db_show_crypto)
+{
+ struct cryptop *crp;
+
+ db_show_drivers();
+ db_printf("\n");
+
+ db_printf("%4s %8s %4s %4s %4s %4s %8s %8s\n",
+ "HID", "Caps", "Ilen", "Olen", "Etype", "Flags",
+ "Desc", "Callback");
+ TAILQ_FOREACH(crp, &crp_q, crp_next) {
+ db_printf("%4u %08x %4u %4u %4u %04x %8p %8p\n"
+ , (int) CRYPTO_SESID2HID(crp->crp_sid)
+ , (int) CRYPTO_SESID2CAPS(crp->crp_sid)
+ , crp->crp_ilen, crp->crp_olen
+ , crp->crp_etype
+ , crp->crp_flags
+ , crp->crp_desc
+ , crp->crp_callback
+ );
+ }
+ if (!TAILQ_EMPTY(&crp_ret_q)) {
+ db_printf("\n%4s %4s %4s %8s\n",
+ "HID", "Etype", "Flags", "Callback");
+ TAILQ_FOREACH(crp, &crp_ret_q, crp_next) {
+ db_printf("%4u %4u %04x %8p\n"
+ , (int) CRYPTO_SESID2HID(crp->crp_sid)
+ , crp->crp_etype
+ , crp->crp_flags
+ , crp->crp_callback
+ );
+ }
+ }
+}
+
+DB_SHOW_COMMAND(kcrypto, db_show_kcrypto)
+{
+ struct cryptkop *krp;
+
+ db_show_drivers();
+ db_printf("\n");
+
+ db_printf("%4s %5s %4s %4s %8s %4s %8s\n",
+ "Op", "Status", "#IP", "#OP", "CRID", "HID", "Callback");
+ TAILQ_FOREACH(krp, &crp_kq, krp_next) {
+ db_printf("%4u %5u %4u %4u %08x %4u %8p\n"
+ , krp->krp_op
+ , krp->krp_status
+ , krp->krp_iparams, krp->krp_oparams
+ , krp->krp_crid, krp->krp_hid
+ , krp->krp_callback
+ );
+ }
+ if (!TAILQ_EMPTY(&crp_ret_q)) {
+ db_printf("%4s %5s %8s %4s %8s\n",
+ "Op", "Status", "CRID", "HID", "Callback");
+ TAILQ_FOREACH(krp, &crp_ret_kq, krp_next) {
+ db_printf("%4u %5u %08x %4u %8p\n"
+ , krp->krp_op
+ , krp->krp_status
+ , krp->krp_crid, krp->krp_hid
+ , krp->krp_callback
+ );
+ }
+ }
+}
+#endif
+
+
+static int
+crypto_init(void)
+{
+ int error;
+ unsigned long cpu;
+
+ dprintk("%s(%p)\n", __FUNCTION__, (void *) crypto_init);
+
+ if (crypto_initted)
+ return 0;
+ crypto_initted = 1;
+
+ spin_lock_init(&crypto_drivers_lock);
+ spin_lock_init(&crypto_q_lock);
+ spin_lock_init(&crypto_ret_q_lock);
+
+ cryptop_zone = kmem_cache_create("cryptop", sizeof(struct cryptop),
+ 0, SLAB_HWCACHE_ALIGN, NULL
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ , NULL
+#endif
+ );
+
+ cryptodesc_zone = kmem_cache_create("cryptodesc", sizeof(struct cryptodesc),
+ 0, SLAB_HWCACHE_ALIGN, NULL
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ , NULL
+#endif
+ );
+
+ if (cryptodesc_zone == NULL || cryptop_zone == NULL) {
+ printk("crypto: crypto_init cannot setup crypto zones\n");
+ error = ENOMEM;
+ goto bad;
+ }
+
+ crypto_drivers_num = CRYPTO_DRIVERS_INITIAL;
+ crypto_drivers = kmalloc(crypto_drivers_num * sizeof(struct cryptocap),
+ GFP_KERNEL);
+ if (crypto_drivers == NULL) {
+ printk("crypto: crypto_init cannot setup crypto drivers\n");
+ error = ENOMEM;
+ goto bad;
+ }
+
+ memset(crypto_drivers, 0, crypto_drivers_num * sizeof(struct cryptocap));
+
+ ocf_for_each_cpu(cpu) {
+ cryptoproc[cpu] = kthread_create(crypto_proc, (void *) cpu,
+ "ocf_%d", (int) cpu);
+ if (IS_ERR(cryptoproc[cpu])) {
+ error = PTR_ERR(cryptoproc[cpu]);
+ printk("crypto: crypto_init cannot start crypto thread; error %d",
+ error);
+ goto bad;
+ }
+ kthread_bind(cryptoproc[cpu], cpu);
+ wake_up_process(cryptoproc[cpu]);
+
+ cryptoretproc[cpu] = kthread_create(crypto_ret_proc, (void *) cpu,
+ "ocf_ret_%d", (int) cpu);
+ if (IS_ERR(cryptoretproc[cpu])) {
+ error = PTR_ERR(cryptoretproc[cpu]);
+ printk("crypto: crypto_init cannot start cryptoret thread; error %d",
+ error);
+ goto bad;
+ }
+ kthread_bind(cryptoretproc[cpu], cpu);
+ wake_up_process(cryptoretproc[cpu]);
+ }
+
+ return 0;
+bad:
+ crypto_exit();
+ return error;
+}
+
+
+static void
+crypto_exit(void)
+{
+ int cpu;
+
+ dprintk("%s()\n", __FUNCTION__);
+
+ /*
+ * Terminate any crypto threads.
+ */
+ ocf_for_each_cpu(cpu) {
+ kthread_stop(cryptoproc[cpu]);
+ kthread_stop(cryptoretproc[cpu]);
+ }
+
+ /*
+ * Reclaim dynamically allocated resources.
+ */
+ if (crypto_drivers != NULL)
+ kfree(crypto_drivers);
+
+ if (cryptodesc_zone != NULL)
+ kmem_cache_destroy(cryptodesc_zone);
+ if (cryptop_zone != NULL)
+ kmem_cache_destroy(cryptop_zone);
+}
+
+
+EXPORT_SYMBOL(crypto_newsession);
+EXPORT_SYMBOL(crypto_freesession);
+EXPORT_SYMBOL(crypto_get_driverid);
+EXPORT_SYMBOL(crypto_kregister);
+EXPORT_SYMBOL(crypto_register);
+EXPORT_SYMBOL(crypto_unregister);
+EXPORT_SYMBOL(crypto_unregister_all);
+EXPORT_SYMBOL(crypto_unblock);
+EXPORT_SYMBOL(crypto_dispatch);
+EXPORT_SYMBOL(crypto_kdispatch);
+EXPORT_SYMBOL(crypto_freereq);
+EXPORT_SYMBOL(crypto_getreq);
+EXPORT_SYMBOL(crypto_done);
+EXPORT_SYMBOL(crypto_kdone);
+EXPORT_SYMBOL(crypto_getfeat);
+EXPORT_SYMBOL(crypto_userasymcrypto);
+EXPORT_SYMBOL(crypto_getcaps);
+EXPORT_SYMBOL(crypto_find_driver);
+EXPORT_SYMBOL(crypto_find_device_byhid);
+
+module_init(crypto_init);
+module_exit(crypto_exit);
+
+MODULE_LICENSE("BSD");
+MODULE_AUTHOR("David McCullough <david_mccullough@mcafee.com>");
+MODULE_DESCRIPTION("OCF (OpenBSD Cryptographic Framework)");
diff --git a/crypto/ocf/cryptodev.c b/crypto/ocf/cryptodev.c
new file mode 100644
index 0000000..2ee3618
--- /dev/null
+++ b/crypto/ocf/cryptodev.c
@@ -0,0 +1,1069 @@
+/* $OpenBSD: cryptodev.c,v 1.52 2002/06/19 07:22:46 deraadt Exp $ */
+
+/*-
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2006-2010 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * Copyright (c) 2001 Theo de Raadt
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ *
+__FBSDID("$FreeBSD: src/sys/opencrypto/cryptodev.c,v 1.34 2007/05/09 19:37:02 gnn Exp $");
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/file.h>
+#include <linux/mount.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+
+#include <cryptodev.h>
+#include <uio.h>
+
+extern asmlinkage long sys_dup(unsigned int fildes);
+
+#define debug cryptodev_debug
+int cryptodev_debug = 0;
+module_param(cryptodev_debug, int, 0644);
+MODULE_PARM_DESC(cryptodev_debug, "Enable cryptodev debug");
+
+struct csession_info {
+ u_int16_t blocksize;
+ u_int16_t minkey, maxkey;
+
+ u_int16_t keysize;
+ /* u_int16_t hashsize; */
+ u_int16_t authsize;
+ u_int16_t authkey;
+ /* u_int16_t ctxsize; */
+};
+
+struct csession {
+ struct list_head list;
+ u_int64_t sid;
+ u_int32_t ses;
+
+ wait_queue_head_t waitq;
+
+ u_int32_t cipher;
+
+ u_int32_t mac;
+
+ caddr_t key;
+ int keylen;
+ u_char tmp_iv[EALG_MAX_BLOCK_LEN];
+
+ caddr_t mackey;
+ int mackeylen;
+
+ struct csession_info info;
+
+ struct iovec iovec;
+ struct uio uio;
+ int error;
+};
+
+struct fcrypt {
+ struct list_head csessions;
+ int sesn;
+};
+
+static struct csession *csefind(struct fcrypt *, u_int);
+static int csedelete(struct fcrypt *, struct csession *);
+static struct csession *cseadd(struct fcrypt *, struct csession *);
+static struct csession *csecreate(struct fcrypt *, u_int64_t,
+ struct cryptoini *crie, struct cryptoini *cria, struct csession_info *);
+static int csefree(struct csession *);
+
+static int cryptodev_op(struct csession *, struct crypt_op *);
+static int cryptodev_key(struct crypt_kop *);
+static int cryptodev_find(struct crypt_find_op *);
+
+static int cryptodev_cb(void *);
+static int cryptodev_open(struct inode *inode, struct file *filp);
+
+/*
+ * Check a crypto identifier to see if it requested
+ * a valid crid and it's capabilities match.
+ */
+static int
+checkcrid(int crid)
+{
+ int hid = crid & ~(CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE);
+ int typ = crid & (CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE);
+ int caps = 0;
+
+ /* if the user hasn't selected a driver, then just call newsession */
+ if (hid == 0 && typ != 0)
+ return 0;
+
+ caps = crypto_getcaps(hid);
+
+ /* didn't find anything with capabilities */
+ if (caps == 0) {
+ dprintk("%s: hid=%x typ=%x not matched\n", __FUNCTION__, hid, typ);
+ return EINVAL;
+ }
+
+ /* the user didn't specify SW or HW, so the driver is ok */
+ if (typ == 0)
+ return 0;
+
+ /* if the type specified didn't match */
+ if (typ != (caps & (CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE))) {
+ dprintk("%s: hid=%x typ=%x caps=%x not matched\n", __FUNCTION__,
+ hid, typ, caps);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+cryptodev_op(struct csession *cse, struct crypt_op *cop)
+{
+ struct cryptop *crp = NULL;
+ struct cryptodesc *crde = NULL, *crda = NULL;
+ int error = 0;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (cop->len > CRYPTO_MAX_DATA_LEN) {
+ dprintk("%s: %d > %d\n", __FUNCTION__, cop->len, CRYPTO_MAX_DATA_LEN);
+ return (E2BIG);
+ }
+
+ if (cse->info.blocksize && (cop->len % cse->info.blocksize) != 0) {
+ dprintk("%s: blocksize=%d len=%d\n", __FUNCTION__, cse->info.blocksize,
+ cop->len);
+ return (EINVAL);
+ }
+
+ cse->uio.uio_iov = &cse->iovec;
+ cse->uio.uio_iovcnt = 1;
+ cse->uio.uio_offset = 0;
+#if 0
+ cse->uio.uio_resid = cop->len;
+ cse->uio.uio_segflg = UIO_SYSSPACE;
+ cse->uio.uio_rw = UIO_WRITE;
+ cse->uio.uio_td = td;
+#endif
+ cse->uio.uio_iov[0].iov_len = cop->len;
+ if (cse->info.authsize)
+ cse->uio.uio_iov[0].iov_len += cse->info.authsize;
+ cse->uio.uio_iov[0].iov_base = kmalloc(cse->uio.uio_iov[0].iov_len,
+ GFP_KERNEL);
+
+ if (cse->uio.uio_iov[0].iov_base == NULL) {
+ dprintk("%s: iov_base kmalloc(%d) failed\n", __FUNCTION__,
+ (int)cse->uio.uio_iov[0].iov_len);
+ return (ENOMEM);
+ }
+
+ crp = crypto_getreq((cse->info.blocksize != 0) + (cse->info.authsize != 0));
+ if (crp == NULL) {
+ dprintk("%s: ENOMEM\n", __FUNCTION__);
+ error = ENOMEM;
+ goto bail;
+ }
+
+ if (cse->info.authsize && cse->info.blocksize) {
+ if (cop->op == COP_ENCRYPT) {
+ crde = crp->crp_desc;
+ crda = crde->crd_next;
+ } else {
+ crda = crp->crp_desc;
+ crde = crda->crd_next;
+ }
+ } else if (cse->info.authsize) {
+ crda = crp->crp_desc;
+ } else if (cse->info.blocksize) {
+ crde = crp->crp_desc;
+ } else {
+ dprintk("%s: bad request\n", __FUNCTION__);
+ error = EINVAL;
+ goto bail;
+ }
+
+ if ((error = copy_from_user(cse->uio.uio_iov[0].iov_base, cop->src,
+ cop->len))) {
+ dprintk("%s: bad copy\n", __FUNCTION__);
+ goto bail;
+ }
+
+ if (crda) {
+ crda->crd_skip = 0;
+ crda->crd_len = cop->len;
+ crda->crd_inject = cop->len;
+
+ crda->crd_alg = cse->mac;
+ crda->crd_key = cse->mackey;
+ crda->crd_klen = cse->mackeylen * 8;
+ }
+
+ if (crde) {
+ if (cop->op == COP_ENCRYPT)
+ crde->crd_flags |= CRD_F_ENCRYPT;
+ else
+ crde->crd_flags &= ~CRD_F_ENCRYPT;
+ crde->crd_len = cop->len;
+ crde->crd_inject = 0;
+
+ crde->crd_alg = cse->cipher;
+ crde->crd_key = cse->key;
+ crde->crd_klen = cse->keylen * 8;
+ }
+
+ crp->crp_ilen = cse->uio.uio_iov[0].iov_len;
+ crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIMM
+ | (cop->flags & COP_F_BATCH);
+ crp->crp_buf = (caddr_t)&cse->uio;
+ crp->crp_callback = (int (*) (struct cryptop *)) cryptodev_cb;
+ crp->crp_sid = cse->sid;
+ crp->crp_opaque = (void *)cse;
+
+ if (cop->iv) {
+ if (crde == NULL) {
+ error = EINVAL;
+ dprintk("%s no crde\n", __FUNCTION__);
+ goto bail;
+ }
+ if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */
+ error = EINVAL;
+ dprintk("%s arc4 with IV\n", __FUNCTION__);
+ goto bail;
+ }
+ if ((error = copy_from_user(cse->tmp_iv, cop->iv,
+ cse->info.blocksize))) {
+ dprintk("%s bad iv copy\n", __FUNCTION__);
+ goto bail;
+ }
+ memcpy(crde->crd_iv, cse->tmp_iv, cse->info.blocksize);
+ crde->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT;
+ crde->crd_skip = 0;
+ } else if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */
+ crde->crd_skip = 0;
+ } else if (crde) {
+ crde->crd_flags |= CRD_F_IV_PRESENT;
+ crde->crd_skip = cse->info.blocksize;
+ crde->crd_len -= cse->info.blocksize;
+ }
+
+ if (cop->mac && crda == NULL) {
+ error = EINVAL;
+ dprintk("%s no crda\n", __FUNCTION__);
+ goto bail;
+ }
+
+ /*
+ * Let the dispatch run unlocked, then, interlock against the
+ * callback before checking if the operation completed and going
+ * to sleep. This insures drivers don't inherit our lock which
+ * results in a lock order reversal between crypto_dispatch forced
+ * entry and the crypto_done callback into us.
+ */
+ error = crypto_dispatch(crp);
+ if (error) {
+ dprintk("%s error in crypto_dispatch\n", __FUNCTION__);
+ goto bail;
+ }
+
+ dprintk("%s about to WAIT\n", __FUNCTION__);
+ /*
+ * we really need to wait for driver to complete to maintain
+ * state, luckily interrupts will be remembered
+ */
+ do {
+ error = wait_event_interruptible(crp->crp_waitq,
+ ((crp->crp_flags & CRYPTO_F_DONE) != 0));
+ /*
+ * we can't break out of this loop or we will leave behind
+ * a huge mess, however, staying here means if your driver
+ * is broken user applications can hang and not be killed.
+ * The solution, fix your driver :-)
+ */
+ if (error) {
+ schedule();
+ error = 0;
+ }
+ } while ((crp->crp_flags & CRYPTO_F_DONE) == 0);
+ dprintk("%s finished WAITING error=%d\n", __FUNCTION__, error);
+
+ if (crp->crp_etype != 0) {
+ error = crp->crp_etype;
+ dprintk("%s error in crp processing\n", __FUNCTION__);
+ goto bail;
+ }
+
+ if (cse->error) {
+ error = cse->error;
+ dprintk("%s error in cse processing\n", __FUNCTION__);
+ goto bail;
+ }
+
+ if (cop->dst && (error = copy_to_user(cop->dst,
+ cse->uio.uio_iov[0].iov_base, cop->len))) {
+ dprintk("%s bad dst copy\n", __FUNCTION__);
+ goto bail;
+ }
+
+ if (cop->mac &&
+ (error=copy_to_user(cop->mac,
+ (caddr_t)cse->uio.uio_iov[0].iov_base + cop->len,
+ cse->info.authsize))) {
+ dprintk("%s bad mac copy\n", __FUNCTION__);
+ goto bail;
+ }
+
+bail:
+ if (crp)
+ crypto_freereq(crp);
+ if (cse->uio.uio_iov[0].iov_base)
+ kfree(cse->uio.uio_iov[0].iov_base);
+
+ return (error);
+}
+
+static int
+cryptodev_cb(void *op)
+{
+ struct cryptop *crp = (struct cryptop *) op;
+ struct csession *cse = (struct csession *)crp->crp_opaque;
+ int error;
+
+ dprintk("%s()\n", __FUNCTION__);
+ error = crp->crp_etype;
+ if (error == EAGAIN) {
+ crp->crp_flags &= ~CRYPTO_F_DONE;
+#ifdef NOTYET
+ /*
+ * DAVIDM I am fairly sure that we should turn this into a batch
+ * request to stop bad karma/lockup, revisit
+ */
+ crp->crp_flags |= CRYPTO_F_BATCH;
+#endif
+ return crypto_dispatch(crp);
+ }
+ if (error != 0 || (crp->crp_flags & CRYPTO_F_DONE)) {
+ cse->error = error;
+ wake_up_interruptible(&crp->crp_waitq);
+ }
+ return (0);
+}
+
+static int
+cryptodevkey_cb(void *op)
+{
+ struct cryptkop *krp = (struct cryptkop *) op;
+ dprintk("%s()\n", __FUNCTION__);
+ wake_up_interruptible(&krp->krp_waitq);
+ return (0);
+}
+
+static int
+cryptodev_key(struct crypt_kop *kop)
+{
+ struct cryptkop *krp = NULL;
+ int error = EINVAL;
+ int in, out, size, i;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (kop->crk_iparams + kop->crk_oparams > CRK_MAXPARAM) {
+ dprintk("%s params too big\n", __FUNCTION__);
+ return (EFBIG);
+ }
+
+ in = kop->crk_iparams;
+ out = kop->crk_oparams;
+ switch (kop->crk_op) {
+ case CRK_MOD_EXP:
+ if (in == 3 && out == 1)
+ break;
+ return (EINVAL);
+ case CRK_MOD_EXP_CRT:
+ if (in == 6 && out == 1)
+ break;
+ return (EINVAL);
+ case CRK_DSA_SIGN:
+ if (in == 5 && out == 2)
+ break;
+ return (EINVAL);
+ case CRK_DSA_VERIFY:
+ if (in == 7 && out == 0)
+ break;
+ return (EINVAL);
+ case CRK_DH_COMPUTE_KEY:
+ if (in == 3 && out == 1)
+ break;
+ return (EINVAL);
+ default:
+ return (EINVAL);
+ }
+
+ krp = (struct cryptkop *)kmalloc(sizeof *krp, GFP_KERNEL);
+ if (!krp)
+ return (ENOMEM);
+ bzero(krp, sizeof *krp);
+ krp->krp_op = kop->crk_op;
+ krp->krp_status = kop->crk_status;
+ krp->krp_iparams = kop->crk_iparams;
+ krp->krp_oparams = kop->crk_oparams;
+ krp->krp_crid = kop->crk_crid;
+ krp->krp_status = 0;
+ krp->krp_flags = CRYPTO_KF_CBIMM;
+ krp->krp_callback = (int (*) (struct cryptkop *)) cryptodevkey_cb;
+ init_waitqueue_head(&krp->krp_waitq);
+
+ for (i = 0; i < CRK_MAXPARAM; i++)
+ krp->krp_param[i].crp_nbits = kop->crk_param[i].crp_nbits;
+ for (i = 0; i < krp->krp_iparams + krp->krp_oparams; i++) {
+ size = (krp->krp_param[i].crp_nbits + 7) / 8;
+ if (size == 0)
+ continue;
+ krp->krp_param[i].crp_p = (caddr_t) kmalloc(size, GFP_KERNEL);
+ if (i >= krp->krp_iparams)
+ continue;
+ error = copy_from_user(krp->krp_param[i].crp_p,
+ kop->crk_param[i].crp_p, size);
+ if (error)
+ goto fail;
+ }
+
+ error = crypto_kdispatch(krp);
+ if (error)
+ goto fail;
+
+ do {
+ error = wait_event_interruptible(krp->krp_waitq,
+ ((krp->krp_flags & CRYPTO_KF_DONE) != 0));
+ /*
+ * we can't break out of this loop or we will leave behind
+ * a huge mess, however, staying here means if your driver
+ * is broken user applications can hang and not be killed.
+ * The solution, fix your driver :-)
+ */
+ if (error) {
+ schedule();
+ error = 0;
+ }
+ } while ((krp->krp_flags & CRYPTO_KF_DONE) == 0);
+
+ dprintk("%s finished WAITING error=%d\n", __FUNCTION__, error);
+
+ kop->crk_crid = krp->krp_crid; /* device that did the work */
+ if (krp->krp_status != 0) {
+ error = krp->krp_status;
+ goto fail;
+ }
+
+ for (i = krp->krp_iparams; i < krp->krp_iparams + krp->krp_oparams; i++) {
+ size = (krp->krp_param[i].crp_nbits + 7) / 8;
+ if (size == 0)
+ continue;
+ error = copy_to_user(kop->crk_param[i].crp_p, krp->krp_param[i].crp_p,
+ size);
+ if (error)
+ goto fail;
+ }
+
+fail:
+ if (krp) {
+ kop->crk_status = krp->krp_status;
+ for (i = 0; i < CRK_MAXPARAM; i++) {
+ if (krp->krp_param[i].crp_p)
+ kfree(krp->krp_param[i].crp_p);
+ }
+ kfree(krp);
+ }
+ return (error);
+}
+
+static int
+cryptodev_find(struct crypt_find_op *find)
+{
+ device_t dev;
+
+ if (find->crid != -1) {
+ dev = crypto_find_device_byhid(find->crid);
+ if (dev == NULL)
+ return (ENOENT);
+ strlcpy(find->name, device_get_nameunit(dev),
+ sizeof(find->name));
+ } else {
+ find->crid = crypto_find_driver(find->name);
+ if (find->crid == -1)
+ return (ENOENT);
+ }
+ return (0);
+}
+
+static struct csession *
+csefind(struct fcrypt *fcr, u_int ses)
+{
+ struct csession *cse;
+
+ dprintk("%s()\n", __FUNCTION__);
+ list_for_each_entry(cse, &fcr->csessions, list)
+ if (cse->ses == ses)
+ return (cse);
+ return (NULL);
+}
+
+static int
+csedelete(struct fcrypt *fcr, struct csession *cse_del)
+{
+ struct csession *cse;
+
+ dprintk("%s()\n", __FUNCTION__);
+ list_for_each_entry(cse, &fcr->csessions, list) {
+ if (cse == cse_del) {
+ list_del(&cse->list);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static struct csession *
+cseadd(struct fcrypt *fcr, struct csession *cse)
+{
+ dprintk("%s()\n", __FUNCTION__);
+ list_add_tail(&cse->list, &fcr->csessions);
+ cse->ses = fcr->sesn++;
+ return (cse);
+}
+
+static struct csession *
+csecreate(struct fcrypt *fcr, u_int64_t sid, struct cryptoini *crie,
+ struct cryptoini *cria, struct csession_info *info)
+{
+ struct csession *cse;
+
+ dprintk("%s()\n", __FUNCTION__);
+ cse = (struct csession *) kmalloc(sizeof(struct csession), GFP_KERNEL);
+ if (cse == NULL)
+ return NULL;
+ memset(cse, 0, sizeof(struct csession));
+
+ INIT_LIST_HEAD(&cse->list);
+ init_waitqueue_head(&cse->waitq);
+
+ cse->key = crie->cri_key;
+ cse->keylen = crie->cri_klen/8;
+ cse->mackey = cria->cri_key;
+ cse->mackeylen = cria->cri_klen/8;
+ cse->sid = sid;
+ cse->cipher = crie->cri_alg;
+ cse->mac = cria->cri_alg;
+ cse->info = *info;
+ cseadd(fcr, cse);
+ return (cse);
+}
+
+static int
+csefree(struct csession *cse)
+{
+ int error;
+
+ dprintk("%s()\n", __FUNCTION__);
+ error = crypto_freesession(cse->sid);
+ if (cse->key)
+ kfree(cse->key);
+ if (cse->mackey)
+ kfree(cse->mackey);
+ kfree(cse);
+ return(error);
+}
+
+static int
+cryptodev_ioctl(
+ struct inode *inode,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct cryptoini cria, crie;
+ struct fcrypt *fcr = filp->private_data;
+ struct csession *cse;
+ struct csession_info info;
+ struct session2_op sop;
+ struct crypt_op cop;
+ struct crypt_kop kop;
+ struct crypt_find_op fop;
+ u_int64_t sid;
+ u_int32_t ses = 0;
+ int feat, fd, error = 0, crid;
+ mm_segment_t fs;
+
+ dprintk("%s(cmd=%x arg=%lx)\n", __FUNCTION__, cmd, arg);
+
+ switch (cmd) {
+
+ case CRIOGET: {
+ dprintk("%s(CRIOGET)\n", __FUNCTION__);
+ fs = get_fs();
+ set_fs(get_ds());
+ for (fd = 0; fd < files_fdtable(current->files)->max_fds; fd++)
+ if (files_fdtable(current->files)->fd[fd] == filp)
+ break;
+ fd = sys_dup(fd);
+ set_fs(fs);
+ put_user(fd, (int *) arg);
+ return IS_ERR_VALUE(fd) ? fd : 0;
+ }
+
+#define CIOCGSESSSTR (cmd == CIOCGSESSION ? "CIOCGSESSION" : "CIOCGSESSION2")
+ case CIOCGSESSION:
+ case CIOCGSESSION2:
+ dprintk("%s(%s)\n", __FUNCTION__, CIOCGSESSSTR);
+ memset(&crie, 0, sizeof(crie));
+ memset(&cria, 0, sizeof(cria));
+ memset(&info, 0, sizeof(info));
+ memset(&sop, 0, sizeof(sop));
+
+ if (copy_from_user(&sop, (void*)arg, (cmd == CIOCGSESSION) ?
+ sizeof(struct session_op) : sizeof(sop))) {
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EFAULT;
+ goto bail;
+ }
+
+ switch (sop.cipher) {
+ case 0:
+ dprintk("%s(%s) - no cipher\n", __FUNCTION__, CIOCGSESSSTR);
+ break;
+ case CRYPTO_NULL_CBC:
+ info.blocksize = NULL_BLOCK_LEN;
+ info.minkey = NULL_MIN_KEY_LEN;
+ info.maxkey = NULL_MAX_KEY_LEN;
+ break;
+ case CRYPTO_DES_CBC:
+ info.blocksize = DES_BLOCK_LEN;
+ info.minkey = DES_MIN_KEY_LEN;
+ info.maxkey = DES_MAX_KEY_LEN;
+ break;
+ case CRYPTO_3DES_CBC:
+ info.blocksize = DES3_BLOCK_LEN;
+ info.minkey = DES3_MIN_KEY_LEN;
+ info.maxkey = DES3_MAX_KEY_LEN;
+ break;
+ case CRYPTO_BLF_CBC:
+ info.blocksize = BLOWFISH_BLOCK_LEN;
+ info.minkey = BLOWFISH_MIN_KEY_LEN;
+ info.maxkey = BLOWFISH_MAX_KEY_LEN;
+ break;
+ case CRYPTO_CAST_CBC:
+ info.blocksize = CAST128_BLOCK_LEN;
+ info.minkey = CAST128_MIN_KEY_LEN;
+ info.maxkey = CAST128_MAX_KEY_LEN;
+ break;
+ case CRYPTO_SKIPJACK_CBC:
+ info.blocksize = SKIPJACK_BLOCK_LEN;
+ info.minkey = SKIPJACK_MIN_KEY_LEN;
+ info.maxkey = SKIPJACK_MAX_KEY_LEN;
+ break;
+ case CRYPTO_AES_CBC:
+ info.blocksize = AES_BLOCK_LEN;
+ info.minkey = AES_MIN_KEY_LEN;
+ info.maxkey = AES_MAX_KEY_LEN;
+ break;
+ case CRYPTO_ARC4:
+ info.blocksize = ARC4_BLOCK_LEN;
+ info.minkey = ARC4_MIN_KEY_LEN;
+ info.maxkey = ARC4_MAX_KEY_LEN;
+ break;
+ case CRYPTO_CAMELLIA_CBC:
+ info.blocksize = CAMELLIA_BLOCK_LEN;
+ info.minkey = CAMELLIA_MIN_KEY_LEN;
+ info.maxkey = CAMELLIA_MAX_KEY_LEN;
+ break;
+ default:
+ dprintk("%s(%s) - bad cipher\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EINVAL;
+ goto bail;
+ }
+
+ switch (sop.mac) {
+ case 0:
+ dprintk("%s(%s) - no mac\n", __FUNCTION__, CIOCGSESSSTR);
+ break;
+ case CRYPTO_NULL_HMAC:
+ info.authsize = NULL_HASH_LEN;
+ break;
+ case CRYPTO_MD5:
+ info.authsize = MD5_HASH_LEN;
+ break;
+ case CRYPTO_SHA1:
+ info.authsize = SHA1_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_256:
+ info.authsize = SHA2_256_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_384:
+ info.authsize = SHA2_384_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_512:
+ info.authsize = SHA2_512_HASH_LEN;
+ break;
+ case CRYPTO_RIPEMD160:
+ info.authsize = RIPEMD160_HASH_LEN;
+ break;
+ case CRYPTO_MD5_HMAC:
+ info.authsize = MD5_HASH_LEN;
+ info.authkey = 16;
+ break;
+ case CRYPTO_SHA1_HMAC:
+ info.authsize = SHA1_HASH_LEN;
+ info.authkey = 20;
+ break;
+ case CRYPTO_SHA2_256_HMAC:
+ info.authsize = SHA2_256_HASH_LEN;
+ info.authkey = 32;
+ break;
+ case CRYPTO_SHA2_384_HMAC:
+ info.authsize = SHA2_384_HASH_LEN;
+ info.authkey = 48;
+ break;
+ case CRYPTO_SHA2_512_HMAC:
+ info.authsize = SHA2_512_HASH_LEN;
+ info.authkey = 64;
+ break;
+ case CRYPTO_RIPEMD160_HMAC:
+ info.authsize = RIPEMD160_HASH_LEN;
+ info.authkey = 20;
+ break;
+ default:
+ dprintk("%s(%s) - bad mac\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EINVAL;
+ goto bail;
+ }
+
+ if (info.blocksize) {
+ crie.cri_alg = sop.cipher;
+ crie.cri_klen = sop.keylen * 8;
+ if ((info.maxkey && sop.keylen > info.maxkey) ||
+ sop.keylen < info.minkey) {
+ dprintk("%s(%s) - bad key\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EINVAL;
+ goto bail;
+ }
+
+ crie.cri_key = (u_int8_t *) kmalloc(crie.cri_klen/8+1, GFP_KERNEL);
+ if (copy_from_user(crie.cri_key, sop.key,
+ crie.cri_klen/8)) {
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EFAULT;
+ goto bail;
+ }
+ if (info.authsize)
+ crie.cri_next = &cria;
+ }
+
+ if (info.authsize) {
+ cria.cri_alg = sop.mac;
+ cria.cri_klen = sop.mackeylen * 8;
+ if (info.authkey && sop.mackeylen != info.authkey) {
+ dprintk("%s(%s) - mackeylen %d != %d\n", __FUNCTION__,
+ CIOCGSESSSTR, sop.mackeylen, info.authkey);
+ error = EINVAL;
+ goto bail;
+ }
+
+ if (cria.cri_klen) {
+ cria.cri_key = (u_int8_t *) kmalloc(cria.cri_klen/8,GFP_KERNEL);
+ if (copy_from_user(cria.cri_key, sop.mackey,
+ cria.cri_klen / 8)) {
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EFAULT;
+ goto bail;
+ }
+ }
+ }
+
+ /* NB: CIOGSESSION2 has the crid */
+ if (cmd == CIOCGSESSION2) {
+ crid = sop.crid;
+ error = checkcrid(crid);
+ if (error) {
+ dprintk("%s(%s) - checkcrid %x\n", __FUNCTION__,
+ CIOCGSESSSTR, error);
+ goto bail;
+ }
+ } else {
+ /* allow either HW or SW to be used */
+ crid = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
+ }
+ error = crypto_newsession(&sid, (info.blocksize ? &crie : &cria), crid);
+ if (error) {
+ dprintk("%s(%s) - newsession %d\n",__FUNCTION__,CIOCGSESSSTR,error);
+ goto bail;
+ }
+
+ cse = csecreate(fcr, sid, &crie, &cria, &info);
+ if (cse == NULL) {
+ crypto_freesession(sid);
+ error = EINVAL;
+ dprintk("%s(%s) - csecreate failed\n", __FUNCTION__, CIOCGSESSSTR);
+ goto bail;
+ }
+ sop.ses = cse->ses;
+
+ if (cmd == CIOCGSESSION2) {
+ /* return hardware/driver id */
+ sop.crid = CRYPTO_SESID2HID(cse->sid);
+ }
+
+ if (copy_to_user((void*)arg, &sop, (cmd == CIOCGSESSION) ?
+ sizeof(struct session_op) : sizeof(sop))) {
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EFAULT;
+ }
+bail:
+ if (error) {
+ dprintk("%s(%s) - bail %d\n", __FUNCTION__, CIOCGSESSSTR, error);
+ if (crie.cri_key)
+ kfree(crie.cri_key);
+ if (cria.cri_key)
+ kfree(cria.cri_key);
+ }
+ break;
+ case CIOCFSESSION:
+ dprintk("%s(CIOCFSESSION)\n", __FUNCTION__);
+ get_user(ses, (uint32_t*)arg);
+ cse = csefind(fcr, ses);
+ if (cse == NULL) {
+ error = EINVAL;
+ dprintk("%s(CIOCFSESSION) - Fail %d\n", __FUNCTION__, error);
+ break;
+ }
+ csedelete(fcr, cse);
+ error = csefree(cse);
+ break;
+ case CIOCCRYPT:
+ dprintk("%s(CIOCCRYPT)\n", __FUNCTION__);
+ if(copy_from_user(&cop, (void*)arg, sizeof(cop))) {
+ dprintk("%s(CIOCCRYPT) - bad copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ cse = csefind(fcr, cop.ses);
+ if (cse == NULL) {
+ error = EINVAL;
+ dprintk("%s(CIOCCRYPT) - Fail %d\n", __FUNCTION__, error);
+ break;
+ }
+ error = cryptodev_op(cse, &cop);
+ if(copy_to_user((void*)arg, &cop, sizeof(cop))) {
+ dprintk("%s(CIOCCRYPT) - bad return copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ break;
+ case CIOCKEY:
+ case CIOCKEY2:
+ dprintk("%s(CIOCKEY)\n", __FUNCTION__);
+ if (!crypto_userasymcrypto)
+ return (EPERM); /* XXX compat? */
+ if(copy_from_user(&kop, (void*)arg, sizeof(kop))) {
+ dprintk("%s(CIOCKEY) - bad copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ if (cmd == CIOCKEY) {
+ /* NB: crypto core enforces s/w driver use */
+ kop.crk_crid =
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
+ }
+ error = cryptodev_key(&kop);
+ if(copy_to_user((void*)arg, &kop, sizeof(kop))) {
+ dprintk("%s(CIOCGKEY) - bad return copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ break;
+ case CIOCASYMFEAT:
+ dprintk("%s(CIOCASYMFEAT)\n", __FUNCTION__);
+ if (!crypto_userasymcrypto) {
+ /*
+ * NB: if user asym crypto operations are
+ * not permitted return "no algorithms"
+ * so well-behaved applications will just
+ * fallback to doing them in software.
+ */
+ feat = 0;
+ } else
+ error = crypto_getfeat(&feat);
+ if (!error) {
+ error = copy_to_user((void*)arg, &feat, sizeof(feat));
+ }
+ break;
+ case CIOCFINDDEV:
+ if (copy_from_user(&fop, (void*)arg, sizeof(fop))) {
+ dprintk("%s(CIOCFINDDEV) - bad copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ error = cryptodev_find(&fop);
+ if (copy_to_user((void*)arg, &fop, sizeof(fop))) {
+ dprintk("%s(CIOCFINDDEV) - bad return copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ break;
+ default:
+ dprintk("%s(unknown ioctl 0x%x)\n", __FUNCTION__, cmd);
+ error = EINVAL;
+ break;
+ }
+ return(-error);
+}
+
+#ifdef HAVE_UNLOCKED_IOCTL
+static long
+cryptodev_unlocked_ioctl(
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ return cryptodev_ioctl(NULL, filp, cmd, arg);
+}
+#endif
+
+static int
+cryptodev_open(struct inode *inode, struct file *filp)
+{
+ struct fcrypt *fcr;
+
+ dprintk("%s()\n", __FUNCTION__);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+ /*
+ * on 2.6.35 private_data points to a miscdevice structure, we override
+ * it, which is currently safe to do.
+ */
+ if (filp->private_data) {
+ printk("cryptodev: Private data already exists - %p!\n", filp->private_data);
+ return(-ENODEV);
+ }
+#endif
+
+ fcr = kmalloc(sizeof(*fcr), GFP_KERNEL);
+ if (!fcr) {
+ dprintk("%s() - malloc failed\n", __FUNCTION__);
+ return(-ENOMEM);
+ }
+ memset(fcr, 0, sizeof(*fcr));
+
+ INIT_LIST_HEAD(&fcr->csessions);
+ filp->private_data = fcr;
+ return(0);
+}
+
+static int
+cryptodev_release(struct inode *inode, struct file *filp)
+{
+ struct fcrypt *fcr = filp->private_data;
+ struct csession *cse, *tmp;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (!filp) {
+ printk("cryptodev: No private data on release\n");
+ return(0);
+ }
+
+ list_for_each_entry_safe(cse, tmp, &fcr->csessions, list) {
+ list_del(&cse->list);
+ (void)csefree(cse);
+ }
+ filp->private_data = NULL;
+ kfree(fcr);
+ return(0);
+}
+
+static struct file_operations cryptodev_fops = {
+ .owner = THIS_MODULE,
+ .open = cryptodev_open,
+ .release = cryptodev_release,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+ .ioctl = cryptodev_ioctl,
+#endif
+#ifdef HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = cryptodev_unlocked_ioctl,
+#endif
+};
+
+static struct miscdevice cryptodev = {
+ .minor = CRYPTODEV_MINOR,
+ .name = "crypto",
+ .fops = &cryptodev_fops,
+};
+
+static int __init
+cryptodev_init(void)
+{
+ int rc;
+
+ dprintk("%s(%p)\n", __FUNCTION__, cryptodev_init);
+ rc = misc_register(&cryptodev);
+ if (rc) {
+ printk(KERN_ERR "cryptodev: registration of /dev/crypto failed\n");
+ return(rc);
+ }
+
+ return(0);
+}
+
+static void __exit
+cryptodev_exit(void)
+{
+ dprintk("%s()\n", __FUNCTION__);
+ misc_deregister(&cryptodev);
+}
+
+module_init(cryptodev_init);
+module_exit(cryptodev_exit);
+
+MODULE_LICENSE("BSD");
+MODULE_AUTHOR("David McCullough <david_mccullough@mcafee.com>");
+MODULE_DESCRIPTION("Cryptodev (user interface to OCF)");
diff --git a/crypto/ocf/cryptodev.h b/crypto/ocf/cryptodev.h
new file mode 100644
index 0000000..6067541
--- /dev/null
+++ b/crypto/ocf/cryptodev.h
@@ -0,0 +1,561 @@
+/* $FreeBSD: src/sys/opencrypto/cryptodev.h,v 1.25 2007/05/09 19:37:02 gnn Exp $ */
+/* $OpenBSD: cryptodev.h,v 1.31 2002/06/11 11:14:29 beck Exp $ */
+
+/*-
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2006-2010 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
+ *
+ * This code was written by Angelos D. Keromytis in Athens, Greece, in
+ * February 2000. Network Security Technologies Inc. (NSTI) kindly
+ * supported the development of this code.
+ *
+ * Copyright (c) 2000 Angelos D. Keromytis
+ *
+ * Permission to use, copy, and modify this software with or without fee
+ * is hereby granted, provided that this entire notice is included in
+ * all source code copies of any software which is or includes a copy or
+ * modification of this software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
+ * PURPOSE.
+ *
+ * Copyright (c) 2001 Theo de Raadt
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ *
+ */
+
+#ifndef _CRYPTO_CRYPTO_H_
+#define _CRYPTO_CRYPTO_H_
+
+#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
+#include <linux/in.h>
+#endif
+
+
+/* Some initial values */
+#define CRYPTO_DRIVERS_INITIAL 4
+#define CRYPTO_SW_SESSIONS 32
+
+/* Hash values */
+#define NULL_HASH_LEN 0
+#define MD5_HASH_LEN 16
+#define SHA1_HASH_LEN 20
+#define RIPEMD160_HASH_LEN 20
+#define SHA2_256_HASH_LEN 32
+#define SHA2_384_HASH_LEN 48
+#define SHA2_512_HASH_LEN 64
+#define MD5_KPDK_HASH_LEN 16
+#define SHA1_KPDK_HASH_LEN 20
+/* Maximum hash algorithm result length */
+#define HASH_MAX_LEN SHA2_512_HASH_LEN /* Keep this updated */
+
+/* HMAC values */
+#define NULL_HMAC_BLOCK_LEN 1
+#define MD5_HMAC_BLOCK_LEN 64
+#define SHA1_HMAC_BLOCK_LEN 64
+#define RIPEMD160_HMAC_BLOCK_LEN 64
+#define SHA2_256_HMAC_BLOCK_LEN 64
+#define SHA2_384_HMAC_BLOCK_LEN 128
+#define SHA2_512_HMAC_BLOCK_LEN 128
+/* Maximum HMAC block length */
+#define HMAC_MAX_BLOCK_LEN SHA2_512_HMAC_BLOCK_LEN /* Keep this updated */
+#define HMAC_IPAD_VAL 0x36
+#define HMAC_OPAD_VAL 0x5C
+
+/* Encryption algorithm block sizes */
+#define NULL_BLOCK_LEN 1
+#define DES_BLOCK_LEN 8
+#define DES3_BLOCK_LEN 8
+#define BLOWFISH_BLOCK_LEN 8
+#define SKIPJACK_BLOCK_LEN 8
+#define CAST128_BLOCK_LEN 8
+#define RIJNDAEL128_BLOCK_LEN 16
+#define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN
+#define CAMELLIA_BLOCK_LEN 16
+#define ARC4_BLOCK_LEN 1
+#define EALG_MAX_BLOCK_LEN AES_BLOCK_LEN /* Keep this updated */
+
+/* Encryption algorithm min and max key sizes */
+#define NULL_MIN_KEY_LEN 0
+#define NULL_MAX_KEY_LEN 0
+#define DES_MIN_KEY_LEN 8
+#define DES_MAX_KEY_LEN 8
+#define DES3_MIN_KEY_LEN 24
+#define DES3_MAX_KEY_LEN 24
+#define BLOWFISH_MIN_KEY_LEN 4
+#define BLOWFISH_MAX_KEY_LEN 56
+#define SKIPJACK_MIN_KEY_LEN 10
+#define SKIPJACK_MAX_KEY_LEN 10
+#define CAST128_MIN_KEY_LEN 5
+#define CAST128_MAX_KEY_LEN 16
+#define RIJNDAEL128_MIN_KEY_LEN 16
+#define RIJNDAEL128_MAX_KEY_LEN 32
+#define AES_MIN_KEY_LEN RIJNDAEL128_MIN_KEY_LEN
+#define AES_MAX_KEY_LEN RIJNDAEL128_MAX_KEY_LEN
+#define CAMELLIA_MIN_KEY_LEN 16
+#define CAMELLIA_MAX_KEY_LEN 32
+#define ARC4_MIN_KEY_LEN 1
+#define ARC4_MAX_KEY_LEN 256
+
+/* Max size of data that can be processed */
+#define CRYPTO_MAX_DATA_LEN 64*1024 - 1
+
+#define CRYPTO_ALGORITHM_MIN 1
+#define CRYPTO_DES_CBC 1
+#define CRYPTO_3DES_CBC 2
+#define CRYPTO_BLF_CBC 3
+#define CRYPTO_CAST_CBC 4
+#define CRYPTO_SKIPJACK_CBC 5
+#define CRYPTO_MD5_HMAC 6
+#define CRYPTO_SHA1_HMAC 7
+#define CRYPTO_RIPEMD160_HMAC 8
+#define CRYPTO_MD5_KPDK 9
+#define CRYPTO_SHA1_KPDK 10
+#define CRYPTO_RIJNDAEL128_CBC 11 /* 128 bit blocksize */
+#define CRYPTO_AES_CBC 11 /* 128 bit blocksize -- the same as above */
+#define CRYPTO_ARC4 12
+#define CRYPTO_MD5 13
+#define CRYPTO_SHA1 14
+#define CRYPTO_NULL_HMAC 15
+#define CRYPTO_NULL_CBC 16
+#define CRYPTO_DEFLATE_COMP 17 /* Deflate compression algorithm */
+#define CRYPTO_SHA2_256_HMAC 18
+#define CRYPTO_SHA2_384_HMAC 19
+#define CRYPTO_SHA2_512_HMAC 20
+#define CRYPTO_CAMELLIA_CBC 21
+#define CRYPTO_SHA2_256 22
+#define CRYPTO_SHA2_384 23
+#define CRYPTO_SHA2_512 24
+#define CRYPTO_RIPEMD160 25
+#define CRYPTO_LZS_COMP 26
+#define CRYPTO_ESP_RFC2406 27
+//#define CRYPTO_ESP_RFC2406_TRANSPORT 20
+#define CRYPTO_ESP_RFC4303 28
+#define CRYPTO_ESP4_RFC4303 28
+#define CRYPTO_ESP6_RFC4303 29
+#define CRYPTO_AH 30
+#define CRYPTO_AH4 30
+#define CRYPTO_AH6 31
+#define CRYPTO_SHA2_HMAC 32 /*TODO is it a duplicate entry*/
+#define CRYPTO_ALGORITHM_MAX 32 /* Keep updated - see below */
+
+/* Algorithm flags */
+#define CRYPTO_ALG_FLAG_SUPPORTED 0x01 /* Algorithm is supported */
+#define CRYPTO_ALG_FLAG_RNG_ENABLE 0x02 /* Has HW RNG for DH/DSA */
+#define CRYPTO_ALG_FLAG_DSA_SHA 0x04 /* Can do SHA on msg */
+
+/*
+ * Crypto driver/device flags. They can set in the crid
+ * parameter when creating a session or submitting a key
+ * op to affect the device/driver assigned. If neither
+ * of these are specified then the crid is assumed to hold
+ * the driver id of an existing (and suitable) device that
+ * must be used to satisfy the request.
+ */
+#define CRYPTO_FLAG_HARDWARE 0x01000000 /* hardware accelerated */
+#define CRYPTO_FLAG_SOFTWARE 0x02000000 /* software implementation */
+
+/* NB: deprecated */
+struct session_op {
+ u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
+ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
+
+ u_int32_t keylen; /* cipher key */
+ caddr_t key;
+ int mackeylen; /* mac key */
+ caddr_t mackey;
+
+ u_int32_t ses; /* returns: session # */
+};
+
+struct session2_op {
+ u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
+ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
+
+ u_int32_t keylen; /* cipher key */
+ caddr_t key;
+ int mackeylen; /* mac key */
+ caddr_t mackey;
+
+ u_int32_t ses; /* returns: session # */
+ int crid; /* driver id + flags (rw) */
+ int pad[4]; /* for future expansion */
+};
+
+struct crypt_op {
+ u_int32_t ses;
+ u_int16_t op; /* i.e. COP_ENCRYPT */
+#define COP_NONE 0
+#define COP_ENCRYPT 1
+#define COP_DECRYPT 2
+ u_int16_t flags;
+#define COP_F_BATCH 0x0008 /* Batch op if possible */
+ u_int len;
+ caddr_t src, dst; /* become iov[] inside kernel */
+ caddr_t mac; /* must be big enough for chosen MAC */
+ caddr_t iv;
+};
+
+/*
+ * Parameters for looking up a crypto driver/device by
+ * device name or by id. The latter are returned for
+ * created sessions (crid) and completed key operations.
+ */
+struct crypt_find_op {
+ int crid; /* driver id + flags */
+ char name[32]; /* device/driver name */
+};
+
+/* bignum parameter, in packed bytes, ... */
+struct crparam {
+ caddr_t crp_p;
+ u_int crp_nbits;
+};
+
+#define CRK_MAXPARAM 8
+
+struct crypt_kop {
+ u_int crk_op; /* ie. CRK_MOD_EXP or other */
+ u_int crk_status; /* return status */
+ u_short crk_iparams; /* # of input parameters */
+ u_short crk_oparams; /* # of output parameters */
+ u_int crk_crid; /* NB: only used by CIOCKEY2 (rw) */
+ struct crparam crk_param[CRK_MAXPARAM];
+};
+#define CRK_ALGORITM_MIN 0
+#define CRK_MOD_EXP 0
+#define CRK_MOD_EXP_CRT 1
+#define CRK_DSA_SIGN 2
+#define CRK_DSA_VERIFY 3
+#define CRK_DH_COMPUTE_KEY 4
+#define CRK_ALGORITHM_MAX 4 /* Keep updated - see below */
+
+#define CRF_MOD_EXP (1 << CRK_MOD_EXP)
+#define CRF_MOD_EXP_CRT (1 << CRK_MOD_EXP_CRT)
+#define CRF_DSA_SIGN (1 << CRK_DSA_SIGN)
+#define CRF_DSA_VERIFY (1 << CRK_DSA_VERIFY)
+#define CRF_DH_COMPUTE_KEY (1 << CRK_DH_COMPUTE_KEY)
+
+/*
+ * done against open of /dev/crypto, to get a cloned descriptor.
+ * Please use F_SETFD against the cloned descriptor.
+ */
+#define CRIOGET _IOWR('c', 100, u_int32_t)
+#define CRIOASYMFEAT CIOCASYMFEAT
+#define CRIOFINDDEV CIOCFINDDEV
+
+/* the following are done against the cloned descriptor */
+#define CIOCGSESSION _IOWR('c', 101, struct session_op)
+#define CIOCFSESSION _IOW('c', 102, u_int32_t)
+#define CIOCCRYPT _IOWR('c', 103, struct crypt_op)
+#define CIOCKEY _IOWR('c', 104, struct crypt_kop)
+#define CIOCASYMFEAT _IOR('c', 105, u_int32_t)
+#define CIOCGSESSION2 _IOWR('c', 106, struct session2_op)
+#define CIOCKEY2 _IOWR('c', 107, struct crypt_kop)
+#define CIOCFINDDEV _IOWR('c', 108, struct crypt_find_op)
+
+struct cryptotstat {
+ struct timespec acc; /* total accumulated time */
+ struct timespec min; /* min time */
+ struct timespec max; /* max time */
+ u_int32_t count; /* number of observations */
+};
+
+struct cryptostats {
+ u_int32_t cs_ops; /* symmetric crypto ops submitted */
+ u_int32_t cs_errs; /* symmetric crypto ops that failed */
+ u_int32_t cs_kops; /* asymetric/key ops submitted */
+ u_int32_t cs_kerrs; /* asymetric/key ops that failed */
+ u_int32_t cs_intrs; /* crypto swi thread activations */
+ u_int32_t cs_rets; /* crypto return thread activations */
+ u_int32_t cs_blocks; /* symmetric op driver block */
+ u_int32_t cs_kblocks; /* symmetric op driver block */
+ /*
+ * When CRYPTO_TIMING is defined at compile time and the
+ * sysctl debug.crypto is set to 1, the crypto system will
+ * accumulate statistics about how long it takes to process
+ * crypto requests at various points during processing.
+ */
+ struct cryptotstat cs_invoke; /* crypto_dipsatch -> crypto_invoke */
+ struct cryptotstat cs_done; /* crypto_invoke -> crypto_done */
+ struct cryptotstat cs_cb; /* crypto_done -> callback */
+ struct cryptotstat cs_finis; /* callback -> callback return */
+
+ u_int32_t cs_drops; /* crypto ops dropped due to congestion */
+};
+
+#ifdef __KERNEL__
+
+/* Standard initialization structure beginning */
+struct cryptoini {
+ int cri_alg; /* Algorithm to use */
+ int cri_flags;
+ union {
+ struct {
+ int cri_mlen; /* Number of bytes we want from the
+ entire hash. 0 means all. */
+ int cri_klen; /* Key length, in bits */
+ caddr_t cri_key; /* key to use */
+ u_int8_t cri_iv[EALG_MAX_BLOCK_LEN]; /* IV to use */
+ } cri_alg;
+ struct {
+ u_int32_t basealg;
+ struct sockaddr_in tun_source;
+ struct sockaddr_in tun_destination;
+ int tun_df_mode;
+ int tun_ds_mode;
+ int tun_ttl_value;
+ int tun_replay_windowsize;
+ int spivalue ;
+ int replayinit; /* set to 0 to disable replay on receive */
+ u_int64_t time_hard_lifetime;
+ u_int64_t time_soft_lifetime;
+ u_int64_t byte_hard_lifetime;
+ u_int64_t byte_soft_lifetime;
+ } cri_pack;
+ } u;
+ struct cryptoini *cri_next;
+};
+#define cri_mlen u.cri_alg.cri_mlen
+#define cri_klen u.cri_alg.cri_klen
+#define cri_key u.cri_alg.cri_key
+#define cri_iv u.cri_alg.cri_iv
+#define crip_basealg u.cri_pack.basealg
+#define crip_tun_source u.cri_pack.tun_source
+#define crip_tun_destination u.cri_pack.tun_destination
+#define crip_tun_df_mode u.cri_pack.tun_df_mode
+#define crip_tun_ds_mode u.cri_pack.tun_ds_mode
+#define crip_tun_ttl_value u.cri_pack.tun_ttl_value
+#define crip_tun_replay_windowsize u.cri_pack.tun_replay_windowsize
+#define crip_spivalue u.cri_pack.spivalue
+#define crip_replayinit u.cri_pack.replayinit
+#define crip_time_hard_lifetime u.cri_pack.time_hard_lifetime
+#define crip_time_soft_lifetime u.cri_pack.time_soft_lifetime
+#define crip_byte_hard_lifetime u.cri_pack.byte_hard_lifetime
+#define crip_byte_soft_lifetime u.cri_pack.byte_soft_lifetime
+
+/* Describe boundaries of a single crypto operation */
+struct cryptodesc {
+ int crd_skip; /* How many bytes to ignore from start */
+ int crd_len; /* How many bytes to process */
+ int crd_inject; /* Where to inject results, if applicable */
+ int crd_flags;
+
+#define CRD_F_ENCRYPT 0x01 /* Set when doing encryption */
+#define CRD_F_IV_PRESENT 0x02 /* When encrypting, IV is already in
+ place, so don't copy. */
+#define CRD_F_IV_EXPLICIT 0x04 /* IV explicitly provided */
+#define CRD_F_DSA_SHA_NEEDED 0x08 /* Compute SHA-1 of buffer for DSA */
+#define CRD_F_KEY_EXPLICIT 0x10 /* Key explicitly provided */
+#define CRD_F_COMP 0x0f /* Set when doing compression */
+
+ struct cryptoini CRD_INI; /* Initialization/context data */
+#define crd_iv CRD_INI.cri_iv
+#define crd_key CRD_INI.cri_key
+#define crd_alg CRD_INI.cri_alg
+#define crd_klen CRD_INI.cri_klen
+#define crd_mlen CRD_INI.cri_mlen
+
+ struct cryptodesc *crd_next;
+};
+
+/* Structure describing complete operation */
+struct cryptop {
+ struct list_head crp_next;
+ wait_queue_head_t crp_waitq;
+
+ u_int64_t crp_sid; /* Session ID */
+ int crp_ilen; /* Input data total length */
+ int crp_olen; /* Result total length */
+
+ int crp_etype; /*
+ * Error type (zero means no error).
+ * All error codes except EAGAIN
+ * indicate possible data corruption (as in,
+ * the data have been touched). On all
+ * errors, the crp_sid may have changed
+ * (reset to a new one), so the caller
+ * should always check and use the new
+ * value on future requests.
+ */
+ int crp_flags;
+
+#define CRYPTO_F_SKBUF 0x0001 /* Input/output are skbuf chains */
+#define CRYPTO_F_IOV 0x0002 /* Input/output are uio */
+#define CRYPTO_F_REL 0x0004 /* Must return data in same place */
+#define CRYPTO_F_BATCH 0x0008 /* Batch op if possible */
+#define CRYPTO_F_CBIMM 0x0010 /* Do callback immediately */
+#define CRYPTO_F_DONE 0x0020 /* Operation completed */
+#define CRYPTO_F_CBIFSYNC 0x0040 /* Do CBIMM if op is synchronous */
+
+ caddr_t crp_buf; /* Data to be processed */
+ caddr_t crp_out_buf; /* Crypto Result Buffer */
+ caddr_t crp_opaque; /* Opaque pointer, passed along */
+ struct cryptodesc *crp_desc; /* Linked list of processing descriptors */
+
+ int (*crp_callback)(struct cryptop *); /* Callback function */
+};
+enum crypto_packet_return_code {
+ CRYPTO_OK=0,
+ CRYPTO_SOFT_TTL = 2,
+ CRYPTO_HARD_TTL,
+ CRYPTO_SA_INACTIVE,
+ CRYPTO_REPLAY,
+ CRYPTO_ICV_FAIL,
+ CRYPTO_SEQ_ROLL,
+ CRYPTO_MEM_ERROR,
+ CRYPTO_VERS_ERROR,
+ CRYPTO_PROT_ERROR,
+ CRYPTO_PYLD_ERROR,
+ CRYPTO_PAD_ERROR
+};
+
+enum crypto_accel_type {
+ CRYPTO_PACKET =0x2, /* OR together desired bits */
+ CRYPTO_HARDWARE=0x1,
+ CRYPTO_SOFTWARE=0x0
+};
+
+enum crypto_flags {
+ CRYPTO_ENCRYPT=0x1, // same for encap (OCF l2)
+ CRYPTO_DECRYPT=0x2, // same for decap (OCF l2)
+ CRYPTO_MAC_GEN=0x4,
+ CRYPTO_MAC_CHECK=0x08,
+ CRYPTO_COMPRESS_SMALLER=0x10,
+ CRYPTO_COMPRESS_BIGGER=0x20
+};
+
+#define CRYPTO_BUF_CONTIG 0x0
+#define CRYPTO_BUF_IOV 0x1
+#define CRYPTO_BUF_SKBUF 0x2
+
+#define CRYPTO_OP_DECRYPT 0x0
+#define CRYPTO_OP_ENCRYPT 0x1
+
+/*
+ * Hints passed to process methods.
+ */
+#define CRYPTO_HINT_MORE 0x1 /* more ops coming shortly */
+
+struct cryptkop {
+ struct list_head krp_next;
+ wait_queue_head_t krp_waitq;
+
+ int krp_flags;
+#define CRYPTO_KF_DONE 0x0001 /* Operation completed */
+#define CRYPTO_KF_CBIMM 0x0002 /* Do callback immediately */
+
+ u_int krp_op; /* ie. CRK_MOD_EXP or other */
+ u_int krp_status; /* return status */
+ u_short krp_iparams; /* # of input parameters */
+ u_short krp_oparams; /* # of output parameters */
+ u_int krp_crid; /* desired device, etc. */
+ u_int32_t krp_hid;
+ struct crparam krp_param[CRK_MAXPARAM]; /* kvm */
+ int (*krp_callback)(struct cryptkop *);
+};
+
+#include <ocf-compat.h>
+
+/*
+ * Session ids are 64 bits. The lower 32 bits contain a "local id" which
+ * is a driver-private session identifier. The upper 32 bits contain a
+ * "hardware id" used by the core crypto code to identify the driver and
+ * a copy of the driver's capabilities that can be used by client code to
+ * optimize operation.
+ */
+#define CRYPTO_SESID2HID(_sid) (((_sid) >> 32) & 0x00ffffff)
+#define CRYPTO_SESID2CAPS(_sid) (((_sid) >> 32) & 0xff000000)
+#define CRYPTO_SESID2LID(_sid) (((u_int32_t) (_sid)) & 0xffffffff)
+
+extern int crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard);
+extern int crypto_freesession(u_int64_t sid);
+#define CRYPTOCAP_F_HARDWARE CRYPTO_FLAG_HARDWARE
+#define CRYPTOCAP_F_SOFTWARE CRYPTO_FLAG_SOFTWARE
+#define CRYPTOCAP_F_SYNC 0x04000000 /* operates synchronously */
+extern int32_t crypto_get_driverid(device_t dev, int flags);
+extern int crypto_find_driver(const char *);
+extern device_t crypto_find_device_byhid(int hid);
+extern int crypto_getcaps(int hid);
+extern int crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
+ u_int32_t flags);
+extern int crypto_kregister(u_int32_t, int, u_int32_t);
+extern int crypto_unregister(u_int32_t driverid, int alg);
+extern int crypto_unregister_all(u_int32_t driverid);
+extern int crypto_dispatch(struct cryptop *crp);
+extern int crypto_kdispatch(struct cryptkop *);
+#define CRYPTO_SYMQ 0x1
+#define CRYPTO_ASYMQ 0x2
+extern int crypto_unblock(u_int32_t, int);
+extern void crypto_done(struct cryptop *crp);
+extern void crypto_kdone(struct cryptkop *);
+extern int crypto_getfeat(int *);
+
+extern void crypto_freereq(struct cryptop *crp);
+extern struct cryptop *crypto_getreq(int num);
+
+extern int crypto_usercrypto; /* userland may do crypto requests */
+extern int crypto_userasymcrypto; /* userland may do asym crypto reqs */
+extern int crypto_devallowsoft; /* only use hardware crypto */
+
+/*
+ * random number support, crypto_unregister_all will unregister
+ */
+extern int crypto_rregister(u_int32_t driverid,
+ int (*read_random)(void *arg, u_int32_t *buf, int len), void *arg);
+extern int crypto_runregister_all(u_int32_t driverid);
+
+/*
+ * Crypto-related utility routines used mainly by drivers.
+ *
+ * XXX these don't really belong here; but for now they're
+ * kept apart from the rest of the system.
+ */
+struct uio;
+extern void cuio_copydata(struct uio* uio, int off, int len, caddr_t cp);
+extern void cuio_copyback(struct uio* uio, int off, int len, caddr_t cp);
+extern struct iovec *cuio_getptr(struct uio *uio, int loc, int *off);
+
+extern void crypto_copyback(int flags, caddr_t buf, int off, int size,
+ caddr_t in);
+extern void crypto_copydata(int flags, caddr_t buf, int off, int size,
+ caddr_t out);
+extern int crypto_apply(int flags, caddr_t buf, int off, int len,
+ int (*f)(void *, void *, u_int), void *arg);
+
+#endif /* __KERNEL__ */
+#endif /* _CRYPTO_CRYPTO_H_ */
diff --git a/crypto/ocf/cryptosoft.c b/crypto/ocf/cryptosoft.c
new file mode 100644
index 0000000..aa2383d
--- /dev/null
+++ b/crypto/ocf/cryptosoft.c
@@ -0,0 +1,1322 @@
+/*
+ * An OCF module that uses the linux kernel cryptoapi, based on the
+ * original cryptosoft for BSD by Angelos D. Keromytis (angelos@cis.upenn.edu)
+ * but is mostly unrecognisable,
+ *
+ * Written by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2004-2011 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ * ---------------------------------------------------------------------------
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/crypto.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+#include <linux/random.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+#include <linux/scatterlist.h>
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+#include <crypto/hash.h>
+#endif
+
+#include <cryptodev.h>
+#include <uio.h>
+
+struct {
+ softc_device_decl sc_dev;
+} swcr_softc;
+
+#define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK)
+
+#define SW_TYPE_CIPHER 0x01
+#define SW_TYPE_HMAC 0x02
+#define SW_TYPE_HASH 0x04
+#define SW_TYPE_COMP 0x08
+#define SW_TYPE_BLKCIPHER 0x10
+#define SW_TYPE_ALG_MASK 0x1f
+
+#define SW_TYPE_ASYNC 0x8000
+
+#define SW_TYPE_INUSE 0x10000000
+
+/* We change some of the above if we have an async interface */
+
+#define SW_TYPE_ALG_AMASK (SW_TYPE_ALG_MASK | SW_TYPE_ASYNC)
+
+#define SW_TYPE_ABLKCIPHER (SW_TYPE_BLKCIPHER | SW_TYPE_ASYNC)
+#define SW_TYPE_AHASH (SW_TYPE_HASH | SW_TYPE_ASYNC)
+#define SW_TYPE_AHMAC (SW_TYPE_HMAC | SW_TYPE_ASYNC)
+
+#define SCATTERLIST_MAX 16
+
+struct swcr_data {
+ struct work_struct workq;
+ int sw_type;
+ int sw_alg;
+ struct crypto_tfm *sw_tfm;
+ spinlock_t sw_tfm_lock;
+ union {
+ struct {
+ char *sw_key;
+ int sw_klen;
+ int sw_mlen;
+ } hmac;
+ void *sw_comp_buf;
+ } u;
+ struct swcr_data *sw_next;
+};
+
+struct swcr_req {
+ struct swcr_data *sw_head;
+ struct swcr_data *sw;
+ struct cryptop *crp;
+ struct cryptodesc *crd;
+ struct scatterlist sg[SCATTERLIST_MAX];
+ unsigned char iv[EALG_MAX_BLOCK_LEN];
+ char result[HASH_MAX_LEN];
+ void *crypto_req;
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static kmem_cache_t *swcr_req_cache;
+#else
+static struct kmem_cache *swcr_req_cache;
+#endif
+
+#ifndef CRYPTO_TFM_MODE_CBC
+/*
+ * As of linux-2.6.21 this is no longer defined, and presumably no longer
+ * needed to be passed into the crypto core code.
+ */
+#define CRYPTO_TFM_MODE_CBC 0
+#define CRYPTO_TFM_MODE_ECB 0
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ /*
+ * Linux 2.6.19 introduced a new Crypto API, setup macro's to convert new
+ * API into old API.
+ */
+
+ /* Symmetric/Block Cipher */
+ struct blkcipher_desc
+ {
+ struct crypto_tfm *tfm;
+ void *info;
+ };
+ #define ecb(X) #X , CRYPTO_TFM_MODE_ECB
+ #define cbc(X) #X , CRYPTO_TFM_MODE_CBC
+ #define crypto_has_blkcipher(X, Y, Z) crypto_alg_available(X, 0)
+ #define crypto_blkcipher_cast(X) X
+ #define crypto_blkcipher_tfm(X) X
+ #define crypto_alloc_blkcipher(X, Y, Z) crypto_alloc_tfm(X, mode)
+ #define crypto_blkcipher_ivsize(X) crypto_tfm_alg_ivsize(X)
+ #define crypto_blkcipher_blocksize(X) crypto_tfm_alg_blocksize(X)
+ #define crypto_blkcipher_setkey(X, Y, Z) crypto_cipher_setkey(X, Y, Z)
+ #define crypto_blkcipher_encrypt_iv(W, X, Y, Z) \
+ crypto_cipher_encrypt_iv((W)->tfm, X, Y, Z, (u8 *)((W)->info))
+ #define crypto_blkcipher_decrypt_iv(W, X, Y, Z) \
+ crypto_cipher_decrypt_iv((W)->tfm, X, Y, Z, (u8 *)((W)->info))
+ #define crypto_blkcipher_set_flags(x, y) /* nop */
+ #define crypto_free_blkcipher(x) crypto_free_tfm(x)
+ #define crypto_free_comp crypto_free_tfm
+ #define crypto_free_hash crypto_free_tfm
+
+ /* Hash/HMAC/Digest */
+ struct hash_desc
+ {
+ struct crypto_tfm *tfm;
+ };
+ #define hmac(X) #X , 0
+ #define crypto_has_hash(X, Y, Z) crypto_alg_available(X, 0)
+ #define crypto_hash_cast(X) X
+ #define crypto_hash_tfm(X) X
+ #define crypto_alloc_hash(X, Y, Z) crypto_alloc_tfm(X, mode)
+ #define crypto_hash_digestsize(X) crypto_tfm_alg_digestsize(X)
+ #define crypto_hash_digest(W, X, Y, Z) \
+ crypto_digest_digest((W)->tfm, X, sg_num, Z)
+
+ /* Asymmetric Cipher */
+ #define crypto_has_cipher(X, Y, Z) crypto_alg_available(X, 0)
+
+ /* Compression */
+ #define crypto_has_comp(X, Y, Z) crypto_alg_available(X, 0)
+ #define crypto_comp_tfm(X) X
+ #define crypto_comp_cast(X) X
+ #define crypto_alloc_comp(X, Y, Z) crypto_alloc_tfm(X, mode)
+ #define plain(X) #X , 0
+#else
+ #define ecb(X) "ecb(" #X ")" , 0
+ #define cbc(X) "cbc(" #X ")" , 0
+ #define hmac(X) "hmac(" #X ")" , 0
+ #define plain(X) #X , 0
+#endif /* if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+/* no ablkcipher in older kernels */
+#define crypto_alloc_ablkcipher(a,b,c) (NULL)
+#define crypto_ablkcipher_tfm(x) ((struct crypto_tfm *)(x))
+#define crypto_ablkcipher_set_flags(a, b) /* nop */
+#define crypto_ablkcipher_setkey(x, y, z) (-EINVAL)
+#define crypto_has_ablkcipher(a,b,c) (0)
+#else
+#define HAVE_ABLKCIPHER
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+/* no ahash in older kernels */
+#define crypto_ahash_tfm(x) ((struct crypto_tfm *)(x))
+#define crypto_alloc_ahash(a,b,c) (NULL)
+#define crypto_ahash_digestsize(x) 0
+#else
+#define HAVE_AHASH
+#endif
+
+struct crypto_details {
+ char *alg_name;
+ int mode;
+ int sw_type;
+};
+
+static struct crypto_details crypto_details[] = {
+ [CRYPTO_DES_CBC] = { cbc(des), SW_TYPE_BLKCIPHER, },
+ [CRYPTO_3DES_CBC] = { cbc(des3_ede), SW_TYPE_BLKCIPHER, },
+ [CRYPTO_BLF_CBC] = { cbc(blowfish), SW_TYPE_BLKCIPHER, },
+ [CRYPTO_CAST_CBC] = { cbc(cast5), SW_TYPE_BLKCIPHER, },
+ [CRYPTO_SKIPJACK_CBC] = { cbc(skipjack), SW_TYPE_BLKCIPHER, },
+ [CRYPTO_MD5_HMAC] = { hmac(md5), SW_TYPE_HMAC, },
+ [CRYPTO_SHA1_HMAC] = { hmac(sha1), SW_TYPE_HMAC, },
+ [CRYPTO_RIPEMD160_HMAC] = { hmac(ripemd160), SW_TYPE_HMAC, },
+ [CRYPTO_MD5_KPDK] = { plain(md5-kpdk), SW_TYPE_HASH, },
+ [CRYPTO_SHA1_KPDK] = { plain(sha1-kpdk), SW_TYPE_HASH, },
+ [CRYPTO_AES_CBC] = { cbc(aes), SW_TYPE_BLKCIPHER, },
+ [CRYPTO_ARC4] = { ecb(arc4), SW_TYPE_BLKCIPHER, },
+ [CRYPTO_MD5] = { plain(md5), SW_TYPE_HASH, },
+ [CRYPTO_SHA1] = { plain(sha1), SW_TYPE_HASH, },
+ [CRYPTO_NULL_HMAC] = { hmac(digest_null), SW_TYPE_HMAC, },
+ [CRYPTO_NULL_CBC] = { cbc(cipher_null), SW_TYPE_BLKCIPHER, },
+ [CRYPTO_DEFLATE_COMP] = { plain(deflate), SW_TYPE_COMP, },
+ [CRYPTO_SHA2_256_HMAC] = { hmac(sha256), SW_TYPE_HMAC, },
+ [CRYPTO_SHA2_384_HMAC] = { hmac(sha384), SW_TYPE_HMAC, },
+ [CRYPTO_SHA2_512_HMAC] = { hmac(sha512), SW_TYPE_HMAC, },
+ [CRYPTO_CAMELLIA_CBC] = { cbc(camellia), SW_TYPE_BLKCIPHER, },
+ [CRYPTO_SHA2_256] = { plain(sha256), SW_TYPE_HASH, },
+ [CRYPTO_SHA2_384] = { plain(sha384), SW_TYPE_HASH, },
+ [CRYPTO_SHA2_512] = { plain(sha512), SW_TYPE_HASH, },
+ [CRYPTO_RIPEMD160] = { plain(ripemd160), SW_TYPE_HASH, },
+};
+
+int32_t swcr_id = -1;
+module_param(swcr_id, int, 0444);
+MODULE_PARM_DESC(swcr_id, "Read-Only OCF ID for cryptosoft driver");
+
+int swcr_fail_if_compression_grows = 1;
+module_param(swcr_fail_if_compression_grows, int, 0644);
+MODULE_PARM_DESC(swcr_fail_if_compression_grows,
+ "Treat compression that results in more data as a failure");
+
+int swcr_no_ahash = 0;
+module_param(swcr_no_ahash, int, 0644);
+MODULE_PARM_DESC(swcr_no_ahash,
+ "Do not use async hash/hmac even if available");
+
+int swcr_no_ablk = 0;
+module_param(swcr_no_ablk, int, 0644);
+MODULE_PARM_DESC(swcr_no_ablk,
+ "Do not use async blk ciphers even if available");
+
+static struct swcr_data **swcr_sessions = NULL;
+static u_int32_t swcr_sesnum = 0;
+
+static int swcr_process(device_t, struct cryptop *, int);
+static int swcr_newsession(device_t, u_int32_t *, struct cryptoini *);
+static int swcr_freesession(device_t, u_int64_t);
+
+static device_method_t swcr_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, swcr_newsession),
+ DEVMETHOD(cryptodev_freesession,swcr_freesession),
+ DEVMETHOD(cryptodev_process, swcr_process),
+};
+
+#define debug swcr_debug
+int swcr_debug = 0;
+module_param(swcr_debug, int, 0644);
+MODULE_PARM_DESC(swcr_debug, "Enable debug");
+
+static void swcr_process_req(struct swcr_req *req);
+
+/*
+ * somethings just need to be run with user context no matter whether
+ * the kernel compression libs use vmalloc/vfree for example.
+ */
+
+typedef struct {
+ struct work_struct wq;
+ void (*func)(void *arg);
+ void *arg;
+} execute_later_t;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void
+doing_it_now(struct work_struct *wq)
+{
+ execute_later_t *w = container_of(wq, execute_later_t, wq);
+ (w->func)(w->arg);
+ kfree(w);
+}
+#else
+static void
+doing_it_now(void *arg)
+{
+ execute_later_t *w = (execute_later_t *) arg;
+ (w->func)(w->arg);
+ kfree(w);
+}
+#endif
+
+static void
+execute_later(void (fn)(void *), void *arg)
+{
+ execute_later_t *w;
+
+ w = (execute_later_t *) kmalloc(sizeof(execute_later_t), SLAB_ATOMIC);
+ if (w) {
+ memset(w, '\0', sizeof(w));
+ w->func = fn;
+ w->arg = arg;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ INIT_WORK(&w->wq, doing_it_now);
+#else
+ INIT_WORK(&w->wq, doing_it_now, w);
+#endif
+ schedule_work(&w->wq);
+ }
+}
+
+/*
+ * Generate a new software session.
+ */
+static int
+swcr_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri)
+{
+ struct swcr_data **swd;
+ u_int32_t i;
+ int error;
+ char *algo;
+ int mode;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (sid == NULL || cri == NULL) {
+ dprintk("%s,%d - EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ if (swcr_sessions) {
+ for (i = 1; i < swcr_sesnum; i++)
+ if (swcr_sessions[i] == NULL)
+ break;
+ } else
+ i = 1; /* NB: to silence compiler warning */
+
+ if (swcr_sessions == NULL || i == swcr_sesnum) {
+ if (swcr_sessions == NULL) {
+ i = 1; /* We leave swcr_sessions[0] empty */
+ swcr_sesnum = CRYPTO_SW_SESSIONS;
+ } else
+ swcr_sesnum *= 2;
+
+ swd = kmalloc(swcr_sesnum * sizeof(struct swcr_data *), SLAB_ATOMIC);
+ if (swd == NULL) {
+ /* Reset session number */
+ if (swcr_sesnum == CRYPTO_SW_SESSIONS)
+ swcr_sesnum = 0;
+ else
+ swcr_sesnum /= 2;
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+ memset(swd, 0, swcr_sesnum * sizeof(struct swcr_data *));
+
+ /* Copy existing sessions */
+ if (swcr_sessions) {
+ memcpy(swd, swcr_sessions,
+ (swcr_sesnum / 2) * sizeof(struct swcr_data *));
+ kfree(swcr_sessions);
+ }
+
+ swcr_sessions = swd;
+ }
+
+ swd = &swcr_sessions[i];
+ *sid = i;
+
+ while (cri) {
+ *swd = (struct swcr_data *) kmalloc(sizeof(struct swcr_data),
+ SLAB_ATOMIC);
+ if (*swd == NULL) {
+ swcr_freesession(NULL, i);
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+ memset(*swd, 0, sizeof(struct swcr_data));
+
+ if (cri->cri_alg < 0 ||
+ cri->cri_alg>=sizeof(crypto_details)/sizeof(crypto_details[0])){
+ printk("cryptosoft: Unknown algorithm 0x%x\n", cri->cri_alg);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ algo = crypto_details[cri->cri_alg].alg_name;
+ if (!algo || !*algo) {
+ printk("cryptosoft: Unsupported algorithm 0x%x\n", cri->cri_alg);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ mode = crypto_details[cri->cri_alg].mode;
+ (*swd)->sw_type = crypto_details[cri->cri_alg].sw_type;
+ (*swd)->sw_alg = cri->cri_alg;
+
+ spin_lock_init(&(*swd)->sw_tfm_lock);
+
+ /* Algorithm specific configuration */
+ switch (cri->cri_alg) {
+ case CRYPTO_NULL_CBC:
+ cri->cri_klen = 0; /* make it work with crypto API */
+ break;
+ default:
+ break;
+ }
+
+ if ((*swd)->sw_type & SW_TYPE_BLKCIPHER) {
+ dprintk("%s crypto_alloc_*blkcipher(%s, 0x%x)\n", __FUNCTION__,
+ algo, mode);
+
+ /* try async first */
+ (*swd)->sw_tfm = swcr_no_ablk ? NULL :
+ crypto_ablkcipher_tfm(crypto_alloc_ablkcipher(algo, 0, 0));
+ if ((*swd)->sw_tfm && !IS_ERR((*swd)->sw_tfm)) {
+ dprintk("%s %s cipher is async\n", __FUNCTION__, algo);
+ (*swd)->sw_type |= SW_TYPE_ASYNC;
+ } else {
+ (*swd)->sw_tfm = crypto_blkcipher_tfm(
+ crypto_alloc_blkcipher(algo, 0, CRYPTO_ALG_ASYNC));
+ if ((*swd)->sw_tfm && !IS_ERR((*swd)->sw_tfm))
+ dprintk("%s %s cipher is sync\n", __FUNCTION__, algo);
+ }
+ if (!(*swd)->sw_tfm || IS_ERR((*swd)->sw_tfm)) {
+ int err;
+ dprintk("cryptosoft: crypto_alloc_blkcipher failed(%s, 0x%x)\n",
+ algo,mode);
+ err = IS_ERR((*swd)->sw_tfm) ? -(PTR_ERR((*swd)->sw_tfm)) : EINVAL;
+ (*swd)->sw_tfm = NULL; /* ensure NULL */
+ swcr_freesession(NULL, i);
+ return err;
+ }
+
+ if (debug) {
+ dprintk("%s key:cri->cri_klen=%d,(cri->cri_klen + 7)/8=%d",
+ __FUNCTION__, cri->cri_klen, (cri->cri_klen + 7) / 8);
+ for (i = 0; i < (cri->cri_klen + 7) / 8; i++)
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",
+ cri->cri_key[i] & 0xff);
+ dprintk("\n");
+ }
+ if ((*swd)->sw_type & SW_TYPE_ASYNC) {
+ /* OCF doesn't enforce keys */
+ crypto_ablkcipher_set_flags(
+ __crypto_ablkcipher_cast((*swd)->sw_tfm),
+ CRYPTO_TFM_REQ_WEAK_KEY);
+ error = crypto_ablkcipher_setkey(
+ __crypto_ablkcipher_cast((*swd)->sw_tfm),
+ cri->cri_key, (cri->cri_klen + 7) / 8);
+ } else {
+ /* OCF doesn't enforce keys */
+ crypto_blkcipher_set_flags(
+ crypto_blkcipher_cast((*swd)->sw_tfm),
+ CRYPTO_TFM_REQ_WEAK_KEY);
+ error = crypto_blkcipher_setkey(
+ crypto_blkcipher_cast((*swd)->sw_tfm),
+ cri->cri_key, (cri->cri_klen + 7) / 8);
+ }
+ if (error) {
+ printk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n", error,
+ (*swd)->sw_tfm->crt_flags);
+ swcr_freesession(NULL, i);
+ return error;
+ }
+ } else if ((*swd)->sw_type & (SW_TYPE_HMAC | SW_TYPE_HASH)) {
+ dprintk("%s crypto_alloc_*hash(%s, 0x%x)\n", __FUNCTION__,
+ algo, mode);
+
+ /* try async first */
+ (*swd)->sw_tfm = swcr_no_ahash ? NULL :
+ crypto_ahash_tfm(crypto_alloc_ahash(algo, 0, 0));
+ if ((*swd)->sw_tfm) {
+ dprintk("%s %s hash is async\n", __FUNCTION__, algo);
+ (*swd)->sw_type |= SW_TYPE_ASYNC;
+ } else {
+ dprintk("%s %s hash is sync\n", __FUNCTION__, algo);
+ (*swd)->sw_tfm = crypto_hash_tfm(
+ crypto_alloc_hash(algo, 0, CRYPTO_ALG_ASYNC));
+ }
+
+ if (!(*swd)->sw_tfm) {
+ dprintk("cryptosoft: crypto_alloc_hash failed(%s,0x%x)\n",
+ algo, mode);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ (*swd)->u.hmac.sw_klen = (cri->cri_klen + 7) / 8;
+ (*swd)->u.hmac.sw_key = (char *)kmalloc((*swd)->u.hmac.sw_klen,
+ SLAB_ATOMIC);
+ if ((*swd)->u.hmac.sw_key == NULL) {
+ swcr_freesession(NULL, i);
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+ memcpy((*swd)->u.hmac.sw_key, cri->cri_key, (*swd)->u.hmac.sw_klen);
+ if (cri->cri_mlen) {
+ (*swd)->u.hmac.sw_mlen = cri->cri_mlen;
+ } else if ((*swd)->sw_type & SW_TYPE_ASYNC) {
+ (*swd)->u.hmac.sw_mlen = crypto_ahash_digestsize(
+ __crypto_ahash_cast((*swd)->sw_tfm));
+ } else {
+ (*swd)->u.hmac.sw_mlen = crypto_hash_digestsize(
+ crypto_hash_cast((*swd)->sw_tfm));
+ }
+ } else if ((*swd)->sw_type & SW_TYPE_COMP) {
+ (*swd)->sw_tfm = crypto_comp_tfm(
+ crypto_alloc_comp(algo, 0, CRYPTO_ALG_ASYNC));
+ if (!(*swd)->sw_tfm) {
+ dprintk("cryptosoft: crypto_alloc_comp failed(%s,0x%x)\n",
+ algo, mode);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+ (*swd)->u.sw_comp_buf = kmalloc(CRYPTO_MAX_DATA_LEN, SLAB_ATOMIC);
+ if ((*swd)->u.sw_comp_buf == NULL) {
+ swcr_freesession(NULL, i);
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+ } else {
+ printk("cryptosoft: Unhandled sw_type %d\n", (*swd)->sw_type);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ cri = cri->cri_next;
+ swd = &((*swd)->sw_next);
+ }
+ return 0;
+}
+
+/*
+ * Free a session.
+ */
+static int
+swcr_freesession(device_t dev, u_int64_t tid)
+{
+ struct swcr_data *swd;
+ u_int32_t sid = CRYPTO_SESID2LID(tid);
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (sid > swcr_sesnum || swcr_sessions == NULL ||
+ swcr_sessions[sid] == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ return(EINVAL);
+ }
+
+ /* Silently accept and return */
+ if (sid == 0)
+ return(0);
+
+ while ((swd = swcr_sessions[sid]) != NULL) {
+ swcr_sessions[sid] = swd->sw_next;
+ if (swd->sw_tfm) {
+ switch (swd->sw_type & SW_TYPE_ALG_AMASK) {
+#ifdef HAVE_AHASH
+ case SW_TYPE_AHMAC:
+ case SW_TYPE_AHASH:
+ crypto_free_ahash(__crypto_ahash_cast(swd->sw_tfm));
+ break;
+#endif
+#ifdef HAVE_ABLKCIPHER
+ case SW_TYPE_ABLKCIPHER:
+ crypto_free_ablkcipher(__crypto_ablkcipher_cast(swd->sw_tfm));
+ break;
+#endif
+ case SW_TYPE_BLKCIPHER:
+ crypto_free_blkcipher(crypto_blkcipher_cast(swd->sw_tfm));
+ break;
+ case SW_TYPE_HMAC:
+ case SW_TYPE_HASH:
+ crypto_free_hash(crypto_hash_cast(swd->sw_tfm));
+ break;
+ case SW_TYPE_COMP:
+ if (in_interrupt())
+ execute_later((void (*)(void *))crypto_free_comp, (void *)crypto_comp_cast(swd->sw_tfm));
+ else
+ crypto_free_comp(crypto_comp_cast(swd->sw_tfm));
+ break;
+ default:
+ crypto_free_tfm(swd->sw_tfm);
+ break;
+ }
+ swd->sw_tfm = NULL;
+ }
+ if (swd->sw_type & SW_TYPE_COMP) {
+ if (swd->u.sw_comp_buf)
+ kfree(swd->u.sw_comp_buf);
+ } else {
+ if (swd->u.hmac.sw_key)
+ kfree(swd->u.hmac.sw_key);
+ }
+ kfree(swd);
+ }
+ return 0;
+}
+
+static void swcr_process_req_complete(struct swcr_req *req)
+{
+ dprintk("%s()\n", __FUNCTION__);
+
+ if (req->sw->sw_type & SW_TYPE_INUSE) {
+ unsigned long flags;
+ spin_lock_irqsave(&req->sw->sw_tfm_lock, flags);
+ req->sw->sw_type &= ~SW_TYPE_INUSE;
+ spin_unlock_irqrestore(&req->sw->sw_tfm_lock, flags);
+ }
+
+ if (req->crp->crp_etype)
+ goto done;
+
+ switch (req->sw->sw_type & SW_TYPE_ALG_AMASK) {
+#if defined(HAVE_AHASH)
+ case SW_TYPE_AHMAC:
+ case SW_TYPE_AHASH:
+ crypto_copyback(req->crp->crp_flags, req->crp->crp_buf,
+ req->crd->crd_inject, req->sw->u.hmac.sw_mlen, req->result);
+ ahash_request_free(req->crypto_req);
+ break;
+#endif
+#if defined(HAVE_ABLKCIPHER)
+ case SW_TYPE_ABLKCIPHER:
+ ablkcipher_request_free(req->crypto_req);
+ break;
+#endif
+ case SW_TYPE_CIPHER:
+ case SW_TYPE_HMAC:
+ case SW_TYPE_HASH:
+ case SW_TYPE_COMP:
+ case SW_TYPE_BLKCIPHER:
+ break;
+ default:
+ req->crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+ req->crd = req->crd->crd_next;
+ if (req->crd) {
+ swcr_process_req(req);
+ return;
+ }
+
+done:
+ dprintk("%s crypto_done %p\n", __FUNCTION__, req);
+ crypto_done(req->crp);
+ kmem_cache_free(swcr_req_cache, req);
+}
+
+#if defined(HAVE_ABLKCIPHER) || defined(HAVE_AHASH)
+static void swcr_process_callback(struct crypto_async_request *creq, int err)
+{
+ struct swcr_req *req = creq->data;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (err) {
+ if (err == -EINPROGRESS)
+ return;
+ dprintk("%s() fail %d\n", __FUNCTION__, -err);
+ req->crp->crp_etype = -err;
+ }
+
+ swcr_process_req_complete(req);
+}
+#endif /* defined(HAVE_ABLKCIPHER) || defined(HAVE_AHASH) */
+
+
+static void swcr_process_req(struct swcr_req *req)
+{
+ struct swcr_data *sw;
+ struct cryptop *crp = req->crp;
+ struct cryptodesc *crd = req->crd;
+ struct sk_buff *skb = (struct sk_buff *) crp->crp_buf;
+ struct uio *uiop = (struct uio *) crp->crp_buf;
+ int sg_num, sg_len, skip;
+
+ dprintk("%s()\n", __FUNCTION__);
+
+ /*
+ * Find the crypto context.
+ *
+ * XXX Note that the logic here prevents us from having
+ * XXX the same algorithm multiple times in a session
+ * XXX (or rather, we can but it won't give us the right
+ * XXX results). To do that, we'd need some way of differentiating
+ * XXX between the various instances of an algorithm (so we can
+ * XXX locate the correct crypto context).
+ */
+ for (sw = req->sw_head; sw && sw->sw_alg != crd->crd_alg; sw = sw->sw_next)
+ ;
+
+ /* No such context ? */
+ if (sw == NULL) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ goto done;
+ }
+
+ /*
+ * for some types we need to ensure only one user as info is stored in
+ * the tfm during an operation that can get corrupted
+ */
+ switch (sw->sw_type & SW_TYPE_ALG_AMASK) {
+#ifdef HAVE_AHASH
+ case SW_TYPE_AHMAC:
+ case SW_TYPE_AHASH:
+#endif
+ case SW_TYPE_HMAC:
+ case SW_TYPE_HASH: {
+ unsigned long flags;
+ spin_lock_irqsave(&sw->sw_tfm_lock, flags);
+ if (sw->sw_type & SW_TYPE_INUSE) {
+ spin_unlock_irqrestore(&sw->sw_tfm_lock, flags);
+ execute_later((void (*)(void *))swcr_process_req, (void *)req);
+ return;
+ }
+ sw->sw_type |= SW_TYPE_INUSE;
+ spin_unlock_irqrestore(&sw->sw_tfm_lock, flags);
+ } break;
+ }
+
+ req->sw = sw;
+ skip = crd->crd_skip;
+
+ /*
+ * setup the SG list skip from the start of the buffer
+ */
+ memset(req->sg, 0, sizeof(req->sg));
+ sg_init_table(req->sg, SCATTERLIST_MAX);
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ int i, len;
+
+ sg_num = 0;
+ sg_len = 0;
+
+ if (skip < skb_headlen(skb)) {
+ len = skb_headlen(skb) - skip;
+ if (len + sg_len > crd->crd_len)
+ len = crd->crd_len - sg_len;
+ sg_set_page(&req->sg[sg_num],
+ virt_to_page(skb->data + skip), len,
+ offset_in_page(skb->data + skip));
+ sg_len += len;
+ sg_num++;
+ skip = 0;
+ } else
+ skip -= skb_headlen(skb);
+
+ for (i = 0; sg_len < crd->crd_len &&
+ i < skb_shinfo(skb)->nr_frags &&
+ sg_num < SCATTERLIST_MAX; i++) {
+ if (skip < skb_shinfo(skb)->frags[i].size) {
+ len = skb_shinfo(skb)->frags[i].size - skip;
+ if (len + sg_len > crd->crd_len)
+ len = crd->crd_len - sg_len;
+ sg_set_page(&req->sg[sg_num],
+ skb_frag_page(&skb_shinfo(skb)->frags[i]),
+ len,
+ skb_shinfo(skb)->frags[i].page_offset + skip);
+ sg_len += len;
+ sg_num++;
+ skip = 0;
+ } else
+ skip -= skb_shinfo(skb)->frags[i].size;
+ }
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ int len;
+
+ sg_len = 0;
+ for (sg_num = 0; sg_len < crd->crd_len &&
+ sg_num < uiop->uio_iovcnt &&
+ sg_num < SCATTERLIST_MAX; sg_num++) {
+ if (skip <= uiop->uio_iov[sg_num].iov_len) {
+ len = uiop->uio_iov[sg_num].iov_len - skip;
+ if (len + sg_len > crd->crd_len)
+ len = crd->crd_len - sg_len;
+ sg_set_page(&req->sg[sg_num],
+ virt_to_page(uiop->uio_iov[sg_num].iov_base+skip),
+ len,
+ offset_in_page(uiop->uio_iov[sg_num].iov_base+skip));
+ sg_len += len;
+ skip = 0;
+ } else
+ skip -= uiop->uio_iov[sg_num].iov_len;
+ }
+ } else {
+ sg_len = (crp->crp_ilen - skip);
+ if (sg_len > crd->crd_len)
+ sg_len = crd->crd_len;
+ sg_set_page(&req->sg[0], virt_to_page(crp->crp_buf + skip),
+ sg_len, offset_in_page(crp->crp_buf + skip));
+ sg_num = 1;
+ }
+ if (sg_num > 0)
+ sg_mark_end(&req->sg[sg_num-1]);
+
+ switch (sw->sw_type & SW_TYPE_ALG_AMASK) {
+
+#ifdef HAVE_AHASH
+ case SW_TYPE_AHMAC:
+ case SW_TYPE_AHASH:
+ {
+ int ret;
+
+ /* check we have room for the result */
+ if (crp->crp_ilen - crd->crd_inject < sw->u.hmac.sw_mlen) {
+ dprintk("cryptosoft: EINVAL crp_ilen=%d, len=%d, inject=%d "
+ "digestsize=%d\n", crp->crp_ilen, crd->crd_skip + sg_len,
+ crd->crd_inject, sw->u.hmac.sw_mlen);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+ req->crypto_req =
+ ahash_request_alloc(__crypto_ahash_cast(sw->sw_tfm),GFP_ATOMIC);
+ if (!req->crypto_req) {
+ crp->crp_etype = ENOMEM;
+ dprintk("%s,%d: ENOMEM ahash_request_alloc", __FILE__, __LINE__);
+ goto done;
+ }
+
+ ahash_request_set_callback(req->crypto_req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG, swcr_process_callback, req);
+
+ memset(req->result, 0, sizeof(req->result));
+
+ if (sw->sw_type & SW_TYPE_AHMAC)
+ crypto_ahash_setkey(__crypto_ahash_cast(sw->sw_tfm),
+ sw->u.hmac.sw_key, sw->u.hmac.sw_klen);
+ ahash_request_set_crypt(req->crypto_req, req->sg, req->result, sg_len);
+ ret = crypto_ahash_digest(req->crypto_req);
+ switch (ret) {
+ case -EINPROGRESS:
+ case -EBUSY:
+ return;
+ default:
+ case 0:
+ dprintk("hash OP %s %d\n", ret ? "failed" : "success", ret);
+ crp->crp_etype = ret;
+ goto done;
+ }
+ } break;
+#endif /* HAVE_AHASH */
+
+#ifdef HAVE_ABLKCIPHER
+ case SW_TYPE_ABLKCIPHER: {
+ int ret;
+ unsigned char *ivp = req->iv;
+ int ivsize =
+ crypto_ablkcipher_ivsize(__crypto_ablkcipher_cast(sw->sw_tfm));
+
+ if (sg_len < crypto_ablkcipher_blocksize(
+ __crypto_ablkcipher_cast(sw->sw_tfm))) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL len %d < %d\n", __FILE__, __LINE__,
+ sg_len, crypto_ablkcipher_blocksize(
+ __crypto_ablkcipher_cast(sw->sw_tfm)));
+ goto done;
+ }
+
+ if (ivsize > sizeof(req->iv)) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ goto done;
+ }
+
+ req->crypto_req = ablkcipher_request_alloc(
+ __crypto_ablkcipher_cast(sw->sw_tfm), GFP_ATOMIC);
+ if (!req->crypto_req) {
+ crp->crp_etype = ENOMEM;
+ dprintk("%s,%d: ENOMEM ablkcipher_request_alloc",
+ __FILE__, __LINE__);
+ goto done;
+ }
+
+ ablkcipher_request_set_callback(req->crypto_req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG, swcr_process_callback, req);
+
+ if (crd->crd_flags & CRD_F_KEY_EXPLICIT) {
+ int i, error;
+
+ if (debug) {
+ dprintk("%s key:", __FUNCTION__);
+ for (i = 0; i < (crd->crd_klen + 7) / 8; i++)
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",
+ crd->crd_key[i] & 0xff);
+ dprintk("\n");
+ }
+ /* OCF doesn't enforce keys */
+ crypto_ablkcipher_set_flags(__crypto_ablkcipher_cast(sw->sw_tfm),
+ CRYPTO_TFM_REQ_WEAK_KEY);
+ error = crypto_ablkcipher_setkey(
+ __crypto_ablkcipher_cast(sw->sw_tfm), crd->crd_key,
+ (crd->crd_klen + 7) / 8);
+ if (error) {
+ dprintk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n",
+ error, sw->sw_tfm->crt_flags);
+ crp->crp_etype = -error;
+ }
+ }
+
+ if (crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */
+
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT)
+ ivp = crd->crd_iv;
+ else
+ get_random_bytes(ivp, ivsize);
+ /*
+ * do we have to copy the IV back to the buffer ?
+ */
+ if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, ivsize, (caddr_t)ivp);
+ }
+ ablkcipher_request_set_crypt(req->crypto_req, req->sg, req->sg,
+ sg_len, ivp);
+ ret = crypto_ablkcipher_encrypt(req->crypto_req);
+
+ } else { /*decrypt */
+
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT)
+ ivp = crd->crd_iv;
+ else
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, ivsize, (caddr_t)ivp);
+ ablkcipher_request_set_crypt(req->crypto_req, req->sg, req->sg,
+ sg_len, ivp);
+ ret = crypto_ablkcipher_decrypt(req->crypto_req);
+ }
+
+ switch (ret) {
+ case -EINPROGRESS:
+ case -EBUSY:
+ return;
+ default:
+ case 0:
+ dprintk("crypto OP %s %d\n", ret ? "failed" : "success", ret);
+ crp->crp_etype = ret;
+ goto done;
+ }
+ } break;
+#endif /* HAVE_ABLKCIPHER */
+
+ case SW_TYPE_BLKCIPHER: {
+ unsigned char iv[EALG_MAX_BLOCK_LEN];
+ unsigned char *ivp = iv;
+ struct blkcipher_desc desc;
+ int ivsize = crypto_blkcipher_ivsize(crypto_blkcipher_cast(sw->sw_tfm));
+
+ if (sg_len < crypto_blkcipher_blocksize(
+ crypto_blkcipher_cast(sw->sw_tfm))) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL len %d < %d\n", __FILE__, __LINE__,
+ sg_len, crypto_blkcipher_blocksize(
+ crypto_blkcipher_cast(sw->sw_tfm)));
+ goto done;
+ }
+
+ if (ivsize > sizeof(iv)) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ goto done;
+ }
+
+ if (crd->crd_flags & CRD_F_KEY_EXPLICIT) {
+ int i, error;
+
+ if (debug) {
+ dprintk("%s key:", __FUNCTION__);
+ for (i = 0; i < (crd->crd_klen + 7) / 8; i++)
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",
+ crd->crd_key[i] & 0xff);
+ dprintk("\n");
+ }
+ /* OCF doesn't enforce keys */
+ crypto_blkcipher_set_flags(crypto_blkcipher_cast(sw->sw_tfm),
+ CRYPTO_TFM_REQ_WEAK_KEY);
+ error = crypto_blkcipher_setkey(
+ crypto_blkcipher_cast(sw->sw_tfm), crd->crd_key,
+ (crd->crd_klen + 7) / 8);
+ if (error) {
+ dprintk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n",
+ error, sw->sw_tfm->crt_flags);
+ crp->crp_etype = -error;
+ }
+ }
+
+ memset(&desc, 0, sizeof(desc));
+ desc.tfm = crypto_blkcipher_cast(sw->sw_tfm);
+
+ if (crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */
+
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT) {
+ ivp = crd->crd_iv;
+ } else {
+ get_random_bytes(ivp, ivsize);
+ }
+ /*
+ * do we have to copy the IV back to the buffer ?
+ */
+ if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, ivsize, (caddr_t)ivp);
+ }
+ desc.info = ivp;
+ crypto_blkcipher_encrypt_iv(&desc, req->sg, req->sg, sg_len);
+
+ } else { /*decrypt */
+
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT) {
+ ivp = crd->crd_iv;
+ } else {
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, ivsize, (caddr_t)ivp);
+ }
+ desc.info = ivp;
+ crypto_blkcipher_decrypt_iv(&desc, req->sg, req->sg, sg_len);
+ }
+ } break;
+
+ case SW_TYPE_HMAC:
+ case SW_TYPE_HASH:
+ {
+ char result[HASH_MAX_LEN];
+ struct hash_desc desc;
+
+ /* check we have room for the result */
+ if (crp->crp_ilen - crd->crd_inject < sw->u.hmac.sw_mlen) {
+ dprintk("cryptosoft: EINVAL crp_ilen=%d, len=%d, inject=%d "
+ "digestsize=%d\n", crp->crp_ilen, crd->crd_skip + sg_len,
+ crd->crd_inject, sw->u.hmac.sw_mlen);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+ memset(&desc, 0, sizeof(desc));
+ desc.tfm = crypto_hash_cast(sw->sw_tfm);
+
+ memset(result, 0, sizeof(result));
+
+ if (sw->sw_type & SW_TYPE_HMAC) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ crypto_hmac(sw->sw_tfm, sw->u.hmac.sw_key, &sw->u.hmac.sw_klen,
+ req->sg, sg_num, result);
+#else
+ crypto_hash_setkey(desc.tfm, sw->u.hmac.sw_key,
+ sw->u.hmac.sw_klen);
+ crypto_hash_digest(&desc, req->sg, sg_len, result);
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+
+ } else { /* SW_TYPE_HASH */
+ crypto_hash_digest(&desc, req->sg, sg_len, result);
+ }
+
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, sw->u.hmac.sw_mlen, result);
+ }
+ break;
+
+ case SW_TYPE_COMP: {
+ void *ibuf = NULL;
+ void *obuf = sw->u.sw_comp_buf;
+ int ilen = sg_len, olen = CRYPTO_MAX_DATA_LEN;
+ int ret = 0;
+
+ /*
+ * we need to use an additional copy if there is more than one
+ * input chunk since the kernel comp routines do not handle
+ * SG yet. Otherwise we just use the input buffer as is.
+ * Rather than allocate another buffer we just split the tmp
+ * buffer we already have.
+ * Perhaps we should just use zlib directly ?
+ */
+ if (sg_num > 1) {
+ int blk;
+
+ ibuf = obuf;
+ for (blk = 0; blk < sg_num; blk++) {
+ memcpy(obuf, sg_virt(&req->sg[blk]),
+ req->sg[blk].length);
+ obuf += req->sg[blk].length;
+ }
+ olen -= sg_len;
+ } else
+ ibuf = sg_virt(&req->sg[0]);
+
+ if (crd->crd_flags & CRD_F_ENCRYPT) { /* compress */
+ ret = crypto_comp_compress(crypto_comp_cast(sw->sw_tfm),
+ ibuf, ilen, obuf, &olen);
+ if (!ret && olen > crd->crd_len) {
+ dprintk("cryptosoft: ERANGE compress %d into %d\n",
+ crd->crd_len, olen);
+ if (swcr_fail_if_compression_grows)
+ ret = ERANGE;
+ }
+ } else { /* decompress */
+ ret = crypto_comp_decompress(crypto_comp_cast(sw->sw_tfm),
+ ibuf, ilen, obuf, &olen);
+ if (!ret && (olen + crd->crd_inject) > crp->crp_olen) {
+ dprintk("cryptosoft: ETOOSMALL decompress %d into %d, "
+ "space for %d,at offset %d\n",
+ crd->crd_len, olen, crp->crp_olen, crd->crd_inject);
+ ret = ETOOSMALL;
+ }
+ }
+ if (ret)
+ dprintk("%s,%d: ret = %d\n", __FILE__, __LINE__, ret);
+
+ /*
+ * on success copy result back,
+ * linux crpyto API returns -errno, we need to fix that
+ */
+ crp->crp_etype = ret < 0 ? -ret : ret;
+ if (ret == 0) {
+ /* copy back the result and return it's size */
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, olen, obuf);
+ crp->crp_olen = olen;
+ }
+ } break;
+
+ default:
+ /* Unknown/unsupported algorithm */
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+done:
+ swcr_process_req_complete(req);
+}
+
+
+/*
+ * Process a crypto request.
+ */
+static int
+swcr_process(device_t dev, struct cryptop *crp, int hint)
+{
+ struct swcr_req *req = NULL;
+ u_int32_t lid;
+
+ dprintk("%s()\n", __FUNCTION__);
+ /* Sanity check */
+ if (crp == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ crp->crp_etype = 0;
+
+ if (crp->crp_desc == NULL || crp->crp_buf == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+ lid = crp->crp_sid & 0xffffffff;
+ if (lid >= swcr_sesnum || lid == 0 || swcr_sessions == NULL ||
+ swcr_sessions[lid] == NULL) {
+ crp->crp_etype = ENOENT;
+ dprintk("%s,%d: ENOENT\n", __FILE__, __LINE__);
+ goto done;
+ }
+
+ /*
+ * do some error checking outside of the loop for SKB and IOV processing
+ * this leaves us with valid skb or uiop pointers for later
+ */
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ struct sk_buff *skb = (struct sk_buff *) crp->crp_buf;
+ if (skb_shinfo(skb)->nr_frags >= SCATTERLIST_MAX) {
+ printk("%s,%d: %d nr_frags > SCATTERLIST_MAX", __FILE__, __LINE__,
+ skb_shinfo(skb)->nr_frags);
+ goto done;
+ }
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ struct uio *uiop = (struct uio *) crp->crp_buf;
+ if (uiop->uio_iovcnt > SCATTERLIST_MAX) {
+ printk("%s,%d: %d uio_iovcnt > SCATTERLIST_MAX", __FILE__, __LINE__,
+ uiop->uio_iovcnt);
+ goto done;
+ }
+ }
+
+ /*
+ * setup a new request ready for queuing
+ */
+ req = kmem_cache_alloc(swcr_req_cache, SLAB_ATOMIC);
+ if (req == NULL) {
+ dprintk("%s,%d: ENOMEM\n", __FILE__, __LINE__);
+ crp->crp_etype = ENOMEM;
+ goto done;
+ }
+ memset(req, 0, sizeof(*req));
+
+ req->sw_head = swcr_sessions[lid];
+ req->crp = crp;
+ req->crd = crp->crp_desc;
+
+ swcr_process_req(req);
+ return 0;
+
+done:
+ crypto_done(crp);
+ if (req)
+ kmem_cache_free(swcr_req_cache, req);
+ return 0;
+}
+
+
+static int
+cryptosoft_init(void)
+{
+ int i, sw_type, mode;
+ char *algo;
+
+ dprintk("%s(%p)\n", __FUNCTION__, cryptosoft_init);
+
+ swcr_req_cache = kmem_cache_create("cryptosoft_req",
+ sizeof(struct swcr_req), 0, SLAB_HWCACHE_ALIGN, NULL
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ , NULL
+#endif
+ );
+ if (!swcr_req_cache) {
+ printk("cryptosoft: failed to create request cache\n");
+ return -ENOENT;
+ }
+
+ softc_device_init(&swcr_softc, "cryptosoft", 0, swcr_methods);
+
+ swcr_id = crypto_get_driverid(softc_get_device(&swcr_softc),
+ CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC);
+ if (swcr_id < 0) {
+ printk("cryptosoft: Software crypto device cannot initialize!");
+ return -ENODEV;
+ }
+
+#define REGISTER(alg) \
+ crypto_register(swcr_id, alg, 0,0)
+
+ for (i = 0; i < sizeof(crypto_details)/sizeof(crypto_details[0]); i++) {
+ int found;
+
+ algo = crypto_details[i].alg_name;
+ if (!algo || !*algo) {
+ dprintk("%s:Algorithm %d not supported\n", __FUNCTION__, i);
+ continue;
+ }
+
+ mode = crypto_details[i].mode;
+ sw_type = crypto_details[i].sw_type;
+
+ found = 0;
+ switch (sw_type & SW_TYPE_ALG_MASK) {
+ case SW_TYPE_CIPHER:
+ found = crypto_has_cipher(algo, 0, CRYPTO_ALG_ASYNC);
+ break;
+ case SW_TYPE_HMAC:
+ found = crypto_has_hash(algo, 0, swcr_no_ahash?CRYPTO_ALG_ASYNC:0);
+ break;
+ case SW_TYPE_HASH:
+ found = crypto_has_hash(algo, 0, swcr_no_ahash?CRYPTO_ALG_ASYNC:0);
+ break;
+ case SW_TYPE_COMP:
+ found = crypto_has_comp(algo, 0, CRYPTO_ALG_ASYNC);
+ break;
+ case SW_TYPE_BLKCIPHER:
+ found = crypto_has_blkcipher(algo, 0, CRYPTO_ALG_ASYNC);
+ if (!found && !swcr_no_ablk)
+ found = crypto_has_ablkcipher(algo, 0, 0);
+ break;
+ }
+ if (found) {
+ REGISTER(i);
+ } else {
+ dprintk("%s:Algorithm Type %d not supported (algorithm %d:'%s')\n",
+ __FUNCTION__, sw_type, i, algo);
+ }
+ }
+ return 0;
+}
+
+static void
+cryptosoft_exit(void)
+{
+ dprintk("%s()\n", __FUNCTION__);
+ crypto_unregister_all(swcr_id);
+ swcr_id = -1;
+ kmem_cache_destroy(swcr_req_cache);
+}
+
+late_initcall(cryptosoft_init);
+module_exit(cryptosoft_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("David McCullough <david_mccullough@mcafee.com>");
+MODULE_DESCRIPTION("Cryptosoft (OCF module for kernel crypto)");
diff --git a/crypto/ocf/ocf-bench.c b/crypto/ocf/ocf-bench.c
new file mode 100644
index 0000000..f3fe9d0
--- /dev/null
+++ b/crypto/ocf/ocf-bench.c
@@ -0,0 +1,514 @@
+/*
+ * A loadable module that benchmarks the OCF crypto speed from kernel space.
+ *
+ * Copyright (C) 2004-2010 David McCullough <david_mccullough@mcafee.com>
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ */
+
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <cryptodev.h>
+
+#ifdef I_HAVE_AN_XSCALE_WITH_INTEL_SDK
+#define BENCH_IXP_ACCESS_LIB 1
+#endif
+#ifdef BENCH_IXP_ACCESS_LIB
+#include <IxTypes.h>
+#include <IxOsBuffMgt.h>
+#include <IxNpeDl.h>
+#include <IxCryptoAcc.h>
+#include <IxQMgr.h>
+#include <IxOsServices.h>
+#include <IxOsCacheMMU.h>
+#endif
+
+/*
+ * support for access lib version 1.4
+ */
+#ifndef IX_MBUF_PRIV
+#define IX_MBUF_PRIV(x) ((x)->priv)
+#endif
+
+/*
+ * the number of simultaneously active requests
+ */
+static int request_q_len = 40;
+module_param(request_q_len, int, 0);
+MODULE_PARM_DESC(request_q_len, "Number of outstanding requests");
+
+/*
+ * how many requests we want to have processed
+ */
+static int request_num = 1024;
+module_param(request_num, int, 0);
+MODULE_PARM_DESC(request_num, "run for at least this many requests");
+
+/*
+ * the size of each request
+ */
+static int request_size = 1488;
+module_param(request_size, int, 0);
+MODULE_PARM_DESC(request_size, "size of each request");
+
+/*
+ * OCF batching of requests
+ */
+static int request_batch = 1;
+module_param(request_batch, int, 0);
+MODULE_PARM_DESC(request_batch, "enable OCF request batching");
+
+/*
+ * OCF immediate callback on completion
+ */
+static int request_cbimm = 1;
+module_param(request_cbimm, int, 0);
+MODULE_PARM_DESC(request_cbimm, "enable OCF immediate callback on completion");
+
+/*
+ * a structure for each request
+ */
+typedef struct {
+ struct work_struct work;
+#ifdef BENCH_IXP_ACCESS_LIB
+ IX_MBUF mbuf;
+#endif
+ unsigned char *buffer;
+} request_t;
+
+static request_t *requests;
+
+static spinlock_t ocfbench_counter_lock;
+static int outstanding;
+static int total;
+
+/*************************************************************************/
+/*
+ * OCF benchmark routines
+ */
+
+static uint64_t ocf_cryptoid;
+static unsigned long jstart, jstop;
+
+static int ocf_init(void);
+static int ocf_cb(struct cryptop *crp);
+static void ocf_request(void *arg);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void ocf_request_wq(struct work_struct *work);
+#endif
+
+static int
+ocf_init(void)
+{
+ int error;
+ struct cryptoini crie, cria;
+ struct cryptodesc crda, crde;
+
+ memset(&crie, 0, sizeof(crie));
+ memset(&cria, 0, sizeof(cria));
+ memset(&crde, 0, sizeof(crde));
+ memset(&crda, 0, sizeof(crda));
+
+ cria.cri_alg = CRYPTO_SHA1_HMAC;
+ cria.cri_klen = 20 * 8;
+ cria.cri_key = "0123456789abcdefghij";
+
+ //crie.cri_alg = CRYPTO_3DES_CBC;
+ crie.cri_alg = CRYPTO_AES_CBC;
+ crie.cri_klen = 24 * 8;
+ crie.cri_key = "0123456789abcdefghijklmn";
+
+ crie.cri_next = &cria;
+
+ error = crypto_newsession(&ocf_cryptoid, &crie,
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE);
+ if (error) {
+ printk("crypto_newsession failed %d\n", error);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+ocf_cb(struct cryptop *crp)
+{
+ request_t *r = (request_t *) crp->crp_opaque;
+ unsigned long flags;
+
+ if (crp->crp_etype)
+ printk("Error in OCF processing: %d\n", crp->crp_etype);
+ crypto_freereq(crp);
+ crp = NULL;
+
+ /* do all requests but take at least 1 second */
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
+ total++;
+ if (total > request_num && jstart + HZ < jiffies) {
+ outstanding--;
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
+
+ schedule_work(&r->work);
+ return 0;
+}
+
+
+static void
+ocf_request(void *arg)
+{
+ request_t *r = arg;
+ struct cryptop *crp = crypto_getreq(2);
+ struct cryptodesc *crde, *crda;
+ unsigned long flags;
+
+ if (!crp) {
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
+ outstanding--;
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
+ return;
+ }
+
+ crde = crp->crp_desc;
+ crda = crde->crd_next;
+
+ crda->crd_skip = 0;
+ crda->crd_flags = 0;
+ crda->crd_len = request_size;
+ crda->crd_inject = request_size;
+ crda->crd_alg = CRYPTO_SHA1_HMAC;
+ crda->crd_key = "0123456789abcdefghij";
+ crda->crd_klen = 20 * 8;
+
+ crde->crd_skip = 0;
+ crde->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_ENCRYPT;
+ crde->crd_len = request_size;
+ crde->crd_inject = request_size;
+ //crde->crd_alg = CRYPTO_3DES_CBC;
+ crde->crd_alg = CRYPTO_AES_CBC;
+ crde->crd_key = "0123456789abcdefghijklmn";
+ crde->crd_klen = 24 * 8;
+
+ crp->crp_ilen = request_size + 64;
+ crp->crp_flags = 0;
+ if (request_batch)
+ crp->crp_flags |= CRYPTO_F_BATCH;
+ if (request_cbimm)
+ crp->crp_flags |= CRYPTO_F_CBIMM;
+ crp->crp_buf = (caddr_t) r->buffer;
+ crp->crp_callback = ocf_cb;
+ crp->crp_sid = ocf_cryptoid;
+ crp->crp_opaque = (caddr_t) r;
+ crypto_dispatch(crp);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void
+ocf_request_wq(struct work_struct *work)
+{
+ request_t *r = container_of(work, request_t, work);
+ ocf_request(r);
+}
+#endif
+
+static void
+ocf_done(void)
+{
+ crypto_freesession(ocf_cryptoid);
+}
+
+/*************************************************************************/
+#ifdef BENCH_IXP_ACCESS_LIB
+/*************************************************************************/
+/*
+ * CryptoAcc benchmark routines
+ */
+
+static IxCryptoAccCtx ixp_ctx;
+static UINT32 ixp_ctx_id;
+static IX_MBUF ixp_pri;
+static IX_MBUF ixp_sec;
+static int ixp_registered = 0;
+
+static void ixp_register_cb(UINT32 ctx_id, IX_MBUF *bufp,
+ IxCryptoAccStatus status);
+static void ixp_perform_cb(UINT32 ctx_id, IX_MBUF *sbufp, IX_MBUF *dbufp,
+ IxCryptoAccStatus status);
+static void ixp_request(void *arg);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void ixp_request_wq(struct work_struct *work);
+#endif
+
+static int
+ixp_init(void)
+{
+ IxCryptoAccStatus status;
+
+ ixp_ctx.cipherCtx.cipherAlgo = IX_CRYPTO_ACC_CIPHER_3DES;
+ ixp_ctx.cipherCtx.cipherMode = IX_CRYPTO_ACC_MODE_CBC;
+ ixp_ctx.cipherCtx.cipherKeyLen = 24;
+ ixp_ctx.cipherCtx.cipherBlockLen = IX_CRYPTO_ACC_DES_BLOCK_64;
+ ixp_ctx.cipherCtx.cipherInitialVectorLen = IX_CRYPTO_ACC_DES_IV_64;
+ memcpy(ixp_ctx.cipherCtx.key.cipherKey, "0123456789abcdefghijklmn", 24);
+
+ ixp_ctx.authCtx.authAlgo = IX_CRYPTO_ACC_AUTH_SHA1;
+ ixp_ctx.authCtx.authDigestLen = 12;
+ ixp_ctx.authCtx.aadLen = 0;
+ ixp_ctx.authCtx.authKeyLen = 20;
+ memcpy(ixp_ctx.authCtx.key.authKey, "0123456789abcdefghij", 20);
+
+ ixp_ctx.useDifferentSrcAndDestMbufs = 0;
+ ixp_ctx.operation = IX_CRYPTO_ACC_OP_ENCRYPT_AUTH ;
+
+ IX_MBUF_MLEN(&ixp_pri) = IX_MBUF_PKT_LEN(&ixp_pri) = 128;
+ IX_MBUF_MDATA(&ixp_pri) = (unsigned char *) kmalloc(128, SLAB_ATOMIC);
+ IX_MBUF_MLEN(&ixp_sec) = IX_MBUF_PKT_LEN(&ixp_sec) = 128;
+ IX_MBUF_MDATA(&ixp_sec) = (unsigned char *) kmalloc(128, SLAB_ATOMIC);
+
+ status = ixCryptoAccCtxRegister(&ixp_ctx, &ixp_pri, &ixp_sec,
+ ixp_register_cb, ixp_perform_cb, &ixp_ctx_id);
+
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS == status) {
+ while (!ixp_registered)
+ schedule();
+ return ixp_registered < 0 ? -1 : 0;
+ }
+
+ printk("ixp: ixCryptoAccCtxRegister failed %d\n", status);
+ return -1;
+}
+
+static void
+ixp_register_cb(UINT32 ctx_id, IX_MBUF *bufp, IxCryptoAccStatus status)
+{
+ if (bufp) {
+ IX_MBUF_MLEN(bufp) = IX_MBUF_PKT_LEN(bufp) = 0;
+ kfree(IX_MBUF_MDATA(bufp));
+ IX_MBUF_MDATA(bufp) = NULL;
+ }
+
+ if (IX_CRYPTO_ACC_STATUS_WAIT == status)
+ return;
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS == status)
+ ixp_registered = 1;
+ else
+ ixp_registered = -1;
+}
+
+static void
+ixp_perform_cb(
+ UINT32 ctx_id,
+ IX_MBUF *sbufp,
+ IX_MBUF *dbufp,
+ IxCryptoAccStatus status)
+{
+ request_t *r = NULL;
+ unsigned long flags;
+
+ /* do all requests but take at least 1 second */
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
+ total++;
+ if (total > request_num && jstart + HZ < jiffies) {
+ outstanding--;
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
+ return;
+ }
+
+ if (!sbufp || !(r = IX_MBUF_PRIV(sbufp))) {
+ printk("crappo %p %p\n", sbufp, r);
+ outstanding--;
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
+
+ schedule_work(&r->work);
+}
+
+static void
+ixp_request(void *arg)
+{
+ request_t *r = arg;
+ IxCryptoAccStatus status;
+ unsigned long flags;
+
+ memset(&r->mbuf, 0, sizeof(r->mbuf));
+ IX_MBUF_MLEN(&r->mbuf) = IX_MBUF_PKT_LEN(&r->mbuf) = request_size + 64;
+ IX_MBUF_MDATA(&r->mbuf) = r->buffer;
+ IX_MBUF_PRIV(&r->mbuf) = r;
+ status = ixCryptoAccAuthCryptPerform(ixp_ctx_id, &r->mbuf, NULL,
+ 0, request_size, 0, request_size, request_size, r->buffer);
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS != status) {
+ printk("status1 = %d\n", status);
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
+ outstanding--;
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
+ return;
+ }
+ return;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void
+ixp_request_wq(struct work_struct *work)
+{
+ request_t *r = container_of(work, request_t, work);
+ ixp_request(r);
+}
+#endif
+
+static void
+ixp_done(void)
+{
+ /* we should free the session here but I am lazy :-) */
+}
+
+/*************************************************************************/
+#endif /* BENCH_IXP_ACCESS_LIB */
+/*************************************************************************/
+
+int
+ocfbench_init(void)
+{
+ int i;
+ unsigned long mbps;
+ unsigned long flags;
+
+ printk("Crypto Speed tests\n");
+
+ requests = kmalloc(sizeof(request_t) * request_q_len, GFP_KERNEL);
+ if (!requests) {
+ printk("malloc failed\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < request_q_len; i++) {
+ /* +64 for return data */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ INIT_WORK(&requests[i].work, ocf_request_wq);
+#else
+ INIT_WORK(&requests[i].work, ocf_request, &requests[i]);
+#endif
+ requests[i].buffer = kmalloc(request_size + 128, GFP_DMA);
+ if (!requests[i].buffer) {
+ printk("malloc failed\n");
+ return -EINVAL;
+ }
+ memset(requests[i].buffer, '0' + i, request_size + 128);
+ }
+
+ /*
+ * OCF benchmark
+ */
+ printk("OCF: testing ...\n");
+ if (ocf_init() == -1)
+ return -EINVAL;
+
+ spin_lock_init(&ocfbench_counter_lock);
+ total = outstanding = 0;
+ jstart = jiffies;
+ for (i = 0; i < request_q_len; i++) {
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
+ outstanding++;
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
+ ocf_request(&requests[i]);
+ }
+ while (outstanding > 0)
+ schedule();
+ jstop = jiffies;
+
+ mbps = 0;
+ if (jstop > jstart) {
+ mbps = (unsigned long) total * (unsigned long) request_size * 8;
+ mbps /= ((jstop - jstart) * 1000) / HZ;
+ }
+ printk("OCF: %d requests of %d bytes in %d jiffies (%d.%03d Mbps)\n",
+ total, request_size, (int)(jstop - jstart),
+ ((int)mbps) / 1000, ((int)mbps) % 1000);
+ ocf_done();
+
+#ifdef BENCH_IXP_ACCESS_LIB
+ /*
+ * IXP benchmark
+ */
+ printk("IXP: testing ...\n");
+ ixp_init();
+ total = outstanding = 0;
+ jstart = jiffies;
+ for (i = 0; i < request_q_len; i++) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ INIT_WORK(&requests[i].work, ixp_request_wq);
+#else
+ INIT_WORK(&requests[i].work, ixp_request, &requests[i]);
+#endif
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
+ outstanding++;
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
+ ixp_request(&requests[i]);
+ }
+ while (outstanding > 0)
+ schedule();
+ jstop = jiffies;
+
+ mbps = 0;
+ if (jstop > jstart) {
+ mbps = (unsigned long) total * (unsigned long) request_size * 8;
+ mbps /= ((jstop - jstart) * 1000) / HZ;
+ }
+ printk("IXP: %d requests of %d bytes in %d jiffies (%d.%03d Mbps)\n",
+ total, request_size, jstop - jstart,
+ ((int)mbps) / 1000, ((int)mbps) % 1000);
+ ixp_done();
+#endif /* BENCH_IXP_ACCESS_LIB */
+
+ for (i = 0; i < request_q_len; i++)
+ kfree(requests[i].buffer);
+ kfree(requests);
+ return -EINVAL; /* always fail to load so it can be re-run quickly ;-) */
+}
+
+static void __exit ocfbench_exit(void)
+{
+}
+
+module_init(ocfbench_init);
+module_exit(ocfbench_exit);
+
+MODULE_LICENSE("BSD");
+MODULE_AUTHOR("David McCullough <david_mccullough@mcafee.com>");
+MODULE_DESCRIPTION("Benchmark various in-kernel crypto speeds");
diff --git a/crypto/ocf/ocf-compat.h b/crypto/ocf/ocf-compat.h
new file mode 100644
index 0000000..4ad1223
--- /dev/null
+++ b/crypto/ocf/ocf-compat.h
@@ -0,0 +1,372 @@
+#ifndef _BSD_COMPAT_H_
+#define _BSD_COMPAT_H_ 1
+/****************************************************************************/
+/*
+ * Provide compat routines for older linux kernels and BSD kernels
+ *
+ * Written by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2010 David McCullough <david_mccullough@mcafee.com>
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this file
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ */
+/****************************************************************************/
+#ifdef __KERNEL__
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+
+/*
+ * fake some BSD driver interface stuff specifically for OCF use
+ */
+
+typedef struct ocf_device *device_t;
+
+typedef struct {
+ int (*cryptodev_newsession)(device_t dev, u_int32_t *sidp, struct cryptoini *cri);
+ int (*cryptodev_freesession)(device_t dev, u_int64_t tid);
+ int (*cryptodev_process)(device_t dev, struct cryptop *crp, int hint);
+ int (*cryptodev_kprocess)(device_t dev, struct cryptkop *krp, int hint);
+} device_method_t;
+#define DEVMETHOD(id, func) id: func
+
+struct ocf_device {
+ char name[32]; /* the driver name */
+ char nameunit[32]; /* the driver name + HW instance */
+ int unit;
+ device_method_t methods;
+ void *softc;
+};
+
+#define CRYPTODEV_NEWSESSION(dev, sid, cri) \
+ ((*(dev)->methods.cryptodev_newsession)(dev,sid,cri))
+#define CRYPTODEV_FREESESSION(dev, sid) \
+ ((*(dev)->methods.cryptodev_freesession)(dev, sid))
+#define CRYPTODEV_PROCESS(dev, crp, hint) \
+ ((*(dev)->methods.cryptodev_process)(dev, crp, hint))
+#define CRYPTODEV_KPROCESS(dev, krp, hint) \
+ ((*(dev)->methods.cryptodev_kprocess)(dev, krp, hint))
+
+#define device_get_name(dev) ((dev)->name)
+#define device_get_nameunit(dev) ((dev)->nameunit)
+#define device_get_unit(dev) ((dev)->unit)
+#define device_get_softc(dev) ((dev)->softc)
+
+#define softc_device_decl \
+ struct ocf_device _device; \
+ device_t
+
+#define softc_device_init(_sc, _name, _unit, _methods) \
+ if (1) {\
+ strncpy((_sc)->_device.name, _name, sizeof((_sc)->_device.name) - 1); \
+ snprintf((_sc)->_device.nameunit, sizeof((_sc)->_device.name), "%s%d", _name, _unit); \
+ (_sc)->_device.unit = _unit; \
+ (_sc)->_device.methods = _methods; \
+ (_sc)->_device.softc = (void *) _sc; \
+ *(device_t *)((softc_get_device(_sc))+1) = &(_sc)->_device; \
+ } else
+
+#define softc_get_device(_sc) (&(_sc)->_device)
+
+/*
+ * iomem support for 2.4 and 2.6 kernels
+ */
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define ocf_iomem_t unsigned long
+
+/*
+ * implement simple workqueue like support for older kernels
+ */
+
+#include <linux/tqueue.h>
+
+#define work_struct tq_struct
+
+#define INIT_WORK(wp, fp, ap) \
+ do { \
+ (wp)->sync = 0; \
+ (wp)->routine = (fp); \
+ (wp)->data = (ap); \
+ } while (0)
+
+#define schedule_work(wp) \
+ do { \
+ queue_task((wp), &tq_immediate); \
+ mark_bh(IMMEDIATE_BH); \
+ } while (0)
+
+#define flush_scheduled_work() run_task_queue(&tq_immediate)
+
+#else
+#define ocf_iomem_t void __iomem *
+
+#include <linux/workqueue.h>
+
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+#include <linux/fdtable.h>
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+#define files_fdtable(files) (files)
+#endif
+
+#ifdef MODULE_PARM
+#undef module_param /* just in case */
+#define module_param(a,b,c) MODULE_PARM(a,"i")
+#endif
+
+#define bzero(s,l) memset(s,0,l)
+#define bcopy(s,d,l) memcpy(d,s,l)
+#define bcmp(x, y, l) memcmp(x,y,l)
+
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+#define device_printf(dev, a...) ({ \
+ printk("%s: ", device_get_nameunit(dev)); printk(a); \
+ })
+
+#undef printf
+#define printf(fmt...) printk(fmt)
+
+#define KASSERT(c,p) if (!(c)) { printk p ; } else
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define ocf_daemonize(str) \
+ daemonize(); \
+ spin_lock_irq(¤t->sigmask_lock); \
+ sigemptyset(¤t->blocked); \
+ recalc_sigpending(current); \
+ spin_unlock_irq(¤t->sigmask_lock); \
+ sprintf(current->comm, str);
+#else
+#define ocf_daemonize(str) daemonize(str);
+#endif
+
+#define TAILQ_INSERT_TAIL(q,d,m) list_add_tail(&(d)->m, (q))
+#define TAILQ_EMPTY(q) list_empty(q)
+#define TAILQ_FOREACH(v, q, m) list_for_each_entry(v, q, m)
+
+#define read_random(p,l) get_random_bytes(p,l)
+
+#define DELAY(x) ((x) > 2000 ? mdelay((x)/1000) : udelay(x))
+#define strtoul simple_strtoul
+
+#define pci_get_vendor(dev) ((dev)->vendor)
+#define pci_get_device(dev) ((dev)->device)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define pci_set_consistent_dma_mask(dev, mask) (0)
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+#define pci_dma_sync_single_for_cpu pci_dma_sync_single
+#endif
+
+#ifndef DMA_32BIT_MASK
+#define DMA_32BIT_MASK 0x00000000ffffffffULL
+#endif
+
+#ifndef htole32
+#define htole32(x) cpu_to_le32(x)
+#endif
+#ifndef htobe32
+#define htobe32(x) cpu_to_be32(x)
+#endif
+#ifndef htole16
+#define htole16(x) cpu_to_le16(x)
+#endif
+#ifndef htobe16
+#define htobe16(x) cpu_to_be16(x)
+#endif
+
+/* older kernels don't have these */
+
+#include <asm/irq.h>
+#if !defined(IRQ_NONE) && !defined(IRQ_RETVAL)
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_WAKE_THREAD
+#define IRQ_RETVAL
+#define irqreturn_t void
+typedef irqreturn_t (*irq_handler_t)(int irq, void *arg, struct pt_regs *regs);
+#endif
+#ifndef IRQF_SHARED
+#define IRQF_SHARED SA_SHIRQ
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+# define strlcpy(dest,src,len) \
+ ({strncpy(dest,src,(len)-1); ((char *)dest)[(len)-1] = '\0'; })
+#endif
+
+#ifndef MAX_ERRNO
+#define MAX_ERRNO 4095
+#endif
+#ifndef IS_ERR_VALUE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,5)
+#include <linux/err.h>
+#endif
+#ifndef IS_ERR_VALUE
+#define IS_ERR_VALUE(x) ((unsigned long)(x) >= (unsigned long)-MAX_ERRNO)
+#endif
+#endif
+
+/*
+ * common debug for all
+ */
+#if 1
+#define dprintk(a...) do { if (debug) printk(a); } while(0)
+#else
+#define dprintk(a...)
+#endif
+
+#ifndef SLAB_ATOMIC
+/* Changed in 2.6.20, must use GFP_ATOMIC now */
+#define SLAB_ATOMIC GFP_ATOMIC
+#endif
+
+/*
+ * need some additional support for older kernels */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,2)
+#define pci_register_driver_compat(driver, rc) \
+ do { \
+ if ((rc) > 0) { \
+ (rc) = 0; \
+ } else if (rc == 0) { \
+ (rc) = -ENODEV; \
+ } else { \
+ pci_unregister_driver(driver); \
+ } \
+ } while (0)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+#define pci_register_driver_compat(driver,rc) ((rc) = (rc) < 0 ? (rc) : 0)
+#else
+#define pci_register_driver_compat(driver,rc)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+
+static inline void sg_set_page(struct scatterlist *sg, struct page *page,
+ unsigned int len, unsigned int offset)
+{
+ sg->page = page;
+ sg->offset = offset;
+ sg->length = len;
+}
+
+static inline void *sg_virt(struct scatterlist *sg)
+{
+ return page_address(sg->page) + sg->offset;
+}
+
+#define sg_init_table(sg, n)
+
+#define sg_mark_end(sg)
+
+#endif
+
+#ifndef late_initcall
+#define late_initcall(init) module_init(init)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) || !defined(CONFIG_SMP)
+#define ocf_for_each_cpu(cpu) for ((cpu) = 0; (cpu) == 0; (cpu)++)
+#else
+#define ocf_for_each_cpu(cpu) for_each_present_cpu(cpu)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#include <linux/sched.h>
+#define kill_proc(p,s,v) send_sig(s,find_task_by_vpid(p),0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4)
+
+struct ocf_thread {
+ struct task_struct *task;
+ int (*func)(void *arg);
+ void *arg;
+};
+
+/* thread startup helper func */
+static inline int ocf_run_thread(void *arg)
+{
+ struct ocf_thread *t = (struct ocf_thread *) arg;
+ if (!t)
+ return -1; /* very bad */
+ t->task = current;
+ daemonize();
+ spin_lock_irq(¤t->sigmask_lock);
+ sigemptyset(¤t->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+ return (*t->func)(t->arg);
+}
+
+#define kthread_create(f,a,fmt...) \
+ ({ \
+ struct ocf_thread t; \
+ pid_t p; \
+ t.task = NULL; \
+ t.func = (f); \
+ t.arg = (a); \
+ p = kernel_thread(ocf_run_thread, &t, CLONE_FS|CLONE_FILES); \
+ while (p != (pid_t) -1 && t.task == NULL) \
+ schedule(); \
+ if (t.task) \
+ snprintf(t.task->comm, sizeof(t.task->comm), fmt); \
+ (t.task); \
+ })
+
+#define kthread_bind(t,cpu) /**/
+
+#define kthread_should_stop() (strcmp(current->comm, "stopping") == 0)
+
+#define kthread_stop(t) \
+ ({ \
+ strcpy((t)->comm, "stopping"); \
+ kill_proc((t)->pid, SIGTERM, 1); \
+ do { \
+ schedule(); \
+ } while (kill_proc((t)->pid, SIGTERM, 1) == 0); \
+ })
+
+#else
+#include <linux/kthread.h>
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
+#define skb_frag_page(x) ((x)->page)
+#endif
+
+#endif /* __KERNEL__ */
+
+/****************************************************************************/
+#endif /* _BSD_COMPAT_H_ */
diff --git a/crypto/ocf/random.c b/crypto/ocf/random.c
new file mode 100644
index 0000000..4bb773f
--- /dev/null
+++ b/crypto/ocf/random.c
@@ -0,0 +1,317 @@
+/*
+ * A system independant way of adding entropy to the kernels pool
+ * this way the drivers can focus on the real work and we can take
+ * care of pushing it to the appropriate place in the kernel.
+ *
+ * This should be fast and callable from timers/interrupts
+ *
+ * Written by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2006-2010 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/unistd.h>
+#include <linux/poll.h>
+#include <linux/random.h>
+#include <cryptodev.h>
+
+#ifdef CONFIG_OCF_FIPS
+#include "rndtest.h"
+#endif
+
+#ifndef HAS_RANDOM_INPUT_WAIT
+#error "Please do not enable OCF_RANDOMHARVEST unless you have applied patches"
+#endif
+
+/*
+ * a hack to access the debug levels from the crypto driver
+ */
+extern int crypto_debug;
+#define debug crypto_debug
+
+/*
+ * a list of all registered random providers
+ */
+static LIST_HEAD(random_ops);
+static int started = 0;
+static int initted = 0;
+
+struct random_op {
+ struct list_head random_list;
+ u_int32_t driverid;
+ int (*read_random)(void *arg, u_int32_t *buf, int len);
+ void *arg;
+};
+
+static int random_proc(void *arg);
+
+static pid_t randomproc = (pid_t) -1;
+static spinlock_t random_lock;
+
+/*
+ * just init the spin locks
+ */
+static int
+crypto_random_init(void)
+{
+ spin_lock_init(&random_lock);
+ initted = 1;
+ return(0);
+}
+
+/*
+ * Add the given random reader to our list (if not present)
+ * and start the thread (if not already started)
+ *
+ * we have to assume that driver id is ok for now
+ */
+int
+crypto_rregister(
+ u_int32_t driverid,
+ int (*read_random)(void *arg, u_int32_t *buf, int len),
+ void *arg)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct random_op *rops, *tmp;
+
+ dprintk("%s,%d: %s(0x%x, %p, %p)\n", __FILE__, __LINE__,
+ __FUNCTION__, driverid, read_random, arg);
+
+ if (!initted)
+ crypto_random_init();
+
+#if 0
+ struct cryptocap *cap;
+
+ cap = crypto_checkdriver(driverid);
+ if (!cap)
+ return EINVAL;
+#endif
+
+ list_for_each_entry_safe(rops, tmp, &random_ops, random_list) {
+ if (rops->driverid == driverid && rops->read_random == read_random)
+ return EEXIST;
+ }
+
+ rops = (struct random_op *) kmalloc(sizeof(*rops), GFP_KERNEL);
+ if (!rops)
+ return ENOMEM;
+
+ rops->driverid = driverid;
+ rops->read_random = read_random;
+ rops->arg = arg;
+
+ spin_lock_irqsave(&random_lock, flags);
+ list_add_tail(&rops->random_list, &random_ops);
+ if (!started) {
+ randomproc = kernel_thread(random_proc, NULL, CLONE_FS|CLONE_FILES);
+ if (randomproc < 0) {
+ ret = randomproc;
+ printk("crypto: crypto_rregister cannot start random thread; "
+ "error %d", ret);
+ } else
+ started = 1;
+ }
+ spin_unlock_irqrestore(&random_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(crypto_rregister);
+
+int
+crypto_runregister_all(u_int32_t driverid)
+{
+ struct random_op *rops, *tmp;
+ unsigned long flags;
+
+ dprintk("%s,%d: %s(0x%x)\n", __FILE__, __LINE__, __FUNCTION__, driverid);
+
+ list_for_each_entry_safe(rops, tmp, &random_ops, random_list) {
+ if (rops->driverid == driverid) {
+ list_del(&rops->random_list);
+ kfree(rops);
+ }
+ }
+
+ spin_lock_irqsave(&random_lock, flags);
+ if (list_empty(&random_ops) && started)
+ kill_proc(randomproc, SIGKILL, 1);
+ spin_unlock_irqrestore(&random_lock, flags);
+ return(0);
+}
+EXPORT_SYMBOL(crypto_runregister_all);
+
+/*
+ * while we can add entropy to random.c continue to read random data from
+ * the drivers and push it to random.
+ */
+static int
+random_proc(void *arg)
+{
+ int n;
+ int wantcnt;
+ int bufcnt = 0;
+ int retval = 0;
+ int *buf = NULL;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ daemonize();
+ spin_lock_irq(¤t->sigmask_lock);
+ sigemptyset(¤t->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+ sprintf(current->comm, "ocf-random");
+#else
+ daemonize("ocf-random");
+ allow_signal(SIGKILL);
+#endif
+
+ (void) get_fs();
+ set_fs(get_ds());
+
+#ifdef CONFIG_OCF_FIPS
+#define NUM_INT (RNDTEST_NBYTES/sizeof(int))
+#else
+#define NUM_INT 32
+#endif
+
+ /*
+ * some devices can transferr their RNG data direct into memory,
+ * so make sure it is device friendly
+ */
+ buf = kmalloc(NUM_INT * sizeof(int), GFP_DMA);
+ if (NULL == buf) {
+ printk("crypto: RNG could not allocate memory\n");
+ retval = -ENOMEM;
+ goto bad_alloc;
+ }
+
+ wantcnt = NUM_INT; /* start by adding some entropy */
+
+ /*
+ * its possible due to errors or driver removal that we no longer
+ * have anything to do, if so exit or we will consume all the CPU
+ * doing nothing
+ */
+ while (!list_empty(&random_ops)) {
+ struct random_op *rops, *tmp;
+
+#ifdef CONFIG_OCF_FIPS
+ if (wantcnt)
+ wantcnt = NUM_INT; /* FIPs mode can do 20000 bits or none */
+#endif
+
+ /* see if we can get enough entropy to make the world
+ * a better place.
+ */
+ while (bufcnt < wantcnt && bufcnt < NUM_INT) {
+ list_for_each_entry_safe(rops, tmp, &random_ops, random_list) {
+
+ n = (*rops->read_random)(rops->arg, &buf[bufcnt],
+ NUM_INT - bufcnt);
+
+ /* on failure remove the random number generator */
+ if (n == -1) {
+ list_del(&rops->random_list);
+ printk("crypto: RNG (driverid=0x%x) failed, disabling\n",
+ rops->driverid);
+ kfree(rops);
+ } else if (n > 0)
+ bufcnt += n;
+ }
+ /* give up CPU for a bit, just in case as this is a loop */
+ schedule();
+ }
+
+
+#ifdef CONFIG_OCF_FIPS
+ if (bufcnt > 0 && rndtest_buf((unsigned char *) &buf[0])) {
+ dprintk("crypto: buffer had fips errors, discarding\n");
+ bufcnt = 0;
+ }
+#endif
+
+ /*
+ * if we have a certified buffer, we can send some data
+ * to /dev/random and move along
+ */
+ if (bufcnt > 0) {
+ /* add what we have */
+ random_input_words(buf, bufcnt, bufcnt*sizeof(int)*8);
+ bufcnt = 0;
+ }
+
+ /* give up CPU for a bit so we don't hog while filling */
+ schedule();
+
+ /* wait for needing more */
+ wantcnt = random_input_wait();
+
+ if (wantcnt <= 0)
+ wantcnt = 0; /* try to get some info again */
+ else
+ /* round up to one word or we can loop forever */
+ wantcnt = (wantcnt + (sizeof(int)*8)) / (sizeof(int)*8);
+ if (wantcnt > NUM_INT) {
+ wantcnt = NUM_INT;
+ }
+
+ if (signal_pending(current)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_lock_irq(¤t->sigmask_lock);
+#endif
+ flush_signals(current);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_unlock_irq(¤t->sigmask_lock);
+#endif
+ }
+ }
+
+ kfree(buf);
+
+bad_alloc:
+ spin_lock_irq(&random_lock);
+ randomproc = (pid_t) -1;
+ started = 0;
+ spin_unlock_irq(&random_lock);
+
+ return retval;
+}
+
diff --git a/crypto/ocf/rndtest.c b/crypto/ocf/rndtest.c
new file mode 100644
index 0000000..7bed6a1
--- /dev/null
+++ b/crypto/ocf/rndtest.c
@@ -0,0 +1,300 @@
+/* $OpenBSD$ */
+
+/*
+ * OCF/Linux port done by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2006-2010 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jason L. Wright
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/unistd.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <cryptodev.h>
+#include "rndtest.h"
+
+static struct rndtest_stats rndstats;
+
+static void rndtest_test(struct rndtest_state *);
+
+/* The tests themselves */
+static int rndtest_monobit(struct rndtest_state *);
+static int rndtest_runs(struct rndtest_state *);
+static int rndtest_longruns(struct rndtest_state *);
+static int rndtest_chi_4(struct rndtest_state *);
+
+static int rndtest_runs_check(struct rndtest_state *, int, int *);
+static void rndtest_runs_record(struct rndtest_state *, int, int *);
+
+static const struct rndtest_testfunc {
+ int (*test)(struct rndtest_state *);
+} rndtest_funcs[] = {
+ { rndtest_monobit },
+ { rndtest_runs },
+ { rndtest_chi_4 },
+ { rndtest_longruns },
+};
+
+#define RNDTEST_NTESTS (sizeof(rndtest_funcs)/sizeof(rndtest_funcs[0]))
+
+static void
+rndtest_test(struct rndtest_state *rsp)
+{
+ int i, rv = 0;
+
+ rndstats.rst_tests++;
+ for (i = 0; i < RNDTEST_NTESTS; i++)
+ rv |= (*rndtest_funcs[i].test)(rsp);
+ rsp->rs_discard = (rv != 0);
+}
+
+
+extern int crypto_debug;
+#define rndtest_verbose 2
+#define rndtest_report(rsp, failure, fmt, a...) \
+ { if (failure || crypto_debug) { printk("rng_test: " fmt "\n", a); } else; }
+
+#define RNDTEST_MONOBIT_MINONES 9725
+#define RNDTEST_MONOBIT_MAXONES 10275
+
+static int
+rndtest_monobit(struct rndtest_state *rsp)
+{
+ int i, ones = 0, j;
+ u_int8_t r;
+
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
+ r = rsp->rs_buf[i];
+ for (j = 0; j < 8; j++, r <<= 1)
+ if (r & 0x80)
+ ones++;
+ }
+ if (ones > RNDTEST_MONOBIT_MINONES &&
+ ones < RNDTEST_MONOBIT_MAXONES) {
+ if (rndtest_verbose > 1)
+ rndtest_report(rsp, 0, "monobit pass (%d < %d < %d)",
+ RNDTEST_MONOBIT_MINONES, ones,
+ RNDTEST_MONOBIT_MAXONES);
+ return (0);
+ } else {
+ if (rndtest_verbose)
+ rndtest_report(rsp, 1,
+ "monobit failed (%d ones)", ones);
+ rndstats.rst_monobit++;
+ return (-1);
+ }
+}
+
+#define RNDTEST_RUNS_NINTERVAL 6
+
+static const struct rndtest_runs_tabs {
+ u_int16_t min, max;
+} rndtest_runs_tab[] = {
+ { 2343, 2657 },
+ { 1135, 1365 },
+ { 542, 708 },
+ { 251, 373 },
+ { 111, 201 },
+ { 111, 201 },
+};
+
+static int
+rndtest_runs(struct rndtest_state *rsp)
+{
+ int i, j, ones, zeros, rv = 0;
+ int onei[RNDTEST_RUNS_NINTERVAL], zeroi[RNDTEST_RUNS_NINTERVAL];
+ u_int8_t c;
+
+ bzero(onei, sizeof(onei));
+ bzero(zeroi, sizeof(zeroi));
+ ones = zeros = 0;
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
+ c = rsp->rs_buf[i];
+ for (j = 0; j < 8; j++, c <<= 1) {
+ if (c & 0x80) {
+ ones++;
+ rndtest_runs_record(rsp, zeros, zeroi);
+ zeros = 0;
+ } else {
+ zeros++;
+ rndtest_runs_record(rsp, ones, onei);
+ ones = 0;
+ }
+ }
+ }
+ rndtest_runs_record(rsp, ones, onei);
+ rndtest_runs_record(rsp, zeros, zeroi);
+
+ rv |= rndtest_runs_check(rsp, 0, zeroi);
+ rv |= rndtest_runs_check(rsp, 1, onei);
+
+ if (rv)
+ rndstats.rst_runs++;
+
+ return (rv);
+}
+
+static void
+rndtest_runs_record(struct rndtest_state *rsp, int len, int *intrv)
+{
+ if (len == 0)
+ return;
+ if (len > RNDTEST_RUNS_NINTERVAL)
+ len = RNDTEST_RUNS_NINTERVAL;
+ len -= 1;
+ intrv[len]++;
+}
+
+static int
+rndtest_runs_check(struct rndtest_state *rsp, int val, int *src)
+{
+ int i, rv = 0;
+
+ for (i = 0; i < RNDTEST_RUNS_NINTERVAL; i++) {
+ if (src[i] < rndtest_runs_tab[i].min ||
+ src[i] > rndtest_runs_tab[i].max) {
+ rndtest_report(rsp, 1,
+ "%s interval %d failed (%d, %d-%d)",
+ val ? "ones" : "zeros",
+ i + 1, src[i], rndtest_runs_tab[i].min,
+ rndtest_runs_tab[i].max);
+ rv = -1;
+ } else {
+ rndtest_report(rsp, 0,
+ "runs pass %s interval %d (%d < %d < %d)",
+ val ? "ones" : "zeros",
+ i + 1, rndtest_runs_tab[i].min, src[i],
+ rndtest_runs_tab[i].max);
+ }
+ }
+ return (rv);
+}
+
+static int
+rndtest_longruns(struct rndtest_state *rsp)
+{
+ int i, j, ones = 0, zeros = 0, maxones = 0, maxzeros = 0;
+ u_int8_t c;
+
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
+ c = rsp->rs_buf[i];
+ for (j = 0; j < 8; j++, c <<= 1) {
+ if (c & 0x80) {
+ zeros = 0;
+ ones++;
+ if (ones > maxones)
+ maxones = ones;
+ } else {
+ ones = 0;
+ zeros++;
+ if (zeros > maxzeros)
+ maxzeros = zeros;
+ }
+ }
+ }
+
+ if (maxones < 26 && maxzeros < 26) {
+ rndtest_report(rsp, 0, "longruns pass (%d ones, %d zeros)",
+ maxones, maxzeros);
+ return (0);
+ } else {
+ rndtest_report(rsp, 1, "longruns fail (%d ones, %d zeros)",
+ maxones, maxzeros);
+ rndstats.rst_longruns++;
+ return (-1);
+ }
+}
+
+/*
+ * chi^2 test over 4 bits: (this is called the poker test in FIPS 140-2,
+ * but it is really the chi^2 test over 4 bits (the poker test as described
+ * by Knuth vol 2 is something different, and I take him as authoritative
+ * on nomenclature over NIST).
+ */
+#define RNDTEST_CHI4_K 16
+#define RNDTEST_CHI4_K_MASK (RNDTEST_CHI4_K - 1)
+
+/*
+ * The unnormalized values are used so that we don't have to worry about
+ * fractional precision. The "real" value is found by:
+ * (V - 1562500) * (16 / 5000) = Vn (where V is the unnormalized value)
+ */
+#define RNDTEST_CHI4_VMIN 1563181 /* 2.1792 */
+#define RNDTEST_CHI4_VMAX 1576929 /* 46.1728 */
+
+static int
+rndtest_chi_4(struct rndtest_state *rsp)
+{
+ unsigned int freq[RNDTEST_CHI4_K], i, sum;
+
+ for (i = 0; i < RNDTEST_CHI4_K; i++)
+ freq[i] = 0;
+
+ /* Get number of occurances of each 4 bit pattern */
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
+ freq[(rsp->rs_buf[i] >> 4) & RNDTEST_CHI4_K_MASK]++;
+ freq[(rsp->rs_buf[i] >> 0) & RNDTEST_CHI4_K_MASK]++;
+ }
+
+ for (i = 0, sum = 0; i < RNDTEST_CHI4_K; i++)
+ sum += freq[i] * freq[i];
+
+ if (sum >= 1563181 && sum <= 1576929) {
+ rndtest_report(rsp, 0, "chi^2(4): pass (sum %u)", sum);
+ return (0);
+ } else {
+ rndtest_report(rsp, 1, "chi^2(4): failed (sum %u)", sum);
+ rndstats.rst_chi++;
+ return (-1);
+ }
+}
+
+int
+rndtest_buf(unsigned char *buf)
+{
+ struct rndtest_state rsp;
+
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.rs_buf = buf;
+ rndtest_test(&rsp);
+ return(rsp.rs_discard);
+}
+
diff --git a/crypto/ocf/rndtest.h b/crypto/ocf/rndtest.h
new file mode 100644
index 0000000..e9d8ec8
--- /dev/null
+++ b/crypto/ocf/rndtest.h
@@ -0,0 +1,54 @@
+/* $FreeBSD: src/sys/dev/rndtest/rndtest.h,v 1.1 2003/03/11 22:54:44 sam Exp $ */
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jason L. Wright
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/* Some of the tests depend on these values */
+#define RNDTEST_NBYTES 2500
+#define RNDTEST_NBITS (8 * RNDTEST_NBYTES)
+
+struct rndtest_state {
+ int rs_discard; /* discard/accept random data */
+ u_int8_t *rs_buf;
+};
+
+struct rndtest_stats {
+ u_int32_t rst_discard; /* number of bytes discarded */
+ u_int32_t rst_tests; /* number of test runs */
+ u_int32_t rst_monobit; /* monobit test failures */
+ u_int32_t rst_runs; /* 0/1 runs failures */
+ u_int32_t rst_longruns; /* longruns failures */
+ u_int32_t rst_chi; /* chi^2 failures */
+};
+
+extern int rndtest_buf(unsigned char *buf);
diff --git a/crypto/ocf/uio.h b/crypto/ocf/uio.h
new file mode 100644
index 0000000..03a6249
--- /dev/null
+++ b/crypto/ocf/uio.h
@@ -0,0 +1,54 @@
+#ifndef _OCF_UIO_H_
+#define _OCF_UIO_H_
+
+#include <linux/uio.h>
+
+/*
+ * The linux uio.h doesn't have all we need. To be fully api compatible
+ * with the BSD cryptodev, we need to keep this around. Perhaps this can
+ * be moved back into the linux/uio.h
+ *
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2006-2010 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ * ---------------------------------------------------------------------------
+ */
+
+struct uio {
+ struct iovec *uio_iov;
+ int uio_iovcnt;
+ off_t uio_offset;
+ int uio_resid;
+#if 0
+ enum uio_seg uio_segflg;
+ enum uio_rw uio_rw;
+ struct thread *uio_td;
+#endif
+};
+
+#endif
diff --git a/drivers/Kconfig b/drivers/Kconfig
index b5e6f24..e116562 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -52,6 +52,8 @@
source "drivers/spi/Kconfig"
+source "drivers/spi2/Kconfig"
+
source "drivers/pps/Kconfig"
source "drivers/ptp/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 1b31421..2fc7c1c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -53,6 +53,7 @@
obj-$(CONFIG_TARGET_CORE) += target/
obj-$(CONFIG_MTD) += mtd/
obj-$(CONFIG_SPI) += spi/
+obj-$(CONFIG_SPI2) += spi2/
obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index b175000..c697159 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -357,4 +357,35 @@
return (cap & 0x1f) + 1;
}
+#if defined (CONFIG_COMCERTO_AHCI_PROF)
+
+#define MAX_AHCI_PORTS 4
+#define MAX_AHCI_SLOTS 32
+#define MAX_BINS 64
+#define US_SHIFT 8
+#define BYTE_SHIFT 14
+#define RATE_SHIFT 2
+
+struct ahci_port_stats {
+ struct timeval first_issue;
+ unsigned int pending_flag;
+ unsigned int nb_pending;
+ unsigned int nb_pending_max;
+ unsigned int nb_pending_total;
+ unsigned int bytes_pending;
+ unsigned int diff_us;
+ unsigned int pending_counter[MAX_BINS];
+ unsigned int rate_counter[MAX_BINS];
+
+ unsigned int init_prof;
+ unsigned int time_counter[MAX_BINS]; // 128us -> 16ms
+ unsigned int data_counter[MAX_BINS]; // 4K-> 1020K
+ unsigned int no_free_slot;
+ struct timeval last_req;
+};
+
+extern struct ahci_port_stats ahci_port_stats[MAX_AHCI_PORTS];
+extern unsigned int enable_ahci_prof;
+#endif
+
#endif /* _AHCI_H */
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 43b8758..d1d9d58 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -21,7 +21,21 @@
#include <linux/platform_device.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
+#include <linux/clk.h>
+#include <mach/reset.h>
+#include <mach/comcerto-2000/pm.h>
#include "ahci.h"
+#include <mach/serdes-c2000.h>
+
+#ifdef CONFIG_ARCH_M86XXX
+/* SATA Clocks */
+static struct clk *sata_oob_clk; /* Core clock */
+static struct clk *sata_pmu_clk; /* PMU alive clock */
+static struct clk *sata_clk; /* Sata AXI ref clock */
+#if defined(CONFIG_COMCERTO_SATA_OCC_CLOCK)
+static struct clk *sata_occ_clk; /* sata OCC clock */
+#endif
+#endif
enum ahci_type {
AHCI, /* standard platform ahci */
@@ -62,6 +76,91 @@
AHCI_SHT("ahci_platform"),
};
+#ifdef CONFIG_PM
+static int ahci_platform_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ata_host *host = platform_get_drvdata(pdev);
+ int ret=0;
+
+#ifdef CONFIG_ARCH_M86XXX
+ /* Check for the Bit_Mask bit for SATA, if it is enabled
+ * then we are not going suspend the SATA device , as by
+ * this device , we will wake from System Resume.
+ */
+ if ( !(host_utilpe_shared_pmu_bitmask & SATA_IRQ )){
+
+ /* We will Just return
+ */
+ return ret;
+ }
+#endif
+
+ if (host)
+ ret = ata_host_suspend(host, state);
+
+#ifdef CONFIG_ARCH_M86XXX
+ if (!ret) /* sucessfully done the host suspend */
+ {
+ /* No do the clock disable PMU,OOB,AXI here */
+ clk_disable(sata_clk);
+ clk_disable(sata_oob_clk);
+ clk_disable(sata_pmu_clk);
+
+ /* PM Performance Enhancement : SRDS1 PD SATA1/SRDS2 PD SATA2 - P2 state, */
+ /* Resets the entire PHY module and CMU power down */
+ if (readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES1_CNF_SATA0)
+ writel((readl((COMCERTO_DWC1_CFG_BASE+0x44)) | 0xCC), (COMCERTO_DWC1_CFG_BASE+0x44));
+ else if (readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES2_CNF_SATA1)
+ writel((readl((COMCERTO_DWC1_CFG_BASE+0x54)) | 0xCC), (COMCERTO_DWC1_CFG_BASE+0x54));
+
+ }
+#endif
+
+ return ret;
+}
+
+static int ahci_platform_resume(struct platform_device *pdev)
+{
+ struct ata_host *host = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_ARCH_M86XXX
+ /* PM Performance Enhancement : SRDS1 PD SATA1/SRDS2 PD SATA2 - P2 state, */
+ /* Enable PHY module and CMU power UP */
+ if (readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES1_CNF_SATA0)
+ writel((readl((COMCERTO_DWC1_CFG_BASE+0x44)) & ~0xCC), (COMCERTO_DWC1_CFG_BASE+0x44));
+ else if (readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES2_CNF_SATA1)
+ writel((readl((COMCERTO_DWC1_CFG_BASE+0x54)) & ~0xCC), (COMCERTO_DWC1_CFG_BASE+0x54));
+
+ /* Check for the Bit_Mask bit for SATA, if it is enabled
+ * then we are not going suspend the SATA device , as by
+ * this device , we will wake from System Resume.
+ */
+
+ if ( !(host_utilpe_shared_pmu_bitmask & SATA_IRQ )){
+
+ /* We will Just return
+ */
+ return 0;
+ }
+
+ /* Do the clock enable here PMU,OOB,AXI */
+ clk_enable(sata_clk);
+ clk_enable(sata_oob_clk);
+ clk_enable(sata_pmu_clk);
+#endif
+
+ if (host)
+ ata_host_resume(host);
+
+ return 0;
+}
+#else
+#define ahci_platform_suspend NULL
+#define ahci_platform_resume NULL
+#endif
+
+
+
static int __init ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -76,7 +175,66 @@
int n_ports;
int i;
int rc;
+#ifdef CONFIG_ARCH_M86XXX
+ /* Get the Reference and Enable the SATA clocks here */
+ sata_clk = clk_get(NULL,"sata");
+ /* Error Handling , if no SATA(AXI) clock reference: return error */
+ if (IS_ERR(sata_clk)) {
+ pr_err("%s: Unable to obtain SATA(AXI) clock: %ld\n",__func__,PTR_ERR(sata_clk));
+ return PTR_ERR(sata_clk);
+ }
+
+ /*Enable the SATA(AXI) clock here */
+ rc = clk_enable(sata_clk);
+ if (rc){
+ pr_err("%s: SATA(AXI) clock enable failed \n",__func__);
+ return rc;
+ }
+ sata_oob_clk = clk_get(NULL,"sata_oob");
+ /* Error Handling , if no SATA_OOB clock reference: return error */
+ if (IS_ERR(sata_oob_clk)) {
+ pr_err("%s: Unable to obtain SATA_OOB clock: %ld\n",__func__,PTR_ERR(sata_oob_clk));
+ return PTR_ERR(sata_oob_clk);
+ }
+
+ sata_pmu_clk = clk_get(NULL,"sata_pmu");
+ /* Error Handling , if no SATA_PMU clock reference: return error */
+ if (IS_ERR(sata_pmu_clk)) {
+ pr_err("%s: Unable to obtain SATA_PMU clock: %ld\n",__func__,PTR_ERR(sata_pmu_clk));
+ return PTR_ERR(sata_pmu_clk);
+ }
+ /*Enable the SATA(PMU and OOB) clocks here */
+ rc = clk_enable(sata_oob_clk);
+ if (rc){
+ pr_err("%s: SATA_OOB clock enable failed \n",__func__);
+ return rc;
+ }
+
+ rc = clk_enable(sata_pmu_clk);
+ if (rc){
+ pr_err("%s: SATA_PMU clock enable failed \n",__func__);
+ return rc;
+ }
+#if defined(CONFIG_COMCERTO_SATA_OCC_CLOCK)
+ sata_occ_clk = clk_get(NULL,"sata_occ");
+ /* Error Handling , if no sata occ clock reference: return error */
+ if (IS_ERR(sata_occ_clk)) {
+ pr_err("%s: Unable to obtain sata occ clock: %ld\n",__func__,PTR_ERR(sata_occ_clk));
+ return PTR_ERR(sata_occ_clk);
+ }
+ /*Enable the sata_occ clocks here */
+ rc = clk_enable(sata_occ_clk);
+ if (rc){
+ pr_err("%s: sata occ clock enable failed \n",__func__);
+ return rc;
+ }
+#endif
+ /* Set the SATA PMU clock to 30 MHZ and OOB clock to 125MHZ */
+ clk_set_rate(sata_oob_clk,125000000);
+ clk_set_rate(sata_pmu_clk,30000000);
+
+#endif
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(dev, "no mmio space\n");
@@ -164,6 +322,12 @@
if (ap->flags & ATA_FLAG_EM)
ap->em_message_type = hpriv->em_msg_type;
+#ifdef CONFIG_ARCH_M86XXX
+ /* Optimized PFE/SATA DDR interaction,
+ limit read burst size of SATA controller */
+ writel(0x41, ahci_port_base(ap) + 0x70);
+#endif
+
/* disabled/not-implemented port */
if (!(hpriv->port_map & (1 << i)))
ap->ops = &ata_dummy_port_ops;
@@ -198,6 +362,31 @@
if (pdata && pdata->exit)
pdata->exit(dev);
+#ifdef CONFIG_ARCH_M86XXX
+ /* Disbale the SATA clocks Here */
+ clk_disable(sata_clk);
+ clk_put(sata_clk);
+ clk_disable(sata_oob_clk);
+ clk_put(sata_oob_clk);
+ clk_disable(sata_pmu_clk);
+ clk_put(sata_pmu_clk);
+#if defined(CONFIG_COMCERTO_SATA_OCC_CLOCK)
+ clk_disable(sata_occ_clk);
+ clk_put(sata_occ_clk);
+#endif
+ /*Putting SATA in reset state
+ * Sata axi clock domain in reset state
+ * Serdes 1/2 in reset state, this depends upon PCIE1 and SGMII
+ * sata 0/1 serdes controller in reset state
+ */
+ c2000_block_reset(COMPONENT_AXI_SATA,1);
+
+ c2000_block_reset(COMPONENT_SERDES1,1);
+ c2000_block_reset(COMPONENT_SERDES_SATA0,1);
+
+ c2000_block_reset(COMPONENT_SERDES2,1);
+ c2000_block_reset(COMPONENT_SERDES_SATA1,1);
+#endif
return 0;
}
@@ -209,13 +398,17 @@
MODULE_DEVICE_TABLE(of, ahci_of_match);
static struct platform_driver ahci_driver = {
- .remove = __devexit_p(ahci_remove),
- .driver = {
- .name = "ahci",
- .owner = THIS_MODULE,
- .of_match_table = ahci_of_match,
+ .remove = __devexit_p(ahci_remove),
+#ifdef CONFIG_PM
+ .suspend = ahci_platform_suspend,
+ .resume = ahci_platform_resume,
+#endif
+ .driver = {
+ .name = "ahci",
+ .owner = THIS_MODULE,
+ .of_match_table = ahci_of_match,
},
- .id_table = ahci_devtype,
+ .id_table = ahci_devtype,
};
static int __init ahci_init(void)
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 3c92dbd..a0e3c7c 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1724,6 +1724,8 @@
}
}
+/*
+*/
irqreturn_t ahci_interrupt(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
@@ -1808,6 +1810,57 @@
pp->fbs_last_dev = qc->dev->link->pmp;
}
+#if defined(CONFIG_COMCERTO_AHCI_PROF)
+ if (enable_ahci_prof) {
+ struct ahci_port_stats *stats = &ahci_port_stats[ap->port_no];
+ struct timeval now;
+ int bin;
+
+ do_gettimeofday(&now);
+
+ if (stats->init_prof) {
+ int diff_time_us;
+
+ diff_time_us = (now.tv_sec - stats->last_req.tv_sec) * 1000 * 1000 + (now.tv_usec - stats->last_req.tv_usec);
+
+ bin = diff_time_us >> US_SHIFT;
+ if (bin >= MAX_BINS)
+ bin = MAX_BINS - 1;
+
+ stats->time_counter[bin]++;
+ }
+ else {
+ stats->init_prof = 1;
+ }
+
+ stats->last_req = now;
+
+ bin = qc->nbytes >> BYTE_SHIFT;
+ if (bin >= MAX_BINS)
+ bin = MAX_BINS - 1;
+
+ stats->data_counter[bin]++;
+
+ if (!stats->nb_pending) {
+ stats->first_issue = now;
+ stats->nb_pending_total = 0;
+ }
+
+ stats->nb_pending_total++;
+
+ /* This should never overflow */
+ stats->pending_counter[stats->nb_pending & (MAX_AHCI_SLOTS - 1)]++;
+
+ stats->nb_pending++;
+
+ if (stats->nb_pending_total > stats->nb_pending_max)
+ stats->nb_pending_max = stats->nb_pending_total;
+
+ stats->bytes_pending += qc->nbytes;
+ stats->pending_flag |= 1 << qc->tag;
+ }
+#endif
+
writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE);
ahci_sw_activity(qc->dev->link);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index c04ad68..0d285e2 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4663,6 +4663,15 @@
#endif /* __BIG_ENDIAN */
}
+#if defined(CONFIG_COMCERTO_AHCI_PROF)
+
+#include "ahci.h"
+
+struct ahci_port_stats ahci_port_stats[MAX_AHCI_PORTS];
+unsigned int enable_ahci_prof = 0;
+
+#endif
+
/**
* ata_qc_new - Request an available ATA command, for queueing
* @ap: target port
@@ -4690,6 +4699,13 @@
if (qc)
qc->tag = i;
+#if defined(CONFIG_COMCERTO_AHCI_PROF)
+ if (enable_ahci_prof)
+ if (qc == NULL) {
+ ahci_port_stats[ap->port_no].no_free_slot++;
+ }
+#endif
+
return qc;
}
@@ -4741,6 +4757,48 @@
if (likely(ata_tag_valid(tag))) {
qc->tag = ATA_TAG_POISON;
clear_bit(tag, &ap->qc_allocated);
+
+#if defined(CONFIG_COMCERTO_AHCI_PROF)
+ if (enable_ahci_prof) {
+ struct ahci_port_stats *stats = &ahci_port_stats[ap->port_no];
+
+ if (stats->pending_flag & (1 << tag)) {
+ stats->pending_flag &= ~(1 << tag);
+ stats->nb_pending--;
+
+ if (!stats->nb_pending) {
+ struct timeval now;
+ int diff_time_us;
+ unsigned int rate;
+ int bin;
+
+ do_gettimeofday(&now);
+
+ diff_time_us = ((now.tv_sec - stats->first_issue.tv_sec) * 1000 * 1000) +
+ (now.tv_usec - stats->first_issue.tv_usec);
+
+ stats->diff_us += diff_time_us;
+
+ /* Do the average for at least 10MiB of data transfered */
+ if (stats->bytes_pending > (10 * (1 << 20))) {
+
+ rate = ((stats->bytes_pending / stats->diff_us) * 1000 * 1000) >> 20; //MiBps
+
+ bin = rate >> RATE_SHIFT;
+ if (bin >= MAX_BINS)
+ bin = MAX_BINS - 1;
+
+ /* Track how many KiB were transfered at this rate */
+ stats->rate_counter[bin] += stats->bytes_pending >> 10;
+
+ /* Reset stats */
+ stats->bytes_pending = 0;
+ stats->diff_us = 0;
+ }
+ }
+ }
+ }
+#endif
}
}
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index b96544a..1955d73 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1146,3 +1146,192 @@
return async_error;
}
EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
+
+
+/*
+ * Code added to suppoprt device SUSPEND(L1 and L2 ) and RESUME
+ * (L1 = clock gating L2 = clock gating + reset) .
+ * Depends upon config option CONFIG_PM_SYSFS_MANUAL
+ *
+ */
+
+#ifdef CONFIG_PM_SYSFS_MANUAL
+
+static DEFINE_MUTEX(dpm_lock);
+
+/**
+ * dpm_manual_resume - resume the device .
+ * uses device_resume and device_complete to acheive feature.
+ * part of the code borrowed from dpm_resume and dpm_resume_complete.
+ * @dev: Device.
+ * @state: State to enter.
+*/
+
+void dpm_manual_resume(struct device *dev,pm_message_t state)
+{
+ int error;
+ struct list_head list;
+ ktime_t starttime = ktime_get();
+
+ might_sleep();
+
+ /* Device resume prepare starts here */
+ mutex_lock(&dpm_list_mtx);
+ pm_transition = state;
+ INIT_COMPLETION(dev->power.completion);
+ mutex_unlock(&dpm_list_mtx);
+
+ error = device_resume(dev, state, false);
+ if (error) {
+ suspend_stats.failed_resume++;
+ dpm_save_failed_step(SUSPEND_RESUME);
+ dpm_save_failed_dev(dev_name(dev));
+ pm_dev_err(dev, state, "", error);
+ }
+
+ mutex_lock(&dpm_list_mtx);
+ if (!list_empty(&dev->power.entry))
+ list_move_tail(&dev->power.entry, &dpm_prepared_list);
+ mutex_unlock(&dpm_list_mtx);
+
+ /* DPM complete start */
+ INIT_LIST_HEAD(&list);
+ mutex_lock(&dpm_list_mtx);
+ dev->power.is_prepared = false;
+ list_move(&dev->power.entry, &list);
+ mutex_unlock(&dpm_list_mtx);
+
+ device_complete(dev, state);
+ dev->power.power_state=state;
+ dpm_show_time(starttime, state, NULL);
+}
+
+
+/**
+ * dpm_manual_resume_start - Start the process for Power one device back to work.
+ * @dev: Device.
+ * @state: State to enter.
+ * Code inspired from dpm_resume_end().
+ *
+ * Bring one device back to the on state by first powering it
+ * on, then restoring state. We only operate on devices that aren't
+ * already on.
+ */
+
+void dpm_manual_resume_start(struct device * dev,pm_message_t state)
+{
+ mutex_lock(&dpm_lock);
+ if (dev->power.power_state.event == state.event){
+ printk(KERN_ERR "PM: We are already in the resume state \n");
+ goto done;
+ }
+ /* Device resume starts from here */
+ dpm_manual_resume(dev,state);
+done:
+ mutex_unlock(&dpm_lock);
+
+}
+
+/**
+ * dpm_manual_prepare - prepare the device for power transition.
+ * Part of the code borrowed from dpm_prapare.
+ * @dev: Device.
+ * @state: State to enter.
+ */
+static int dpm_manual_prepare(struct device * dev , pm_message_t state)
+{
+ /* This part of code is borrowed from dpm_prepare
+ * make the deice for prepare.
+ */
+ int error = 0;
+ might_sleep();
+
+ /* Call the device prepare */
+ error = device_prepare(dev, state);
+
+ mutex_lock(&dpm_list_mtx);
+ if (error){
+ printk(KERN_INFO "PM: Device %s not prepared " "for power transition: code %d\n",
+ dev_name(dev), error);
+ goto done;
+ }
+ dev->power.is_prepared = true;
+ if (!list_empty(&dev->power.entry))
+ list_move_tail(&dev->power.entry, &dpm_prepared_list);
+
+done:
+ mutex_unlock(&dpm_list_mtx);
+ return error;
+}
+
+/**
+ * dpm_manual_suspend - Helper routing to call the device_suspend.
+ * Part of the code borrowed from dpm_supend().
+ * @dev: Device.
+ * @state: State to enter.
+ */
+static int dpm_manual_suspend(struct device * dev, pm_message_t state)
+{
+ ktime_t starttime;
+ int error=0;
+
+ might_sleep();
+
+ mutex_lock(&dpm_list_mtx);
+ pm_transition = state;
+ mutex_unlock(&dpm_list_mtx);
+
+ error = device_suspend(dev);
+
+ mutex_lock(&dpm_list_mtx);
+ if (error){
+ pm_dev_err(dev, state, "", error);
+ dpm_save_failed_dev(dev_name(dev));
+ }
+ if (!list_empty(&dev->power.entry))
+ list_move(&dev->power.entry, &dpm_suspended_list);
+ mutex_unlock(&dpm_list_mtx);
+
+ dev->power.power_state=state;
+ dpm_show_time(starttime, state, NULL);
+ return error;
+}
+
+/**
+ * dpm_manual_suspend_start - Put one device in Power of L1/L2 state.
+ * Power off L1 - clock gating , Power off L2 - clock gating + device reset
+ * Part of the code borrowed from dpm_suspend_start.
+ * @dev: Device.
+ * @state: State to enter.
+ */
+int dpm_manual_suspend_start(struct device * dev, pm_message_t state)
+{
+ int error=0;
+
+ /* Start the global mutex value*/
+ mutex_lock(&dpm_lock);
+
+ if (dev->power.power_state.event == state.event){
+ if ( state.event == PM_EVENT_SUSPEND )
+ printk(KERN_ERR "PM: We are already in the suspend (power off L1) state \n");
+#if 0
+ else if ( state.event == PM_EVENT_SUSPEND_L2)
+ printk(KERN_ERR "PM: We are already in the suspend (Power off L2) state \n");
+#endif
+ goto done;
+ }
+
+ /* Devce PM prepare starts from here */
+ error=dpm_manual_prepare(dev,state);
+
+ if (error){
+ suspend_stats.failed_prepare++;
+ dpm_save_failed_step(SUSPEND_PREPARE);
+ goto done;
+ }else
+ error = dpm_manual_suspend(dev,state);
+done:
+ mutex_unlock(&dpm_lock);
+ return error;
+}
+#endif
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
index 95706fa..fe10b841 100644
--- a/drivers/base/power/opp.c
+++ b/drivers/base/power/opp.c
@@ -330,6 +330,7 @@
{
struct device_opp *dev_opp;
struct opp *temp_opp, *opp = ERR_PTR(-ENODEV);
+ int c = 0;
if (!dev || !freq) {
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
@@ -341,10 +342,15 @@
return opp;
list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
+ ++c;
if (temp_opp->available) {
/* go to the next node, before choosing prev */
if (temp_opp->rate > *freq)
+ {
+ if (c == 1)
+ opp = temp_opp;
break;
+ }
else
opp = temp_opp;
}
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 9bf6232..8e9d09d 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -81,3 +81,15 @@
static inline void wakeup_sysfs_remove(struct device *dev) {}
#endif
+
+/* Added for SYSFS support to handle from Device power management from
+ * user space. Manual PM configuration.
+ */
+
+#ifdef CONFIG_PM_SYSFS_MANUAL
+
+extern int dpm_manual_suspend_start(struct device * , pm_message_t );
+extern void dpm_manual_resume_start(struct device * , pm_message_t);
+
+#endif /* CONFIG_PM_SYSFS_MANUAL */
+
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index adf41be0..1d2db86 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -89,6 +89,12 @@
* value are used only if the driver calls pm_runtime_use_autosuspend().
*
* wakeup_count - Report the number of wakeup events related to the device
+ *
+ * MSPD: Added the support for manual PM operation for NON-CPU devices.
+ * This is operated through power/state file . operates in two states
+ * PM_EVENT_SUSPEND= Power off L1 state (device Clock gating )
+ * PM_EVENT_SUSPEND_L2=Power off L2 state ( device Clock gating + reset).
+ *
*/
static const char enabled[] = "enabled";
@@ -97,6 +103,44 @@
const char power_group_name[] = "power";
EXPORT_SYMBOL_GPL(power_group_name);
+#ifdef CONFIG_PM_SYSFS_MANUAL
+static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf)
+{
+ if (dev->power.power_state.event == PM_EVENT_SUSPEND) /* Power off L1 state */
+ return sprintf(buf, "2\n");
+#if 0
+ else if (dev->power.power_state.event == PM_EVENT_SUSPEND_L2) /* Power off L2 state */
+ return sprintf(buf, "3\n");
+#endif
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t state_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t n)
+{
+ pm_message_t state;
+ int error = -EINVAL;
+
+ if ((n == 2) && (buf[0] == '2')) {
+ state.event = PM_EVENT_SUSPEND;
+ error = dpm_manual_suspend_start(dev, state); /* Power off L1 state */
+ }
+#if 0
+ if ((n == 2) && (buf[0] == '3')) {
+ state.event = PM_EVENT_SUSPEND_L2;
+ error = dpm_manual_suspend(dev, state); /* Power off L2 state */
+ }
+#endif
+ if ((n == 2) && (buf[0] == '0')) {
+ state.event = PM_EVENT_RESUME;
+ dpm_manual_resume_start(dev, state);
+ error = 0;
+ }
+ return error ? error : n;
+}
+static DEVICE_ATTR(state, 0644, state_show, state_store);
+#endif
+
#ifdef CONFIG_PM_RUNTIME
static const char ctrl_auto[] = "auto";
static const char ctrl_on[] = "on";
@@ -432,11 +476,13 @@
return -EINVAL;
return n;
}
-
static DEVICE_ATTR(async, 0644, async_show, async_store);
#endif /* CONFIG_PM_ADVANCED_DEBUG */
static struct attribute *power_attrs[] = {
+#ifdef CONFIG_PM_SYSFS_MANUAL
+ &dev_attr_state.attr,
+#endif
#ifdef CONFIG_PM_ADVANCED_DEBUG
#ifdef CONFIG_PM_SLEEP
&dev_attr_async.attr,
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 6035ab8..8c3acdf 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -130,6 +130,9 @@
* void add_interrupt_randomness(int irq);
* void add_disk_randomness(struct gendisk *disk);
*
+ * void random_input_words(__u32 *buf, size_t wordcount, int ent_count)
+ * int random_input_wait(void);
+ *
* add_input_randomness() uses the input layer interrupt timing, as well as
* the event type information from the hardware.
*
@@ -147,6 +150,13 @@
* seek times do not make for good sources of entropy, as their seek
* times are usually fairly consistent.
*
+ * random_input_words() just provides a raw block of entropy to the input
+ * pool, such as from a hardware entropy generator.
+ *
+ * random_input_wait() suspends the caller until such time as the
+ * entropy pool falls below the write threshold, and returns a count of how
+ * much entropy (in bits) is needed to sustain the pool.
+ *
* All of these routines try to estimate how many bits of randomness a
* particular randomness source. They do this by keeping track of the
* first and second order deltas of the event timings.
@@ -722,6 +732,63 @@
}
#endif
+/*
+ * random_input_words - add bulk entropy to pool
+ *
+ * @buf: buffer to add
+ * @wordcount: number of __u32 words to add
+ * @ent_count: total amount of entropy (in bits) to credit
+ *
+ * this provides bulk input of entropy to the input pool
+ *
+ */
+void random_input_words(__u32 *buf, size_t wordcount, int ent_count)
+{
+ mix_pool_bytes(&input_pool, buf, wordcount*4);
+
+ credit_entropy_bits(&input_pool, ent_count);
+
+ DEBUG_ENT("crediting %d bits => %d\n",
+ ent_count, input_pool.entropy_count);
+ /*
+ * Wake up waiting processes if we have enough
+ * entropy.
+ */
+ if (input_pool.entropy_count >= random_read_wakeup_thresh)
+ wake_up_interruptible(&random_read_wait);
+}
+EXPORT_SYMBOL(random_input_words);
+
+/*
+ * random_input_wait - wait until random needs entropy
+ *
+ * this function sleeps until the /dev/random subsystem actually
+ * needs more entropy, and then return the amount of entropy
+ * that it would be nice to have added to the system.
+ */
+int random_input_wait(void)
+{
+ int count;
+
+ wait_event_interruptible(random_write_wait,
+ input_pool.entropy_count < random_write_wakeup_thresh);
+
+ count = random_write_wakeup_thresh - input_pool.entropy_count;
+
+ /* likely we got woken up due to a signal */
+ if (count <= 0) count = random_read_wakeup_thresh;
+
+ DEBUG_ENT("requesting %d bits from input_wait()er %d<%d\n",
+ count,
+ input_pool.entropy_count, random_write_wakeup_thresh);
+
+ return count;
+}
+EXPORT_SYMBOL(random_input_wait);
+
+
+#define EXTRACT_SIZE 10
+
/*********************************************************************
*
* Entropy extraction routines
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index e24a2a1..c08b968 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -45,7 +45,7 @@
choice
prompt "Default CPUFreq governor"
default CPU_FREQ_DEFAULT_GOV_USERSPACE if CPU_FREQ_SA1100 || CPU_FREQ_SA1110
- default CPU_FREQ_DEFAULT_GOV_PERFORMANCE
+ default CPU_FREQ_DEFAULT_GOV_ONDEMAND
help
This option sets which CPUFreq governor shall be loaded at
startup. If in doubt, select 'performance'.
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 72a0044..ad6b9bfb 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -30,3 +30,8 @@
SoC (S5PV310 or S5PC210).
If in doubt, say N.
+
+config CPU_FREQ_C2K
+ bool "CPUfreq driver for Comcerto"
+ depends on ARCH_COMCERTO && CPU_FREQ
+ default y
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index a48bc02..5cc3681 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -47,3 +47,4 @@
##################################################################################
# PowerPC platform drivers
obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o
+obj-$(CONFIG_CPU_FREQ_C2K) += c2k-cpufreq.o
diff --git a/drivers/cpufreq/c2k-cpufreq.c b/drivers/cpufreq/c2k-cpufreq.c
new file mode 100644
index 0000000..dd3b9c4
--- /dev/null
+++ b/drivers/cpufreq/c2k-cpufreq.c
@@ -0,0 +1,250 @@
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+#include <linux/gfp.h>
+
+#include <asm/system.h>
+
+#include <mach/hardware.h>
+#include <linux/netdevice.h>
+#include <linux/clk.h>
+
+#define NR_FREQS 15
+
+//#define C2K_CPUFREQ_DEBUG
+#ifdef C2K_CPUFREQ_DEBUG
+ #define c2k_cpufreq_debug(fmt, arg...) printk(fmt, ##arg)
+#else
+ #define c2k_cpufreq_debug(fmt, arg...) ;
+#endif
+
+extern struct cpufreq_governor cpufreq_gov_ondemand;
+
+static struct cpufreq_frequency_table comcerto_clk_frqs[NR_FREQS + 1];
+
+static int comcerto_verify_speed(struct cpufreq_policy *policy)
+{
+ if (policy->cpu < 0)
+ return -EINVAL;
+
+ return cpufreq_frequency_table_verify(policy, comcerto_clk_frqs);
+}
+
+static unsigned int comcerto_getspeed(unsigned int cpu)
+{
+ struct clk *clk_arm;
+
+ if (cpu < 0)
+ return -EINVAL;
+
+ c2k_cpufreq_debug ("%s: Get speed for cpu(%d)\n", __func__, cpu);
+
+ clk_arm = clk_get(NULL,"arm");
+ if (IS_ERR(clk_arm)) {
+ pr_err("cpufreq: Unable to obtain ARMCLK: %ld\n",
+ PTR_ERR(clk_arm));
+ return PTR_ERR(clk_arm);
+ }
+
+ return clk_get_rate(clk_arm); /* we get freq in hz */
+}
+
+static int comcerto_set_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct cpufreq_freqs freqs;
+ unsigned int index;
+ struct clk *clk_arm;
+ int ret;
+ int j = 0;
+
+ ret = cpufreq_frequency_table_target(policy, comcerto_clk_frqs, target_freq, relation, &index);
+ if (ret < 0)
+ return ret;
+
+ if (policy->cpu >= NR_CPUS) {
+ pr_debug("%s: couldn't limit to CPUs in this domain\n", \
+ __func__);
+ return -EAGAIN;
+ }
+
+ clk_arm = clk_get(NULL,"arm");
+ if (IS_ERR(clk_arm)) {
+ pr_err("%s: cpufreq: Unable to obtain ARMCLK: %ld\n", __func__,\
+ PTR_ERR(clk_arm));
+ return PTR_ERR(clk_arm);
+ }
+
+ freqs.old = comcerto_getspeed(policy->cpu);
+ if (freqs.old == target_freq)
+ {
+ c2k_cpufreq_debug ("%s: old freq (%d) equals new freq(%d).\n", \
+ __func__, freqs.old, target_freq);
+ return 0;
+ }
+
+ freqs.new = comcerto_clk_frqs[index].frequency;
+
+ c2k_cpufreq_debug("%s: Transition(cpu:%d) %d-%dHz\n", __func__,\
+ policy->cpu, freqs.old, freqs.new);
+
+ while (j < NR_CPUS)
+ {
+ if (!cpu_online(j))
+ {
+ j++;
+ continue;
+ }
+ freqs.cpu = j;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ j++;
+ }
+ j = 0;
+
+ policy->cur = freqs.new;
+ ret = clk_set_rate(clk_arm, freqs.new);
+ if (ret < 0) {
+ pr_debug("cpufreq: Failed to set rate %dHz: %d\n",
+ freqs.new, ret);
+ j = 0;
+ goto err;
+ }
+
+ while (j < NR_CPUS)
+ {
+ if (!cpu_online(j))
+ {
+ j++;
+ continue;
+ }
+ freqs.cpu = j;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ j++;
+ }
+
+ c2k_cpufreq_debug("%s:Set actual frequency %luHz\n", __func__, \
+ clk_get_rate(clk_arm));
+
+ return 0;
+err:
+ policy->cur = freqs.old;
+ freqs.new = freqs.old;
+ while (j < NR_CPUS)
+ {
+ if (!cpu_online(j))
+ {
+ j++;
+ continue;
+ }
+ freqs.cpu = j;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ j++;
+ }
+
+ return ret;
+}
+
+static int comcerto_init_table(int cpu)
+{
+ int i;
+ struct clk *clk_arm;
+ struct clk *clk_pll;
+
+ if (cpu != 0)
+ return 0;
+
+ clk_arm = clk_get(NULL,"arm");
+ if (IS_ERR(clk_arm)) {
+ pr_err("cpufreq: Unable to obtain ARMCLK: %ld\n",
+ PTR_ERR(clk_arm));
+ return PTR_ERR(clk_arm);
+ }
+
+ clk_pll = clk_get_parent(clk_arm);
+
+ comcerto_clk_frqs[0].frequency = clk_get_rate(clk_arm);
+ comcerto_clk_frqs[0].index = clk_get_rate(clk_pll)/clk_get_rate(clk_arm);
+
+ c2k_cpufreq_debug("\n\n ### cpufreq Table ###\n");
+
+ c2k_cpufreq_debug ("comcerto_clk_frqs[0].index = %d, comcerto_clk_frqs[0].frequency = %d\n",\
+ comcerto_clk_frqs[0].index, comcerto_clk_frqs[0].frequency);
+
+ for (i = 1; comcerto_clk_frqs[0].index + i <= NR_FREQS; i++)
+ {
+ comcerto_clk_frqs[i].frequency = (comcerto_clk_frqs[0].frequency \
+ * comcerto_clk_frqs[0].index) / (comcerto_clk_frqs[0].index + i);
+
+ comcerto_clk_frqs[i].index = comcerto_clk_frqs[0].index + i;
+
+ c2k_cpufreq_debug ("comcerto_clk_frqs[%d].index = %d, comcerto_clk_frqs[%d].frequency = %d\n",\
+ i, comcerto_clk_frqs[i].index, i, comcerto_clk_frqs[i].frequency);
+ }
+
+ comcerto_clk_frqs[i].frequency = CPUFREQ_TABLE_END;
+
+ return 0;
+}
+
+static int comcerto_cpu_init(struct cpufreq_policy *policy)
+{
+ comcerto_init_table(policy->cpu);
+
+ cpufreq_frequency_table_cpuinfo(policy, comcerto_clk_frqs);
+
+ cpufreq_frequency_table_get_attr(comcerto_clk_frqs, policy->cpu);
+
+ /* PLL stabalisation time */
+ policy->cpuinfo.transition_latency = 10000; /* 10us */
+
+ /* Current Frequency */
+ policy->cur = comcerto_clk_frqs[0].frequency;
+
+ /* Governor used */
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+ return 0;
+}
+
+static struct freq_attr *comcerto_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver comcerto_driver = {
+ .name = "comcerto",
+ .init = comcerto_cpu_init,
+ .verify = comcerto_verify_speed,
+ .target = comcerto_set_target,
+ .get = comcerto_getspeed,
+ .attr = comcerto_cpufreq_attr,
+ .flags = CPUFREQ_STICKY,
+ .owner = THIS_MODULE,
+};
+
+static int __init comcerto_cpufreq_init(void)
+{
+ printk ("Registering CPUFreq(%s)\n", comcerto_driver.name);
+
+ return cpufreq_register_driver(&comcerto_driver);
+}
+
+static void __exit comcerto_cpufreq_exit(void)
+{
+ cpufreq_unregister_driver(&comcerto_driver);
+}
+
+MODULE_AUTHOR ("Satendra Pratap <satendra.pratap@gmail.com>");
+MODULE_DESCRIPTION ("CPUFreq driver for Mindspeed's C2000 SoC");
+MODULE_LICENSE ("GPL");
+
+late_initcall(comcerto_cpufreq_init);
+module_exit(comcerto_cpufreq_exit);
+
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 987a165..c23af68 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -923,8 +923,10 @@
#ifdef CONFIG_HOTPLUG_CPU
for_each_online_cpu(sibling) {
struct cpufreq_policy *cp = per_cpu(cpufreq_cpu_data, sibling);
+
if (cp && cp->governor &&
(cpumask_test_cpu(cpu, cp->related_cpus))) {
+
policy->governor = cp->governor;
found = 1;
break;
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 8f04910..85302e6 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -65,4 +65,10 @@
comment "DEVFREQ Drivers"
+config COMCERTO_DEVFREQ_SUPPORT
+ bool "Enable Comcerto devfreq"
+ select DEVFREQ_GOV_SIMPLE_ONDEMAND
+ help
+ Adds devfreq support in Comcerto.
+
endif # PM_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 4564a89..af24e66 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -3,3 +3,4 @@
obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o
obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
+obj-$(CONFIG_COMCERTO_DEVFREQ_SUPPORT) += c2k-devfreq.o
diff --git a/drivers/devfreq/c2k-devfreq.c b/drivers/devfreq/c2k-devfreq.c
new file mode 100644
index 0000000..5919d0a
--- /dev/null
+++ b/drivers/devfreq/c2k-devfreq.c
@@ -0,0 +1,279 @@
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/opp.h>
+#include <linux/devfreq.h>
+
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/jiffies.h>
+
+#include <linux/clk.h>
+#include <linux/c2k-devfreq.h>
+
+//#define C2K_DEVFREQ_DEBUG
+
+int devfreq_counters_init(struct c2k_devfreq_data *data)
+{
+ devfreq_counters **dc = &data->dpc;
+
+ *dc = kzalloc(sizeof (devfreq_counters), GFP_KERNEL);
+ if (dc == NULL) {
+ printk ("%s: Cannot allocate memory for devfreq_counters.\n"\
+ , __func__);
+ return -ENOMEM;
+ }
+
+#ifdef C2K_DEVFREQ_USE_KTIME
+ (*dc)->start_time = ktime_get();
+#else
+ (*dc)->fentry_time = 0UL;
+ (*dc)->prev_count = 0UL;
+ (*dc)->busy_time = 0UL;
+ (*dc)->start_time = jiffies;
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(devfreq_counters_init);
+
+static int c2k_devfreq_pm_notifier_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct c2k_devfreq_data *data = container_of(&this, struct c2k_devfreq_data,
+ pm_notifier);
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ /* Deactivate DVFS */
+ mutex_lock(&data->lock);
+ data->disabled = true;
+ mutex_unlock(&data->lock);
+ return NOTIFY_OK;
+ case PM_POST_RESTORE:
+ case PM_POST_SUSPEND:
+ /* Reactivate */
+ mutex_lock(&data->lock);
+ data->disabled = false;
+ mutex_unlock(&data->lock);
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * @devfreq_target Returns desired operating frequency for the device.
+ * Basically, get_target_freq will run
+ * devfreq_dev_profile.get_dev_status() to get the
+ * status of the device (load = busy_time / total_time).
+ */
+static int devfreq_target(struct device *dev, unsigned long *freq)
+{
+ struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+ struct c2k_devfreq_data *data = platform_get_drvdata(pdev);
+ unsigned long old_freq = opp_get_freq(data->curr_opp);
+ struct opp *new_opp;
+ int err = 0;
+
+ mutex_lock(&data->lock);
+
+#ifdef C2K_DEVFREQ_DEBUG
+ c2k_devfreq_debug ("%s:current opp <f=%lu v=%lu>\n", __func__, \
+ opp_get_freq(data->curr_opp), opp_get_voltage(data->curr_opp));
+#endif
+ if (data->disabled)
+ goto out;
+
+ /* here...find corresponding OPP w.r.t |_*freq_| */
+ new_opp = opp_find_freq_floor(dev, freq);
+ if (IS_ERR(new_opp)) {
+ printk("%s: Invalid frequency %lu kHz.\n",
+ __func__, *freq);
+ err = PTR_ERR(new_opp);
+ goto out;
+ }
+#ifdef C2K_DEVFREQ_DEBUG
+ c2k_devfreq_debug ("%s:new opp <f=%lu v=%lu>\n", __func__, \
+ opp_get_freq(new_opp), opp_get_voltage(new_opp));
+#endif
+ data->curr_opp = new_opp;
+ *freq = opp_get_freq(new_opp);
+
+ /* now set freq */
+ if (old_freq != *freq)
+ {
+ if (*freq >= UINT_MAX)
+ *freq = data->max_freq;
+ if (*freq == 0)
+ *freq = data->min_freq;
+
+ err = data->set_freq(data, freq); /* uses clk struct */
+ }
+ else
+ goto out;
+
+ if (err)
+ goto out;
+
+ /* we could set voltage also */
+out:
+ mutex_unlock(&data->lock);
+ return err;
+}
+
+/*
+ * @total_time The total time represented by this instance of
+ * devfreq_dev_status
+ * @busy_time The time that the device was working among the
+ * total_time.
+ */
+static int get_devfreq_status(struct device *dev, struct devfreq_dev_status *stat)
+{
+ struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+ struct c2k_devfreq_data *data = platform_get_drvdata(pdev);
+ devfreq_counters *dc = data->dpc;
+
+ stat->current_frequency = opp_get_freq(data->curr_opp);
+
+ /* Here goes a list of mechanisms to fetch busy/total time:
+ - Performance counters
+ - Measure the time between "operation start" and "operation end" and
+ accumulate the time (getting busy time. probably by ktime?)
+ - Measure the idle time (CPUIDLE/CPUFREQ does this)
+ - Count the number of operation and calculate the operational time
+ based on the number.
+ */
+
+ stat->busy_time = (unsigned long)module_busy_time(dc);
+
+ stat->total_time = (unsigned long)module_busy_plus_not_busy_time(dc);
+
+ devfreq_reset_counters(dc);
+
+ return 0;
+}
+
+int c2k_driver_devfreq(struct device *dev, struct c2k_devfreq_data *data)
+{
+ struct devfreq *ldevfreq;
+ struct opp *opp;
+ int i, err = 0;
+
+ BUG_ON(data==NULL);
+
+#ifdef C2K_DEVFREQ_DEBUG
+ c2k_devfreq_debug("%s: initializing for dev %s\n", __func__, dev_name(dev));
+#endif
+ if (!(data->vdd_int))
+ {
+ data->vdd_int = regulator_get(dev, "c2k_default_vcc");
+
+ if (IS_ERR(data->vdd_int)) {
+ printk ("%s: Cannot get the regulator \"vcc_c2k\"\n", __func__);
+ err = PTR_ERR(data->vdd_int);
+ goto err_regulator;
+ }
+ }
+
+ data->dev = dev;
+ mutex_init(&data->lock);
+
+ /* OPP Entries assumed to be setup by now */
+ if (data->opp_table)
+ {
+ for (i = 0; data->opp_table[i].idx != 0; i++) {
+ #ifdef C2K_DEVFREQ_DEBUG
+ c2k_devfreq_debug("%s: Adding opp entry: %ld Hz, %ld uvolt.\n",\
+ __func__, data->opp_table[i].freq, data->opp_table[i].volt);
+ #endif
+ err = opp_add(dev, data->opp_table[i].freq, data->opp_table[i].volt);
+
+ if (err) {
+ printk("%s: Cannot add opp entries: err = %d\n",__func__, err);
+ goto err_opp_add;
+ }
+ }
+ }
+ else
+ {
+ printk ("%s: Not using OPP framework.\n", __func__);
+ }
+
+ if (data->devfreq_profile->initial_freq > 0)
+ {
+ opp = opp_find_freq_floor(dev, &(data->devfreq_profile->initial_freq));
+ if (IS_ERR(opp)) {
+ printk("%s: Invalid initial frequency %lu kHz.\n",
+ __func__, data->devfreq_profile->initial_freq);
+ err = PTR_ERR(opp);
+ goto err_opp_add;
+ }
+ data->curr_opp = opp;
+ }
+ else
+ return -EINVAL;
+
+ if (!data->devfreq_profile->target)
+ data->devfreq_profile->target = devfreq_target; /* using default */
+
+ if (!data->devfreq_profile->get_dev_status)
+ data->devfreq_profile->get_dev_status = get_devfreq_status; /* using default */
+
+ if (!data->gov)
+ data->gov = &devfreq_simple_ondemand; /* using default governer */
+
+ dev_set_drvdata(dev, data);
+
+ ldevfreq = devfreq_add_device(dev, data->devfreq_profile, data->gov, NULL);
+ if (!ldevfreq)
+ {
+ printk ("%s: devfreq_add_device failed with err = %d\n", __func__, err);
+ goto err_opp_add;
+ }
+
+ if (data->pm_notifier)
+ {
+ err = register_pm_notifier(data->pm_notifier);
+ if (err) {
+ printk ("%s: Failed to setup pm notifier.\n", __func__);
+ goto err_notifier;
+ }
+ }
+ else
+ {
+ data->pm_notifier = (struct notifier_block*)kzalloc\
+ (sizeof(struct notifier_block), GFP_KERNEL);
+
+ if (data->pm_notifier == NULL) {
+ printk ("%s: Cannot allocate memory for pm_notifier.\n",\
+ __func__);
+ return -ENOMEM;
+ }
+
+ data->pm_notifier->notifier_call = c2k_devfreq_pm_notifier_event; /* default pm notifier */
+
+ err = register_pm_notifier(data->pm_notifier);
+ if (err) {
+ printk ("%s: Failed to setup pm notifier.\n", __func__);
+ goto err_notifier;
+ }
+ }
+
+ err = devfreq_counters_init(data);
+
+ return err;
+
+err_notifier:
+ devfreq_remove_device(ldevfreq);
+err_opp_add:
+ regulator_put(data->vdd_int);
+err_regulator:
+ kfree(data);
+ return err;
+}
+EXPORT_SYMBOL(c2k_driver_devfreq);
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index da85c0d..c5005a9 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -122,6 +122,22 @@
---help---
Enable support for the Marvell XOR engine.
+config COMCERTO_XOR
+ tristate "Mindspeed Comcerto 2000 XOR engine support"
+ depends on ARCH_M86XXX
+ select DMA_ENGINE
+ select ASYNC_TX_ENABLE_CHANNEL_SWITCH
+ ---help---
+ Enable support for the Mindspeed XOR engine.
+
+config COMCERTO_DMA_BASIC
+ tristate "Mindspeed Comcerto 2000 MDMA engine basic support"
+ depends on ARCH_M86XXX
+ select DMA_ENGINE
+ select ASYNC_TX_ENABLE_CHANNEL_SWITCH
+ ---help---
+ Enable support for the Mindspeed mdma engine.
+
config MX3_IPU
bool "MX3x Image Processing Unit support"
depends on SOC_IMX31 || SOC_IMX35
@@ -272,6 +288,15 @@
If unsure, say N.
+config RAID_ZERO_COPY
+ bool "Optimized DMA/XOR offload: reduce raid5 memcpy which offloaded for dma"
+ depends on ASYNC_TX_DMA
+ help
+ This allows the async_tx api try to reduce raid5 memcpy operations for
+ dma. If you have dma device to support memcpy offloading, you can set
+ it as Y, else N.
+
+
config DMATEST
tristate "DMA Test client"
depends on DMA_ENGINE
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 30cf3b1..4e08999 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -26,3 +26,5 @@
obj-$(CONFIG_PCH_DMA) += pch_dma.o
obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
+obj-$(CONFIG_COMCERTO_XOR) += comcerto_xor.o
+obj-$(CONFIG_COMCERTO_DMA_BASIC) += c2k_dma.o
diff --git a/drivers/dma/c2k_dma.c b/drivers/dma/c2k_dma.c
new file mode 100644
index 0000000..c5b7d91
--- /dev/null
+++ b/drivers/dma/c2k_dma.c
@@ -0,0 +1,1178 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <asm/io.h>
+#include <mach/c2k_dma.h>
+#include <linux/dmaengine.h>
+#include <linux/circ_buf.h>
+#include <linux/delay.h>
+
+
+#define XOR_MAX_SRC_CNT 6
+
+#define virt_to_xor_dma(pbase, vbase, vaddr) ((pbase + ((unsigned long)vaddr - (unsigned long)vbase)))
+
+struct comcerto_xor_device {
+ struct dma_device device;
+};
+
+struct comcerto_xor_chan {
+ struct comcerto_xor_device *device;
+ struct dma_chan chan;
+ struct tasklet_struct irq_tasklet;
+ spinlock_t lock;
+ dma_cookie_t completed_cookie;
+};
+
+struct comcerto_sw_xor_desc {
+ struct dma_async_tx_descriptor async_tx;
+ int src_cnt;
+ size_t len;
+ dma_addr_t dma_dest;
+ dma_addr_t dma_src[XOR_MAX_SRC_CNT];
+};
+
+void *comcerto_xor_pool_virt;
+dma_addr_t comcerto_xor_pool_phy;
+
+spinlock_t comcerto_xor_cleanup_lock;
+static DECLARE_WAIT_QUEUE_HEAD(comcerto_xor_wait_queue);
+static int comcerto_xor_sleeping = 0;
+
+static int xor_rd_idx = 0;
+static int xor_wr_idx = 0;
+static int xor_dma_idx = 0;
+static int xor_sw_rd_idx = 0;
+static int xor_sw_wr_idx = 0;
+static int xor_sw_wr_prev_idx = 0;
+static int xor_current_batch_count = 0;
+
+#define XOR_FDESC_COUNT 512
+#if defined(CONFIG_COMCERTO_64K_PAGES)
+#define XOR_SW_FDESC_COUNT 32
+#else
+#define XOR_SW_FDESC_COUNT XOR_FDESC_COUNT
+#endif
+
+struct timer_list comcerto_xor_timer;
+#define COMPLETION_TIMEOUT msecs_to_jiffies(100)
+static int comerto_xor_timer_first = 1;
+
+struct comcerto_xor_inbound_fdesc *xor_in_fdesc[XOR_FDESC_COUNT];
+struct comcerto_xor_outbound_fdesc *xor_out_fdesc[XOR_FDESC_COUNT];
+struct comcerto_sw_xor_desc sw_xor_desc[XOR_SW_FDESC_COUNT];
+
+struct comcerto_xor_device *comcerto_xor_dev;
+struct comcerto_xor_chan comcerto_xor_ch;
+#define OWNER_XOR_FREE 1
+#define OWNER_MEMCPY_FREE 2
+#define OWNER_XOR_BUSY 3
+#define OWNER_MEMCPY_BUSY 4
+static int dma_owned = 0;
+static int memcpy_processed_ongoing = 0;
+static int memcpy_pending_count = 0;
+
+
+//static int mdma_busy = 0;
+static int mdma_done;
+static spinlock_t mdma_lock;
+
+static void *virtbase;
+
+#define M2IO_CONTROL (virtbase)
+#define M2IO_HEAD (virtbase + 0x4)
+#define M2IO_BURST (virtbase + 0x8)
+#define M2IO_FLEN (virtbase + 0xC)
+#define M2IO_IRQ_ENABLE (virtbase + 0x10)
+#define M2IO_IRQ_STATUS (virtbase + 0x14)
+#define M2IO_RESET (virtbase + 0x20)
+
+#define IO2M_CONTROL (virtbase + 0x80)
+#define IO2M_HEAD (virtbase + 0x84)
+#define IO2M_BURST (virtbase + 0x88)
+#define IO2M_FLEN (virtbase + 0x8C)
+#define IO2M_IRQ_ENABLE (virtbase + 0x90)
+#define IO2M_IRQ_STATUS (virtbase + 0x94)
+#define IO2M_RESET (virtbase + 0xA0)
+
+#define FDONE_MASK 0x80000000
+
+#define FLENEN 0x2
+
+static DECLARE_WAIT_QUEUE_HEAD(mdma_memcpy_busy_queue);
+static DECLARE_WAIT_QUEUE_HEAD(mdma_done_queue);
+
+unsigned long mdma_in_desc_phy;
+unsigned long mdma_out_desc_phy;
+
+struct comcerto_memcpy_inbound_fdesc *mdma_in_desc;
+struct comcerto_memcpy_outbound_fdesc *mdma_out_desc;
+
+EXPORT_SYMBOL(mdma_in_desc);
+EXPORT_SYMBOL(mdma_out_desc);
+
+static inline void comcerto_xor_set_in_bdesc(u32 buf_idx, u32 bdesc_idx, u32 addr, u32 ctrl)
+{
+ xor_in_fdesc[buf_idx]->bdesc[bdesc_idx].bpointer = addr;
+ xor_in_fdesc[buf_idx]->bdesc[bdesc_idx].bcontrol = ctrl;
+}
+
+static inline void comcerto_xor_set_out_bdesc(u32 buf_idx, u32 bdesc_idx, u32 addr, u32 ctrl)
+{
+ xor_out_fdesc[buf_idx]->bdesc[bdesc_idx].bpointer = addr;
+ xor_out_fdesc[buf_idx]->bdesc[bdesc_idx].bcontrol = ctrl;
+}
+
+static void comcerto_xor_set_desc(sw_idx, hw_idx)
+{
+ int i,split_no;
+ u32 fstatus0 = 0;
+ u32 addr;
+ int split_size;
+ dma_addr_t dest;
+ dma_addr_t *srcs;
+ u32 block_size;
+ int src_cnt;
+
+ block_size = sw_xor_desc[sw_idx].len;
+ src_cnt = sw_xor_desc[sw_idx].src_cnt;
+ srcs = sw_xor_desc[sw_idx].dma_src;
+ dest = sw_xor_desc[sw_idx].dma_dest;
+
+ if(block_size != PAGE_SIZE)
+ printk("%s: input buffers not %d len\n",__func__, (unsigned int)PAGE_SIZE);
+
+#if defined(CONFIG_COMCERTO_64K_PAGES)
+ block_size = block_size/16; //to get 4K
+ split_size = 16;
+#else
+ split_size = 1;
+#endif
+
+ for(split_no = 0 ; split_no < split_size; split_no++)
+ {
+
+ for(i = 0; i < src_cnt - 1; i++) {
+ addr = (u32)sw_xor_desc[sw_idx].dma_src[i] + 4096 * split_no;
+ comcerto_xor_set_in_bdesc(hw_idx, i, addr, block_size);
+ }
+
+ addr = (u32)sw_xor_desc[sw_idx].dma_src[src_cnt - 1] + 4096 * split_no;
+ comcerto_xor_set_in_bdesc(hw_idx, src_cnt - 1, addr, block_size | BLAST);
+
+ fstatus0 = 1; // New Req, reset block counter, block offset, clear scratchpad (overwrite existing data)
+ fstatus0 |= (1 << 1); // Read SP, return content of scratch pad after processing input data
+ fstatus0 |= (0 << 2); // Mode, Encode
+ fstatus0 |= (src_cnt << 4); // Number of blocks to be processed
+ fstatus0 |= (1 << 9); // Type, XOR
+ fstatus0 |= (XOR_BLOCK_SIZE_4096 << 11);
+
+ xor_in_fdesc[hw_idx]->fcontrol = 0;
+ xor_in_fdesc[hw_idx]->fstatus0 = fstatus0;
+ xor_in_fdesc[hw_idx]->fstatus1 = 0;
+
+ addr = (u32)sw_xor_desc[sw_idx].dma_dest + 4096 * split_no;
+ comcerto_xor_set_out_bdesc(hw_idx, 0, addr, block_size | BLAST);
+
+ xor_out_fdesc[hw_idx]->fcontrol = 0;
+ xor_out_fdesc[hw_idx]->fstatus0 = 0;
+ xor_out_fdesc[hw_idx]->fstatus1 = 0;
+
+ hw_idx = (hw_idx + 1) % XOR_FDESC_COUNT;
+ }
+
+ xor_wr_idx = hw_idx;
+}
+
+static inline int comcerto_dma_busy(void)
+{
+ return (readl_relaxed(IO2M_CONTROL) & 0x1);
+}
+
+static void comcerto_xor_update_dma_head(int idx)
+{
+ u32 out_desc_head, in_desc_head;
+ out_desc_head = virt_to_xor_dma(comcerto_xor_pool_phy, comcerto_xor_pool_virt, xor_out_fdesc[idx]);
+ in_desc_head = virt_to_xor_dma(comcerto_xor_pool_phy, comcerto_xor_pool_virt, xor_in_fdesc[idx]);
+
+ wmb();
+ writel_relaxed(out_desc_head, IO2M_HEAD);
+ writel_relaxed(in_desc_head, M2IO_HEAD);
+}
+
+static void comcerto_xor_update_dma_flen(int flen)
+{
+ wmb();
+ writel_relaxed(flen, M2IO_FLEN);
+ writel_relaxed(flen, IO2M_FLEN);
+}
+
+static int comcerto_xor_rb_full(void)
+{
+ if(CIRC_SPACE(xor_sw_wr_idx, xor_sw_rd_idx, XOR_SW_FDESC_COUNT) > 0)
+ return 0;
+ else
+ return 1;
+}
+
+void comcerto_xor_request_wait(void)
+{
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(&comcerto_xor_wait_queue, &wait, TASK_UNINTERRUPTIBLE);
+
+ comcerto_xor_sleeping++;
+
+ while (comcerto_xor_rb_full()) {
+ spin_unlock_bh(&comcerto_xor_ch.lock);
+ schedule();
+ spin_lock_bh(&comcerto_xor_ch.lock);
+ prepare_to_wait(&comcerto_xor_wait_queue, &wait, TASK_UNINTERRUPTIBLE);
+ }
+
+ finish_wait(&comcerto_xor_wait_queue, &wait);
+ comcerto_xor_sleeping--;
+}
+
+static void comcerto_dma_process(void)
+{
+ unsigned long pending_count;
+
+ if(!dma_owned || dma_owned==OWNER_XOR_FREE)
+ {
+ pending_count = CIRC_CNT(xor_wr_idx, xor_dma_idx, XOR_FDESC_COUNT);
+ if(pending_count)
+ {
+ dma_owned = OWNER_XOR_BUSY;
+ xor_current_batch_count = pending_count;
+ comcerto_xor_update_dma_flen(pending_count);
+ comcerto_xor_update_dma_head(xor_dma_idx);
+ }
+ }
+}
+
+static void comcerto_xor_cleanup(void)
+{
+ int i,j,k;
+ int idx;
+ int cleanup_count;
+ unsigned long flags;
+ int split_size;
+ struct comcerto_sw_xor_desc *sw_desc;
+
+ spin_lock_irqsave(&mdma_lock, flags);
+ cleanup_count = CIRC_CNT(xor_dma_idx, xor_rd_idx, XOR_FDESC_COUNT);
+ spin_unlock_irqrestore(&mdma_lock, flags);
+
+#if defined(CONFIG_COMCERTO_64K_PAGES)
+ split_size = 16;
+#else
+ split_size = 1;
+#endif
+
+ if(cleanup_count && (cleanup_count % split_size == 0))
+ {
+ for(i = 0 ; i < cleanup_count/split_size; i++)
+ {
+ struct dma_async_tx_descriptor *tx;
+
+#if defined(CONFIG_COMCERTO_64K_PAGES)
+ if(xor_rd_idx%16)
+ printk("%s: xor_rd_idx %d not multiple of 16\n",__func__, xor_rd_idx);
+#endif
+ idx = xor_sw_rd_idx;
+ tx = &sw_xor_desc[idx].async_tx;
+ sw_desc = &sw_xor_desc[idx];
+
+ for (j = 0; j < sw_desc->src_cnt; j++) {
+ dma_unmap_page(NULL, sw_desc->dma_src[j], sw_desc->len, DMA_TO_DEVICE);
+ }
+ dma_unmap_page(NULL, sw_desc->dma_dest, sw_desc->len, DMA_BIDIRECTIONAL);
+
+ comcerto_xor_ch.completed_cookie = tx->cookie;
+
+ if (tx->callback) {
+ tx->callback(tx->callback_param);
+ tx->callback = NULL;
+ }
+ else
+ printk("No Callback\n");
+
+ smp_mb();
+
+ spin_lock_irqsave(&mdma_lock, flags);
+ for(k = 0 ; k < split_size; k++)
+ {
+ xor_rd_idx = (xor_rd_idx + 1) % XOR_FDESC_COUNT;
+ }
+ spin_unlock_irqrestore(&mdma_lock, flags);
+
+ xor_sw_rd_idx = (xor_sw_rd_idx + 1) % XOR_SW_FDESC_COUNT;
+ }
+ }
+ else
+ {
+ if(cleanup_count)
+ printk("%s: cleanup_count %d not multiple of 16\n",__func__, cleanup_count);
+ }
+}
+
+static void comcerto_xor_tasklet(unsigned long data)
+{
+ spin_lock_bh(&comcerto_xor_ch.lock);
+
+ comcerto_xor_cleanup();
+
+ spin_unlock_bh(&comcerto_xor_ch.lock);
+}
+
+static void comcerto_xor_timer_fnc(unsigned long data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdma_lock, flags);
+
+ if(comcerto_xor_sleeping)
+ wake_up(&comcerto_xor_wait_queue);
+
+ spin_unlock_irqrestore(&mdma_lock, flags);
+
+ comcerto_xor_tasklet(0);
+
+ mod_timer(&comcerto_xor_timer, jiffies + COMPLETION_TIMEOUT);
+
+}
+
+
+static void comcerto_xor_issue_pending(struct dma_chan *chan)
+{
+ unsigned long flags;
+
+
+ spin_lock_irqsave(&mdma_lock, flags);
+
+ comcerto_dma_process();
+
+ if(comcerto_xor_sleeping)
+ wake_up(&comcerto_xor_wait_queue);
+
+ spin_unlock_irqrestore(&mdma_lock, flags);
+
+ comcerto_xor_tasklet(0);
+}
+
+static inline struct comcerto_sw_xor_desc *txd_to_comcerto_desc(struct dma_async_tx_descriptor *txd)
+{
+ return container_of(txd, struct comcerto_sw_xor_desc, async_tx);
+}
+
+
+static dma_cookie_t comcerto_xor_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ unsigned long flags;
+
+ struct dma_chan *c = tx->chan;
+ dma_cookie_t cookie;
+
+ spin_lock_bh(&comcerto_xor_ch.lock);
+
+ cookie = c->cookie;
+ cookie++;
+ if (cookie < 0)
+ cookie = 1;
+ tx->cookie = cookie;
+ c->cookie = cookie;
+
+ spin_lock_irqsave(&mdma_lock, flags);
+
+ comcerto_xor_set_desc(xor_sw_wr_prev_idx, xor_wr_idx);
+ xor_sw_wr_prev_idx = (xor_sw_wr_prev_idx + 1) % XOR_SW_FDESC_COUNT ;
+ comcerto_dma_process();
+
+ spin_unlock_irqrestore(&mdma_lock, flags);
+
+ spin_unlock_bh(&comcerto_xor_ch.lock);
+
+ if(comerto_xor_timer_first)
+ {
+ mod_timer(&comcerto_xor_timer, jiffies + COMPLETION_TIMEOUT);
+ comerto_xor_timer_first = 0;
+ }
+ return cookie;
+}
+
+ struct dma_async_tx_descriptor *
+comcerto_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *srcs,
+ unsigned int src_cnt, size_t len, unsigned long xor_flags)
+{
+ int desc_idx;
+ int i;
+
+ spin_lock_bh(&comcerto_xor_ch.lock);
+ if(comcerto_xor_rb_full())
+ {
+ comcerto_xor_request_wait();
+ }
+
+ desc_idx = xor_sw_wr_idx;
+ xor_sw_wr_idx = (xor_sw_wr_idx + 1) % XOR_SW_FDESC_COUNT ;
+
+ sw_xor_desc[desc_idx].async_tx.flags = xor_flags;
+ sw_xor_desc[desc_idx].src_cnt = src_cnt;
+ sw_xor_desc[desc_idx].len = len;
+ sw_xor_desc[desc_idx].dma_dest = dest;
+ for(i = 0; i < src_cnt; i++)
+ sw_xor_desc[desc_idx].dma_src[i] = srcs[i];
+
+ spin_unlock_bh(&comcerto_xor_ch.lock);
+
+ return &sw_xor_desc[desc_idx].async_tx;
+
+}
+
+
+static int comcerto_xor_alloc_chan_resources(struct dma_chan *chan)
+{
+ int i;
+
+ for(i = 0; i < XOR_SW_FDESC_COUNT; i++) {
+ memset(&sw_xor_desc[i], 0, sizeof(struct comcerto_sw_xor_desc));
+ sw_xor_desc[i].async_tx.tx_submit = comcerto_xor_tx_submit;
+ sw_xor_desc[i].async_tx.cookie = 0;
+ dma_async_tx_descriptor_init(&sw_xor_desc[i].async_tx, chan);
+ }
+ return XOR_SW_FDESC_COUNT;
+}
+
+static void comcerto_xor_free_chan_resources(struct dma_chan *chan)
+{
+ //TODO
+
+ printk("*** %s ***\n",__func__);
+
+ return;
+}
+
+static enum dma_status comcerto_xor_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ dma_cookie_t last_used;
+ dma_cookie_t last_complete;
+
+ last_used = chan->cookie;
+ last_complete = comcerto_xor_ch.completed_cookie;
+ dma_set_tx_state(txstate, last_complete, last_used, 0);
+
+ return dma_async_is_complete(cookie, last_complete, last_used);
+}
+
+
+#if defined(CONFIG_COMCERTO_MDMA_PROF)
+
+unsigned int mdma_time_counter[256];
+unsigned int mdma_reqtime_counter[256];
+unsigned int mdma_data_counter[256];
+static struct timeval last_mdma;
+unsigned int init_mdma_prof = 0;
+unsigned int enable_mdma_prof = 0;
+
+void comcerto_dma_profiling_start(struct comcerto_dma_sg *sg, unsigned int len)
+{
+ long diff_time_us;
+
+ if (enable_mdma_prof) {
+ do_gettimeofday(&sg->start);
+
+ if (init_mdma_prof) {
+ diff_time_us = ((sg->start.tv_sec - last_mdma.tv_sec) * 1000000 + sg->start.tv_usec - last_mdma.tv_usec) >> 4;
+ if (diff_time_us < 256) {
+ mdma_time_counter[diff_time_us]++;
+ }
+ else {
+ mdma_time_counter[255]++;
+ }
+ }
+
+ len >>= 13;
+
+ if (len < 256)
+ mdma_data_counter[len]++;
+ else
+ mdma_data_counter[255]++;
+ }
+}
+
+void comcerto_dma_profiling_end(struct comcerto_dma_sg *sg)
+{
+ long diff_time_us;
+
+ if (enable_mdma_prof) {
+ do_gettimeofday(&sg->end);
+
+ diff_time_us = ((sg->end.tv_sec - sg->start.tv_sec) * 1000000 + sg->end.tv_usec - sg->start.tv_usec) >> 4;
+ if (diff_time_us < 256) {
+ mdma_reqtime_counter[diff_time_us]++;
+ }
+ else
+ mdma_reqtime_counter[255]++;
+
+ if (!init_mdma_prof)
+ init_mdma_prof = 1;
+
+ last_mdma = sg->end;
+ }
+}
+
+#else
+void comcerto_dma_profiling_start(struct comcerto_dma_sg *sg, unsigned int len) {}
+void comcerto_dma_profiling_end(struct comcerto_dma_sg *sg) {}
+#endif
+
+
+static inline dma_addr_t dma_acp_map_page(struct comcerto_dma_sg *sg, void *p, unsigned int len, int dir, int use_acp)
+{
+ dma_addr_t phys_addr = virt_to_phys(p);
+ dma_addr_t low, high;
+
+ if (!use_acp)
+ goto map;
+
+ if ((phys_addr >= sg->low_phys_addr) && (phys_addr + len) < sg->high_phys_addr)
+ {
+ /* In range, skip mapping */
+ return COMCERTO_AXI_ACP_BASE + phys_addr;
+ }
+
+ /* Try to grow window, if possible */
+ if (phys_addr < sg->low_phys_addr)
+ low = phys_addr & ~(COMCERTO_AXI_ACP_SIZE - 1);
+ else
+ low = sg->low_phys_addr;
+
+ if ((phys_addr + len) > sg->high_phys_addr)
+ high = (phys_addr + len + COMCERTO_AXI_ACP_SIZE - 1) & ~(COMCERTO_AXI_ACP_SIZE - 1);
+ else
+ high = sg->high_phys_addr;
+
+ if ((high - low) <= COMCERTO_AXI_ACP_SIZE) {
+ sg->low_phys_addr = low;
+ sg->high_phys_addr = high;
+
+ return COMCERTO_AXI_ACP_BASE + phys_addr;
+ }
+
+map:
+ return dma_map_single(NULL, p, len, dir); //TODO add proper checks
+}
+
+
+int comcerto_dma_sg_add_input(struct comcerto_dma_sg *sg, void *p, unsigned int len, int use_acp)
+{
+ dma_addr_t phys_addr;
+
+ if (unlikely(len > (MDMA_MAX_BUF_SIZE + 1))) {
+ printk(KERN_ERR "%s: tried to add a page larger than %d kB.\n", __func__, MDMA_MAX_BUF_SIZE + 1);
+ return -2;
+ }
+
+ if (len <= MDMA_MAX_BUF_SIZE) {
+ if (sg->input_idx >= MDMA_INBOUND_BUF_DESC)
+ return -1;
+
+ phys_addr = dma_acp_map_page(sg, p, len, DMA_TO_DEVICE, use_acp);
+
+ sg->in_bdesc[sg->input_idx].split = 0;
+ sg->in_bdesc[sg->input_idx].phys_addr = phys_addr;
+ sg->in_bdesc[sg->input_idx].len = len;
+ sg->input_idx++;
+
+ return 0;
+ }
+ else { /* len = MSPD_MDMA_MAX_BUF_SIZE +1, split it in 2 pieces */
+ if (sg->input_idx >= (MDMA_INBOUND_BUF_DESC - 1))
+ return -1;
+
+ phys_addr = dma_acp_map_page(sg, p, len, DMA_TO_DEVICE, use_acp);
+
+ sg->in_bdesc[sg->input_idx].split = 1;
+ sg->in_bdesc[sg->input_idx].phys_addr = phys_addr;
+ sg->in_bdesc[sg->input_idx].len = MDMA_SPLIT_BUF_SIZE;
+ sg->input_idx++;
+ sg->in_bdesc[sg->input_idx].split = 0;
+ sg->in_bdesc[sg->input_idx].phys_addr = phys_addr + MDMA_SPLIT_BUF_SIZE;
+ sg->in_bdesc[sg->input_idx].len = len - MDMA_SPLIT_BUF_SIZE;
+ sg->input_idx++;
+
+ return 0;
+ }
+}
+EXPORT_SYMBOL(comcerto_dma_sg_add_input);
+
+int comcerto_dma_sg_add_output(struct comcerto_dma_sg *sg, void *p, unsigned int len, int use_acp)
+{
+ dma_addr_t phys_addr;
+
+ if (unlikely(len > (MDMA_MAX_BUF_SIZE + 1))) {
+ printk(KERN_ERR "%s: tried to add a page larger than %d kB.\n", __func__, MDMA_MAX_BUF_SIZE + 1);
+ return -2;
+ }
+
+ if (len <= MDMA_MAX_BUF_SIZE) {
+ if (sg->output_idx >= MDMA_OUTBOUND_BUF_DESC)
+ return -1;
+
+ phys_addr = dma_acp_map_page(sg, p, len, DMA_FROM_DEVICE, use_acp);
+
+ sg->out_bdesc[sg->output_idx].split = 0;
+ sg->out_bdesc[sg->output_idx].phys_addr = phys_addr;
+ sg->out_bdesc[sg->output_idx].len = len;
+ sg->output_idx++;
+
+ return 0;
+ }
+ else { /* len = MDMA_MAX_BUF_SIZE +1, split it in 2 pieces */
+ if (sg->output_idx >= (MDMA_OUTBOUND_BUF_DESC - 1))
+ return -1;
+
+ phys_addr = dma_acp_map_page(sg, p, len, DMA_FROM_DEVICE, use_acp);
+
+ sg->out_bdesc[sg->output_idx].split = 1;
+ sg->out_bdesc[sg->output_idx].phys_addr = phys_addr;
+ sg->out_bdesc[sg->output_idx].len = MDMA_SPLIT_BUF_SIZE;
+ sg->output_idx++;
+ sg->out_bdesc[sg->output_idx].split = 0;
+ sg->out_bdesc[sg->output_idx].phys_addr = phys_addr + MDMA_SPLIT_BUF_SIZE;
+ sg->out_bdesc[sg->output_idx].len = len - MDMA_SPLIT_BUF_SIZE;
+ sg->output_idx++;
+
+ return 0;
+ }
+}
+EXPORT_SYMBOL(comcerto_dma_sg_add_output);
+
+void comcerto_dma_sg_setup(struct comcerto_dma_sg *sg, unsigned int len)
+{
+ int i;
+ unsigned int remaining;
+
+ comcerto_dma_profiling_start(sg, len);
+
+ writel_relaxed(sg->low_phys_addr |
+ AWUSER_COHERENT(WRITEBACK) | AWPROT(0x0) | AWCACHE(CACHEABLE | BUFFERABLE) |
+ ARUSER_COHERENT(WRITEBACK) | ARPROT(0x0) | ARCACHE(CACHEABLE | BUFFERABLE),
+ COMCERTO_GPIO_A9_ACP_CONF_REG);
+
+ remaining = len;
+ i = 0;
+ while (remaining > sg->in_bdesc[i].len) {
+
+ if (sg->in_bdesc[i].phys_addr >= COMCERTO_AXI_ACP_BASE)
+ sg->in_bdesc[i].phys_addr -= sg->low_phys_addr;
+
+ comcerto_dma_set_in_bdesc(i, sg->in_bdesc[i].phys_addr, sg->in_bdesc[i].len);
+ remaining -= sg->in_bdesc[i].len;
+ i++;
+ }
+
+ if (sg->in_bdesc[i].phys_addr >= COMCERTO_AXI_ACP_BASE)
+ sg->in_bdesc[i].phys_addr -= sg->low_phys_addr;
+
+ comcerto_dma_set_in_bdesc(i, sg->in_bdesc[i].phys_addr, remaining | BLAST);
+
+ remaining = len;
+ i = 0;
+
+ while (remaining > sg->out_bdesc[i].len) {
+ if (sg->out_bdesc[i].phys_addr >= COMCERTO_AXI_ACP_BASE)
+ sg->out_bdesc[i].phys_addr -= sg->low_phys_addr;
+
+ comcerto_dma_set_out_bdesc(i, sg->out_bdesc[i].phys_addr, sg->out_bdesc[i].len);
+ remaining -= sg->out_bdesc[i].len;
+ i++;
+ }
+
+ if (sg->out_bdesc[i].phys_addr >= COMCERTO_AXI_ACP_BASE)
+ sg->out_bdesc[i].phys_addr -= sg->low_phys_addr;
+
+ comcerto_dma_set_out_bdesc(i, sg->out_bdesc[i].phys_addr, remaining | BLAST);
+}
+EXPORT_SYMBOL(comcerto_dma_sg_setup);
+
+void comcerto_dma_sg_cleanup(struct comcerto_dma_sg *sg, unsigned int len)
+{
+ int i;
+ unsigned int remaining;
+
+ remaining = len;
+ i = 0;
+ while (remaining > sg->in_bdesc[i].len) {
+ if (sg->in_bdesc[i].split) {
+ sg->in_bdesc[i+1].phys_addr = sg->in_bdesc[i].phys_addr;
+ sg->in_bdesc[i+1].len += sg->in_bdesc[i].len;
+ }
+ else {
+ if (sg->in_bdesc[i].phys_addr < COMCERTO_AXI_ACP_BASE)
+ dma_unmap_page(NULL, sg->in_bdesc[i].phys_addr, sg->in_bdesc[i].len, DMA_TO_DEVICE);
+ remaining -= sg->in_bdesc[i].len;
+ }
+ i++;
+ }
+
+ if (sg->in_bdesc[i].phys_addr < COMCERTO_AXI_ACP_BASE)
+ dma_unmap_page(NULL, sg->in_bdesc[i].phys_addr, sg->in_bdesc[i].len, DMA_TO_DEVICE);
+
+ remaining = len;
+ i = 0;
+ while (remaining > sg->out_bdesc[i].len) {
+ if (sg->out_bdesc[i].split) {
+ sg->out_bdesc[i+1].phys_addr = sg->out_bdesc[i].phys_addr;
+ sg->out_bdesc[i+1].len += sg->out_bdesc[i].len;
+ }
+ else {
+ if (sg->out_bdesc[i].phys_addr < COMCERTO_AXI_ACP_BASE)
+ dma_unmap_page(NULL, sg->out_bdesc[i].phys_addr, sg->out_bdesc[i].len, DMA_FROM_DEVICE);
+ remaining -= sg->out_bdesc[i].len;
+ }
+ i++;
+ }
+
+ if (sg->out_bdesc[i].phys_addr < COMCERTO_AXI_ACP_BASE)
+ dma_unmap_page(NULL, sg->out_bdesc[i].phys_addr, sg->out_bdesc[i].len, DMA_FROM_DEVICE);
+
+
+ comcerto_dma_profiling_end(sg);
+}
+EXPORT_SYMBOL(comcerto_dma_sg_cleanup);
+
+void comcerto_dma_get(void)
+{
+ unsigned long flags;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irqsave(&mdma_lock, flags);
+
+ if (dma_owned && (dma_owned != OWNER_MEMCPY_FREE)) {
+ prepare_to_wait(&mdma_memcpy_busy_queue, &wait, TASK_UNINTERRUPTIBLE);
+
+ memcpy_pending_count++;
+
+ while (!dma_owned || !(dma_owned == OWNER_MEMCPY_FREE)) {
+ spin_unlock_irqrestore(&mdma_lock, flags);
+ schedule();
+ spin_lock_irqsave(&mdma_lock, flags);
+ prepare_to_wait(&mdma_memcpy_busy_queue, &wait, TASK_UNINTERRUPTIBLE);
+ }
+
+ memcpy_pending_count--;
+
+ finish_wait(&mdma_memcpy_busy_queue, &wait);
+ }
+ dma_owned = OWNER_MEMCPY_BUSY;
+
+ spin_unlock_irqrestore(&mdma_lock, flags);
+}
+EXPORT_SYMBOL(comcerto_dma_get);
+
+void comcerto_dma_put(void)
+{
+#if 0
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdma_lock, flags);
+ mdma_busy = 0;
+ spin_unlock_irqrestore(&mdma_lock, flags);
+
+ wake_up(&mdma_memcpy_busy_queue);
+#endif
+}
+EXPORT_SYMBOL(comcerto_dma_put);
+
+/* Called once to setup common registers */
+static void comcerto_dma_setup(void)
+{
+ /* IO2M_IRQ_ENABLE: Enable IRQ_IRQFDON*/
+ writel_relaxed(IRQ_IRQFDON|IRQ_IRQFLEN|IRQ_IRQFTHLD|IRQ_IRQFLST, IO2M_IRQ_ENABLE);
+ writel_relaxed(IRQ_IRQFDON|IRQ_IRQFLEN|IRQ_IRQFTHLD|IRQ_IRQFLST, M2IO_IRQ_ENABLE);
+
+ writel_relaxed(FLENEN, M2IO_CONTROL);
+ writel_relaxed(0xf | (0x3ff << 8), M2IO_BURST);
+ writel_relaxed(FLENEN, IO2M_CONTROL);
+ writel_relaxed(0xf | (0x3ff << 8), IO2M_BURST);
+}
+
+
+void comcerto_dma_start(void)
+{
+ mdma_done = 0;
+
+ mdma_in_desc->next_desc = 0;
+ mdma_in_desc->fcontrol = 0;
+ mdma_in_desc->fstatus0 = 0;
+ mdma_in_desc->fstatus1 = 0;
+
+ // outbound
+ mdma_out_desc->next_desc = 0;
+ mdma_out_desc->fcontrol = 0;
+ mdma_out_desc->fstatus0 = 0;
+ mdma_out_desc->fstatus1 = 0;
+
+ wmb();
+
+ // Initialize the Outbound Head Pointer
+ writel_relaxed(mdma_out_desc_phy, IO2M_HEAD);
+
+ // Initialize the Inbound Head Pointer
+ writel_relaxed(mdma_in_desc_phy, M2IO_HEAD);
+
+ writel_relaxed(1, M2IO_FLEN);
+ writel_relaxed(1, IO2M_FLEN);
+
+ wmb();
+}
+EXPORT_SYMBOL(comcerto_dma_start);
+
+
+void comcerto_dma_wait(void)
+{
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(&mdma_done_queue, &wait, TASK_UNINTERRUPTIBLE);
+
+ if (!mdma_done)
+ schedule();
+
+ finish_wait(&mdma_done_queue, &wait);
+}
+EXPORT_SYMBOL(comcerto_dma_wait);
+
+static void comcerto_dump_regs(void)
+{
+ u32 val;
+
+ val = readl_relaxed(M2IO_CONTROL);
+ printk(KERN_ERR"M2IO_CONTROL 0x%8x.\n",val);
+
+ val = readl_relaxed(M2IO_HEAD);
+ printk(KERN_ERR"M2IO_HEAD 0x%8x.\n",val);
+
+ val = readl_relaxed(M2IO_BURST);
+ printk(KERN_ERR"M2IO_BURST 0x%8x.\n",val);
+
+ val = readl_relaxed(M2IO_FLEN);
+ printk(KERN_ERR"M2IO_FLEN 0x%8x.\n",val);
+
+ val = readl_relaxed(M2IO_IRQ_ENABLE);
+ printk(KERN_ERR"M2IO_IRQ_ENABLE 0x%8x.\n",val);
+
+ val = readl_relaxed(M2IO_IRQ_STATUS);
+ printk(KERN_ERR"M2IO_IRQ_STATUS 0x%8x.\n",val);
+
+ val = readl_relaxed(M2IO_RESET);
+ printk(KERN_ERR"M2IO_RESET 0x%8x.\n",val);
+
+ val = readl_relaxed(IO2M_CONTROL);
+ printk(KERN_ERR"IO2M_CONTROL 0x%8x.\n",val);
+
+ val = readl_relaxed(IO2M_HEAD);
+ printk(KERN_ERR"IO2M_HEAD 0x%8x.\n",val);
+
+ val = readl_relaxed(IO2M_BURST);
+ printk(KERN_ERR"IO2M_BURST 0x%8x.\n",val);
+
+ val = readl_relaxed(IO2M_FLEN);
+ printk(KERN_ERR"IO2M_FLEN 0x%8x.\n",val);
+
+ val = readl_relaxed(IO2M_IRQ_ENABLE);
+ printk(KERN_ERR"IO2M_IRQ_ENABLE 0x%8x.\n",val);
+
+ val = readl_relaxed(IO2M_IRQ_STATUS);
+ printk(KERN_ERR"IO2M_IRQ_STATUS 0x%8x.\n",val);
+
+ val = readl_relaxed(IO2M_RESET);
+ printk(KERN_ERR"IO2M_RESET 0x%8x.\n",val);
+}
+
+static irqreturn_t c2k_dma_handle_interrupt(int irq, void *data)
+{
+ int i;
+ int pending_count;
+ unsigned long flags;
+ u32 intr_cause = readl_relaxed(IO2M_IRQ_STATUS);
+
+ writel_relaxed(intr_cause, IO2M_IRQ_STATUS);
+
+ if (unlikely(intr_cause & ~(IRQ_IRQFDON | IRQ_IRQFLST | IRQ_IRQFLEN))) {
+ if (intr_cause & IRQ_IRQFRDYN)
+ printk(KERN_ALERT "IRQFRDYN: A frame is started but the frame is not ready");
+
+ if (intr_cause & IRQ_IRQFLSH)
+ printk(KERN_ALERT "IRQFLSH: IO has more data than the memory buffer");
+
+ if (intr_cause & IRQ_IRQFTHLD)
+ printk(KERN_ALERT "IRQFTHLD: Frame threshold reached. FLEN=FTHLDL");
+
+ if (intr_cause & IRQ_IRQFCTRL)
+ printk(KERN_ALERT "IRQFCTRL: 1 frame is completed or when a frame is started but not ready");
+
+ comcerto_dump_regs();
+ }
+
+ if (intr_cause & IRQ_IRQFDON) {
+
+ }
+
+ if (intr_cause & IRQ_IRQFLEN) {
+
+ spin_lock_irqsave(&mdma_lock, flags);
+ if(!dma_owned)
+ printk(KERN_ALERT " NULL MDMA Ownership !!!\n");
+
+
+ if(dma_owned==OWNER_XOR_BUSY)
+ {
+
+ for(i = 0 ; i < xor_current_batch_count; i++)
+ xor_dma_idx = (xor_dma_idx + 1) % XOR_FDESC_COUNT;
+
+ xor_current_batch_count = 0;
+
+
+ if(memcpy_pending_count)
+ {
+ dma_owned = OWNER_MEMCPY_FREE;
+ wake_up(&mdma_memcpy_busy_queue);
+ }
+ else
+ {
+ pending_count = CIRC_CNT(xor_wr_idx, xor_dma_idx, XOR_FDESC_COUNT);
+ if(pending_count)
+ {
+ dma_owned = OWNER_XOR_BUSY;
+ xor_current_batch_count = pending_count;
+ comcerto_xor_update_dma_flen(xor_current_batch_count);
+ comcerto_xor_update_dma_head(xor_dma_idx);
+ }
+ else
+ {
+ dma_owned = 0;
+ }
+ }
+
+ if(comcerto_xor_sleeping)
+ {
+ wake_up(&comcerto_xor_wait_queue);
+ }
+
+ tasklet_schedule(&comcerto_xor_ch.irq_tasklet);
+ }
+ else //memcpy
+ {
+ mdma_done = 1;
+ wake_up(&mdma_done_queue);
+
+ memcpy_processed_ongoing++;
+ pending_count = CIRC_CNT(xor_wr_idx, xor_dma_idx, XOR_FDESC_COUNT);
+
+ if(pending_count)
+ {
+ memcpy_processed_ongoing = 0;
+ dma_owned = OWNER_XOR_BUSY;
+ xor_current_batch_count = pending_count;
+ comcerto_xor_update_dma_flen(xor_current_batch_count);
+ comcerto_xor_update_dma_head(xor_dma_idx);
+
+ if(comcerto_xor_sleeping)
+ {
+ wake_up(&comcerto_xor_wait_queue);
+ }
+ }
+ else
+ {
+ if(memcpy_pending_count)
+ {
+ dma_owned = OWNER_MEMCPY_FREE;
+ wake_up(&mdma_memcpy_busy_queue);
+ }
+ else
+ {
+ dma_owned = 0;
+ }
+ }
+
+ }
+ spin_unlock_irqrestore(&mdma_lock, flags);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devexit comcerto_dma_remove(struct platform_device *pdev)
+{
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+
+ iounmap(virtbase);
+
+ free_irq(irq, NULL);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static int __devinit comcerto_dma_probe(struct platform_device *pdev)
+{
+ struct resource *io;
+ int irq;
+ void *memcpy_pool = (void *)IRAM_MEMORY_VADDR;
+ void *xor_pool;
+ int i;
+ struct dma_device *dma_dev;
+ int ret = 0;
+
+ /* Retrieve related resources(mem, irq) from platform_device */
+ io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!io)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev,0);
+ if (irq < 0)
+ return irq;
+
+ ret = request_irq(irq, c2k_dma_handle_interrupt, 0, "MDMA", NULL);
+ if (ret < 0)
+ return ret;
+
+ virtbase = ioremap(io->start, resource_size(io));
+ if (!virtbase)
+ goto err_free_irq;
+
+ /* Initialize comcerto_xor_device */
+ comcerto_xor_dev = devm_kzalloc(&pdev->dev, sizeof(struct comcerto_xor_device), GFP_KERNEL);
+
+ if(!comcerto_xor_dev)
+ {
+ ret = -ENOMEM;
+ goto err_free_remap;
+ }
+
+ dma_dev = &comcerto_xor_dev->device;
+ INIT_LIST_HEAD(&dma_dev->channels);
+ dma_cap_set(DMA_XOR,dma_dev->cap_mask);
+ dma_dev->dev = &pdev->dev;
+ dma_dev->device_alloc_chan_resources = comcerto_xor_alloc_chan_resources;
+ dma_dev->device_free_chan_resources = comcerto_xor_free_chan_resources;
+ dma_dev->device_tx_status = comcerto_xor_status;
+ dma_dev->device_issue_pending = comcerto_xor_issue_pending;
+ dma_dev->device_prep_dma_xor = comcerto_xor_prep_dma_xor;
+ dma_dev->max_xor = XOR_MAX_SRC_CNT;
+ platform_set_drvdata(pdev,comcerto_xor_dev);
+
+ /* Initialize comcerto_xor_chan */
+ comcerto_xor_ch.chan.device = dma_dev;
+ list_add_tail(&comcerto_xor_ch.chan.device_node,&dma_dev->channels);
+
+ ret = dma_async_device_register(dma_dev);
+ if (unlikely(ret)) {
+ printk(KERN_ERR "%s: Failed to register XOR DMA channel %d\n",__func__,ret);
+ goto err_free_dma;
+ }
+ else
+ printk(KERN_INFO "%s: XOR DMA channel registered\n",__func__);
+
+ spin_lock_init(&comcerto_xor_ch.lock);
+ spin_lock_init(&comcerto_xor_cleanup_lock);
+
+ spin_lock_init(&mdma_lock);
+
+ //Memcpy descriptor initializing
+ mdma_in_desc = (struct comcerto_memcpy_inbound_fdesc *) (memcpy_pool);
+ memcpy_pool += sizeof(struct comcerto_memcpy_inbound_fdesc);
+ memcpy_pool = (void *)((unsigned long)(memcpy_pool + 15) & ~15);
+ mdma_out_desc = (struct comcerto_memcpy_outbound_fdesc *) (memcpy_pool);
+ memcpy_pool += sizeof(struct comcerto_memcpy_outbound_fdesc);
+ memcpy_pool = (void *)((unsigned long)(memcpy_pool + 15) & ~15);
+
+ mdma_in_desc_phy = virt_to_aram(mdma_in_desc);
+ mdma_out_desc_phy = virt_to_aram(mdma_out_desc);
+
+ //XOR descriptor initializing
+ comcerto_xor_pool_virt = dma_alloc_coherent(NULL, XOR_FDESC_COUNT * (sizeof(struct comcerto_xor_inbound_fdesc)
+ + sizeof(struct comcerto_xor_outbound_fdesc)), &comcerto_xor_pool_phy, GFP_KERNEL);
+ xor_pool = comcerto_xor_pool_virt;
+
+ for (i = 0; i < XOR_FDESC_COUNT; i++) {
+ xor_in_fdesc[i] = (struct comcerto_xor_inbound_fdesc *) (xor_pool);
+ xor_pool += sizeof(struct comcerto_xor_inbound_fdesc);
+ xor_pool = (void *)((unsigned long)(xor_pool + 15) & ~15);
+ xor_out_fdesc[i] = (struct comcerto_xor_outbound_fdesc *) (xor_pool);
+ xor_pool += sizeof(struct comcerto_xor_outbound_fdesc);
+ xor_pool = (void *)((unsigned long)(xor_pool + 15) & ~15);
+
+ memset(xor_in_fdesc[i], 0 , sizeof(struct comcerto_xor_inbound_fdesc));
+ memset(xor_out_fdesc[i], 0 , sizeof(struct comcerto_xor_outbound_fdesc));
+ }
+
+ for(i = 0; i < XOR_FDESC_COUNT - 1; i++) {
+ xor_in_fdesc[i]->next_desc = virt_to_xor_dma(comcerto_xor_pool_phy, comcerto_xor_pool_virt, xor_in_fdesc[i+1]);
+ xor_out_fdesc[i]->next_desc = virt_to_xor_dma(comcerto_xor_pool_phy, comcerto_xor_pool_virt, xor_out_fdesc[i+1]);
+ }
+ xor_in_fdesc[XOR_FDESC_COUNT-1]->next_desc = virt_to_xor_dma(comcerto_xor_pool_phy, comcerto_xor_pool_virt, xor_in_fdesc[0]);
+ xor_out_fdesc[XOR_FDESC_COUNT-1]->next_desc = virt_to_xor_dma(comcerto_xor_pool_phy, comcerto_xor_pool_virt, xor_out_fdesc[0]);
+
+ init_timer(&comcerto_xor_timer);
+ comcerto_xor_timer.function = comcerto_xor_timer_fnc;
+ comcerto_xor_timer.data = 0;
+
+ tasklet_init(&comcerto_xor_ch.irq_tasklet, comcerto_xor_tasklet, 0);
+
+ comcerto_dma_setup();
+
+ goto out;
+
+err_free_dma:
+ platform_set_drvdata(pdev,NULL);
+ kfree(comcerto_xor_dev);
+err_free_remap:
+ iounmap(virtbase);
+err_free_irq:
+ free_irq(irq, NULL);
+out:
+ return ret;
+}
+
+
+static struct platform_driver comcerto_dma_driver = {
+ .probe = comcerto_dma_probe,
+ .remove = comcerto_dma_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "comcerto_dma",
+ },
+};
+
+static int __init comcerto_dma_init(void)
+{
+ return platform_driver_register(&comcerto_dma_driver);
+}
+module_init(comcerto_dma_init);
+
+static void __exit comcerto_dma_exit(void)
+{
+ platform_driver_unregister(&comcerto_dma_driver);
+}
+module_exit(comcerto_dma_exit);
+
+MODULE_DESCRIPTION("DMA engine driver for Mindspeed Comcerto C2000 devices");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/dma/comcerto_xor.c b/drivers/dma/comcerto_xor.c
new file mode 100644
index 0000000..eb0b3a2
--- /dev/null
+++ b/drivers/dma/comcerto_xor.c
@@ -0,0 +1,1237 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/memory.h>
+#include "comcerto_xor.h"
+
+#define to_comcerto_xor_chan(dma_chan) \
+ container_of(dma_chan,struct comcerto_xor_chan, chan)
+
+#define to_comcerto_xor_device(dev) \
+ container_of(dev,struct comcerto_xor_device, device)
+
+#define to_comcerto_xor_slot(tx) \
+ container_of(tx, struct comcerto_xor_desc_slot, async_tx)
+
+/* ---------------------- Functions to set/read frame descriptor --------------------------- */
+
+/* ------- Set inbound frame descriptor ------- */
+
+static void comcerto_set_next_desc_inbound(struct comcerto_xor_desc_slot *slot, u32 next_desc_addr)
+{
+ struct comcerto_xor_inbound_desc *hw_desc = slot->hw_desc_inbound;
+ hw_desc->next_desc = next_desc_addr;
+}
+
+static void comcerto_set_fcontrol_inbound(struct comcerto_xor_desc_slot *slot)
+{
+ struct comcerto_xor_inbound_desc *hw_desc = slot->hw_desc_inbound;
+
+ switch (slot->type) {
+ case DMA_XOR:
+ case DMA_XOR_VAL:
+ case DMA_MEMCPY:
+ hw_desc->fcontrol = 0;
+ break;
+ default:
+ dev_printk(KERN_ERR, slot->async_tx.chan->device->dev,
+ "error: unsupported operation %d.\n",slot->type);
+ }
+}
+
+static void comcerto_set_fstatus0_inbound(struct comcerto_xor_desc_slot *slot)
+{
+ struct comcerto_xor_inbound_desc *hw_desc = slot->hw_desc_inbound;
+
+ switch (slot->type) {
+
+ case DMA_XOR:
+ hw_desc->fstatus0 = 1; // New Req, reset block counter, block offset, clear scratchpad (overwrite existing data)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (1 << 1); // Read SP, return content of scratch pad after processing input data
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (0 << 2); // Mode, Encode
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (slot->src_cnt << 4); // Number of blocks to be processed
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (1 << 9); // Type, XOR
+ if(slot->len == 256)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_256 << 11);
+ if(slot->len == 512)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_512 << 11);
+ if(slot->len == 1024)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_1024 << 11);
+ if(slot->len == 2048)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_2048 << 11);
+ if(slot->len == 4096)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_4096 << 11);
+ break;
+
+ case DMA_XOR_VAL:
+ hw_desc->fstatus0 = 1; // New Req, reset block counter, block offset, clear scratchpad (overwrite existing data)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (0 << 1); // Read SP, no output generated, only status
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (1 << 2); // Mode, Validate
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (slot->src_cnt << 4); // Number of blocks to be processed
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (1 << 9); // Type, XOR
+ if(slot->len == 256)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_256 << 11);
+ if(slot->len == 512)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_512 << 11);
+ if(slot->len == 1024)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_1024 << 11);
+ if(slot->len == 2048)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_2048 << 11);
+ if(slot->len == 4096)
+ hw_desc->fstatus0 = hw_desc->fstatus0 | (XOR_BLOCK_SIZE_4096 << 11);
+ break;
+ case DMA_MEMCPY:
+ break;
+ default:
+ dev_printk(KERN_ERR, slot->async_tx.chan->device->dev,
+ "error: unsupported operation %d.\n",slot->type);
+ return;
+ }
+}
+
+static void comcerto_set_buff_info_inbound(struct comcerto_xor_desc_slot *slot, dma_addr_t *src)
+{
+ struct comcerto_xor_inbound_desc *hw_desc = slot->hw_desc_inbound;
+ int i;
+
+ if(slot->src_cnt > COMCERTO_XOR_MAX_SRC){
+ dev_printk(KERN_ERR, slot->async_tx.chan->device->dev,
+ "error: max source available is %d.\n",COMCERTO_XOR_MAX_SRC);
+ return;
+ }
+
+ for(i=0; i<slot->src_cnt; i++) {
+ hw_desc->buff_info[i*2] = src[i];
+ if(i == slot->src_cnt - 1)
+ hw_desc->buff_info[i*2 + 1] = slot->len | BLAST;
+ else
+ hw_desc->buff_info[i*2 + 1] = slot->len;
+ }
+}
+
+/* ------- Set outbound frame descriptor ------- */
+
+static void comcerto_set_next_desc_outbound(struct comcerto_xor_desc_slot *slot, u32 next_desc_addr)
+{
+ struct comcerto_xor_outbound_desc *hw_desc = slot->hw_desc_outbound;
+ hw_desc->next_desc = next_desc_addr;
+}
+
+static void comcerto_set_fcontrol_outbound(struct comcerto_xor_desc_slot *slot)
+{
+ struct comcerto_xor_outbound_desc *hw_desc = slot->hw_desc_outbound;
+
+ switch (slot->type) {
+ case DMA_XOR:
+ case DMA_XOR_VAL:
+ case DMA_MEMCPY:
+ hw_desc->fcontrol = 0;
+ break;
+ default:
+ dev_printk(KERN_ERR, slot->async_tx.chan->device->dev,
+ "error: unsupported operation %d.\n",slot->type);
+ return;
+ }
+}
+
+static void comcerto_set_buff_info_outbound(struct comcerto_xor_desc_slot *slot, dma_addr_t dest)
+{
+ struct comcerto_xor_outbound_desc *hw_desc = slot->hw_desc_outbound;
+
+ hw_desc->buff_info[0] = dest;
+ hw_desc->buff_info[1] = slot->len | BLAST;
+}
+
+/* ------- Read inbound frame descriptor ------- */
+
+static u32 comcerto_get_buff_info_inbound(struct comcerto_xor_desc_slot *slot, u16 i)
+{
+ struct comcerto_xor_inbound_desc *hw_desc = slot->hw_desc_inbound;
+ return hw_desc->buff_info[i*2];
+}
+
+/* ------- Read outbound frame descriptor ------- */
+
+static u32 comcerto_get_fstatus0_outbound(struct comcerto_xor_desc_slot *slot)
+{
+ struct comcerto_xor_outbound_desc *hw_desc = slot->hw_desc_outbound;
+ return hw_desc->fstatus0;
+}
+
+static u32 comcerto_get_buff_info_outbound(struct comcerto_xor_desc_slot *slot, u16 i)
+{
+ struct comcerto_xor_outbound_desc *hw_desc = slot->hw_desc_outbound;
+ return hw_desc->buff_info[i*2];
+}
+
+static void comcerto_xor_inbound_desc_init(struct comcerto_xor_desc_slot *slot,
+ dma_addr_t *src)
+{
+ struct comcerto_xor_inbound_desc *hw_desc = slot->hw_desc_inbound;
+
+ memset(hw_desc, 0, sizeof(struct comcerto_xor_inbound_desc));
+ comcerto_set_fcontrol_inbound(slot);
+ comcerto_set_fstatus0_inbound(slot);
+ comcerto_set_next_desc_inbound(slot, 0);
+ comcerto_set_buff_info_inbound(slot, src);
+}
+
+static void comcerto_xor_outbound_desc_init(struct comcerto_xor_desc_slot *slot,
+ dma_addr_t dest)
+{
+ struct comcerto_xor_outbound_desc *hw_desc = slot->hw_desc_outbound;
+
+ memset(hw_desc, 0, sizeof(struct comcerto_xor_outbound_desc));
+ comcerto_set_fcontrol_outbound(slot);
+ comcerto_set_next_desc_outbound(slot, 0);
+
+ if(dest)
+ comcerto_set_buff_info_outbound(slot, dest);
+}
+
+/* --------------------- Functions of register configuration --------------------------*/
+
+/* ------- Set inbound register ------- */
+
+static void comcerto_xor_set_register_m2io_control(struct comcerto_xor_chan *comcerto_xor_ch, u32 value)
+{
+ __raw_writel(value, M2IO_CONTROL(comcerto_xor_ch));
+}
+
+static void comcerto_xor_set_register_m2io_head(struct comcerto_xor_chan *comcerto_xor_ch, u32 value)
+{
+ __raw_writel(value, M2IO_HEAD(comcerto_xor_ch));
+}
+
+/* ------- Set outbound register ------- */
+
+static void comcerto_xor_set_register_io2m_head(struct comcerto_xor_chan *comcerto_xor_ch, u32 value)
+{
+ __raw_writel(value, IO2M_HEAD(comcerto_xor_ch));
+}
+
+static void comcerto_xor_set_register_io2m_irq_enable(struct comcerto_xor_chan *comcerto_xor_ch, u32 value)
+{
+ __raw_writel(value, IO2M_IRQ_ENABLE(comcerto_xor_ch));
+}
+
+static void comcerto_xor_set_register_io2m_irq_status(struct comcerto_xor_chan *comcerto_xor_ch, u32 value)
+{
+ __raw_writel(value, IO2M_IRQ_STATUS(comcerto_xor_ch));
+}
+
+/* ------- Read inbound register ------- */
+
+static u32 comcerto_xor_get_register_m2io_control(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ return __raw_readl(M2IO_CONTROL(comcerto_xor_ch));
+}
+
+/* ------- Read outbound register ------- */
+
+static u32 comcerto_xor_get_register_io2m_control(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ return __raw_readl(IO2M_CONTROL(comcerto_xor_ch));
+}
+
+static u32 comcerto_xor_get_register_io2m_head(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ return __raw_readl(IO2M_HEAD(comcerto_xor_ch));
+}
+
+static u32 comcerto_xor_get_register_io2m_irq_status(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ return __raw_readl(IO2M_IRQ_STATUS(comcerto_xor_ch));
+}
+
+static void comcerto_dump_xor_regs(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ u32 val;
+
+ val = __raw_readl(M2IO_CONTROL(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "M2IO_CONTROL 0x%8x.\n",val);
+
+ val = __raw_readl(M2IO_HEAD(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "M2IO_HEAD 0x%8x.\n",val);
+
+ val = __raw_readl(M2IO_BURST(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "M2IO_BURST 0x%8x.\n",val);
+
+ val = __raw_readl(M2IO_FLEN(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "M2IO_FLEN 0x%8x.\n",val);
+
+ val = __raw_readl(M2IO_IRQ_ENABLE(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "M2IO_IRQ_ENABLE 0x%8x.\n",val);
+
+ val = __raw_readl(M2IO_IRQ_STATUS(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "M2IO_IRQ_STATUS 0x%8x.\n",val);
+
+ val = __raw_readl(M2IO_RESET(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "M2IO_RESET 0x%8x.\n",val);
+
+ val = __raw_readl(IO2M_CONTROL(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "IO2M_CONTROL 0x%8x.\n",val);
+
+ val = __raw_readl(IO2M_HEAD(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "IO2M_HEAD 0x%8x.\n",val);
+
+ val = __raw_readl(IO2M_BURST(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "IO2M_BURST 0x%8x.\n",val);
+
+ val = __raw_readl(IO2M_FLEN(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "IO2M_FLEN 0x%8x.\n",val);
+
+ val = __raw_readl(IO2M_IRQ_ENABLE(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "IO2M_IRQ_ENABLE 0x%8x.\n",val);
+
+ val = __raw_readl(IO2M_IRQ_STATUS(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "IO2M_IRQ_STATUS 0x%8x.\n",val);
+
+ val = __raw_readl(IO2M_RESET(comcerto_xor_ch));
+ dev_printk(KERN_ERR, comcerto_xor_ch->device->device.dev,
+ "IO2M_RESET 0x%8x.\n",val);
+
+}
+
+static void comcerto_xor_register_init(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ u32 value;
+
+ /* M2IO_CONTROL: Enable DONOSTA */
+ value = M2IO_DONOSTA;
+ comcerto_xor_set_register_m2io_control(comcerto_xor_ch,value);
+
+ /* IO2M_IRQ_ENABLE: Enable IRQFRDYN, IRQFLST and IRQFLSH*/
+ value = IO2M_IRQFRDYN | IO2M_IRQFLST | IO2M_IRQFLSH;
+ comcerto_xor_set_register_io2m_irq_enable(comcerto_xor_ch,value);
+}
+
+
+/* --------------------------- Miscellaneous functions --------------------------------*/
+
+static dma_cookie_t comcerto_xor_run_tx_complete_actions(struct comcerto_xor_desc_slot *slot,
+ struct comcerto_xor_chan *comcerto_xor_ch,
+ dma_cookie_t cookie)
+{
+ u16 src_cnt;
+
+ if(slot->async_tx.cookie < 0)
+ printk(KERN_ERR "Invalid cookie.");
+
+ if(slot->async_tx.cookie >0 ) {
+ cookie = slot->async_tx.cookie;
+ src_cnt = slot->src_cnt;
+
+ if(slot->async_tx.callback)
+ slot->async_tx.callback(slot->async_tx.callback_param);
+
+ if(slot->len) {
+ struct device *dev = comcerto_xor_ch->device->device.dev;
+ enum dma_ctrl_flags flags = slot->async_tx.flags;
+ dma_addr_t src, dest;
+
+ dest = comcerto_get_buff_info_outbound(slot,0);
+ if(!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
+ enum dma_data_direction dir;
+
+ if(src_cnt > 1)
+ dir = DMA_BIDIRECTIONAL;
+ else
+ dir = DMA_FROM_DEVICE;
+
+ dma_unmap_page(dev, dest, slot->len, dir);
+ }
+
+ if(!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
+ while(src_cnt--){
+ src = comcerto_get_buff_info_inbound(slot, src_cnt);
+ if(src == dest)
+ continue;
+ dma_unmap_page(dev, src, slot->len, DMA_TO_DEVICE);
+ }
+ }
+ }
+ }
+ dma_run_dependencies(&slot->async_tx);
+ return cookie;
+}
+
+static int comcerto_xor_clean_completed_slots(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ struct comcerto_xor_desc_slot *iter, *__iter;
+
+ list_for_each_entry_safe(iter, __iter, &comcerto_xor_ch->completed_slots, completed_node) {
+ if(async_tx_test_ack(&iter->async_tx)) {
+ list_del(&iter->completed_node);
+ iter->busy = 0;
+ }
+ }
+ return 0;
+}
+
+static void __comcerto_xor_slot_cleanup(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ struct comcerto_xor_desc_slot *iter, *__iter;
+ dma_cookie_t cookie = 0;
+ u32 current_desc = comcerto_xor_get_register_io2m_head(comcerto_xor_ch);
+ int seen_current = 0;
+
+ comcerto_xor_clean_completed_slots(comcerto_xor_ch);
+
+ list_for_each_entry_safe(iter,__iter,&comcerto_xor_ch->chain,chain_node) {
+ prefetch(__iter);
+ prefetch(&__iter->async_tx);
+
+ if(seen_current)
+ break;
+
+ if(iter->hw_desc_outbound_dma == current_desc) {
+ seen_current = 1;
+
+ /* When we arrived here, the hardware may in two
+ * possible situations:
+ *
+ * 1. Not working, stopped
+ * 2. Working on current_desc
+ *
+ * For case 1, following if statement will be false and we'll
+ * continue to clean this slot.
+ * For case 2, we just break and leave the last slot
+ * to be cleaned at next execution.
+ */
+
+ if(!(comcerto_xor_get_register_m2io_control(comcerto_xor_ch)&(0x1)) &&
+ !(comcerto_xor_get_register_io2m_control(comcerto_xor_ch)&(0x1)))
+ break;
+ }
+
+ if(iter->xor_check_result && iter->async_tx.cookie)
+ *iter->xor_check_result = comcerto_get_fstatus0_outbound(iter)&(0x1);
+
+ cookie = comcerto_xor_run_tx_complete_actions(iter,comcerto_xor_ch,cookie);
+ list_del(&iter->chain_node);
+ if(!async_tx_test_ack(&iter->async_tx))
+ list_add_tail(&iter->completed_node, &comcerto_xor_ch->completed_slots);
+ else
+ iter->busy = 0;
+ }
+
+ if(cookie > 0)
+ comcerto_xor_ch->completed_cookie = cookie;
+}
+
+
+static void comcerto_xor_slot_cleanup(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ spin_lock_bh(&comcerto_xor_ch->lock);
+ __comcerto_xor_slot_cleanup(comcerto_xor_ch);
+ spin_unlock_bh(&comcerto_xor_ch->lock);
+}
+
+static void comcerto_xor_tasklet(unsigned long data)
+{
+ struct comcerto_xor_chan *comcerto_xor_ch = (struct comcerto_xor_chan *) data;
+ comcerto_xor_slot_cleanup(comcerto_xor_ch);
+}
+
+static struct comcerto_xor_desc_slot *
+comcerto_xor_alloc_slot(struct comcerto_xor_chan *comcerto_xor_ch)
+{
+ struct comcerto_xor_desc_slot *iter, *__iter = NULL;
+ int slot_found, retry = 0;
+
+retry:
+ slot_found = 0;
+ if(retry == 0 )
+ iter = comcerto_xor_ch->last_used;
+ else
+ iter = list_entry(&comcerto_xor_ch->all_slots,struct comcerto_xor_desc_slot, slot_node);
+
+ list_for_each_entry_safe_continue(
+ iter, __iter, &comcerto_xor_ch->all_slots, slot_node) {
+ prefetch(__iter);
+ prefetch(&__iter->async_tx);
+ if(iter->busy) {
+ if (retry)
+ break;
+ slot_found=0;
+ continue;
+ }
+
+ iter->async_tx.cookie = -EBUSY;
+ iter->xor_check_result = NULL;
+ iter->busy = 1;
+ comcerto_xor_ch->last_used = iter;
+ return iter;
+ }
+
+ if(!retry++)
+ goto retry;
+
+ tasklet_schedule(&comcerto_xor_ch->irq_tasklet);
+
+ return NULL;
+}
+
+static dma_cookie_t comcerto_desc_assign_cookie(struct comcerto_xor_chan *comcerto_xor_ch,
+ struct comcerto_xor_desc_slot * slot)
+{
+ dma_cookie_t cookie = comcerto_xor_ch->chan.cookie;
+
+ if(++cookie < 0)
+ cookie = 1;
+ comcerto_xor_ch->chan.cookie = slot->async_tx.cookie = cookie;
+ return cookie;
+}
+
+
+/*------------------------------- XOR API Function-------------------------------------*/
+
+static enum dma_status comcerto_xor_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct comcerto_xor_chan *comcerto_xor_ch = to_comcerto_xor_chan(chan);
+ dma_cookie_t last_used;
+ dma_cookie_t last_complete;
+
+ last_used = chan->cookie;
+ last_complete = comcerto_xor_ch->completed_cookie;
+ dma_set_tx_state(txstate, last_complete, last_used, 0);
+
+ return dma_async_is_complete(cookie, last_complete, last_used);
+}
+
+static void comcerto_xor_issue_pending(struct dma_chan *chan)
+{
+ struct comcerto_xor_chan *comcerto_xor_ch = to_comcerto_xor_chan(chan);
+
+ if(comcerto_xor_ch->pending >= COMCERTO_XOR_THRESHOLD && comcerto_xor_ch->to_be_started) {
+ comcerto_xor_ch->pending = 0;
+ comcerto_xor_set_register_m2io_head(comcerto_xor_ch, comcerto_xor_ch->to_be_started->async_tx.phys);
+ comcerto_xor_set_register_io2m_head(comcerto_xor_ch, comcerto_xor_ch->to_be_started->hw_desc_outbound_dma);
+ comcerto_xor_ch->to_be_started = NULL;
+ }
+}
+
+static dma_cookie_t comcerto_xor_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ struct comcerto_xor_desc_slot *slot = to_comcerto_xor_slot(tx);
+ struct comcerto_xor_desc_slot *old_chain_tail;
+ dma_cookie_t cookie;
+ struct comcerto_xor_chan *comcerto_xor_ch;
+ struct dma_chan *chan;
+ chan = tx->chan;
+ comcerto_xor_ch = to_comcerto_xor_chan(chan);
+
+ spin_lock_bh(&comcerto_xor_ch->lock);
+ cookie = comcerto_desc_assign_cookie(comcerto_xor_ch, slot);
+
+ /* If chain list is empty, the hardware is absolutely stopped.
+ * Thus, we need only reset the head register in order to start
+ * the DMA
+ */
+
+ if(list_empty(&comcerto_xor_ch->chain)){
+ list_add_tail(&slot->chain_node, &comcerto_xor_ch->chain);
+ comcerto_xor_ch->to_be_started = slot;
+ comcerto_xor_ch->pending++;
+ comcerto_xor_issue_pending(&comcerto_xor_ch->chan);
+ }
+ else {
+
+ /* If chain list is not empty, the hardware may or may not stopped.
+ * - It may not stopped because the chain is not empty. That is to
+ * say, the hardware still have request to process
+ * - It may stopped because the hardware has already finished the
+ * processing. But just haven't cleared the chain. (The chain can
+ * be only cleared in the tasklet.)
+ * For whatever case, we just:
+ * - update the FNext field of last element in chain.
+ * - set head register to restart the XOR processing if the hardware
+ * is not working
+ */
+
+ old_chain_tail = list_entry(comcerto_xor_ch->chain.prev,
+ struct comcerto_xor_desc_slot,
+ chain_node);
+
+ list_add_tail(&slot->chain_node,&comcerto_xor_ch->chain);
+
+ comcerto_set_next_desc_outbound(old_chain_tail,slot->hw_desc_outbound_dma);
+ comcerto_set_next_desc_inbound(old_chain_tail,slot->async_tx.phys);
+
+ if(!(comcerto_xor_get_register_m2io_control(comcerto_xor_ch)&(0x1)) &&
+ !(comcerto_xor_get_register_io2m_control(comcerto_xor_ch)&(0x1))){
+
+ if(!comcerto_xor_ch->to_be_started)
+ comcerto_xor_ch->to_be_started = slot;
+ comcerto_xor_ch->pending++;
+ comcerto_xor_issue_pending(&comcerto_xor_ch->chan);
+ }
+ }
+ spin_unlock_bh(&comcerto_xor_ch->lock);
+ return cookie;
+}
+
+static void comcerto_xor_free_chan_resources(struct dma_chan *chan)
+{
+ struct comcerto_xor_chan *comcerto_xor_ch = to_comcerto_xor_chan(chan);
+ struct comcerto_xor_desc_slot *iter, *__iter;
+ int in_use_descs = 0;
+
+ comcerto_xor_slot_cleanup(comcerto_xor_ch);
+
+ spin_lock_bh(&comcerto_xor_ch->lock);
+
+ list_for_each_entry_safe(iter, __iter, &comcerto_xor_ch->chain, chain_node) {
+ in_use_descs++;
+ list_del(&iter->chain_node);
+ }
+
+ list_for_each_entry_safe(iter, __iter, &comcerto_xor_ch->completed_slots, completed_node) {
+ in_use_descs++;
+ list_del(&iter->completed_node);
+ }
+
+ list_for_each_entry_safe_reverse(iter, __iter, &comcerto_xor_ch->all_slots, slot_node) {
+ list_del(&iter->slot_node);
+ kfree(iter);
+ comcerto_xor_ch->slot_allocated--;
+ }
+
+ comcerto_xor_ch->last_used = NULL;
+
+ spin_unlock_bh(&comcerto_xor_ch->lock);
+
+ if(in_use_descs)
+ dev_err(comcerto_xor_ch->device->device.dev,
+ "freeing %d in use descriptors!\n", in_use_descs);
+}
+
+static int comcerto_xor_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct comcerto_xor_chan *comcerto_xor_ch = to_comcerto_xor_chan(chan);
+ struct comcerto_xor_desc_slot *slot = NULL;
+ int num_slots_per_pool = PAGE_SIZE / (COMCERTO_XOR_INBOUND_DESC_SIZE + COMCERTO_XOR_OUTBOUND_DESC_SIZE);
+ int total_slots = POOL_NUMBER * num_slots_per_pool;
+ int gap = PAGE_SIZE - num_slots_per_pool * (COMCERTO_XOR_INBOUND_DESC_SIZE + COMCERTO_XOR_OUTBOUND_DESC_SIZE);
+ int i,j;
+ char *dma_desc_pool_virt_addr;
+ char *dma_desc_pool_addr;
+
+ i = j = comcerto_xor_ch->slot_allocated;
+ j %= num_slots_per_pool;
+
+ while( i < total_slots) {
+
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if(!slot) {
+ printk(KERN_INFO "comcerto XOR Channel only initialized %d slot descriptors.\n", i);
+ break;
+ }
+
+ dma_desc_pool_virt_addr = (char *) comcerto_xor_ch->device->dma_desc_pool_virt[i/num_slots_per_pool];
+ slot->hw_desc_inbound = (void *) &dma_desc_pool_virt_addr[j*COMCERTO_XOR_INBOUND_DESC_SIZE];
+ slot->hw_desc_outbound = (void *) &dma_desc_pool_virt_addr[num_slots_per_pool*COMCERTO_XOR_INBOUND_DESC_SIZE + gap + j*COMCERTO_XOR_OUTBOUND_DESC_SIZE];
+
+ dma_async_tx_descriptor_init(&slot->async_tx, chan);
+ slot->async_tx.tx_submit = comcerto_xor_tx_submit;
+
+ INIT_LIST_HEAD(&slot->slot_node);
+ INIT_LIST_HEAD(&slot->chain_node);
+ INIT_LIST_HEAD(&slot->completed_node);
+
+ dma_desc_pool_addr = (char *) comcerto_xor_ch->device->dma_desc_pool[i/num_slots_per_pool];
+ slot->async_tx.phys = (dma_addr_t) &dma_desc_pool_addr[j*COMCERTO_XOR_INBOUND_DESC_SIZE];
+ slot->hw_desc_outbound_dma = (dma_addr_t) &dma_desc_pool_addr[num_slots_per_pool*COMCERTO_XOR_INBOUND_DESC_SIZE + gap + j*COMCERTO_XOR_OUTBOUND_DESC_SIZE];
+ slot->busy=0;
+
+ i++;
+ if(++j == num_slots_per_pool)
+ j=0;
+
+ spin_lock_bh(&comcerto_xor_ch->lock);
+ comcerto_xor_ch->slot_allocated = i;
+ list_add_tail(&slot->slot_node, &comcerto_xor_ch->all_slots);
+ spin_unlock_bh(&comcerto_xor_ch->lock);
+
+ }
+
+ if(comcerto_xor_ch->slot_allocated && !comcerto_xor_ch->last_used)
+ comcerto_xor_ch->last_used = list_entry(comcerto_xor_ch->all_slots.next, struct comcerto_xor_desc_slot, slot_node);
+
+ return comcerto_xor_ch->slot_allocated ? comcerto_xor_ch->slot_allocated : -ENOMEM;
+}
+
+static struct dma_async_tx_descriptor *
+comcerto_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct comcerto_xor_chan *comcerto_xor_ch = to_comcerto_xor_chan(chan);
+ struct comcerto_xor_desc_slot *slot;
+
+ spin_lock_bh(&comcerto_xor_ch->lock);
+ slot = comcerto_xor_alloc_slot(comcerto_xor_ch);
+ if(slot) {
+ slot->type = DMA_MEMCPY;
+ slot->len = len;
+ slot->src_cnt = 1;
+ slot->async_tx.flags = flags;
+ comcerto_xor_inbound_desc_init(slot,&src);
+
+ comcerto_xor_outbound_desc_init(slot,dest);
+ }
+ spin_unlock_bh(&comcerto_xor_ch->lock);
+
+ return slot ? &slot->async_tx : NULL;
+}
+
+static struct dma_async_tx_descriptor *
+comcerto_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
+ unsigned int src_cnt, size_t len, unsigned long flags)
+{
+ struct comcerto_xor_chan *comcerto_xor_ch = to_comcerto_xor_chan(chan);
+ struct comcerto_xor_desc_slot *slot;
+
+ if(unlikely(len!=256&&len!=512&&len!=1024&&len!=2048&&len!=4096)) {
+ printk(KERN_ERR "Length %d is not supported for XOR.\n", len);
+ return NULL;
+ }
+
+ spin_lock_bh(&comcerto_xor_ch->lock);
+ slot = comcerto_xor_alloc_slot(comcerto_xor_ch);
+ if(slot) {
+ slot->type = DMA_XOR;
+ slot->len = len;
+ slot->src_cnt = src_cnt;
+ slot->async_tx.flags = flags;
+ comcerto_xor_inbound_desc_init(slot,src);
+
+ comcerto_xor_outbound_desc_init(slot,dest);
+ }
+ spin_unlock_bh(&comcerto_xor_ch->lock);
+
+ return slot ? &slot->async_tx : NULL;
+}
+
+static struct dma_async_tx_descriptor *
+comcerto_xor_prep_dma_xor_val(struct dma_chan *chan, dma_addr_t *src,
+ unsigned int src_cnt, size_t len,
+ u32 *result, unsigned long flags)
+{
+ struct comcerto_xor_chan *comcerto_xor_ch = to_comcerto_xor_chan(chan);
+ struct comcerto_xor_desc_slot *slot;
+
+ if(unlikely(len!=256&&len!=512&&len!=1024&&len!=2048&&len!=4096)) {
+ printk(KERN_ERR "Length %d is not supported for XOR.\n", len);
+ return NULL;
+ }
+
+ spin_lock_bh(&comcerto_xor_ch->lock);
+ slot = comcerto_xor_alloc_slot(comcerto_xor_ch);
+
+ if(slot) {
+ slot->type = DMA_XOR_VAL;
+ slot->len = len;
+ slot->src_cnt = src_cnt;
+ slot->async_tx.flags = flags;
+ slot->xor_check_result = result;
+ comcerto_xor_inbound_desc_init(slot,src);
+
+ comcerto_xor_outbound_desc_init(slot,0);
+ }
+ spin_unlock_bh(&comcerto_xor_ch->lock);
+
+ return slot ? &slot->async_tx : NULL;
+}
+
+static irqreturn_t comcerto_xor_interrupt_handler(int irq, void *data)
+{
+ struct comcerto_xor_chan *comcerto_xor_ch = data;
+ int do_taskset=0;
+ u32 intr_cause = comcerto_xor_get_register_io2m_irq_status(comcerto_xor_ch);
+
+ if(intr_cause & IRQ_IRQFRDYN) {
+ printk(KERN_ALERT "IRQFRDYN: A frame is started but the frame is not ready");
+ comcerto_dump_xor_regs(comcerto_xor_ch);
+ comcerto_xor_set_register_io2m_irq_status(comcerto_xor_ch,1<<0);
+ do_taskset=1;
+ }
+
+ if(intr_cause & IRQ_IRQFLST) {
+ comcerto_xor_set_register_io2m_irq_status(comcerto_xor_ch,1<<1);
+ do_taskset=1;
+ }
+
+ if(intr_cause & IRQ_IRQFDON)
+ comcerto_xor_set_register_io2m_irq_status(comcerto_xor_ch,1<<2);
+
+ if(intr_cause & IRQ_IRQFLSH) {
+ printk(KERN_ALERT "IRQFLSH: IO has more data than the memory buffer");
+ comcerto_dump_xor_regs(comcerto_xor_ch);
+ comcerto_xor_set_register_io2m_irq_status(comcerto_xor_ch,1<<3);
+ do_taskset=1;
+ }
+
+ if(intr_cause & IRQ_IRQFLEN) {
+ comcerto_xor_set_register_io2m_irq_status(comcerto_xor_ch,1<<4);
+ do_taskset=1;
+ }
+
+ if(intr_cause & IRQ_IRQFTHLD) {
+ printk(KERN_ALERT "IRQFTHLD: Frame threshold reached. FLEN=FTHLDL");
+ comcerto_dump_xor_regs(comcerto_xor_ch);
+ comcerto_xor_set_register_io2m_irq_status(comcerto_xor_ch,1<<5);
+ do_taskset=1;
+ }
+
+ if(intr_cause & IRQ_IRQFCTRL) {
+ printk(KERN_ALERT "IRQFCTRL: 1 frame is completed or when a frame is started but not ready");
+ comcerto_dump_xor_regs(comcerto_xor_ch);
+ comcerto_xor_set_register_io2m_irq_status(comcerto_xor_ch,1<<6);
+ do_taskset=1;
+ }
+
+ if(do_taskset)
+ tasklet_schedule(&comcerto_xor_ch->irq_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+#define COMCERTO_XOR_NUM_SRC_TEST 4
+static int __devinit comcerto_xor_xor_self_test(struct comcerto_xor_device *device)
+{
+ int i, src_idx;
+ struct page *dest;
+ struct page *xor_srcs[COMCERTO_XOR_NUM_SRC_TEST];
+ struct page *zero_sum_srcs[COMCERTO_XOR_NUM_SRC_TEST+1];
+ dma_addr_t dma_srcs[COMCERTO_XOR_NUM_SRC_TEST];
+ dma_addr_t dma_zero_srcs[COMCERTO_XOR_NUM_SRC_TEST+1];
+ dma_addr_t dest_dma;
+ struct dma_async_tx_descriptor *tx;
+ struct dma_chan *dma_chan;
+ dma_cookie_t cookie;
+ u8 cmp_byte = 0;
+ u32 cmp_word;
+ u32 zero_sum_result;
+ int err = 0;
+ struct comcerto_xor_chan *comcerto_chan;
+
+ for (src_idx = 0; src_idx < COMCERTO_XOR_NUM_SRC_TEST; src_idx++) {
+ xor_srcs[src_idx] = alloc_page(GFP_KERNEL);
+ if (!xor_srcs[src_idx]) {
+ while (src_idx--)
+ __free_page(xor_srcs[src_idx]);
+ return -ENOMEM;
+ }
+ }
+
+ dest = alloc_page(GFP_KERNEL);
+ if (!dest) {
+ while (src_idx--)
+ __free_page(xor_srcs[src_idx]);
+ return -ENOMEM;
+ }
+
+ for (src_idx = 0; src_idx < COMCERTO_XOR_NUM_SRC_TEST; src_idx++) {
+ u8 *ptr = page_address(xor_srcs[src_idx]);
+ for (i = 0; i < PAGE_SIZE; i++)
+ ptr[i] = (1 << src_idx);
+ }
+
+ for (src_idx = 0; src_idx < COMCERTO_XOR_NUM_SRC_TEST; src_idx++)
+ cmp_byte ^= (u8) (1 << src_idx);
+
+ cmp_word = (cmp_byte << 24) | (cmp_byte << 16) |
+ (cmp_byte << 8) | cmp_byte;
+
+ memset(page_address(dest), 0, PAGE_SIZE);
+
+ dma_chan = container_of(device->device.channels.next,
+ struct dma_chan,
+ device_node);
+ if (comcerto_xor_alloc_chan_resources(dma_chan) < 1) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ dest_dma = dma_map_page(dma_chan->device->dev, dest, 0,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ comcerto_chan = to_comcerto_xor_chan(dma_chan);
+
+ for (i = 0; i < COMCERTO_XOR_NUM_SRC_TEST; i++)
+ dma_srcs[i] = dma_map_page(dma_chan->device->dev, xor_srcs[i],
+ 0, PAGE_SIZE, DMA_TO_DEVICE);
+
+ tx = comcerto_xor_prep_dma_xor(dma_chan, dest_dma, dma_srcs,
+ COMCERTO_XOR_NUM_SRC_TEST, PAGE_SIZE, 0);
+
+ cookie = comcerto_xor_tx_submit(tx);
+ comcerto_xor_issue_pending(dma_chan);
+ async_tx_ack(tx);
+ msleep(8);
+
+ if (comcerto_xor_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
+ dev_printk(KERN_ERR, dma_chan->device->dev,
+ "Self-test xor timed out, disabling\n");
+ err = -ENODEV;
+ goto free_resources;
+ }
+
+ dma_sync_single_for_cpu(device->device.dev, dest_dma,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) {
+ u32 *ptr = page_address(dest);
+ if (ptr[i] != cmp_word) {
+ dev_printk(KERN_ERR, dma_chan->device->dev,
+ "Self-test xor failed compare, disabling."
+ " index %d, data %x, expected %x\n", i,
+ ptr[i], cmp_word);
+ err = -ENODEV;
+ goto free_resources;
+ }
+ }
+ dma_sync_single_for_device(device->device.dev,dest_dma,
+ PAGE_SIZE, DMA_TO_DEVICE);
+
+ if(!dma_has_cap(DMA_XOR_VAL,dma_chan->device->cap_mask))
+ goto free_resources;
+
+ for(i = 0; i < COMCERTO_XOR_NUM_SRC_TEST; i++)
+ zero_sum_srcs[i]=xor_srcs[i];
+ zero_sum_srcs[i] = alloc_page(GFP_KERNEL);
+ for (src_idx = 0; src_idx < 1; src_idx++) {
+ u8 *ptr = page_address(zero_sum_srcs[i]);
+ for (i = 0; i < PAGE_SIZE; i++)
+ ptr[i] = 1 | 1<<1 | 1<<2 | 1<<3;
+ }
+
+ zero_sum_result=1;
+
+ for(i=0;i<COMCERTO_XOR_NUM_SRC_TEST+1;i++)
+ dma_zero_srcs[i]=dma_map_page(dma_chan->device->dev,
+ zero_sum_srcs[i],0,PAGE_SIZE,
+ DMA_TO_DEVICE);
+ tx = comcerto_xor_prep_dma_xor_val(dma_chan,dma_zero_srcs,
+ COMCERTO_XOR_NUM_SRC_TEST+1, PAGE_SIZE,
+ &zero_sum_result,
+ DMA_CTRL_ACK);
+ cookie = comcerto_xor_tx_submit(tx);
+ comcerto_xor_issue_pending(dma_chan);
+ msleep(8);
+
+ if (comcerto_xor_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
+ dev_printk(KERN_ERR, dma_chan->device->dev,
+ "Self-test zero sum timed out, disabling\n");
+ err = -ENODEV;
+ goto free_resources;
+ }
+
+ if(zero_sum_result != 0) {
+ dev_printk(KERN_ERR, dma_chan->device->dev,
+ "Self-test zero sum failed compare, disabling\n");
+ err = -ENODEV;
+ goto free_resources;
+ }
+ __free_page(zero_sum_srcs[COMCERTO_XOR_NUM_SRC_TEST]);
+free_resources:
+ comcerto_xor_free_chan_resources(dma_chan);
+out:
+ src_idx = COMCERTO_XOR_NUM_SRC_TEST;
+
+ while (src_idx--)
+ __free_page(xor_srcs[src_idx]);
+ __free_page(dest);
+ return err;
+
+}
+
+static int __devinit comcerto_xor_memcpy_self_test(struct comcerto_xor_device *device)
+{
+ int i;
+ struct page *dest;
+ struct page *src;
+ dma_addr_t src_dma;
+ dma_addr_t dest_dma;
+ struct dma_async_tx_descriptor *tx;
+ struct dma_chan *dma_chan;
+ dma_cookie_t cookie;
+ int err = 0;
+ struct comcerto_xor_chan *comcerto_chan;
+
+ src = alloc_page(GFP_KERNEL);
+ if (!src)
+ return -ENOMEM;
+
+ dest = alloc_page(GFP_KERNEL);
+ if (!dest) {
+ __free_page(src);
+ return -ENOMEM;
+ }
+
+ /* Fill in src buffer */
+ for (i = 0; i < PAGE_SIZE; i++) {
+ u8 *ptr = page_address(src);
+ ((u8 *) ptr)[i] = (u8)i;
+ }
+
+ memset(page_address(dest), 0, PAGE_SIZE);
+
+ dma_chan = container_of(device->device.channels.next,
+ struct dma_chan,
+ device_node);
+ if (comcerto_xor_alloc_chan_resources(dma_chan) < 1) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ dest_dma = dma_map_page(dma_chan->device->dev, dest, 0,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ comcerto_chan = to_comcerto_xor_chan(dma_chan);
+
+ src_dma = dma_map_page(dma_chan->device->dev, src,
+ 0, PAGE_SIZE, DMA_TO_DEVICE);
+
+ tx = comcerto_xor_prep_dma_memcpy(dma_chan, dest_dma, src_dma,
+ PAGE_SIZE, 0);
+
+ cookie = comcerto_xor_tx_submit(tx);
+ comcerto_xor_issue_pending(dma_chan);
+ async_tx_ack(tx);
+ msleep(8);
+
+ if (comcerto_xor_status(dma_chan, cookie, NULL) != DMA_SUCCESS) {
+ dev_printk(KERN_ERR, dma_chan->device->dev,
+ "Self-test xor timed out, disabling\n");
+ err = -ENODEV;
+ goto free_resources;
+ }
+
+ dma_sync_single_for_cpu(device->device.dev, dest_dma,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+
+ if (memcmp(page_address(src), page_address(dest), PAGE_SIZE)) {
+ dev_printk(KERN_ERR, dma_chan->device->dev,
+ "Self-test copy failed compare, disabling\n");
+ err = -ENODEV;
+ goto free_resources;
+ }
+
+free_resources:
+ comcerto_xor_free_chan_resources(dma_chan);
+out:
+ __free_page(src);
+ __free_page(dest);
+ return err;
+
+}
+
+static int __devexit comcerto_xor_remove(struct platform_device *pdev)
+{
+ struct comcerto_xor_device *comcerto_xor_dev = platform_get_drvdata(pdev);
+ struct dma_device *dma_dev = &comcerto_xor_dev->device;
+ struct comcerto_xor_chan *comcerto_xor_ch;
+ struct dma_chan *chan, *__chan;
+ int i;
+ int irq;
+
+ irq = platform_get_irq(pdev,0);
+
+ dma_async_device_unregister(dma_dev);
+
+ for(i=0;i<POOL_NUMBER;i++)
+ dma_free_coherent(&pdev->dev, PAGE_SIZE,
+ comcerto_xor_dev->dma_desc_pool_virt[i],
+ comcerto_xor_dev->dma_desc_pool[i]);
+
+ list_for_each_entry_safe(chan, __chan, &dma_dev->channels,
+ device_node) {
+ comcerto_xor_ch = to_comcerto_xor_chan(chan);
+ devm_free_irq(&pdev->dev, irq, comcerto_xor_ch);
+ list_del(&chan->device_node);
+ devm_iounmap(&pdev->dev, comcerto_xor_ch->mmr_base);
+ kfree(comcerto_xor_ch);
+ }
+
+ platform_set_drvdata(pdev,NULL);
+ kfree(comcerto_xor_dev);
+
+ return 0;
+}
+
+static int __devinit comcerto_xor_probe(struct platform_device *pdev)
+{
+ struct resource *io;
+ struct dma_device *dma_dev;
+ struct comcerto_xor_device *comcerto_xor_dev;
+ struct comcerto_xor_chan *comcerto_xor_ch;
+ int irq;
+ int ret = 0;
+ int i;
+
+ /* Retrieve related resources(mem, irq) from platform_device */
+ io = platform_get_resource(pdev,IORESOURCE_MEM,0);
+ if(!io)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev,0);
+ if(irq<0)
+ return irq;
+
+ /* Initialize comcerto_xor_device */
+ comcerto_xor_dev = devm_kzalloc(&pdev->dev, sizeof(*comcerto_xor_dev), GFP_KERNEL);
+
+ if(!comcerto_xor_dev)
+ return -ENOMEM;
+
+ dma_dev = &comcerto_xor_dev->device;
+ INIT_LIST_HEAD(&dma_dev->channels);
+
+ for(i = 0 ; i < POOL_NUMBER; i++)
+ {
+ comcerto_xor_dev->dma_desc_pool_virt[i] = dma_alloc_writecombine(&pdev->dev,
+ PAGE_SIZE,
+ &comcerto_xor_dev->dma_desc_pool[i],
+ GFP_KERNEL);
+ if(!comcerto_xor_dev->dma_desc_pool_virt[i])
+ {
+ ret = -ENOMEM;
+ goto err_free_dma;
+ }
+ }
+
+ dma_cap_set(DMA_XOR,dma_dev->cap_mask);
+ dma_cap_set(DMA_XOR_VAL,dma_dev->cap_mask);
+// dma_cap_set(DMA_MEMCPY,dma_dev->cap_mask);
+
+ dma_dev->dev = &pdev->dev;
+ dma_dev->device_alloc_chan_resources = comcerto_xor_alloc_chan_resources;
+ dma_dev->device_free_chan_resources = comcerto_xor_free_chan_resources;
+ dma_dev->device_tx_status = comcerto_xor_status;
+ dma_dev->device_issue_pending = comcerto_xor_issue_pending;
+ dma_dev->device_prep_dma_xor = comcerto_xor_prep_dma_xor;
+ dma_dev->device_prep_dma_xor_val = comcerto_xor_prep_dma_xor_val;
+// dma_dev->device_prep_dma_memcpy = comcerto_xor_prep_dma_memcpy;
+ dma_dev->max_xor = COMCERTO_XOR_MAX_SRC;
+
+ platform_set_drvdata(pdev,comcerto_xor_dev);
+
+ /* Initialize comcerto_xor_chan */
+
+ comcerto_xor_ch = devm_kzalloc(&pdev->dev,sizeof(*comcerto_xor_ch),GFP_KERNEL);
+ if(!comcerto_xor_ch) {
+ ret = -ENOMEM;
+ goto err_free_dma;
+ }
+
+ comcerto_xor_ch->device = comcerto_xor_dev;
+ comcerto_xor_ch->pending = 0;
+ comcerto_xor_ch->slot_allocated = 0;
+ comcerto_xor_ch->completed_cookie = 0;
+ comcerto_xor_ch->mmr_base = devm_ioremap(&pdev->dev, io->start,resource_size(io));
+ if(!comcerto_xor_ch->mmr_base) {
+ ret = -ENOMEM;
+ goto err_free_ch;
+ }
+
+ tasklet_init(&comcerto_xor_ch->irq_tasklet,comcerto_xor_tasklet,(unsigned long)comcerto_xor_ch);
+
+ ret = devm_request_irq(&pdev->dev, irq, comcerto_xor_interrupt_handler,
+ 0, dev_name(&pdev->dev), comcerto_xor_ch);
+ if(ret)
+ goto err_free_remap;
+
+ spin_lock_init(&comcerto_xor_ch->lock);
+ INIT_LIST_HEAD(&comcerto_xor_ch->all_slots);
+ INIT_LIST_HEAD(&comcerto_xor_ch->chain);
+ INIT_LIST_HEAD(&comcerto_xor_ch->completed_slots);
+ comcerto_xor_ch->chan.device = dma_dev;
+
+ list_add_tail(&comcerto_xor_ch->chan.device_node,&dma_dev->channels);
+
+ comcerto_xor_register_init(comcerto_xor_ch);
+
+ if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
+ ret = comcerto_xor_xor_self_test(comcerto_xor_dev);
+ dev_dbg(&pdev->dev, "xor self test returned %d\n", ret);
+ if(ret)
+ goto err_free_irq;
+ }
+
+ if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
+ ret = comcerto_xor_memcpy_self_test(comcerto_xor_dev);
+ dev_dbg(&pdev->dev, "memcpy self test returned %d\n", ret);
+ if(ret)
+ goto err_free_irq;
+ }
+
+
+ ret = dma_async_device_register(dma_dev);
+
+ if (ret) {
+ dev_dbg(&pdev->dev, "Failed to register channel %d\n",ret);
+ goto err_free_irq;
+ }
+
+ goto out;
+
+err_free_irq:
+ devm_free_irq(&pdev->dev, irq, comcerto_xor_ch);
+err_free_remap:
+ devm_iounmap(&pdev->dev, comcerto_xor_ch->mmr_base);
+err_free_ch:
+ devm_kfree(&pdev->dev, comcerto_xor_ch);
+err_free_dma:
+ platform_set_drvdata(pdev,NULL);
+ while(i--)
+ dma_free_coherent(&pdev->dev,PAGE_SIZE,comcerto_xor_dev->dma_desc_pool_virt[i],
+ comcerto_xor_dev->dma_desc_pool[i]);
+ devm_kfree(&pdev->dev, comcerto_xor_dev);
+out:
+ return ret;
+}
+
+static struct platform_driver comcerto_xor_driver = {
+ .probe = comcerto_xor_probe,
+ .remove = comcerto_xor_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "comcerto_xor",
+ },
+};
+
+static int __init comcerto_xor_init(void)
+{
+ int ret = platform_driver_register(&comcerto_xor_driver);
+ return ret;
+}
+module_init(comcerto_xor_init);
+
+static void __exit comcerto_xor_exit(void)
+{
+ platform_driver_unregister(&comcerto_xor_driver);
+ return;
+}
+module_exit(comcerto_xor_exit);
+
+MODULE_DESCRIPTION("XOR engine driver for Mindspeed Comcerto C2000 devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/comcerto_xor.h b/drivers/dma/comcerto_xor.h
new file mode 100644
index 0000000..a53534a
--- /dev/null
+++ b/drivers/dma/comcerto_xor.h
@@ -0,0 +1,135 @@
+/*
+ * comcerto_xor.h
+ *
+ * Created on: May 11, 2012
+ * Author: bwang
+ */
+
+#ifndef COMCERTO_XOR_H_
+#define COMCERTO_XOR_H_
+
+#include<linux/types.h>
+#include<linux/io.h>
+#include<linux/dmaengine.h>
+#include<linux/interrupt.h>
+
+#define POOL_NUMBER 4
+#define COMCERTO_XOR_INBOUND_DESC_SIZE 64
+#define COMCERTO_XOR_OUTBOUND_DESC_SIZE 32
+#define COMCERTO_XOR_MAX_SRC 6
+#define COMCERTO_XOR_MAX_DEST 2
+#ifdef CONFIG_COMCERTO_XOR_EXTENDED_TEST
+#define COMCERTO_XOR_THRESHOLD 100
+#endif
+#ifndef CONFIG_COMCERTO_XOR_EXTENDED_TEST
+#define COMCERTO_XOR_THRESHOLD 1
+#endif
+
+#define M2IO_CONTROL(chan) (chan->mmr_base)
+#define M2IO_HEAD(chan) (chan->mmr_base + 0x4)
+#define M2IO_BURST(chan) (chan->mmr_base + 0x8)
+#define M2IO_FLEN(chan) (chan->mmr_base + 0xC)
+#define M2IO_IRQ_ENABLE(chan) (chan->mmr_base + 0x10)
+#define M2IO_IRQ_STATUS(chan) (chan->mmr_base + 0x14)
+#define M2IO_RESET(chan) (chan->mmr_base + 0x20)
+
+#define IO2M_CONTROL(chan) (chan->mmr_base + 0x80)
+#define IO2M_HEAD(chan) (chan->mmr_base + 0x84)
+#define IO2M_BURST(chan) (chan->mmr_base + 0x88)
+#define IO2M_FLEN(chan) (chan->mmr_base + 0x8C)
+#define IO2M_IRQ_ENABLE(chan) (chan->mmr_base + 0x90)
+#define IO2M_IRQ_STATUS(chan) (chan->mmr_base + 0x94)
+#define IO2M_RESET(chan) (chan->mmr_base + 0xA0)
+
+// Mem to IO Control
+#define M2IO_START (1 << 0)
+#define M2IO_FLENEN (1 << 1)
+#define M2IO_FCOM (1 << 2)
+#define M2IO_DONOSTOP (1 << 3)
+#define M2IO_DONOSTA (1 << 4)
+
+// IO to Mem: DMA Control
+#define IO2M_IRQFRDYN (1 << 0)
+#define IO2M_IRQFLST (1 << 1)
+#define IO2M_IRQFDON (1 << 2)
+#define IO2M_IRQFLSH (1 << 3)
+#define IO2M_IRQFLEN (1 << 4)
+#define IO2M_IRQFTHLD (1 << 5)
+
+// IRQ Status Register
+#define IRQ_IRQFRDYN (1 << 0)
+#define IRQ_IRQFLST (1 << 1)
+#define IRQ_IRQFDON (1 << 2)
+#define IRQ_IRQFLSH (1 << 3)
+#define IRQ_IRQFLEN (1 << 4)
+#define IRQ_IRQFTHLD (1 << 5)
+#define IRQ_IRQFCTRL (1 << 6)
+
+// Inbound Frame and Buffer Descriptor Programming
+
+// BControl
+#define BLAST (1 << 16)
+#define BFIX (1 << 17)
+
+// Block Size
+#define XOR_BLOCK_SIZE_256 0
+#define XOR_BLOCK_SIZE_512 1
+#define XOR_BLOCK_SIZE_1024 2
+#define XOR_BLOCK_SIZE_2048 3
+#define XOR_BLOCK_SIZE_4096 4
+
+struct comcerto_xor_device {
+ dma_addr_t dma_desc_pool[POOL_NUMBER];
+ void *dma_desc_pool_virt[POOL_NUMBER];
+ struct dma_device device;
+};
+
+struct comcerto_xor_desc_slot {
+ struct list_head slot_node;
+ struct list_head chain_node;
+ struct list_head completed_node;
+ enum dma_transaction_type type;
+ void *hw_desc_inbound;
+ void *hw_desc_outbound;
+ dma_addr_t hw_desc_outbound_dma;
+ int busy;
+ u16 src_cnt;
+ size_t len;
+ struct dma_async_tx_descriptor async_tx;
+ u32 *xor_check_result;
+};
+
+struct comcerto_xor_chan {
+ int pending;
+ int slot_allocated;
+ dma_cookie_t completed_cookie;
+ spinlock_t lock;
+ void __iomem *mmr_base;
+ struct list_head all_slots;
+ struct list_head chain;
+ struct list_head completed_slots;
+ struct comcerto_xor_device *device;
+ struct dma_chan chan;
+ struct comcerto_xor_desc_slot *last_used;
+ struct comcerto_xor_desc_slot *to_be_started;
+ struct tasklet_struct irq_tasklet;
+};
+
+struct comcerto_xor_inbound_desc {
+ u32 next_desc;
+ u32 fcontrol;
+ u32 fstatus0;
+ u32 fstatus1;
+ u32 buff_info[12]; // 6 Buffer Descriptors
+};
+
+struct comcerto_xor_outbound_desc {
+ u32 next_desc;
+ u32 fcontrol;
+ u32 fstatus0;
+ u32 fstatus1;
+ u32 buff_info[4]; // 2 Buffer Descriptors
+};
+
+
+#endif /* COMCERTO_XOR_H_ */
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4e04157..ea83fe8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -147,6 +147,13 @@
help
Say yes here to support the Xilinx FPGA GPIO device
+config GPIO_C2K
+ def_bool y
+ depends on ARCH_M86XXX
+ bool "Comcerto 2000 GPIO support"
+ help
+ Say yes here to support the Comcerto 2000 GPIO device
+
config GPIO_VR41XX
tristate "NEC VR4100 series General-purpose I/O Uint support"
depends on CPU_VR41XX
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 4e018d6..924348b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -62,3 +62,5 @@
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
+obj-$(CONFIG_GPIO_C2K) += gpio-c2k.o
+
diff --git a/drivers/gpio/gpio-c2k.c b/drivers/gpio/gpio-c2k.c
new file mode 100644
index 0000000..df9d7d2
--- /dev/null
+++ b/drivers/gpio/gpio-c2k.c
@@ -0,0 +1,152 @@
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <mach/comcerto-common.h>
+#include <asm/io.h>
+#include <mach/gpio.h>
+
+#define DRV_NAME "c2k-gpio"
+static DEFINE_SPINLOCK(c2k_gpio_lock);
+
+static struct c2k_gpio_chip {
+ struct gpio_chip chip;
+};
+
+static int c2k_is_gpio_rsvd(unsigned offset)
+{
+ if (offset < 32)
+ return ((c2k_gpio_pin_stat.c2k_gpio_pins_0_31 >> offset) & 0x1) ? 1 : 0 ;
+ else if (offset < 64)
+ return ((c2k_gpio_pin_stat.c2k_gpio_pins_32_63 >> (offset - 32)) & 0x1) ? 1 : 0;
+ else
+ return -EINVAL;
+}
+
+static int c2k_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset < 32)
+ return __raw_readl(COMCERTO_GPIO_INPUT_REG) & (0x1 << offset);
+ else if (offset < 64)
+ return __raw_readl(COMCERTO_GPIO_63_32_PIN_INPUT) & (0x1 << (offset - 32));
+ else
+ return -EINVAL;
+}
+
+static inline void __c2k_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ u32 data;
+
+ if (offset < 32) {
+ data = __raw_readl(COMCERTO_GPIO_OUTPUT_REG);
+ if (value)
+ data |= (1 << offset);
+ else
+ data &= ~(1 << offset);
+ __raw_writel(data, COMCERTO_GPIO_OUTPUT_REG);
+ } else {
+ data = __raw_readl(COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ if (value)
+ data |= (1 << (offset - 32));
+ else
+ data &= ~(1 << (offset - 32));
+ __raw_writel(data, COMCERTO_GPIO_63_32_PIN_OUTPUT);
+ }
+}
+
+static void c2k_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ u32 data;
+ unsigned long flags;
+
+ if (offset > 63)
+ return;
+
+ spin_lock_irqsave(&c2k_gpio_lock, flags);
+
+ __c2k_gpio_set(chip, offset, value);
+
+ spin_unlock_irqrestore(&c2k_gpio_lock, flags);
+}
+
+static int c2k_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned long flags;
+
+ if (offset > 63)
+ return -EINVAL;
+
+ if (c2k_is_gpio_rsvd(offset)) {
+ printk(KERN_ERR "GPIO-%d is reserved and cannot be used \n", offset);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&c2k_gpio_lock, flags);
+
+ if (offset < 32)
+ __raw_writel(__raw_readl(COMCERTO_GPIO_OE_REG) & ~(0x1 << offset), COMCERTO_GPIO_OE_REG);
+ else
+ __raw_writel(__raw_readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) & ~(0x1 << (offset - 32)), COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+
+ spin_unlock_irqrestore(&c2k_gpio_lock, flags);
+
+ return 0;
+}
+
+static int c2k_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ unsigned long flags;
+ u32 data;
+
+ if (offset > 63)
+ return -EINVAL;
+
+ if (c2k_is_gpio_rsvd(offset)) {
+ printk(KERN_ERR "GPIO-%d is reserved and cannot be used \n", offset);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&c2k_gpio_lock, flags);
+
+ if (offset < 32) {
+ __raw_writel(__raw_readl(COMCERTO_GPIO_OE_REG) | (0x1 << offset), COMCERTO_GPIO_OE_REG);
+ __c2k_gpio_set(chip, offset, value);
+ } else {
+ __raw_writel(__raw_readl(COMCERTO_GPIO_63_32_PIN_OUTPUT_EN) | (0x1 << (offset - 32)), COMCERTO_GPIO_63_32_PIN_OUTPUT_EN);
+ __c2k_gpio_set(chip, offset, value);
+ }
+
+ spin_unlock_irqrestore(&c2k_gpio_lock, flags);
+
+ return 0;
+}
+
+static struct c2k_gpio_chip c2k_gpio_chip = {
+ .chip = {
+ .label = DRV_NAME,
+ .owner = THIS_MODULE,
+ .direction_input = c2k_direction_input,
+ .direction_output = c2k_direction_output,
+ .set = c2k_gpio_set,
+ .get = c2k_gpio_get,
+ .base = 0,
+ .ngpio = C2K_GPIO_NR_GPIOS,
+ },
+};
+
+void __init c2k_init_gpio(void)
+{
+ int ret;
+
+ ret = gpiochip_add(&c2k_gpio_chip.chip);
+
+ if (ret)
+ printk(KERN_WARNING "C2K GPIO registration failed: %d\n", ret);
+}
+pure_initcall(c2k_init_gpio);
+
+MODULE_AUTHOR("Mindspeed Technologies");
+MODULE_DESCRIPTION("COMCERTO 2000 GPIO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 5f13c62..3151581 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -44,7 +44,7 @@
programs use the I2C bus. Information on how to do this is
contained in the file <file:Documentation/i2c/dev-interface>.
- This support is also available as a module. If so, the module
+ This support is also available as a module. If so, the module
will be called i2c-dev.
config I2C_MUX
@@ -86,6 +86,7 @@
source drivers/i2c/algos/Kconfig
source drivers/i2c/busses/Kconfig
+source drivers/i2c/chips/Kconfig
config I2C_DEBUG_CORE
bool "I2C Core debugging messages"
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index beee6b2..a29d46d 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -7,7 +7,7 @@
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
-obj-y += algos/ busses/ muxes/
+obj-y += algos/ busses/ chips/ muxes/
ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
CFLAGS_i2c-core.o := -Wno-deprecated-declarations
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index a3afac4..1010a44 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -308,6 +308,16 @@
This driver can also be built as a module. If so, the module
will be called i2c-au1550.
+config I2C_COMCERTO
+ tristate "Comcerto I2C interface"
+ depends on I2C && (ARCH_COMCERTO)
+ help
+ If you say yes to this option, support will be included for the
+ Comcerto I2C interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-comcerto.
+
config I2C_BLACKFIN_TWI
tristate "Blackfin TWI I2C support"
depends on BLACKFIN
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index fba6da6..f960665 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -31,6 +31,7 @@
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
+obj-$(CONFIG_I2C_COMCERTO) += i2c-comcerto.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
diff --git a/drivers/i2c/busses/i2c-comcerto.c b/drivers/i2c/busses/i2c-comcerto.c
new file mode 100644
index 0000000..c2c8649
--- /dev/null
+++ b/drivers/i2c/busses/i2c-comcerto.c
@@ -0,0 +1,744 @@
+/*
+ * drivers/i2c/busses/i2c-comcerto.c
+ *
+ * Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include <mach/i2c.h>
+#include <mach/irqs.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <mach/reset.h>
+
+MODULE_AUTHOR("Mindspeed Technologies, Inc.");
+MODULE_DESCRIPTION("Comcerto I2C bus driver");
+MODULE_LICENSE("GPL");
+
+#define SPEED_HIGH_KHZ 3400
+#define SPEED_FULL_KHZ 400
+#define SPEED_NORMAL_KHZ 100
+
+static int force_poll = 0;
+static struct clk *clk_i2c;
+module_param(force_poll, bool, S_IRUGO);
+MODULE_PARM_DESC(force_poll, "Force polling mode: 0=interrupt mode, polling mode otherwise");
+
+static int speed = 0;
+module_param(speed, int, S_IRUGO);
+MODULE_PARM_DESC(speed, "I2C speed: 0=standard, 1=fast, 2=high speed");
+
+struct comcerto_i2c
+{
+ struct i2c_adapter *adapter;
+ struct device *dev;
+ unsigned long membase;
+ struct resource *io;
+ int irq;
+ u32 speed_khz;
+
+ wait_queue_head_t wait;
+ struct i2c_msg *msg;
+ int msg_state;
+ int msg_status; /* < 0: error, == 0: success, > 0: message in progress */
+ int msg_len;
+ int msg_retries;
+};
+
+#define REG_ADDR(i2c, offset) ((i2c)->membase + (offset))
+#define RD_REG(i2c, offset) __raw_readb(REG_ADDR(i2c, offset))
+#define WR_REG(i2c, offset, byte) __raw_writeb(byte, REG_ADDR(i2c, offset))
+#define RD_DATA(i2c) RD_REG(i2c, COMCERTO_I2C_DATA)
+#define WR_DATA(i2c, byte) WR_REG(i2c, COMCERTO_I2C_DATA, byte)
+#define RD_CNTR(i2c) RD_REG(i2c, COMCERTO_I2C_CNTR)
+#define WR_CNTR(i2c, byte) WR_REG(i2c, COMCERTO_I2C_CNTR, byte)
+#define RD_STAT(i2c) RD_REG(i2c, COMCERTO_I2C_STAT)
+#define WR_CCRFS(i2c, byte) WR_REG(i2c, COMCERTO_I2C_CCRFS, byte)
+#define WR_CCRH(i2c, byte) WR_REG(i2c, COMCERTO_I2C_CCRH, byte)
+#define WR_RESET(i2c, byte) WR_REG(i2c, COMCERTO_I2C_RESET, byte)
+
+enum
+{
+ TR_IDLE = 0,
+ TR_START_ACK,
+ TR_ADDR_ACK,
+ TR_DATA_ACK,
+ RX_DATA_NACK,
+};
+
+static u8 comcerto_i2c_calculate_dividers(struct comcerto_i2c *i2c)
+{
+ int m, n, hz, speed_hz;
+ int saved_n, saved_m, saved_hz;
+ u8 dividers;
+ unsigned int i2c_clk;
+
+ /* Get the i2c clock rate */
+ i2c_clk = clk_get_rate(clk_i2c);
+
+ speed_hz = i2c->speed_khz * 1000;
+ saved_hz = saved_n = saved_m = 0;
+
+ for (m = 0; m < 16; m++) {
+ for (n = 0; n < 8; n++) {
+ hz = i2c_clk / ((1 << n) * (m + 1) * 10);
+ if (!saved_hz || abs(speed_hz - hz) < abs(speed_hz - saved_hz)) {
+ saved_n = n;
+ saved_m = m;
+ saved_hz = hz;
+ }
+ }
+ }
+
+ dividers = (saved_m << 3) | saved_n;
+ dev_dbg(i2c->dev, "%s: speed=%dkHz, M=%d, N=%d, dividers=0x%02x\n", __FUNCTION__,
+ saved_hz/1000, saved_m, saved_n, dividers);
+ printk("%s: speed=%dkHz, M=%d, N=%d, dividers=0x%02x\n", __FUNCTION__, saved_hz/1000, saved_m, saved_n, dividers);
+
+ return dividers;
+}
+
+/*
+ * Returns the timeout (in jiffies) for the given message.
+ */
+static int comcerto_i2c_calculate_timeout(struct comcerto_i2c *i2c, struct i2c_msg *msg)
+{
+ int timeout;
+
+ /* if no timeout was specified, calculate it */
+ if (i2c->adapter->timeout <= 0) {
+ if (i2c->irq >= 0) {
+ /* for the interrupt mode calculate timeout for 'full' message */
+ timeout = ((int)msg->len) * 10; /* convert approx. to bits */
+ timeout /= i2c->speed_khz; /* convert to bits per ms (note of kHz scale) */
+ timeout += timeout >> 1; /* add 50% */
+ timeout = timeout*HZ / 1000; /* convert to jiffies */
+ if (timeout < HZ / 5) /* at least 200ms */
+ timeout = HZ / 5;
+ }
+ else
+ timeout = HZ; /* 1 second for the polling mode */
+ }
+ else
+ timeout = i2c->adapter->timeout;
+
+ return timeout;
+}
+
+/*
+ * Initialize I2C core. Zero CNTR and DATA, try RESET. Short busy wait and check core status.
+ * After that set dividers for choosen speed.
+ */
+static void comcerto_i2c_reset(struct comcerto_i2c *i2c)
+{
+ u8 status, dividers;
+
+ dev_dbg(i2c->dev, "%s\n", __FUNCTION__);
+
+ WR_CNTR(i2c, 0);
+ WR_DATA(i2c, 0);
+ WR_RESET(i2c, 1);
+
+ udelay(10);
+
+ status = RD_STAT(i2c);
+ if (status != STAT_NO_RELEVANT_INFO)
+ dev_warn(i2c->dev, "%s: unexpected status after reset: 0x%02x\n", __FUNCTION__, status);
+
+ /* dividers should be placed in CCRH for high-sped mode and in CCRFS for standard/full modes */
+ dividers = comcerto_i2c_calculate_dividers(i2c);
+ if (i2c->speed_khz == SPEED_HIGH_KHZ)
+ WR_CCRH(i2c, dividers);
+ else
+ WR_CCRFS(i2c, dividers);
+}
+
+static inline void comcerto_i2c_message_complete(struct comcerto_i2c *i2c, int status)
+{
+ i2c->msg_status = status;
+ WR_CNTR(i2c, CNTR_STP);
+}
+
+static inline int comcerto_i2c_message_in_progress(struct comcerto_i2c *i2c)
+{
+ return i2c->msg_status > 0;
+}
+
+/*
+ * Wait event. This function sleeps in polling mode, in interrupt
+ * mode it enables IRQ from I2C core and exits immediately.
+ */
+static int comcerto_i2c_wait(struct comcerto_i2c *i2c, u8 cntr)
+{
+ cntr &= ~(CNTR_IFLG | CNTR_IEN); /* clear both IFLG and IEN */
+
+ if (i2c->irq < 0) {
+ ulong jiffies_mark = jiffies + comcerto_i2c_calculate_timeout(i2c, i2c->msg);
+
+ WR_CNTR(i2c, cntr);
+ while ((RD_CNTR(i2c) & CNTR_IFLG) == 0) {
+ if (need_resched())
+ schedule();
+
+ if (time_after(jiffies, jiffies_mark)) {
+ dev_dbg(i2c->dev, "%s: polling transfer timeout\n", __FUNCTION__);
+ comcerto_i2c_message_complete(i2c, -ETIME);
+ comcerto_i2c_reset(i2c);
+ break;
+ }
+ }
+ }
+ else {
+ /* enable interrupt */
+ WR_CNTR(i2c, cntr | CNTR_IEN);
+ }
+
+ return 0;
+}
+
+static void comcerto_i2c_state_idle(struct comcerto_i2c *i2c, u8 *cntr)
+{
+ if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) {
+ i2c->msg_state = TR_ADDR_ACK;
+ }
+ else {
+ *cntr = CNTR_STP|CNTR_STA; /* SPT|STA to auto recover from bus error state transparently at the start of the transfer */
+ i2c->msg_state = TR_START_ACK;
+ }
+}
+
+static void comcerto_i2c_state_start_ack(struct comcerto_i2c *i2c, u8 *cntr)
+{
+ u8 status, addr;
+
+ *cntr = 0; /* zero IFLG, IEN (for the interrupt mode it will be enabled in wait function) */
+
+ status = RD_STAT(i2c);
+
+ if (status == STAT_START || status == STAT_START_REPEATED) {
+ i2c->msg_state = TR_ADDR_ACK;
+
+ addr = i2c->msg->addr << 1;
+ if (i2c->msg->flags & I2C_M_RD)
+ addr |= 1;
+ if (i2c->msg->flags & I2C_M_REV_DIR_ADDR)
+ addr ^= 1; /* invert RW bit if it's requested */
+
+ WR_DATA(i2c, addr); /* write address and read/write bit */
+ } else {
+ dev_dbg(i2c->dev, "%s: unexpected state (%#x) on start phase, %s\n",
+ __FUNCTION__, status, i2c->msg_retries > 1 ? "retrying":"aborting");
+
+ if (--i2c->msg_retries < 0)
+ comcerto_i2c_message_complete(i2c, -1);
+ else
+ comcerto_i2c_state_idle(i2c, cntr);
+ }
+}
+
+static void comcerto_i2c_rx(struct comcerto_i2c *i2c)
+{
+ u8 status, cntr = 0;
+
+restart:
+ switch (i2c->msg_state) {
+ case TR_IDLE:
+ comcerto_i2c_state_idle(i2c, &cntr);
+ if (unlikely(i2c->msg->flags & I2C_M_NOSTART))
+ goto restart; /* needed to avoid event loss in interrupt mode */
+ break;
+
+ case TR_START_ACK:
+ comcerto_i2c_state_start_ack(i2c, &cntr);
+ break;
+
+ case TR_ADDR_ACK:
+ if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) {
+ /* we can enter this state if skip start/addr flag is set, so fake good ack */
+ status = STAT_ADDR_RD_ACK;
+ }
+ else {
+ status = RD_STAT(i2c);
+ /* check whether we should ignore NACK */
+ if (status == STAT_DATA_RD_NACK && (i2c->msg->flags & I2C_M_IGNORE_NAK))
+ status = STAT_DATA_RD_ACK;
+ }
+
+ if (likely(status == STAT_ADDR_RD_ACK)) {
+ /* start reception phase - wait until data is ready and loop in RX_DATA_ACK state
+ * until we read all the data, sending ACK after each byte (but the last)
+ */
+ i2c->msg_len = 0;
+ if (i2c->msg->len > 1) {
+ i2c->msg_state = TR_DATA_ACK;
+ cntr = CNTR_AAK;
+ }
+ else if (i2c->msg->len == 1) {
+ i2c->msg_state = RX_DATA_NACK;
+ }
+ else { /* nothing to receive, send STOP and signal success */
+ comcerto_i2c_message_complete(i2c, 0);
+ }
+ }
+ else {
+ dev_dbg(i2c->dev, "%s: unexpected state (%#x) on address phase, %s\n",
+ __FUNCTION__, status, i2c->msg_retries > 1 ? "retrying":"aborting");
+
+ if (--i2c->msg_retries < 0)
+ comcerto_i2c_message_complete(i2c, -1);
+ else
+ comcerto_i2c_state_idle(i2c, &cntr);
+ }
+ break;
+
+ case TR_DATA_ACK:
+ status = RD_STAT(i2c);
+
+ if (likely(status == STAT_DATA_RD_ACK)) {
+ i2c->msg->buf[i2c->msg_len++] = RD_DATA(i2c);
+ if (likely(i2c->msg->len - i2c->msg_len > 1)) {
+ cntr = CNTR_AAK;
+ }
+ else {
+ i2c->msg_state = RX_DATA_NACK;
+ /* NACK should be transmitted on the last byte */
+ }
+ }
+ else {
+ dev_dbg(i2c->dev, "%s: unexpected state (%#x) on read phase\n", __FUNCTION__, status);
+ comcerto_i2c_message_complete(i2c, -1);
+ }
+ break;
+
+ case RX_DATA_NACK:
+ status = RD_STAT(i2c);
+ if (likely(status == STAT_DATA_RD_NACK)) {
+ i2c->msg->buf[i2c->msg_len++] = RD_DATA(i2c);
+ comcerto_i2c_message_complete(i2c, 0);
+ }
+ else {
+ dev_dbg(i2c->dev, "%s: unexpected state (%#x) on finishing read phase\n", __FUNCTION__, status);
+ comcerto_i2c_message_complete(i2c, -1);
+ }
+ }
+
+ /* no wait if we completed message */
+ if (comcerto_i2c_message_in_progress(i2c))
+ comcerto_i2c_wait(i2c, cntr);
+}
+
+static void comcerto_i2c_tx(struct comcerto_i2c *i2c)
+{
+ u8 status, cntr = 0;
+
+restart:
+ switch (i2c->msg_state) {
+ case TR_IDLE:
+ comcerto_i2c_state_idle(i2c, &cntr);
+ if (unlikely(i2c->msg->flags & I2C_M_NOSTART))
+ goto restart; /* needed to avoid event loss in interrupt mode */
+ break;
+
+ case TR_START_ACK:
+ comcerto_i2c_state_start_ack(i2c, &cntr);
+ break;
+
+ case TR_ADDR_ACK:
+ if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) {
+ /* we can enter this state if skip start/addr flag is set, so fake good ack */
+ status = STAT_ADDR_WR_ACK;
+ }
+ else {
+ status = RD_STAT(i2c);
+ if (status == STAT_DATA_WR_NACK && (i2c->msg->flags & I2C_M_IGNORE_NAK))
+ status = STAT_DATA_WR_ACK;
+ }
+
+ if (likely(status == STAT_ADDR_WR_ACK)) {
+ /* start reception phase - wait until data is ready and loop in TX_DATA_ACK state
+ * until we read all the data, sending ACK after each byte (but the last)
+ */
+ i2c->msg_state = TR_DATA_ACK;
+ i2c->msg_len = 0;
+ if (likely(i2c->msg->len != 0)) {
+ WR_DATA(i2c, i2c->msg->buf[i2c->msg_len++]);
+ //printk("comcerto_i2c_tx: i2c->msg->buf[i2c->msg_len - 1]=%d\n", i2c->msg->buf[i2c->msg_len - 1]);
+ }
+ else {
+ /* nothing to transmit, send STOP and signal success */
+ comcerto_i2c_message_complete(i2c, 0);
+ }
+ }
+ else {
+ dev_dbg(i2c->dev, "%s: unexpected state (%#x) on address phase, %s\n",
+ __FUNCTION__, status, i2c->msg_retries > 1 ? "retrying":"aborting");
+
+ if (--i2c->msg_retries < 0)
+ comcerto_i2c_message_complete(i2c, -1);
+ else
+ comcerto_i2c_state_idle(i2c, &cntr);
+ }
+ break;
+
+ case TR_DATA_ACK:
+ status = RD_STAT(i2c);
+ if (status == STAT_DATA_WR_NACK && (i2c->msg->flags & I2C_M_IGNORE_NAK))
+ status = STAT_DATA_WR_ACK;
+
+ if (likely(status == STAT_DATA_WR_ACK)) {
+ if (i2c->msg->len > i2c->msg_len)
+ WR_DATA(i2c, i2c->msg->buf[i2c->msg_len++]);
+ else
+ comcerto_i2c_message_complete(i2c, 0);
+ }
+ else {
+ dev_dbg(i2c->dev, "%s: unexpected state (%#x) on read data phase\n", __FUNCTION__, status);
+ comcerto_i2c_message_complete(i2c, -1);
+ }
+ break;
+ }
+
+ if (comcerto_i2c_message_in_progress(i2c))
+ comcerto_i2c_wait(i2c, cntr);
+}
+
+static irqreturn_t comcerto_i2c_interrupt(int irq, void *dev_id)
+{
+ struct comcerto_i2c *i2c = dev_id;
+
+ if (!(RD_CNTR(i2c) & CNTR_IFLG))
+ goto none;
+
+ /* IRQ enable/disable logic is hidden in state handlers, all we need is to wake
+ * process when message completed.
+ */
+ if (i2c->msg->flags & I2C_M_RD)
+ comcerto_i2c_rx(i2c);
+ else
+ comcerto_i2c_tx(i2c);
+
+ if (!comcerto_i2c_message_in_progress(i2c)) {
+ WR_CNTR(i2c, RD_CNTR(i2c) & ~CNTR_IEN); /* disable interrupt unconditionally */
+ wake_up(&i2c->wait);
+ }
+
+ return IRQ_HANDLED;
+none:
+ return IRQ_NONE;
+}
+
+static void comcerto_i2c_message_process(struct comcerto_i2c *i2c, struct i2c_msg *msg)
+{
+ i2c->msg = msg;
+ i2c->msg_state = TR_IDLE;
+ i2c->msg_status = 1;
+ i2c->msg_retries = i2c->adapter->retries;
+
+polling_mode:
+ if (msg->flags & I2C_M_RD)
+ comcerto_i2c_rx(i2c);
+ else
+ comcerto_i2c_tx(i2c);
+
+ if (i2c->irq < 0) {
+ /*if (i2c->msg != NULL)*/
+ goto polling_mode;
+ }
+ else {
+ int timeout, res;
+ ulong flags;
+
+ timeout = comcerto_i2c_calculate_timeout(i2c, msg);
+
+ res = wait_event_timeout(i2c->wait, i2c->msg_status <= 0, timeout);
+
+ local_irq_save(flags);
+
+ /* check if we timed out and set respective error codes */
+ if (res == 0) {
+ if (comcerto_i2c_message_in_progress(i2c)) {
+ dev_dbg(i2c->dev, "%s: interrupt transfer timeout\n", __FUNCTION__);
+ comcerto_i2c_message_complete(i2c, -ETIME);
+ comcerto_i2c_reset(i2c);
+ }
+ }
+
+ local_irq_restore(flags);
+ }
+}
+
+/*
+ * Generic master transfer entrypoint.
+ * Returns the number of processed messages or error value
+ */
+static int comcerto_i2c_master_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num)
+{
+ struct comcerto_i2c *i2c = i2c_get_adapdata(adapter);
+ int i;
+
+ dev_dbg(i2c->dev, "%s: %d messages to process\n", __FUNCTION__, num);
+
+ for (i = 0; i < num; i++) {
+ dev_dbg(i2c->dev, "%s: message #%d: addr=%#x, flags=%#x, len=%u\n", __FUNCTION__,
+ i, msgs[i].addr, msgs[i].flags, msgs[i].len);
+
+ comcerto_i2c_message_process(i2c, &msgs[i]);
+
+ if (i2c->msg_status < 0) {
+ dev_dbg(i2c->dev, "%s: transfer failed on message #%d (addr=%#x, flags=%#x, len=%u)\n",
+ __FUNCTION__, i, msgs[i].addr, msgs[i].flags, msgs[i].len);
+ break;
+ }
+ }
+
+ if (i2c->msg_status == -1)
+ i2c->msg_status = -EIO;
+
+ if (i2c->msg_status == 0)
+ i2c->msg_status = num;
+
+ return i2c->msg_status;
+}
+
+static u32 comcerto_i2c_functionality(struct i2c_adapter *adap)
+{
+ return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL);
+}
+
+static struct i2c_algorithm comcerto_i2c_algo = {
+ .master_xfer = comcerto_i2c_master_xfer,
+ .functionality = comcerto_i2c_functionality,
+};
+
+static struct i2c_adapter comcerto_i2c_adapter = {
+ .name = "comcerto_i2c",
+ .owner = THIS_MODULE,
+ .algo = &comcerto_i2c_algo,
+ .timeout = 0, /* <= zero means that we calculate timeout in run-time, can be changed with ioctl call */
+ .retries = 0, /* no retries by default - let the user decide what's the best, can be changed with ioctl call */
+};
+
+static int comcerto_i2c_probe(struct platform_device *pdev)
+{
+ struct comcerto_i2c *i2c;
+ struct resource *irq;
+ int res = -1;
+
+ dev_dbg(&pdev->dev, "%s\n", __FUNCTION__);
+
+ /* Put the I2C device Out-Of-Reset*/
+ c2000_block_reset(COMPONENT_AXI_I2C,0);
+
+ /* Clock divider configuration, get the i2c clock*/
+ clk_i2c = clk_get(NULL, "spi_i2c");
+ if (IS_ERR(clk_i2c)){
+ pr_err("%s: Unable to obtain i2c clock: %ld\n", __func__, PTR_ERR(clk_i2c));
+ return PTR_ERR(clk_i2c);
+ }
+
+ /*Enable the i2c clock */
+ res = clk_enable(clk_i2c);
+ if (res){
+ pr_err("%s: i2c clock failed to enable:\n", __func__);
+ goto err0;
+ }
+
+ i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+ if (i2c == NULL) {
+ dev_err(&pdev->dev, "%s: failed allocate memory\n", __FUNCTION__);
+ res = -ENOMEM;
+ goto err0;
+ }
+
+ i2c->adapter = &comcerto_i2c_adapter;
+ i2c->adapter->dev.parent = &pdev->dev;
+ i2c->dev = &pdev->dev;
+
+ init_waitqueue_head(&i2c->wait);
+
+ platform_set_drvdata(pdev, i2c);
+ i2c_set_adapdata(&comcerto_i2c_adapter, i2c);
+
+ i2c->io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (i2c->io == NULL) {
+ dev_err(&pdev->dev, "%s: no IO region specified\n", __FUNCTION__);
+ res = -ENOENT;
+ goto err1;
+ }
+
+ if (!request_mem_region(i2c->io->start, i2c->io->end - i2c->io->start + 1, "I2C")) {
+ dev_err(i2c->dev, "%s: failed to request memory region\n", __FUNCTION__);
+ goto err1;
+ }
+
+ i2c->membase = APB_VADDR(i2c->io->start);
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (irq == NULL && !force_poll) {
+ dev_warn(i2c->dev, "%s: no IRQ specified in resources, polling mode forced\n", __FUNCTION__);
+ force_poll = 1;
+ i2c->irq = -1;
+ }
+
+ if (speed == 0) {
+ i2c->speed_khz = SPEED_NORMAL_KHZ;
+ }
+ else if (speed == 1) {
+ i2c->speed_khz = SPEED_FULL_KHZ;
+ }
+ else if (speed == 2) {
+ i2c->speed_khz = SPEED_HIGH_KHZ;
+ }
+ else {
+ dev_err(i2c->dev, "%s: invalid 'speed' module option provided (%d, must be 0,1,2 for normal/full/high modes)\n",
+ __FUNCTION__, speed);
+ goto err2;
+ }
+
+
+ comcerto_i2c_reset(i2c);
+
+ if (!force_poll) {
+ i2c->irq = irq->start;
+
+ res = request_irq(i2c->irq, comcerto_i2c_interrupt, IRQF_SHARED, "I2C", i2c);
+ if (res < 0) {
+ dev_warn(i2c->dev, "%s: failed to request IRQ%d, polling mode forced\n", __FUNCTION__, i2c->irq);
+ force_poll = 1;
+ i2c->irq = -1;
+ }
+ }
+ else
+ i2c->irq = -1;
+
+ if (i2c_add_adapter(&comcerto_i2c_adapter) != 0) {
+ dev_err(i2c->dev, "%s: failed to add I2C adapter\n", __FUNCTION__);
+ goto err3;
+ }
+
+ dev_dbg(&pdev->dev, "%s: I2C adapter registered\n", __FUNCTION__);
+
+ return 0;
+
+err3:
+ if (i2c->irq >= 0)
+ free_irq(i2c->irq, i2c);
+
+err2:
+ release_mem_region(i2c->io->start, i2c->io->end - i2c->io->end + 1);
+
+err1:
+ kfree(i2c);
+
+err0:
+ return res;
+}
+
+static int comcerto_i2c_remove(struct platform_device *pdev)
+{
+ struct comcerto_i2c *i2c = platform_get_drvdata(pdev);
+
+ dev_dbg(i2c->dev, "%s\n", __FUNCTION__);
+
+ platform_set_drvdata(pdev, NULL);
+
+ i2c_del_adapter(i2c->adapter);
+
+ if (i2c->irq >= 0)
+ free_irq(i2c->irq, i2c);
+
+ release_mem_region(i2c->io->start, i2c->io->end - i2c->io->start + 1);
+
+ kfree(i2c);
+
+ /* Disable the Clock */
+ clk_disable(clk_i2c);
+ clk_put(clk_i2c);
+
+ /* Put the I2C device in reset state*/
+ c2000_block_reset(COMPONENT_AXI_I2C,1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int comcerto_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ /* No need to suspend client drivers here. Because clients are
+ * children, client drivers get suspended before adapter driver
+ */
+
+ /* So do the Clock disable here , This clock is depends upon Legacy SPI*/
+ clk_disable(clk_i2c);
+ return 0;
+}
+
+static int comcerto_i2c_resume(struct platform_device *pdev)
+{
+ int ret;
+ /* No need to suspend client drivers here. Because clients are
+ * children, client drivers get suspended before adapter driver
+ */
+
+ /* So do the Clock Enable here , This clock is depends upon Legacy SPI*/
+ ret = clk_enable(clk_i2c);
+ if (ret)
+ {
+ pr_err("%s: I2C clock enable failed \n",__func__);
+ return ret;
+ }
+ return 0;
+}
+#endif
+
+static struct platform_driver comcerto_i2c_driver = {
+ .driver = {
+ .name = "comcerto_i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = comcerto_i2c_probe,
+ .remove = comcerto_i2c_remove,
+#ifdef CONFIG_PM
+ .suspend = comcerto_i2c_suspend,
+ .resume = comcerto_i2c_resume,
+#endif
+};
+
+static int __init comcerto_i2c_init(void)
+{
+ if (platform_driver_register(&comcerto_i2c_driver)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __exit comcerto_i2c_exit(void)
+{
+ platform_driver_unregister(&comcerto_i2c_driver);
+}
+
+module_init(comcerto_i2c_init);
+module_exit(comcerto_i2c_exit);
+
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
new file mode 100644
index 0000000..6b3e093
--- /dev/null
+++ b/drivers/i2c/chips/Kconfig
@@ -0,0 +1,15 @@
+#
+# Miscellaneous I2C chip drivers configuration
+#
+# *** DEPRECATED! Do not add new entries! See Makefile ***
+#
+
+menu "Miscellaneous I2C Chip support"
+
+config EEPROM_AT
+ tristate "I2c-eeprom-at"
+ depends on I2C
+ help
+ This provides an interface to read and write the EEPROM and reset the chip.
+
+endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
new file mode 100644
index 0000000..6ca1e2a
--- /dev/null
+++ b/drivers/i2c/chips/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for miscellaneous I2C chip drivers.
+#
+# Do not add new drivers to this directory! It is DEPRECATED.
+#
+# Device drivers are better grouped according to the functionality they
+# implement rather than to the bus they are connected to. In particular:
+# * Hardware monitoring chip drivers go to drivers/hwmon
+# * RTC chip drivers go to drivers/rtc
+# * I/O expander drivers go to drivers/gpio
+#
+
+obj-$(CONFIG_EEPROM_AT) += eeprom.o
+
+ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c
new file mode 100644
index 0000000..73eaa5a
--- /dev/null
+++ b/drivers/i2c/chips/eeprom.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Mindspeed Technologies, Inc.");
+MODULE_DESCRIPTION("I2C EEPROM driver");
+MODULE_LICENSE("GPL");
+
+#define CONFIG_EEPROM_PAGE_SIZE 128
+#define CONFIG_EEPROM_CHIP_SIZE (512*1024)
+#define CONFIG_EEPROM_WRITE_TIME 5
+
+#define EEPROM_PAGE (CONFIG_EEPROM_PAGE_SIZE)
+#define EEPROM_SIZE (CONFIG_EEPROM_CHIP_SIZE)
+#define EEPROM_WAIT (CONFIG_EEPROM_WRITE_TIME)
+
+#define DRV_NAME "eeprom"
+
+struct i2c_client *eeprom_i2c_client;
+
+static inline int eeprom_set_address(struct i2c_msg *msg, u32 addr)
+{
+ int addr_len = 1;
+
+ msg->buf[0] = addr >> 8;
+ msg->len++;
+ if (EEPROM_SIZE > 256) {
+ msg->buf[1] = addr & 255;
+ msg->len++;
+ addr_len++;
+ }
+
+ return addr_len;
+}
+
+static int eeprom_set_pointer(struct i2c_client *client, u32 addr)
+{
+ int err;
+ u8 buf[2];
+ struct i2c_msg msg =
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 0,
+ .buf = buf,
+ };
+
+ eeprom_set_address(&msg, addr);
+ if ((err = i2c_transfer(client->adapter, &msg, 1)) != 1) {
+ dev_err(&client->dev, "read transaction failed - couldn't set address, code: %d\n", err);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int eeprom_read_data(struct i2c_client *client, void *buf, int len)
+{
+ int err, i = 0;
+ struct i2c_msg msg =
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ };
+
+ if ((err = i2c_transfer(client->adapter, &msg, 1)) != 1) {
+ dev_err(&client->dev, "read transaction failed, code: %d\n", err);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int eeprom_write_data(struct i2c_client *client, u32 addr, void *buf, void *src, int len)
+{
+ int err;
+ struct i2c_msg msg =
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = len,
+ .buf = buf,
+ };
+
+ /* set address and copy data just after address bytes */
+ memcpy(buf + eeprom_set_address(&msg, addr), src, len);
+
+ if ((err = i2c_transfer(client->adapter, &msg, 1)) != 1) {
+ dev_err(&client->dev, "write transaction failed, code: %d\n", err);
+ return -EIO;
+ }
+
+ mdelay(EEPROM_WAIT);
+
+ return 0;
+}
+
+static int eeprom_read_write(struct kobject *kobj, char *buf, loff_t offs, size_t len, int read)
+{
+ struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
+ int count;
+
+ if (offs + len > EEPROM_SIZE)
+ len = EEPROM_SIZE - offs;
+ if (len < 0)
+ return -EINVAL;
+ if (len == 0)
+ return 0;
+
+ if (read) {
+ count = eeprom_set_pointer(client, offs); /* for the read transaction set address once at start */
+ if (count == 0)
+ count = eeprom_read_data(client, buf, len);
+ if (count == 0)
+ count = len;
+ }
+ else {
+ u8 *txbuf = kmalloc(EEPROM_PAGE + 2, GFP_KERNEL);
+ u32 tmp;
+ int req, err;
+
+ if (txbuf != NULL) {
+ /* write all data in no more than one page size transaction */
+ count = 0;
+
+ do {
+ tmp = (offs + count + EEPROM_PAGE) & ~(EEPROM_PAGE - 1);
+ req = tmp - offs - count;
+ if (req + count > len)
+ req = len - count;
+
+ err = eeprom_write_data(client, offs+count, txbuf, buf+count, req);
+ if (err < 0) {
+ count = err;
+ break;
+ }
+
+ count += req;
+ } while (count < len);
+
+ kfree(txbuf);
+ } else {
+ dev_err(&client->dev, "failed to allocate memory\n");
+ count = -ENOMEM;
+ }
+ }
+
+ return count;
+}
+
+static ssize_t eeprom_read(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int i = 0;
+ loff_t off = 0x50;
+ size_t ret, count = 4;
+
+ ret = eeprom_read_write(&dev->kobj, buf, off + i, count, 1);
+
+ return ret;
+}
+
+static ssize_t eeprom_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ loff_t off = 0x50;
+
+ return eeprom_read_write(&dev->kobj, buf, off, count, 0);
+}
+
+static struct device_attribute eeprom_attr = __ATTR(eeprom, 0644, eeprom_read, eeprom_write);
+
+int eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err = 0;
+
+ client = eeprom_i2c_client;
+
+ err = sysfs_create_file(&client->dev.kobj, &eeprom_attr);
+ if (err) {
+ dev_err(&client->dev, "failed to create sysfs node\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return err;
+}
+
+static int eeprom_remove(struct i2c_client *client)
+{
+ sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int eeprom_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int eeprom_resume(struct i2c_client *client)
+{
+ return 0;
+}
+#else
+#define eeprom_suspend NULL
+#define eeprom_resume NULL
+#endif /* CONFIG_PM */
+
+static struct i2c_device_id eeprom_idtable[] = {
+ { "eeprom", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, eeprom_idtable);
+
+static struct i2c_driver eeprom_driver = {
+ .id_table = eeprom_idtable,
+ .probe = eeprom_probe,
+ .remove = __devexit_p(eeprom_remove),
+ .suspend = eeprom_suspend,
+ .resume = eeprom_resume,
+ .driver = {
+ .name = DRV_NAME,
+ .owner =THIS_MODULE,
+ },
+};
+
+static unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END };
+
+static int eeprom_init(void)
+{
+ struct i2c_adapter *i2c_adap;
+ struct i2c_board_info i2c_info;
+
+ i2c_adap = i2c_get_adapter(0);
+ memset(&i2c_info, 0, sizeof(struct i2c_board_info));
+ strlcpy(i2c_info.type, "eeprom", I2C_NAME_SIZE);
+ eeprom_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info, normal_i2c, NULL);
+
+ i2c_put_adapter(i2c_adap);
+
+ i2c_add_driver(&eeprom_driver);
+
+ return 0;
+}
+
+static void eeprom_exit(void)
+{
+ i2c_del_driver(&eeprom_driver);
+}
+
+module_init(eeprom_init);
+module_exit(eeprom_exit);
+
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 58d8c6d..5040ce1 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -29,6 +29,16 @@
#include <linux/device-mapper.h>
+#if defined(CONFIG_OCF_DM_CRYPT)
+#undef DM_DEBUG
+#ifdef DM_DEBUG
+#define dmprintk printk
+#else
+#define dmprintk(fmt,args...)
+#endif
+#include <linux/cryptodev.h>
+#endif /* CONFIG_OCF_DM_CRYPT */
+
#define DM_MSG_PREFIX "crypt"
/*
@@ -146,6 +156,10 @@
sector_t iv_offset;
unsigned int iv_size;
+#if defined(CONFIG_OCF_DM_CRYPT)
+ struct cryptoini cr_dm; /* OCF session */
+ uint64_t ocf_cryptoid; /* OCF sesssion ID */
+#endif
/*
* Duplicated per cpu state. Access through
* per_cpu_ptr() only.
@@ -269,7 +283,7 @@
return err;
for_each_possible_cpu(cpu) {
- essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private,
+ essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private;
err = crypto_cipher_setkey(essiv_tfm, essiv->salt,
crypto_hash_digestsize(essiv->hash_tfm));
@@ -305,7 +319,7 @@
struct dm_target *ti,
u8 *salt, unsigned saltsize)
{
- struct crypto_cipher *essiv_tfm;
+ struct crypto_cipher *essiv_tfm=NULL;
int err;
/* Setup the essiv_tfm with the given salt */
@@ -315,8 +329,12 @@
return essiv_tfm;
}
+#if defined(CONFIG_OCF_DM_CRYPT)
+ if (crypto_cipher_blocksize(essiv_tfm) != cc->iv_size) {
+#else
if (crypto_cipher_blocksize(essiv_tfm) !=
crypto_ablkcipher_ivsize(any_tfm(cc))) {
+#endif
ti->error = "Block size of ESSIV cipher does "
"not match IV size of block cipher";
crypto_free_cipher(essiv_tfm);
@@ -419,6 +437,7 @@
return 0;
}
+#if !defined(CONFIG_OCF_DM_CRYPT)
static int crypt_iv_benbi_ctr(struct crypt_config *cc, struct dm_target *ti,
const char *opts)
{
@@ -459,6 +478,7 @@
return 0;
}
+#endif
static int crypt_iv_null_gen(struct crypt_config *cc, u8 *iv,
struct dm_crypt_request *dmreq)
@@ -634,11 +654,13 @@
.generator = crypt_iv_essiv_gen
};
+#if !defined(CONFIG_OCF_DM_CRYPT)
static struct crypt_iv_operations crypt_iv_benbi_ops = {
.ctr = crypt_iv_benbi_ctr,
.dtr = crypt_iv_benbi_dtr,
.generator = crypt_iv_benbi_gen
};
+#endif
static struct crypt_iv_operations crypt_iv_null_ops = {
.generator = crypt_iv_null_gen
@@ -687,6 +709,275 @@
crypto_ablkcipher_alignmask(any_tfm(cc)) + 1);
}
+static void kcryptd_crypt_read_done(struct dm_crypt_io *io);
+static void crypt_alloc_req(struct crypt_config *cc,
+ struct convert_context *ctx);
+static void crypt_inc_pending(struct dm_crypt_io *io);
+static void crypt_dec_pending(struct dm_crypt_io *io);
+
+#if defined(CONFIG_OCF_DM_CRYPT)
+struct ocf_wr_priv {
+ u32 dm_ocf_wr_completed; /* Num of wr completions */
+ u32 dm_ocf_wr_pending; /* Num of wr pendings */
+ wait_queue_head_t dm_ocf_wr_queue; /* waiting Q, for wr completion */
+};
+
+struct ocf_rd_priv {
+ u32 dm_ocf_rd_completed; /* Num of rd completions */
+ u32 dm_ocf_rd_pending; /* Num of rd pendings */
+ wait_queue_head_t dm_ocf_rd_queue; /* waiting Q, for rd completion */
+};
+
+static int dm_ocf_wr_cb(struct cryptop *crp)
+{
+ struct ocf_wr_priv *ocf_wr_priv;
+
+ if(crp == NULL) {
+ printk("dm_ocf_wr_cb: crp is NULL!! \n");
+ return 0;
+ }
+
+ ocf_wr_priv = (struct ocf_wr_priv*)crp->crp_opaque;
+
+ ocf_wr_priv->dm_ocf_wr_completed++;
+
+ /* if no more pending for read, wake up the read task. */
+ if(ocf_wr_priv->dm_ocf_wr_completed == ocf_wr_priv->dm_ocf_wr_pending)
+ wake_up(&ocf_wr_priv->dm_ocf_wr_queue);
+
+ crypto_freereq(crp);
+ return 0;
+}
+
+static int dm_ocf_rd_cb(struct cryptop *crp)
+{
+ struct ocf_rd_priv *ocf_rd_priv;
+
+ if(crp == NULL) {
+ printk("dm_ocf_rd_cb: crp is NULL!! \n");
+ return 0;
+ }
+
+ ocf_rd_priv = (struct ocf_rd_priv*)crp->crp_opaque;
+
+ ocf_rd_priv->dm_ocf_rd_completed++;
+
+ /* if no more pending for read, wake up the read task. */
+ if(ocf_rd_priv->dm_ocf_rd_completed == ocf_rd_priv->dm_ocf_rd_pending)
+ wake_up(&ocf_rd_priv->dm_ocf_rd_queue);
+
+ crypto_freereq(crp);
+ return 0;
+}
+
+static inline int dm_ocf_process(struct crypt_config *cc, struct scatterlist *out,
+ struct scatterlist *in, unsigned int len, u8 *iv, int iv_size, int write, void *priv)
+{
+ struct cryptop *crp;
+ struct cryptodesc *crda = NULL;
+ struct page *in_page = sg_page(in);
+ struct page *out_page = sg_page(out);
+ int r=0;
+
+ if(!iv) {
+ printk("dm_ocf_process: only CBC mode is supported\n");
+ return -EPERM;
+ }
+
+ crp = crypto_getreq(1); /* only encryption/decryption */
+ if (!crp) {
+ printk("\ndm_ocf_process: crypto_getreq failed!!\n");
+ return -ENOMEM;
+ }
+
+ crda = crp->crp_desc;
+
+ crda->crd_flags = (write)? CRD_F_ENCRYPT: 0;
+ crda->crd_alg = cc->cr_dm.cri_alg;
+ crda->crd_skip = 0;
+ crda->crd_len = len;
+ crda->crd_inject = 0; /* NA */
+ crda->crd_klen = cc->cr_dm.cri_klen;
+ crda->crd_key = cc->cr_dm.cri_key;
+
+ if (iv) {
+ //crda->crd_flags |= (CRD_F_IV_PRESENT);
+ crda->crd_flags |= (CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT);
+ if( iv_size > EALG_MAX_BLOCK_LEN ) {
+ printk("dm_ocf_process: iv is too big!!\n");
+ }
+ memcpy(&crda->crd_iv, iv, iv_size);
+ }
+
+
+ //dmprintk("len: %d\n",len);
+ crp->crp_ilen = len; /* Total input length */
+ //crp->crp_flags = CRYPTO_F_CBIMM;
+ crp->crp_flags = CRYPTO_F_CBIMM | CRYPTO_F_BATCH;
+ crp->crp_buf = page_address(in_page) + in->offset;
+ crp->crp_out_buf = page_address(out_page) + out->offset;
+ crp->crp_opaque = priv;
+ if(write) {
+ crp->crp_callback = dm_ocf_wr_cb;
+ }
+ else {
+ crp->crp_callback = dm_ocf_rd_cb;
+ }
+ crp->crp_sid = cc->ocf_cryptoid;
+ r = crypto_dispatch(crp);
+ if (r == ENOMEM) {
+ dmprintk("crypto_dispatch returned ENOMEM \n");
+ }
+ return r;
+}
+
+static int ocf_crypt_convert_block(struct crypt_config *cc,
+ struct convert_context *ctx,
+ struct ablkcipher_request *req,
+ int write, void *priv)
+{
+ struct bio_vec *bv_in = bio_iovec_idx(ctx->bio_in, ctx->idx_in);
+ struct bio_vec *bv_out = bio_iovec_idx(ctx->bio_out, ctx->idx_out);
+ struct dm_crypt_request *dmreq;
+ u8 iv[cc->iv_size];
+ int r = 0;
+
+ dmreq = dmreq_of_req(cc, req);
+ dmreq->iv_sector = ctx->sector;
+ dmreq->ctx = ctx;
+ sg_init_table(&dmreq->sg_in, 1);
+ sg_set_page(&dmreq->sg_in, bv_in->bv_page, 1 << SECTOR_SHIFT,
+ bv_in->bv_offset + ctx->offset_in);
+
+ sg_init_table(&dmreq->sg_out, 1);
+ sg_set_page(&dmreq->sg_out, bv_out->bv_page, 1 << SECTOR_SHIFT,
+ bv_out->bv_offset + ctx->offset_out);
+
+ ctx->offset_in += 1 << SECTOR_SHIFT;
+ if (ctx->offset_in >= bv_in->bv_len) {
+ ctx->offset_in = 0;
+ ctx->idx_in++;
+ }
+
+ ctx->offset_out += 1 << SECTOR_SHIFT;
+ if (ctx->offset_out >= bv_out->bv_len) {
+ ctx->offset_out = 0;
+ ctx->idx_out++;
+ }
+
+ if (cc->iv_gen_ops) {
+ r = cc->iv_gen_ops->generator(cc, iv, dmreq);
+ if (r < 0)
+ return r;
+ r = dm_ocf_process(cc, &dmreq->sg_out, &dmreq->sg_in, dmreq->sg_in.length, iv, cc->iv_size, write, priv);
+ } else {
+ r = dm_ocf_process(cc, &dmreq->sg_in, &dmreq->sg_in, dmreq->sg_in.length, NULL, 0, write, priv);
+ }
+
+ return r;
+}
+
+/*
+ * Encrypt / decrypt data from one bio to another one (can be the same one)
+ */
+static int ocf_crypt_convert(struct crypt_config *cc,
+ struct convert_context *ctx, struct dm_crypt_io *io)
+{
+ int r = 0;
+ long wr_timeout = 300000;
+ long rd_timeout = 300000;
+ long wr_tm, rd_tm;
+ int num = 0, num1 =0;
+ void *priv = NULL;
+ struct ocf_wr_priv *ocf_wr_priv = NULL;
+ struct ocf_rd_priv *ocf_rd_priv = NULL;
+ int write=0;
+ struct crypt_cpu *this_cc = this_crypt_config(cc);
+
+ atomic_set(&ctx->pending, 1);
+
+
+ if(bio_data_dir(ctx->bio_in) == WRITE) {
+ write = 1;
+ ocf_wr_priv = kmalloc(sizeof(struct ocf_wr_priv),GFP_KERNEL);
+ if(!ocf_wr_priv) {
+ printk("ocf_crypt_convert: out of memory \n");
+ return -ENOMEM;
+ }
+ ocf_wr_priv->dm_ocf_wr_pending = 0;
+ ocf_wr_priv->dm_ocf_wr_completed = 0;
+ init_waitqueue_head(&ocf_wr_priv->dm_ocf_wr_queue);
+ priv = ocf_wr_priv;
+ }
+ else {
+ ocf_rd_priv = kmalloc(sizeof(struct ocf_rd_priv),GFP_KERNEL);
+ if(!ocf_rd_priv) {
+ printk("ocf_crypt_convert: out of memory \n");
+ return -ENOMEM;
+ }
+ ocf_rd_priv->dm_ocf_rd_pending = 0;
+ ocf_rd_priv->dm_ocf_rd_completed = 0;
+ init_waitqueue_head(&ocf_rd_priv->dm_ocf_rd_queue);
+ priv = ocf_rd_priv;
+ }
+
+ while(ctx->idx_in < ctx->bio_in->bi_vcnt &&
+ ctx->idx_out < ctx->bio_out->bi_vcnt) {
+
+ crypt_alloc_req(cc, ctx);
+
+ atomic_inc(&ctx->pending);
+
+ if(bio_data_dir(ctx->bio_in) == WRITE) {
+ num++;
+ }
+ /* if last read in the context - send the io, so the OCF read callback will release the IO. */
+ else {
+ num1++;
+ }
+
+ r = ocf_crypt_convert_block(cc, ctx, this_cc->req, write, priv);
+
+ switch (r) {
+ case 0:
+ atomic_dec(&ctx->pending);
+ ctx->sector++;
+ //cond_resched();
+ continue;
+ /* error */
+ default:
+ atomic_dec(&ctx->pending);
+ //return r;
+ }
+ }
+
+ if(bio_data_dir(ctx->bio_in) == WRITE) {
+ ocf_wr_priv->dm_ocf_wr_pending += num;
+ wr_tm = wait_event_timeout(ocf_wr_priv->dm_ocf_wr_queue,
+ (ocf_wr_priv->dm_ocf_wr_pending == ocf_wr_priv->dm_ocf_wr_completed)
+ , msecs_to_jiffies(wr_timeout) );
+ if (!wr_tm) {
+ printk("ocf_crypt_convert: wr work was not finished in %ld msecs, %d pending %d completed.\n",
+ wr_timeout, ocf_wr_priv->dm_ocf_wr_pending, ocf_wr_priv->dm_ocf_wr_completed);
+ }
+ kfree(ocf_wr_priv);
+ }
+ else {
+ ocf_rd_priv->dm_ocf_rd_pending += num1;
+ rd_tm = wait_event_timeout(ocf_rd_priv->dm_ocf_rd_queue,
+ (ocf_rd_priv->dm_ocf_rd_pending == ocf_rd_priv->dm_ocf_rd_completed)
+ , msecs_to_jiffies(rd_timeout) );
+ if (!rd_tm) {
+ printk("ocf_crypt_convert: rd work was not finished in %ld msecs, %d pending %d completed.\n",
+ rd_timeout, ocf_rd_priv->dm_ocf_rd_pending, ocf_rd_priv->dm_ocf_rd_completed);
+ }
+ kfree(ocf_rd_priv);
+ }
+
+ return r;
+}
+#endif /*CONFIG_OCF_DM_CRYPT*/
+
static int crypt_convert_block(struct crypt_config *cc,
struct convert_context *ctx,
struct ablkcipher_request *req)
@@ -749,15 +1040,19 @@
struct convert_context *ctx)
{
struct crypt_cpu *this_cc = this_crypt_config(cc);
+#if !defined(CONFIG_OCF_DM_CRYPT)
unsigned key_index = ctx->sector & (cc->tfms_count - 1);
+#endif
if (!this_cc->req)
this_cc->req = mempool_alloc(cc->req_pool, GFP_NOIO);
+#if !defined(CONFIG_OCF_DM_CRYPT)
ablkcipher_request_set_tfm(this_cc->req, this_cc->tfms[key_index]);
ablkcipher_request_set_callback(this_cc->req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
kcryptd_async_done, dmreq_of_req(cc, this_cc->req));
+#endif
}
/*
@@ -1102,10 +1397,18 @@
sector += bio_sectors(clone);
crypt_inc_pending(io);
+#if defined(CONFIG_OCF_DM_CRYPT)
+ r = ocf_crypt_convert(cc, &io->ctx, io);
+ if(r < 0) {
+ printk("\n%s() ocf_crypt_convert failed\n",__FUNCTION__);
+ io->error = -EIO;
+ }
+#else
r = crypt_convert(cc, &io->ctx);
if (r < 0)
io->error = -EIO;
+#endif
crypt_finished = atomic_dec_and_test(&io->ctx.pending);
@@ -1177,9 +1480,17 @@
crypt_convert_init(cc, &io->ctx, io->base_bio, io->base_bio,
io->sector);
+#if defined(CONFIG_OCF_DM_CRYPT)
+ r = ocf_crypt_convert(cc, &io->ctx, io);
+ if(r < 0) {
+ printk("\n%s() ocf_crypt_convert failed\n",__FUNCTION__);
+ io->error = -EIO;
+ }
+#else
r = crypt_convert(cc, &io->ctx);
if (r < 0)
io->error = -EIO;
+#endif
if (atomic_dec_and_test(&io->ctx.pending))
kcryptd_crypt_read_done(io);
@@ -1341,7 +1652,11 @@
set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
+#if !defined(CONFIG_OCF_DM_CRYPT)
r = crypt_setkey_allcpus(cc);
+#else
+ r = 0;
+#endif
out:
/* Hex key string not needed after here, so wipe it. */
@@ -1355,7 +1670,11 @@
clear_bit(DM_CRYPT_KEY_VALID, &cc->flags);
memset(&cc->key, 0, cc->key_size * sizeof(u8));
+#if !defined(CONFIG_OCF_DM_CRYPT)
return crypt_setkey_allcpus(cc);
+#else
+ return 0;
+#endif
}
static void crypt_dtr(struct dm_target *ti)
@@ -1379,8 +1698,14 @@
cpu_cc = per_cpu_ptr(cc->cpu, cpu);
if (cpu_cc->req)
mempool_free(cpu_cc->req, cc->req_pool);
+#if !defined(CONFIG_OCF_DM_CRYPT)
crypt_free_tfms(cc, cpu);
+#endif
}
+#if defined(CONFIG_OCF_DM_CRYPT)
+ if(cc->ocf_cryptoid)
+ crypto_freesession(cc->ocf_cryptoid);
+#endif
if (cc->bs)
bioset_free(cc->bs);
@@ -1414,7 +1739,11 @@
struct crypt_config *cc = ti->private;
char *tmp, *cipher, *chainmode, *ivmode, *ivopts, *keycount;
char *cipher_api = NULL;
+#if !defined(CONFIG_OCF_DM_CRYPT)
int cpu, ret = -EINVAL;
+#else
+ int ret = -EINVAL;
+#endif
/* Convert to crypto api definition? */
if (strchr(cipher_in, '(')) {
@@ -1487,6 +1816,40 @@
goto bad_mem;
}
+ /* Initialize and set key */
+ ret = crypt_set_key(cc, key);
+ if (ret < 0) {
+ ti->error = "Error decoding and setting key";
+ goto bad;
+ }
+
+#if defined(CONFIG_OCF_DM_CRYPT)
+ /* prepare a new OCF session */
+ memset(&cc->cr_dm, 0, sizeof(struct cryptoini));
+
+ if((strcmp(cipher,"aes") == 0) && (strcmp(chainmode, "cbc") == 0))
+ cc->cr_dm.cri_alg = CRYPTO_AES_CBC;
+ else if((strcmp(cipher,"des") == 0) && (strcmp(chainmode, "cbc") == 0))
+ cc->cr_dm.cri_alg = CRYPTO_DES_CBC;
+ else if((strcmp(cipher,"des3_ede") == 0) && (strcmp(chainmode, "cbc") == 0))
+ cc->cr_dm.cri_alg = CRYPTO_3DES_CBC;
+ else {
+ dmprintk("\ncrypt_ctr: using OCF: unknown cipher or bad chain mode\n");
+ ti->error = DM_MSG_PREFIX "using OCF: unknown cipher or bad chain mode";
+ goto bad;
+ }
+
+ //dmprintk("key size is %d\n",cc->key_size);
+ cc->cr_dm.cri_klen = cc->key_size*8;
+ cc->cr_dm.cri_key = cc->key;
+ cc->cr_dm.cri_next = NULL;
+
+ if(crypto_newsession(&cc->ocf_cryptoid, &cc->cr_dm, CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)){
+ dmprintk("crypt_ctr: crypto_newsession failed\n");
+ ti->error = DM_MSG_PREFIX "crypto_newsession failed";
+ goto bad;
+ }
+#else
/* Allocate cipher */
for_each_possible_cpu(cpu) {
ret = crypt_alloc_tfms(cc, cpu, cipher_api);
@@ -1496,13 +1859,6 @@
}
}
- /* Initialize and set key */
- ret = crypt_set_key(cc, key);
- if (ret < 0) {
- ti->error = "Error decoding and setting key";
- goto bad;
- }
-
/* Initialize IV */
cc->iv_size = crypto_ablkcipher_ivsize(any_tfm(cc));
if (cc->iv_size)
@@ -1513,6 +1869,7 @@
DMWARN("Selected cipher does not support IVs");
ivmode = NULL;
}
+#endif/*CONFIG_OCF_DM_CRYPT*/
/* Choose ivmode, see comments at iv code. */
if (ivmode == NULL)
@@ -1523,8 +1880,10 @@
cc->iv_gen_ops = &crypt_iv_plain64_ops;
else if (strcmp(ivmode, "essiv") == 0)
cc->iv_gen_ops = &crypt_iv_essiv_ops;
+#if !defined(CONFIG_OCF_DM_CRYPT)
else if (strcmp(ivmode, "benbi") == 0)
cc->iv_gen_ops = &crypt_iv_benbi_ops;
+#endif
else if (strcmp(ivmode, "null") == 0)
cc->iv_gen_ops = &crypt_iv_null_ops;
else if (strcmp(ivmode, "lmk") == 0) {
@@ -1541,6 +1900,17 @@
goto bad;
}
+#if defined(CONFIG_OCF_DM_CRYPT)
+ switch (cc->cr_dm.cri_alg) {
+ case CRYPTO_AES_CBC:
+ cc->iv_size = 16;
+ break;
+ default:
+ cc->iv_size = 8;
+ break;
+ }
+#endif
+
/* Allocate IV */
if (cc->iv_gen_ops && cc->iv_gen_ops->ctr) {
ret = cc->iv_gen_ops->ctr(cc, ti, ivopts);
@@ -1550,6 +1920,7 @@
}
}
+#if !defined(CONFIG_OCF_DM_CRYPT)
/* Initialize IV (set keys for ESSIV etc) */
if (cc->iv_gen_ops && cc->iv_gen_ops->init) {
ret = cc->iv_gen_ops->init(cc);
@@ -1558,6 +1929,7 @@
goto bad;
}
}
+#endif
ret = 0;
bad:
@@ -1613,10 +1985,12 @@
}
cc->dmreq_start = sizeof(struct ablkcipher_request);
+#if !defined(CONFIG_OCF_DM_CRYPT)
cc->dmreq_start += crypto_ablkcipher_reqsize(any_tfm(cc));
cc->dmreq_start = ALIGN(cc->dmreq_start, crypto_tfm_ctx_alignment());
cc->dmreq_start += crypto_ablkcipher_alignmask(any_tfm(cc)) &
~(crypto_tfm_ctx_alignment() - 1);
+#endif
cc->req_pool = mempool_create_kmalloc_pool(MIN_IOS, cc->dmreq_start +
sizeof(struct dm_crypt_request) + cc->iv_size);
@@ -1895,6 +2269,10 @@
kmem_cache_destroy(_crypt_io_pool);
}
+#ifdef CONFIG_OCF_DM_CRYPT
+ printk("dm_crypt using the OCF for crypto acceleration.\n");
+#endif
+
return r;
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 2d97bf0..09846f0 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -571,12 +571,30 @@
best_good_sectors = sectors;
dist = abs(this_sector - conf->mirrors[disk].head_position);
+ /* Prefer idle disk, but still choose best if more than one idle disk:
+ * We add an artifical, arbitrary (Max/2) weight to busy disks to favor
+ * idle disks. If the distance is greater than Max/2, then we make it
+ * the maximum value. We may not choose the smaller distance if all disks
+ * fall into that situation, but since all disks will have long seek times
+ * we don't really care.
+ * We may end up choosing a busy disk if idle disks have a very high
+ * distance, but this may actually be preferable to minimize seek times.
+ * If dist == 0, we choose that disk even if it busy to again minimize seek
+ * times, as it is likely a sequential read with a single pending request.
+ * TODO: use per disk next_seq_sect to better detect sequential reads?
+ */
+ if (dist == 0) {
+ best_disk = disk;
+ break;
+ }
+ if (atomic_read(&rdev->nr_pending) != 0) {
+ if (dist < MaxSector/2)
+ dist += MaxSector / 2;
+ else dist = MaxSector - 1;
+ }
if (choose_first
/* Don't change to another disk for sequential reads */
- || conf->next_seq_sect == this_sector
- || dist == 0
- /* If device is idle, use it */
- || atomic_read(&rdev->nr_pending) == 0) {
+ || conf->next_seq_sect == this_sector) {
best_disk = disk;
break;
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 26ef63a..2316102 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -485,6 +485,28 @@
static void
raid5_end_write_request(struct bio *bi, int error);
+#ifdef CONFIG_RAID_ZERO_COPY
+static inline void r5dev_switch_page(struct r5dev *dev, struct page *page)
+{
+ BUG_ON(dev->page_save != NULL);
+ BUG_ON(dev->page != bio_iovec_idx(&dev->req, 0)->bv_page);
+ /* The pointer must be restored whenever the LOCKED gets cleared. */
+ dev->page_save = dev->page;
+ dev->page = bio_iovec_idx(&dev->req, 0)->bv_page = page;
+ kmap(dev->page); /* for sync_xor on 32-bit systems */
+}
+
+static inline void r5dev_restore_page(struct r5dev *dev)
+{
+ BUG_ON(dev->page_save == NULL);
+ BUG_ON(dev->page != bio_iovec_idx(&dev->req, 0)->bv_page);
+ BUG_ON(dev->page == dev->page_save);
+ kunmap(dev->page_save);
+ dev->page = bio_iovec_idx(&dev->req, 0)->bv_page = dev->page_save;
+ dev->page_save = NULL;
+}
+#endif
+
static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
{
struct r5conf *conf = sh->raid_conf;
@@ -585,6 +607,11 @@
set_bit(STRIPE_DEGRADED, &sh->state);
pr_debug("skip op %ld on disc %d for sector %llu\n",
bi->bi_rw, i, (unsigned long long)sh->sector);
+#ifdef CONFIG_RAID_ZERO_COPY
+ if (test_bit(R5_DirectAccess, &sh->dev[i].flags)) {
+ r5dev_restore_page(&sh->dev[i]);
+ }
+#endif
clear_bit(R5_LOCKED, &sh->dev[i].flags);
set_bit(STRIPE_HANDLE, &sh->state);
}
@@ -781,9 +808,23 @@
__func__, (unsigned long long)sh->sector, target);
BUG_ON(!test_bit(R5_Wantcompute, &tgt->flags));
+#if 0
for (i = disks; i--; )
if (i != target)
xor_srcs[count++] = sh->dev[i].page;
+#else
+ for (i = disks; i--; ) {
+ struct r5dev *dev = &sh->dev[i];
+ struct page *pg = dev->page;
+ if (i != target) {
+#ifdef CONFIG_RAID_ZERO_COPY
+ if (test_bit(R5_DirectAccess, &dev->flags))
+ pg = dev->req.bi_io_vec[0].bv_page;
+#endif
+ xor_srcs[count++] = pg;
+ }
+ }
+#endif
atomic_inc(&sh->count);
@@ -1020,8 +1061,19 @@
for (i = disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
/* Only process blocks that are known to be uptodate */
+#if 0
if (test_bit(R5_Wantdrain, &dev->flags))
xor_srcs[count++] = dev->page;
+#else
+ if (test_bit(R5_Wantdrain, &dev->flags)) {
+ struct page *pg = dev->page;
+#ifdef CONFIG_RAID_ZERO_COPY
+ if (test_bit(R5_DirectAccess, &dev->flags))
+ pg = dev->req.bi_io_vec[0].bv_page;
+#endif
+ xor_srcs[count++] = pg;
+ }
+#endif
}
init_async_submit(&submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
@@ -1031,12 +1083,55 @@
return tx;
}
+#ifdef CONFIG_RAID_ZERO_COPY
+static struct page *raid5_zero_copy(struct bio *bio, sector_t sector)
+{
+ sector_t bi_sector = bio->bi_sector;
+ struct page *page = NULL;
+ struct bio_vec *bv;
+ int i;
+
+ bio_for_each_segment(bv, bio, i) {
+ if (sector == bi_sector)
+ page = bio_iovec_idx(bio, i)->bv_page;
+
+ bi_sector += bio_iovec_idx(bio, i)->bv_len >> 9;
+ if (bi_sector >= sector + STRIPE_SECTORS) {
+ /* check if the stripe is covered by one page */
+ if (page == bio_iovec_idx(bio, i)->bv_page) {
+ SetPageConstant(page);
+ return page;
+ }
+ return NULL;
+ }
+ }
+ return NULL;
+}
+#endif
+
+
+
static struct dma_async_tx_descriptor *
ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx)
{
int disks = sh->disks;
int i;
+#ifdef CONFIG_RAID_ZERO_COPY
+//This is the workaround for the data corruption which happens in the read-modify-write writes scenario
+ int write_count = 0;
+ int do_zero_copy = 0;
+
+ for (i = disks; i--; )
+ {
+ struct r5dev *dev = &sh->dev[i];
+ if(dev->towrite)
+ write_count++;
+ }
+ if(write_count == (disks-1))
+ do_zero_copy = 1;
+#endif
+
pr_debug("%s: stripe %llu\n", __func__,
(unsigned long long)sh->sector);
@@ -1052,10 +1147,30 @@
dev->towrite = NULL;
BUG_ON(dev->written);
wbi = dev->written = chosen;
- spin_unlock_irq(&sh->raid_conf->device_lock);
+#ifdef CONFIG_RAID_ZERO_COPY
+ set_bit(R5_LOCKED, &dev->flags);
+ BUG_ON(test_bit(R5_DirectAccess, &dev->flags));
+ spin_unlock_irq(&sh->raid_conf->device_lock);
+ if (!wbi->bi_next && test_bit(R5_OVERWRITE, &dev->flags)
+ && test_bit(R5_Insync, &dev->flags) && do_zero_copy) {
+ struct page *pg = raid5_zero_copy(wbi,
+ dev->sector);
+ if (pg) {
+ set_bit(R5_DirectAccess, &dev->flags);
+ r5dev_switch_page(dev, pg);
+ clear_bit(R5_UPTODATE, &dev->flags);
+ clear_bit(R5_OVERWRITE, &dev->flags);
+ continue;
+ }
+ }
- while (wbi && wbi->bi_sector <
- dev->sector + STRIPE_SECTORS) {
+ clear_bit(R5_OVERWRITE, &dev->flags);
+ set_bit(R5_UPTODATE, &dev->flags);
+#else
+ spin_unlock_irq(&sh->raid_conf->device_lock);
+#endif
+ while (wbi && wbi->bi_sector <
+ dev->sector + STRIPE_SECTORS) {
if (wbi->bi_rw & REQ_FUA)
set_bit(R5_WantFUA, &dev->flags);
tx = async_copy_data(1, wbi, dev->page,
@@ -1086,13 +1201,17 @@
for (i = disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
+#ifdef CONFIG_RAID_ZERO_COPY
+ if ((dev->written && !test_bit(R5_DirectAccess, &dev->flags)) || i == pd_idx || i == qd_idx) {
+#else
if (dev->written || i == pd_idx || i == qd_idx) {
+#endif
set_bit(R5_UPTODATE, &dev->flags);
if (fua)
set_bit(R5_WantFUA, &dev->flags);
}
}
-
+
if (sh->reconstruct_state == reconstruct_state_drain_run)
sh->reconstruct_state = reconstruct_state_drain_result;
else if (sh->reconstruct_state == reconstruct_state_prexor_drain_run)
@@ -1129,15 +1248,39 @@
xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page;
for (i = disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
+#if 0
if (dev->written)
xor_srcs[count++] = dev->page;
+#else
+ struct page *pg = dev->page;
+
+ if (dev->written) {
+#ifdef CONFIG_RAID_ZERO_COPY
+ if (test_bit(R5_DirectAccess, &dev->flags))
+ pg = dev->req.bi_io_vec[0].bv_page;
+#endif
+ xor_srcs[count++] = pg;
+ }
+#endif
}
} else {
xor_dest = sh->dev[pd_idx].page;
for (i = disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
+#if 0
if (i != pd_idx)
xor_srcs[count++] = dev->page;
+#else
+ struct page *pg = dev->page;
+
+ if (i != pd_idx) {
+#ifdef CONFIG_RAID_ZERO_COPY
+ if (test_bit(R5_DirectAccess, &dev->flags))
+ pg = dev->req.bi_io_vec[0].bv_page;
+#endif
+ xor_srcs[count++] = pg;
+ }
+#endif
}
}
@@ -1700,6 +1843,12 @@
set_bit(R5_MadeGood, &sh->dev[i].flags);
rdev_dec_pending(conf->disks[i].rdev, conf->mddev);
+
+#ifdef CONFIG_RAID_ZERO_COPY
+ if (test_bit(R5_DirectAccess, &sh->dev[i].flags)) {
+ r5dev_restore_page(&sh->dev[i]);
+ }
+#endif
clear_bit(R5_LOCKED, &sh->dev[i].flags);
set_bit(STRIPE_HANDLE, &sh->state);
@@ -2520,7 +2669,11 @@
if (sh->dev[i].written) {
dev = &sh->dev[i];
if (!test_bit(R5_LOCKED, &dev->flags) &&
- test_bit(R5_UPTODATE, &dev->flags)) {
+ (test_bit(R5_UPTODATE, &dev->flags)
+#ifdef CONFIG_RAID_ZERO_COPY
+ || test_bit(R5_DirectAccess, &dev->flags)
+#endif
+ )) {
/* We can return any write requests */
struct bio *wbi, *wbi2;
int bitmap_end = 0;
@@ -2528,6 +2681,9 @@
spin_lock_irq(&conf->device_lock);
wbi = dev->written;
dev->written = NULL;
+#ifdef CONFIG_RAID_ZERO_COPY
+ clear_bit(R5_DirectAccess, &dev->flags);
+#endif
while (wbi && wbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
wbi2 = r5_next_bio(wbi, dev->sector);
@@ -3227,7 +3383,11 @@
/* All the 'written' buffers and the parity block are ready to
* be written back to disk
*/
+#ifdef CONFIG_RAID_ZERO_COPY
+ BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags) && !test_bit(R5_DirectAccess, &sh->dev[sh->pd_idx].flags));
+#else
BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags));
+#endif
BUG_ON(sh->qd_idx >= 0 &&
!test_bit(R5_UPTODATE, &sh->dev[sh->qd_idx].flags));
for (i = disks; i--; ) {
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index e10c553..9a1c2c4 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -208,6 +208,7 @@
atomic_t count; /* nr of active thread/requests */
int bm_seq; /* sequence number for bitmap flushes */
int disks; /* disks in stripe */
+ int zero_copy_count;
enum check_states check_state;
enum reconstruct_states reconstruct_state;
/**
@@ -228,10 +229,11 @@
struct r5dev {
struct bio req;
struct bio_vec vec;
- struct page *page;
+ struct page *page, *page_save;;
struct bio *toread, *read, *towrite, *written;
sector_t sector; /* sector of this page */
unsigned long flags;
+ dma_addr_t pg_dma;
} dev[1]; /* allocated with extra space depending of RAID geometry */
};
@@ -275,6 +277,14 @@
#define R5_WantFUA 14 /* Write should be FUA */
#define R5_WriteError 15 /* got a write error - need to record it */
#define R5_MadeGood 16 /* A bad block has been fixed by writing to it*/
+
+#ifdef CONFIG_RAID_ZERO_COPY
+#define R5_DirectAccess 17 /* access cached pages directly instead of
+ * sh pages */
+#define R5_DirectAccessLock 18
+#endif
+
+
/*
* Write method
*/
@@ -306,6 +316,7 @@
STRIPE_BIOFILL_RUN,
STRIPE_COMPUTE_RUN,
STRIPE_OPS_REQ_PENDING,
+ STRIPE_ZERO_COPY_OPS,
};
/*
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 283d887..b6d7e9a 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -98,7 +98,7 @@
config M25PXX_USE_FAST_READ
bool "Use FAST_READ OPCode allowing SPI CLK <= 50MHz"
depends on MTD_M25P80
- default y
+ default n
help
This option enables FAST_READ access supported by ST M25Pxx.
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 9f9982f..0506d27 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -35,6 +35,7 @@
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
+
/* Flash opcodes. */
#define OPCODE_WREN 0x06 /* Write enable */
#define OPCODE_RDSR 0x05 /* Read status register */
@@ -100,6 +101,54 @@
return container_of(mtd, struct m25p, mtd);
}
+
+int m25p80_write_then_read(struct spi_device *spi,
+ const void *txbuf, unsigned n_tx,
+ void *rxbuf, unsigned n_rx)
+{
+ static DEFINE_MUTEX(m25p80_lock);
+ int status;
+ struct spi_message message;
+ struct spi_transfer x[2];
+ u8 *local_buf;
+ u_char rx_data[8];
+ int len_to_tx = 0;
+ int len_tx_total = 0;
+ int len_tx_done = 0;
+
+ /* Use preallocated DMA-safe buffer. We can't avoid copying here,
+ * (as a pure convenience thing), but we can keep heap costs
+ * out of the hot path ...
+ */
+ if ((n_tx + n_rx) > 8)
+ {
+ printk(KERN_ERR "##### %s: size greater than 8 #########\n",__func__);
+ return -EINVAL;
+ }
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof x);
+
+ len_to_tx = n_tx + n_rx;
+
+ x[0].len = len_to_tx;
+ x[0].tx_buf = txbuf;
+ x[0].rx_buf = &rx_data[0];
+
+ spi_message_add_tail(&x[0], &message);
+
+ mutex_lock(&m25p80_lock);
+
+ /* do the i/o */
+ status = spi_sync(spi, &message);
+ if (status == 0)
+ memcpy(rxbuf, rx_data + n_tx, n_rx);
+
+ mutex_unlock(&m25p80_lock);
+
+ return status;
+}
+
/****************************************************************************/
/*
@@ -117,7 +166,7 @@
u8 code = OPCODE_RDSR;
u8 val;
- retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);
+ retval = m25p80_write_then_read(flash->spi, &code, 1, &val, 1);
if (retval < 0) {
dev_err(&flash->spi->dev, "error %d reading SR\n",
@@ -148,7 +197,7 @@
{
u8 code = OPCODE_WREN;
- return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
+ return m25p80_write_then_read(flash->spi, &code, 1, NULL, 0);
}
/*
@@ -158,7 +207,7 @@
{
u8 code = OPCODE_WRDI;
- return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
+ return m25p80_write_then_read(flash->spi, &code, 1, NULL, 0);
}
/*
@@ -249,8 +298,8 @@
*/
static int erase_sector(struct m25p *flash, u32 offset)
{
- pr_debug("%s: %s %dKiB at 0x%08x\n", dev_name(&flash->spi->dev),
- __func__, flash->mtd.erasesize / 1024, offset);
+ pr_debug("%s: %s %dKiB at 0x%08x cmdsz %d\n", dev_name(&flash->spi->dev),
+ __func__, flash->mtd.erasesize / 1024, offset, m25p_cmdsz(flash));
/* Wait until finished previous write command. */
if (wait_till_ready(flash))
@@ -339,6 +388,7 @@
* Read an address range from the flash chip. The address range
* may be any size provided it is within the physical boundaries.
*/
+#if 0
static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
@@ -371,6 +421,8 @@
t[1].len = len;
spi_message_add_tail(&t[1], &m);
+ pr_debug("%s: t[0].len 0x%x t[1].len 0x%x\n",__func__, t[0].len, t[1].len);
+
/* Byte count starts at zero. */
*retlen = 0;
@@ -400,14 +452,95 @@
return 0;
}
+#endif
+
+static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct m25p *flash = mtd_to_m25p(mtd);
+ struct spi_transfer t[2];
+ struct spi_message m;
+ int len_to_tx = 0;
+ int len_tx_total = 0;
+ int len_tx_done = 0;
+ int i;
+ u_char rx_data[8];
+ int cmd_size = m25p_cmdsz(flash);
+ int max_data_len = 8 - m25p_cmdsz(flash);
+
+ pr_debug("%s: %s from 0x%08x,len %zd\n", dev_name(&flash->spi->dev),
+ __func__, (u32)from, len);
+
+ /* sanity checks */
+ if (!len)
+ return 0;
+
+ if (from + len > flash->mtd.size)
+ return -EINVAL;
+
+ while(len_tx_done < len)
+ {
+
+ spi_message_init(&m);
+ memset(t, 0, (sizeof t));
+
+ if((len - len_tx_done) < max_data_len)
+ len_to_tx = len - len_tx_done;
+ else
+ len_to_tx = max_data_len;
+ /* NOTE:
+ * OPCODE_FAST_READ (if available) is faster.
+ * Should add 1 byte DUMMY_BYTE.
+ */
+ t[0].tx_buf = flash->command;
+ t[0].rx_buf = &rx_data[0] ;
+ t[0].len = cmd_size + len_to_tx;
+ spi_message_add_tail(&t[0], &m);
+
+
+ /* Byte count starts at zero. */
+ *retlen = 0;
+
+ mutex_lock(&flash->lock);
+
+ /* Wait till previous write/erase is done. */
+ if (wait_till_ready(flash)) {
+ /* REVISIT status return?? */
+ mutex_unlock(&flash->lock);
+ return 1;
+ }
+
+ /* FIXME switch to OPCODE_FAST_READ. It's required for higher
+ * clocks; and at this writing, every chip this driver handles
+ * supports that opcode.
+ */
+
+ /* Set up the write data buffer. */
+ flash->command[0] = OPCODE_READ;
+ m25p_addr2cmd(flash, from + len_tx_done, flash->command);
+
+ spi_sync(flash->spi, &m);
+
+ memcpy(buf + len_tx_done, &rx_data[cmd_size], len_to_tx);
+
+ len_tx_done += len_to_tx;//valid data which will be read out of 8 bytes
+
+ mutex_unlock(&flash->lock);
+ }
+
+ *retlen = len_tx_done;
+
+ return 0;
+}
/*
* Write an address range to the flash chip. Data must be written in
* FLASH_PAGESIZE chunks. The address range may be any size provided
* it is within the physical boundaries.
*/
+#if 0
static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
+ size_t *retlen, const u_char *buf)
{
struct m25p *flash = mtd_to_m25p(mtd);
u32 page_offset, page_size;
@@ -496,6 +629,78 @@
return 0;
}
+#endif
+
+static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct m25p *flash = mtd_to_m25p(mtd);
+ u32 page_offset, page_size;
+ struct spi_transfer t[2];
+ struct spi_message m;
+ u_char tx_buf[8];
+ int len_to_tx = 0;
+ int len_tx_total = 0;
+ int len_tx_done = 0;
+ int cmd_size = m25p_cmdsz(flash);
+ int max_data_len = 8 - m25p_cmdsz(flash);
+
+ pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
+ __func__, (u32)to, len);
+
+ *retlen = 0;
+
+ /* sanity checks */
+ if (!len)
+ return(0);
+
+ if (to + len > flash->mtd.size)
+ return -EINVAL;
+
+ mutex_lock(&flash->lock);
+
+ while(len_tx_done < len)
+ {
+
+ spi_message_init(&m);
+ memset(t, 0, (sizeof t));
+
+ if((len - len_tx_done) < max_data_len)
+ len_to_tx = len - len_tx_done;
+ else
+ len_to_tx = max_data_len;
+
+ /* Set up the opcode in the write buffer. */
+ flash->command[0] = OPCODE_PP;
+ m25p_addr2cmd(flash, to + len_tx_done, flash->command);
+ memcpy(&tx_buf[0], flash->command, cmd_size);
+ memcpy(&tx_buf[cmd_size], buf + len_tx_done, len_to_tx);
+
+ t[0].tx_buf = &tx_buf[0];
+ t[0].len = cmd_size + len_to_tx;
+
+ spi_message_add_tail(&t[0], &m);
+
+ /* Wait until finished previous write command. */
+ if (wait_till_ready(flash)) {
+ mutex_unlock(&flash->lock);
+ return 1;
+ }
+
+ write_enable(flash);
+
+ spi_sync(flash->spi, &m);
+
+ len_tx_done += len_to_tx;//valid data which sent out of 8 bytes
+ }
+
+ *retlen = len_tx_done;
+
+ mutex_unlock(&flash->lock);
+
+ return 0;
+}
+
static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
@@ -783,7 +988,7 @@
* string for after vendor-specific data, after the three bytes
* we use here. Supporting some chips might require using it.
*/
- tmp = spi_write_then_read(spi, &code, 1, id, 5);
+ tmp = m25p80_write_then_read(spi, &code, 1, id, 5);
if (tmp < 0) {
pr_debug("%s: error %d reading JEDEC ID\n",
dev_name(&spi->dev), tmp);
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 45dcb8b..cebc106 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -58,3 +58,4 @@
obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o
obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o
obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o
+obj-$(CONFIG_MTD_COMCERTO_NOR) += comcerto-nor.o
diff --git a/drivers/mtd/maps/comcerto-nor.c b/drivers/mtd/maps/comcerto-nor.c
new file mode 100644
index 0000000..fd097fd
--- /dev/null
+++ b/drivers/mtd/maps/comcerto-nor.c
@@ -0,0 +1,155 @@
+/*
+ * linux/drivers/mtd/maps/comcerto-nor.c
+ *
+ * Copyright (C) 2004,2008 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/mach/flash.h>
+
+static const char *part_probes[] = { "cmdlinepart", NULL };
+
+struct comcertoflash_info {
+ struct mtd_info *mtd;
+ struct map_info map;
+ struct resource *res;
+};
+
+static int __devinit comcertoflash_probe(struct platform_device *pdev)
+{
+ int err;
+ struct comcertoflash_info *info;
+ struct flash_platform_data *pdata = pdev->dev.platform_data;
+ struct resource *res = pdev->resource;
+ unsigned long size = res->end - res->start + 1;
+
+
+ info = kmalloc(sizeof(struct comcertoflash_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ memset(info, 0, sizeof(struct comcertoflash_info));
+
+ if (!request_mem_region(res->start, size, "flash")) {
+ err = -EBUSY;
+ goto out_free_info;
+ }
+
+ info->map.virt = ioremap(res->start, size);
+ if (!info->map.virt) {
+ err = -ENOMEM;
+ goto out_release_mem_region;
+ }
+
+ info->map.name = dev_name(&pdev->dev);
+ info->map.phys = res->start;
+ info->map.size = size;
+ info->map.bankwidth = pdata->width;
+ // info->map.set_vpp = comcerto_set_vpp;
+
+ simple_map_init(&info->map);
+
+ info->mtd = do_map_probe(pdata->map_name, &info->map);
+ if (!info->mtd) {
+ err = -EIO;
+ goto out_iounmap;
+ }
+ info->mtd->owner = THIS_MODULE;
+
+ platform_set_drvdata(pdev, info);
+
+ err = mtd_device_parse_register(info->mtd, part_probes, pdev->resource->start,
+ pdata->parts, pdata->nr_parts);
+ if (err) {
+ printk(KERN_ERR "Could not parse partitions\n");
+
+ }
+
+ return 0;
+
+ out_iounmap:
+ iounmap(info->map.virt);
+ out_release_mem_region:
+ release_mem_region(res->start, size);
+ out_free_info:
+ kfree(info);
+
+ return err;
+}
+
+static int __devexit comcertoflash_remove(struct platform_device *pdev)
+{
+ struct comcertoflash_info *info = platform_get_drvdata(pdev);
+ struct flash_platform_data *plat = pdev->dev.platform_data;
+
+ platform_set_drvdata(pdev, NULL);
+
+ if(!info)
+ return 0;
+
+ if (info->mtd) {
+ mtd_device_unregister(info->mtd);
+ map_destroy(info->mtd);
+ }
+ if (info->map.virt)
+ iounmap(info->map.virt);
+
+ if (info->res) {
+ release_resource(info->res);
+ kfree(info->res);
+ }
+
+ if (plat->exit)
+ plat->exit();
+
+ return 0;
+}
+
+static struct platform_driver comcertoflash_driver = {
+ .probe = comcertoflash_probe,
+ .remove = __devexit_p(comcertoflash_remove),
+ .driver = {
+ .name = "comcertoflash",
+ },
+};
+
+static int __init comcertoflash_init(void)
+{
+ return platform_driver_register(&comcertoflash_driver);
+}
+
+static void __exit comcertoflash_exit(void)
+{
+ platform_driver_unregister(&comcertoflash_driver);
+}
+
+module_init(comcertoflash_init);
+module_exit(comcertoflash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MTD NOR map driver for Mindspeed Comcerto boards");
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index cce7b70..e7a9446d 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -46,6 +46,18 @@
ECC codes. They are used with NAND devices requiring more than 1 bit
of error correction.
+config NAND_COMCERTO_ECC_8_HW_BCH
+ bool
+ depends on MTD_NAND_COMCERTO
+ default n
+ prompt "Comcerto ECC-8 syndrome calculation using BCH"
+
+config NAND_COMCERTO_ECC_24_HW_BCH
+ bool
+ depends on MTD_NAND_COMCERTO
+ default y
+ prompt "Comcerto ECC-24 syndrome calculation using BCH"
+
config MTD_SM_COMMON
tristate
default n
@@ -537,4 +549,10 @@
Enables support for NAND Flash chips on the ST Microelectronics
Flexible Static Memory Controller (FSMC)
+config MTD_NAND_COMCERTO
+ tristate "Comcerto NAND driver"
+ depends on MTD_NAND && (ARCH_COMCERTO)
+ help
+ Comcerto NAND Driver.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 618f4ba..0763907 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -49,5 +49,6 @@
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
+obj-$(CONFIG_MTD_NAND_COMCERTO) += comcerto_nand.o
nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/comcerto_nand.c b/drivers/mtd/nand/comcerto_nand.c
new file mode 100644
index 0000000..c23439a
--- /dev/null
+++ b/drivers/mtd/nand/comcerto_nand.c
@@ -0,0 +1,759 @@
+/*
+ * linux/drivers/mtd/nand/comcerto-nand.c
+ *
+ * Copyright (C) Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device found on the
+ * Comcerto board which utilizes the Toshiba TC58V64AFT part. This is
+ * a 128Mibit (8MiB x 8 bits) NAND flash device.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <mach/ecc.h>
+
+/*
+ * MTD structure for Comcerto board
+ */
+struct comcerto_nand_info {
+ struct mtd_partition *parts;
+ struct mtd_info *mtd;
+};
+
+static void __iomem *ecc_base_addr;
+
+/*
+ * Define partitions for flash device
+ */
+
+/* Partitions coming from command line*/
+static const char *part_probes[] = { "cmdlinepart", NULL };
+
+uint32_t COMCERTO_NAND_ALE = 0x00000200;
+uint32_t COMCERTO_NAND_CLE = 0x00000400;
+
+#ifdef CONFIG_NAND_COMCERTO_ECC_24_HW_BCH
+/*
+ * spare area layout for BCH ECC bytes calculated over 512-Bytes ECC block size
+ */
+static struct nand_ecclayout comcerto_ecc_info_512_bch = {
+ .eccbytes = 42,
+ .eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41},
+ .oobfree = {
+ {.offset = 43, .length = 13}
+ }
+};
+
+/*
+ * spare area layout for BCH ECC bytes calculated over 1024-Bytes ECC block size
+ */
+static struct nand_ecclayout comcerto_ecc_info_1024_bch = {
+ .eccbytes = 42,
+ .eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41},
+ .oobfree = {
+ {.offset = 43, .length = 13}
+ }
+};
+
+#elif CONFIG_NAND_COMCERTO_ECC_8_HW_BCH
+
+/*
+ * spare area layout for BCH ECC bytes calculated over 512-Bytes ECC block size
+ */
+static struct nand_ecclayout comcerto_ecc_info_512_bch = {
+ .eccbytes = 14,
+ .eccpos = {0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13},
+ .oobfree = {
+ {.offset = 15, .length = 1}
+ }
+};
+
+/*
+ * spare area layout for BCH ECC bytes calculated over 1024-Bytes ECC block size
+ */
+static struct nand_ecclayout comcerto_ecc_info_1024_bch = {
+ .eccbytes = 14,
+ .eccpos = {0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13},
+ .oobfree = {
+ {.offset = 15, .length = 17}
+ }
+};
+
+#else
+
+/*
+ * spare area layout for Hamming ECC bytes calculated over 512-Bytes ECC block
+ * size
+ */
+static struct nand_ecclayout comcerto_ecc_info_512_hamm = {
+ .eccbytes = 4,
+ .eccpos = {0, 1, 2, 3},
+ .oobfree = {
+ {.offset = 5, .length = 12}
+ }
+};
+
+/*
+ * spare area layout for Hamming ECC bytes calculated over 1024-Bytes ECC block
+ * size
+ */
+static struct nand_ecclayout comcerto_ecc_info_1024_hamm = {
+ .eccbytes = 4,
+ .eccpos = {0, 1, 2, 3},
+ .oobfree = {
+ {.offset = 5, .length = 28}
+ }
+};
+#endif
+
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_8BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 44,
+ .len = 4,
+ .veroffs = 48,
+ .maxblocks = 8,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_8BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 44,
+ .len = 4,
+ .veroffs = 48,
+ .maxblocks = 8,
+ .pattern = mirror_pattern,
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff };
+
+#ifdef CONFIG_NAND_COMCERTO_ECC_24_HW_BCH
+static struct nand_bbt_descr c2000_badblock_pattern = {
+ .offs = 42,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+#elif CONFIG_NAND_COMCERTO_ECC_8_HW_BCH
+static struct nand_bbt_descr c2000_badblock_pattern = {
+ .offs = 14,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+#endif
+
+/** Disable/Enable shifting of data to parity module
+ *
+ * @param[in] en_dis_shift Enable or disable shift to parity module.
+ *
+ */
+static void comcerto_ecc_shift(uint8_t en_dis_shift)
+{
+ writel_relaxed(en_dis_shift, ecc_base_addr + ECC_SHIFT_EN_CFG);
+}
+
+/** Initializes h/w ECC with proper configuration values.
+ *
+ * @param[in] mtd MTD device structure
+ * @param[in] mode Select between BCH and Hamming
+ *
+ */
+static void comcerto_enable_hw_ecc(struct mtd_info *mtd, int mode)
+{
+ struct nand_chip *nand_device = (struct nand_chip *)(mtd->priv);
+ uint32_t ecc_gen_cfg_val = 0;
+
+
+ /* CS4 will have the option for ECC calculation */
+ writel_relaxed(ECC_CS4_SEL, ecc_base_addr + ECC_CS_SEL_CFG);
+
+ /* parity calculation for write, syndrome calculation for read.*/
+ (mode == NAND_ECC_WRITE) ? (ecc_gen_cfg_val |= PRTY_CALC) : (ecc_gen_cfg_val &= SYNDROME_CALC);
+
+#if defined (CONFIG_NAND_COMCERTO_ECC_8_HW_BCH) || defined (CONFIG_NAND_COMCERTO_ECC_24_HW_BCH)
+ ecc_gen_cfg_val &= BCH_MODE;
+ ecc_gen_cfg_val = (ecc_gen_cfg_val & ~(ECC_LVL_MASK)) | (ECC_LVL_VAL << ECC_LVL_SHIFT);
+#else
+ ecc_gen_cfg_val |= HAMM_MODE;
+#endif
+
+ ecc_gen_cfg_val = (ecc_gen_cfg_val & ~(BLK_SIZE_MASK)) | nand_device->ecc.size;
+
+ writel_relaxed(ecc_gen_cfg_val, ecc_base_addr + ECC_GEN_CFG);
+ /* Reset parity module and latch configured values */
+ writel_relaxed(ECC_INIT, ecc_base_addr + ECC_INIT_CFG);
+ comcerto_ecc_shift(ECC_SHIFT_ENABLE);
+ return;
+}
+
+/** writes ECC bytes generated by the parity module into the flash
+ *
+ * @param[in] mtd MTD device structure
+ * @param[in] dat raw data
+ * @param[in] ecc_code buffer for ECC
+ *
+ */
+static int comcerto_calculate_ecc(struct mtd_info *mtd,
+ const uint8_t *dat,
+ uint8_t *ecc_code)
+{
+ struct nand_chip *nand_device = mtd->priv;
+ uint32_t ecc_bytes = nand_device->ecc.bytes;
+ uint8_t dummy_var = 0xFF;
+ unsigned long timeo = jiffies + 2;
+
+ comcerto_ecc_shift(ECC_SHIFT_DISABLE);
+
+ do {
+ if ((readl_relaxed(ecc_base_addr + ECC_IDLE_STAT)) & ECC_IDLE)
+ break;
+ touch_softlockup_watchdog();
+ } while (time_before(jiffies, timeo));
+
+ comcerto_ecc_shift(ECC_SHIFT_ENABLE);
+
+ writel_relaxed(ECC_PARITY_OUT_EN, ecc_base_addr + ECC_PRTY_OUT_SEL_CFG);
+
+ /* Even though we do a dummy write to NAND flash, actual ECC bytes are
+ * written to the ECC location in the flash. */
+ for ( ; ecc_bytes; ecc_bytes--)
+ writeb(dummy_var, nand_device->IO_ADDR_W);
+
+ comcerto_ecc_shift(ECC_SHIFT_DISABLE);
+ writel_relaxed(ECC_PARITY_OUT_DISABLE, ecc_base_addr + ECC_PRTY_OUT_SEL_CFG);
+
+ return 0;
+}
+
+/** Checks ECC registers for errors and will correct them, if correctable
+ *
+ * @param[in] mtd MTD device structure
+ * @param[in] dat raw data
+ * @param[in] read_ecc ECC read out from flash
+ * @param[in] calc_ecc ECC calculated over the raw data
+ *
+ */
+static int comcerto_correct_ecc(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+#if !defined (CONFIG_NAND_COMCERTO_ECC_8_HW_BCH) && !defined (CONFIG_NAND_COMCERTO_ECC_24_HW_BCH)
+ struct nand_chip *nand_device = mtd->priv;
+#else
+ uint8_t err_count = 0;
+ uint32_t err_corr_data_prev;
+#endif
+ uint32_t err_corr_data;
+ uint16_t mask, index;
+ uint32_t temp_nand_ecc_errors[4];
+ unsigned long timeo = jiffies + 2;
+
+ /* Wait for syndrome calculation to complete */
+ do {
+ if ((readl_relaxed(ecc_base_addr + ECC_IDLE_STAT)) & ECC_IDLE)
+ break;
+ touch_softlockup_watchdog();
+ } while (time_before(jiffies, timeo));
+
+ /* If no correction is required */
+ if (likely(!((readl_relaxed(ecc_base_addr + ECC_POLY_STAT)) & ECC_CORR_REQ))) {
+ return 0;
+ }
+
+ /* Error found! Correction required */
+#if defined (CONFIG_NAND_COMCERTO_ECC_8_HW_BCH) || defined (CONFIG_NAND_COMCERTO_ECC_24_HW_BCH)
+ /* Initiate correction operation */
+ writel_relaxed(ECC_POLY_START, ecc_base_addr + ECC_POLY_START_CFG);
+
+ udelay(25);
+
+ err_corr_data_prev = 0;
+ /* Read Correction data status register till header is 0x7FD */
+ do {
+ err_corr_data_prev = readl_relaxed(ecc_base_addr + ECC_CORR_DATA_STAT);
+ if ((err_corr_data_prev >> ECC_BCH_INDEX_SHIFT) == 0x87FD)
+ break;
+
+ touch_softlockup_watchdog();
+ } while (time_before(jiffies, timeo));
+
+ udelay(25);
+ err_corr_data = 0x0;
+ /* start reading error locations */
+ while (((err_corr_data >> 16) != 0x87FE)) {
+ err_corr_data = readl_relaxed(ecc_base_addr + ECC_CORR_DATA_STAT);
+ if ((err_corr_data >> 16) == 0x87FE)
+ break;
+ if (err_corr_data == err_corr_data_prev)
+ continue;
+ err_corr_data_prev = err_corr_data;
+ index = (err_corr_data >> 16) & 0x7FF;
+ mask = err_corr_data & 0xFFFF;
+ *((uint16_t *)(dat + (index * 2))) ^= mask;
+ while (mask) {
+ if (mask & 1)
+ err_count++;
+ mask >>= 1;
+ }
+ }
+
+ if (!((readl_relaxed(ecc_base_addr + ECC_CORR_DONE_STAT)) & ECC_DONE)) {
+ temp_nand_ecc_errors[0] += 1 ;
+ printk(KERN_WARNING "ECC: uncorrectable error 1 !!!\n");
+ return -1;
+ }
+
+ /* Check if the block has uncorrectable number of errors */
+ if ((readl_relaxed(ecc_base_addr + ECC_CORR_STAT)) & ECC_UNCORR) {
+ printk(KERN_WARNING "ECC: uncorrectable error 2 !!!\n");
+ temp_nand_ecc_errors[1] += 1 ;
+ return -EIO;
+ }
+
+ temp_nand_ecc_errors[3] += err_count;
+
+#else /* Hamming Mode */
+ if (readl_relaxed(ecc_base_addr + ECC_POLY_STAT) == ECC_UNCORR_ERR_HAMM) {
+ /* 2 or more errors detected and hence cannot
+ be corrected */
+ return -1; /* uncorrectable */
+ } else { /* 1-bit correctable error */
+ err_corr_data = readl_relaxed(ecc_base_addr + ECC_CORR_DATA_STAT);
+ index = (err_corr_data >> 16) & 0x1FF;
+
+ if (nand_device->options & NAND_BUSWIDTH_16) {
+ mask = 1 << (err_corr_data & 0xF);
+ *((uint16_t *)(dat + index)) ^= mask;
+ } else {
+ mask = 1 << (err_corr_data & 0x7);
+ *(dat + index) ^= mask;
+ }
+ return 1;
+
+ }
+#endif
+ return 0;
+}
+
+/** writes single page to the NAND device along with the ECC bytes
+ *
+ * @param[in] mtd MTD device structure
+ * @param[in] chip nand chip info structure
+ * @param[in] buf data buffer
+ *
+ */
+static void comcerto_nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ const uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+
+ /* CS4 will have the option for ECC calculation */
+ writel_relaxed(ECC_CS4_SEL, ecc_base_addr + ECC_CS_SEL_CFG);
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+ chip->write_buf(mtd, p, eccsize);
+
+ chip->ecc.calculate(mtd, p, oob);
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->write_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i)
+ chip->write_buf(mtd, oob, i);
+}
+
+/** reads single page from the NAND device and will read ECC bytes from flash. A
+ * function call to comcerto_correct_ecc() will be used to validate the data.
+ *
+ * @param[in] mtd MTD device structure
+ * @param[in] chip nand chip info structure
+ * @param[in] buf data buffer
+ *
+ */
+static int comcerto_nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int page)
+{
+ struct nand_chip *nand_device = mtd->priv;
+ int i, eccsize = nand_device->ecc.size;
+ int eccbytes = nand_device->ecc.bytes;
+ int eccsteps = nand_device->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_code = nand_device->buffers->ecccode;
+ uint8_t ecc_bytes = nand_device->ecc.bytes;
+ uint8_t stat;
+ uint8_t *oob = nand_device->oob_poi;
+
+ for (; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+ chip->read_buf(mtd, ecc_code, ecc_bytes);
+
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+
+ comcerto_ecc_shift(ECC_SHIFT_DISABLE);
+
+ if (chip->ecc.postpad) {
+ chip->read_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i)
+ chip->read_buf(mtd, oob, i);
+
+ return 0;
+}
+
+/*********************************************************************
+ * NAND Hardware functions
+ *
+ *********************************************************************/
+
+/*
+ * hardware specific access to control-lines
+*/
+void comcerto_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd->priv;
+
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if (ctrl & NAND_NCE)
+ comcerto_gpio_set_0(COMCERTO_NAND_CE);
+ else
+ comcerto_gpio_set_1(COMCERTO_NAND_CE);
+ }
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ writeb(cmd, chip->IO_ADDR_W + COMCERTO_NAND_CLE);
+ else if (ctrl & NAND_ALE)
+ writeb(cmd, chip->IO_ADDR_W + COMCERTO_NAND_ALE);
+ else
+ return;
+
+}
+
+int comcerto_nand_ready(struct mtd_info *mtd)
+{
+ return comcerto_gpio_read(COMCERTO_NAND_BR) ? 1 : 0;
+}
+
+/*********************************************************************
+ * NAND Probe
+ *
+ *********************************************************************/
+static int comcerto_nand_probe(struct platform_device *pdev)
+{
+ struct comcerto_nand_info *info;
+ struct mtd_info *mtd;
+ struct nand_chip *nand_device;
+ int err = 0;
+
+ /* Allocate memory for info structure */
+ info = kmalloc(sizeof(struct comcerto_nand_info), GFP_KERNEL);
+ if (!info) {
+ printk(KERN_ERR
+ "comcerto nand: unable to allocate info structure\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ memset(info, 0, sizeof(struct comcerto_nand_info));
+
+ /* Allocate memory for MTD device structure */
+ mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
+ if (!mtd) {
+ printk(KERN_ERR
+ "comcerto nand: unable to allocate mtd info structure\n");
+ err = -ENOMEM;
+ goto out_info;
+ }
+ memset(mtd, 0, sizeof(struct mtd_info));
+
+ /* Link the private data with the MTD structure */
+ info->mtd = mtd;
+ mtd->owner = THIS_MODULE;
+
+ /* Allocate pointer to nand_device data */
+ nand_device = kmalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!nand_device) {
+ printk(KERN_ERR
+ "comcerto nand: unable to allocate nand chip structure\n");
+ err = -ENOMEM;
+ goto out_mtd;
+ }
+ memset(nand_device, 0, sizeof(struct nand_chip));
+
+ /* Link the private data with the MTD structure */
+ mtd->priv = nand_device;
+
+ printk(KERN_INFO "pdev->resource->start = %x, pdev->resource->end = %x\n", pdev->resource->start, pdev->resource->end);
+
+ /*Map physical address of nand into virtual space */
+ nand_device->IO_ADDR_R = ioremap_nocache(pdev->resource->start, pdev->resource->end - pdev->resource->start + 1);
+ if (nand_device->IO_ADDR_R == NULL) {
+ printk(KERN_ERR "comcerto nand: cannot map nand memory\n");
+ err = -EIO;
+ goto out_ior;
+ }
+
+ ecc_base_addr = ioremap(COMCERTO_AXI_EXP_ECC_BASE, 0xFFFF);
+ if (!ecc_base_addr) {
+ printk(KERN_ERR "comcerto nand: cannot map ecc config\n");
+ err = -EIO;
+ goto out_ior;
+ }
+
+ /* This is the same address to read and write */
+ nand_device->IO_ADDR_W = nand_device->IO_ADDR_R;
+
+ printk(KERN_INFO "nand_probe: %s base: 0x%08x \n", pdev->name, (resource_size_t) nand_device->IO_ADDR_R);
+
+ /* Set address of hardware control function */
+ nand_device->cmd_ctrl = comcerto_nand_hwcontrol;
+ nand_device->dev_ready = comcerto_nand_ready;
+
+ /* 20 us command delay time */
+ nand_device->chip_delay = 20;
+
+ nand_device->ecc.mode = NAND_ECC_HW_SYNDROME;
+// nand_device->ecc.mode = NAND_ECC_SOFT_BCH;
+
+#if defined(CONFIG_C2K_ASIC) && defined(CONFIG_NAND_TYPE_SLC)
+ nand_device->options = NAND_BUSWIDTH_16;
+#else
+ nand_device->options = 0;
+#endif
+
+ /* Scan to find existence of the device */
+ if (nand_scan_ident(mtd, 1, NULL)) {
+ err = -ENXIO;
+ goto out_nand;
+ }
+
+#if 1
+ if (nand_device->ecc.mode == NAND_ECC_HW_SYNDROME) {
+ nand_device->ecc.hwctl = comcerto_enable_hw_ecc;
+ nand_device->ecc.write_page = comcerto_nand_write_page_hwecc;
+ nand_device->ecc.read_page = comcerto_nand_read_page_hwecc;
+ nand_device->ecc.calculate = comcerto_calculate_ecc;
+ nand_device->ecc.correct = comcerto_correct_ecc;
+
+ switch (mtd->writesize) {
+ case 512:
+ nand_device->ecc.size = mtd->writesize;
+#if defined (CONFIG_NAND_COMCERTO_ECC_24_HW_BCH)
+ nand_device->ecc.layout = &comcerto_ecc_info_512_bch;
+ nand_device->ecc.bytes = 42;
+ nand_device->ecc.prepad = 0;
+ nand_device->ecc.postpad = 14;
+#elif defined (CONFIG_NAND_COMCERTO_ECC_8_HW_BCH)
+ nand_device->ecc.layout = &comcerto_ecc_info_512_bch;
+ nand_device->ecc.bytes = 14;
+ nand_device->ecc.prepad = 0;
+ nand_device->ecc.postpad = 2;
+#else
+ nand_device->ecc.layout = &comcerto_ecc_info_512_hamm;
+ nand_device->ecc.bytes = 4;
+ nand_device->ecc.prepad = 0;
+ nand_device->ecc.postpad = 2;
+#endif
+ break;
+ case 1024:
+ nand_device->ecc.size = mtd->writesize;
+#ifdef CONFIG_NAND_COMCERTO_ECC_24_HW_BCH
+ nand_device->ecc.layout = &comcerto_ecc_info_1024_bch;
+ nand_device->ecc.bytes = 42;
+ nand_device->ecc.prepad = 0;
+ nand_device->ecc.postpad = 14;
+#elif CONFIG_NAND_COMCERTO_ECC_8_HW_BCH
+ nand_device->ecc.layout = &comcerto_ecc_info_1024_bch;
+ nand_device->ecc.bytes = 14;
+ nand_device->ecc.prepad = 0;
+ nand_device->ecc.postpad = 18;
+#else
+ nand_device->ecc.layout = &comcerto_ecc_info_1024_hamm;
+ nand_device->ecc.bytes = 4;
+ nand_device->ecc.prepad = 0;
+ nand_device->ecc.postpad = 18;
+#endif
+ break;
+ default:
+ printk(KERN_ERR "Using default values for hw ecc\n");
+ nand_device->ecc.size = 1024;
+#ifdef CONFIG_NAND_COMCERTO_ECC_24_HW_BCH
+ nand_device->ecc.layout = &comcerto_ecc_info_1024_bch;
+ nand_device->ecc.bytes = 42;
+ nand_device->ecc.prepad = 0;
+ nand_device->ecc.postpad = 14;
+#elif CONFIG_NAND_COMCERTO_ECC_8_HW_BCH
+ nand_device->ecc.layout = &comcerto_ecc_info_1024_bch;
+ nand_device->ecc.bytes = 14;
+ nand_device->ecc.prepad = 0;
+ nand_device->ecc.postpad = 18;
+#else
+ nand_device->ecc.layout = &comcerto_ecc_info_1024_hamm;
+ nand_device->ecc.bytes = 4;
+ nand_device->ecc.prepad = 0;
+ nand_device->ecc.postpad = 18;
+#endif
+ break;
+ }
+ nand_device->ecc.steps = mtd->writesize / nand_device->ecc.size;
+ if(nand_device->ecc.steps * nand_device->ecc.size != mtd->writesize) {
+ printk(KERN_ERR "Invalid ecc parameters\n");
+ BUG();
+ }
+ nand_device->ecc.total = nand_device->ecc.steps * nand_device->ecc.bytes;
+
+
+ nand_device->bbt_td = &bbt_main_descr;
+ nand_device->bbt_md = &bbt_mirror_descr;
+ nand_device->badblock_pattern = &c2000_badblock_pattern;
+ nand_device->bbt_options |= NAND_BBT_USE_FLASH;
+
+ } else {
+ nand_device->ecc.mode = NAND_ECC_SOFT;
+ }
+
+#endif
+
+ nand_device->options |= NAND_NO_SUBPAGE_WRITE;
+
+ if(nand_scan_tail(mtd)) {
+ printk(KERN_ERR "nand_scan_tail returned error\n");
+ err = -ENXIO;
+ goto out_ior;
+ }
+
+ /*Name of the mtd device */
+ mtd->name = dev_name(&pdev->dev);
+
+ /* Link the info stucture with platform_device */
+ platform_set_drvdata(pdev, info);
+
+ err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 4);
+
+ if (err) {
+ printk(KERN_ERR "Could not parse partitions\n");
+ return err;
+ }
+
+
+ goto out;
+
+ out_ior:
+ iounmap(nand_device->IO_ADDR_R);
+ iounmap(ecc_base_addr);
+ out_nand:
+ kfree(nand_device);
+ out_mtd:
+ kfree(mtd);
+ out_info:
+ kfree(info);
+ out:
+ return err;
+}
+
+/*********************************************************************
+ * NAND Remove
+ *
+ *********************************************************************/
+static int comcerto_nand_remove(struct platform_device *pdev)
+{
+ struct comcerto_nand_info *info =
+ (struct comcerto_nand_info *)platform_get_drvdata(pdev);
+ struct mtd_info *mtd = info->mtd;
+ struct nand_chip *nand_device = (struct nand_chip *)mtd->priv;
+
+ platform_set_drvdata(pdev, NULL);
+
+ /* Release resources, unregister device */
+ nand_release(info->mtd);
+
+ /*Deregister virtual address */
+ iounmap(nand_device->IO_ADDR_R);
+ iounmap(ecc_base_addr);
+
+ kfree(nand_device);
+
+ /* Free the MTD device structure */
+ kfree(mtd);
+
+ kfree(info);
+
+ return 0;
+}
+
+/*********************************************************************
+ * Driver Registration
+ *
+ *********************************************************************/
+
+static struct platform_driver comcerto_nand_driver = {
+ .probe = comcerto_nand_probe,
+ .remove = __devexit_p(comcerto_nand_remove),
+ .driver = {
+ .name = "comcertonand",
+ },
+};
+
+int __init comcerto_nand_init(void)
+{
+ return platform_driver_register(&comcerto_nand_driver);
+}
+
+static void __exit comcerto_nand_exit(void)
+{
+ platform_driver_unregister(&comcerto_nand_driver);
+}
+
+module_init(comcerto_nand_init);
+module_exit(comcerto_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Comcerto board");
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 3ed9c5e..ed4d76d 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3262,6 +3262,9 @@
case 128:
chip->ecc.layout = &nand_oob_128;
break;
+ case 224:
+ chip->ecc.layout = &nand_oob_128;
+ break;
default:
pr_warn("No oob scheme defined for oobsize %d\n",
mtd->oobsize);
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index a702443..cd082ad 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -19,6 +19,11 @@
tristate "Drivers for Marvell PHYs"
---help---
Currently has a driver for the 88E1011S
+
+config ATHEROS_PHY
+ tristate "Drivers for Atheros PHYs"
+ ---help---
+ Currently supports AR8035 and AR8327
config DAVICOM_PHY
tristate "Drivers for Davicom PHYs"
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 2333215..a00faff 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_PHYLIB) += libphy.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o
+obj-$(CONFIG_ATHEROS_PHY) += atheros.o
obj-$(CONFIG_DAVICOM_PHY) += davicom.o
obj-$(CONFIG_CICADA_PHY) += cicada.o
obj-$(CONFIG_LXT_PHY) += lxt.o
diff --git a/drivers/net/phy/atheros.c b/drivers/net/phy/atheros.c
new file mode 100644
index 0000000..ef238ca
--- /dev/null
+++ b/drivers/net/phy/atheros.c
@@ -0,0 +1,185 @@
+/*
+ * Driver for Atheros PHYs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+/* Atheros PHY registers*/
+#define AR8x_INTR_EN 0x12
+#define AR8x_INTR_STS 0x13
+#define AR8x_DEBUG_PORT_ADDR 0x1D
+#define AR8x_DEBUG_PORT_DATA 0x1E
+
+/* Interrupt mask */
+#define AR8x_INTR_MASK 0xfc03
+
+/* Atheros PHY registers on debug port */
+/* RGMII Rx clock control */
+#define AR8x_DBG_RGMII_RXCLK_CTRL 0x00
+#define AR8x_DBG_RGMII_RXCLK_MASK 0x8000
+
+/*RGMII Tx clock control */
+#define AR8x_DBG_RGMII_TXCLK_CTRL 0x05
+#define AR8x_DBG_RGMII_TXCLK_MASK 0x0100
+
+#define PHY_ID_AR8035 0x004dd072
+#define PHY_ID_AR8033 0x004dd074
+#define PHY_ID_AR8327 0x004dd033
+
+MODULE_DESCRIPTION("Atheros PHY driver");
+MODULE_LICENSE("GPL");
+
+static int ar8x_phy_dbg_read(struct phy_device *phydev, int reg_addr)
+{
+ phy_write(phydev, AR8x_DEBUG_PORT_ADDR, reg_addr);
+ return phy_read(phydev, AR8x_DEBUG_PORT_DATA);
+}
+
+static void ar8x_phy_dbg_write(struct phy_device *phydev, int reg_addr, u32 val)
+{
+ phy_write(phydev, AR8x_DEBUG_PORT_ADDR, reg_addr);
+ phy_write(phydev, AR8x_DEBUG_PORT_DATA, val);
+}
+
+int ar8x_add_skew(struct phy_device *phydev)
+{
+ int tmp, err;
+
+ /* Enable Rx delay */
+ tmp = ar8x_phy_dbg_read(phydev, AR8x_DBG_RGMII_RXCLK_CTRL);
+ tmp |= AR8x_DBG_RGMII_RXCLK_MASK;
+ ar8x_phy_dbg_write(phydev, AR8x_DBG_RGMII_RXCLK_CTRL, tmp);
+ /* Enable Tx delay */
+ tmp = ar8x_phy_dbg_read(phydev, AR8x_DBG_RGMII_TXCLK_CTRL);
+ tmp |= AR8x_DBG_RGMII_TXCLK_MASK;
+ ar8x_phy_dbg_write(phydev, AR8x_DBG_RGMII_TXCLK_CTRL, tmp);
+
+ err = genphy_restart_aneg(phydev);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+EXPORT_SYMBOL(ar8x_add_skew);
+
+static int ar8x_config_init(struct phy_device *phydev)
+{
+ int err = 0;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
+ err = ar8x_add_skew(phydev);
+
+ return err;
+
+}
+
+
+static int ar8x_ack_interrupt(struct phy_device *phydev)
+{
+ int err = 0;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_read(phydev, AR8x_INTR_STS);
+
+ return (err < 0) ? err : 0;
+}
+
+static int ar8x_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, AR8x_INTR_EN, AR8x_INTR_MASK);
+ else {
+ /* Clear the interrupts */
+ err = phy_read(phydev, AR8x_INTR_STS);
+
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, AR8x_INTR_EN, 0);
+ }
+
+ return err;
+}
+
+/* Atheros Ar8035 */
+static struct phy_driver ar8035_driver = {
+ .phy_id = PHY_ID_AR8035,
+ .name = "Atheros AR8035",
+ .phy_id_mask = 0xffffff00,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = &ar8x_config_init,
+ .config_aneg = &genphy_config_aneg,
+ .read_status = &genphy_read_status,
+ .ack_interrupt = &ar8x_ack_interrupt,
+ .config_intr = &ar8x_config_intr,
+#ifdef CONFIG_PM
+ .suspend = &genphy_suspend,
+ .resume = &genphy_resume,
+#endif
+ .driver = { .owner = THIS_MODULE,},
+};
+
+/* Atheros Ar8327 */
+static struct phy_driver ar8327_driver = {
+ .phy_id = PHY_ID_AR8327,
+ .name = "Atheros AR8327",
+ .phy_id_mask = 0xffffff00,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = &ar8x_config_init,
+ .config_aneg = &genphy_config_aneg,
+ .read_status = &genphy_read_status,
+ .ack_interrupt = &ar8x_ack_interrupt,
+ .config_intr = &ar8x_config_intr,
+#ifdef CONFIG_PM
+ .suspend = &genphy_suspend,
+ .resume = &genphy_resume,
+#endif
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init ar8x_init(void)
+{
+ int err;
+
+ err = phy_driver_register(&ar8035_driver);
+ if (err < 0)
+ return err;
+
+ err = phy_driver_register(&ar8327_driver);
+ if (err < 0)
+ phy_driver_unregister(&ar8035_driver);
+
+ return err;
+}
+
+static void __exit ar8x_exit(void)
+{
+ phy_driver_unregister(&ar8035_driver);
+ phy_driver_unregister(&ar8327_driver);
+}
+
+module_init(ar8x_init);
+module_exit(ar8x_exit);
+
+static struct mdio_device_id __maybe_unused atheros_tbl[] = {
+ { PHY_ID_AR8035, 0xffffff00 },
+ { PHY_ID_AR8327, 0xffffff00 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(mdio, atheros_tbl);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 83a5a5a..0a728ad 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -35,6 +35,11 @@
#include <asm/irq.h>
#include <asm/uaccess.h>
+#ifdef CONFIG_ARCH_M86XXX
+#include <mach/comcerto-2000/pm.h>
+#endif
+
+
MODULE_DESCRIPTION("PHY library");
MODULE_AUTHOR("Andy Fleming");
MODULE_LICENSE("GPL");
@@ -885,7 +890,17 @@
int genphy_suspend(struct phy_device *phydev)
{
int value;
-
+#ifdef CONFIG_ARCH_M86XXX
+ /* Check for the Bit_Mask bit for WoL, if it is enabled
+ * then we are not going suspend the WoL device , as by
+ * this device , we will wake from System Resume.
+ */
+ if ( !(host_utilpe_shared_pmu_bitmask & WOL_IRQ )){
+ /* We will just return
+ */
+ return 0;
+ }
+#endif
mutex_lock(&phydev->lock);
value = phy_read(phydev, MII_BMCR);
@@ -900,7 +915,17 @@
int genphy_resume(struct phy_device *phydev)
{
int value;
-
+#ifdef CONFIG_ARCH_M86XXX
+ /* Check for the Bit_Mask bit for WoL, if it is enabled
+ * then we are not going suspend the WoL device , as by
+ * this device , we will wake from System Resume.
+ */
+ if ( !(host_utilpe_shared_pmu_bitmask & WOL_IRQ )){
+ /* We will just return
+ */
+ return 0;
+ }
+#endif
mutex_lock(&phydev->lock);
value = phy_read(phydev, MII_BMCR);
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 3ed983c..f035281 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -53,6 +53,9 @@
#include <linux/nsproxy.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
+#if defined(CONFIG_COMCERTO_FP)
+#include <linux/jiffies.h>
+#endif
#define PPP_VERSION "2.4.2"
@@ -559,6 +562,9 @@
struct ppp *ppp;
int err = -EFAULT, val, val2, i;
struct ppp_idle idle;
+#if defined(CONFIG_COMCERTO_FP)
+ struct ppp_idle fppidle;
+#endif
struct npioctl npi;
int unit, cflags;
struct slcompress *vj;
@@ -738,6 +744,30 @@
err = 0;
break;
+#if defined(CONFIG_COMCERTO_FP)
+ case PPPIOCSFPPIDLE:
+ if (copy_from_user(&fppidle, argp, sizeof(fppidle)))
+ break;
+
+ ppp_xmit_lock(ppp);
+
+ if (time_after((jiffies - (fppidle.xmit_idle * HZ)) , ppp->last_xmit))
+ ppp->last_xmit = (jiffies - fppidle.xmit_idle * HZ);
+
+ ppp_xmit_unlock(ppp);
+
+ ppp_recv_lock(ppp);
+
+ if (time_after((jiffies - (fppidle.recv_idle * HZ)) , ppp->last_recv))
+ ppp->last_recv = (jiffies - fppidle.recv_idle * HZ);
+
+ ppp_recv_unlock(ppp);
+
+ err = 0;
+ break;
+#endif
+
+
#ifdef CONFIG_PPP_FILTER
case PPPIOCSPASS:
{
@@ -2825,8 +2855,10 @@
goto out;
write_lock_bh(&pch->upl);
ret = -EINVAL;
- if (pch->ppp)
- goto outl;
+ if (pch->ppp) {
+ write_unlock_bh(&pch->upl);
+ goto out;
+ }
ppp_lock(ppp);
if (pch->file.hdrlen > ppp->file.hdrlen)
@@ -2839,10 +2871,21 @@
pch->ppp = ppp;
atomic_inc(&ppp->file.refcnt);
ppp_unlock(ppp);
- ret = 0;
- outl:
write_unlock_bh(&pch->upl);
+
+/*We need to revisit below changes when PPPoE automode is introduced to C2K*/
+#if 0
+#if defined(CONFIG_COMCERTO_FP)
+ if ((ppp->dev) && (!ppp->closing)) {
+ rtnl_lock();
+ rtmsg_ifinfo(RTM_NEWLINK, ppp->dev, 0);
+ rtnl_unlock();
+ }
+#endif
+#endif
+
+ ret = 0;
out:
mutex_unlock(&pn->all_ppp_mutex);
return ret;
@@ -2868,6 +2911,18 @@
if (--ppp->n_channels == 0)
wake_up_interruptible(&ppp->file.rwait);
ppp_unlock(ppp);
+
+/*We need to revisit below changes when PPPoE automode is introduced to C2K*/
+#if 0
+#if defined(CONFIG_COMCERTO_FP)
+ if ((ppp->dev) && (!ppp->closing)) {
+ rtnl_lock();
+ rtmsg_ifinfo(RTM_NEWLINK, ppp->dev, 0);
+ rtnl_unlock();
+ }
+#endif
+#endif
+
if (atomic_dec_and_test(&ppp->file.refcnt))
ppp_destroy_interface(ppp);
err = 0;
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 49c6da9..53cc103 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -940,8 +940,16 @@
skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
skb->dev = dev;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if((skb->ipsec_offload == 1) && (!skb->sp))
+ {
+ dev_hard_header(skb, dev, ETH_P_PPP_SES,
+ dev->dev_addr, po->pppoe_pa.remote, data_len);
- dev_hard_header(skb, dev, ETH_P_PPP_SES,
+ }
+ else
+#endif
+ dev_hard_header(skb, dev, ETH_P_PPP_SES,
po->pppoe_pa.remote, NULL, data_len);
dev_queue_xmit(skb);
@@ -1004,17 +1012,33 @@
{
struct pppox_sock *po;
char *dev_name;
+#if defined(CONFIG_COMCERTO_FP)
+ char *ppp_name;
+#endif
if (v == SEQ_START_TOKEN) {
- seq_puts(seq, "Id Address Device\n");
+#if defined(CONFIG_COMCERTO_FP)
+ seq_puts(seq, "Id Address Device PPPDevice\n");
+#else
+ seq_puts(seq, "Id Address Device\n");
+#endif
goto out;
}
po = v;
dev_name = po->pppoe_pa.dev;
- seq_printf(seq, "%08X %pM %8s\n",
- po->pppoe_pa.sid, po->pppoe_pa.remote, dev_name);
+#if defined(CONFIG_COMCERTO_FP)
+ ppp_name = ppp_dev_name(&po->chan);
+ if (!ppp_name)
+ goto out;
+
+ seq_printf(seq, "%04X %pM %8s %s\n",
+ ntohs(po->pppoe_pa.sid), po->pppoe_pa.remote, dev_name, ppp_name);
+#else
+ seq_printf(seq, "%08X %pM %8s\n",
+ po->pppoe_pa.sid, po->pppoe_pa.remote, dev_name);
+#endif
out:
return 0;
}
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index b873b5d..2864780 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -49,6 +49,14 @@
#define DRIVER_VERSION "22-Aug-2005"
+#ifdef CONFIG_ARCH_M86XXX
+/*
+ * We alllocate extra 64 Bytes to reserve headroom in the sk_buff
+ * To be used by Fast Path (Head Room must be 4 Byte aligned
+ * because USB 2.0 controller doesn't support 2 byte alignment
+ */
+#define C2K_USBNET_SKB_HEADROOM_FAST_PATH 64
+#endif
/*-------------------------------------------------------------------------*/
@@ -344,13 +352,26 @@
unsigned long lockflags;
size_t size = dev->rx_urb_size;
+#ifdef CONFIG_ARCH_M86XXX
+ /*
+ * We alllocate extra 64 Bytes to reserve headroom in the sk_buff
+ * To be used by Fast Path (Head Room must be 4 Byte aligned
+ * because USB 2.0 controller doesn't support 2 byte alignment
+ */
+ if ((skb = alloc_skb (size + C2K_USBNET_SKB_HEADROOM_FAST_PATH, flags)) == NULL) {
+#else
if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
+#endif
netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
usb_free_urb (urb);
return -ENOMEM;
}
+#ifdef CONFIG_ARCH_M86XXX
+ skb_reserve (skb, C2K_USBNET_SKB_HEADROOM_FAST_PATH);
+#else
skb_reserve (skb, NET_IP_ALIGN);
+#endif
entry = (struct skb_data *) skb->cb;
entry->urb = urb;
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 53eb4e5..c76b916 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -762,6 +762,13 @@
This driver can also be build as a module. If so, the module
will be called rtc-s3c.
+config RTC_DRV_C2K
+ tristate "Mindspeed C2000 SoC RTC"
+ depends on ARCH_M86XXX
+ default y
+ help
+ Comcerto C2000 RTC (Realtime Clock) driver.
+
config RTC_DRV_EP93XX
tristate "Cirrus Logic EP93XX"
depends on ARCH_EP93XX
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e69823..fae32d3 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -91,6 +91,7 @@
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
+obj-$(CONFIG_RTC_DRV_C2K) += rtc-c2k.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
diff --git a/drivers/rtc/rtc-c2k.c b/drivers/rtc/rtc-c2k.c
new file mode 100644
index 0000000..2af8618
--- /dev/null
+++ b/drivers/rtc/rtc-c2k.c
@@ -0,0 +1,530 @@
+/*
+ * drivers/rtc/rtc-c2k.c
+ *
+ * Copyright (c) 2010 Mindspeed Technologies Co., Ltd.
+ * http://www.mindspeed.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/log2.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <mach/hardware.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/rtc/rtc-c2k.h>
+
+/* Initial by default values */
+#define YEAR 112 /* 2012 */
+#define MONTH 6 /* 6 */
+#define DATE 12 /* 18 */
+#define DAY 3 //0:SUNDAY 1:MONDAY 2:TUESDAY 3:WEDNESDAY 4:THURSDAY 5:FRIDAY 6:SATURDAY
+#define HOUR 18 //24 hours time format
+#define MIN 25
+#define SEC 1
+/*End*/
+
+#define C2K_RTC_RTCALM_SECEN 0x81
+#define C2K_RTC_RTCALM_MINEN 0x82
+#define C2K_RTC_RTCALM_HOUREN 0x84
+#define C2K_RTC_RTCALM_DATEEN 0x88
+#define C2K_RTC_RTCALM_DAYEN 0x90
+#define C2K_RTC_RTCALM_MONEN 0xA0
+#define C2K_RTC_RTCALM_YEAREN 0xC0
+#define C2K_RTC_RTCALM_GLOBALEN 0x80
+
+#define C2K_RTC_RTCALM_ALMEN 0xff
+#define DISABLE_ALL_ALAM 0x0
+
+#define C2K_RTCALM_ALMEN (0x3<<0)
+
+static struct resource *c2k_rtc_mem;
+static volatile char __iomem *c2k_rtc_base;
+static int rtc_alarmno;
+
+#define writel_rtc(v,a) (*(int *)(a) = (v))
+#define readl_rtc(a) (*(int *)(a))
+
+void rtc_reg_write(unsigned int WrData, volatile char *WrAd)
+{
+ unsigned int WrAddr = (unsigned int)WrAd;
+ unsigned int WriteData;
+
+ WriteData = (0x0000FFFF & WrData);
+
+ /* The core APB runs at 250Hz ,where as the RTC APB runs at 50MHz.
+ * So the delay need to be inserted between two APB requests, other
+ * wise transaction gets dropped.
+ * Each Core time unit = 4000ps , RTC time unit = 20000ps. 5 times
+ * core time unit = RTC time unit.
+ */
+
+ writel_rtc(WriteData, WrAddr);
+
+ udelay(5);
+
+ return;
+}
+
+int rtc_reg_read(volatile char *RdAd)
+{
+ unsigned int *RdAddr = (unsigned int*)RdAd;
+ unsigned int WriteData,ReadData;
+
+ WriteData = 0x00010000;
+
+ /* The core APB runs at 250Hz ,where as the RTC APB runs at 50MHz.
+ * So the delay need to be inserted between two APB requests, other
+ * wise transaction gets dropped.
+ * Each Core time unit = 4000ps , RTC time unit = 20000ps. 5 times
+ * core time unit = RTC time unit.
+ */
+
+ writel_rtc(WriteData, RdAddr);
+
+ /* The core APB runs at 250Hz ,where as the RTC APB runs at 50MHz.
+ * So the delay need to be inserted between two APB requests, other
+ * wise transaction gets dropped.
+ * Each Core time unit = 4000ps , RTC time unit = 20000ps. 5 times
+ * core time unit = RTC time unit.
+ */
+
+ udelay(5);
+
+ ReadData = readl_rtc(RdAddr);
+
+ while ( (0x00010000 & ReadData) != 0x10000)
+ {
+ udelay(5);
+ ReadData = readl_rtc(RdAddr);
+ }
+
+ return((0x0000FFFF & ReadData));
+}
+
+void dbg_rtc_time(struct rtc_time *rtc_tm)
+{
+ printk ("\n%s: \
+ \n\trtc_tm->tm_sec=%d \
+ \n\trtc_tm->tm_min=%d \
+ \n\trtc_tm->tm_hour=%d \
+ \n\trtc_tm->tm_mday=%d \
+ \n\trtc_tm->tm_wday=%d \
+ \n\trtc_tm->tm_mon=%d \
+ \n\trtc_tm->tm_year=%d \n\n", __func__, \
+ rtc_tm->tm_sec, rtc_tm->tm_min, rtc_tm->tm_hour, rtc_tm->tm_mday,\
+ rtc_tm->tm_wday, rtc_tm->tm_mon, rtc_tm->tm_year);
+}
+
+/* IRQ Handlers */
+
+static irqreturn_t c2k_rtc_alarmirq(int irq, void *id)
+{
+ struct rtc_device *rdev = id;
+
+ pr_debug ("%s: irq=%d: Alaram Rang ..............!!!\n \
+ \n\t*(0x%x)=0x%x \
+ \n\t*(0x%x)=0x%x \n", __func__, irq, \
+ (unsigned int)(c2k_rtc_base + C2K_RTC_RTCALM), rtc_reg_read(c2k_rtc_base + C2K_RTC_RTCALM),\
+ (unsigned int)(c2k_rtc_base + C2K_RTC_RTCIM), rtc_reg_read(c2k_rtc_base + C2K_RTC_RTCIM));
+
+ rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
+ /*-------------------------
+ * DISBABLING alarm
+ *------------------------
+ */
+ rtc_reg_write(DISABLE_ALL_ALAM, c2k_rtc_base + C2K_RTC_RTCALM);
+
+ /*-------------------------
+ * DISBABLING the alarm interrupt
+ *------------------------
+ */
+ rtc_reg_write(DISABLE_ALL_ALAM, c2k_rtc_base + C2K_RTC_RTCIM);
+
+ /*-------------------------
+ * DISBABLING the alarm PENDING bit
+ *------------------------
+ */
+ rtc_reg_write(DISABLE_ALL_ALAM, c2k_rtc_base + C2K_RTC_RTCPEND);
+
+ return IRQ_HANDLED;
+}
+
+/* Time read/write */
+static int c2k_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ rtc_tm->tm_year = rtc_reg_read(c2k_rtc_base + C2K_RTC_BCDYEAR);
+ rtc_tm->tm_mon = rtc_reg_read(c2k_rtc_base + C2K_RTC_BCDMON);
+ rtc_tm->tm_mday = rtc_reg_read(c2k_rtc_base + C2K_RTC_BCDDATE);
+ rtc_tm->tm_wday = rtc_reg_read(c2k_rtc_base + C2K_RTC_BCDDAY);
+ rtc_tm->tm_hour = rtc_reg_read(c2k_rtc_base + C2K_RTC_BCDHOUR);
+ rtc_tm->tm_min = rtc_reg_read(c2k_rtc_base + C2K_RTC_BCDMIN);
+ rtc_tm->tm_sec = rtc_reg_read(c2k_rtc_base + C2K_RTC_BCDSEC);
+
+ pr_debug("%s: BCD:\n \
+ \n\tyear.mon.date.day hr:min:sec\n \
+ \n\t0x%x.0x%x.0x%x.0x%x 0x%x:0x%x:0x%x\n",__func__,\
+ rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,rtc_tm->tm_wday,\
+ rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+
+ rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
+ rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
+ rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
+ rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
+ rtc_tm->tm_wday = bcd2bin(rtc_tm->tm_wday);
+ rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
+ rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
+
+ rtc_tm->tm_year += 100;
+
+ rtc_tm->tm_mon -= 1;
+
+ return rtc_valid_tm(rtc_tm);
+}
+
+#define C2K_RTC_RTCCON_STARTB (0x1<<0) /* RTC Halt */
+#define C2K_RTC_RTCCON_RTCEN (0x1<<1) /* RTC Write Enable */
+#define C2K_RTC_RTCCON_CLKRST (0x1<<2) /* RTC RESET */
+
+static int c2k_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ int year = tm->tm_year - 100;
+
+ pr_debug("%s: Will set:\
+ \n%04d.%02d.%02d %02d:%02d:%02d\n",__func__,\
+ tm->tm_year+1900, tm->tm_mon, tm->tm_mday, \
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ if (year < 0 || year >= 100) {
+ dev_err(dev, "rtc only supports 100 years\n");
+ return -EINVAL;
+ }
+
+ rtc_reg_write((C2K_RTC_RTCCON_STARTB | C2K_RTC_RTCCON_RTCEN | C2K_RTC_RTCCON_CLKRST), \
+ c2k_rtc_base + C2K_RTC_RTCCON);
+
+ rtc_reg_write(bin2bcd(tm->tm_min), c2k_rtc_base + C2K_RTC_BCDMIN);
+ rtc_reg_write(bin2bcd(tm->tm_hour), c2k_rtc_base + C2K_RTC_BCDHOUR);
+ rtc_reg_write(bin2bcd(tm->tm_mday), c2k_rtc_base + C2K_RTC_BCDDATE);
+ rtc_reg_write(bin2bcd(tm->tm_wday), c2k_rtc_base + C2K_RTC_BCDDAY);
+ rtc_reg_write(bin2bcd(tm->tm_mon+1), c2k_rtc_base + C2K_RTC_BCDMON);
+ rtc_reg_write(bin2bcd(year), c2k_rtc_base + C2K_RTC_BCDYEAR);
+ rtc_reg_write(bin2bcd(tm->tm_sec), c2k_rtc_base + C2K_RTC_BCDSEC);
+
+ rtc_reg_write((C2K_RTC_RTCCON_RTCEN | C2K_RTC_RTCCON_CLKRST), c2k_rtc_base + C2K_RTC_RTCCON);
+
+ return 0;
+}
+
+static int c2k_rtc_setaie(struct device *dev, unsigned int enabled)
+{
+ unsigned int tmp;
+
+ pr_debug ("%s: aie=%d\n", __func__, enabled);
+
+ tmp = rtc_reg_read(c2k_rtc_base + C2K_RTC_RTCIM) & ~C2K_RTCALM_ALMEN;
+
+ if (enabled)
+ tmp |= C2K_RTCALM_ALMEN;
+
+ rtc_reg_write(tmp, c2k_rtc_base + C2K_RTC_RTCIM);
+
+ return 0;
+}
+
+static int c2k_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *alm_tm = &alrm->time;
+ unsigned int alm_en;
+
+ alm_tm->tm_sec = rtc_reg_read(c2k_rtc_base + C2K_RTC_ALMSEC);
+ alm_tm->tm_min = rtc_reg_read(c2k_rtc_base + C2K_RTC_ALMMIN);
+ alm_tm->tm_hour = rtc_reg_read(c2k_rtc_base + C2K_RTC_ALMHOUR);
+ alm_tm->tm_mon = rtc_reg_read(c2k_rtc_base + C2K_RTC_ALMMON);
+ alm_tm->tm_mday = rtc_reg_read(c2k_rtc_base + C2K_RTC_ALMDATE);
+ alm_tm->tm_wday = rtc_reg_read(c2k_rtc_base + C2K_RTC_ALMDAY);
+ alm_tm->tm_year = rtc_reg_read(c2k_rtc_base + C2K_RTC_ALMYEAR);
+
+ alm_en = rtc_reg_read(c2k_rtc_base + C2K_RTC_RTCALM);
+
+ alrm->enabled = (alm_en & C2K_RTC_RTCALM_ALMEN) ? 1 : 0;
+
+ pr_debug("%s: alm_en=%d, %04d.%02d.%02d.%02d %02d:%02d:%02d\n",__func__,
+ alm_en,
+ 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
+ alm_tm->tm_wday, alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+
+ /* decode the alarm enable field */
+
+ if (alm_en & C2K_RTC_RTCALM_SECEN)
+ alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
+ else
+ alm_tm->tm_sec = -1;
+
+ if (alm_en & C2K_RTC_RTCALM_MINEN)
+ alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
+ else
+ alm_tm->tm_min = -1;
+
+ if (alm_en & C2K_RTC_RTCALM_HOUREN)
+ alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
+ else
+ alm_tm->tm_hour = -1;
+
+ if (alm_en & C2K_RTC_RTCALM_DAYEN)
+ alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
+ else
+ alm_tm->tm_mday = -1;
+
+ if (alm_en & C2K_RTC_RTCALM_MONEN) {
+ alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
+ alm_tm->tm_mon -= 1;
+ } else {
+ alm_tm->tm_mon = -1;
+ }
+
+ if (alm_en & C2K_RTC_RTCALM_YEAREN){
+ alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
+ alm_tm->tm_year += 100;
+ }
+ else
+ alm_tm->tm_year = -1;
+
+ return 0;
+}
+
+static int c2k_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *tm = &alrm->time;
+
+ pr_debug("%s: %d, %04d.%02d.%02d %02d:%02d:%02d\n",__func__,
+ alrm->enabled,
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ rtc_reg_write(0x3, (c2k_rtc_base + C2K_RTC_RTCIM)); //enable alarm interrupt mode
+ rtc_reg_write(0xff, (c2k_rtc_base + C2K_RTC_RTCALM)); //enable all alarms.
+
+ if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
+ rtc_reg_write(bin2bcd(tm->tm_sec), c2k_rtc_base + C2K_RTC_ALMSEC);
+ }
+
+ if (tm->tm_min < 60 && tm->tm_min >= 0) {
+ rtc_reg_write(bin2bcd(tm->tm_min), c2k_rtc_base + C2K_RTC_ALMMIN);
+ }
+
+ if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
+ rtc_reg_write(bin2bcd(tm->tm_hour), c2k_rtc_base + C2K_RTC_ALMHOUR);
+ }
+
+ if (tm->tm_mday < 32 && tm->tm_mday >= 1) {
+ rtc_reg_write(bin2bcd(tm->tm_mday), c2k_rtc_base + C2K_RTC_ALMDATE);
+ }
+
+ if (tm->tm_wday < 7 && tm->tm_wday >= 0) {
+ rtc_reg_write(bin2bcd(tm->tm_wday), c2k_rtc_base + C2K_RTC_ALMDAY);
+ }
+
+ if (tm->tm_mon < 12 && tm->tm_mon >= 0) {
+ rtc_reg_write(bin2bcd(tm->tm_mon+1), c2k_rtc_base + C2K_RTC_ALMMON);
+ }
+
+ if (tm->tm_year >= 1) {
+ int year = tm->tm_year - 100;
+
+ if (year < 0 || year >= 100) {
+ dev_err(dev, "rtc only supports 100 years\n");
+ return -EINVAL;
+ }
+
+ rtc_reg_write(bin2bcd(year), c2k_rtc_base + C2K_RTC_ALMYEAR);
+ }
+
+ c2k_rtc_setaie(dev, alrm->enabled);
+
+ return 0;
+}
+
+static const struct rtc_class_ops c2k_rtcops = {
+ .read_time = c2k_rtc_gettime,
+ .set_time = c2k_rtc_settime,
+ .read_alarm = c2k_rtc_getalarm,
+ .set_alarm = c2k_rtc_setalarm,
+ .alarm_irq_enable = c2k_rtc_setaie,
+};
+
+static int __devexit c2k_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ free_irq(rtc_alarmno, rtc);
+
+ platform_set_drvdata(dev, NULL);
+ rtc_device_unregister(rtc);
+
+ c2k_rtc_setaie(&dev->dev, 0);
+
+ iounmap(c2k_rtc_base);
+ release_resource(c2k_rtc_mem);
+ kfree(c2k_rtc_mem);
+
+ return 0;
+}
+
+static char __initdata banner[] = "C2000 RTC, (c) 2012 Mindspeed Technologies\n";
+
+static int __devinit c2k_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct rtc_time rtc_tm;
+ struct resource *res;
+ int ret;
+
+ rtc_alarmno = platform_get_irq(pdev, 0);
+ if (rtc_alarmno < 0) {
+ dev_err(&pdev->dev, "no irq for alarm\n");
+ return -ENOENT;
+ }
+
+ /* get the memory region */
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region resource\n");
+ return -ENOENT;
+ }
+
+ pr_debug ("%s: alarm irq=%d res->start=0x%x res->end=0x%x\n", __func__, \
+ rtc_alarmno, res->start, res->end);
+
+ c2k_rtc_mem = request_mem_region(res->start, resource_size(res),
+ pdev->name);
+ if (c2k_rtc_mem == NULL) {
+ printk("%s: failed to reserve memory region\n", __func__);
+ ret = -ENOENT;
+ goto err_nores;
+ }
+
+ c2k_rtc_base = ioremap(res->start, resource_size(res));
+ if (c2k_rtc_base == NULL) {
+ printk ("%s: failed ioremap()\n", __func__);
+ ret = -EINVAL;
+ goto err_nomap;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ /* register RTC and exit */
+
+ rtc = rtc_device_register("c2k", &pdev->dev, &c2k_rtcops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "cannot attach rtc\n");
+ ret = PTR_ERR(rtc);
+ goto err_nortc;
+ } else
+ printk(banner);
+
+ /* Check RTC Time */
+ c2k_rtc_gettime(NULL, &rtc_tm);
+
+ if (rtc_valid_tm(&rtc_tm)) {
+ rtc_tm.tm_year = YEAR;
+ rtc_tm.tm_mon = MONTH;
+ rtc_tm.tm_mday = DATE;
+ rtc_tm.tm_wday = DAY;
+ rtc_tm.tm_hour = HOUR;
+ rtc_tm.tm_min = MIN;
+ rtc_tm.tm_sec = SEC;
+
+ c2k_rtc_settime(NULL, &rtc_tm);
+
+ dev_warn(&pdev->dev, "Warning: Invalid RTC value so initializing it\n");
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ ret = request_irq(rtc_alarmno, c2k_rtc_alarmirq,
+ IRQF_DISABLED, "rtc-alarm", rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "IRQ%d error %d\n", rtc_alarmno, ret);
+ goto err_alarm_irq;
+ }
+
+ return 0;
+
+err_alarm_irq:
+ platform_set_drvdata(pdev, NULL);
+ rtc_device_unregister(rtc);
+
+err_nortc:
+ iounmap(c2k_rtc_base);
+
+err_nomap:
+ release_resource(c2k_rtc_mem);
+
+err_nores:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+
+/* RTC Power management control */
+static int c2k_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+static int c2k_rtc_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define c2k_rtc_suspend NULL
+#define c2k_rtc_resume NULL
+#endif
+
+static struct platform_driver c2k_rtc_driver = {
+ .probe = c2k_rtc_probe,
+ .remove = __devexit_p(c2k_rtc_remove),
+ .suspend = c2k_rtc_suspend,
+ .resume = c2k_rtc_resume,
+ .driver = {
+ .name = "c2k-rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init c2k_rtc_init(void)
+{
+ return platform_driver_register(&c2k_rtc_driver);
+}
+
+static void __exit c2k_rtc_exit(void)
+{
+ platform_driver_unregister(&c2k_rtc_driver);
+}
+
+module_init(c2k_rtc_init);
+module_exit(c2k_rtc_exit);
+
+MODULE_DESCRIPTION("Mindspeed RTC Driver");
+MODULE_AUTHOR("Satendra Pratap <satendra.pratap@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:c2k-rtc");
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 7587796..7aea2c0 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -373,6 +373,20 @@
Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
+config SPI_MSPD_LOW_SPEED
+ tristate "MSPD SPI low-speed controller module"
+ select SPI_DESIGNWARE
+ default SPI_MASTER
+ help
+ This exposes the MSPD SPI low-speed controller.
+
+config SPI_MSPD_HIGH_SPEED
+ tristate "MSPD SPI high-speed controller module"
+ select SPI_DESIGNWARE
+ default SPI_MASTER
+ help
+ This exposes the MSPD SPI high-speed controller.
+
config SPI_NUC900
tristate "Nuvoton NUC900 series SPI"
depends on ARCH_W90X900 && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 61c3261..41c6530 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -19,8 +19,26 @@
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
+
+SPI_MSPD=n
+ifeq ($(CONFIG_SPI_MSPD_HIGH_SPEED),m)
+ SPI_MSPD=m
+endif
+ifeq ($(CONFIG_SPI_MSPD_LOW_SPEED),m)
+ SPI_MSPD=m
+endif
+ifeq ($(CONFIG_SPI_MSPD_HIGH_SPEED),y)
+ SPI_MSPD=y
+endif
+ifeq ($(CONFIG_SPI_MSPD_LOW_SPEED),y)
+ SPI_MSPD=y
+endif
+#obj-$(SPI_MSPD) += spi-c2000-dma.o
+#obj-$(SPI_MSPD) += spi-c2000.o
+
obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o
-obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o
+obj-$(CONFIG_SPI_DESIGNWARE) += designware_spi.o
+#obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o
obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o
obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o
spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o
diff --git a/drivers/spi/comcerto_spi.c b/drivers/spi/comcerto_spi.c
new file mode 100644
index 0000000..7b537af
--- /dev/null
+++ b/drivers/spi/comcerto_spi.c
@@ -0,0 +1,640 @@
+/*
+ * Mindspeed SPI controller driver (master mode only)
+ *
+ * Author: Satendra Pratap
+ * satendra.pratap@mindspeed.com
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/io.h>
+
+#include <linux/spi/comcerto_spi.h>
+
+#define SPI_VERSION "0.0.1"
+
+#define spi_err(dev, fmt, args...) dev_err(dev, "%s: " fmt, __FUNCTION__, ##args)
+#define spi_dbg(dev, fmt, args...) dev_dbg(dev, "%s: " fmt, __FUNCTION__, ##args)
+#define spi_warn(dev, fmt, args...) dev_warn(dev, "%s: " fmt, __FUNCTION__, ##args)
+
+struct comcerto_spi {
+ /* bitbang has to be first */
+ struct spi_bitbang bitbang;
+ struct platform_device *dev;
+ struct completion done;
+ struct resource mem; /* phys mem */
+ void __iomem *regs; /* virt. address of the control registers */
+
+ u32 irq;
+
+ u8 *rx_ptr; /* pointer in the Tx buffer */
+ const u8 *tx_ptr; /* pointer in the Rx buffer */
+ int remaining_bytes; /* the number of bytes left to transfer */
+ u8 bits_per_word;
+
+ int (*do_read8)(void __iomem *, u8 *buf, unsigned int *len);
+ int (*do_write8)(void __iomem *, u8 *buf, unsigned int *len);
+
+ int (*do_read16)(void __iomem *, u16 *buf, unsigned int *len);
+ int (*do_write16)(void __iomem *, u16 *buf, unsigned int *len);
+
+ int (*do_write_read)(void __iomem *, u8 fs, u8 *wbuf, unsigned int *wlen, u8 *rbuf, unsigned int *rlen);
+};
+
+static int do_write_read_transfer(void __iomem *reg_base, u8 fs, u8 *wbuf, unsigned int *wlen, u8 *rbuf, unsigned int *rlen)
+{
+ u32 sr, dr;
+ unsigned int wlen_now = 0, rlen_now = 0;
+ int rc = 0;
+
+ while (wlen_now < *wlen) {
+ sr = __raw_readl(reg_base + COMCERTO_SPI_SR);
+
+ if (sr & TFNF) {
+ if (wlen_now < *wlen) {
+ __raw_writew(cpu_to_le16((u16) *wbuf), reg_base + COMCERTO_SPI_DR);
+ wbuf++;
+ wlen_now++;
+ }
+ }
+ }
+
+ while (rlen_now < *rlen) {
+ sr = __raw_readl(reg_base + COMCERTO_SPI_SR);
+
+ if (sr & (RFF | DCOL)) {
+ rc = -1;
+ goto out;
+ }
+
+ if (sr & RFNE) {
+ dr = __raw_readw(reg_base + COMCERTO_SPI_DR);
+ if (rlen_now < *rlen) {
+ *rbuf = (u8) (le16_to_cpu(dr) & 0xff);
+ rbuf++;
+ rlen_now++;
+ } else {
+ rc = -1;
+ goto out;
+ }
+ }
+ }
+
+out:
+ *rlen = rlen_now;
+ *wlen = wlen_now;
+
+ return rc;
+}
+
+/**
+ * * do_write_only_transfer8 -
+ * *
+ * *
+ * */
+static int do_write_only_transfer8(void __iomem *reg_base, u8 *buf, unsigned int *len)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int tmp = *len;
+ u32 dr = (u32)reg_base + COMCERTO_SPI_DR;
+ u32 txflr = (u32)reg_base + COMCERTO_SPI_TXFLR;
+
+ while (tmp)
+ {
+ len_now = 8 - __raw_readl(txflr);
+ if (len_now > tmp)
+ len_now = tmp;
+
+ tmp -= len_now;
+
+ /* warm-up
+ * write
+ * fifo
+ * to
+ * avoid
+ * underruns
+ * */
+ while (len_now--)
+ __raw_writew(cpu_to_le16((u16) *buf++), dr);
+ }
+
+ *len -= tmp;
+
+ return rc;
+}
+
+/**
+ * * do_write_only_transfer -
+ * *
+ * *
+ * */
+
+static int do_write_only_transfer16(void __iomem *reg_base, u16 *buf, unsigned int *len)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int tmp = *len;
+ u32 dr = (u32)reg_base + COMCERTO_SPI_DR;
+ u32 txflr = (u32)reg_base + COMCERTO_SPI_TXFLR;
+
+ while (tmp)
+ {
+ len_now = 8 - __raw_readl(txflr);
+ if (len_now > tmp)
+ len_now = tmp;
+
+ tmp -= len_now;
+
+ /* warm-up
+ * write
+ * fifo
+ * to
+ * avoid
+ * underruns
+ * */
+ while (len_now--)
+ __raw_writew(cpu_to_le16(*buf++), dr);
+ }
+
+ *len -= tmp;
+
+ return rc;
+}
+
+
+/**
+ * * do_read_only_transfer -
+ * *
+ * *
+ * */
+static int do_read_only_transfer8(void __iomem *reg_base, u8 *buf, unsigned int *len)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int tmp = *len;
+ u32 dr = (u32)reg_base + COMCERTO_SPI_DR;
+ u32 rxflr = (u32)reg_base + COMCERTO_SPI_RXFLR;
+
+ /* start the serial clock */
+ __raw_writew(0, dr);
+
+ while (tmp)
+ {
+ len_now = __raw_readl(rxflr);
+ if (len_now > tmp)
+ len_now = tmp;
+
+ tmp -= len_now;
+
+ while (len_now--) {
+ *buf = (u8) (le16_to_cpu(__raw_readw(dr)) & 0xff);
+ buf++;
+ }
+ }
+
+ *len -= tmp;
+
+ return rc;
+}
+
+/**
+ * * do_read_only_transfer -
+ * *
+ * *
+ * */
+static int do_read_only_transfer16(void __iomem *reg_base, u16 *buf, unsigned int *len)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int tmp = *len;
+ u32 dr = (u32)reg_base + COMCERTO_SPI_DR;
+ u32 rxflr = (u32)reg_base + COMCERTO_SPI_RXFLR;
+
+ /* start the serial clock */
+ __raw_writew(0, dr);
+
+ while (tmp)
+ {
+ len_now = __raw_readl(rxflr);
+ if (len_now > tmp)
+ len_now = tmp;
+
+ tmp -= len_now;
+
+ while (len_now--) {
+ *buf = le16_to_cpu(__raw_readw(dr));
+ buf++;
+ }
+ }
+
+ *len -= tmp;
+
+ return rc;
+}
+
+
+
+int comcerto_spi_do_transfer(struct comcerto_spi *xspi, struct spi_client_conf *cconf, struct comcerto_transfer *transfer)
+{
+ u32 ctrlr0, ctrlr1, baudr = 0, ser = 0;
+ int rc;
+ void __iomem *reg_base = xspi->regs;
+
+ baudr = transfer->baudr;
+ ser = transfer->ser;
+ /* make sure last transaction is finished */
+ while (__raw_readl(reg_base + COMCERTO_SPI_SR) & BUSY) ;
+
+ ctrlr0 = transfer->ctrlr0;
+
+ __raw_writel(0, reg_base + COMCERTO_SPI_SSIENR);
+
+ switch (transfer->mode & 0x0f) {
+ default:
+ rc = -1;
+ break;
+
+ case SPI_TRANSFER_MODE_WRITE_ONLY:
+ ctrlr0 |= (0x0001 << 8);
+
+ __raw_writel(ctrlr0, reg_base + COMCERTO_SPI_CTRLR0);
+ __raw_writel(baudr, reg_base + COMCERTO_SPI_BAUDR);
+ __raw_writel(ser, reg_base + COMCERTO_SPI_SER);
+ __raw_writel(8, reg_base + COMCERTO_SPI_RXFTLR);
+ __raw_writel(0, reg_base + COMCERTO_SPI_TXFTLR);
+ __raw_writel(0, reg_base + COMCERTO_SPI_IMR);
+ __raw_writel(1, reg_base + COMCERTO_SPI_SSIENR);
+
+ if (transfer->fs <= 8)
+ rc = xspi->do_write8(reg_base, transfer->wbuf, &transfer->wlen);
+ else
+ rc = xspi->do_write16(reg_base, (u16 *) transfer->wbuf, &transfer->wlen);
+
+ break;
+
+ case SPI_TRANSFER_MODE_READ_ONLY:
+ ctrlr0 |= (0x0002 << 8);
+ ctrlr1 = transfer->rlen - 1;
+
+ __raw_writel(ctrlr0, reg_base + COMCERTO_SPI_CTRLR0);
+
+ __raw_writel(ctrlr1, reg_base + COMCERTO_SPI_CTRLR1);
+ __raw_writel(baudr, reg_base + COMCERTO_SPI_BAUDR);
+ __raw_writel(ser, reg_base + COMCERTO_SPI_SER);
+ __raw_writel(8, reg_base + COMCERTO_SPI_RXFTLR);
+ __raw_writel(0, reg_base + COMCERTO_SPI_TXFTLR);
+ __raw_writel(0, reg_base + COMCERTO_SPI_IMR);
+ __raw_writel(1, reg_base + COMCERTO_SPI_SSIENR);
+
+ if (transfer->fs <= 8)
+ rc = xspi->do_read8(reg_base, transfer->rbuf, &transfer->rlen);
+ else
+ rc = xspi->do_read16(reg_base, (u16 *) transfer->rbuf, &transfer->rlen);
+
+ break;
+
+ case SPI_TRANSFER_MODE_WRITE_READ:
+ ctrlr0 |= (0x0000 << 8);
+
+ __raw_writel(ctrlr0, reg_base + COMCERTO_SPI_CTRLR0);
+ __raw_writel(baudr, reg_base + COMCERTO_SPI_BAUDR);
+ __raw_writel(ser, reg_base + COMCERTO_SPI_SER);
+ __raw_writel(8, reg_base + COMCERTO_SPI_RXFTLR);
+ __raw_writel(0, reg_base + COMCERTO_SPI_TXFTLR);
+ __raw_writel(0, reg_base + COMCERTO_SPI_IMR);
+ __raw_writel(1, reg_base + COMCERTO_SPI_SSIENR);
+
+ rc = xspi->do_write_read(reg_base, transfer->fs, transfer->wbuf, &transfer->wlen, transfer->rbuf, &transfer->rlen);
+
+ break;
+ }
+
+ return rc;
+}
+
+static int comcerto_spi_setup(struct spi_device *spi)
+{
+ int rc = 0;
+
+ spi_dbg(&spi->dev, "bits per word %u, max speed %uHz, mode %#x\n",
+ spi->bits_per_word, spi->max_speed_hz, spi->mode);
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ if (spi->bits_per_word < SPI_FRAME_SIZE_MIN || spi->bits_per_word > SPI_FRAME_SIZE_MAX) {
+ spi_err(&spi->dev, "bits per word (frame size) %u out of range %u..%u\n",
+ spi->max_speed_hz, SPI_FRAME_SIZE_MIN, SPI_FRAME_SIZE_MAX);
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if (spi->max_speed_hz < SPI_SPEED_MIN) {
+ spi_err(&spi->dev, "such low speed %u isn't supported, min is %u\n",
+ spi->max_speed_hz, SPI_SPEED_MIN);
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if (spi->max_speed_hz > SPI_SPEED_MAX) {
+ spi_warn(&spi->dev, "decreasing speed %u to max supported %u\n",
+ spi->max_speed_hz, SPI_SPEED_MAX);
+ spi->max_speed_hz = SPI_SPEED_MAX;
+ }
+
+ if (spi->chip_select > SPI_CHIP_SELECT_MAX) {
+ spi_err(&spi->dev, "chip select %u out of range 0..%u\n",
+ spi->max_speed_hz, SPI_CHIP_SELECT_MAX);
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if (spi->mode & SPI_CS_HIGH) {
+ spi_err(&spi->dev, "chip select active high isn't supported\n");
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if (spi->mode & SPI_LSB_FIRST) {
+ spi_err(&spi->dev, "LSB first mode isn't supported\n");
+ rc = -EINVAL;
+ goto err;
+ }
+
+err:
+ return rc;
+ return 0;
+}
+
+static void spi_set_control (struct comcerto_spi *xspi, struct spi_client_conf *cconf, struct comcerto_transfer *ct)
+{
+ u32 ctrlr0;
+ struct spi_master *master;
+ struct comcerto_spi_platform *pdata = (struct comcerto_spi_platform *)xspi->dev->dev.platform_data;
+ struct spi_client_conf *scc = (struct spi_client_conf *)pdata->devices->platform_data;
+
+ master = xspi->bitbang.master;
+
+ cconf->cs_msk = scc->cs_msk;
+ cconf->sc_polarity = scc->sc_polarity;
+ cconf->sc_phase = scc->sc_phase;
+ cconf->sc_rate = scc->sc_rate;
+ cconf->cs_delay = scc->cs_delay;
+
+ ctrlr0 = ((cconf->sc_polarity & 0x1) << 7) | ((cconf->sc_phase & 0x1) << 6) | (((ct->fs - 1) & 0xf) << 0);
+
+ ct->ctrlr0 = ctrlr0;
+ ct->mode = master->mode_bits;
+
+ /* FIXME: this will be fixed after clock PLL implementation done.*/
+ ct->baudr = pdata->clock_rate / cconf->sc_rate;
+ ct->ser = cconf->cs_msk & pdata->cs_msk;
+}
+
+int comcerto_spi_transfer(struct spi_device *spi,
+ struct spi_message *mesg)
+{
+ struct spi_transfer *t = NULL;
+ struct comcerto_transfer ct;
+ struct spi_client_conf cconf;
+ int ret = 0;
+ struct comcerto_spi *xspi = spi_master_get_devdata(spi->master);
+
+ ct.fs = 8;
+
+ spi_set_control(xspi, &cconf, &ct);
+
+ list_for_each_entry (t, &mesg->transfers, transfer_list) {
+ ct.wbuf = (u8 *)t->tx_buf;
+ ct.rbuf = (u8 *)t->rx_buf;
+ ct.wlen = t->len;
+ ct.rlen = t->len;
+
+ ret = comcerto_spi_do_transfer(xspi, &cconf, &ct);
+
+ if (ret == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void comcerto_spi_hw_init(struct comcerto_spi *xspi)
+{
+ void __iomem *reg_base = xspi->regs;
+
+ /* enable SPI bus : FIXME */
+ //comcerto_gpio_ctrl(0x1 << 9, 0x1 << 9);
+
+
+ /* disable SPI operation */
+ writel(0, reg_base + COMCERTO_SPI_SSIENR);
+
+ /* mask all SPI irq's */
+ writel(0, reg_base + COMCERTO_SPI_IMR);
+}
+
+struct spi_master *comcerto_spi_init(struct platform_device *pdev, struct resource *mem,
+ u32 irq, s16 bus_num, int num_cs)
+{
+ struct device *dev = &pdev->dev;
+ struct spi_master *master;
+ struct comcerto_spi *xspi;
+
+ master = spi_alloc_master(dev, sizeof(struct comcerto_spi));
+ if (!master)
+ return NULL;
+
+ /* the spi->mode bits understood by this driver: */
+ master->mode_bits = SPI_CPOL | SPI_CPHA;
+
+ xspi = spi_master_get_devdata(master);
+ //memcpy(&(xspi->dev), pdev, sizeof(struct platform_device));
+ xspi->dev = pdev;
+ xspi->bitbang.master = spi_master_get(master);
+ xspi->bitbang.master->setup = comcerto_spi_setup;
+ xspi->bitbang.master->transfer = comcerto_spi_transfer;
+ xspi->bitbang.master->cleanup = NULL;
+
+ init_completion(&xspi->done);
+
+ if (!request_mem_region(mem->start, resource_size(mem),
+ comcerto_SPI_NAME))
+ goto put_master;
+
+ xspi->regs = ioremap(mem->start, resource_size(mem));
+ if (xspi->regs == NULL) {
+ dev_warn(dev, "ioremap failure\n");
+ goto map_failed;
+ }
+
+ master->bus_num = bus_num;
+ master->num_chipselect = num_cs;
+ master->dev.of_node = dev->of_node;
+
+ xspi->mem = *mem;
+ xspi->irq = irq;
+
+ xspi->do_read8 = do_read_only_transfer8;
+ xspi->do_write8 = do_write_only_transfer8;
+
+ xspi->do_read16 = do_read_only_transfer16;
+ xspi->do_write16 = do_write_only_transfer16;
+
+ xspi->do_write_read = do_write_read_transfer;
+
+ /* SPI controller initializations */
+ comcerto_spi_hw_init(xspi);
+
+ /* Register for SPI Interrupt : FIXME*/
+ /*
+ ret = request_irq(xspi->irq, comcerto_spi_irq, 0, comcerto_SPI_NAME, xspi);
+ if (ret)
+ goto unmap_io;
+
+
+ ret = spi_bitbang_start(&xspi->bitbang);
+ if (ret) {
+ dev_err(dev, "spi_bitbang_start FAILED\n");
+ goto free_irq;
+ }
+ */
+ dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n",
+ (unsigned long long)mem->start, xspi->regs, xspi->irq);
+ return master;
+
+/* FIXME */
+//free_irq:
+ //free_irq(xspi->irq, xspi);
+ //iounmap(xspi->regs);
+map_failed:
+ release_mem_region(mem->start, resource_size(mem));
+put_master:
+ spi_master_put(master);
+ return NULL;
+}
+EXPORT_SYMBOL(comcerto_spi_init);
+
+void comcerto_spi_deinit(struct spi_master *master)
+{
+ struct comcerto_spi *xspi;
+
+ xspi = spi_master_get_devdata(master);
+
+ //FIXME spi_bitbang_stop(&xspi->bitbang);
+ //free_irq(xspi->irq, xspi);
+ iounmap(xspi->regs);
+
+ release_mem_region(xspi->mem.start, resource_size(&xspi->mem));
+ spi_master_put(xspi->bitbang.master);
+
+ spi_unregister_master(master);
+}
+EXPORT_SYMBOL(comcerto_spi_deinit);
+
+static int __devinit comcerto_spi_probe(struct platform_device *dev)
+{
+ struct comcerto_spi_platform *pdata;
+ struct resource *r;
+ int irq, num_cs = 0;
+ struct spi_master *master;
+ int rc = -EINVAL;
+ int i;
+
+ pdata = (struct comcerto_spi_platform *)dev->dev.platform_data;
+
+ if (pdata) {
+ num_cs = pdata->num_chipselect;
+ }
+
+ if (!num_cs) {
+ dev_err(&dev->dev, "Missing slave select configuration data\n");
+ return -EINVAL;
+ }
+
+
+ r = platform_get_resource(dev, IORESOURCE_MEM, dev->num_resources);
+ if (!r)
+ return -ENODEV;
+
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0)
+ return -ENXIO;
+
+ master = comcerto_spi_init(dev, r, irq, dev->id, num_cs);
+
+ if (!master)
+ return -ENODEV;
+
+ if (pdata) {
+ for (i = 0; i < pdata->num_devices; i++)
+ spi_new_device(master, pdata->devices + i);
+ }
+
+ platform_set_drvdata(dev, master);
+
+ rc = spi_register_master(master);
+
+ if (rc != 0) {
+ spi_err(&dev->dev, "error registering SPI master\n");
+ goto err1;
+ }
+
+ return 0;
+
+err1:
+ spi_master_put(master);
+
+ return rc;
+}
+
+static int __devexit comcerto_spi_remove(struct platform_device *dev)
+{
+ comcerto_spi_deinit(platform_get_drvdata(dev));
+
+ platform_set_drvdata(dev, 0);
+
+ return 0;
+}
+
+static struct platform_driver comcerto_spi_driver = {
+ .probe = comcerto_spi_probe,
+ .remove = __devexit_p(comcerto_spi_remove),
+ .driver = {
+ .name = comcerto_SPI_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init comcerto_spi_pltfm_init(void)
+{
+ printk(KERN_INFO "c2k-spi: %s: loaded version %s\n", __FUNCTION__, SPI_VERSION);
+
+ if(platform_driver_register(&comcerto_spi_driver)) {
+ printk(KERN_ERR "c2k-spi: %s: error registering driver\n", __FUNCTION__);
+ goto err0;
+ }
+
+ return 0;
+
+err0:
+ return -1;
+}
+module_init(comcerto_spi_pltfm_init);
+
+static void __exit comcerto_spi_pltfm_exit(void)
+{
+ platform_driver_unregister(&comcerto_spi_driver);
+}
+module_exit(comcerto_spi_pltfm_exit);
+
+MODULE_AUTHOR("Satendra Pratap");
+MODULE_DESCRIPTION("Mindspeed SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
new file mode 100644
index 0000000..5e2046b
--- /dev/null
+++ b/drivers/spi/designware_spi.c
@@ -0,0 +1,793 @@
+/*
+ * designware_spi.c
+ *
+ * Synopsys DesignWare AMBA SPI controller driver (master mode only)
+ *
+ * Author: Baruch Siach, Tk Open Systems
+ * baruch-NswTu9S1W3P6gbPvEgmw2w@...org
+ *
+ * Base on the Xilinx SPI controller driver by MontaVista
+ *
+ * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ *
+ * 2008, 2009 (c) Provigent Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/designware.h>
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <mach/reset.h>
+
+#define DESIGNWARE_SPI_NAME "comcerto_spi"
+
+/* Register definitions as per "DesignWare DW_apb_ssi Databook", Capture 6. */
+
+#define DWSPI_CTRLR0 0x00
+#define DWSPI_CTRLR1 0x04
+#define DWSPI_SSIENR 0x08
+#define DWSPI_MWCR 0x0C
+#define DWSPI_SER 0x10
+#define DWSPI_BAUDR 0x14
+#define DWSPI_TXFTLR 0x18
+#define DWSPI_RXFTLR 0x1C
+#define DWSPI_TXFLR 0x20
+#define DWSPI_RXFLR 0x24
+#define DWSPI_SR 0x28
+#define DWSPI_IMR 0x2C
+#define DWSPI_ISR 0x30
+#define DWSPI_RISR 0x34
+#define DWSPI_TXOICR 0x38
+#define DWSPI_RXOICR 0x3C
+#define DWSPI_RXUICR 0x40
+#define DWSPI_ICR 0x44
+#define DWSPI_DMACR 0x4C
+#define DWSPI_DMATDLR 0x50
+#define DWSPI_DMARDLR 0x54
+#define DWSPI_IDR 0x58
+#define DWSPI_SSI_COMP_VERSION 0x5C
+#define DWSPI_DR 0x60
+
+#define DWSPI_CTRLR0_DFS_MASK 0x000f
+#define DWSPI_CTRLR0_SCPOL 0x0080
+#define DWSPI_CTRLR0_SCPH 0x0040
+#define DWSPI_CTRLR0_TMOD_MASK 0x0300
+
+#define DWSPI_SR_BUSY_MASK 0x01
+#define DWSPI_SR_TFNF_MASK 0x02
+#define DWSPI_SR_TFE_MASK 0x04
+#define DWSPI_SR_RFNE_MASK 0x08
+#define DWSPI_SR_RFF_MASK 0x10
+
+#define DWSPI_ISR_TXEIS_MASK 0x01
+#define DWSPI_ISR_RXFIS_MASK 0x10
+
+#define DWSPI_IMR_TXEIM_MASK 0x01
+#define DWSPI_IMR_RXFIM_MASK 0x10
+
+struct designware_spi {
+ struct device *dev;
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ struct tasklet_struct pump_transfers;
+
+ struct mutex lock; /* lock this struct except from queue */
+ struct list_head queue; /* spi_message queue */
+ spinlock_t qlock; /* lock the queue */
+
+ void __iomem *regs; /* virt. address of the control registers */
+ unsigned int ssi_clk; /* clock in Hz */
+ unsigned int tx_fifo_depth; /* bytes in TX FIFO */
+ unsigned int rx_fifo_depth; /* bytes in RX FIFO */
+
+ u32 irq;
+
+ u8 bits_per_word; /* current data frame size */
+ struct spi_transfer *tx_t; /* current tx transfer */
+ struct spi_transfer *rx_t; /* current rx transfer */
+ const u8 *tx_ptr; /* current tx buffer */
+ u8 *rx_ptr; /* current rx buffer */
+ int remaining_tx_bytes; /* bytes left to tx in the current transfer */
+ int remaining_rx_bytes; /* bytes left to rx in the current transfer */
+ int status; /* status of the current spi_transfer */
+
+ struct completion done; /* signal the end of tx for current sequence */
+
+ struct spi_device *spi; /* current spi slave device */
+ struct clk *clk_spi;
+ struct list_head *transfers_list; /* head of the current sequence */
+ unsigned int tx_count, rx_count; /* bytes in the current sequence */
+};
+
+static void dwspi_init_hw(struct designware_spi *dwspi)
+{
+ u16 ctrlr0;
+
+ /* Disable the SPI master */
+ writel(0, dwspi->regs + DWSPI_SSIENR);
+ /* Disable all the interrupts just in case */
+ writel(0, dwspi->regs + DWSPI_IMR);
+ /* Set TX empty IRQ threshold */
+ writew(dwspi->tx_fifo_depth / 2, dwspi->regs + DWSPI_TXFTLR);
+
+ /* Set transmit & receive mode */
+ ctrlr0 = readw(dwspi->regs + DWSPI_CTRLR0);
+ ctrlr0 &= ~DWSPI_CTRLR0_TMOD_MASK;
+ writew(ctrlr0, dwspi->regs + DWSPI_CTRLR0);
+}
+
+static void dwspi_baudcfg(struct designware_spi *dwspi, u32 speed_hz)
+{
+ u16 div = (speed_hz) ? dwspi->ssi_clk/speed_hz : 0xffff;
+
+ writew(div, dwspi->regs + DWSPI_BAUDR);
+}
+
+static void dwspi_enable(struct designware_spi *dwspi, int on)
+{
+ writel(on ? 1 : 0, dwspi->regs + DWSPI_SSIENR);
+}
+
+static void designware_spi_chipselect(struct spi_device *spi, int on)
+{
+ struct designware_spi *dwspi = spi_master_get_devdata(spi->master);
+#if 0
+ long gpio = (long) spi->controller_data;
+ unsigned active = spi->mode & SPI_CS_HIGH;
+#endif
+ /*
+ * Note, the SPI controller must have been enabled at this point, i.e.
+ * SSIENR == 1
+ */
+
+ if (on) {
+ #if 0
+
+ /* Turn the actual chip select on for GPIO chip selects */
+ if (gpio >= 0)
+ gpio_set_value(gpio, active);
+ #endif
+ /* Activate slave on the SPI controller */
+ writel(1 << spi->chip_select, dwspi->regs + DWSPI_SER);
+ } else {
+ /* Deselect the slave on the SPI bus */
+ writel(0, dwspi->regs + DWSPI_SER);
+ #if 0
+ if (gpio >= 0)
+ gpio_set_value(gpio, !active);
+ #endif
+ }
+}
+
+static int designware_spi_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ u8 bits_per_word;
+ u32 hz;
+ struct designware_spi *dwspi = spi_master_get_devdata(spi->master);
+ u16 ctrlr0 = readw(dwspi->regs + DWSPI_CTRLR0);
+
+ bits_per_word = (t) ? (t->bits_per_word ? t->bits_per_word : spi->bits_per_word) : spi->bits_per_word;
+ hz = (t) ? (t->speed_hz ? t->speed_hz : spi->max_speed_hz) : spi->max_speed_hz;
+
+ if (bits_per_word < 4 || bits_per_word > 16) {
+ dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
+ __func__, bits_per_word);
+ return -EINVAL;
+ } else {
+ ctrlr0 &= ~DWSPI_CTRLR0_DFS_MASK;
+ ctrlr0 |= bits_per_word - 1;
+
+ dwspi->bits_per_word = bits_per_word;
+ }
+
+ /* Set the SPI clock phase and polarity */
+ if (spi->mode & SPI_CPHA)
+ ctrlr0 |= DWSPI_CTRLR0_SCPH;
+ else
+ ctrlr0 &= ~DWSPI_CTRLR0_SCPH;
+ if (spi->mode & SPI_CPOL)
+ ctrlr0 |= DWSPI_CTRLR0_SCPOL;
+ else
+ ctrlr0 &= ~DWSPI_CTRLR0_SCPOL;
+
+ writew(ctrlr0, dwspi->regs + DWSPI_CTRLR0);
+
+ /* set speed */
+ dwspi_baudcfg(dwspi, hz);
+
+ return 0;
+}
+
+/* the spi->mode bits currently understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+
+static int designware_spi_setup(struct spi_device *spi)
+{
+ struct designware_spi *dwspi;
+ int retval;
+
+ dwspi = spi_master_get_devdata(spi->master);
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ if (spi->mode & ~MODEBITS) {
+ dev_err(&spi->dev, "%s, SP unsupported mode bits %x\n",
+ __func__, spi->mode & ~MODEBITS);
+ return -EINVAL;
+ }
+
+ if (spi->chip_select > spi->master->num_chipselect) {
+ dev_err(&spi->dev,
+ "setup: invalid chipselect %u (%u defined)\n",
+ spi->chip_select, spi->master->num_chipselect);
+ return -EINVAL;
+ }
+
+ retval = designware_spi_setup_transfer(spi, NULL);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
+ __func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
+
+ return 0;
+}
+
+static void designware_spi_do_tx(struct designware_spi *dwspi)
+{
+ u8 sr;
+ int bytes_to_tx = dwspi->remaining_tx_bytes;
+ u8 valid_tx_fifo_bytes = readb(dwspi->regs + DWSPI_TXFLR);
+ u8 valid_rx_fifo_bytes = readb(dwspi->regs + DWSPI_RXFLR);
+ int tx_limit = min(dwspi->tx_fifo_depth - valid_tx_fifo_bytes,\
+ dwspi->rx_fifo_depth - valid_rx_fifo_bytes);
+
+ /* Fill the Tx FIFO with as many bytes as possible */
+ sr = readb(dwspi->regs + DWSPI_SR);
+ while ((sr & DWSPI_SR_TFNF_MASK) && dwspi->remaining_tx_bytes > 0) {
+ if (dwspi->bits_per_word <= 8) {
+ u8 dr = (dwspi->tx_ptr) ? *dwspi->tx_ptr++ : 0;
+
+ writeb(dr, dwspi->regs + DWSPI_DR);
+ dwspi->remaining_tx_bytes--;
+ } else {
+ u16 dr = (dwspi->tx_ptr) ? *(u16 *) dwspi->tx_ptr : 0;
+
+ dwspi->tx_ptr += 2;
+ writew(dr, dwspi->regs + DWSPI_DR);
+ dwspi->remaining_tx_bytes -= 2;
+ }
+
+ if(dwspi->bits_per_word <= 8){
+ --tx_limit;
+ }else{
+ tx_limit -= 2;
+ }
+
+ if (tx_limit <= 0)
+ break;
+
+ sr = readb(dwspi->regs + DWSPI_SR);
+ }
+
+ dwspi->tx_count += bytes_to_tx - dwspi->remaining_tx_bytes;
+}
+
+/* Return 1 when done, 0 otherwise */
+static int designware_spi_fill_tx_fifo(struct designware_spi *dwspi)
+{
+ unsigned cs_change = 0;
+ unsigned int ser;
+
+ ser = readw(dwspi->regs + DWSPI_SER);
+
+ list_for_each_entry_from(dwspi->tx_t, dwspi->transfers_list,
+ transfer_list) {
+ if (dwspi->remaining_tx_bytes == 0) {
+ /* Initialize new spi_transfer */
+ dwspi->tx_ptr = dwspi->tx_t->tx_buf;
+ dwspi->remaining_tx_bytes = dwspi->tx_t->len;
+ dwspi->status = 0;
+
+ if (!dwspi->tx_t->tx_buf && !dwspi->tx_t->rx_buf
+ && dwspi->tx_t->len) {
+ dwspi->status = -EINVAL;
+ break;
+ }
+
+ if (cs_change)
+ break;
+ }
+
+ designware_spi_do_tx(dwspi);
+
+ /* Don't advance dwspi->tx_t, we'll get back to this
+ * spi_transfer later
+ */
+ if (dwspi->remaining_tx_bytes > 0)
+ {
+ return 0;
+ }
+
+ cs_change = dwspi->tx_t->cs_change;
+ }
+
+ complete(&dwspi->done);
+
+ return 1;
+}
+
+static void designware_spi_do_rx(struct designware_spi *dwspi)
+{
+ u8 sr;
+ int bytes_to_rx = dwspi->remaining_rx_bytes;
+
+ sr = readb(dwspi->regs + DWSPI_SR);
+
+ if (sr & DWSPI_SR_RFF_MASK) {
+ #if 0
+ dev_err(dwspi->dev, "%s: RX FIFO overflow\n", __func__);
+ dwspi->status = -EIO;
+ #endif
+ }
+
+ /* Read as long as RX FIFO is not empty */
+ while ((sr & DWSPI_SR_RFNE_MASK) != 0
+ && dwspi->remaining_rx_bytes > 0) {
+ int rx_level = readl(dwspi->regs + DWSPI_RXFLR);
+
+ while (rx_level-- && dwspi->remaining_rx_bytes > 0) {
+ if (dwspi->bits_per_word <= 8) {
+ u8 data;
+
+ data = readb(dwspi->regs + DWSPI_DR);
+ dwspi->remaining_rx_bytes--;
+ if (dwspi->rx_ptr)
+ *dwspi->rx_ptr++ = data;
+ } else {
+ u16 data;
+
+ data = readw(dwspi->regs + DWSPI_DR);
+ dwspi->remaining_rx_bytes -= 2;
+ if (dwspi->rx_ptr) {
+ *(u16 *) dwspi->rx_ptr = data;
+ dwspi->rx_ptr += 2;
+ }
+ }
+ }
+ sr = readb(dwspi->regs + DWSPI_SR);
+ }
+
+ dwspi->rx_count += (bytes_to_rx - dwspi->remaining_rx_bytes);
+}
+
+/* return 1 if cs_change is true, 0 otherwise */
+static int designware_spi_read_rx_fifo(struct designware_spi *dwspi)
+{
+ unsigned cs_change = 0;
+ unsigned int ser;
+
+ ser = readw(dwspi->regs + DWSPI_SER);
+
+ list_for_each_entry_from(dwspi->rx_t, dwspi->transfers_list,
+ transfer_list) {
+ if (dwspi->remaining_rx_bytes == 0) {
+ dwspi->rx_ptr = dwspi->rx_t->rx_buf;
+ dwspi->remaining_rx_bytes = dwspi->rx_t->len;
+
+ if (cs_change)
+ return 1;
+ }
+
+ designware_spi_do_rx(dwspi);
+
+ /* The rx buffer is filling up with more bytes. Don't advance
+ * dwspi->rx_t, as we have more bytes to read in this
+ * spi_transfer.
+ */
+ if (dwspi->remaining_rx_bytes > 0)
+ return 0;
+
+ cs_change = dwspi->rx_t->cs_change;
+ }
+
+ return 0;
+}
+
+/* interate through the list of spi_transfer elements.
+ * stop at the end of the list or when t->cs_change is true.
+ */
+static void designware_spi_do_transfers(struct designware_spi *dwspi)
+{
+ int tx_done, cs_change;
+
+ init_completion(&dwspi->done);
+
+ /* transfer kickoff */
+ tx_done = designware_spi_fill_tx_fifo(dwspi);
+ designware_spi_chipselect(dwspi->spi, 1);
+
+ if (!tx_done) {
+ /* Enable the transmit empty interrupt, which we use to
+ * determine progress on the transmission in case we're
+ * not done yet.
+ */
+ writeb(DWSPI_IMR_TXEIM_MASK, dwspi->regs + DWSPI_IMR);
+
+ /* wait for tx completion */
+ wait_for_completion(&dwspi->done);
+ }
+
+ /* This delay should be good enough for 100KHz spi transfers. Slower
+ * transfers may need a longer delay.
+ */
+ udelay(10);
+
+ /* get remaining rx bytes */
+ do {
+ cs_change = designware_spi_read_rx_fifo(dwspi);
+ } while (readb(dwspi->regs + DWSPI_SR) &
+ (DWSPI_SR_BUSY_MASK | DWSPI_SR_RFNE_MASK));
+
+ /* transaction is done */
+ designware_spi_chipselect(dwspi->spi, 0);
+
+ if (dwspi->status < 0)
+ return;
+
+ if (!cs_change && (dwspi->remaining_rx_bytes > 0 ||
+ dwspi->remaining_tx_bytes > 0)) {
+ #if 0
+ dev_err(dwspi->dev, "%s: remaining_rx_bytes = %d, "
+ "remaining_tx_bytes = %d\n",
+ __func__, dwspi->remaining_rx_bytes,
+ dwspi->remaining_tx_bytes);
+ #endif
+ dwspi->status = -EIO;
+ }
+
+ if (dwspi->rx_count != dwspi->tx_count) {
+ #if 0
+ dev_err(dwspi->dev, "%s: rx_count == %d, tx_count == %d\n",
+ __func__, dwspi->rx_count, dwspi->tx_count);
+ #endif
+ dwspi->status = -EIO;
+ }
+}
+
+static int designware_spi_transfer(struct spi_device *spi,
+ struct spi_message *mesg)
+{
+ struct designware_spi *dwspi = spi_master_get_devdata(spi->master);
+
+ mesg->actual_length = 0;
+ mesg->status = -EINPROGRESS;
+
+ /* we can't block here, so we use a spinlock
+ * here instead of the global mutex
+ */
+ spin_lock(&dwspi->qlock);
+ list_add_tail(&mesg->queue, &dwspi->queue);
+ queue_work(dwspi->workqueue, &dwspi->work);
+ spin_unlock(&dwspi->qlock);
+
+ return 0;
+}
+
+static void designware_work(struct work_struct *work)
+{
+ struct designware_spi *dwspi = container_of(work,
+ struct designware_spi, work);
+
+ mutex_lock(&dwspi->lock);
+ spin_lock(&dwspi->qlock);
+
+ while (!list_empty(&dwspi->queue)) {
+ struct spi_message *m;
+
+ m = container_of(dwspi->queue.next, struct spi_message, queue);
+ list_del_init(&m->queue);
+ spin_unlock(&dwspi->qlock);
+
+ dwspi->spi = m->spi;
+ dwspi->tx_t = dwspi->rx_t =
+ list_first_entry(&m->transfers, struct spi_transfer,
+ transfer_list);
+
+ /*
+ * Interate through groups of spi_transfer structs
+ * that are separated by cs_change being true
+ */
+ dwspi->transfers_list = &m->transfers;
+ do {
+ dwspi->remaining_tx_bytes =
+ dwspi->remaining_rx_bytes = 0;
+ dwspi->tx_count = dwspi->rx_count = 0;
+ designware_spi_setup_transfer(m->spi, dwspi->tx_t);
+ dwspi_enable(dwspi, 1);
+ designware_spi_do_transfers(dwspi);
+ dwspi_enable(dwspi, 0);
+ if (dwspi->status < 0)
+ break;
+ m->actual_length +=
+ dwspi->tx_count; /* same as rx_count */
+ } while (&dwspi->tx_t->transfer_list != &m->transfers);
+
+ m->status = dwspi->status;
+ m->complete(m->context);
+ spin_lock(&dwspi->qlock);
+ }
+ spin_unlock(&dwspi->qlock);
+ mutex_unlock(&dwspi->lock);
+}
+
+static void designware_pump_transfers(unsigned long data)
+{
+ struct designware_spi *dwspi = (struct designware_spi *) data;
+
+ designware_spi_read_rx_fifo(dwspi);
+ if (!designware_spi_fill_tx_fifo(dwspi))
+ /* reenable the interrupt */
+ writeb(DWSPI_IMR_TXEIM_MASK, dwspi->regs + DWSPI_IMR);
+}
+
+static irqreturn_t designware_spi_irq(int irq, void *dev_id)
+{
+ struct designware_spi *dwspi = dev_id;
+
+ tasklet_schedule(&dwspi->pump_transfers);
+ /* disable the interrupt for now */
+ writeb(0, dwspi->regs + DWSPI_IMR);
+
+ return IRQ_HANDLED;
+}
+
+static void designware_spi_cleanup(struct spi_device *spi)
+{
+}
+
+static int __init designware_spi_probe(struct platform_device *dev)
+{
+ int ret = 0;
+ struct spi_master *master;
+ struct designware_spi *dwspi;
+ //struct designware_platform_data *pdata;
+ struct spi_controller_pdata *pdata;
+ struct resource *r;
+ struct clk *clk_spi;
+
+ pdata = dev->dev.platform_data;
+ if (pdata == NULL) {
+ dev_err(&dev->dev, "no device data specified\n");
+ return -EINVAL;
+ }
+
+ if(memcmp(pdata->clk_name, "DUS", 3))
+ c2000_block_reset(COMPONENT_AXI_LEGACY_SPI, 0);
+ else
+ c2000_block_reset(COMPONENT_AXI_FAST_SPI, 0);
+
+ clk_spi = clk_get(NULL,pdata->clk_name);
+ if (IS_ERR(clk_spi)) {
+ ret = PTR_ERR(clk_spi);
+ pr_err("%s:Unable to obtain spi clock: %d\n",\
+ __func__, ret);
+ goto err_clk;
+ }
+
+ clk_enable(clk_spi);
+
+ printk ("%s:Initializing SPI Controller : Using dma=%d CLK(%s)=%ld Hz\n", __func__, \
+ pdata->use_dma, pdata->clk_name, clk_get_rate(clk_spi));
+
+ /* Get resources(memory, IRQ) associated with the device */
+ master = spi_alloc_master(&dev->dev, sizeof(struct designware_spi));
+ if (master == NULL) {
+ ret = -ENOMEM;
+ goto err_nomem;
+ }
+
+ master->mode_bits = SPI_CPOL | SPI_CPHA;
+ master->bus_num = pdata->bus_num;
+ master->num_chipselect = pdata->num_chipselects;
+ master->setup = designware_spi_setup;
+ master->transfer = designware_spi_transfer;
+ master->cleanup = designware_spi_cleanup;
+ platform_set_drvdata(dev, master);
+
+ r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ ret = -ENODEV;
+ goto put_master;
+ }
+
+ dwspi = spi_master_get_devdata(master);
+ dwspi->clk_spi = clk_spi;
+ dwspi->ssi_clk = clk_get_rate(dwspi->clk_spi);
+ //dwspi->ssi_clk = pdata->max_freq;
+ dwspi->tx_fifo_depth = TX_FIFO_DEPTH;
+ dwspi->rx_fifo_depth = RX_FIFO_DEPTH;
+#if 0
+ dwspi->tx_fifo_depth = pdata->tx_fifo_depth;
+ dwspi->rx_fifo_depth = pdata->rx_fifo_depth;
+#endif
+ dwspi->dev = &dev->dev;
+ spin_lock_init(&dwspi->qlock);
+ mutex_init(&dwspi->lock);
+ INIT_LIST_HEAD(&dwspi->queue);
+ INIT_WORK(&dwspi->work, designware_work);
+ dwspi->workqueue =
+ create_singlethread_workqueue(dev_name(master->dev.parent));
+
+ if (dwspi->workqueue == NULL) {
+ ret = -EBUSY;
+ goto put_master;
+ }
+ tasklet_init(&dwspi->pump_transfers, designware_pump_transfers,
+ (unsigned long) dwspi);
+
+ if (!request_mem_region(r->start,
+ r->end - r->start + 1, DESIGNWARE_SPI_NAME)) {
+ ret = -ENXIO;
+ goto destroy_wq;
+ }
+
+ dwspi->regs = ioremap(r->start, r->end - r->start + 1);
+ if (dwspi->regs == NULL) {
+ ret = -ENOMEM;
+ goto destroy_wq;
+ }
+
+ dwspi->irq = platform_get_irq(dev, 0);
+ if (dwspi->irq < 0) {
+ ret = -ENXIO;
+ goto unmap_io;
+ }
+
+ /* SPI controller initializations */
+ dwspi_init_hw(dwspi);
+
+ /* Register for SPI Interrupt */
+ ret = request_irq(dwspi->irq, designware_spi_irq, 0,
+ DESIGNWARE_SPI_NAME, dwspi);
+ if (ret != 0)
+ goto unmap_io;
+
+ ret = spi_register_master(master);
+ if (ret < 0)
+ goto free_irq;
+
+ dev_info(&dev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
+ r->start, (u32)dwspi->regs, dwspi->irq);
+
+ return ret;
+
+free_irq:
+ free_irq(dwspi->irq, dwspi);
+unmap_io:
+ iounmap(dwspi->regs);
+destroy_wq:
+ destroy_workqueue(dwspi->workqueue);
+put_master:
+ spi_master_put(master);
+err_nomem:
+ clk_disable(clk_spi);
+ clk_put(clk_spi);
+err_clk:
+ if(memcmp(pdata->clk_name, "DUS", 3))
+ c2000_block_reset(COMPONENT_AXI_LEGACY_SPI, 1);
+ else
+ c2000_block_reset(COMPONENT_AXI_FAST_SPI, 1);
+
+ return ret;
+}
+
+static int __devexit designware_spi_remove(struct platform_device *dev)
+{
+ struct designware_spi *dwspi;
+ struct spi_master *master;
+ struct spi_controller_pdata *pdata;
+
+ master = platform_get_drvdata(dev);
+ dwspi = spi_master_get_devdata(master);
+
+ free_irq(dwspi->irq, dwspi);
+ iounmap(dwspi->regs);
+ destroy_workqueue(dwspi->workqueue);
+ tasklet_kill(&dwspi->pump_transfers);
+ platform_set_drvdata(dev, 0);
+ spi_master_put(master);
+ clk_disable(dwspi->clk_spi);
+ clk_put(dwspi->clk_spi);
+
+ pdata = dev->dev.platform_data;
+ if(!pdata)
+ {
+ return -EINVAL;
+ }
+
+ if(memcmp(pdata->clk_name, "DUS", 3))
+ c2000_block_reset(COMPONENT_AXI_LEGACY_SPI, 1);
+ else
+ c2000_block_reset(COMPONENT_AXI_FAST_SPI, 1);
+
+ return 0;
+}
+
+#if CONFIG_PM
+static int designware_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct designware_spi *dwspi;
+ struct spi_master *master;
+
+ master = platform_get_drvdata(pdev);
+ dwspi = spi_master_get_devdata(master);
+
+ /* Disable the SPI master */
+ writel(0, dwspi->regs + DWSPI_SSIENR);
+
+ clk_disable(dwspi->clk_spi);
+
+ return 0;
+
+}
+
+static int designware_spi_resume(struct platform_device *pdev)
+{
+ struct designware_spi *dwspi;
+ struct spi_master *master;
+
+ master = platform_get_drvdata(pdev);
+ dwspi = spi_master_get_devdata(master);
+
+ clk_enable(dwspi->clk_spi);
+ dwspi_init_hw(dwspi);
+
+ return 0;
+}
+#endif
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:" DESIGNWARE_SPI_NAME);
+
+static struct platform_driver designware_spi_driver = {
+ .probe = designware_spi_probe,
+ .remove = __devexit_p(designware_spi_remove),
+#if CONFIG_PM
+ .suspend = designware_spi_suspend,
+ .resume = designware_spi_resume,
+#endif
+ .driver = {
+ .name = DESIGNWARE_SPI_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init designware_spi_init(void)
+{
+ return platform_driver_register(&designware_spi_driver);
+}
+module_init(designware_spi_init);
+
+static void __exit designware_spi_exit(void)
+{
+ platform_driver_unregister(&designware_spi_driver);
+}
+module_exit(designware_spi_exit);
+
+MODULE_AUTHOR("Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@...org>");
+MODULE_DESCRIPTION("Synopsys DesignWare SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-c2000-dma.c b/drivers/spi/spi-c2000-dma.c
new file mode 100644
index 0000000..3292922
--- /dev/null
+++ b/drivers/spi/spi-c2000-dma.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2012, Mindspeed Technologies.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include "spi-dw.h"
+
+#include "spi-c2000-dma.h"
+
+struct spi_dma {
+ struct spi_dma_slave dmas_tx;
+ struct spi_dma_slave dmas_rx;
+};
+
+/* optional */
+static bool spi_dma_chan_filter(struct dma_chan *chan, void *param)
+{
+ struct dw_spi *dws = param;
+
+ return dws->master && (&dws->master->dev == chan->device->dev);
+}
+
+static int spi_dma_init(struct dw_spi *dws)
+{
+ struct spi_dma *dw_dma = dws->dma_priv;
+ struct spi_dma_slave *rxs, *txs;
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* 1. Init rx channel */
+ dws->rxchan = dma_request_channel(mask, spi_dma_chan_filter, dws);
+ if (!dws->rxchan)
+ goto err_exit;
+ rxs = &dw_dma->dmas_rx;
+ rxs->hs_mode = LNW_DMA_HW_HS;
+ rxs->cfg_mode = LNW_DMA_PER_TO_MEM;
+ dws->rxchan->private = rxs;
+
+ /* 2. Init tx channel */
+ dws->txchan = dma_request_channel(mask, spi_dma_chan_filter, dws);
+ if (!dws->txchan)
+ goto free_rxchan;
+ txs = &dw_dma->dmas_tx;
+ txs->hs_mode = LNW_DMA_HW_HS;
+ txs->cfg_mode = LNW_DMA_MEM_TO_PER;
+ dws->txchan->private = txs;
+
+ dws->dma_inited = 1;
+ return 0;
+
+free_rxchan:
+ dma_release_channel(dws->rxchan);
+err_exit:
+ return -1;
+
+}
+
+static void spi_dma_exit(struct dw_spi *dws)
+{
+ dma_release_channel(dws->txchan);
+ dma_release_channel(dws->rxchan);
+}
+
+/*
+ * dws->dma_chan_done is cleared before the dma transfer starts,
+ * callback for rx/tx channel will each increment it by 1.
+ * Reaching 2 means the whole spi transaction is done.
+ */
+static void dw_spi_dma_done(void *arg)
+{
+ struct dw_spi *dws = arg;
+
+ if (++dws->dma_chan_done != 2)
+ return;
+ dw_spi_xfer_done(dws);
+}
+
+static int spi_dma_transfer(struct dw_spi *dws, int cs_change)
+{
+ struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL;
+ struct dma_chan *txchan, *rxchan;
+ struct dma_slave_config txconf, rxconf;
+ u16 dma_ctrl = 0;
+
+ /* 1. setup DMA related registers */
+ if (cs_change) {
+ spi_enable_chip(dws, 0);
+ dw_writew(dws, DW_SPI_DMARDLR, 0xf);
+ dw_writew(dws, DW_SPI_DMATDLR, 0x10);
+ if (dws->tx_dma)
+ dma_ctrl |= 0x2;
+ if (dws->rx_dma)
+ dma_ctrl |= 0x1;
+ dw_writew(dws, DW_SPI_DMACR, dma_ctrl);
+ spi_enable_chip(dws, 1);
+ }
+
+ dws->dma_chan_done = 0;
+ txchan = dws->txchan;
+ rxchan = dws->rxchan;
+
+ /* 2. Prepare the TX dma transfer */
+ txconf.direction = DMA_TO_DEVICE;
+ txconf.dst_addr = dws->dma_addr;
+ txconf.dst_maxburst = LNW_DMA_MSIZE_16;
+ txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
+ (unsigned long) &txconf);
+
+ memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl));
+ dws->tx_sgl.dma_address = dws->tx_dma;
+ dws->tx_sgl.length = dws->len;
+
+ txdesc = txchan->device->device_prep_slave_sg(txchan,
+ &dws->tx_sgl,
+ 1,
+ DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
+ txdesc->callback = dw_spi_dma_done;
+ txdesc->callback_param = dws;
+
+ /* 3. Prepare the RX dma transfer */
+ rxconf.direction = DMA_FROM_DEVICE;
+ rxconf.src_addr = dws->dma_addr;
+ rxconf.src_maxburst = LNW_DMA_MSIZE_16;
+ rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
+ (unsigned long) &rxconf);
+
+ memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl));
+ dws->rx_sgl.dma_address = dws->rx_dma;
+ dws->rx_sgl.length = dws->len;
+
+ rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
+ &dws->rx_sgl,
+ 1,
+ DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
+ rxdesc->callback = dw_spi_dma_done;
+ rxdesc->callback_param = dws;
+
+ /* rx must be started before tx due to spi instinct */
+ rxdesc->tx_submit(rxdesc);
+ txdesc->tx_submit(txdesc);
+ return 0;
+}
+
+static struct dw_spi_dma_ops spi_dma_ops = {
+ .dma_init = spi_dma_init,
+ .dma_exit = spi_dma_exit,
+ .dma_transfer = spi_dma_transfer,
+};
+
+int dw_spi_dma_init(struct dw_spi *dws)
+{
+ dws->dma_priv = kzalloc(sizeof(struct spi_dma), GFP_KERNEL);
+ if (!dws->dma_priv)
+ return -ENOMEM;
+
+ dws->dma_ops = &spi_dma_ops;
+
+ return 0;
+}
diff --git a/drivers/spi/spi-c2000-dma.h b/drivers/spi/spi-c2000-dma.h
new file mode 100644
index 0000000..751daea
--- /dev/null
+++ b/drivers/spi/spi-c2000-dma.h
@@ -0,0 +1,74 @@
+/*
+ * spi-c2000-dma.h
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#ifndef __SPI_C2000_DMA_H__
+#define __SPI_C2000_DMA_H___
+
+#include <linux/dmaengine.h>
+
+#define DMA_PREP_CIRCULAR_LIST (1 << 10)
+
+/*DMA mode configurations*/
+enum spi_dma_mode {
+ LNW_DMA_PER_TO_MEM = 0, /*periphral to memory configuration*/
+ LNW_DMA_MEM_TO_PER, /*memory to periphral configuration*/
+ LNW_DMA_MEM_TO_MEM, /*mem to mem confg (testing only)*/
+};
+
+/*DMA handshaking*/
+enum spi_dma_hs_mode {
+ LNW_DMA_HW_HS = 0, /*HW Handshaking only*/
+ LNW_DMA_SW_HS = 1, /*SW Handshaking not recommended*/
+};
+
+/*Burst size configuration*/
+enum spi_dma_msize {
+ LNW_DMA_MSIZE_1 = 0x0,
+ LNW_DMA_MSIZE_4 = 0x1,
+ LNW_DMA_MSIZE_8 = 0x2,
+ LNW_DMA_MSIZE_16 = 0x3,
+ LNW_DMA_MSIZE_32 = 0x4,
+ LNW_DMA_MSIZE_64 = 0x5,
+};
+
+/**
+ * struct spi_dma_slave - DMA slave structure
+ *
+ * @dirn: DMA trf direction
+ * @src_width: tx register width
+ * @dst_width: rx register width
+ * @hs_mode: HW/SW handshaking mode
+ * @cfg_mode: DMA data transfer mode (per-per/mem-per/mem-mem)
+ * @src_msize: Source DMA burst size
+ * @dst_msize: Dst DMA burst size
+ * @per_addr: Periphral address
+ * @device_instance: DMA peripheral device instance, we can have multiple
+ * peripheral device connected to single DMAC
+ */
+struct spi_dma_slave {
+ enum spi_dma_hs_mode hs_mode; /*handshaking*/
+ enum spi_dma_mode cfg_mode; /*mode configuration*/
+ unsigned int device_instance; /*0, 1 for periphral instance*/
+ struct dma_slave_config dma_slave;
+};
+
+#endif /*__SPI_C2000_DMA_H__*/
diff --git a/drivers/spi/spi-c2000.c b/drivers/spi/spi-c2000.c
new file mode 100644
index 0000000..c970a3d
--- /dev/null
+++ b/drivers/spi/spi-c2000.c
@@ -0,0 +1,214 @@
+/*
+ * Memory-mapped interface driver for DW SPI Core
+ * Author: Satendra Pratap <satendra.pratap@gmail.com>
+ * Copyright (c) 2012, Mindspeed Technologies.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/scatterlist.h>
+#include <linux/module.h>
+#include <mach/reset.h>
+
+#include "spi-dw.h"
+
+#define DRIVER_NAME "comcerto_spi"
+
+extern int dw_spi_dma_init(struct dw_spi *dws);
+
+#define CLK_NAME 10
+struct spi_controller_pdata {
+ int use_dma;
+ int num_chipselects;
+ int bus_num;
+ u32 max_freq;
+ char clk_name[CLK_NAME];
+};
+
+struct dw_spi_c2000 {
+ struct dw_spi dws;
+};
+
+static int __devinit dw_spi_c2000_probe(struct platform_device *pdev)
+{
+ struct dw_spi_c2000 *dwsmmio;
+ struct dw_spi *dws;
+ struct resource *mem, *ioarea;
+ struct spi_controller_pdata *pdata;
+ struct clk *clk_spi;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if(!pdata)
+ {
+ ret = -EINVAL;
+ goto err_end;
+ }
+
+ dwsmmio = kzalloc(sizeof(struct dw_spi_c2000), GFP_KERNEL);
+ if (!dwsmmio) {
+ ret = -ENOMEM;
+ goto err_end;
+ }
+
+ dws = &dwsmmio->dws;
+
+ clk_spi = clk_get(NULL,pdata->clk_name);
+ if (IS_ERR(clk_spi)) {
+ ret = PTR_ERR(clk_spi);
+ pr_err("%s:Unable to obtain spi clock: %d\n",\
+ __func__, ret);
+ goto err_kfree;
+ }
+ dws->clk_spi=clk_spi;
+
+ dws->max_freq = clk_get_rate(dws->clk_spi);
+
+ printk ("%s:Initializing SPI Controller : use_dma=%d CLK(%s)=%d Hz\n", __func__, \
+ pdata->use_dma, pdata->clk_name, dws->max_freq);
+
+ if(memcmp(pdata->clk_name, "DUS", 3))
+ c2000_block_reset(COMPONENT_AXI_LEGACY_SPI, 0);
+ else
+ c2000_block_reset(COMPONENT_AXI_FAST_SPI, 0);
+
+ if(pdata->use_dma) /* DMA */
+ {
+ ret = dw_spi_dma_init(dws);
+ if (ret)
+ goto err_clk;
+ }
+
+ dws->bus_num = pdata->bus_num;
+ dws->num_cs = pdata->num_chipselects;
+
+ /* Get basic io resource and map it */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ ret = -EINVAL;
+ goto err_clk;
+ }
+
+ ioarea = request_mem_region(mem->start, resource_size(mem),
+ pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "SPI region already claimed\n");
+ ret = -EBUSY;
+ goto err_clk;
+ }
+
+ dws->regs = ioremap_nocache(mem->start, resource_size(mem));
+ if (!dws->regs) {
+ dev_err(&pdev->dev, "SPI region already mapped\n");
+ ret = -ENOMEM;
+ goto err_release_reg;
+ }
+
+ dws->irq = platform_get_irq(pdev, 0);
+ if (dws->irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ ret = dws->irq; /* -ENXIO */
+ goto err_unmap;
+ }
+
+ dws->parent_dev = &pdev->dev;
+
+ ret = dw_spi_add_host(dws);
+ if (ret)
+ goto err_unmap;
+
+ platform_set_drvdata(pdev, dwsmmio);
+ return 0;
+
+err_unmap:
+ iounmap(dws->regs);
+err_release_reg:
+ release_mem_region(mem->start, resource_size(mem));
+err_clk:
+ if(memcmp(pdata->clk_name, "DUS", 3))
+ c2000_block_reset(COMPONENT_AXI_LEGACY_SPI, 1);
+ else
+ c2000_block_reset(COMPONENT_AXI_FAST_SPI, 1);
+
+ clk_put(dws->clk_spi);
+err_kfree:
+ kfree(dwsmmio);
+err_end:
+ return ret;
+}
+
+static int __devexit dw_spi_c2000_remove(struct platform_device *pdev)
+{
+ struct dw_spi_c2000 *dwsmmio = platform_get_drvdata(pdev);
+ struct spi_controller_pdata *pdata;
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+
+ dw_spi_remove_host(&dwsmmio->dws);
+ iounmap(dwsmmio->dws.regs);
+
+ clk_put(dwsmmio->dws.clk_spi);
+ kfree(dwsmmio);
+
+ pdata = pdev->dev.platform_data;
+ if(!pdata)
+ {
+ return -EINVAL;
+ }
+
+ if(memcmp(pdata->clk_name, "DUS", 3))
+ c2000_block_reset(COMPONENT_AXI_LEGACY_SPI, 1);
+ else
+ c2000_block_reset(COMPONENT_AXI_FAST_SPI, 1);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, resource_size(mem));
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int c2000_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct dw_spi_c2000 *dwsmmio = platform_get_drvdata(pdev);
+ struct dw_spi *dws = &dwsmmio->dws;
+
+ return dw_spi_suspend_host(dws);
+}
+
+static int c2000_spi_resume(struct platform_device *pdev)
+{
+ struct dw_spi_c2000 *dwsmmio = platform_get_drvdata(pdev);
+ struct dw_spi *dws = &dwsmmio->dws;
+
+ return dw_spi_resume_host(dws);
+}
+#endif
+
+static struct platform_driver dw_spi_c2000_driver = {
+ .probe = dw_spi_c2000_probe,
+ .remove = __devexit_p(dw_spi_c2000_remove),
+#ifdef CONFIG_PM
+ .suspend = c2000_spi_suspend,
+ .resume = c2000_spi_resume,
+#endif
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(dw_spi_c2000_driver);
+
+MODULE_AUTHOR("Satendra Pratap <satendra.pratap@gmail.com>");
+MODULE_DESCRIPTION("Memory-mapped I/O interface driver for DW SPI Core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 082458d..2c0806b 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -24,6 +24,12 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
+#include <linux/clk.h>
+
+#ifdef CONFIG_C2K_DEVFREQ_DW
+ #include <linux/c2k-devfreq.h>
+ #include <linux/devfreq.h>
+#endif
#include "spi-dw.h"
@@ -62,6 +68,48 @@
void (*cs_control)(u32 command);
};
+#ifdef CONFIG_C2K_DEVFREQ_DW
+static devfreq_counters dc;
+
+static int set_spi_freq(struct c2k_devfreq_data *data, unsigned long *freq)
+{
+ struct spi_device *spi = container_of(data->dev, struct spi_device, dev);
+ struct dw_spi *dws = container_of(&spi, struct dw_spi, cur_dev);
+ struct chip_data *chip;
+ u32 clk_div;
+
+ /* Only alloc on first setup */
+ chip = spi_get_ctldata(spi);
+
+ if (!chip)
+ {
+ chip = spi_get_ctldata(spi);
+ if (!chip) {
+ chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ }
+ }
+
+ if ((*freq <= data->max_freq) && (*freq >= data->min_freq))
+ {
+ chip->speed_hz = (u32)*freq;
+ clk_div = dws->max_freq / chip->speed_hz;
+ clk_div = (clk_div + 1) & 0xfffe;
+
+ chip->clk_div = clk_div;
+ spi_set_clk(dws, chip->clk_div);
+ }
+ else
+ {
+ printk (KERN_ERR "%s: Trying to set out of range spi freq: %lu\n\
+ ", __func__, *freq);
+ }
+
+ return 0;
+}
+#endif
+
#ifdef CONFIG_DEBUG_FS
static int spi_show_regs_open(struct inode *inode, struct file *file)
{
@@ -394,6 +442,10 @@
u32 speed = 0;
u32 cr0 = 0;
+#ifdef CONFIG_C2K_DEVFREQ_DW
+ devfreq_func_start(&dc);
+#endif
+
/* Get current state information */
message = dws->cur_msg;
transfer = dws->cur_transfer;
@@ -548,6 +600,10 @@
if (chip->poll_mode)
poll_transfer(dws);
+#ifdef CONFIG_C2K_DEVFREQ_DW
+ devfreq_func_end(&dc);
+#endif
+
return;
early_exit:
@@ -598,6 +654,9 @@
{
struct dw_spi *dws = spi_master_get_devdata(spi->master);
unsigned long flags;
+#ifdef CONFIG_C2K_DEVFREQ_DW
+ devfreq_func_start(&dc);
+#endif
spin_lock_irqsave(&dws->lock, flags);
@@ -626,6 +685,9 @@
}
spin_unlock_irqrestore(&dws->lock, flags);
+#ifdef CONFIG_C2K_DEVFREQ_DW
+ devfreq_func_end(&dc);
+#endif
return 0;
}
@@ -801,6 +863,49 @@
}
}
+#ifdef CONFIG_C2K_DEVFREQ_DW
+#define SPI_MAXFREQ_MHZ 2000000
+#define SPI_MINFREQ_MHZ 1000000
+#define POLLING_MS 1000
+#define OPP_TABLE_SIZE 4
+
+struct devfreq_dev_profile spi_devfreq_profile;
+struct c2k_devfreq_data devfreq_spi_data;
+static struct c2k_devfreq_opp_table spi_opp_tbl[OPP_TABLE_SIZE];
+
+/*
+ * intialize OPP table, profile data (initial freq, polling interval),
+ * max/min freq supported by SPI controller.
+ */
+static void init_spi_devfreq_data(struct dw_spi *dws)
+{
+ int i = 0;
+ struct c2k_devfreq_opp_table opp_tbl[OPP_TABLE_SIZE] = {
+ {1, 1000000, 0},
+ {2, 2000000, 0},
+ {3, 4000000, 0},
+ {0, 0, 0},
+ };
+
+ while (i < sizeof(opp_tbl))
+ {
+ spi_opp_tbl[i].idx = opp_tbl[i].idx;
+ spi_opp_tbl[i].freq = opp_tbl[i].freq;
+ spi_opp_tbl[i].volt = opp_tbl[i].volt;
+ i++;
+ }
+
+ spi_devfreq_profile.initial_freq = dws->max_freq;
+ spi_devfreq_profile.polling_ms = POLLING_MS;
+
+ devfreq_spi_data.devfreq_profile = &spi_devfreq_profile;
+ devfreq_spi_data.opp_table = &spi_opp_tbl[0];
+ devfreq_spi_data.set_freq = set_spi_freq;
+ devfreq_spi_data.max_freq = SPI_MAXFREQ_MHZ;
+ devfreq_spi_data.min_freq = SPI_MINFREQ_MHZ;
+}
+#endif
+
int __devinit dw_spi_add_host(struct dw_spi *dws)
{
struct spi_master *master;
@@ -814,6 +919,8 @@
goto exit;
}
+ clk_enable(dws->clk_spi);
+
dws->master = master;
dws->type = SSI_MOTO_SPI;
dws->prev_chip = NULL;
@@ -867,6 +974,14 @@
}
mrst_spi_debugfs_init(dws);
+
+#ifdef CONFIG_C2K_DEVFREQ_DW
+ init_spi_devfreq_data(dws);
+
+ ret = c2k_driver_devfreq(&master->dev, &devfreq_spi_data);
+ if(ret < 0)
+ goto err_queue_alloc;
+#endif
return 0;
err_queue_alloc:
@@ -877,6 +992,7 @@
spi_enable_chip(dws, 0);
free_irq(dws->irq, dws);
err_free_master:
+ clk_disable(dws->clk_spi);
spi_master_put(master);
exit:
return ret;
@@ -900,12 +1016,12 @@
if (dws->dma_ops && dws->dma_ops->dma_exit)
dws->dma_ops->dma_exit(dws);
spi_enable_chip(dws, 0);
- /* Disable clk */
- spi_set_clk(dws, 0);
free_irq(dws->irq, dws);
/* Disconnect from the SPI framework */
spi_unregister_master(dws->master);
+ /* Disable clk */
+ clk_disable(dws->clk_spi);
}
EXPORT_SYMBOL_GPL(dw_spi_remove_host);
@@ -917,7 +1033,9 @@
if (ret)
return ret;
spi_enable_chip(dws, 0);
- spi_set_clk(dws, 0);
+
+ clk_disable(dws->clk_spi);
+
return ret;
}
EXPORT_SYMBOL_GPL(dw_spi_suspend_host);
@@ -926,6 +1044,7 @@
{
int ret;
+ clk_enable(dws->clk_spi);
spi_hw_init(dws);
ret = start_queue(dws);
if (ret)
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 9c57c07..b4bd29b 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -92,6 +92,7 @@
struct dw_spi {
struct spi_master *master;
struct spi_device *cur_dev;
+ struct clk *clk_spi;
struct device *parent_dev;
enum dw_ssi_type type;
char name[16];
diff --git a/drivers/spi2/Kconfig b/drivers/spi2/Kconfig
new file mode 100644
index 0000000..cab6011
--- /dev/null
+++ b/drivers/spi2/Kconfig
@@ -0,0 +1,15 @@
+#
+# Character device configuration
+#
+
+menu "SPI2 support"
+ depends on (ARCH_COMCERTO)
+config SPI2
+ tristate "Mindspeed SPI support"
+ help
+ Mindspeed SPI (Serial Pheripheral Interface bus) core support
+
+source drivers/spi2/busses/Kconfig
+
+endmenu
+
diff --git a/drivers/spi2/Makefile b/drivers/spi2/Makefile
new file mode 100644
index 0000000..84b51f2
--- /dev/null
+++ b/drivers/spi2/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the spi core.
+#
+
+obj-$(CONFIG_SPI2) += spi-core.o
+obj-y += busses/
+
diff --git a/drivers/spi2/busses/Kconfig b/drivers/spi2/busses/Kconfig
new file mode 100644
index 0000000..e13f420
--- /dev/null
+++ b/drivers/spi2/busses/Kconfig
@@ -0,0 +1,15 @@
+
+menu "SPI Hardware Bus support"
+ depends on SPI2
+
+config SPI2_MSPD_LOW_SPEED
+ tristate "Comcerto Low-speed"
+ depends on SPI2
+ default y
+
+config SPI2_MSPD_HIGH_SPEED
+ tristate "Comcerto High-speed"
+ depends on SPI2
+ default y
+
+endmenu
diff --git a/drivers/spi2/busses/Makefile b/drivers/spi2/busses/Makefile
new file mode 100644
index 0000000..de53bd2
--- /dev/null
+++ b/drivers/spi2/busses/Makefile
@@ -0,0 +1,15 @@
+SPI2_MSPD=n
+ifeq ($(CONFIG_SPI2_MSPD_HIGH_SPEED),m)
+ SPI2_MSPD=m
+endif
+ifeq ($(CONFIG_SPI2_MSPD_LOW_SPEED),m)
+ SPI2_MSPD=m
+endif
+ifeq ($(CONFIG_SPI2_MSPD_HIGH_SPEED),y)
+ SPI2_MSPD=y
+endif
+ifeq ($(CONFIG_SPI2_MSPD_LOW_SPEED),y)
+ SPI2_MSPD=y
+endif
+
+obj-$(SPI2_MSPD) += comcerto_spi.o
diff --git a/drivers/spi2/busses/comcerto_spi.c b/drivers/spi2/busses/comcerto_spi.c
new file mode 100644
index 0000000..d8e2d6f
--- /dev/null
+++ b/drivers/spi2/busses/comcerto_spi.c
@@ -0,0 +1,772 @@
+/*
+ * drivers/spi2/busses/comcerto_spi.c
+ *
+ * Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/version.h>
+#if !defined (AUTOCONF_INCLUDED)
+#if 0
+ #include <linux/config.h>
+#endif
+#endif
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include <mach/irqs.h>
+#include <linux/delay.h>
+
+#include <linux/platform_device.h>
+#include "comcerto_spi.h"
+
+/**/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+/**/
+
+/**
+ * do_write_read_transfer8 -
+ *
+ *
+ */
+static int do_write_read_transfer8(struct comcerto_spi *spi, u8 *wbuf, unsigned int *wlen, u8 *rbuf, unsigned int *rlen, u32 ser_reg)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int wtmp = *wlen, rtmp = *rlen;
+ u32 dr = spi->membase + COMCERTO_SPI_DR;
+ u32 txflr = spi->membase + COMCERTO_SPI_TXFLR;
+ u32 rxflr = spi->membase + COMCERTO_SPI_RXFLR;
+ int ser_done = 0;
+
+// printk(KERN_INFO "do_write_read_transfer(%#lx, %#lx, %d, %#lx, %d)\n", (unsigned long)spi,
+// (unsigned long)wbuf, *wlen,
+// (unsigned long)rbuf, *rlen);
+
+ while (wtmp || rtmp) {
+ len_now = 8 - __raw_readl(txflr);
+ if (len_now > wtmp)
+ len_now = wtmp;
+
+ wtmp -= len_now;
+
+ /* warm-up write fifo to avoid underruns */
+ while (len_now--)
+ __raw_writew(cpu_to_le16((u16) *wbuf++), dr);
+
+ if (!ser_done){
+ __raw_writel(ser_reg, spi->membase + COMCERTO_SPI_SER);
+ ser_done = 1;
+ }
+
+ len_now = __raw_readl(rxflr);
+ if (len_now > rtmp)
+ len_now = rtmp;
+
+ rtmp -= len_now;
+
+ while (len_now--) {
+ *rbuf = (u8) (le16_to_cpu(__raw_readw(dr)) & 0xff);
+ rbuf++;
+ }
+ }
+
+ *rlen -= rtmp;
+ *wlen -= wtmp;
+
+ return rc;
+}
+
+/**
+ * do_write_read_transfer16 -
+ *
+ */
+static int do_write_read_transfer16(struct comcerto_spi *spi, u16 *wbuf, unsigned int *wlen, u16 *rbuf, unsigned int *rlen, u32 ser_reg)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int wtmp = *wlen, rtmp = *rlen;
+ unsigned int wpadding, rpadding;
+ u32 dr = spi->membase + COMCERTO_SPI_DR;
+ u32 txflr = spi->membase + COMCERTO_SPI_TXFLR;
+ u32 rxflr = spi->membase + COMCERTO_SPI_RXFLR;
+ int ser_done = 0;
+
+// printk(KERN_INFO "do_write_read_transfer(%#lx, %#lx, %d, %#lx, %d)\n", (unsigned long)spi
+// (unsigned long)wbuf, *wlen,
+// (unsigned long)rbuf, *rlen);
+
+ if (wtmp > rtmp) {
+ wpadding = 0;
+ rpadding = wtmp - rtmp;
+ } else {
+ wpadding = rtmp - wtmp;
+ rpadding = 0;
+ }
+
+ while (wtmp || rtmp) {
+ len_now = 8 - __raw_readl(txflr);
+
+ if (wtmp) {
+ if (len_now > wtmp)
+ len_now = wtmp;
+
+ wtmp -= len_now;
+
+ while (len_now--)
+ __raw_writew(cpu_to_le16(*wbuf++), dr);
+
+ } else if (wpadding) {
+ if (len_now > wpadding)
+ len_now = wpadding;
+
+ wpadding -= len_now;
+
+ while (len_now--)
+ __raw_writew(0, dr);
+ }
+
+ if (!ser_done){
+ __raw_writel(ser_reg, spi->membase + COMCERTO_SPI_SER);
+ ser_done = 1;
+ }
+
+ len_now = __raw_readl(rxflr);
+ if (rtmp) {
+ if (len_now > rtmp)
+ len_now = rtmp;
+
+ rtmp -= len_now;
+
+ while (len_now--) {
+ *rbuf = le16_to_cpu(__raw_readw(dr));
+ rbuf++;
+ }
+ } else if (rpadding) {
+ if (len_now > rpadding)
+ len_now = rpadding;
+
+ rpadding -= len_now;
+
+ while (len_now--)
+ __raw_readw(dr);
+ }
+ }
+
+ *rlen -= rtmp;
+ *wlen -= wtmp;
+
+ return rc;
+}
+
+
+/**
+ * do_write_only_transfer8 -
+ *
+ *
+ */
+static int do_write_only_transfer8(struct comcerto_spi *spi, u8 *buf, unsigned int *len, u32 ser_reg)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int tmp = *len;
+ u32 dr = spi->membase + COMCERTO_SPI_DR;
+ u32 txflr = spi->membase + COMCERTO_SPI_TXFLR;
+ int ser_done = 0;
+
+// printk(KERN_INFO "do_write_only_transfer8(%#lx, %#lx, %d)\n", (unsigned long)spi, (unsigned long)buf, *len);
+
+ while (tmp) {
+ len_now = 8 - __raw_readl(txflr);
+ if (len_now > tmp)
+ len_now = tmp;
+
+ tmp -= len_now;
+
+ while (len_now--)
+ __raw_writew(cpu_to_le16((u16) *buf++), dr);
+
+ if (!ser_done)
+ {
+ __raw_writel(ser_reg, spi->membase + COMCERTO_SPI_SER);
+ ser_done = 1;
+ }
+ }
+
+ *len -= tmp;
+
+// printk(KERN_INFO "exit do_write_only_transfer(%d, %d)\n", *len, rc);
+
+ return rc;
+}
+
+/**
+ * do_write_only_transfer -
+ *
+ *
+ */
+static int do_write_only_transfer16(struct comcerto_spi *spi, u16 *buf, unsigned int *len, u32 ser_reg)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int tmp = *len;
+ u32 dr = spi->membase + COMCERTO_SPI_DR;
+ u32 txflr = spi->membase + COMCERTO_SPI_TXFLR;
+ int ser_done = 0;
+
+// printk(KERN_INFO "do_write_only_transfer(%#lx, %#lx, %d)\n", (unsigned long)spi, (unsigned long)buf, *len);
+
+ while (tmp) {
+ len_now = 8 - __raw_readl(txflr);
+ if (len_now > tmp)
+ len_now = tmp;
+
+ tmp -= len_now;
+
+ while (len_now--)
+ __raw_writew(cpu_to_le16(*buf++), dr);
+
+ if (!ser_done)
+ {
+ __raw_writel(ser_reg, spi->membase + COMCERTO_SPI_SER);
+ ser_done = 1;
+ }
+ }
+
+ *len -= tmp;
+
+// printk(KERN_INFO "exit do_write_only_transfer(%d, %d)\n", *len, rc);
+
+ return rc;
+}
+
+
+/**
+ * do_read_only_transfer -
+ *
+ *
+ */
+static int do_read_only_transfer8(struct comcerto_spi *spi, u8 *buf, unsigned int *len)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int tmp = *len;
+ u32 dr = spi->membase + COMCERTO_SPI_DR;
+ u32 rxflr = spi->membase + COMCERTO_SPI_RXFLR;
+
+// printk(KERN_INFO "do_read_only_transfer8(%#lx, %#lx, %d)\n", (unsigned long)spi, (unsigned long)buf, *len);
+
+ /* start the serial clock */
+ __raw_writew(0, dr);
+
+ while (tmp) {
+ len_now = __raw_readl(rxflr);
+ if (len_now > tmp)
+ len_now = tmp;
+
+ tmp -= len_now;
+
+ while (len_now--) {
+ *buf = (u8) (le16_to_cpu(__raw_readw(dr)) & 0xff);
+ buf++;
+ }
+ }
+
+ *len -= tmp;
+
+ return rc;
+}
+
+/**
+ * do_read_only_transfer -
+ *
+ *
+ */
+static int do_read_only_transfer16(struct comcerto_spi *spi, u16 *buf, unsigned int *len)
+{
+ unsigned int len_now;
+ int rc = 0;
+ unsigned int tmp = *len;
+ u32 dr = spi->membase + COMCERTO_SPI_DR;
+ u32 rxflr = spi->membase + COMCERTO_SPI_RXFLR;
+
+// printk(KERN_INFO "do_read_only_transfer(%#lx, %#lx, %d)\n", (unsigned long)spi, (unsigned long)buf, *len);
+
+ /* start the serial clock */
+ __raw_writew(0, dr);
+
+ while (tmp) {
+ len_now = __raw_readl(rxflr);
+ if (len_now > tmp)
+ len_now = tmp;
+
+ tmp -= len_now;
+
+ while (len_now--) {
+ *buf = le16_to_cpu(__raw_readw(dr));
+ buf++;
+ }
+ }
+
+ *len -= tmp;
+
+ return rc;
+}
+
+
+/**
+ * comcerto_spi_do_transfer -
+ *
+ *
+ */
+static int comcerto_spi_do_transfer(struct spi_adapter *adapter, struct spi_transfer *transfer, struct spi_client_config *config)
+{
+ struct comcerto_spi *spi = (struct comcerto_spi *)adapter->data;
+ u32 ctrlr0, ctrlr1, baudr, ser;
+ int rc;
+
+// printk(KERN_INFO "comcerto_spi_do_transfer(%#lx, %#lx, %#lx)\n", (unsigned long) adapter, (unsigned long) transfer, (unsigned long) config);
+
+ /* make sure last transaction is finished */
+ while (__raw_readl(spi->membase + COMCERTO_SPI_SR) & BUSY) ;
+
+ if (config->ba_delay)
+ udelay(config->ba_delay);
+
+ ctrlr0 = ((config->sc_polarity & 0x1) << 7) | ((config->sc_phase & 0x1) << 6) | (((transfer->fs - 1) & 0xf) << 0);
+
+ baudr = spi->clock_rate / config->sc_rate;
+
+ ser = config->cs_msk & adapter->caps.cs_msk;
+
+ __raw_writel(0, spi->membase + COMCERTO_SPI_SSIENR);
+
+ switch (transfer->mode & 0x0f) {
+ default:
+ rc = -1;
+ break;
+
+ case SPI_TRANSFER_MODE_WRITE_ONLY:
+ ctrlr0 |= (0x0001 << 8);
+
+ __raw_writel(ctrlr0, spi->membase + COMCERTO_SPI_CTRLR0);
+ __raw_writel(baudr, spi->membase + COMCERTO_SPI_BAUDR);
+ //__raw_writel(ser, spi->membase + COMCERTO_SPI_SER);
+ __raw_writel(8, spi->membase + COMCERTO_SPI_RXFTLR);
+ __raw_writel(0, spi->membase + COMCERTO_SPI_TXFTLR);
+ __raw_writel(0, spi->membase + COMCERTO_SPI_IMR);
+ __raw_writel(1, spi->membase + COMCERTO_SPI_SSIENR);
+
+ if (transfer->fs <= 8)
+ rc = do_write_only_transfer8(spi, transfer->wbuf, &transfer->wlen, ser);
+ else
+ rc = do_write_only_transfer16(spi, (u16 *) transfer->wbuf, &transfer->wlen, ser);
+
+ break;
+
+ case SPI_TRANSFER_MODE_READ_ONLY:
+ ctrlr0 |= (0x0002 << 8);
+ ctrlr1 = transfer->rlen - 1;
+
+ __raw_writel(ctrlr0, spi->membase + COMCERTO_SPI_CTRLR0);
+ __raw_writel(ctrlr1, spi->membase + COMCERTO_SPI_CTRLR1);
+ __raw_writel(baudr, spi->membase + COMCERTO_SPI_BAUDR);
+ __raw_writel(ser, spi->membase + COMCERTO_SPI_SER);
+ __raw_writel(8, spi->membase + COMCERTO_SPI_RXFTLR);
+ __raw_writel(0, spi->membase + COMCERTO_SPI_TXFTLR);
+ __raw_writel(0, spi->membase + COMCERTO_SPI_IMR);
+ __raw_writel(1, spi->membase + COMCERTO_SPI_SSIENR);
+
+ if (transfer->fs <= 8)
+ rc = do_read_only_transfer8(spi, transfer->rbuf, &transfer->rlen);
+ else
+ rc = do_read_only_transfer16(spi, (u16 *) transfer->rbuf, &transfer->rlen);
+
+ break;
+
+ case SPI_TRANSFER_MODE_WRITE_READ:
+ ctrlr0 |= (0x0000 << 8);
+
+ __raw_writel(ctrlr0, spi->membase + COMCERTO_SPI_CTRLR0);
+ __raw_writel(baudr, spi->membase + COMCERTO_SPI_BAUDR);
+ // __raw_writel(ser, spi->membase + COMCERTO_SPI_SER);
+ __raw_writel(8, spi->membase + COMCERTO_SPI_RXFTLR);
+ __raw_writel(0, spi->membase + COMCERTO_SPI_TXFTLR);
+ __raw_writel(0, spi->membase + COMCERTO_SPI_IMR);
+ __raw_writel(1, spi->membase + COMCERTO_SPI_SSIENR);
+
+ if (transfer->fs <= 8)
+ rc = do_write_read_transfer8(spi, transfer->wbuf, &transfer->wlen, transfer->rbuf, &transfer->rlen, ser);
+ else
+ rc = do_write_read_transfer16(spi, (u16 *) transfer->wbuf, &transfer->wlen, (u16 *) transfer->rbuf, &transfer->rlen, ser);
+
+ break;
+ }
+
+ if (config->ba_delay) {
+ udelay(config->ba_delay);
+ /* make sure this transaction is finished */
+ while (__raw_readl(spi->membase + COMCERTO_SPI_SR) & BUSY) ;
+ }
+
+ __raw_writel(0, spi->membase + COMCERTO_SPI_SER);
+
+ return rc;
+}
+
+#if 0
+/**
+ * comcerto_spi_irq_handler -
+ *
+ *
+ */
+irqreturn_t comcerto_spi_irq_handler(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct comcerto_spi *spi = (struct comcerto_spi *)dev_id;
+ struct spi_adapter *adapter = spi->adapter;
+ u32 callback_status = 0;
+ u32 status;
+ u32 imr;
+ irqreturn_t ret = IRQ_NONE;
+
+ printk(KERN_INFO "comcerto_spi_irq_handler(%d, %#lx, %#lx)\n", irq, (unsigned long)dev_id, (unsigned long)regs);
+
+ status = readl(spi->membase + COMCERTO_SPI_ISR);
+ if (!status)
+ goto out;
+
+ printk(KERN_INFO "status %x\n", status);
+
+ ret = IRQ_HANDLED;
+
+ if (status & TXEIS) {
+ callback_status |= SPI_WRITE_DONE;
+
+ printk(KERN_INFO "%x %x\n", readl(spi->membase + COMCERTO_SPI_SR), readl(spi->membase + COMCERTO_SPI_TXFLR));
+
+ /* disable fifo empty interrupt */
+ imr = readl(spi->membase + COMCERTO_SPI_IMR) & ~(TXEIM);
+ writel(imr, spi->membase + COMCERTO_SPI_IMR);
+ }
+
+ if (status & TXOIS) {
+ callback_status |= SPI_WRITE_ERROR;
+ }
+
+ if (status & RXUIS) {
+ callback_status |= SPI_READ_ERROR;
+ }
+
+ if (status & RXOIS) {
+ callback_status |= SPI_READ_ERROR;
+ }
+
+ if (status & RXFIS) {
+ callback_status |= SPI_DATA_AVAILABLE;
+ }
+
+ spi_callback(adapter, callback_status);
+
+ /* clear all interrupts */
+ readl(spi->membase + COMCERTO_SPI_ICR);
+
+ out:
+ return ret;
+}
+#endif
+
+/**
+ * comcerto_spi_hw_init -
+ *
+ *
+ */
+static void comcerto_spi_hw_init(struct comcerto_spi *spi)
+{
+// printk(KERN_INFO "comcerto_spi_hw_init(%#lx)\n", (unsigned long)spi);
+
+#ifndef CONFIG_ARCH_M83XXX
+ /* enable SPI bus: not needed for c2k */
+ //comcerto_gpio_ctrl(0x3 << 4, 0x3 << 4);
+#endif
+
+ /* disable SPI operation */
+ writel(0, spi->membase + COMCERTO_SPI_SSIENR);
+
+ /* mask all SPI irq's */
+ writel(0, spi->membase + COMCERTO_SPI_IMR);
+}
+
+/**
+ * comcerto_spi_hw_reset -
+ *
+ *
+ */
+static void comcerto_spi_hw_reset(struct comcerto_spi *spi)
+{
+ /* disable SPI operation */
+ writel(0, spi->membase + COMCERTO_SPI_SSIENR);
+
+ /* mask all SPI irq's */
+ writel(0, spi->membase + COMCERTO_SPI_IMR);
+
+#ifndef CONFIG_ARCH_M83XXX
+ /* disable SPI bus: not needed for c2k */
+ //comcerto_gpio_ctrl(0x0 << 4, 0x3 << 4);
+#endif
+}
+#if 0
+struct spi_adapter comcerto_spi_adapter = {
+ .name = "comcerto_spi",
+ .do_transfer = comcerto_spi_do_transfer,
+};
+#endif
+
+#define SPI2_DRV_NAME_LEN 15
+char spi2_drv_name[SPI2_DRV_NAME_LEN] = "comcerto_spi";
+
+/**
+* comcerto_spi_probe -
+ *
+ *
+ */
+static int __init comcerto_spi_probe(struct platform_device *pdev)
+{
+ struct comcerto_spi *spi;
+ struct spi_adapter *ladapter;
+ unsigned long base, len;
+
+// printk(KERN_INFO "comcerto_spi_probe(%#lx)\n", (unsigned long) pdev);
+ printk(KERN_INFO "%s: comcerto_spi_probe(%#lx)\
+ \npdev->resource[0].start=0x%x\
+ \npdev->resource[0].end=0x%x\
+ \npdev->name=%s\
+ \npdev->id=%d\n", __func__, (unsigned long) pdev, \
+ pdev->resource[0].start, pdev->resource[0].end, pdev->name, pdev->id);
+
+
+ spi = kmalloc(sizeof(struct comcerto_spi), GFP_KERNEL);
+ if (spi == NULL) {
+ printk(KERN_INFO "comcerto_spi: error allocating memory");
+ goto err0;
+ }
+
+ ladapter = kmalloc(sizeof(struct spi_adapter), GFP_KERNEL);
+ if (ladapter == NULL) {
+ printk(KERN_INFO "%s: Error allocating memory: ladapter=0x%x", __func__,\
+ (unsigned int)ladapter);
+ goto err1;
+ }
+
+ base = pdev->resource[0].start;
+ len = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+ if (!request_mem_region(base, len, COMCERTO_SPI_DRIVER_NAME)) {
+ printk(KERN_INFO "comcerto_spi: error requesting memory region %#lx - %#lx", base, base + len);
+ goto err2;
+ }
+
+ /* io-remaped in arch/arm/mm.c */
+ if(pdev->id == 0)
+ spi->membase = APB_VADDR(pdev->resource[0].start);
+ else
+ if(pdev->id == 1)
+ spi->membase = AXI_VADDR(pdev->resource[0].start);
+ else
+ {
+ printk (KERN_INFO "%s: No support for pdev->id = %d\n",\
+ __func__, pdev->id);
+ goto err3;
+ }
+
+ printk (KERN_INFO "%s: pdev->id=%d spi->membase=0x%x\n", __func__,\
+ pdev->id, (unsigned int)spi->membase);
+ //spi->membase = ioremap(pdev->resource[0].start, len);
+ spi->irq = pdev->resource[1].start;
+
+ //pratapc pwr_mgmt_clk_restore(COMPONENT_SPI);
+
+ comcerto_spi_hw_init(spi);
+#if 0
+ if (request_irq(spi->irq, comcerto_spi_irq_handler, SA_SHIRQ, COMCERTO_SPI_DRIVER_NAME, spi)) {
+ printk(KERN_INFO "comcerto_spi: error requesting irq %d\n", IRQ_SPI);
+ goto err2;
+ }
+#endif
+ spi->adapter = ladapter;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ ladapter->dev.parent = &pdev->dev;
+#endif
+ ladapter->name = spi2_drv_name;
+ ladapter->do_transfer = comcerto_spi_do_transfer;
+ ladapter->data = spi;
+ ladapter->caps.max_sc_rate = COMCERTO_DEFAULTAXICLK / 2;
+ ladapter->caps.min_sc_rate = COMCERTO_DEFAULTAXICLK / 0xffff;
+ ladapter->caps.max_fs = 16;
+ ladapter->caps.min_fs = 4;
+ ladapter->caps.max_nframe = 0xffff;
+ ladapter->caps.min_nframe = 1;
+ ladapter->caps.cs_msk = 0xf;
+ ladapter->bus_num = pdev->id;
+
+ printk ("%s: ladapter->bus_num=0x%x\n", __func__,\
+ ladapter->bus_num);
+
+ if (spi_add_adapter(ladapter)) {
+ printk(KERN_INFO "%s:error adding adapter\n", __func__);
+ goto err3;
+ }
+
+ spi->clock_rate = COMCERTO_DEFAULTAXICLK;
+ platform_set_drvdata(pdev, spi);
+
+ return 0;
+
+err3:
+#if 0
+ free_irq(spi->irq, spi);
+
+ err2:
+#endif
+ release_mem_region(base, len);
+err2:
+ kfree(ladapter);
+
+err1:
+ kfree(spi);
+
+ err0:
+ return -1;
+}
+
+/**
+ * comcerto_spi_remove -
+ *
+ *
+ */
+static int comcerto_spi_remove(struct platform_device *pdev)
+{
+ struct comcerto_spi *spi = platform_get_drvdata(pdev);
+ unsigned long base, len;
+
+ platform_set_drvdata(pdev, NULL);
+
+ spi_del_adapter(spi->adapter);
+
+ comcerto_spi_hw_reset(spi);
+
+ //pratapc pwr_mgmt_clk_down(COMPONENT_SPI);
+
+// free_irq(spi->irq, spi);
+
+ base = pdev->resource[0].start;
+ len = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+ release_mem_region(base, len);
+
+ kfree(spi->adapter);
+ kfree(spi);
+
+ return 0;
+}
+
+/* FIXME: we are not supporting Power management in SPI-2 driver */
+#if 0
+#ifdef CONFIG_PM
+static int spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret;
+ struct spi_client *client;
+ struct list_head *item, *_n;
+ struct comcerto_spi *spi = platform_get_drvdata(pdev);
+ state.event = PM_EVENT_SUSPEND;
+ list_for_each_safe(item, _n, &spi->adapter->clients) {
+ client = list_entry(item, struct spi_client, list);
+ if(client->driver->suspend != NULL)
+ ret = client->driver->suspend(client,state);
+ }
+
+ if(ret == 0)
+ pwr_mgmt_clk_down(COMPONENT_SPI);
+
+ return 0;
+}
+
+static int spi_resume(struct platform_device *pdev)
+{
+ struct spi_client *client;
+ struct list_head *item, *_n;
+ struct comcerto_spi *spi = platform_get_drvdata(pdev);
+
+ pwr_mgmt_clk_restore(COMPONENT_SPI);
+
+ list_for_each_safe(item, _n, &spi->adapter->clients) {
+ client = list_entry(item, struct spi_client, list);
+ if(client->driver->resume != NULL)
+ client->driver->resume(client);
+ }
+
+ return 0;
+}
+#endif
+#endif
+
+static struct platform_driver comcerto_spi_driver = {
+ .probe = comcerto_spi_probe,
+ .remove = comcerto_spi_remove,
+#if 0
+#ifdef CONFIG_PM
+ .suspend = spi_suspend,
+ .resume = spi_resume,
+#endif
+#endif
+ .driver = {
+ .name = "comcerto_spi",
+ },
+};
+
+/**
+ * comcerto_spi_init -
+ *
+ *
+ */
+static int __init comcerto_spi_init(void)
+{
+// printk(KERN_INFO "comcerto_spi_init()\n");
+
+ return platform_driver_register(&comcerto_spi_driver);
+}
+
+/**
+ * comcerto_spi_exit -
+ *
+ *
+ */
+static void __exit comcerto_spi_exit(void)
+{
+ platform_driver_unregister(&comcerto_spi_driver);
+}
+
+MODULE_AUTHOR("Mindspeed Technologies, Inc.");
+MODULE_DESCRIPTION("Comcerto SPI bus driver");
+MODULE_LICENSE("GPL");
+
+module_init(comcerto_spi_init);
+module_exit(comcerto_spi_exit);
diff --git a/drivers/spi2/busses/comcerto_spi.h b/drivers/spi2/busses/comcerto_spi.h
new file mode 100644
index 0000000..38ef6c2b
--- /dev/null
+++ b/drivers/spi2/busses/comcerto_spi.h
@@ -0,0 +1,99 @@
+/*
+ * linux/drivers/spi2/busses/comcerto_spi.h
+ *
+ * Copyright (C) Mindspeed Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#ifndef _COMCERTO_SPI_H
+#define _COMCERTO_SPI_H
+
+#include <linux/version.h>
+#include <linux/spi2/spi.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <linux/ioport.h>
+
+struct platform_device {
+ char *name;
+ u32 id;
+ u32 num_resources;
+ struct resource *resource;
+
+ unsigned long data;
+};
+#endif
+
+#define COMCERTO_SPI_DRIVER_NAME "Comcerto SPI"
+
+
+#define COMCERTO_SPI_CTRLR0 0x00
+#define COMCERTO_SPI_CTRLR1 0x04
+#define COMCERTO_SPI_SSIENR 0x08
+#define COMCERTO_SPI_MWCR 0x0C
+#define COMCERTO_SPI_SER 0x10
+#define COMCERTO_SPI_BAUDR 0x14
+#define COMCERTO_SPI_TXFTLR 0x18
+#define COMCERTO_SPI_RXFTLR 0x1C
+#define COMCERTO_SPI_TXFLR 0x20
+#define COMCERTO_SPI_RXFLR 0x24
+#define COMCERTO_SPI_SR 0x28
+#define COMCERTO_SPI_IMR 0x2C
+#define COMCERTO_SPI_ISR 0x30
+#define COMCERTO_SPI_RISR 0x34
+#define COMCERTO_SPI_TXOICR 0x38
+#define COMCERTO_SPI_RXOICR 0x3C
+#define COMCERTO_SPI_RXUICR 0x40
+#define COMCERTO_SPI_MSTICR 0x44
+#define COMCERTO_SPI_ICR 0x48
+#define COMCERTO_SPI_IDR 0x58
+#define COMCERTO_SPI_DR 0x60
+
+
+/* SR - status register bits */
+#define BUSY (1<<0) /* SSI busy flag, serial transfer in progress */
+#define TFNF (1<<1) /* Transmit FIFO not full */
+#define TFE (1<<2) /* Transmit FIFO empty */
+#define RFNE (1<<3) /* Receive FIFO not empty */
+#define RFF (1<<4) /* Receive FIFO full */
+#define TXE (1<<5) /* Transmission error */
+#define DCOL (1<<6) /* Data collision error */
+
+/* Interrupt status after being masked */
+#define TXEIS (1<<0) /* Transmit FIFO empty interrupt status */
+#define TXOIS (1<<1) /* Transmit FIFO overflow interrupt status */
+#define RXUIS (1<<2) /* Receive FIFO underflow interrupt status */
+#define RXOIS (1<<3) /* Receive FIFO overflow interrupt status */
+#define RXFIS (1<<4) /* Receive FIFO full interrupt status */
+#define MSTIS (1<<5) /* Multi-Master contention interrupt status */
+
+/* Interrupt status before being masked */
+#define TXEIR (1<<0) /* Transmit FIFO empty interrupt status */
+#define TXOIR (1<<1) /* Transmit FIFO overflow interrupt status */
+#define RXUIR (1<<2) /* Receive FIFO underflow interrupt status */
+#define RXOIR (1<<3) /* Receive FIFO overflow interrupt status */
+#define RXFIR (1<<4) /* Receive FIFO full interrupt status */
+#define MSTIR (1<<5) /* Multi-Master contention interrupt status */
+
+
+/* Interrupt mask register */
+#define TXEIM (1<<0) /* Transmit FIFO empty interrupt status */
+#define TXOIM (1<<1) /* Transmit FIFO overflow interrupt status */
+#define RXUIM (1<<2) /* Receive FIFO underflow interrupt status */
+#define RXOIM (1<<3) /* Receive FIFO overflow interrupt status */
+#define RXFIM (1<<4) /* Receive FIFO full interrupt status */
+#define MSTIM (1<<5) /* Multi-Master contention interrupt status */
+
+struct comcerto_spi
+{
+ struct spi_adapter *adapter;
+ unsigned long membase;
+ int irq;
+ unsigned long clock_rate;
+};
+
+
+#endif /* _COMCERTO_SPI_H */
diff --git a/drivers/spi2/spi-core.c b/drivers/spi2/spi-core.c
new file mode 100644
index 0000000..9b17c08
--- /dev/null
+++ b/drivers/spi2/spi-core.c
@@ -0,0 +1,523 @@
+/*
+ * linux/drivers/spi2/spi-core.c
+ *
+ * Copyright (C) 2006 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#if !defined (AUTOCONF_INCLUDED)
+#if 0
+ #include <linux/config.h>
+#endif
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <asm/delay.h>
+
+#include <linux/spi2/spi.h>
+
+static LIST_HEAD(spi_adapters);
+static LIST_HEAD(spi_drivers);
+
+/**
+ * spi_write_mem -
+ *
+ *
+ */
+int spi_write_mem(struct spi_client *client, u8 fs, u8 *buffer, int len)
+{
+ struct spi_transfer transfer;
+ struct spi_adapter *adapter = client->adapter;
+ unsigned long flags;
+ int rc;
+
+ memset(&transfer, 0, sizeof (struct spi_transfer));
+
+ transfer.fs = fs;
+ transfer.mode = SPI_TRANSFER_MODE_WRITE_ONLY;
+ transfer.wbuf = buffer;
+ transfer.wlen = len;
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ rc = adapter->do_transfer(adapter, &transfer, &client->config);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+
+ /* deassert the chip select at least for this long */
+ udelay (1 + ((1000000 * client->config.cs_delay) / client->config.sc_rate));
+
+ return rc;
+}
+
+
+/**
+ * spi_write_single -
+ *
+ *
+ */
+int spi_write_single(struct spi_client *client, u8 fs, u16 value)
+{
+ return spi_write_mem(client, fs, (u8 *)&value, 1);
+}
+
+/**
+ * spi_writen -
+ *
+ *
+ */
+int spi_writen(struct spi_client *client, u4 value)
+{
+ return spi_write_mem(client, 4, (u8 *)&value, 1);
+}
+
+
+/**
+ * spi_writeb -
+ *
+ *
+ */
+int spi_writeb(struct spi_client *client, u8 value)
+{
+ return spi_write_mem(client, 8, &value, 1);
+}
+
+
+/**
+ * spi_writew -
+ *
+ *
+ */
+int spi_writew(struct spi_client *client, u16 value)
+{
+ return spi_write_mem(client, 16, (u8 *)&value, 1);
+}
+
+/**
+ * spi_read_mem -
+ *
+ *
+ */
+int spi_read_mem(struct spi_client *client, u8 fs, u8 *buffer, int len)
+{
+ struct spi_transfer transfer;
+ struct spi_adapter *adapter = client->adapter;
+ unsigned long flags;
+ int rc;
+
+ memset(&transfer, 0, sizeof (struct spi_transfer));
+
+ transfer.fs = fs;
+ transfer.mode = SPI_TRANSFER_MODE_READ_ONLY;
+ transfer.rbuf = buffer;
+ transfer.rlen = len;
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ rc = adapter->do_transfer(adapter, &transfer, &client->config);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+
+ /* deassert the chip select at least for this long */
+ udelay (1 + ((1000000 * client->config.cs_delay) / client->config.sc_rate));
+
+ return rc;
+}
+
+
+/**
+ * spi_read_single -
+ *
+ *
+ */
+int spi_read_single(struct spi_client *client, u8 fs, u16 *value)
+{
+ return spi_read_mem(client, fs, (u8 *) value, 1);
+}
+
+/**
+ * spi_readn -
+ *
+ *
+ */
+int spi_readn(struct spi_client *client, u4 *value)
+{
+ return spi_read_mem(client, 4, (u8 *)value, 1);
+}
+
+
+/**
+ * spi_readb -
+ *
+ *
+ */
+int spi_readb(struct spi_client *client, u8 *value)
+{
+ return spi_read_mem(client, 8, value, 1);
+}
+
+
+/**
+ * spi_readw -
+ *
+ *
+ */
+int spi_readw(struct spi_client *client, u16 *value)
+{
+ return spi_read_mem(client, 16, (u8 *)value, 1);
+}
+
+
+/**
+ * spi_read_mem -
+ *
+ *
+ */
+int spi_writeread_mem(struct spi_client *client, u8 fs, u8 *rbuffer, int rlen, u8 *wbuffer, int wlen)
+{
+ struct spi_transfer transfer;
+ struct spi_adapter *adapter = client->adapter;
+ unsigned long flags;
+ int rc;
+
+ memset(&transfer, 0, sizeof (struct spi_transfer));
+
+ transfer.fs = fs;
+ transfer.mode = SPI_TRANSFER_MODE_WRITE_READ;
+ transfer.rbuf = rbuffer;
+ transfer.rlen = rlen;
+ transfer.wbuf = wbuffer;
+ transfer.wlen = wlen;
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ rc = adapter->do_transfer(adapter, &transfer, &client->config);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+
+ /* deassert the chip select at least for this long */
+ udelay (1 + ((1000000 * client->config.cs_delay) / client->config.sc_rate));
+
+ return rc;
+}
+/**
+ * spi_add_adapter -
+ *
+ *
+ */
+int spi_add_adapter(struct spi_adapter *adapter)
+{
+ struct spi_driver *driver;
+ struct list_head *item;
+
+ printk(KERN_INFO "SPI core: add adapter %s\n", adapter->name);
+
+#ifdef _USE_DRIVER_MODEL_
+ sprintf(adapter->dev.bus_id, "spi-%d", adapter->nr);
+ adapter->dev.driver = &spi_adapter_driver;
+ adapter->dev.release = &spi_adapter_dev_release;
+ device_register(&adapter->dev);
+#endif
+
+ list_add(&adapter->list, &spi_adapters);
+ INIT_LIST_HEAD(&adapter->clients);
+
+ //adapter->lock = SPIN_LOCK_UNLOCKED;
+ //Above method is obsolete and new is used below.
+ spin_lock_init(&adapter->lock);
+
+ list_for_each(item, &spi_drivers) {
+ driver = list_entry(item, struct spi_driver, list);
+ printk ("%s: Matching: adapter->bus_num=0x%x driver->bus_id 0x%x\n", __func__,\
+ adapter->bus_num, driver->bus_id);
+ if (driver->bus_id == adapter->bus_num)
+ {
+ printk ("%s: Found adapter 0x%x \n", __func__, adapter->bus_num);
+ /* We ignore the return code; if it fails, too bad */
+ driver->attach_adapter(adapter);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * spi_del_adapter -
+ *
+ *
+ */
+int spi_del_adapter(struct spi_adapter *adapter)
+{
+ struct spi_client *client;
+ struct list_head *item, *_n;
+
+ printk(KERN_INFO "SPI core: del adapter %s\n", adapter->name);
+
+ list_for_each_safe(item, _n, &adapter->clients) {
+ client = list_entry(item, struct spi_client, list);
+
+ if (client->driver->detach_client(client))
+ goto out;
+ }
+
+ list_del(&adapter->list);
+
+#ifdef _USE_DRIVER_MODEL_
+ device_unregister(&adapter->dev);
+#endif
+
+ out:
+ return 0;
+}
+
+/**
+ * spi_attach_client -
+ *
+ *
+ */
+int spi_attach_client(struct spi_client *client)
+{
+ struct spi_adapter *adapter = client->adapter;
+ struct spi_client_config *config = &client->config;
+ struct spi_adapter_caps *caps = &adapter->caps;
+
+ printk(KERN_INFO "SPI core: attach client to adapter %s\n", client->adapter->name);
+
+ if ((config->sc_rate >= caps->max_sc_rate) || (config->sc_rate < caps->min_sc_rate)) {
+ printk(KERN_INFO "SPI core: client serial clock rate %ld out of range [%ld, %ld]", config->sc_rate,
+ caps->min_sc_rate, caps->max_sc_rate);
+
+ goto err;
+ }
+
+ if (config->cs_msk & ~caps->cs_msk) {
+ printk(KERN_INFO "SPI core: client cs mask %#x not supported %#x", config->cs_msk,
+ caps->cs_msk);
+
+ goto err;
+ }
+
+#ifdef _USE_DRIVER_MODEL_
+ device_register(&client->device);
+#endif
+ list_add(&client->list, &adapter->clients);
+
+ return 0;
+
+ err:
+ return -1;
+}
+
+/**
+ * spi_dettach_client -
+ *
+ *
+ */
+int spi_detach_client(struct spi_client *client)
+{
+ printk(KERN_INFO "SPI core: client detach from adapter %s\n", client->adapter->name);
+
+ list_del(&client->list);
+
+#ifdef _USE_DRIVER_MODEL_
+ device_unregister(&client->dev);
+#endif
+ return 0;
+}
+
+/**
+ * spi_add_driver -
+ *
+ *
+ */
+int spi_add_driver(struct spi_driver *driver)
+{
+ struct spi_adapter *adapter;
+ struct list_head *item;
+
+ printk(KERN_INFO "SPI core: add driver %s\n", driver->name);
+
+#ifdef _USE_DRIVER_MODEL_
+ driver->driver.name = driver->name;
+ driver->driver.bus = &spi2_bus_type;
+ driver->driver.probe = spi_device_probe;
+ driver->driver.remove = spi_device_remove;
+
+ if (driver_register(&driver->driver))
+ goto err;
+#endif
+ list_add(&driver->list, &spi_drivers);
+
+ list_for_each(item, &spi_adapters) {
+ adapter = list_entry(item, struct spi_adapter, list);
+ printk ("%s: Checking: adapter bus_num=0x%x driver bus_id=0x%x\n",\
+ __func__, adapter->bus_num, driver->bus_id);
+ if (driver->bus_id == adapter->bus_num)
+ {
+ printk ("%s: Found adapter 0x%x\n", __func__, adapter->bus_num);
+ driver->attach_adapter(adapter);
+ break;
+ }
+ }
+
+ return 0;
+
+#ifdef _USE_DRIVER_MODEL_
+ err:
+ return -1;
+#endif
+}
+
+/**
+ * spi_del_driver -
+ *
+ *
+ */
+int spi_del_driver(struct spi_driver *driver)
+{
+ struct list_head *item1, *item2, *_n;
+ struct spi_adapter *adapter;
+ struct spi_client *client;
+
+ printk(KERN_INFO "SPI core: delete driver %s\n", driver->name);
+
+ list_for_each(item1, &spi_adapters) {
+ adapter = list_entry(item1, struct spi_adapter, list);
+
+ list_for_each_safe(item2, _n, &adapter->clients) {
+ client = list_entry(item2, struct spi_client, list);
+ if (client->driver != driver)
+ continue;
+
+ if (driver->detach_client(client))
+ goto err;
+ }
+ }
+#ifdef _USE_DRIVER_MODEL_
+ driver_unregister(&driver->driver);
+#endif
+ list_del(&driver->list);
+
+ return 0;
+
+ err:
+ return -1;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+/* match always succeeds, as we want the probe() to tell if we really accept this match */
+static int spi2_device_match(struct device *dev, struct device_driver *drv)
+{
+ return 1;
+}
+
+struct bus_type spi2_bus_type = {
+ .name = "spi2",
+ .match = spi2_device_match,
+};
+
+/**
+ * spi_driver_init -
+ *
+ *
+ */
+static int __init spi_driver_init(void)
+{
+ int retval;
+
+ printk(KERN_INFO "SPI core: loaded version 0.2\n");
+
+ retval = bus_register(&spi2_bus_type);
+ if (retval)
+ goto err0;
+
+#ifdef _USE_DRIVER_MODEL_
+ retval = driver_register(&spi_driver);
+ if (retval)
+ goto err1;
+#endif
+
+ return 0;
+
+#ifdef _USE_DRIVER_MODEL_
+ err1:
+ bus_unregister(&spi2_bus_type);
+#endif
+
+ err0:
+ return retval;
+}
+
+/**
+ * spi_driver_exit -
+ *
+ *
+ */
+static void __exit spi_driver_exit(void)
+{
+#ifdef _USE_DRIVER_MODEL_
+ driver_unregister(&spi_driver);
+#endif
+ bus_unregister(&spi2_bus_type);
+}
+
+subsys_initcall(spi_driver_init);
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
+
+static int __init spi_driver_init(void)
+{
+ printk(KERN_INFO "SPI core: loaded version 0.2\n");
+
+ return 0;
+}
+
+static void __exit spi_driver_exit(void)
+{
+
+}
+
+module_init(spi_driver_init);
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
+
+module_exit(spi_driver_exit);
+
+EXPORT_SYMBOL(spi_add_driver);
+EXPORT_SYMBOL(spi_del_driver);
+
+EXPORT_SYMBOL(spi_add_adapter);
+EXPORT_SYMBOL(spi_del_adapter);
+
+EXPORT_SYMBOL(spi_attach_client);
+EXPORT_SYMBOL(spi_detach_client);
+
+EXPORT_SYMBOL(spi_write_single);
+EXPORT_SYMBOL(spi_write_mem);
+
+EXPORT_SYMBOL(spi_writen);
+EXPORT_SYMBOL(spi_writeb);
+EXPORT_SYMBOL(spi_writew);
+
+EXPORT_SYMBOL(spi_read_mem);
+EXPORT_SYMBOL(spi_read_single);
+EXPORT_SYMBOL(spi_writeread_mem);
+
+EXPORT_SYMBOL(spi_readn);
+EXPORT_SYMBOL(spi_readb);
+EXPORT_SYMBOL(spi_readw);
+
+MODULE_AUTHOR("Rui Sousa <rui.sousa@mindspeed.com>");
+MODULE_DESCRIPTION("SPI core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 830cd62..bfb5415 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -1,6 +1,6 @@
config VT
bool "Virtual terminal" if EXPERT
- depends on !S390 && !UML
+ depends on !S390 && !UML && !ARCH_COMCERTO
select INPUT
default y
---help---
diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c
index 70585b6..ad90014 100644
--- a/drivers/tty/serial/8250.c
+++ b/drivers/tty/serial/8250.c
@@ -48,6 +48,12 @@
#include "suncore.h"
#endif
+#ifdef CONFIG_ARCH_M86XXX
+#include <linux/clk.h>
+#include <mach/reset.h>
+static struct clk *uart_clk; /*UART Clock(DUS) depends upon the AXI*/
+#endif
+
/*
* Configuration:
* share_irqs - whether we pass IRQF_SHARED to request_irq(). This option
@@ -420,10 +426,36 @@
return readb(p->membase + offset);
}
+#ifdef CONFIG_ARCH_M86XXX
+void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p);
+
+void dw8250_force_idle(struct uart_port *p)
+{
+ struct uart_8250_port *up = container_of(p, struct uart_8250_port, port);
+
+ serial8250_clear_and_reinit_fifos(up);
+ (void)p->serial_in(p, UART_RX);
+}
+#endif
+
static void mem_serial_out(struct uart_port *p, int offset, int value)
{
- offset = map_8250_out_reg(p, offset) << p->regshift;
- writeb(value, p->membase + offset);
+ int offset1 = map_8250_out_reg(p, offset) << p->regshift;
+ writeb(value, p->membase + offset1);
+
+#ifdef CONFIG_ARCH_M86XXX
+ /* Make sure LCR write wasn't ignored */
+ if (offset == UART_LCR) {
+ int tries = 1000;
+ while (tries--) {
+ if (value == p->serial_in(p, UART_LCR))
+ return;
+ dw8250_force_idle(p);
+ writeb(value, p->membase + offset1);
+ }
+ dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+ }
+#endif
}
static void mem32_serial_out(struct uart_port *p, int offset, int value)
@@ -616,6 +648,17 @@
}
}
+#ifdef CONFIG_ARCH_M86XXX
+void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p)
+{
+ unsigned char fcr;
+
+ serial8250_clear_fifos(p);
+ fcr = uart_config[p->port.type].fcr;
+ serial_outp(p, UART_FCR, fcr);
+}
+#endif
+
/*
* IER sleep support. UARTs which have EFRs need the "extended
* capability" bit enabled. Note that on XR16C850s, we need to
@@ -2342,11 +2385,17 @@
* have sufficient FIFO entries for the latency of the remote
* UART to respond. IOW, at least 32 bytes of FIFO.
*/
+#ifdef CONFIG_ARCH_M86XXX
+ up->mcr &= ~UART_MCR_AFE;
+ if (termios->c_cflag & CRTSCTS)
+ up->mcr |= UART_MCR_AFE;
+#else
if (up->capabilities & UART_CAP_AFE && up->port.fifosize >= 32) {
up->mcr &= ~UART_MCR_AFE;
if (termios->c_cflag & CRTSCTS)
up->mcr |= UART_MCR_AFE;
}
+#endif
/*
* Ok, we're now changing the port state. Do it with
@@ -3055,6 +3104,31 @@
struct uart_port port;
int ret, i, irqflag = 0;
+#ifdef CONFIG_ARCH_M86XXX
+ unsigned long uart_rate;
+
+ /* Take the Fast-UART device Out-Of-Reset*/
+ c2000_block_reset(COMPONENT_AXI_FAST_UART,0);
+
+ /* Get the FAST-UART clk structure from DUS */
+ uart_clk = clk_get(NULL,"DUS");
+
+ if (IS_ERR(uart_clk)) {
+ pr_err("%s: Unable to get UART clock: %ld\n",__func__,PTR_ERR(uart_clk));
+ return PTR_ERR(uart_clk);
+ }
+
+ /* Enable the FAST-UART Clock */
+ ret = clk_enable(uart_clk);
+ if (ret){
+ pr_err("%s: UART clock failed to enable:\n",__func__);
+ return ret;
+ }
+
+ /* Get the UART Clock in Hz */
+ uart_rate = clk_get_rate(uart_clk);
+#endif
+
memset(&port, 0, sizeof(struct uart_port));
if (share_irqs)
@@ -3065,7 +3139,11 @@
port.membase = p->membase;
port.irq = p->irq;
port.irqflags = p->irqflags;
- port.uartclk = p->uartclk;
+#ifdef CONFIG_ARCH_M86XXX
+ port.uartclk = uart_rate; /* Assigning the rate value to the ports */
+#else
+ port.uartclk = p->uartclk; /* Assigning the rate value got from Platfrom device*/
+#endif
port.regshift = p->regshift;
port.iotype = p->iotype;
port.flags = p->flags;
@@ -3088,6 +3166,7 @@
p->irq, ret);
}
}
+
return 0;
}
@@ -3104,9 +3183,19 @@
if (up->port.dev == &dev->dev)
serial8250_unregister_port(i);
}
+#ifdef CONFIG_ARCH_M86XXX
+ /*Disable the Fast-UART clock here*/
+ clk_disable(uart_clk);
+ clk_put(uart_clk);
+
+ /* Put the Fast-UART device in Reset*/
+ c2000_block_reset(COMPONENT_AXI_FAST_UART,1);
+#endif
+
return 0;
}
+#if CONFIG_PM
static int serial8250_suspend(struct platform_device *dev, pm_message_t state)
{
int i;
@@ -3118,6 +3207,16 @@
uart_suspend_port(&serial8250_reg, &up->port);
}
+#ifdef CONFIG_ARCH_M86XXX
+ /* Now Do the FAST_UART_CLOCK gating here, be sure no other devices
+ * are using the DUS clock to shutdown the clock.
+ * Here above clock is derived from DUS , henece it will be not
+ * gated , unless and until DMA/FAST-SPI will disable the DUS clock
+ * to make the usecount 0.
+ */
+ clk_disable(uart_clk);
+#endif
+
return 0;
}
@@ -3125,6 +3224,20 @@
{
int i;
+#ifdef CONFIG_ARCH_M86XXX
+ /* Now Enable the FAST_UART_CLOCK here , before
+ * before resuming any opertions.
+ */
+ if (clk_enable(uart_clk)){
+ pr_err("%s: Unable to enable FAST-UART clock: \n",__func__);
+ /* Here we are not able to enable the FAST-UART clock ,
+ * beacause of clk_disable unable to shutdown( usecount is
+ * not zero ,due to dependancy with DMA and FAST-SPI) clock
+ * so Let resume the port only .
+ */
+ }
+#endif
+
for (i = 0; i < UART_NR; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
@@ -3134,12 +3247,15 @@
return 0;
}
+#endif
static struct platform_driver serial8250_isa_driver = {
.probe = serial8250_probe,
.remove = __devexit_p(serial8250_remove),
+#if CONFIG_PM
.suspend = serial8250_suspend,
.resume = serial8250_resume,
+#endif
.driver = {
.name = "serial8250",
.owner = THIS_MODULE,
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 791f11b..e5d25bb 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -80,7 +80,8 @@
# some non-PCI HCDs implement xHCI
config USB_ARCH_HAS_XHCI
boolean
- default PCI
+ default y
+# default PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
config USB
@@ -181,4 +182,6 @@
source "drivers/usb/otg/Kconfig"
+source "drivers/usb/dwc_otg/Kconfig"
+
endif # USB_SUPPORT
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 75eca76..ff19eba 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -49,6 +49,8 @@
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
+obj-$(CONFIG_DWC_OTG) += dwc_otg/
+
obj-$(CONFIG_USB_MUSB_HDRC) += musb/
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
obj-$(CONFIG_USB_OTG_UTILS) += otg/
diff --git a/drivers/usb/dwc_otg/Kconfig b/drivers/usb/dwc_otg/Kconfig
new file mode 100644
index 0000000..2868292
--- /dev/null
+++ b/drivers/usb/dwc_otg/Kconfig
@@ -0,0 +1,37 @@
+config DWC_OTG
+ tristate "Synopsis DWC_OTG support"
+ depends on USB_SUPPORT
+ help
+ This driver supports Synopsis DWC_OTG IP core
+ embebbed on many SOCs (ralink, infineon, etc)
+
+choice
+ prompt "USB Operation Mode"
+ depends on DWC_OTG
+ default DWC_OTG_HOST_ONLY
+
+config DWC_OTG_HOST_ONLY
+ bool "HOST ONLY MODE"
+ depends on DWC_OTG
+
+config DWC_OTG_DEVICE_ONLY
+ bool "DEVICE ONLY MODE"
+ depends on DWC_OTG
+endchoice
+
+choice
+ prompt "Platform"
+ depends on DWC_OTG
+ default DWC_OTG_COMCERTO
+
+config DWC_OTG_COMCERTO
+ bool "Comcerto 2000"
+ depends on ARCH_COMCERTO
+ help
+ USB 2.0 USB Host Controller
+ platform support
+endchoice
+
+config DWC_OTG_DEBUG
+ bool "Enable debug mode"
+ depends on DWC_OTG
diff --git a/drivers/usb/dwc_otg/Makefile b/drivers/usb/dwc_otg/Makefile
new file mode 100644
index 0000000..6cc1e97
--- /dev/null
+++ b/drivers/usb/dwc_otg/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for DWC_otg Highspeed USB controller driver
+#
+
+ifeq ($(CONFIG_DWC_OTG_DEBUG),y)
+endif
+
+# Use one of the following flags to compile the software in host-only or
+# device-only mode.
+ifeq ($(CONFIG_DWC_OTG_HOST_ONLY),y)
+EXTRA_CFLAGS += -DDWC_HOST_ONLY
+EXTRA_CFLAGS += -DDWC_EN_ISOC
+EXTRA_CFLAGS += -DDWC_LINUX
+EXTRA_CFLAGS += -DDWC_LIBMODULE
+endif
+
+ifeq ($(CONFIG_DWC_OTG_DEVICE_ONLY),y)
+EXTRA_CFLAGS += -DDWC_DEVICE_ONLY
+endif
+
+obj-$(CONFIG_DWC_OTG) := dwc_otg.o
+
+dwc_otg-objs := dwc_otg_driver.o dwc_otg_attr.o
+dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o
+dwc_otg-objs += dwc_otg_hcd_linux.o dwc_otg_adp.o dwc_otg_cfi.o
+dwc_otg-objs += dwc_otg_hcd_ddma.o
+dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o
+dwc_otg-objs += dwc_common_linux.o dwc_mem.o
diff --git a/drivers/usb/dwc_otg/dwc_cc.c b/drivers/usb/dwc_otg/dwc_cc.c
new file mode 100644
index 0000000..327b21e
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_cc.c
@@ -0,0 +1,532 @@
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_cc.c $
+ * $Revision: #4 $
+ * $Date: 2010/11/04 $
+ * $Change: 1621692 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifdef DWC_CCLIB
+
+#include "dwc_cc.h"
+
+typedef struct dwc_cc
+{
+ uint32_t uid;
+ uint8_t chid[16];
+ uint8_t cdid[16];
+ uint8_t ck[16];
+ uint8_t *name;
+ uint8_t length;
+ DWC_CIRCLEQ_ENTRY(dwc_cc) list_entry;
+} dwc_cc_t;
+
+DWC_CIRCLEQ_HEAD(context_list, dwc_cc);
+
+/** The main structure for CC management. */
+struct dwc_cc_if
+{
+ dwc_mutex_t *mutex;
+ char *filename;
+
+ unsigned is_host:1;
+
+ dwc_notifier_t *notifier;
+
+ struct context_list list;
+};
+
+#ifdef DEBUG
+static inline void dump_bytes(char *name, uint8_t *bytes, int len)
+{
+ int i;
+ DWC_PRINTF("%s: ", name);
+ for (i=0; i<len; i++) {
+ DWC_PRINTF("%02x ", bytes[i]);
+ }
+ DWC_PRINTF("\n");
+}
+#else
+#define dump_bytes(x...)
+#endif
+
+static dwc_cc_t *alloc_cc(void *mem_ctx, uint8_t *name, uint32_t length)
+{
+ dwc_cc_t *cc = dwc_alloc(mem_ctx, sizeof(dwc_cc_t));
+ if (!cc) {
+ return NULL;
+ }
+ DWC_MEMSET(cc, 0, sizeof(dwc_cc_t));
+
+ if (name) {
+ cc->length = length;
+ cc->name = dwc_alloc(mem_ctx, length);
+ if (!cc->name) {
+ dwc_free(mem_ctx, cc);
+ return NULL;
+ }
+
+ DWC_MEMCPY(cc->name, name, length);
+ }
+
+ return cc;
+}
+
+static void free_cc(void *mem_ctx, dwc_cc_t *cc)
+{
+ if (cc->name) {
+ dwc_free(mem_ctx, cc->name);
+ }
+ dwc_free(mem_ctx, cc);
+}
+
+static uint32_t next_uid(dwc_cc_if_t *cc_if)
+{
+ uint32_t uid = 0;
+ dwc_cc_t *cc;
+ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+ if (cc->uid > uid) {
+ uid = cc->uid;
+ }
+ }
+
+ if (uid == 0) {
+ uid = 255;
+ }
+
+ return uid + 1;
+}
+
+static dwc_cc_t *cc_find(dwc_cc_if_t *cc_if, uint32_t uid)
+{
+ dwc_cc_t *cc;
+ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+ if (cc->uid == uid) {
+ return cc;
+ }
+ }
+ return NULL;
+}
+
+static unsigned int cc_data_size(dwc_cc_if_t *cc_if)
+{
+ unsigned int size = 0;
+ dwc_cc_t *cc;
+ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+ size += (48 + 1);
+ if (cc->name) {
+ size += cc->length;
+ }
+ }
+ return size;
+}
+
+static uint32_t cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid)
+{
+ uint32_t uid = 0;
+ dwc_cc_t *cc;
+
+ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+ if (DWC_MEMCMP(cc->chid, chid, 16) == 0) {
+ uid = cc->uid;
+ break;
+ }
+ }
+ return uid;
+}
+static uint32_t cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid)
+{
+ uint32_t uid = 0;
+ dwc_cc_t *cc;
+
+ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+ if (DWC_MEMCMP(cc->cdid, cdid, 16) == 0) {
+ uid = cc->uid;
+ break;
+ }
+ }
+ return uid;
+}
+
+/* Internal cc_add */
+static int32_t cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
+ uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
+{
+ dwc_cc_t *cc;
+ uint32_t uid;
+
+ if (cc_if->is_host) {
+ uid = cc_match_cdid(cc_if, cdid);
+ }
+ else {
+ uid = cc_match_chid(cc_if, chid);
+ }
+
+ if (uid) {
+ DWC_DEBUG("Replacing previous connection context id=%d name=%p name_len=%d", uid, name, length);
+ cc = cc_find(cc_if, uid);
+ }
+ else {
+ cc = alloc_cc(mem_ctx, name, length);
+ cc->uid = next_uid(cc_if);
+ DWC_CIRCLEQ_INSERT_TAIL(&cc_if->list, cc, list_entry);
+ }
+
+ DWC_MEMCPY(&(cc->chid[0]), chid, 16);
+ DWC_MEMCPY(&(cc->cdid[0]), cdid, 16);
+ DWC_MEMCPY(&(cc->ck[0]), ck, 16);
+
+ DWC_DEBUG("Added connection context id=%d name=%p name_len=%d", cc->uid, name, length);
+ dump_bytes("CHID", cc->chid, 16);
+ dump_bytes("CDID", cc->cdid, 16);
+ dump_bytes("CK", cc->ck, 16);
+ return cc->uid;
+}
+
+/* Internal cc_clear */
+static void cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if)
+{
+ while (!DWC_CIRCLEQ_EMPTY(&cc_if->list)) {
+ dwc_cc_t *cc = DWC_CIRCLEQ_FIRST(&cc_if->list);
+ DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry);
+ free_cc(mem_ctx, cc);
+ }
+}
+
+dwc_cc_if_t *dwc_cc_if_alloc(void *mem_ctx, void *mtx_ctx,
+ dwc_notifier_t *notifier, unsigned is_host)
+{
+ dwc_cc_if_t *cc_if = NULL;
+
+ /* Allocate a common_cc_if structure */
+ cc_if = dwc_alloc(mem_ctx, sizeof(dwc_cc_if_t));
+
+ if (!cc_if)
+ return NULL;
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+ DWC_MUTEX_ALLOC_LINUX_DEBUG(cc_if->mutex);
+#else
+ cc_if->mutex = dwc_mutex_alloc(mtx_ctx);
+#endif
+ if (!cc_if->mutex) {
+ dwc_free(mem_ctx, cc_if);
+ return NULL;
+ }
+
+ DWC_CIRCLEQ_INIT(&cc_if->list);
+ cc_if->is_host = is_host;
+ cc_if->notifier = notifier;
+ return cc_if;
+}
+
+void dwc_cc_if_free(void *mem_ctx, void *mtx_ctx, dwc_cc_if_t *cc_if)
+{
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+ DWC_MUTEX_FREE(cc_if->mutex);
+#else
+ dwc_mutex_free(mtx_ctx, cc_if->mutex);
+#endif
+ cc_clear(mem_ctx, cc_if);
+ dwc_free(mem_ctx, cc_if);
+}
+
+static void cc_changed(dwc_cc_if_t *cc_if)
+{
+ if (cc_if->notifier) {
+ dwc_notify(cc_if->notifier, DWC_CC_LIST_CHANGED_NOTIFICATION, cc_if);
+ }
+}
+
+void dwc_cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if)
+{
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ cc_clear(mem_ctx, cc_if);
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ cc_changed(cc_if);
+}
+
+int32_t dwc_cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
+ uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
+{
+ uint32_t uid;
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ uid = cc_add(mem_ctx, cc_if, chid, cdid, ck, name, length);
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ cc_changed(cc_if);
+
+ return uid;
+}
+
+void dwc_cc_change(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id, uint8_t *chid,
+ uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
+{
+ dwc_cc_t* cc;
+
+ DWC_DEBUG("Change connection context %d", id);
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ cc = cc_find(cc_if, id);
+ if (!cc) {
+ DWC_ERROR("Uid %d not found in cc list\n", id);
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ return;
+ }
+
+ if (chid) {
+ DWC_MEMCPY(&(cc->chid[0]), chid, 16);
+ }
+ if (cdid) {
+ DWC_MEMCPY(&(cc->cdid[0]), cdid, 16);
+ }
+ if (ck) {
+ DWC_MEMCPY(&(cc->ck[0]), ck, 16);
+ }
+
+ if (name) {
+ if (cc->name) {
+ dwc_free(mem_ctx, cc->name);
+ }
+ cc->name = dwc_alloc(mem_ctx, length);
+ if (!cc->name) {
+ DWC_ERROR("Out of memory in dwc_cc_change()\n");
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ return;
+ }
+ cc->length = length;
+ DWC_MEMCPY(cc->name, name, length);
+ }
+
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+ cc_changed(cc_if);
+
+ DWC_DEBUG("Changed connection context id=%d\n", id);
+ dump_bytes("New CHID", cc->chid, 16);
+ dump_bytes("New CDID", cc->cdid, 16);
+ dump_bytes("New CK", cc->ck, 16);
+}
+
+void dwc_cc_remove(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id)
+{
+ dwc_cc_t *cc;
+
+ DWC_DEBUG("Removing connection context %d", id);
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ cc = cc_find(cc_if, id);
+ if (!cc) {
+ DWC_ERROR("Uid %d not found in cc list\n", id);
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ return;
+ }
+
+ DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry);
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ free_cc(mem_ctx, cc);
+
+ cc_changed(cc_if);
+}
+
+uint8_t *dwc_cc_data_for_save(void *mem_ctx, dwc_cc_if_t *cc_if, unsigned int *length)
+{
+ uint8_t *buf, *x;
+ uint8_t zero = 0;
+ dwc_cc_t *cc;
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ *length = cc_data_size(cc_if);
+ if (!(*length)) {
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ return NULL;
+ }
+
+ DWC_DEBUG("Creating data for saving (length=%d)", *length);
+
+ buf = dwc_alloc(mem_ctx, *length);
+ if (!buf) {
+ *length = 0;
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ return NULL;
+ }
+
+ x = buf;
+ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+ DWC_MEMCPY(x, cc->chid, 16);
+ x += 16;
+ DWC_MEMCPY(x, cc->cdid, 16);
+ x += 16;
+ DWC_MEMCPY(x, cc->ck, 16);
+ x += 16;
+ if (cc->name) {
+ DWC_MEMCPY(x, &cc->length, 1);
+ x += 1;
+ DWC_MEMCPY(x, cc->name, cc->length);
+ x += cc->length;
+ }
+ else {
+ DWC_MEMCPY(x, &zero, 1);
+ x += 1;
+ }
+ }
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+ return buf;
+}
+
+void dwc_cc_restore_from_data(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *data, uint32_t length)
+{
+ uint8_t name_length;
+ uint8_t *name;
+ uint8_t *chid;
+ uint8_t *cdid;
+ uint8_t *ck;
+ uint32_t i = 0;
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ cc_clear(mem_ctx, cc_if);
+
+ while (i < length) {
+ chid = &data[i];
+ i += 16;
+ cdid = &data[i];
+ i += 16;
+ ck = &data[i];
+ i += 16;
+
+ name_length = data[i];
+ i ++;
+
+ if (name_length) {
+ name = &data[i];
+ i += name_length;
+ }
+ else {
+ name = NULL;
+ }
+
+ /* check to see if we haven't overflown the buffer */
+ if (i > length) {
+ DWC_ERROR("Data format error while attempting to load CCs "
+ "(nlen=%d, iter=%d, buflen=%d).\n", name_length, i, length);
+ break;
+ }
+
+ cc_add(mem_ctx, cc_if, chid, cdid, ck, name, name_length);
+ }
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+ cc_changed(cc_if);
+}
+
+uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid)
+{
+ uint32_t uid = 0;
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ uid = cc_match_chid(cc_if, chid);
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ return uid;
+}
+uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid)
+{
+ uint32_t uid = 0;
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ uid = cc_match_cdid(cc_if, cdid);
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+ return uid;
+}
+
+uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id)
+{
+ uint8_t *ck = NULL;
+ dwc_cc_t *cc;
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ cc = cc_find(cc_if, id);
+ if (cc) {
+ ck = cc->ck;
+ }
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+ return ck;
+
+}
+
+uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id)
+{
+ uint8_t *retval = NULL;
+ dwc_cc_t *cc;
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ cc = cc_find(cc_if, id);
+ if (cc) {
+ retval = cc->chid;
+ }
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+ return retval;
+}
+
+uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id)
+{
+ uint8_t *retval = NULL;
+ dwc_cc_t *cc;
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ cc = cc_find(cc_if, id);
+ if (cc) {
+ retval = cc->cdid;
+ }
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+ return retval;
+}
+
+uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length)
+{
+ uint8_t *retval = NULL;
+ dwc_cc_t *cc;
+
+ DWC_MUTEX_LOCK(cc_if->mutex);
+ *length = 0;
+ cc = cc_find(cc_if, id);
+ if (cc) {
+ *length = cc->length;
+ retval = cc->name;
+ }
+ DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+ return retval;
+}
+
+#endif /* DWC_CCLIB */
diff --git a/drivers/usb/dwc_otg/dwc_cc.h b/drivers/usb/dwc_otg/dwc_cc.h
new file mode 100644
index 0000000..6b2bbaf
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_cc.h
@@ -0,0 +1,225 @@
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_cc.h $
+ * $Revision: #4 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifndef _DWC_CC_H_
+#define _DWC_CC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file
+ *
+ * This file defines the Context Context library.
+ *
+ * The main data structure is dwc_cc_if_t which is returned by either the
+ * dwc_cc_if_alloc function or returned by the module to the user via a provided
+ * function. The data structure is opaque and should only be manipulated via the
+ * functions provied in this API.
+ *
+ * It manages a list of connection contexts and operations can be performed to
+ * add, remove, query, search, and change, those contexts. Additionally,
+ * a dwc_notifier_t object can be requested from the manager so that
+ * the user can be notified whenever the context list has changed.
+ */
+
+#include "dwc_os.h"
+#include "dwc_list.h"
+#include "dwc_notifier.h"
+
+
+/* Notifications */
+#define DWC_CC_LIST_CHANGED_NOTIFICATION "DWC_CC_LIST_CHANGED_NOTIFICATION"
+
+struct dwc_cc_if;
+typedef struct dwc_cc_if dwc_cc_if_t;
+
+
+/** @name Connection Context Operations */
+/** @{ */
+
+/** This function allocates memory for a dwc_cc_if_t structure, initializes
+ * fields to default values, and returns a pointer to the structure or NULL on
+ * error. */
+extern dwc_cc_if_t *dwc_cc_if_alloc(void *mem_ctx, void *mtx_ctx,
+ dwc_notifier_t *notifier, unsigned is_host);
+
+/** Frees the memory for the specified CC structure allocated from
+ * dwc_cc_if_alloc(). */
+extern void dwc_cc_if_free(void *mem_ctx, void *mtx_ctx, dwc_cc_if_t *cc_if);
+
+/** Removes all contexts from the connection context list */
+extern void dwc_cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if);
+
+/** Adds a connection context (CHID, CK, CDID, Name) to the connection context list.
+ * If a CHID already exists, the CK and name are overwritten. Statistics are
+ * not overwritten.
+ *
+ * @param cc_if The cc_if structure.
+ * @param chid A pointer to the 16-byte CHID. This value will be copied.
+ * @param ck A pointer to the 16-byte CK. This value will be copied.
+ * @param cdid A pointer to the 16-byte CDID. This value will be copied.
+ * @param name An optional host friendly name as defined in the association model
+ * spec. Must be a UTF16-LE unicode string. Can be NULL to indicated no name.
+ * @param length The length othe unicode string.
+ * @return A unique identifier used to refer to this context that is valid for
+ * as long as this context is still in the list. */
+extern int32_t dwc_cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
+ uint8_t *cdid, uint8_t *ck, uint8_t *name,
+ uint8_t length);
+
+/** Changes the CHID, CK, CDID, or Name values of a connection context in the
+ * list, preserving any accumulated statistics. This would typically be called
+ * if the host decideds to change the context with a SET_CONNECTION request.
+ *
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context.
+ * @param chid A pointer to the 16-byte CHID. This value will be copied. NULL
+ * indicates no change.
+ * @param cdid A pointer to the 16-byte CDID. This value will be copied. NULL
+ * indicates no change.
+ * @param ck A pointer to the 16-byte CK. This value will be copied. NULL
+ * indicates no change.
+ * @param name Host friendly name UTF16-LE. NULL indicates no change.
+ * @param length Length of name. */
+extern void dwc_cc_change(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id,
+ uint8_t *chid, uint8_t *cdid, uint8_t *ck,
+ uint8_t *name, uint8_t length);
+
+/** Remove the specified connection context.
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context to remove. */
+extern void dwc_cc_remove(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id);
+
+/** Get a binary block of data for the connection context list and attributes.
+ * This data can be used by the OS specific driver to save the connection
+ * context list into non-volatile memory.
+ *
+ * @param cc_if The cc_if structure.
+ * @param length Return the length of the data buffer.
+ * @return A pointer to the data buffer. The memory for this buffer should be
+ * freed with DWC_FREE() after use. */
+extern uint8_t *dwc_cc_data_for_save(void *mem_ctx, dwc_cc_if_t *cc_if,
+ unsigned int *length);
+
+/** Restore the connection context list from the binary data that was previously
+ * returned from a call to dwc_cc_data_for_save. This can be used by the OS specific
+ * driver to load a connection context list from non-volatile memory.
+ *
+ * @param cc_if The cc_if structure.
+ * @param data The data bytes as returned from dwc_cc_data_for_save.
+ * @param length The length of the data. */
+extern void dwc_cc_restore_from_data(void *mem_ctx, dwc_cc_if_t *cc_if,
+ uint8_t *data, unsigned int length);
+
+/** Find the connection context from the specified CHID.
+ *
+ * @param cc_if The cc_if structure.
+ * @param chid A pointer to the CHID data.
+ * @return A non-zero identifier of the connection context if the CHID matches.
+ * Otherwise returns 0. */
+extern uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid);
+
+/** Find the connection context from the specified CDID.
+ *
+ * @param cc_if The cc_if structure.
+ * @param cdid A pointer to the CDID data.
+ * @return A non-zero identifier of the connection context if the CHID matches.
+ * Otherwise returns 0. */
+extern uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid);
+
+/** Retrieve the CK from the specified connection context.
+ *
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context.
+ * @return A pointer to the CK data. The memory does not need to be freed. */
+extern uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id);
+
+/** Retrieve the CHID from the specified connection context.
+ *
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context.
+ * @return A pointer to the CHID data. The memory does not need to be freed. */
+extern uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id);
+
+/** Retrieve the CDID from the specified connection context.
+ *
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context.
+ * @return A pointer to the CDID data. The memory does not need to be freed. */
+extern uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id);
+
+extern uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length);
+
+/** Checks a buffer for non-zero.
+ * @param id A pointer to a 16 byte buffer.
+ * @return true if the 16 byte value is non-zero. */
+static inline unsigned dwc_assoc_is_not_zero_id(uint8_t *id) {
+ int i;
+ for (i=0; i<16; i++) {
+ if (id[i]) return 1;
+ }
+ return 0;
+}
+
+/** Checks a buffer for zero.
+ * @param id A pointer to a 16 byte buffer.
+ * @return true if the 16 byte value is zero. */
+static inline unsigned dwc_assoc_is_zero_id(uint8_t *id) {
+ return !dwc_assoc_is_not_zero_id(id);
+}
+
+/** Prints an ASCII representation for the 16-byte chid, cdid, or ck, into
+ * buffer. */
+static inline int dwc_print_id_string(char *buffer, uint8_t *id) {
+ char *ptr = buffer;
+ int i;
+ for (i=0; i<16; i++) {
+ ptr += DWC_SPRINTF(ptr, "%02x", id[i]);
+ if (i < 15) {
+ ptr += DWC_SPRINTF(ptr, " ");
+ }
+ }
+ return ptr - buffer;
+}
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_CC_H_ */
+
diff --git a/drivers/usb/dwc_otg/dwc_cfi_common.h b/drivers/usb/dwc_otg/dwc_cfi_common.h
new file mode 100644
index 0000000..be56af4
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_cfi_common.h
@@ -0,0 +1,142 @@
+/* ==========================================================================
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_CFI_COMMON_H__)
+#define __DWC_CFI_COMMON_H__
+
+//#include <linux/types.h>
+
+/**
+ * @file
+ *
+ * This file contains the CFI specific common constants, interfaces
+ * (functions and macros) and structures for Linux. No PCD specific
+ * data structure or definition is to be included in this file.
+ *
+ */
+
+/** This is a request for all Core Features */
+#define VEN_CORE_GET_FEATURES 0xB1
+
+/** This is a request to get the value of a specific Core Feature */
+#define VEN_CORE_GET_FEATURE 0xB2
+
+/** This command allows the host to set the value of a specific Core Feature */
+#define VEN_CORE_SET_FEATURE 0xB3
+
+/** This command allows the host to set the default values of
+ * either all or any specific Core Feature
+ */
+#define VEN_CORE_RESET_FEATURES 0xB4
+
+/** This command forces the PCD to write the deferred values of a Core Features */
+#define VEN_CORE_ACTIVATE_FEATURES 0xB5
+
+/** This request reads a DWORD value from a register at the specified offset */
+#define VEN_CORE_READ_REGISTER 0xB6
+
+/** This request writes a DWORD value into a register at the specified offset */
+#define VEN_CORE_WRITE_REGISTER 0xB7
+
+/** This structure is the header of the Core Features dataset returned to
+ * the Host
+ */
+struct cfi_all_features_header {
+/** The features header structure length is */
+#define CFI_ALL_FEATURES_HDR_LEN 8
+ /**
+ * The total length of the features dataset returned to the Host
+ */
+ uint16_t wTotalLen;
+
+ /**
+ * CFI version number inBinary-Coded Decimal (i.e., 1.00 is 100H).
+ * This field identifies the version of the CFI Specification with which
+ * the device is compliant.
+ */
+ uint16_t wVersion;
+
+ /** The ID of the Core */
+ uint16_t wCoreID;
+#define CFI_CORE_ID_UDC 1
+#define CFI_CORE_ID_OTG 2
+#define CFI_CORE_ID_WUDEV 3
+
+ /** Number of features returned by VEN_CORE_GET_FEATURES request */
+ uint16_t wNumFeatures;
+} UPACKED;
+
+typedef struct cfi_all_features_header cfi_all_features_header_t;
+
+/** This structure is a header of the Core Feature descriptor dataset returned to
+ * the Host after the VEN_CORE_GET_FEATURES request
+ */
+struct cfi_feature_desc_header {
+#define CFI_FEATURE_DESC_HDR_LEN 8
+
+ /** The feature ID */
+ uint16_t wFeatureID;
+
+ /** Length of this feature descriptor in bytes - including the
+ * length of the feature name string
+ */
+ uint16_t wLength;
+
+ /** The data length of this feature in bytes */
+ uint16_t wDataLength;
+
+ /**
+ * Attributes of this features
+ * D0: Access rights
+ * 0 - Read/Write
+ * 1 - Read only
+ */
+ uint8_t bmAttributes;
+#define CFI_FEATURE_ATTR_RO 1
+#define CFI_FEATURE_ATTR_RW 0
+
+ /** Length of the feature name in bytes */
+ uint8_t bNameLen;
+
+ /** The feature name buffer */
+ //uint8_t *name;
+} UPACKED;
+
+typedef struct cfi_feature_desc_header cfi_feature_desc_header_t;
+
+/**
+ * This structure describes a NULL terminated string referenced by its id field.
+ * It is very similar to usb_string structure but has the id field type set to 16-bit.
+ */
+struct cfi_string {
+ uint16_t id;
+ const uint8_t *s;
+};
+typedef struct cfi_string cfi_string_t;
+
+#endif
diff --git a/drivers/usb/dwc_otg/dwc_common_fbsd.c b/drivers/usb/dwc_otg/dwc_common_fbsd.c
new file mode 100644
index 0000000..6dd04b5
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_common_fbsd.c
@@ -0,0 +1,1308 @@
+#include "dwc_os.h"
+#include "dwc_list.h"
+
+#ifdef DWC_CCLIB
+# include "dwc_cc.h"
+#endif
+
+#ifdef DWC_CRYPTOLIB
+# include "dwc_modpow.h"
+# include "dwc_dh.h"
+# include "dwc_crypto.h"
+#endif
+
+#ifdef DWC_NOTIFYLIB
+# include "dwc_notifier.h"
+#endif
+
+/* OS-Level Implementations */
+
+/* This is the FreeBSD 7.0 kernel implementation of the DWC platform library. */
+
+
+/* MISC */
+
+void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size)
+{
+ return memset(dest, byte, size);
+}
+
+void *DWC_MEMCPY(void *dest, void const *src, uint32_t size)
+{
+ return memcpy(dest, src, size);
+}
+
+void *DWC_MEMMOVE(void *dest, void *src, uint32_t size)
+{
+ bcopy(src, dest, size);
+ return dest;
+}
+
+int DWC_MEMCMP(void *m1, void *m2, uint32_t size)
+{
+ return memcmp(m1, m2, size);
+}
+
+int DWC_STRNCMP(void *s1, void *s2, uint32_t size)
+{
+ return strncmp(s1, s2, size);
+}
+
+int DWC_STRCMP(void *s1, void *s2)
+{
+ return strcmp(s1, s2);
+}
+
+int DWC_STRLEN(char const *str)
+{
+ return strlen(str);
+}
+
+char *DWC_STRCPY(char *to, char const *from)
+{
+ return strcpy(to, from);
+}
+
+char *DWC_STRDUP(char const *str)
+{
+ int len = DWC_STRLEN(str) + 1;
+ char *new = DWC_ALLOC_ATOMIC(len);
+
+ if (!new) {
+ return NULL;
+ }
+
+ DWC_MEMCPY(new, str, len);
+ return new;
+}
+
+int DWC_ATOI(char *str, int32_t *value)
+{
+ char *end = NULL;
+
+ *value = strtol(str, &end, 0);
+ if (*end == '\0') {
+ return 0;
+ }
+
+ return -1;
+}
+
+int DWC_ATOUI(char *str, uint32_t *value)
+{
+ char *end = NULL;
+
+ *value = strtoul(str, &end, 0);
+ if (*end == '\0') {
+ return 0;
+ }
+
+ return -1;
+}
+
+
+#ifdef DWC_UTFLIB
+/* From usbstring.c */
+
+int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len)
+{
+ int count = 0;
+ u8 c;
+ u16 uchar;
+
+ /* this insists on correct encodings, though not minimal ones.
+ * BUT it currently rejects legit 4-byte UTF-8 code points,
+ * which need surrogate pairs. (Unicode 3.1 can use them.)
+ */
+ while (len != 0 && (c = (u8) *s++) != 0) {
+ if (unlikely(c & 0x80)) {
+ // 2-byte sequence:
+ // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+ if ((c & 0xe0) == 0xc0) {
+ uchar = (c & 0x1f) << 6;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0xc0)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c;
+
+ // 3-byte sequence (most CJKV characters):
+ // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+ } else if ((c & 0xf0) == 0xe0) {
+ uchar = (c & 0x0f) << 12;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0xc0)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c << 6;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0xc0)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c;
+
+ /* no bogus surrogates */
+ if (0xd800 <= uchar && uchar <= 0xdfff)
+ goto fail;
+
+ // 4-byte sequence (surrogate pairs, currently rare):
+ // 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+ // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+ // (uuuuu = wwww + 1)
+ // FIXME accept the surrogate code points (only)
+ } else
+ goto fail;
+ } else
+ uchar = c;
+ put_unaligned (cpu_to_le16 (uchar), cp++);
+ count++;
+ len--;
+ }
+ return count;
+fail:
+ return -1;
+}
+
+#endif /* DWC_UTFLIB */
+
+
+/* dwc_debug.h */
+
+dwc_bool_t DWC_IN_IRQ(void)
+{
+// return in_irq();
+ return 0;
+}
+
+dwc_bool_t DWC_IN_BH(void)
+{
+// return in_softirq();
+ return 0;
+}
+
+void DWC_VPRINTF(char *format, va_list args)
+{
+ vprintf(format, args);
+}
+
+int DWC_VSNPRINTF(char *str, int size, char *format, va_list args)
+{
+ return vsnprintf(str, size, format, args);
+}
+
+void DWC_PRINTF(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+
+int DWC_SPRINTF(char *buffer, char *format, ...)
+{
+ int retval;
+ va_list args;
+
+ va_start(args, format);
+ retval = vsprintf(buffer, format, args);
+ va_end(args);
+ return retval;
+}
+
+int DWC_SNPRINTF(char *buffer, int size, char *format, ...)
+{
+ int retval;
+ va_list args;
+
+ va_start(args, format);
+ retval = vsnprintf(buffer, size, format, args);
+ va_end(args);
+ return retval;
+}
+
+void __DWC_WARN(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+
+void __DWC_ERROR(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+
+void DWC_EXCEPTION(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+// BUG_ON(1); ???
+}
+
+#ifdef DEBUG
+void __DWC_DEBUG(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+#endif
+
+
+/* dwc_mem.h */
+
+#if 0
+dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size,
+ uint32_t align,
+ uint32_t alloc)
+{
+ struct dma_pool *pool = dma_pool_create("Pool", NULL,
+ size, align, alloc);
+ return (dwc_pool_t *)pool;
+}
+
+void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool)
+{
+ dma_pool_destroy((struct dma_pool *)pool);
+}
+
+void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+// return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr);
+ return dma_pool_alloc((struct dma_pool *)pool, M_WAITOK, dma_addr);
+}
+
+void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+ void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr);
+ memset(..);
+}
+
+void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr)
+{
+ dma_pool_free(pool, vaddr, daddr);
+}
+#endif
+
+static void dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ if (error)
+ return;
+ *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
+{
+ dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
+ int error;
+
+ error = bus_dma_tag_create(
+#if __FreeBSD_version >= 700000
+ bus_get_dma_tag(dma->dev), /* parent */
+#else
+ NULL, /* parent */
+#endif
+ 4, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ size, /* maxsize */
+ 1, /* nsegments */
+ size, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &dma->dma_tag);
+ if (error) {
+ device_printf(dma->dev, "%s: bus_dma_tag_create failed: %d\n",
+ __func__, error);
+ goto fail_0;
+ }
+
+ error = bus_dmamem_alloc(dma->dma_tag, &dma->dma_vaddr,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &dma->dma_map);
+ if (error) {
+ device_printf(dma->dev, "%s: bus_dmamem_alloc(%ju) failed: %d\n",
+ __func__, (uintmax_t)size, error);
+ goto fail_1;
+ }
+
+ dma->dma_paddr = 0;
+ error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, size,
+ dmamap_cb, &dma->dma_paddr, BUS_DMA_NOWAIT);
+ if (error || dma->dma_paddr == 0) {
+ device_printf(dma->dev, "%s: bus_dmamap_load failed: %d\n",
+ __func__, error);
+ goto fail_2;
+ }
+
+ *dma_addr = dma->dma_paddr;
+ return dma->dma_vaddr;
+
+fail_2:
+ bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+fail_1:
+ bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
+ bus_dma_tag_destroy(dma->dma_tag);
+fail_0:
+ dma->dma_map = NULL;
+ dma->dma_tag = NULL;
+
+ return NULL;
+}
+
+void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr)
+{
+ dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
+
+ if (dma->dma_tag == NULL)
+ return;
+ if (dma->dma_map != NULL) {
+ bus_dmamap_sync(dma->dma_tag, dma->dma_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+ bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
+ dma->dma_map = NULL;
+ }
+
+ bus_dma_tag_destroy(dma->dma_tag);
+ dma->dma_tag = NULL;
+}
+
+void *__DWC_ALLOC(void *mem_ctx, uint32_t size)
+{
+ return malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+}
+
+void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size)
+{
+ return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+}
+
+void __DWC_FREE(void *mem_ctx, void *addr)
+{
+ free(addr, M_DEVBUF);
+}
+
+
+#ifdef DWC_CRYPTOLIB
+/* dwc_crypto.h */
+
+void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length)
+{
+ get_random_bytes(buffer, length);
+}
+
+int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out)
+{
+ struct crypto_blkcipher *tfm;
+ struct blkcipher_desc desc;
+ struct scatterlist sgd;
+ struct scatterlist sgs;
+
+ tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (tfm == NULL) {
+ printk("failed to load transform for aes CBC\n");
+ return -1;
+ }
+
+ crypto_blkcipher_setkey(tfm, key, keylen);
+ crypto_blkcipher_set_iv(tfm, iv, 16);
+
+ sg_init_one(&sgd, out, messagelen);
+ sg_init_one(&sgs, message, messagelen);
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) {
+ crypto_free_blkcipher(tfm);
+ DWC_ERROR("AES CBC encryption failed");
+ return -1;
+ }
+
+ crypto_free_blkcipher(tfm);
+ return 0;
+}
+
+int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out)
+{
+ struct crypto_hash *tfm;
+ struct hash_desc desc;
+ struct scatterlist sg;
+
+ tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ DWC_ERROR("Failed to load transform for sha256: %ld", PTR_ERR(tfm));
+ return 0;
+ }
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ sg_init_one(&sg, message, len);
+ crypto_hash_digest(&desc, &sg, len, out);
+ crypto_free_hash(tfm);
+
+ return 1;
+}
+
+int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen,
+ uint8_t *key, uint32_t keylen, uint8_t *out)
+{
+ struct crypto_hash *tfm;
+ struct hash_desc desc;
+ struct scatterlist sg;
+
+ tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ DWC_ERROR("Failed to load transform for hmac(sha256): %ld", PTR_ERR(tfm));
+ return 0;
+ }
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ sg_init_one(&sg, message, messagelen);
+ crypto_hash_setkey(tfm, key, keylen);
+ crypto_hash_digest(&desc, &sg, messagelen, out);
+ crypto_free_hash(tfm);
+
+ return 1;
+}
+
+#endif /* DWC_CRYPTOLIB */
+
+
+/* Byte Ordering Conversions */
+
+uint32_t DWC_CPU_TO_LE32(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_CPU_TO_BE32(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_LE32_TO_CPU(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_BE32_TO_CPU(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint16_t DWC_CPU_TO_LE16(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_CPU_TO_BE16(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_LE16_TO_CPU(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_BE16_TO_CPU(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+
+/* Registers */
+
+uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ return bus_space_read_4(io->iot, io->ioh, ior);
+}
+
+#if 0
+uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ return bus_space_read_8(io->iot, io->ioh, ior);
+}
+#endif
+
+void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ bus_space_write_4(io->iot, io->ioh, ior, value);
+}
+
+#if 0
+void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ bus_space_write_8(io->iot, io->ioh, ior, value);
+}
+#endif
+
+void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask,
+ uint32_t set_mask)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ bus_space_write_4(io->iot, io->ioh, ior,
+ (bus_space_read_4(io->iot, io->ioh, ior) &
+ ~clear_mask) | set_mask);
+}
+
+#if 0
+void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask,
+ uint64_t set_mask)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ bus_space_write_8(io->iot, io->ioh, ior,
+ (bus_space_read_8(io->iot, io->ioh, ior) &
+ ~clear_mask) | set_mask);
+}
+#endif
+
+
+/* Locking */
+
+dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void)
+{
+ struct mtx *sl = DWC_ALLOC(sizeof(*sl));
+
+ if (!sl) {
+ DWC_ERROR("Cannot allocate memory for spinlock");
+ return NULL;
+ }
+
+ mtx_init(sl, "dw3spn", NULL, MTX_SPIN);
+ return (dwc_spinlock_t *)sl;
+}
+
+void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock)
+{
+ struct mtx *sl = (struct mtx *)lock;
+
+ mtx_destroy(sl);
+ DWC_FREE(sl);
+}
+
+void DWC_SPINLOCK(dwc_spinlock_t *lock)
+{
+ mtx_lock_spin((struct mtx *)lock); // ???
+}
+
+void DWC_SPINUNLOCK(dwc_spinlock_t *lock)
+{
+ mtx_unlock_spin((struct mtx *)lock); // ???
+}
+
+void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags)
+{
+ mtx_lock_spin((struct mtx *)lock);
+}
+
+void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags)
+{
+ mtx_unlock_spin((struct mtx *)lock);
+}
+
+dwc_mutex_t *DWC_MUTEX_ALLOC(void)
+{
+ struct mtx *m;
+ dwc_mutex_t *mutex = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mtx));
+
+ if (!mutex) {
+ DWC_ERROR("Cannot allocate memory for mutex");
+ return NULL;
+ }
+
+ m = (struct mtx *)mutex;
+ mtx_init(m, "dw3mtx", NULL, MTX_DEF);
+ return mutex;
+}
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+#else
+void DWC_MUTEX_FREE(dwc_mutex_t *mutex)
+{
+ mtx_destroy((struct mtx *)mutex);
+ DWC_FREE(mutex);
+}
+#endif
+
+void DWC_MUTEX_LOCK(dwc_mutex_t *mutex)
+{
+ struct mtx *m = (struct mtx *)mutex;
+
+ mtx_lock(m);
+}
+
+int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex)
+{
+ struct mtx *m = (struct mtx *)mutex;
+
+ return mtx_trylock(m);
+}
+
+void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex)
+{
+ struct mtx *m = (struct mtx *)mutex;
+
+ mtx_unlock(m);
+}
+
+
+/* Timing */
+
+void DWC_UDELAY(uint32_t usecs)
+{
+ DELAY(usecs);
+}
+
+void DWC_MDELAY(uint32_t msecs)
+{
+ do {
+ DELAY(1000);
+ } while (--msecs);
+}
+
+void DWC_MSLEEP(uint32_t msecs)
+{
+ struct timeval tv;
+
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
+ pause("dw3slp", tvtohz(&tv));
+}
+
+uint32_t DWC_TIME(void)
+{
+ struct timeval tv;
+
+ microuptime(&tv); // or getmicrouptime? (less precise, but faster)
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+
+/* Timers */
+
+struct dwc_timer {
+ struct callout t;
+ char *name;
+ dwc_spinlock_t *lock;
+ dwc_timer_callback_t cb;
+ void *data;
+};
+
+dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
+{
+ dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
+
+ if (!t) {
+ DWC_ERROR("Cannot allocate memory for timer");
+ return NULL;
+ }
+
+ callout_init(&t->t, 1);
+
+ t->name = DWC_STRDUP(name);
+ if (!t->name) {
+ DWC_ERROR("Cannot allocate memory for timer->name");
+ goto no_name;
+ }
+
+ t->lock = DWC_SPINLOCK_ALLOC();
+ if (!t->lock) {
+ DWC_ERROR("Cannot allocate memory for lock");
+ goto no_lock;
+ }
+
+ t->cb = cb;
+ t->data = data;
+
+ return t;
+
+ no_lock:
+ DWC_FREE(t->name);
+ no_name:
+ DWC_FREE(t);
+
+ return NULL;
+}
+
+void DWC_TIMER_FREE(dwc_timer_t *timer)
+{
+ callout_stop(&timer->t);
+ DWC_SPINLOCK_FREE(timer->lock);
+ DWC_FREE(timer->name);
+ DWC_FREE(timer);
+}
+
+void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time)
+{
+ struct timeval tv;
+
+ tv.tv_sec = time / 1000;
+ tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
+ callout_reset(&timer->t, tvtohz(&tv), timer->cb, timer->data);
+}
+
+void DWC_TIMER_CANCEL(dwc_timer_t *timer)
+{
+ callout_stop(&timer->t);
+}
+
+
+/* Wait Queues */
+
+struct dwc_waitq {
+ struct mtx lock;
+ int abort;
+};
+
+dwc_waitq_t *DWC_WAITQ_ALLOC(void)
+{
+ dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+ if (!wq) {
+ DWC_ERROR("Cannot allocate memory for waitqueue");
+ return NULL;
+ }
+
+ mtx_init(&wq->lock, "dw3wtq", NULL, MTX_DEF);
+ wq->abort = 0;
+
+ return wq;
+}
+
+void DWC_WAITQ_FREE(dwc_waitq_t *wq)
+{
+ mtx_destroy(&wq->lock);
+ DWC_FREE(wq);
+}
+
+int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data)
+{
+// intrmask_t ipl;
+ int result = 0;
+
+ mtx_lock(&wq->lock);
+// ipl = splbio();
+
+ /* Skip the sleep if already aborted or triggered */
+ if (!wq->abort && !cond(data)) {
+// splx(ipl);
+ result = msleep(wq, &wq->lock, PCATCH, "dw3wat", 0); // infinite timeout
+// ipl = splbio();
+ }
+
+ if (result == ERESTART) { // signaled - restart
+ result = -DWC_E_RESTART;
+
+ } else if (result == EINTR) { // signaled - interrupt
+ result = -DWC_E_ABORT;
+
+ } else if (wq->abort) {
+ result = -DWC_E_ABORT;
+
+ } else {
+ result = 0;
+ }
+
+ wq->abort = 0;
+// splx(ipl);
+ mtx_unlock(&wq->lock);
+ return result;
+}
+
+int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
+ void *data, int32_t msecs)
+{
+ struct timeval tv, tv1, tv2;
+// intrmask_t ipl;
+ int result = 0;
+
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
+
+ mtx_lock(&wq->lock);
+// ipl = splbio();
+
+ /* Skip the sleep if already aborted or triggered */
+ if (!wq->abort && !cond(data)) {
+// splx(ipl);
+ getmicrouptime(&tv1);
+ result = msleep(wq, &wq->lock, PCATCH, "dw3wto", tvtohz(&tv));
+ getmicrouptime(&tv2);
+// ipl = splbio();
+ }
+
+ if (result == 0) { // awoken
+ if (wq->abort) {
+ result = -DWC_E_ABORT;
+ } else {
+ tv2.tv_usec -= tv1.tv_usec;
+ if (tv2.tv_usec < 0) {
+ tv2.tv_usec += 1000000;
+ tv2.tv_sec--;
+ }
+
+ tv2.tv_sec -= tv1.tv_sec;
+ result = tv2.tv_sec * 1000 + tv2.tv_usec / 1000;
+ result = msecs - result;
+ if (result <= 0)
+ result = 1;
+ }
+ } else if (result == ERESTART) { // signaled - restart
+ result = -DWC_E_RESTART;
+
+ } else if (result == EINTR) { // signaled - interrupt
+ result = -DWC_E_ABORT;
+
+ } else { // timed out
+ result = -DWC_E_TIMEOUT;
+ }
+
+ wq->abort = 0;
+// splx(ipl);
+ mtx_unlock(&wq->lock);
+ return result;
+}
+
+void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq)
+{
+ wakeup(wq);
+}
+
+void DWC_WAITQ_ABORT(dwc_waitq_t *wq)
+{
+// intrmask_t ipl;
+
+ mtx_lock(&wq->lock);
+// ipl = splbio();
+ wq->abort = 1;
+ wakeup(wq);
+// splx(ipl);
+ mtx_unlock(&wq->lock);
+}
+
+
+/* Threading */
+
+struct dwc_thread {
+ struct proc *proc;
+ int abort;
+};
+
+dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
+{
+ int retval;
+ dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread));
+
+ if (!thread) {
+ return NULL;
+ }
+
+ thread->abort = 0;
+ retval = kthread_create((void (*)(void *))func, data, &thread->proc,
+ RFPROC | RFNOWAIT, 0, "%s", name);
+ if (retval) {
+ DWC_FREE(thread);
+ return NULL;
+ }
+
+ return thread;
+}
+
+int DWC_THREAD_STOP(dwc_thread_t *thread)
+{
+ int retval;
+
+ thread->abort = 1;
+ retval = tsleep(&thread->abort, 0, "dw3stp", 60 * hz);
+
+ if (retval == 0) {
+ /* DWC_THREAD_EXIT() will free the thread struct */
+ return 0;
+ }
+
+ /* NOTE: We leak the thread struct if thread doesn't die */
+
+ if (retval == EWOULDBLOCK) {
+ return -DWC_E_TIMEOUT;
+ }
+
+ return -DWC_E_UNKNOWN;
+}
+
+dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread)
+{
+ return thread->abort;
+}
+
+void DWC_THREAD_EXIT(dwc_thread_t *thread)
+{
+ wakeup(&thread->abort);
+ DWC_FREE(thread);
+ kthread_exit(0);
+}
+
+
+/* tasklets
+ - Runs in interrupt context (cannot sleep)
+ - Each tasklet runs on a single CPU [ How can we ensure this on FreeBSD? Does it matter? ]
+ - Different tasklets can be running simultaneously on different CPUs [ shouldn't matter ]
+ */
+struct dwc_tasklet {
+ struct task t;
+ dwc_tasklet_callback_t cb;
+ void *data;
+};
+
+static void tasklet_callback(void *data, int pending) // what to do with pending ???
+{
+ dwc_tasklet_t *task = (dwc_tasklet_t *)data;
+
+ task->cb(task->data);
+}
+
+dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data)
+{
+ dwc_tasklet_t *task = DWC_ALLOC(sizeof(*task));
+
+ if (task) {
+ task->cb = cb;
+ task->data = data;
+ TASK_INIT(&task->t, 0, tasklet_callback, task);
+ } else {
+ DWC_ERROR("Cannot allocate memory for tasklet");
+ }
+
+ return task;
+}
+
+void DWC_TASK_FREE(dwc_tasklet_t *task)
+{
+ taskqueue_drain(taskqueue_fast, &task->t); // ???
+ DWC_FREE(task);
+}
+
+void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
+{
+ /* Uses predefined system queue */
+ taskqueue_enqueue_fast(taskqueue_fast, &task->t);
+}
+
+
+/* workqueues
+ - Runs in process context (can sleep)
+ */
+typedef struct work_container {
+ dwc_work_callback_t cb;
+ void *data;
+ dwc_workq_t *wq;
+ char *name;
+ int hz;
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_ENTRY(work_container) entry;
+#endif
+ struct task task;
+} work_container_t;
+
+#ifdef DEBUG
+DWC_CIRCLEQ_HEAD(work_container_queue, work_container);
+#endif
+
+struct dwc_workq {
+ struct taskqueue *taskq;
+ dwc_spinlock_t *lock;
+ dwc_waitq_t *waitq;
+ int pending;
+
+#ifdef DEBUG
+ struct work_container_queue entries;
+#endif
+};
+
+static void do_work(void *data, int pending) // what to do with pending ???
+{
+ work_container_t *container = (work_container_t *)data;
+ dwc_workq_t *wq = container->wq;
+ dwc_irqflags_t flags;
+
+ if (container->hz) {
+ pause("dw3wrk", container->hz);
+ }
+
+ container->cb(container->data);
+ DWC_DEBUG("Work done: %s, container=%p", container->name, container);
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_REMOVE(&wq->entries, container, entry);
+#endif
+ if (container->name)
+ DWC_FREE(container->name);
+ DWC_FREE(container);
+ wq->pending--;
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+ DWC_WAITQ_TRIGGER(wq->waitq);
+}
+
+static int work_done(void *data)
+{
+ dwc_workq_t *workq = (dwc_workq_t *)data;
+
+ return workq->pending == 0;
+}
+
+int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout)
+{
+ return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout);
+}
+
+dwc_workq_t *DWC_WORKQ_ALLOC(char *name)
+{
+ dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+ if (!wq) {
+ DWC_ERROR("Cannot allocate memory for workqueue");
+ return NULL;
+ }
+
+ wq->taskq = taskqueue_create(name, M_NOWAIT, taskqueue_thread_enqueue, &wq->taskq);
+ if (!wq->taskq) {
+ DWC_ERROR("Cannot allocate memory for taskqueue");
+ goto no_taskq;
+ }
+
+ wq->pending = 0;
+
+ wq->lock = DWC_SPINLOCK_ALLOC();
+ if (!wq->lock) {
+ DWC_ERROR("Cannot allocate memory for spinlock");
+ goto no_lock;
+ }
+
+ wq->waitq = DWC_WAITQ_ALLOC();
+ if (!wq->waitq) {
+ DWC_ERROR("Cannot allocate memory for waitqueue");
+ goto no_waitq;
+ }
+
+ taskqueue_start_threads(&wq->taskq, 1, PWAIT, "%s taskq", "dw3tsk");
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_INIT(&wq->entries);
+#endif
+ return wq;
+
+ no_waitq:
+ DWC_SPINLOCK_FREE(wq->lock);
+ no_lock:
+ taskqueue_free(wq->taskq);
+ no_taskq:
+ DWC_FREE(wq);
+
+ return NULL;
+}
+
+void DWC_WORKQ_FREE(dwc_workq_t *wq)
+{
+#ifdef DEBUG
+ dwc_irqflags_t flags;
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+
+ if (wq->pending != 0) {
+ struct work_container *container;
+
+ DWC_ERROR("Destroying work queue with pending work");
+
+ DWC_CIRCLEQ_FOREACH(container, &wq->entries, entry) {
+ DWC_ERROR("Work %s still pending", container->name);
+ }
+ }
+
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+#endif
+ DWC_WAITQ_FREE(wq->waitq);
+ DWC_SPINLOCK_FREE(wq->lock);
+ taskqueue_free(wq->taskq);
+ DWC_FREE(wq);
+}
+
+void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data,
+ char *format, ...)
+{
+ dwc_irqflags_t flags;
+ work_container_t *container;
+ static char name[128];
+ va_list args;
+
+ va_start(args, format);
+ DWC_VSNPRINTF(name, 128, format, args);
+ va_end(args);
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+ wq->pending++;
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+ DWC_WAITQ_TRIGGER(wq->waitq);
+
+ container = DWC_ALLOC_ATOMIC(sizeof(*container));
+ if (!container) {
+ DWC_ERROR("Cannot allocate memory for container");
+ return;
+ }
+
+ container->name = DWC_STRDUP(name);
+ if (!container->name) {
+ DWC_ERROR("Cannot allocate memory for container->name");
+ DWC_FREE(container);
+ return;
+ }
+
+ container->cb = cb;
+ container->data = data;
+ container->wq = wq;
+ container->hz = 0;
+
+ DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+
+ TASK_INIT(&container->task, 0, do_work, container);
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
+#endif
+ taskqueue_enqueue_fast(wq->taskq, &container->task);
+}
+
+void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb,
+ void *data, uint32_t time, char *format, ...)
+{
+ dwc_irqflags_t flags;
+ work_container_t *container;
+ static char name[128];
+ struct timeval tv;
+ va_list args;
+
+ va_start(args, format);
+ DWC_VSNPRINTF(name, 128, format, args);
+ va_end(args);
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+ wq->pending++;
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+ DWC_WAITQ_TRIGGER(wq->waitq);
+
+ container = DWC_ALLOC_ATOMIC(sizeof(*container));
+ if (!container) {
+ DWC_ERROR("Cannot allocate memory for container");
+ return;
+ }
+
+ container->name = DWC_STRDUP(name);
+ if (!container->name) {
+ DWC_ERROR("Cannot allocate memory for container->name");
+ DWC_FREE(container);
+ return;
+ }
+
+ container->cb = cb;
+ container->data = data;
+ container->wq = wq;
+
+ tv.tv_sec = time / 1000;
+ tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
+ container->hz = tvtohz(&tv);
+
+ DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+
+ TASK_INIT(&container->task, 0, do_work, container);
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
+#endif
+ taskqueue_enqueue_fast(wq->taskq, &container->task);
+}
+
+int DWC_WORKQ_PENDING(dwc_workq_t *wq)
+{
+ return wq->pending;
+}
diff --git a/drivers/usb/dwc_otg/dwc_common_linux.c b/drivers/usb/dwc_otg/dwc_common_linux.c
new file mode 100644
index 0000000..c2ba7e3
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_common_linux.c
@@ -0,0 +1,1421 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#ifdef DWC_CCLIB
+# include "dwc_cc.h"
+#endif
+
+#ifdef DWC_CRYPTOLIB
+# include "dwc_modpow.h"
+# include "dwc_dh.h"
+# include "dwc_crypto.h"
+#endif
+
+#ifdef DWC_NOTIFYLIB
+# include "dwc_notifier.h"
+#endif
+
+/* OS-Level Implementations */
+
+/* This is the Linux kernel implementation of the DWC platform library. */
+#include <linux/moduleparam.h>
+#include <linux/ctype.h>
+#include <linux/crypto.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/cdev.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+# include <linux/usb/gadget.h>
+#else
+# include <linux/usb_gadget.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include "dwc_os.h"
+#include "dwc_list.h"
+
+
+/* MISC */
+
+void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size)
+{
+ return memset(dest, byte, size);
+}
+
+void *DWC_MEMCPY(void *dest, void const *src, uint32_t size)
+{
+ return memcpy(dest, src, size);
+}
+
+void *DWC_MEMMOVE(void *dest, void *src, uint32_t size)
+{
+ return memmove(dest, src, size);
+}
+
+int DWC_MEMCMP(void *m1, void *m2, uint32_t size)
+{
+ return memcmp(m1, m2, size);
+}
+
+int DWC_STRNCMP(void *s1, void *s2, uint32_t size)
+{
+ return strncmp(s1, s2, size);
+}
+
+int DWC_STRCMP(void *s1, void *s2)
+{
+ return strcmp(s1, s2);
+}
+
+int DWC_STRLEN(char const *str)
+{
+ return strlen(str);
+}
+
+char *DWC_STRCPY(char *to, char const *from)
+{
+ return strcpy(to, from);
+}
+
+char *DWC_STRDUP(char const *str)
+{
+ int len = DWC_STRLEN(str) + 1;
+ char *new = DWC_ALLOC_ATOMIC(len);
+
+ if (!new) {
+ return NULL;
+ }
+
+ DWC_MEMCPY(new, str, len);
+ return new;
+}
+
+int DWC_ATOI(const char *str, int32_t *value)
+{
+ char *end = NULL;
+
+ *value = simple_strtol(str, &end, 0);
+ if (*end == '\0') {
+ return 0;
+ }
+
+ return -1;
+}
+
+int DWC_ATOUI(const char *str, uint32_t *value)
+{
+ char *end = NULL;
+
+ *value = simple_strtoul(str, &end, 0);
+ if (*end == '\0') {
+ return 0;
+ }
+
+ return -1;
+}
+
+
+#ifdef DWC_UTFLIB
+/* From usbstring.c */
+
+int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len)
+{
+ int count = 0;
+ u8 c;
+ u16 uchar;
+
+ /* this insists on correct encodings, though not minimal ones.
+ * BUT it currently rejects legit 4-byte UTF-8 code points,
+ * which need surrogate pairs. (Unicode 3.1 can use them.)
+ */
+ while (len != 0 && (c = (u8) *s++) != 0) {
+ if (unlikely(c & 0x80)) {
+ // 2-byte sequence:
+ // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+ if ((c & 0xe0) == 0xc0) {
+ uchar = (c & 0x1f) << 6;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0xc0)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c;
+
+ // 3-byte sequence (most CJKV characters):
+ // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+ } else if ((c & 0xf0) == 0xe0) {
+ uchar = (c & 0x0f) << 12;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0xc0)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c << 6;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0xc0)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c;
+
+ /* no bogus surrogates */
+ if (0xd800 <= uchar && uchar <= 0xdfff)
+ goto fail;
+
+ // 4-byte sequence (surrogate pairs, currently rare):
+ // 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+ // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+ // (uuuuu = wwww + 1)
+ // FIXME accept the surrogate code points (only)
+ } else
+ goto fail;
+ } else
+ uchar = c;
+ put_unaligned (cpu_to_le16 (uchar), cp++);
+ count++;
+ len--;
+ }
+ return count;
+fail:
+ return -1;
+}
+#endif /* DWC_UTFLIB */
+
+
+/* dwc_debug.h */
+
+dwc_bool_t DWC_IN_IRQ(void)
+{
+ return in_irq();
+}
+
+dwc_bool_t DWC_IN_BH(void)
+{
+ return in_softirq();
+}
+
+void DWC_VPRINTF(char *format, va_list args)
+{
+ vprintk(format, args);
+}
+
+int DWC_VSNPRINTF(char *str, int size, char *format, va_list args)
+{
+ return vsnprintf(str, size, format, args);
+}
+
+void DWC_PRINTF(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+
+int DWC_SPRINTF(char *buffer, char *format, ...)
+{
+ int retval;
+ va_list args;
+
+ va_start(args, format);
+ retval = vsprintf(buffer, format, args);
+ va_end(args);
+ return retval;
+}
+
+int DWC_SNPRINTF(char *buffer, int size, char *format, ...)
+{
+ int retval;
+ va_list args;
+
+ va_start(args, format);
+ retval = vsnprintf(buffer, size, format, args);
+ va_end(args);
+ return retval;
+}
+
+void __DWC_WARN(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_PRINTF(KERN_WARNING);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+
+void __DWC_ERROR(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_PRINTF(KERN_ERR);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+
+void DWC_EXCEPTION(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_PRINTF(KERN_ERR);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+ BUG_ON(1);
+}
+
+#ifdef DEBUG
+void __DWC_DEBUG(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_PRINTF(KERN_DEBUG);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+#endif
+
+
+/* dwc_mem.h */
+
+#if 0
+dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size,
+ uint32_t align,
+ uint32_t alloc)
+{
+ struct dma_pool *pool = dma_pool_create("Pool", NULL,
+ size, align, alloc);
+ return (dwc_pool_t *)pool;
+}
+
+void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool)
+{
+ dma_pool_destroy((struct dma_pool *)pool);
+}
+
+void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+ return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr);
+}
+
+void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+ void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr);
+ memset(..);
+}
+
+void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr)
+{
+ dma_pool_free(pool, vaddr, daddr);
+}
+#endif
+
+void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
+{
+#ifdef xxCOSIM /* Only works for 32-bit cosim */
+ void *buf = dma_alloc_coherent(dma_ctx, (size_t)size, dma_addr, GFP_KERNEL);
+#else
+ void *buf = dma_alloc_coherent(dma_ctx, (size_t)size, dma_addr, GFP_KERNEL);
+#endif
+ if (!buf) {
+ return NULL;
+ }
+
+ memset(buf, 0, (size_t)size);
+ return buf;
+}
+
+void *__DWC_DMA_ALLOC_ATOMIC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
+{
+ void *buf = dma_alloc_coherent(NULL, (size_t)size, dma_addr, GFP_ATOMIC);
+ if (!buf) {
+ return NULL;
+ }
+ memset(buf, 0, (size_t)size);
+ return buf;
+}
+
+void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr)
+{
+ dma_free_coherent(dma_ctx, size, virt_addr, dma_addr);
+}
+
+void *__DWC_ALLOC(void *mem_ctx, uint32_t size)
+{
+ return kzalloc(size, GFP_KERNEL);
+}
+
+void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size)
+{
+ return kzalloc(size, GFP_ATOMIC);
+}
+
+void __DWC_FREE(void *mem_ctx, void *addr)
+{
+ kfree(addr);
+}
+
+
+#ifdef DWC_CRYPTOLIB
+/* dwc_crypto.h */
+
+void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length)
+{
+ get_random_bytes(buffer, length);
+}
+
+int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out)
+{
+ struct crypto_blkcipher *tfm;
+ struct blkcipher_desc desc;
+ struct scatterlist sgd;
+ struct scatterlist sgs;
+
+ tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (tfm == NULL) {
+ printk("failed to load transform for aes CBC\n");
+ return -1;
+ }
+
+ crypto_blkcipher_setkey(tfm, key, keylen);
+ crypto_blkcipher_set_iv(tfm, iv, 16);
+
+ sg_init_one(&sgd, out, messagelen);
+ sg_init_one(&sgs, message, messagelen);
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) {
+ crypto_free_blkcipher(tfm);
+ DWC_ERROR("AES CBC encryption failed");
+ return -1;
+ }
+
+ crypto_free_blkcipher(tfm);
+ return 0;
+}
+
+int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out)
+{
+ struct crypto_hash *tfm;
+ struct hash_desc desc;
+ struct scatterlist sg;
+
+ tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ DWC_ERROR("Failed to load transform for sha256: %ld\n", PTR_ERR(tfm));
+ return 0;
+ }
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ sg_init_one(&sg, message, len);
+ crypto_hash_digest(&desc, &sg, len, out);
+ crypto_free_hash(tfm);
+
+ return 1;
+}
+
+int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen,
+ uint8_t *key, uint32_t keylen, uint8_t *out)
+{
+ struct crypto_hash *tfm;
+ struct hash_desc desc;
+ struct scatterlist sg;
+
+ tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ DWC_ERROR("Failed to load transform for hmac(sha256): %ld\n", PTR_ERR(tfm));
+ return 0;
+ }
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ sg_init_one(&sg, message, messagelen);
+ crypto_hash_setkey(tfm, key, keylen);
+ crypto_hash_digest(&desc, &sg, messagelen, out);
+ crypto_free_hash(tfm);
+
+ return 1;
+}
+#endif /* DWC_CRYPTOLIB */
+
+
+/* Byte Ordering Conversions */
+
+uint32_t DWC_CPU_TO_LE32(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_CPU_TO_BE32(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_LE32_TO_CPU(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_BE32_TO_CPU(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint16_t DWC_CPU_TO_LE16(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_CPU_TO_BE16(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_LE16_TO_CPU(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_BE16_TO_CPU(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+
+/* Registers */
+
+uint32_t DWC_READ_REG32(uint32_t volatile *reg)
+{
+ return readl(reg);
+}
+
+#if 0
+uint64_t DWC_READ_REG64(uint64_t volatile *reg)
+{
+}
+#endif
+
+void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value)
+{
+ writel(value, reg);
+}
+
+#if 0
+void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value)
+{
+}
+#endif
+
+void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask)
+{
+ writel((readl(reg) & ~clear_mask) | set_mask, reg);
+}
+
+#if 0
+void DWC_MODIFY_REG64(uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask)
+{
+}
+#endif
+
+
+/* Locking */
+
+dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void)
+{
+ spinlock_t *sl = (spinlock_t *)1;
+
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+ sl = DWC_ALLOC(sizeof(*sl));
+ if (!sl) {
+ DWC_ERROR("Cannot allocate memory for spinlock\n");
+ return NULL;
+ }
+
+ spin_lock_init(sl);
+#endif
+ return (dwc_spinlock_t *)sl;
+}
+
+void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock)
+{
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+ DWC_FREE(lock);
+#endif
+}
+
+void DWC_SPINLOCK(dwc_spinlock_t *lock)
+{
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+ spin_lock((spinlock_t *)lock);
+#endif
+}
+
+void DWC_SPINUNLOCK(dwc_spinlock_t *lock)
+{
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+ spin_unlock((spinlock_t *)lock);
+#endif
+}
+
+void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags)
+{
+ dwc_irqflags_t f;
+
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+ spin_lock_irqsave((spinlock_t *)lock, f);
+#else
+ local_irq_save(f);
+#endif
+ *flags = f;
+}
+
+void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags)
+{
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+ spin_unlock_irqrestore((spinlock_t *)lock, flags);
+#else
+ local_irq_restore(flags);
+#endif
+}
+
+dwc_mutex_t *DWC_MUTEX_ALLOC(void)
+{
+ struct mutex *m;
+ dwc_mutex_t *mutex = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mutex));
+
+ if (!mutex) {
+ DWC_ERROR("Cannot allocate memory for mutex\n");
+ return NULL;
+ }
+
+ m = (struct mutex *)mutex;
+ mutex_init(m);
+ return mutex;
+}
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+#else
+void DWC_MUTEX_FREE(dwc_mutex_t *mutex)
+{
+ mutex_destroy((struct mutex *)mutex);
+ DWC_FREE(mutex);
+}
+#endif
+
+void DWC_MUTEX_LOCK(dwc_mutex_t *mutex)
+{
+ struct mutex *m = (struct mutex *)mutex;
+ mutex_lock(m);
+}
+
+int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex)
+{
+ struct mutex *m = (struct mutex *)mutex;
+ return mutex_trylock(m);
+}
+
+void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex)
+{
+ struct mutex *m = (struct mutex *)mutex;
+ mutex_unlock(m);
+}
+
+
+/* Timing */
+
+void DWC_UDELAY(uint32_t usecs)
+{
+ udelay(usecs);
+}
+
+void DWC_MDELAY(uint32_t msecs)
+{
+ mdelay(msecs);
+}
+
+void DWC_MSLEEP(uint32_t msecs)
+{
+ msleep(msecs);
+}
+
+uint32_t DWC_TIME(void)
+{
+ return jiffies_to_msecs(jiffies);
+}
+
+
+/* Timers */
+
+struct dwc_timer {
+ struct timer_list *t;
+ char *name;
+ dwc_timer_callback_t cb;
+ void *data;
+ uint8_t scheduled;
+ dwc_spinlock_t *lock;
+};
+
+static void timer_callback(unsigned long data)
+{
+ dwc_timer_t *timer = (dwc_timer_t *)data;
+ dwc_irqflags_t flags;
+
+ DWC_SPINLOCK_IRQSAVE(timer->lock, &flags);
+ timer->scheduled = 0;
+ DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags);
+ DWC_DEBUG("Timer %s callback", timer->name);
+ timer->cb(timer->data);
+}
+
+dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
+{
+ dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
+
+ if (!t) {
+ DWC_ERROR("Cannot allocate memory for timer");
+ return NULL;
+ }
+
+ t->t = DWC_ALLOC(sizeof(*t->t));
+ if (!t->t) {
+ DWC_ERROR("Cannot allocate memory for timer->t");
+ goto no_timer;
+ }
+
+ t->name = DWC_STRDUP(name);
+ if (!t->name) {
+ DWC_ERROR("Cannot allocate memory for timer->name");
+ goto no_name;
+ }
+
+ t->lock = DWC_SPINLOCK_ALLOC();
+ if (!t->lock) {
+ DWC_ERROR("Cannot allocate memory for lock");
+ goto no_lock;
+ }
+
+ t->scheduled = 0;
+ t->t->base = &boot_tvec_bases;
+ t->t->expires = jiffies;
+ setup_timer(t->t, timer_callback, (unsigned long)t);
+
+ t->cb = cb;
+ t->data = data;
+
+ return t;
+
+ no_lock:
+ DWC_FREE(t->name);
+ no_name:
+ DWC_FREE(t->t);
+ no_timer:
+ DWC_FREE(t);
+ return NULL;
+}
+
+void DWC_TIMER_FREE(dwc_timer_t *timer)
+{
+ dwc_irqflags_t flags;
+
+ DWC_SPINLOCK_IRQSAVE(timer->lock, &flags);
+
+ if (timer->scheduled) {
+ del_timer(timer->t);
+ timer->scheduled = 0;
+ }
+
+ DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags);
+ DWC_SPINLOCK_FREE(timer->lock);
+ DWC_FREE(timer->t);
+ DWC_FREE(timer->name);
+ DWC_FREE(timer);
+}
+
+void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time)
+{
+ dwc_irqflags_t flags;
+
+ DWC_SPINLOCK_IRQSAVE(timer->lock, &flags);
+
+ if (!timer->scheduled) {
+ timer->scheduled = 1;
+ DWC_DEBUG("Scheduling timer %s to expire in +%d msec", timer->name, time);
+ timer->t->expires = jiffies + msecs_to_jiffies(time);
+ add_timer(timer->t);
+ } else {
+ DWC_DEBUG("Modifying timer %s to expire in +%d msec", timer->name, time);
+ mod_timer(timer->t, jiffies + msecs_to_jiffies(time));
+ }
+
+ DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags);
+}
+
+void DWC_TIMER_CANCEL(dwc_timer_t *timer)
+{
+ del_timer(timer->t);
+}
+
+
+/* Wait Queues */
+
+struct dwc_waitq {
+ wait_queue_head_t queue;
+ int abort;
+};
+
+dwc_waitq_t *DWC_WAITQ_ALLOC(void)
+{
+ dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+ if (!wq) {
+ DWC_ERROR("Cannot allocate memory for waitqueue\n");
+ return NULL;
+ }
+
+ init_waitqueue_head(&wq->queue);
+ wq->abort = 0;
+ return wq;
+}
+
+void DWC_WAITQ_FREE(dwc_waitq_t *wq)
+{
+ DWC_FREE(wq);
+}
+
+int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data)
+{
+ int result = wait_event_interruptible(wq->queue,
+ cond(data) || wq->abort);
+ if (result == -ERESTARTSYS) {
+ wq->abort = 0;
+ return -DWC_E_RESTART;
+ }
+
+ if (wq->abort == 1) {
+ wq->abort = 0;
+ return -DWC_E_ABORT;
+ }
+
+ wq->abort = 0;
+
+ if (result == 0) {
+ return 0;
+ }
+
+ return -DWC_E_UNKNOWN;
+}
+
+int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
+ void *data, int32_t msecs)
+{
+ int32_t tmsecs;
+ int result = wait_event_interruptible_timeout(wq->queue,
+ cond(data) || wq->abort,
+ msecs_to_jiffies(msecs));
+ if (result == -ERESTARTSYS) {
+ wq->abort = 0;
+ return -DWC_E_RESTART;
+ }
+
+ if (wq->abort == 1) {
+ wq->abort = 0;
+ return -DWC_E_ABORT;
+ }
+
+ wq->abort = 0;
+
+ if (result > 0) {
+ tmsecs = jiffies_to_msecs(result);
+ if (!tmsecs) {
+ return 1;
+ }
+
+ return tmsecs;
+ }
+
+ if (result == 0) {
+ return -DWC_E_TIMEOUT;
+ }
+
+ return -DWC_E_UNKNOWN;
+}
+
+void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq)
+{
+ wq->abort = 0;
+ wake_up_interruptible(&wq->queue);
+}
+
+void DWC_WAITQ_ABORT(dwc_waitq_t *wq)
+{
+ wq->abort = 1;
+ wake_up_interruptible(&wq->queue);
+}
+
+
+/* Threading */
+
+dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
+{
+ struct task_struct *thread = kthread_run(func, data, name);
+
+ if (thread == ERR_PTR(-ENOMEM)) {
+ return NULL;
+ }
+
+ return (dwc_thread_t *)thread;
+}
+
+int DWC_THREAD_STOP(dwc_thread_t *thread)
+{
+ return kthread_stop((struct task_struct *)thread);
+}
+
+dwc_bool_t DWC_THREAD_SHOULD_STOP(void)
+{
+ return kthread_should_stop();
+}
+
+
+/* tasklets
+ - run in interrupt context (cannot sleep)
+ - each tasklet runs on a single CPU
+ - different tasklets can be running simultaneously on different CPUs
+ */
+struct dwc_tasklet {
+ struct tasklet_struct t;
+ dwc_tasklet_callback_t cb;
+ void *data;
+};
+
+static void tasklet_callback(unsigned long data)
+{
+ dwc_tasklet_t *t = (dwc_tasklet_t *)data;
+ t->cb(t->data);
+}
+
+dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data)
+{
+ dwc_tasklet_t *t = DWC_ALLOC(sizeof(*t));
+
+ if (t) {
+ t->cb = cb;
+ t->data = data;
+ tasklet_init(&t->t, tasklet_callback, (unsigned long)t);
+ } else {
+ DWC_ERROR("Cannot allocate memory for tasklet\n");
+ }
+
+ return t;
+}
+
+void DWC_TASK_FREE(dwc_tasklet_t *task)
+{
+ DWC_FREE(task);
+}
+
+void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
+{
+ tasklet_schedule(&task->t);
+}
+
+
+/* workqueues
+ - run in process context (can sleep)
+ */
+typedef struct work_container {
+ dwc_work_callback_t cb;
+ void *data;
+ dwc_workq_t *wq;
+ char *name;
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_ENTRY(work_container) entry;
+#endif
+ struct delayed_work work;
+} work_container_t;
+
+#ifdef DEBUG
+DWC_CIRCLEQ_HEAD(work_container_queue, work_container);
+#endif
+
+struct dwc_workq {
+ struct workqueue_struct *wq;
+ dwc_spinlock_t *lock;
+ dwc_waitq_t *waitq;
+ int pending;
+
+#ifdef DEBUG
+ struct work_container_queue entries;
+#endif
+};
+
+static void do_work(struct work_struct *work)
+{
+ dwc_irqflags_t flags;
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ work_container_t *container = container_of(dw, struct work_container, work);
+ dwc_workq_t *wq = container->wq;
+
+ container->cb(container->data);
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_REMOVE(&wq->entries, container, entry);
+#endif
+ DWC_DEBUG("Work done: %s, container=%p", container->name, container);
+ if (container->name) {
+ DWC_FREE(container->name);
+ }
+ DWC_FREE(container);
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+ wq->pending--;
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+ DWC_WAITQ_TRIGGER(wq->waitq);
+}
+
+static int work_done(void *data)
+{
+ dwc_workq_t *workq = (dwc_workq_t *)data;
+ return workq->pending == 0;
+}
+
+int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout)
+{
+ return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout);
+}
+
+dwc_workq_t *DWC_WORKQ_ALLOC(char *name)
+{
+ dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+ if (!wq) {
+ return NULL;
+ }
+
+ wq->wq = create_singlethread_workqueue(name);
+ if (!wq->wq) {
+ goto no_wq;
+ }
+
+ wq->pending = 0;
+
+ wq->lock = DWC_SPINLOCK_ALLOC();
+ if (!wq->lock) {
+ goto no_lock;
+ }
+
+ wq->waitq = DWC_WAITQ_ALLOC();
+ if (!wq->waitq) {
+ goto no_waitq;
+ }
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_INIT(&wq->entries);
+#endif
+ return wq;
+
+ no_waitq:
+ DWC_SPINLOCK_FREE(wq->lock);
+ no_lock:
+ destroy_workqueue(wq->wq);
+ no_wq:
+ DWC_FREE(wq);
+
+ return NULL;
+}
+
+void DWC_WORKQ_FREE(dwc_workq_t *wq)
+{
+#ifdef DEBUG
+ if (wq->pending != 0) {
+ struct work_container *wc;
+ DWC_ERROR("Destroying work queue with pending work");
+ DWC_CIRCLEQ_FOREACH(wc, &wq->entries, entry) {
+ DWC_ERROR("Work %s still pending", wc->name);
+ }
+ }
+#endif
+ destroy_workqueue(wq->wq);
+ DWC_SPINLOCK_FREE(wq->lock);
+ DWC_WAITQ_FREE(wq->waitq);
+ DWC_FREE(wq);
+}
+
+void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data,
+ char *format, ...)
+{
+ dwc_irqflags_t flags;
+ work_container_t *container;
+ static char name[128];
+ va_list args;
+
+ va_start(args, format);
+ DWC_VSNPRINTF(name, 128, format, args);
+ va_end(args);
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+ wq->pending++;
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+ DWC_WAITQ_TRIGGER(wq->waitq);
+
+ container = DWC_ALLOC_ATOMIC(sizeof(*container));
+ if (!container) {
+ DWC_ERROR("Cannot allocate memory for container\n");
+ return;
+ }
+
+ container->name = DWC_STRDUP(name);
+ if (!container->name) {
+ DWC_ERROR("Cannot allocate memory for container->name\n");
+ DWC_FREE(container);
+ return;
+ }
+
+ container->cb = cb;
+ container->data = data;
+ container->wq = wq;
+ DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+ INIT_WORK(&container->work.work, do_work);
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
+#endif
+ queue_work(wq->wq, &container->work.work);
+}
+
+void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb,
+ void *data, uint32_t time, char *format, ...)
+{
+ dwc_irqflags_t flags;
+ work_container_t *container;
+ static char name[128];
+ va_list args;
+
+ va_start(args, format);
+ DWC_VSNPRINTF(name, 128, format, args);
+ va_end(args);
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+ wq->pending++;
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+ DWC_WAITQ_TRIGGER(wq->waitq);
+
+ container = DWC_ALLOC_ATOMIC(sizeof(*container));
+ if (!container) {
+ DWC_ERROR("Cannot allocate memory for container\n");
+ return;
+ }
+
+ container->name = DWC_STRDUP(name);
+ if (!container->name) {
+ DWC_ERROR("Cannot allocate memory for container->name\n");
+ DWC_FREE(container);
+ return;
+ }
+
+ container->cb = cb;
+ container->data = data;
+ container->wq = wq;
+ DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+ INIT_DELAYED_WORK(&container->work, do_work);
+
+#ifdef DEBUG
+ DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
+#endif
+ queue_delayed_work(wq->wq, &container->work, msecs_to_jiffies(time));
+}
+
+int DWC_WORKQ_PENDING(dwc_workq_t *wq)
+{
+ return wq->pending;
+}
+
+
+#ifdef DWC_LIBMODULE
+
+#ifdef DWC_CCLIB
+/* CC */
+EXPORT_SYMBOL(dwc_cc_if_alloc);
+EXPORT_SYMBOL(dwc_cc_if_free);
+EXPORT_SYMBOL(dwc_cc_clear);
+EXPORT_SYMBOL(dwc_cc_add);
+EXPORT_SYMBOL(dwc_cc_remove);
+EXPORT_SYMBOL(dwc_cc_change);
+EXPORT_SYMBOL(dwc_cc_data_for_save);
+EXPORT_SYMBOL(dwc_cc_restore_from_data);
+EXPORT_SYMBOL(dwc_cc_match_chid);
+EXPORT_SYMBOL(dwc_cc_match_cdid);
+EXPORT_SYMBOL(dwc_cc_ck);
+EXPORT_SYMBOL(dwc_cc_chid);
+EXPORT_SYMBOL(dwc_cc_cdid);
+EXPORT_SYMBOL(dwc_cc_name);
+#endif /* DWC_CCLIB */
+
+#ifdef DWC_CRYPTOLIB
+# ifndef CONFIG_MACH_IPMATE
+/* Modpow */
+EXPORT_SYMBOL(dwc_modpow);
+
+/* DH */
+EXPORT_SYMBOL(dwc_dh_modpow);
+EXPORT_SYMBOL(dwc_dh_derive_keys);
+EXPORT_SYMBOL(dwc_dh_pk);
+# endif /* CONFIG_MACH_IPMATE */
+
+/* Crypto */
+EXPORT_SYMBOL(dwc_wusb_aes_encrypt);
+EXPORT_SYMBOL(dwc_wusb_cmf);
+EXPORT_SYMBOL(dwc_wusb_prf);
+EXPORT_SYMBOL(dwc_wusb_fill_ccm_nonce);
+EXPORT_SYMBOL(dwc_wusb_gen_nonce);
+EXPORT_SYMBOL(dwc_wusb_gen_key);
+EXPORT_SYMBOL(dwc_wusb_gen_mic);
+#endif /* DWC_CRYPTOLIB */
+
+/* Notification */
+#ifdef DWC_NOTIFYLIB
+EXPORT_SYMBOL(dwc_alloc_notification_manager);
+EXPORT_SYMBOL(dwc_free_notification_manager);
+EXPORT_SYMBOL(dwc_register_notifier);
+EXPORT_SYMBOL(dwc_unregister_notifier);
+EXPORT_SYMBOL(dwc_add_observer);
+EXPORT_SYMBOL(dwc_remove_observer);
+EXPORT_SYMBOL(dwc_notify);
+#endif
+
+/* Memory Debugging Routines */
+#ifdef DWC_DEBUG_MEMORY
+EXPORT_SYMBOL(dwc_alloc_debug);
+EXPORT_SYMBOL(dwc_alloc_atomic_debug);
+EXPORT_SYMBOL(dwc_free_debug);
+EXPORT_SYMBOL(dwc_dma_alloc_debug);
+EXPORT_SYMBOL(dwc_dma_free_debug);
+#endif
+
+EXPORT_SYMBOL(DWC_MEMSET);
+EXPORT_SYMBOL(DWC_MEMCPY);
+EXPORT_SYMBOL(DWC_MEMMOVE);
+EXPORT_SYMBOL(DWC_MEMCMP);
+EXPORT_SYMBOL(DWC_STRNCMP);
+EXPORT_SYMBOL(DWC_STRCMP);
+EXPORT_SYMBOL(DWC_STRLEN);
+EXPORT_SYMBOL(DWC_STRCPY);
+EXPORT_SYMBOL(DWC_STRDUP);
+EXPORT_SYMBOL(DWC_ATOI);
+EXPORT_SYMBOL(DWC_ATOUI);
+
+#ifdef DWC_UTFLIB
+EXPORT_SYMBOL(DWC_UTF8_TO_UTF16LE);
+#endif /* DWC_UTFLIB */
+
+EXPORT_SYMBOL(DWC_IN_IRQ);
+EXPORT_SYMBOL(DWC_IN_BH);
+EXPORT_SYMBOL(DWC_VPRINTF);
+EXPORT_SYMBOL(DWC_VSNPRINTF);
+EXPORT_SYMBOL(DWC_PRINTF);
+EXPORT_SYMBOL(DWC_SPRINTF);
+EXPORT_SYMBOL(DWC_SNPRINTF);
+EXPORT_SYMBOL(__DWC_WARN);
+EXPORT_SYMBOL(__DWC_ERROR);
+EXPORT_SYMBOL(DWC_EXCEPTION);
+
+#ifdef DEBUG
+EXPORT_SYMBOL(__DWC_DEBUG);
+#endif
+
+EXPORT_SYMBOL(__DWC_DMA_ALLOC);
+EXPORT_SYMBOL(__DWC_DMA_ALLOC_ATOMIC);
+EXPORT_SYMBOL(__DWC_DMA_FREE);
+EXPORT_SYMBOL(__DWC_ALLOC);
+EXPORT_SYMBOL(__DWC_ALLOC_ATOMIC);
+EXPORT_SYMBOL(__DWC_FREE);
+
+#ifdef DWC_CRYPTOLIB
+EXPORT_SYMBOL(DWC_RANDOM_BYTES);
+EXPORT_SYMBOL(DWC_AES_CBC);
+EXPORT_SYMBOL(DWC_SHA256);
+EXPORT_SYMBOL(DWC_HMAC_SHA256);
+#endif
+
+EXPORT_SYMBOL(DWC_CPU_TO_LE32);
+EXPORT_SYMBOL(DWC_CPU_TO_BE32);
+EXPORT_SYMBOL(DWC_LE32_TO_CPU);
+EXPORT_SYMBOL(DWC_BE32_TO_CPU);
+EXPORT_SYMBOL(DWC_CPU_TO_LE16);
+EXPORT_SYMBOL(DWC_CPU_TO_BE16);
+EXPORT_SYMBOL(DWC_LE16_TO_CPU);
+EXPORT_SYMBOL(DWC_BE16_TO_CPU);
+EXPORT_SYMBOL(DWC_READ_REG32);
+EXPORT_SYMBOL(DWC_WRITE_REG32);
+EXPORT_SYMBOL(DWC_MODIFY_REG32);
+
+#if 0
+EXPORT_SYMBOL(DWC_READ_REG64);
+EXPORT_SYMBOL(DWC_WRITE_REG64);
+EXPORT_SYMBOL(DWC_MODIFY_REG64);
+#endif
+
+EXPORT_SYMBOL(DWC_SPINLOCK_ALLOC);
+EXPORT_SYMBOL(DWC_SPINLOCK_FREE);
+EXPORT_SYMBOL(DWC_SPINLOCK);
+EXPORT_SYMBOL(DWC_SPINUNLOCK);
+EXPORT_SYMBOL(DWC_SPINLOCK_IRQSAVE);
+EXPORT_SYMBOL(DWC_SPINUNLOCK_IRQRESTORE);
+EXPORT_SYMBOL(DWC_MUTEX_ALLOC);
+
+#if (!defined(DWC_LINUX) || !defined(CONFIG_DEBUG_MUTEXES))
+EXPORT_SYMBOL(DWC_MUTEX_FREE);
+#endif
+
+EXPORT_SYMBOL(DWC_MUTEX_LOCK);
+EXPORT_SYMBOL(DWC_MUTEX_TRYLOCK);
+EXPORT_SYMBOL(DWC_MUTEX_UNLOCK);
+EXPORT_SYMBOL(DWC_UDELAY);
+EXPORT_SYMBOL(DWC_MDELAY);
+EXPORT_SYMBOL(DWC_MSLEEP);
+EXPORT_SYMBOL(DWC_TIME);
+EXPORT_SYMBOL(DWC_TIMER_ALLOC);
+EXPORT_SYMBOL(DWC_TIMER_FREE);
+EXPORT_SYMBOL(DWC_TIMER_SCHEDULE);
+EXPORT_SYMBOL(DWC_TIMER_CANCEL);
+EXPORT_SYMBOL(DWC_WAITQ_ALLOC);
+EXPORT_SYMBOL(DWC_WAITQ_FREE);
+EXPORT_SYMBOL(DWC_WAITQ_WAIT);
+EXPORT_SYMBOL(DWC_WAITQ_WAIT_TIMEOUT);
+EXPORT_SYMBOL(DWC_WAITQ_TRIGGER);
+EXPORT_SYMBOL(DWC_WAITQ_ABORT);
+EXPORT_SYMBOL(DWC_THREAD_RUN);
+EXPORT_SYMBOL(DWC_THREAD_STOP);
+EXPORT_SYMBOL(DWC_THREAD_SHOULD_STOP);
+EXPORT_SYMBOL(DWC_TASK_ALLOC);
+EXPORT_SYMBOL(DWC_TASK_FREE);
+EXPORT_SYMBOL(DWC_TASK_SCHEDULE);
+EXPORT_SYMBOL(DWC_WORKQ_WAIT_WORK_DONE);
+EXPORT_SYMBOL(DWC_WORKQ_ALLOC);
+EXPORT_SYMBOL(DWC_WORKQ_FREE);
+EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE);
+EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE_DELAYED);
+EXPORT_SYMBOL(DWC_WORKQ_PENDING);
+
+static int dwc_common_port_init_module(void)
+{
+ int result = 0;
+
+ printk(KERN_DEBUG "Module dwc_common_port init\n" );
+
+#ifdef DWC_DEBUG_MEMORY
+ result = dwc_memory_debug_start(NULL);
+ if (result) {
+ printk(KERN_ERR
+ "dwc_memory_debug_start() failed with error %d\n",
+ result);
+ return result;
+ }
+#endif
+
+#ifdef DWC_NOTIFYLIB
+ result = dwc_alloc_notification_manager(NULL, NULL);
+ if (result) {
+ printk(KERN_ERR
+ "dwc_alloc_notification_manager() failed with error %d\n",
+ result);
+ return result;
+ }
+#endif
+ return result;
+}
+
+static void dwc_common_port_exit_module(void)
+{
+ printk(KERN_DEBUG "Module dwc_common_port exit\n" );
+
+#ifdef DWC_NOTIFYLIB
+ dwc_free_notification_manager();
+#endif
+
+#ifdef DWC_DEBUG_MEMORY
+ dwc_memory_debug_stop();
+#endif
+}
+
+module_init(dwc_common_port_init_module);
+module_exit(dwc_common_port_exit_module);
+
+MODULE_DESCRIPTION("DWC Common Library - Portable version");
+MODULE_AUTHOR("Synopsys Inc.");
+MODULE_LICENSE ("GPL");
+
+#endif /* DWC_LIBMODULE */
diff --git a/drivers/usb/dwc_otg/dwc_common_nbsd.c b/drivers/usb/dwc_otg/dwc_common_nbsd.c
new file mode 100644
index 0000000..49b07e1
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_common_nbsd.c
@@ -0,0 +1,1275 @@
+#include "dwc_os.h"
+#include "dwc_list.h"
+
+#ifdef DWC_CCLIB
+# include "dwc_cc.h"
+#endif
+
+#ifdef DWC_CRYPTOLIB
+# include "dwc_modpow.h"
+# include "dwc_dh.h"
+# include "dwc_crypto.h"
+#endif
+
+#ifdef DWC_NOTIFYLIB
+# include "dwc_notifier.h"
+#endif
+
+/* OS-Level Implementations */
+
+/* This is the NetBSD 4.0.1 kernel implementation of the DWC platform library. */
+
+
+/* MISC */
+
+void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size)
+{
+ return memset(dest, byte, size);
+}
+
+void *DWC_MEMCPY(void *dest, void const *src, uint32_t size)
+{
+ return memcpy(dest, src, size);
+}
+
+void *DWC_MEMMOVE(void *dest, void *src, uint32_t size)
+{
+ bcopy(src, dest, size);
+ return dest;
+}
+
+int DWC_MEMCMP(void *m1, void *m2, uint32_t size)
+{
+ return memcmp(m1, m2, size);
+}
+
+int DWC_STRNCMP(void *s1, void *s2, uint32_t size)
+{
+ return strncmp(s1, s2, size);
+}
+
+int DWC_STRCMP(void *s1, void *s2)
+{
+ return strcmp(s1, s2);
+}
+
+int DWC_STRLEN(char const *str)
+{
+ return strlen(str);
+}
+
+char *DWC_STRCPY(char *to, char const *from)
+{
+ return strcpy(to, from);
+}
+
+char *DWC_STRDUP(char const *str)
+{
+ int len = DWC_STRLEN(str) + 1;
+ char *new = DWC_ALLOC_ATOMIC(len);
+
+ if (!new) {
+ return NULL;
+ }
+
+ DWC_MEMCPY(new, str, len);
+ return new;
+}
+
+int DWC_ATOI(char *str, int32_t *value)
+{
+ char *end = NULL;
+
+ /* NetBSD doesn't have 'strtol' in the kernel, but 'strtoul'
+ * should be equivalent on 2's complement machines
+ */
+ *value = strtoul(str, &end, 0);
+ if (*end == '\0') {
+ return 0;
+ }
+
+ return -1;
+}
+
+int DWC_ATOUI(char *str, uint32_t *value)
+{
+ char *end = NULL;
+
+ *value = strtoul(str, &end, 0);
+ if (*end == '\0') {
+ return 0;
+ }
+
+ return -1;
+}
+
+
+#ifdef DWC_UTFLIB
+/* From usbstring.c */
+
+int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len)
+{
+ int count = 0;
+ u8 c;
+ u16 uchar;
+
+ /* this insists on correct encodings, though not minimal ones.
+ * BUT it currently rejects legit 4-byte UTF-8 code points,
+ * which need surrogate pairs. (Unicode 3.1 can use them.)
+ */
+ while (len != 0 && (c = (u8) *s++) != 0) {
+ if (unlikely(c & 0x80)) {
+ // 2-byte sequence:
+ // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+ if ((c & 0xe0) == 0xc0) {
+ uchar = (c & 0x1f) << 6;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0xc0)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c;
+
+ // 3-byte sequence (most CJKV characters):
+ // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+ } else if ((c & 0xf0) == 0xe0) {
+ uchar = (c & 0x0f) << 12;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0xc0)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c << 6;
+
+ c = (u8) *s++;
+ if ((c & 0xc0) != 0xc0)
+ goto fail;
+ c &= 0x3f;
+ uchar |= c;
+
+ /* no bogus surrogates */
+ if (0xd800 <= uchar && uchar <= 0xdfff)
+ goto fail;
+
+ // 4-byte sequence (surrogate pairs, currently rare):
+ // 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+ // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+ // (uuuuu = wwww + 1)
+ // FIXME accept the surrogate code points (only)
+ } else
+ goto fail;
+ } else
+ uchar = c;
+ put_unaligned (cpu_to_le16 (uchar), cp++);
+ count++;
+ len--;
+ }
+ return count;
+fail:
+ return -1;
+}
+
+#endif /* DWC_UTFLIB */
+
+
+/* dwc_debug.h */
+
+dwc_bool_t DWC_IN_IRQ(void)
+{
+// return in_irq();
+ return 0;
+}
+
+dwc_bool_t DWC_IN_BH(void)
+{
+// return in_softirq();
+ return 0;
+}
+
+void DWC_VPRINTF(char *format, va_list args)
+{
+ vprintf(format, args);
+}
+
+int DWC_VSNPRINTF(char *str, int size, char *format, va_list args)
+{
+ return vsnprintf(str, size, format, args);
+}
+
+void DWC_PRINTF(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+
+int DWC_SPRINTF(char *buffer, char *format, ...)
+{
+ int retval;
+ va_list args;
+
+ va_start(args, format);
+ retval = vsprintf(buffer, format, args);
+ va_end(args);
+ return retval;
+}
+
+int DWC_SNPRINTF(char *buffer, int size, char *format, ...)
+{
+ int retval;
+ va_list args;
+
+ va_start(args, format);
+ retval = vsnprintf(buffer, size, format, args);
+ va_end(args);
+ return retval;
+}
+
+void __DWC_WARN(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+
+void __DWC_ERROR(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+
+void DWC_EXCEPTION(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+// BUG_ON(1); ???
+}
+
+#ifdef DEBUG
+void __DWC_DEBUG(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ DWC_VPRINTF(format, args);
+ va_end(args);
+}
+#endif
+
+
+/* dwc_mem.h */
+
+#if 0
+dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size,
+ uint32_t align,
+ uint32_t alloc)
+{
+ struct dma_pool *pool = dma_pool_create("Pool", NULL,
+ size, align, alloc);
+ return (dwc_pool_t *)pool;
+}
+
+void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool)
+{
+ dma_pool_destroy((struct dma_pool *)pool);
+}
+
+void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+// return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr);
+ return dma_pool_alloc((struct dma_pool *)pool, M_WAITOK, dma_addr);
+}
+
+void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+ void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr);
+ memset(..);
+}
+
+void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr)
+{
+ dma_pool_free(pool, vaddr, daddr);
+}
+#endif
+
+void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
+{
+ dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
+ int error;
+
+ error = bus_dmamem_alloc(dma->dma_tag, size, 1, size, dma->segs,
+ sizeof(dma->segs) / sizeof(dma->segs[0]),
+ &dma->nsegs, BUS_DMA_NOWAIT);
+ if (error) {
+ printf("%s: bus_dmamem_alloc(%ju) failed: %d\n", __func__,
+ (uintmax_t)size, error);
+ goto fail_0;
+ }
+
+ error = bus_dmamem_map(dma->dma_tag, dma->segs, dma->nsegs, size,
+ (caddr_t *)&dma->dma_vaddr,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
+ if (error) {
+ printf("%s: bus_dmamem_map failed: %d\n", __func__, error);
+ goto fail_1;
+ }
+
+ error = bus_dmamap_create(dma->dma_tag, size, 1, size, 0,
+ BUS_DMA_NOWAIT, &dma->dma_map);
+ if (error) {
+ printf("%s: bus_dmamap_create failed: %d\n", __func__, error);
+ goto fail_2;
+ }
+
+ error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr,
+ size, NULL, BUS_DMA_NOWAIT);
+ if (error) {
+ printf("%s: bus_dmamap_load failed: %d\n", __func__, error);
+ goto fail_3;
+ }
+
+ dma->dma_paddr = (bus_addr_t)dma->segs[0].ds_addr;
+ *dma_addr = dma->dma_paddr;
+ return dma->dma_vaddr;
+
+fail_3:
+ bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
+fail_2:
+ bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size);
+fail_1:
+ bus_dmamem_free(dma->dma_tag, dma->segs, dma->nsegs);
+fail_0:
+ dma->dma_map = NULL;
+ dma->dma_vaddr = NULL;
+ dma->nsegs = 0;
+
+ return NULL;
+}
+
+void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr)
+{
+ dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
+
+ if (dma->dma_map != NULL) {
+ bus_dmamap_sync(dma->dma_tag, dma->dma_map, 0, size,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+ bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
+ bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size);
+ bus_dmamem_free(dma->dma_tag, dma->segs, dma->nsegs);
+ dma->dma_paddr = 0;
+ dma->dma_map = NULL;
+ dma->dma_vaddr = NULL;
+ dma->nsegs = 0;
+ }
+}
+
+void *__DWC_ALLOC(void *mem_ctx, uint32_t size)
+{
+ return malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+}
+
+void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size)
+{
+ return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+}
+
+void __DWC_FREE(void *mem_ctx, void *addr)
+{
+ free(addr, M_DEVBUF);
+}
+
+
+#ifdef DWC_CRYPTOLIB
+/* dwc_crypto.h */
+
+void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length)
+{
+ get_random_bytes(buffer, length);
+}
+
+int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out)
+{
+ struct crypto_blkcipher *tfm;
+ struct blkcipher_desc desc;
+ struct scatterlist sgd;
+ struct scatterlist sgs;
+
+ tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (tfm == NULL) {
+ printk("failed to load transform for aes CBC\n");
+ return -1;
+ }
+
+ crypto_blkcipher_setkey(tfm, key, keylen);
+ crypto_blkcipher_set_iv(tfm, iv, 16);
+
+ sg_init_one(&sgd, out, messagelen);
+ sg_init_one(&sgs, message, messagelen);
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) {
+ crypto_free_blkcipher(tfm);
+ DWC_ERROR("AES CBC encryption failed");
+ return -1;
+ }
+
+ crypto_free_blkcipher(tfm);
+ return 0;
+}
+
+int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out)
+{
+ struct crypto_hash *tfm;
+ struct hash_desc desc;
+ struct scatterlist sg;
+
+ tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ DWC_ERROR("Failed to load transform for sha256: %ld", PTR_ERR(tfm));
+ return 0;
+ }
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ sg_init_one(&sg, message, len);
+ crypto_hash_digest(&desc, &sg, len, out);
+ crypto_free_hash(tfm);
+
+ return 1;
+}
+
+int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen,
+ uint8_t *key, uint32_t keylen, uint8_t *out)
+{
+ struct crypto_hash *tfm;
+ struct hash_desc desc;
+ struct scatterlist sg;
+
+ tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ DWC_ERROR("Failed to load transform for hmac(sha256): %ld", PTR_ERR(tfm));
+ return 0;
+ }
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ sg_init_one(&sg, message, messagelen);
+ crypto_hash_setkey(tfm, key, keylen);
+ crypto_hash_digest(&desc, &sg, messagelen, out);
+ crypto_free_hash(tfm);
+
+ return 1;
+}
+
+#endif /* DWC_CRYPTOLIB */
+
+
+/* Byte Ordering Conversions */
+
+uint32_t DWC_CPU_TO_LE32(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_CPU_TO_BE32(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_LE32_TO_CPU(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_BE32_TO_CPU(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+
+ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint16_t DWC_CPU_TO_LE16(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_CPU_TO_BE16(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_LE16_TO_CPU(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_BE16_TO_CPU(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+ return *p;
+#else
+ uint8_t *u_p = (uint8_t *)p;
+ return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+
+/* Registers */
+
+uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ return bus_space_read_4(io->iot, io->ioh, ior);
+}
+
+#if 0
+uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ return bus_space_read_8(io->iot, io->ioh, ior);
+}
+#endif
+
+void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ bus_space_write_4(io->iot, io->ioh, ior, value);
+}
+
+#if 0
+void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ bus_space_write_8(io->iot, io->ioh, ior, value);
+}
+#endif
+
+void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask,
+ uint32_t set_mask)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ bus_space_write_4(io->iot, io->ioh, ior,
+ (bus_space_read_4(io->iot, io->ioh, ior) &
+ ~clear_mask) | set_mask);
+}
+
+#if 0
+void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask,
+ uint64_t set_mask)
+{
+ dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+ bus_size_t ior = (bus_size_t)reg;
+
+ bus_space_write_8(io->iot, io->ioh, ior,
+ (bus_space_read_8(io->iot, io->ioh, ior) &
+ ~clear_mask) | set_mask);
+}
+#endif
+
+
+/* Locking */
+
+dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void)
+{
+ struct simplelock *sl = DWC_ALLOC(sizeof(*sl));
+
+ if (!sl) {
+ DWC_ERROR("Cannot allocate memory for spinlock");
+ return NULL;
+ }
+
+ simple_lock_init(sl);
+ return (dwc_spinlock_t *)sl;
+}
+
+void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock)
+{
+ struct simplelock *sl = (struct simplelock *)lock;
+
+ DWC_FREE(sl);
+}
+
+void DWC_SPINLOCK(dwc_spinlock_t *lock)
+{
+ simple_lock((struct simplelock *)lock);
+}
+
+void DWC_SPINUNLOCK(dwc_spinlock_t *lock)
+{
+ simple_unlock((struct simplelock *)lock);
+}
+
+void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags)
+{
+ simple_lock((struct simplelock *)lock);
+ *flags = splbio();
+}
+
+void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags)
+{
+ splx(flags);
+ simple_unlock((struct simplelock *)lock);
+}
+
+dwc_mutex_t *DWC_MUTEX_ALLOC(void)
+{
+ dwc_mutex_t *mutex = DWC_ALLOC(sizeof(struct lock));
+
+ if (!mutex) {
+ DWC_ERROR("Cannot allocate memory for mutex");
+ return NULL;
+ }
+
+ lockinit((struct lock *)mutex, 0, "dw3mtx", 0, 0);
+ return mutex;
+}
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+#else
+void DWC_MUTEX_FREE(dwc_mutex_t *mutex)
+{
+ DWC_FREE(mutex);
+}
+#endif
+
+void DWC_MUTEX_LOCK(dwc_mutex_t *mutex)
+{
+ lockmgr((struct lock *)mutex, LK_EXCLUSIVE, NULL);
+}
+
+int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex)
+{
+ int status;
+
+ status = lockmgr((struct lock *)mutex, LK_EXCLUSIVE | LK_NOWAIT, NULL);
+ return status == 0;
+}
+
+void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex)
+{
+ lockmgr((struct lock *)mutex, LK_RELEASE, NULL);
+}
+
+
+/* Timing */
+
+void DWC_UDELAY(uint32_t usecs)
+{
+ DELAY(usecs);
+}
+
+void DWC_MDELAY(uint32_t msecs)
+{
+ do {
+ DELAY(1000);
+ } while (--msecs);
+}
+
+void DWC_MSLEEP(uint32_t msecs)
+{
+ struct timeval tv;
+
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
+ tsleep(&tv, 0, "dw3slp", tvtohz(&tv));
+}
+
+uint32_t DWC_TIME(void)
+{
+ struct timeval tv;
+
+ microuptime(&tv); // or getmicrouptime? (less precise, but faster)
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+
+/* Timers */
+
+struct dwc_timer {
+ struct callout t;
+ char *name;
+ dwc_spinlock_t *lock;
+ dwc_timer_callback_t cb;
+ void *data;
+};
+
+dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
+{
+ dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
+
+ if (!t) {
+ DWC_ERROR("Cannot allocate memory for timer");
+ return NULL;
+ }
+
+ callout_init(&t->t);
+
+ t->name = DWC_STRDUP(name);
+ if (!t->name) {
+ DWC_ERROR("Cannot allocate memory for timer->name");
+ goto no_name;
+ }
+
+ t->lock = DWC_SPINLOCK_ALLOC();
+ if (!t->lock) {
+ DWC_ERROR("Cannot allocate memory for timer->lock");
+ goto no_lock;
+ }
+
+ t->cb = cb;
+ t->data = data;
+
+ return t;
+
+ no_lock:
+ DWC_FREE(t->name);
+ no_name:
+ DWC_FREE(t);
+
+ return NULL;
+}
+
+void DWC_TIMER_FREE(dwc_timer_t *timer)
+{
+ callout_stop(&timer->t);
+ DWC_SPINLOCK_FREE(timer->lock);
+ DWC_FREE(timer->name);
+ DWC_FREE(timer);
+}
+
+void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time)
+{
+ struct timeval tv;
+
+ tv.tv_sec = time / 1000;
+ tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
+ callout_reset(&timer->t, tvtohz(&tv), timer->cb, timer->data);
+}
+
+void DWC_TIMER_CANCEL(dwc_timer_t *timer)
+{
+ callout_stop(&timer->t);
+}
+
+
+/* Wait Queues */
+
+struct dwc_waitq {
+ struct simplelock lock;
+ int abort;
+};
+
+dwc_waitq_t *DWC_WAITQ_ALLOC(void)
+{
+ dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+ if (!wq) {
+ DWC_ERROR("Cannot allocate memory for waitqueue");
+ return NULL;
+ }
+
+ simple_lock_init(&wq->lock);
+ wq->abort = 0;
+
+ return wq;
+}
+
+void DWC_WAITQ_FREE(dwc_waitq_t *wq)
+{
+ DWC_FREE(wq);
+}
+
+int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data)
+{
+ int ipl;
+ int result = 0;
+
+ simple_lock(&wq->lock);
+ ipl = splbio();
+
+ /* Skip the sleep if already aborted or triggered */
+ if (!wq->abort && !cond(data)) {
+ splx(ipl);
+ result = ltsleep(wq, PCATCH, "dw3wat", 0, &wq->lock); // infinite timeout
+ ipl = splbio();
+ }
+
+ if (result == 0) { // awoken
+ if (wq->abort) {
+ wq->abort = 0;
+ result = -DWC_E_ABORT;
+ } else {
+ result = 0;
+ }
+
+ splx(ipl);
+ simple_unlock(&wq->lock);
+ } else {
+ wq->abort = 0;
+ splx(ipl);
+ simple_unlock(&wq->lock);
+
+ if (result == ERESTART) { // signaled - restart
+ result = -DWC_E_RESTART;
+ } else { // signaled - must be EINTR
+ result = -DWC_E_ABORT;
+ }
+ }
+
+ return result;
+}
+
+int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
+ void *data, int32_t msecs)
+{
+ struct timeval tv, tv1, tv2;
+ int ipl;
+ int result = 0;
+
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
+
+ simple_lock(&wq->lock);
+ ipl = splbio();
+
+ /* Skip the sleep if already aborted or triggered */
+ if (!wq->abort && !cond(data)) {
+ splx(ipl);
+ getmicrouptime(&tv1);
+ result = ltsleep(wq, PCATCH, "dw3wto", tvtohz(&tv), &wq->lock);
+ getmicrouptime(&tv2);
+ ipl = splbio();
+ }
+
+ if (result == 0) { // awoken
+ if (wq->abort) {
+ wq->abort = 0;
+ splx(ipl);
+ simple_unlock(&wq->lock);
+ result = -DWC_E_ABORT;
+ } else {
+ splx(ipl);
+ simple_unlock(&wq->lock);
+
+ tv2.tv_usec -= tv1.tv_usec;
+ if (tv2.tv_usec < 0) {
+ tv2.tv_usec += 1000000;
+ tv2.tv_sec--;
+ }
+
+ tv2.tv_sec -= tv1.tv_sec;
+ result = tv2.tv_sec * 1000 + tv2.tv_usec / 1000;
+ result = msecs - result;
+ if (result <= 0)
+ result = 1;
+ }
+ } else {
+ wq->abort = 0;
+ splx(ipl);
+ simple_unlock(&wq->lock);
+
+ if (result == ERESTART) { // signaled - restart
+ result = -DWC_E_RESTART;
+
+ } else if (result == EINTR) { // signaled - interrupt
+ result = -DWC_E_ABORT;
+
+ } else { // timed out
+ result = -DWC_E_TIMEOUT;
+ }
+ }
+
+ return result;
+}
+
+void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq)
+{
+ wakeup(wq);
+}
+
+void DWC_WAITQ_ABORT(dwc_waitq_t *wq)
+{
+ int ipl;
+
+ simple_lock(&wq->lock);
+ ipl = splbio();
+ wq->abort = 1;
+ wakeup(wq);
+ splx(ipl);
+ simple_unlock(&wq->lock);
+}
+
+
+/* Threading */
+
+struct dwc_thread {
+ struct proc *proc;
+ int abort;
+};
+
+dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
+{
+ int retval;
+ dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread));
+
+ if (!thread) {
+ return NULL;
+ }
+
+ thread->abort = 0;
+ retval = kthread_create1((void (*)(void *))func, data, &thread->proc,
+ "%s", name);
+ if (retval) {
+ DWC_FREE(thread);
+ return NULL;
+ }
+
+ return thread;
+}
+
+int DWC_THREAD_STOP(dwc_thread_t *thread)
+{
+ int retval;
+
+ thread->abort = 1;
+ retval = tsleep(&thread->abort, 0, "dw3stp", 60 * hz);
+
+ if (retval == 0) {
+ /* DWC_THREAD_EXIT() will free the thread struct */
+ return 0;
+ }
+
+ /* NOTE: We leak the thread struct if thread doesn't die */
+
+ if (retval == EWOULDBLOCK) {
+ return -DWC_E_TIMEOUT;
+ }
+
+ return -DWC_E_UNKNOWN;
+}
+
+dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread)
+{
+ return thread->abort;
+}
+
+void DWC_THREAD_EXIT(dwc_thread_t *thread)
+{
+ wakeup(&thread->abort);
+ DWC_FREE(thread);
+ kthread_exit(0);
+}
+
+/* tasklets
+ - Runs in interrupt context (cannot sleep)
+ - Each tasklet runs on a single CPU
+ - Different tasklets can be running simultaneously on different CPUs
+ [ On NetBSD there is no corresponding mechanism, drivers don't have bottom-
+ halves. So we just call the callback directly from DWC_TASK_SCHEDULE() ]
+ */
+struct dwc_tasklet {
+ dwc_tasklet_callback_t cb;
+ void *data;
+};
+
+static void tasklet_callback(void *data)
+{
+ dwc_tasklet_t *task = (dwc_tasklet_t *)data;
+
+ task->cb(task->data);
+}
+
+dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data)
+{
+ dwc_tasklet_t *task = DWC_ALLOC(sizeof(*task));
+
+ if (task) {
+ task->cb = cb;
+ task->data = data;
+ } else {
+ DWC_ERROR("Cannot allocate memory for tasklet");
+ }
+
+ return task;
+}
+
+void DWC_TASK_FREE(dwc_tasklet_t *task)
+{
+ DWC_FREE(task);
+}
+
+void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
+{
+ tasklet_callback(task);
+}
+
+
+/* workqueues
+ - Runs in process context (can sleep)
+ */
+typedef struct work_container {
+ dwc_work_callback_t cb;
+ void *data;
+ dwc_workq_t *wq;
+ char *name;
+ int hz;
+ struct work task;
+} work_container_t;
+
+struct dwc_workq {
+ struct workqueue *taskq;
+ dwc_spinlock_t *lock;
+ dwc_waitq_t *waitq;
+ int pending;
+ struct work_container *container;
+};
+
+static void do_work(struct work *task, void *data)
+{
+ dwc_workq_t *wq = (dwc_workq_t *)data;
+ work_container_t *container = wq->container;
+ dwc_irqflags_t flags;
+
+ if (container->hz) {
+ tsleep(container, 0, "dw3wrk", container->hz);
+ }
+
+ container->cb(container->data);
+ DWC_DEBUG("Work done: %s, container=%p", container->name, container);
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+ if (container->name)
+ DWC_FREE(container->name);
+ DWC_FREE(container);
+ wq->pending--;
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+ DWC_WAITQ_TRIGGER(wq->waitq);
+}
+
+static int work_done(void *data)
+{
+ dwc_workq_t *workq = (dwc_workq_t *)data;
+
+ return workq->pending == 0;
+}
+
+int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout)
+{
+ return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout);
+}
+
+dwc_workq_t *DWC_WORKQ_ALLOC(char *name)
+{
+ int result;
+ dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+ if (!wq) {
+ DWC_ERROR("Cannot allocate memory for workqueue");
+ return NULL;
+ }
+
+ result = workqueue_create(&wq->taskq, name, do_work, wq, 0 /*PWAIT*/,
+ IPL_BIO, 0);
+ if (result) {
+ DWC_ERROR("Cannot create workqueue");
+ goto no_taskq;
+ }
+
+ wq->pending = 0;
+
+ wq->lock = DWC_SPINLOCK_ALLOC();
+ if (!wq->lock) {
+ DWC_ERROR("Cannot allocate memory for spinlock");
+ goto no_lock;
+ }
+
+ wq->waitq = DWC_WAITQ_ALLOC();
+ if (!wq->waitq) {
+ DWC_ERROR("Cannot allocate memory for waitqueue");
+ goto no_waitq;
+ }
+
+ return wq;
+
+ no_waitq:
+ DWC_SPINLOCK_FREE(wq->lock);
+ no_lock:
+ workqueue_destroy(wq->taskq);
+ no_taskq:
+ DWC_FREE(wq);
+
+ return NULL;
+}
+
+void DWC_WORKQ_FREE(dwc_workq_t *wq)
+{
+#ifdef DEBUG
+ dwc_irqflags_t flags;
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+
+ if (wq->pending != 0) {
+ struct work_container *container = wq->container;
+
+ DWC_ERROR("Destroying work queue with pending work");
+
+ if (container && container->name) {
+ DWC_ERROR("Work %s still pending", container->name);
+ }
+ }
+
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+#endif
+ DWC_WAITQ_FREE(wq->waitq);
+ DWC_SPINLOCK_FREE(wq->lock);
+ workqueue_destroy(wq->taskq);
+ DWC_FREE(wq);
+}
+
+void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data,
+ char *format, ...)
+{
+ dwc_irqflags_t flags;
+ work_container_t *container;
+ static char name[128];
+ va_list args;
+
+ va_start(args, format);
+ DWC_VSNPRINTF(name, 128, format, args);
+ va_end(args);
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+ wq->pending++;
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+ DWC_WAITQ_TRIGGER(wq->waitq);
+
+ container = DWC_ALLOC_ATOMIC(sizeof(*container));
+ if (!container) {
+ DWC_ERROR("Cannot allocate memory for container");
+ return;
+ }
+
+ container->name = DWC_STRDUP(name);
+ if (!container->name) {
+ DWC_ERROR("Cannot allocate memory for container->name");
+ DWC_FREE(container);
+ return;
+ }
+
+ container->cb = cb;
+ container->data = data;
+ container->wq = wq;
+ container->hz = 0;
+ wq->container = container;
+
+ DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+ workqueue_enqueue(wq->taskq, &container->task);
+}
+
+void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb,
+ void *data, uint32_t time, char *format, ...)
+{
+ dwc_irqflags_t flags;
+ work_container_t *container;
+ static char name[128];
+ struct timeval tv;
+ va_list args;
+
+ va_start(args, format);
+ DWC_VSNPRINTF(name, 128, format, args);
+ va_end(args);
+
+ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+ wq->pending++;
+ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+ DWC_WAITQ_TRIGGER(wq->waitq);
+
+ container = DWC_ALLOC_ATOMIC(sizeof(*container));
+ if (!container) {
+ DWC_ERROR("Cannot allocate memory for container");
+ return;
+ }
+
+ container->name = DWC_STRDUP(name);
+ if (!container->name) {
+ DWC_ERROR("Cannot allocate memory for container->name");
+ DWC_FREE(container);
+ return;
+ }
+
+ container->cb = cb;
+ container->data = data;
+ container->wq = wq;
+ tv.tv_sec = time / 1000;
+ tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
+ container->hz = tvtohz(&tv);
+ wq->container = container;
+
+ DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+ workqueue_enqueue(wq->taskq, &container->task);
+}
+
+int DWC_WORKQ_PENDING(dwc_workq_t *wq)
+{
+ return wq->pending;
+}
diff --git a/drivers/usb/dwc_otg/dwc_crypto.c b/drivers/usb/dwc_otg/dwc_crypto.c
new file mode 100644
index 0000000..3b03532
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_crypto.c
@@ -0,0 +1,308 @@
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_crypto.c $
+ * $Revision: #5 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+
+/** @file
+ * This file contains the WUSB cryptographic routines.
+ */
+
+#ifdef DWC_CRYPTOLIB
+
+#include "dwc_crypto.h"
+#include "usb.h"
+
+#ifdef DEBUG
+static inline void dump_bytes(char *name, uint8_t *bytes, int len)
+{
+ int i;
+ DWC_PRINTF("%s: ", name);
+ for (i=0; i<len; i++) {
+ DWC_PRINTF("%02x ", bytes[i]);
+ }
+ DWC_PRINTF("\n");
+}
+#else
+#define dump_bytes(x...)
+#endif
+
+/* Display a block */
+void show_block(const u8 *blk, const char *prefix, const char *suffix, int a)
+{
+#ifdef DWC_DEBUG_CRYPTO
+ int i, blksize = 16;
+
+ DWC_DEBUG("%s", prefix);
+
+ if (suffix == NULL) {
+ suffix = "\n";
+ blksize = a;
+ }
+
+ for (i = 0; i < blksize; i++)
+ DWC_PRINT("%02x%s", *blk++, ((i & 3) == 3) ? " " : " ");
+ DWC_PRINT(suffix);
+#endif
+}
+
+/**
+ * Encrypts an array of bytes using the AES encryption engine.
+ * If <code>dst</code> == <code>src</code>, then the bytes will be encrypted
+ * in-place.
+ *
+ * @return 0 on success, negative error code on error.
+ */
+int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst)
+{
+ u8 block_t[16];
+ DWC_MEMSET(block_t, 0, 16);
+
+ return DWC_AES_CBC(src, 16, key, 16, block_t, dst);
+}
+
+/**
+ * The CCM-MAC-FUNCTION described in section 6.5 of the WUSB spec.
+ * This function takes a data string and returns the encrypted CBC
+ * Counter-mode MIC.
+ *
+ * @param key The 128-bit symmetric key.
+ * @param nonce The CCM nonce.
+ * @param label The unique 14-byte ASCII text label.
+ * @param bytes The byte array to be encrypted.
+ * @param len Length of the byte array.
+ * @param result Byte array to receive the 8-byte encrypted MIC.
+ */
+void dwc_wusb_cmf(u8 *key, u8 *nonce,
+ char *label, u8 *bytes, int len, u8 *result)
+{
+ u8 block_m[16];
+ u8 block_x[16];
+ u8 block_t[8];
+ int idx, blkNum;
+ u16 la = (u16)(len + 14);
+
+ /* Set the AES-128 key */
+ //dwc_aes_setkey(tfm, key, 16);
+
+ /* Fill block B0 from flags = 0x59, N, and l(m) = 0 */
+ block_m[0] = 0x59;
+ for (idx = 0; idx < 13; idx++)
+ block_m[idx + 1] = nonce[idx];
+ block_m[14] = 0;
+ block_m[15] = 0;
+
+ /* Produce the CBC IV */
+ dwc_wusb_aes_encrypt(block_m, key, block_x);
+ show_block(block_m, "CBC IV in: ", "\n", 0);
+ show_block(block_x, "CBC IV out:", "\n", 0);
+
+ /* Fill block B1 from l(a) = Blen + 14, and A */
+ block_x[0] ^= (u8)(la >> 8);
+ block_x[1] ^= (u8)la;
+ for (idx = 0; idx < 14; idx++)
+ block_x[idx + 2] ^= label[idx];
+ show_block(block_x, "After xor: ", "b1\n", 16);
+
+ dwc_wusb_aes_encrypt(block_x, key, block_x);
+ show_block(block_x, "After AES: ", "b1\n", 16);
+
+ idx = 0;
+ blkNum = 0;
+
+ /* Fill remaining blocks with B */
+ while (len-- > 0) {
+ block_x[idx] ^= *bytes++;
+ if (++idx >= 16) {
+ idx = 0;
+ show_block(block_x, "After xor: ", "\n", blkNum);
+ dwc_wusb_aes_encrypt(block_x, key, block_x);
+ show_block(block_x, "After AES: ", "\n", blkNum);
+ blkNum++;
+ }
+ }
+
+ /* Handle partial last block */
+ if (idx > 0) {
+ show_block(block_x, "After xor: ", "\n", blkNum);
+ dwc_wusb_aes_encrypt(block_x, key, block_x);
+ show_block(block_x, "After AES: ", "\n", blkNum);
+ }
+
+ /* Save the MIC tag */
+ DWC_MEMCPY(block_t, block_x, 8);
+ show_block(block_t, "MIC tag : ", NULL, 8);
+
+ /* Fill block A0 from flags = 0x01, N, and counter = 0 */
+ block_m[0] = 0x01;
+ block_m[14] = 0;
+ block_m[15] = 0;
+
+ /* Encrypt the counter */
+ dwc_wusb_aes_encrypt(block_m, key, block_x);
+ show_block(block_x, "CTR[MIC] : ", NULL, 8);
+
+ /* XOR with MIC tag */
+ for (idx = 0; idx < 8; idx++) {
+ block_t[idx] ^= block_x[idx];
+ }
+
+ /* Return result to caller */
+ DWC_MEMCPY(result, block_t, 8);
+ show_block(result, "CCM-MIC : ", NULL, 8);
+
+}
+
+/**
+ * The PRF function described in section 6.5 of the WUSB spec. This function
+ * concatenates MIC values returned from dwc_cmf() to create a value of
+ * the requested length.
+ *
+ * @param prf_len Length of the PRF function in bits (64, 128, or 256).
+ * @param key, nonce, label, bytes, len Same as for dwc_cmf().
+ * @param result Byte array to receive the result.
+ */
+void dwc_wusb_prf(int prf_len, u8 *key,
+ u8 *nonce, char *label, u8 *bytes, int len, u8 *result)
+{
+ int i;
+
+ nonce[0] = 0;
+ for (i = 0; i < prf_len >> 6; i++, nonce[0]++) {
+ dwc_wusb_cmf(key, nonce, label, bytes, len, result);
+ result += 8;
+ }
+}
+
+/**
+ * Fills in CCM Nonce per the WUSB spec.
+ *
+ * @param[in] haddr Host address.
+ * @param[in] daddr Device address.
+ * @param[in] tkid Session Key(PTK) identifier.
+ * @param[out] nonce Pointer to where the CCM Nonce output is to be written.
+ */
+void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid,
+ uint8_t *nonce)
+{
+
+ DWC_DEBUG("%s %x %x\n", __func__, daddr, haddr);
+
+ DWC_MEMSET(&nonce[0], 0, 16);
+
+ DWC_MEMCPY(&nonce[6], tkid, 3);
+ nonce[9] = daddr & 0xFF;
+ nonce[10] = (daddr >> 8) & 0xFF;
+ nonce[11] = haddr & 0xFF;
+ nonce[12] = (haddr >> 8) & 0xFF;
+
+ dump_bytes("CCM nonce", nonce, 16);
+}
+
+/**
+ * Generates a 16-byte cryptographic-grade random number for the Host/Device
+ * Nonce.
+ */
+void dwc_wusb_gen_nonce(uint16_t addr, uint8_t *nonce)
+{
+ uint8_t inonce[16];
+ uint32_t temp[4];
+
+ /* Fill in the Nonce */
+ DWC_MEMSET(&inonce[0], 0, sizeof(inonce));
+ inonce[9] = addr & 0xFF;
+ inonce[10] = (addr >> 8) & 0xFF;
+ inonce[11] = inonce[9];
+ inonce[12] = inonce[10];
+
+ /* Collect "randomness samples" */
+ DWC_RANDOM_BYTES((uint8_t *)temp, 16);
+
+ dwc_wusb_prf_128((uint8_t *)temp, nonce,
+ "Random Numbers", (uint8_t *)temp, sizeof(temp),
+ nonce);
+}
+
+/**
+ * Generates the Session Key (PTK) and Key Confirmation Key (KCK) per the
+ * WUSB spec.
+ *
+ * @param[in] ccm_nonce Pointer to CCM Nonce.
+ * @param[in] mk Master Key to derive the session from
+ * @param[in] hnonce Pointer to Host Nonce.
+ * @param[in] dnonce Pointer to Device Nonce.
+ * @param[out] kck Pointer to where the KCK output is to be written.
+ * @param[out] ptk Pointer to where the PTK output is to be written.
+ */
+void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk, uint8_t *hnonce,
+ uint8_t *dnonce, uint8_t *kck, uint8_t *ptk)
+{
+ uint8_t idata[32];
+ uint8_t odata[32];
+
+ dump_bytes("ck", mk, 16);
+ dump_bytes("hnonce", hnonce, 16);
+ dump_bytes("dnonce", dnonce, 16);
+
+ /* The data is the HNonce and DNonce concatenated */
+ DWC_MEMCPY(&idata[0], hnonce, 16);
+ DWC_MEMCPY(&idata[16], dnonce, 16);
+
+ dwc_wusb_prf_256(mk, ccm_nonce, "Pair-wise keys", idata, 32, odata);
+
+ /* Low 16 bytes of the result is the KCK, high 16 is the PTK */
+ DWC_MEMCPY(kck, &odata[0], 16);
+ DWC_MEMCPY(ptk, &odata[16], 16);
+
+ dump_bytes("kck", kck, 16);
+ dump_bytes("ptk", ptk, 16);
+}
+
+/**
+ * Generates the Message Integrity Code over the Handshake data per the
+ * WUSB spec.
+ *
+ * @param ccm_nonce Pointer to CCM Nonce.
+ * @param kck Pointer to Key Confirmation Key.
+ * @param data Pointer to Handshake data to be checked.
+ * @param mic Pointer to where the MIC output is to be written.
+ */
+void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t *kck,
+ uint8_t *data, uint8_t *mic)
+{
+
+ dwc_wusb_prf_64(kck, ccm_nonce, "out-of-bandMIC",
+ data, WUSB_HANDSHAKE_LEN_FOR_MIC, mic);
+}
+
+#endif /* DWC_CRYPTOLIB */
diff --git a/drivers/usb/dwc_otg/dwc_crypto.h b/drivers/usb/dwc_otg/dwc_crypto.h
new file mode 100644
index 0000000..26fcddc
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_crypto.h
@@ -0,0 +1,111 @@
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_crypto.h $
+ * $Revision: #3 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+
+#ifndef _DWC_CRYPTO_H_
+#define _DWC_CRYPTO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file
+ *
+ * This file contains declarations for the WUSB Cryptographic routines as
+ * defined in the WUSB spec. They are only to be used internally by the DWC UWB
+ * modules.
+ */
+
+#include "dwc_os.h"
+
+int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst);
+
+void dwc_wusb_cmf(u8 *key, u8 *nonce,
+ char *label, u8 *bytes, int len, u8 *result);
+void dwc_wusb_prf(int prf_len, u8 *key,
+ u8 *nonce, char *label, u8 *bytes, int len, u8 *result);
+
+/**
+ * The PRF-64 function described in section 6.5 of the WUSB spec.
+ *
+ * @param key, nonce, label, bytes, len, result Same as for dwc_prf().
+ */
+static inline void dwc_wusb_prf_64(u8 *key, u8 *nonce,
+ char *label, u8 *bytes, int len, u8 *result)
+{
+ dwc_wusb_prf(64, key, nonce, label, bytes, len, result);
+}
+
+/**
+ * The PRF-128 function described in section 6.5 of the WUSB spec.
+ *
+ * @param key, nonce, label, bytes, len, result Same as for dwc_prf().
+ */
+static inline void dwc_wusb_prf_128(u8 *key, u8 *nonce,
+ char *label, u8 *bytes, int len, u8 *result)
+{
+ dwc_wusb_prf(128, key, nonce, label, bytes, len, result);
+}
+
+/**
+ * The PRF-256 function described in section 6.5 of the WUSB spec.
+ *
+ * @param key, nonce, label, bytes, len, result Same as for dwc_prf().
+ */
+static inline void dwc_wusb_prf_256(u8 *key, u8 *nonce,
+ char *label, u8 *bytes, int len, u8 *result)
+{
+ dwc_wusb_prf(256, key, nonce, label, bytes, len, result);
+}
+
+
+void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid,
+ uint8_t *nonce);
+void dwc_wusb_gen_nonce(uint16_t addr,
+ uint8_t *nonce);
+
+void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk,
+ uint8_t *hnonce, uint8_t *dnonce,
+ uint8_t *kck, uint8_t *ptk);
+
+
+void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t
+ *kck, uint8_t *data, uint8_t *mic);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_CRYPTO_H_ */
diff --git a/drivers/usb/dwc_otg/dwc_dh.c b/drivers/usb/dwc_otg/dwc_dh.c
new file mode 100644
index 0000000..997f753
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_dh.c
@@ -0,0 +1,291 @@
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_dh.c $
+ * $Revision: #3 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifdef DWC_CRYPTOLIB
+
+#ifndef CONFIG_MACH_IPMATE
+
+#include "dwc_dh.h"
+#include "dwc_modpow.h"
+
+#ifdef DEBUG
+/* This function prints out a buffer in the format described in the Association
+ * Model specification. */
+static void dh_dump(char *str, void *_num, int len)
+{
+ uint8_t *num = _num;
+ int i;
+ DWC_PRINTF("%s\n", str);
+ for (i = 0; i < len; i ++) {
+ DWC_PRINTF("%02x", num[i]);
+ if (((i + 1) % 2) == 0) DWC_PRINTF(" ");
+ if (((i + 1) % 26) == 0) DWC_PRINTF("\n");
+ }
+
+ DWC_PRINTF("\n");
+}
+#else
+#define dh_dump(_x...) do {; } while(0)
+#endif
+
+/* Constant g value */
+static __u32 dh_g[] = {
+ 0x02000000,
+};
+
+/* Constant p value */
+static __u32 dh_p[] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xA2DA0FC9, 0x34C26821, 0x8B62C6C4, 0xD11CDC80, 0x084E0229, 0x74CC678A,
+ 0xA6BE0B02, 0x229B133B, 0x79084A51, 0xDD04348E, 0xB31995EF, 0x1B433ACD, 0x6D0A2B30, 0x37145FF2,
+ 0x6D35E14F, 0x45C2516D, 0x76B585E4, 0xC67E5E62, 0xE9424CF4, 0x6BED37A6, 0xB65CFF0B, 0xEDB706F4,
+ 0xFB6B38EE, 0xA59F895A, 0x11249FAE, 0xE61F4B7C, 0x51662849, 0x3D5BE4EC, 0xB87C00C2, 0x05BF63A1,
+ 0x3648DA98, 0x9AD3551C, 0xA83F1669, 0x5FCF24FD, 0x235D6583, 0x96ADA3DC, 0x56F3621C, 0xBB528520,
+ 0x0729D59E, 0x6D969670, 0x4E350C67, 0x0498BC4A, 0x086C74F1, 0x7C2118CA, 0x465E9032, 0x3BCE362E,
+ 0x2C779EE3, 0x03860E18, 0xA283279B, 0x8FA207EC, 0xF05DC5B5, 0xC9524C6F, 0xF6CB2BDE, 0x18175895,
+ 0x7C499539, 0xE56A95EA, 0x1826D215, 0x1005FA98, 0x5A8E7215, 0x2DC4AA8A, 0x0D1733AD, 0x337A5004,
+ 0xAB2155A8, 0x64BA1CDF, 0x0485FBEC, 0x0AEFDB58, 0x5771EA8A, 0x7D0C065D, 0x850F97B3, 0xC7E4E1A6,
+ 0x8CAEF5AB, 0xD73309DB, 0xE0948C1E, 0x9D61254A, 0x26D2E3CE, 0x6BEED21A, 0x06FA2FF1, 0x64088AD9,
+ 0x730276D8, 0x646AC83E, 0x182B1F52, 0x0C207B17, 0x5717E1BB, 0x6C5D617A, 0xC0880977, 0xE246D9BA,
+ 0xA04FE208, 0x31ABE574, 0xFC5BDB43, 0x8E10FDE0, 0x20D1824B, 0xCAD23AA9, 0xFFFFFFFF, 0xFFFFFFFF,
+};
+
+static void dh_swap_bytes(void *_in, void *_out, uint32_t len)
+{
+ uint8_t *in = _in;
+ uint8_t *out = _out;
+ int i;
+ for (i=0; i<len; i++) {
+ out[i] = in[len-1-i];
+ }
+}
+
+/* Computes the modular exponentiation (num^exp % mod). num, exp, and mod are
+ * big endian numbers of size len, in bytes. Each len value must be a multiple
+ * of 4. */
+int dwc_dh_modpow(void *mem_ctx, void *num, uint32_t num_len,
+ void *exp, uint32_t exp_len,
+ void *mod, uint32_t mod_len,
+ void *out)
+{
+ /* modpow() takes little endian numbers. AM uses big-endian. This
+ * function swaps bytes of numbers before passing onto modpow. */
+
+ int retval = 0;
+ uint32_t *result;
+
+ uint32_t *bignum_num = dwc_alloc(mem_ctx, num_len + 4);
+ uint32_t *bignum_exp = dwc_alloc(mem_ctx, exp_len + 4);
+ uint32_t *bignum_mod = dwc_alloc(mem_ctx, mod_len + 4);
+
+ dh_swap_bytes(num, &bignum_num[1], num_len);
+ bignum_num[0] = num_len / 4;
+
+ dh_swap_bytes(exp, &bignum_exp[1], exp_len);
+ bignum_exp[0] = exp_len / 4;
+
+ dh_swap_bytes(mod, &bignum_mod[1], mod_len);
+ bignum_mod[0] = mod_len / 4;
+
+ result = dwc_modpow(mem_ctx, bignum_num, bignum_exp, bignum_mod);
+ if (!result) {
+ retval = -1;
+ goto dh_modpow_nomem;
+ }
+
+ dh_swap_bytes(&result[1], out, result[0] * 4);
+ dwc_free(mem_ctx, result);
+
+ dh_modpow_nomem:
+ dwc_free(mem_ctx, bignum_num);
+ dwc_free(mem_ctx, bignum_exp);
+ dwc_free(mem_ctx, bignum_mod);
+ return retval;
+}
+
+
+int dwc_dh_pk(void *mem_ctx, uint8_t nd, uint8_t *exp, uint8_t *pk, uint8_t *hash)
+{
+ int retval;
+ uint8_t m3[385];
+
+#ifndef DH_TEST_VECTORS
+ DWC_RANDOM_BYTES(exp, 32);
+#endif
+
+ /* Compute the pkd */
+ if ((retval = dwc_dh_modpow(mem_ctx, dh_g, 4,
+ exp, 32,
+ dh_p, 384, pk))) {
+ return retval;
+ }
+
+ m3[384] = nd;
+ DWC_MEMCPY(&m3[0], pk, 384);
+ DWC_SHA256(m3, 385, hash);
+
+ dh_dump("PK", pk, 384);
+ dh_dump("SHA-256(M3)", hash, 32);
+ return 0;
+}
+
+int dwc_dh_derive_keys(void *mem_ctx, uint8_t nd, uint8_t *pkh, uint8_t *pkd,
+ uint8_t *exp, int is_host,
+ char *dd, uint8_t *ck, uint8_t *kdk)
+{
+ int retval;
+ uint8_t mv[784];
+ uint8_t sha_result[32];
+ uint8_t dhkey[384];
+ uint8_t shared_secret[384];
+ char *message;
+ uint32_t vd;
+
+ uint8_t *pk;
+
+ if (is_host) {
+ pk = pkd;
+ }
+ else {
+ pk = pkh;
+ }
+
+ if ((retval = dwc_dh_modpow(mem_ctx, pk, 384,
+ exp, 32,
+ dh_p, 384, shared_secret))) {
+ return retval;
+ }
+ dh_dump("Shared Secret", shared_secret, 384);
+
+ DWC_SHA256(shared_secret, 384, dhkey);
+ dh_dump("DHKEY", dhkey, 384);
+
+ DWC_MEMCPY(&mv[0], pkd, 384);
+ DWC_MEMCPY(&mv[384], pkh, 384);
+ DWC_MEMCPY(&mv[768], "displayed digest", 16);
+ dh_dump("MV", mv, 784);
+
+ DWC_SHA256(mv, 784, sha_result);
+ dh_dump("SHA-256(MV)", sha_result, 32);
+ dh_dump("First 32-bits of SHA-256(MV)", sha_result, 4);
+
+ dh_swap_bytes(sha_result, &vd, 4);
+#ifdef DEBUG
+ DWC_PRINTF("Vd (decimal) = %d\n", vd);
+#endif
+
+ switch (nd) {
+ case 2:
+ vd = vd % 100;
+ DWC_SPRINTF(dd, "%02d", vd);
+ break;
+ case 3:
+ vd = vd % 1000;
+ DWC_SPRINTF(dd, "%03d", vd);
+ break;
+ case 4:
+ vd = vd % 10000;
+ DWC_SPRINTF(dd, "%04d", vd);
+ break;
+ }
+#ifdef DEBUG
+ DWC_PRINTF("Display Digits: %s\n", dd);
+#endif
+
+ message = "connection key";
+ DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result);
+ dh_dump("HMAC(SHA-256, DHKey, connection key)", sha_result, 32);
+ DWC_MEMCPY(ck, sha_result, 16);
+
+ message = "key derivation key";
+ DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result);
+ dh_dump("HMAC(SHA-256, DHKey, key derivation key)", sha_result, 32);
+ DWC_MEMCPY(kdk, sha_result, 32);
+
+ return 0;
+}
+
+
+#ifdef DH_TEST_VECTORS
+
+static __u8 dh_a[] = {
+ 0x44, 0x00, 0x51, 0xd6,
+ 0xf0, 0xb5, 0x5e, 0xa9,
+ 0x67, 0xab, 0x31, 0xc6,
+ 0x8a, 0x8b, 0x5e, 0x37,
+ 0xd9, 0x10, 0xda, 0xe0,
+ 0xe2, 0xd4, 0x59, 0xa4,
+ 0x86, 0x45, 0x9c, 0xaa,
+ 0xdf, 0x36, 0x75, 0x16,
+};
+
+static __u8 dh_b[] = {
+ 0x5d, 0xae, 0xc7, 0x86,
+ 0x79, 0x80, 0xa3, 0x24,
+ 0x8c, 0xe3, 0x57, 0x8f,
+ 0xc7, 0x5f, 0x1b, 0x0f,
+ 0x2d, 0xf8, 0x9d, 0x30,
+ 0x6f, 0xa4, 0x52, 0xcd,
+ 0xe0, 0x7a, 0x04, 0x8a,
+ 0xde, 0xd9, 0x26, 0x56,
+};
+
+void dwc_run_dh_test_vectors(void *mem_ctx)
+{
+ uint8_t pkd[384];
+ uint8_t pkh[384];
+ uint8_t hashd[32];
+ uint8_t hashh[32];
+ uint8_t ck[16];
+ uint8_t kdk[32];
+ char dd[5];
+
+ DWC_PRINTF("\n\n\nDH_TEST_VECTORS\n\n");
+
+ /* compute the PKd and SHA-256(PKd || Nd) */
+ DWC_PRINTF("Computing PKd\n");
+ dwc_dh_pk(mem_ctx, 2, dh_a, pkd, hashd);
+
+ /* compute the PKd and SHA-256(PKh || Nd) */
+ DWC_PRINTF("Computing PKh\n");
+ dwc_dh_pk(mem_ctx, 2, dh_b, pkh, hashh);
+
+ /* compute the dhkey */
+ dwc_dh_derive_keys(mem_ctx, 2, pkh, pkd, dh_a, 0, dd, ck, kdk);
+}
+#endif /* DH_TEST_VECTORS */
+
+#endif /* !CONFIG_MACH_IPMATE */
+
+#endif /* DWC_CRYPTOLIB */
diff --git a/drivers/usb/dwc_otg/dwc_dh.h b/drivers/usb/dwc_otg/dwc_dh.h
new file mode 100644
index 0000000..25c1cc0
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_dh.h
@@ -0,0 +1,106 @@
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_dh.h $
+ * $Revision: #4 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifndef _DWC_DH_H_
+#define _DWC_DH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "dwc_os.h"
+
+/** @file
+ *
+ * This file defines the common functions on device and host for performing
+ * numeric association as defined in the WUSB spec. They are only to be
+ * used internally by the DWC UWB modules. */
+
+extern int dwc_dh_sha256(uint8_t *message, uint32_t len, uint8_t *out);
+extern int dwc_dh_hmac_sha256(uint8_t *message, uint32_t messagelen,
+ uint8_t *key, uint32_t keylen,
+ uint8_t *out);
+extern int dwc_dh_modpow(void *mem_ctx, void *num, uint32_t num_len,
+ void *exp, uint32_t exp_len,
+ void *mod, uint32_t mod_len,
+ void *out);
+
+/** Computes PKD or PKH, and SHA-256(PKd || Nd)
+ *
+ * PK = g^exp mod p.
+ *
+ * Input:
+ * Nd = Number of digits on the device.
+ *
+ * Output:
+ * exp = A 32-byte buffer to be filled with a randomly generated number.
+ * used as either A or B.
+ * pk = A 384-byte buffer to be filled with the PKH or PKD.
+ * hash = A 32-byte buffer to be filled with SHA-256(PK || ND).
+ */
+extern int dwc_dh_pk(void *mem_ctx, uint8_t nd, uint8_t *exp, uint8_t *pkd, uint8_t *hash);
+
+/** Computes the DHKEY, and VD.
+ *
+ * If called from host, then it will comput DHKEY=PKD^exp % p.
+ * If called from device, then it will comput DHKEY=PKH^exp % p.
+ *
+ * Input:
+ * pkd = The PKD value.
+ * pkh = The PKH value.
+ * exp = The A value (if device) or B value (if host) generated in dwc_wudev_dh_pk.
+ * is_host = Set to non zero if a WUSB host is calling this function.
+ *
+ * Output:
+
+ * dd = A pointer to an buffer to be set to the displayed digits string to be shown
+ * to the user. This buffer should be at 5 bytes long to hold 4 digits plus a
+ * null termination character. This buffer can be used directly for display.
+ * ck = A 16-byte buffer to be filled with the CK.
+ * kdk = A 32-byte buffer to be filled with the KDK.
+ */
+extern int dwc_dh_derive_keys(void *mem_ctx, uint8_t nd, uint8_t *pkh, uint8_t *pkd,
+ uint8_t *exp, int is_host,
+ char *dd, uint8_t *ck, uint8_t *kdk);
+
+#ifdef DH_TEST_VECTORS
+extern void dwc_run_dh_test_vectors(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_DH_H_ */
diff --git a/drivers/usb/dwc_otg/dwc_list.h b/drivers/usb/dwc_otg/dwc_list.h
new file mode 100644
index 0000000..89cc325
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_list.h
@@ -0,0 +1,594 @@
+/* $OpenBSD: queue.h,v 1.26 2004/05/04 16:59:32 grange Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _DWC_LIST_H_
+#define _DWC_LIST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file
+ *
+ * This file defines linked list operations. It is derived from BSD with
+ * only the MACRO names being prefixed with DWC_. This is because a few of
+ * these names conflict with those on Linux. For documentation on use, see the
+ * inline comments in the source code. The original license for this source
+ * code applies and is preserved in the dwc_list.h source file.
+ */
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Double-linked List.
+ */
+
+typedef struct dwc_list_link {
+ struct dwc_list_link *next;
+ struct dwc_list_link *prev;
+} dwc_list_link_t;
+
+#define DWC_LIST_INIT(link) do { \
+ (link)->next = (link); \
+ (link)->prev = (link); \
+} while (0)
+
+#define DWC_LIST_FIRST(link) ((link)->next)
+#define DWC_LIST_LAST(link) ((link)->prev)
+#define DWC_LIST_END(link) (link)
+#define DWC_LIST_NEXT(link) ((link)->next)
+#define DWC_LIST_PREV(link) ((link)->prev)
+#define DWC_LIST_EMPTY(link) \
+ (DWC_LIST_FIRST(link) == DWC_LIST_END(link))
+#define DWC_LIST_ENTRY(link, type, field) \
+ (type *)((uint8_t *)(link) - (size_t)(&((type *)0)->field))
+
+#if 0
+#define DWC_LIST_INSERT_HEAD(list, link) do { \
+ (link)->next = (list)->next; \
+ (link)->prev = (list); \
+ (list)->next->prev = (link); \
+ (list)->next = (link); \
+} while (0)
+
+#define DWC_LIST_INSERT_TAIL(list, link) do { \
+ (link)->next = (list); \
+ (link)->prev = (list)->prev; \
+ (list)->prev->next = (link); \
+ (list)->prev = (link); \
+} while (0)
+#else
+#define DWC_LIST_INSERT_HEAD(list, link) do { \
+ dwc_list_link_t *__next__ = (list)->next; \
+ __next__->prev = (link); \
+ (link)->next = __next__; \
+ (link)->prev = (list); \
+ (list)->next = (link); \
+} while (0)
+
+#define DWC_LIST_INSERT_TAIL(list, link) do { \
+ dwc_list_link_t *__prev__ = (list)->prev; \
+ (list)->prev = (link); \
+ (link)->next = (list); \
+ (link)->prev = __prev__; \
+ __prev__->next = (link); \
+} while (0)
+#endif
+
+#if 0
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+#endif
+
+#define DWC_LIST_REMOVE(link) do { \
+ (link)->next->prev = (link)->prev; \
+ (link)->prev->next = (link)->next; \
+} while (0)
+
+#define DWC_LIST_REMOVE_INIT(link) do { \
+ DWC_LIST_REMOVE(link); \
+ DWC_LIST_INIT(link); \
+} while (0)
+
+#define DWC_LIST_MOVE_HEAD(list, link) do { \
+ DWC_LIST_REMOVE(link); \
+ DWC_LIST_INSERT_HEAD(list, link); \
+} while (0)
+
+#define DWC_LIST_MOVE_TAIL(list, link) do { \
+ DWC_LIST_REMOVE(link); \
+ DWC_LIST_INSERT_TAIL(list, link); \
+} while (0)
+
+#define DWC_LIST_FOREACH(var, list) \
+ for((var) = DWC_LIST_FIRST(list); \
+ (var) != DWC_LIST_END(list); \
+ (var) = DWC_LIST_NEXT(var))
+
+#define DWC_LIST_FOREACH_SAFE(var, var2, list) \
+ for((var) = DWC_LIST_FIRST(list), (var2) = DWC_LIST_NEXT(var); \
+ (var) != DWC_LIST_END(list); \
+ (var) = (var2), (var2) = DWC_LIST_NEXT(var2))
+
+#define DWC_LIST_FOREACH_REVERSE(var, list) \
+ for((var) = DWC_LIST_LAST(list); \
+ (var) != DWC_LIST_END(list); \
+ (var) = DWC_LIST_PREV(var))
+
+/*
+ * Singly-linked List definitions.
+ */
+#define DWC_SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define DWC_SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define DWC_SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define DWC_SLIST_FIRST(head) ((head)->slh_first)
+#define DWC_SLIST_END(head) NULL
+#define DWC_SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define DWC_SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define DWC_SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define DWC_SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != SLIST_END(head); \
+ (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define DWC_SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define DWC_SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define DWC_SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define DWC_SLIST_REMOVE_NEXT(head, elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define DWC_SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define DWC_SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while( curelm->field.sle_next != (elm) ) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define DWC_SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define DWC_SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define DWC_SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define DWC_SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define DWC_SIMPLEQ_END(head) NULL
+#define DWC_SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define DWC_SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define DWC_SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define DWC_SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define DWC_SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define DWC_SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define DWC_SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define DWC_SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define DWC_TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define DWC_TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define DWC_TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define DWC_TAILQ_FIRST(head) ((head)->tqh_first)
+#define DWC_TAILQ_END(head) NULL
+#define DWC_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define DWC_TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define DWC_TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define DWC_TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define DWC_TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define DWC_TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define DWC_TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define DWC_TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define DWC_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define DWC_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define DWC_TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+#define DWC_TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define DWC_CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define DWC_CIRCLEQ_HEAD_INITIALIZER(head) \
+ { DWC_CIRCLEQ_END(&head), DWC_CIRCLEQ_END(&head) }
+
+#define DWC_CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define DWC_CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define DWC_CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define DWC_CIRCLEQ_END(head) ((void *)(head))
+#define DWC_CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define DWC_CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define DWC_CIRCLEQ_EMPTY(head) \
+ (DWC_CIRCLEQ_FIRST(head) == DWC_CIRCLEQ_END(head))
+
+#define DWC_CIRCLEQ_EMPTY_ENTRY(elm, field) (((elm)->field.cqe_next == NULL) && ((elm)->field.cqe_prev == NULL))
+
+#define DWC_CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = DWC_CIRCLEQ_FIRST(head); \
+ (var) != DWC_CIRCLEQ_END(head); \
+ (var) = DWC_CIRCLEQ_NEXT(var, field))
+
+#define DWC_CIRCLEQ_FOREACH_SAFE(var, var2, head, field) \
+ for((var) = DWC_CIRCLEQ_FIRST(head), var2 = DWC_CIRCLEQ_NEXT(var, field); \
+ (var) != DWC_CIRCLEQ_END(head); \
+ (var) = var2, var2 = DWC_CIRCLEQ_NEXT(var, field))
+
+#define DWC_CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = DWC_CIRCLEQ_LAST(head); \
+ (var) != DWC_CIRCLEQ_END(head); \
+ (var) = DWC_CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define DWC_CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = DWC_CIRCLEQ_END(head); \
+ (head)->cqh_last = DWC_CIRCLEQ_END(head); \
+} while (0)
+
+#define DWC_CIRCLEQ_INIT_ENTRY(elm, field) do { \
+ (elm)->field.cqe_next = NULL; \
+ (elm)->field.cqe_prev = NULL; \
+} while (0)
+
+#define DWC_CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define DWC_CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define DWC_CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = DWC_CIRCLEQ_END(head); \
+ if ((head)->cqh_last == DWC_CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define DWC_CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = DWC_CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == DWC_CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define DWC_CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
+
+#define DWC_CIRCLEQ_REMOVE_INIT(head, elm, field) do { \
+ DWC_CIRCLEQ_REMOVE(head, elm, field); \
+ DWC_CIRCLEQ_INIT_ENTRY(elm, field); \
+} while (0)
+
+#define DWC_CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ DWC_CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ DWC_CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+} while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_LIST_H_ */
diff --git a/drivers/usb/dwc_otg/dwc_mem.c b/drivers/usb/dwc_otg/dwc_mem.c
new file mode 100644
index 0000000..ad645ff
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_mem.c
@@ -0,0 +1,245 @@
+/* Memory Debugging */
+#ifdef DWC_DEBUG_MEMORY
+
+#include "dwc_os.h"
+#include "dwc_list.h"
+
+struct allocation {
+ void *addr;
+ void *ctx;
+ char *func;
+ int line;
+ uint32_t size;
+ int dma;
+ DWC_CIRCLEQ_ENTRY(allocation) entry;
+};
+
+DWC_CIRCLEQ_HEAD(allocation_queue, allocation);
+
+struct allocation_manager {
+ void *mem_ctx;
+ struct allocation_queue allocations;
+
+ /* statistics */
+ int num;
+ int num_freed;
+ int num_active;
+ uint32_t total;
+ uint32_t cur;
+ uint32_t max;
+};
+
+static struct allocation_manager *manager = NULL;
+
+static int add_allocation(void *ctx, uint32_t size, char const *func, int line, void *addr,
+ int dma)
+{
+ struct allocation *a;
+
+ DWC_ASSERT(manager != NULL, "manager not allocated");
+
+ a = __DWC_ALLOC_ATOMIC(manager->mem_ctx, sizeof(*a));
+ if (!a) {
+ return -DWC_E_NO_MEMORY;
+ }
+
+ a->func = __DWC_ALLOC_ATOMIC(manager->mem_ctx, DWC_STRLEN(func) + 1);
+ if (!a->func) {
+ __DWC_FREE(manager->mem_ctx, a);
+ return -DWC_E_NO_MEMORY;
+ }
+
+ DWC_MEMCPY(a->func, func, DWC_STRLEN(func) + 1);
+ a->addr = addr;
+ a->ctx = ctx;
+ a->line = line;
+ a->size = size;
+ a->dma = dma;
+ DWC_CIRCLEQ_INSERT_TAIL(&manager->allocations, a, entry);
+
+ /* Update stats */
+ manager->num++;
+ manager->num_active++;
+ manager->total += size;
+ manager->cur += size;
+
+ if (manager->max < manager->cur) {
+ manager->max = manager->cur;
+ }
+
+ return 0;
+}
+
+static struct allocation *find_allocation(void *ctx, void *addr)
+{
+ struct allocation *a;
+
+ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
+ if (a->ctx == ctx && a->addr == addr) {
+ return a;
+ }
+ }
+
+ return NULL;
+}
+
+static void free_allocation(void *ctx, void *addr, char const *func, int line)
+{
+ struct allocation *a = find_allocation(ctx, addr);
+
+ if (!a) {
+ DWC_ASSERT(0,
+ "Free of address %p that was never allocated or already freed %s:%d",
+ addr, func, line);
+ return;
+ }
+
+ DWC_CIRCLEQ_REMOVE(&manager->allocations, a, entry);
+
+ manager->num_active--;
+ manager->num_freed++;
+ manager->cur -= a->size;
+ __DWC_FREE(manager->mem_ctx, a->func);
+ __DWC_FREE(manager->mem_ctx, a);
+}
+
+int dwc_memory_debug_start(void *mem_ctx)
+{
+ DWC_ASSERT(manager == NULL, "Memory debugging has already started\n");
+
+ if (manager) {
+ return -DWC_E_BUSY;
+ }
+
+ manager = __DWC_ALLOC(mem_ctx, sizeof(*manager));
+ if (!manager) {
+ return -DWC_E_NO_MEMORY;
+ }
+
+ DWC_CIRCLEQ_INIT(&manager->allocations);
+ manager->mem_ctx = mem_ctx;
+ manager->num = 0;
+ manager->num_freed = 0;
+ manager->num_active = 0;
+ manager->total = 0;
+ manager->cur = 0;
+ manager->max = 0;
+
+ return 0;
+}
+
+void dwc_memory_debug_stop(void)
+{
+ struct allocation *a;
+
+ dwc_memory_debug_report();
+
+ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
+ DWC_ERROR("Memory leaked from %s:%d\n", a->func, a->line);
+ free_allocation(a->ctx, a->addr, NULL, -1);
+ }
+
+ __DWC_FREE(manager->mem_ctx, manager);
+}
+
+void dwc_memory_debug_report(void)
+{
+ struct allocation *a;
+
+ DWC_PRINTF("\n\n\n----------------- Memory Debugging Report -----------------\n\n");
+ DWC_PRINTF("Num Allocations = %d\n", manager->num);
+ DWC_PRINTF("Freed = %d\n", manager->num_freed);
+ DWC_PRINTF("Active = %d\n", manager->num_active);
+ DWC_PRINTF("Current Memory Used = %d\n", manager->cur);
+ DWC_PRINTF("Total Memory Used = %d\n", manager->total);
+ DWC_PRINTF("Maximum Memory Used at Once = %d\n", manager->max);
+ DWC_PRINTF("Unfreed allocations:\n");
+
+ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
+ DWC_PRINTF(" addr=%p, size=%d from %s:%d, DMA=%d\n",
+ a->addr, a->size, a->func, a->line, a->dma);
+ }
+}
+
+/* The replacement functions */
+void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line)
+{
+ void *addr = __DWC_ALLOC(mem_ctx, size);
+
+ if (!addr) {
+ return NULL;
+ }
+
+ if (add_allocation(mem_ctx, size, func, line, addr, 0)) {
+ __DWC_FREE(mem_ctx, addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func,
+ int line)
+{
+ void *addr = __DWC_ALLOC_ATOMIC(mem_ctx, size);
+
+ if (!addr) {
+ return NULL;
+ }
+
+ if (add_allocation(mem_ctx, size, func, line, addr, 0)) {
+ __DWC_FREE(mem_ctx, addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line)
+{
+ free_allocation(mem_ctx, addr, func, line);
+ __DWC_FREE(mem_ctx, addr);
+}
+
+void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
+ char const *func, int line)
+{
+ void *addr = __DWC_DMA_ALLOC(dma_ctx, size, dma_addr);
+
+ if (!addr) {
+ return NULL;
+ }
+
+ if (add_allocation(dma_ctx, size, func, line, addr, 1)) {
+ __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size,
+ dwc_dma_t *dma_addr, char const *func, int line)
+{
+ void *addr = __DWC_DMA_ALLOC_ATOMIC(dma_ctx, size, dma_addr);
+
+ if (!addr) {
+ return NULL;
+ }
+
+ if (add_allocation(dma_ctx, size, func, line, addr, 1)) {
+ __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr,
+ dwc_dma_t dma_addr, char const *func, int line)
+{
+ free_allocation(dma_ctx, virt_addr, func, line);
+ __DWC_DMA_FREE(dma_ctx, size, virt_addr, dma_addr);
+}
+
+#endif /* DWC_DEBUG_MEMORY */
diff --git a/drivers/usb/dwc_otg/dwc_modpow.c b/drivers/usb/dwc_otg/dwc_modpow.c
new file mode 100644
index 0000000..f410f6e
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_modpow.c
@@ -0,0 +1,633 @@
+/* Bignum routines adapted from PUTTY sources. PuTTY copyright notice follows.
+ *
+ * PuTTY is copyright 1997-2007 Simon Tatham.
+ *
+ * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+ * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
+ * Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
+ * Kuhn, and CORE SDI S.A.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifdef DWC_CRYPTOLIB
+
+#ifndef CONFIG_MACH_IPMATE
+
+#include "dwc_modpow.h"
+
+#define BIGNUM_INT_MASK 0xFFFFFFFFUL
+#define BIGNUM_TOP_BIT 0x80000000UL
+#define BIGNUM_INT_BITS 32
+
+
+static void *snmalloc(void *mem_ctx, size_t n, size_t size)
+{
+ void *p;
+ size *= n;
+ if (size == 0) size = 1;
+ p = dwc_alloc(mem_ctx, size);
+ return p;
+}
+
+#define snewn(ctx, n, type) ((type *)snmalloc((ctx), (n), sizeof(type)))
+#define sfree dwc_free
+
+/*
+ * Usage notes:
+ * * Do not call the DIVMOD_WORD macro with expressions such as array
+ * subscripts, as some implementations object to this (see below).
+ * * Note that none of the division methods below will cope if the
+ * quotient won't fit into BIGNUM_INT_BITS. Callers should be careful
+ * to avoid this case.
+ * If this condition occurs, in the case of the x86 DIV instruction,
+ * an overflow exception will occur, which (according to a correspondent)
+ * will manifest on Windows as something like
+ * 0xC0000095: Integer overflow
+ * The C variant won't give the right answer, either.
+ */
+
+#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
+
+#if defined __GNUC__ && defined __i386__
+#define DIVMOD_WORD(q, r, hi, lo, w) \
+ __asm__("div %2" : \
+ "=d" (r), "=a" (q) : \
+ "r" (w), "d" (hi), "a" (lo))
+#else
+#define DIVMOD_WORD(q, r, hi, lo, w) do { \
+ BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
+ q = n / w; \
+ r = n % w; \
+} while (0)
+#endif
+
+#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8)
+
+#define BIGNUM_INTERNAL
+
+static Bignum newbn(void *mem_ctx, int length)
+{
+ Bignum b = snewn(mem_ctx, length + 1, BignumInt);
+ //if (!b)
+ //abort(); /* FIXME */
+ DWC_MEMSET(b, 0, (length + 1) * sizeof(*b));
+ b[0] = length;
+ return b;
+}
+
+void freebn(void *mem_ctx, Bignum b)
+{
+ /*
+ * Burn the evidence, just in case.
+ */
+ DWC_MEMSET(b, 0, sizeof(b[0]) * (b[0] + 1));
+ sfree(mem_ctx, b);
+}
+
+/*
+ * Compute c = a * b.
+ * Input is in the first len words of a and b.
+ * Result is returned in the first 2*len words of c.
+ */
+static void internal_mul(BignumInt *a, BignumInt *b,
+ BignumInt *c, int len)
+{
+ int i, j;
+ BignumDblInt t;
+
+ for (j = 0; j < 2 * len; j++)
+ c[j] = 0;
+
+ for (i = len - 1; i >= 0; i--) {
+ t = 0;
+ for (j = len - 1; j >= 0; j--) {
+ t += MUL_WORD(a[i], (BignumDblInt) b[j]);
+ t += (BignumDblInt) c[i + j + 1];
+ c[i + j + 1] = (BignumInt) t;
+ t = t >> BIGNUM_INT_BITS;
+ }
+ c[i] = (BignumInt) t;
+ }
+}
+
+static void internal_add_shifted(BignumInt *number,
+ unsigned n, int shift)
+{
+ int word = 1 + (shift / BIGNUM_INT_BITS);
+ int bshift = shift % BIGNUM_INT_BITS;
+ BignumDblInt addend;
+
+ addend = (BignumDblInt)n << bshift;
+
+ while (addend) {
+ addend += number[word];
+ number[word] = (BignumInt) addend & BIGNUM_INT_MASK;
+ addend >>= BIGNUM_INT_BITS;
+ word++;
+ }
+}
+
+/*
+ * Compute a = a % m.
+ * Input in first alen words of a and first mlen words of m.
+ * Output in first alen words of a
+ * (of which first alen-mlen words will be zero).
+ * The MSW of m MUST have its high bit set.
+ * Quotient is accumulated in the `quotient' array, which is a Bignum
+ * rather than the internal bigendian format. Quotient parts are shifted
+ * left by `qshift' before adding into quot.
+ */
+static void internal_mod(BignumInt *a, int alen,
+ BignumInt *m, int mlen,
+ BignumInt *quot, int qshift)
+{
+ BignumInt m0, m1;
+ unsigned int h;
+ int i, k;
+
+ m0 = m[0];
+ if (mlen > 1)
+ m1 = m[1];
+ else
+ m1 = 0;
+
+ for (i = 0; i <= alen - mlen; i++) {
+ BignumDblInt t;
+ unsigned int q, r, c, ai1;
+
+ if (i == 0) {
+ h = 0;
+ } else {
+ h = a[i - 1];
+ a[i - 1] = 0;
+ }
+
+ if (i == alen - 1)
+ ai1 = 0;
+ else
+ ai1 = a[i + 1];
+
+ /* Find q = h:a[i] / m0 */
+ if (h >= m0) {
+ /*
+ * Special case.
+ *
+ * To illustrate it, suppose a BignumInt is 8 bits, and
+ * we are dividing (say) A1:23:45:67 by A1:B2:C3. Then
+ * our initial division will be 0xA123 / 0xA1, which
+ * will give a quotient of 0x100 and a divide overflow.
+ * However, the invariants in this division algorithm
+ * are not violated, since the full number A1:23:... is
+ * _less_ than the quotient prefix A1:B2:... and so the
+ * following correction loop would have sorted it out.
+ *
+ * In this situation we set q to be the largest
+ * quotient we _can_ stomach (0xFF, of course).
+ */
+ q = BIGNUM_INT_MASK;
+ } else {
+ /* Macro doesn't want an array subscript expression passed
+ * into it (see definition), so use a temporary. */
+ BignumInt tmplo = a[i];
+ DIVMOD_WORD(q, r, h, tmplo, m0);
+
+ /* Refine our estimate of q by looking at
+ h:a[i]:a[i+1] / m0:m1 */
+ t = MUL_WORD(m1, q);
+ if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) {
+ q--;
+ t -= m1;
+ r = (r + m0) & BIGNUM_INT_MASK; /* overflow? */
+ if (r >= (BignumDblInt) m0 &&
+ t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) q--;
+ }
+ }
+
+ /* Subtract q * m from a[i...] */
+ c = 0;
+ for (k = mlen - 1; k >= 0; k--) {
+ t = MUL_WORD(q, m[k]);
+ t += c;
+ c = (unsigned)(t >> BIGNUM_INT_BITS);
+ if ((BignumInt) t > a[i + k])
+ c++;
+ a[i + k] -= (BignumInt) t;
+ }
+
+ /* Add back m in case of borrow */
+ if (c != h) {
+ t = 0;
+ for (k = mlen - 1; k >= 0; k--) {
+ t += m[k];
+ t += a[i + k];
+ a[i + k] = (BignumInt) t;
+ t = t >> BIGNUM_INT_BITS;
+ }
+ q--;
+ }
+ if (quot)
+ internal_add_shifted(quot, q, qshift + BIGNUM_INT_BITS * (alen - mlen - i));
+ }
+}
+
+/*
+ * Compute p % mod.
+ * The most significant word of mod MUST be non-zero.
+ * We assume that the result array is the same size as the mod array.
+ * We optionally write out a quotient if `quotient' is non-NULL.
+ * We can avoid writing out the result if `result' is NULL.
+ */
+void bigdivmod(void *mem_ctx, Bignum p, Bignum mod, Bignum result, Bignum quotient)
+{
+ BignumInt *n, *m;
+ int mshift;
+ int plen, mlen, i, j;
+
+ /* Allocate m of size mlen, copy mod to m */
+ /* We use big endian internally */
+ mlen = mod[0];
+ m = snewn(mem_ctx, mlen, BignumInt);
+ //if (!m)
+ //abort(); /* FIXME */
+ for (j = 0; j < mlen; j++)
+ m[j] = mod[mod[0] - j];
+
+ /* Shift m left to make msb bit set */
+ for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++)
+ if ((m[0] << mshift) & BIGNUM_TOP_BIT)
+ break;
+ if (mshift) {
+ for (i = 0; i < mlen - 1; i++)
+ m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift));
+ m[mlen - 1] = m[mlen - 1] << mshift;
+ }
+
+ plen = p[0];
+ /* Ensure plen > mlen */
+ if (plen <= mlen)
+ plen = mlen + 1;
+
+ /* Allocate n of size plen, copy p to n */
+ n = snewn(mem_ctx, plen, BignumInt);
+ //if (!n)
+ //abort(); /* FIXME */
+ for (j = 0; j < plen; j++)
+ n[j] = 0;
+ for (j = 1; j <= (int)p[0]; j++)
+ n[plen - j] = p[j];
+
+ /* Main computation */
+ internal_mod(n, plen, m, mlen, quotient, mshift);
+
+ /* Fixup result in case the modulus was shifted */
+ if (mshift) {
+ for (i = plen - mlen - 1; i < plen - 1; i++)
+ n[i] = (n[i] << mshift) | (n[i + 1] >> (BIGNUM_INT_BITS - mshift));
+ n[plen - 1] = n[plen - 1] << mshift;
+ internal_mod(n, plen, m, mlen, quotient, 0);
+ for (i = plen - 1; i >= plen - mlen; i--)
+ n[i] = (n[i] >> mshift) | (n[i - 1] << (BIGNUM_INT_BITS - mshift));
+ }
+
+ /* Copy result to buffer */
+ if (result) {
+ for (i = 1; i <= (int)result[0]; i++) {
+ int j = plen - i;
+ result[i] = j >= 0 ? n[j] : 0;
+ }
+ }
+
+ /* Free temporary arrays */
+ for (i = 0; i < mlen; i++)
+ m[i] = 0;
+ sfree(mem_ctx, m);
+ for (i = 0; i < plen; i++)
+ n[i] = 0;
+ sfree(mem_ctx, n);
+}
+
+/*
+ * Simple remainder.
+ */
+Bignum bigmod(void *mem_ctx, Bignum a, Bignum b)
+{
+ Bignum r = newbn(mem_ctx, b[0]);
+ bigdivmod(mem_ctx, a, b, r, NULL);
+ return r;
+}
+
+/*
+ * Compute (base ^ exp) % mod.
+ */
+Bignum dwc_modpow(void *mem_ctx, Bignum base_in, Bignum exp, Bignum mod)
+{
+ BignumInt *a, *b, *n, *m;
+ int mshift;
+ int mlen, i, j;
+ Bignum base, result;
+
+ /*
+ * The most significant word of mod needs to be non-zero. It
+ * should already be, but let's make sure.
+ */
+ //assert(mod[mod[0]] != 0);
+
+ /*
+ * Make sure the base is smaller than the modulus, by reducing
+ * it modulo the modulus if not.
+ */
+ base = bigmod(mem_ctx, base_in, mod);
+
+ /* Allocate m of size mlen, copy mod to m */
+ /* We use big endian internally */
+ mlen = mod[0];
+ m = snewn(mem_ctx, mlen, BignumInt);
+ //if (!m)
+ //abort(); /* FIXME */
+ for (j = 0; j < mlen; j++)
+ m[j] = mod[mod[0] - j];
+
+ /* Shift m left to make msb bit set */
+ for (mshift = 0; mshift < BIGNUM_INT_BITS - 1; mshift++)
+ if ((m[0] << mshift) & BIGNUM_TOP_BIT)
+ break;
+ if (mshift) {
+ for (i = 0; i < mlen - 1; i++)
+ m[i] =
+ (m[i] << mshift) | (m[i + 1] >>
+ (BIGNUM_INT_BITS - mshift));
+ m[mlen - 1] = m[mlen - 1] << mshift;
+ }
+
+ /* Allocate n of size mlen, copy base to n */
+ n = snewn(mem_ctx, mlen, BignumInt);
+ //if (!n)
+ //abort(); /* FIXME */
+ i = mlen - base[0];
+ for (j = 0; j < i; j++)
+ n[j] = 0;
+ for (j = 0; j < base[0]; j++)
+ n[i + j] = base[base[0] - j];
+
+ /* Allocate a and b of size 2*mlen. Set a = 1 */
+ a = snewn(mem_ctx, 2 * mlen, BignumInt);
+ //if (!a)
+ //abort(); /* FIXME */
+ b = snewn(mem_ctx, 2 * mlen, BignumInt);
+ //if (!b)
+ //abort(); /* FIXME */
+ for (i = 0; i < 2 * mlen; i++)
+ a[i] = 0;
+ a[2 * mlen - 1] = 1;
+
+ /* Skip leading zero bits of exp. */
+ i = 0;
+ j = BIGNUM_INT_BITS - 1;
+ while (i < exp[0] && (exp[exp[0] - i] & (1 << j)) == 0) {
+ j--;
+ if (j < 0) {
+ i++;
+ j = BIGNUM_INT_BITS - 1;
+ }
+ }
+
+ /* Main computation */
+ while (i < exp[0]) {
+ while (j >= 0) {
+ internal_mul(a + mlen, a + mlen, b, mlen);
+ internal_mod(b, mlen * 2, m, mlen, NULL, 0);
+ if ((exp[exp[0] - i] & (1 << j)) != 0) {
+ internal_mul(b + mlen, n, a, mlen);
+ internal_mod(a, mlen * 2, m, mlen, NULL, 0);
+ } else {
+ BignumInt *t;
+ t = a;
+ a = b;
+ b = t;
+ }
+ j--;
+ }
+ i++;
+ j = BIGNUM_INT_BITS - 1;
+ }
+
+ /* Fixup result in case the modulus was shifted */
+ if (mshift) {
+ for (i = mlen - 1; i < 2 * mlen - 1; i++)
+ a[i] =
+ (a[i] << mshift) | (a[i + 1] >>
+ (BIGNUM_INT_BITS - mshift));
+ a[2 * mlen - 1] = a[2 * mlen - 1] << mshift;
+ internal_mod(a, mlen * 2, m, mlen, NULL, 0);
+ for (i = 2 * mlen - 1; i >= mlen; i--)
+ a[i] =
+ (a[i] >> mshift) | (a[i - 1] <<
+ (BIGNUM_INT_BITS - mshift));
+ }
+
+ /* Copy result to buffer */
+ result = newbn(mem_ctx, mod[0]);
+ for (i = 0; i < mlen; i++)
+ result[result[0] - i] = a[i + mlen];
+ while (result[0] > 1 && result[result[0]] == 0)
+ result[0]--;
+
+ /* Free temporary arrays */
+ for (i = 0; i < 2 * mlen; i++)
+ a[i] = 0;
+ sfree(mem_ctx, a);
+ for (i = 0; i < 2 * mlen; i++)
+ b[i] = 0;
+ sfree(mem_ctx, b);
+ for (i = 0; i < mlen; i++)
+ m[i] = 0;
+ sfree(mem_ctx, m);
+ for (i = 0; i < mlen; i++)
+ n[i] = 0;
+ sfree(mem_ctx, n);
+
+ freebn(mem_ctx, base);
+
+ return result;
+}
+
+
+#ifdef UNITTEST
+
+static __u32 dh_p[] = {
+ 96,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xA93AD2CA,
+ 0x4B82D120,
+ 0xE0FD108E,
+ 0x43DB5BFC,
+ 0x74E5AB31,
+ 0x08E24FA0,
+ 0xBAD946E2,
+ 0x770988C0,
+ 0x7A615D6C,
+ 0xBBE11757,
+ 0x177B200C,
+ 0x521F2B18,
+ 0x3EC86A64,
+ 0xD8760273,
+ 0xD98A0864,
+ 0xF12FFA06,
+ 0x1AD2EE6B,
+ 0xCEE3D226,
+ 0x4A25619D,
+ 0x1E8C94E0,
+ 0xDB0933D7,
+ 0xABF5AE8C,
+ 0xA6E1E4C7,
+ 0xB3970F85,
+ 0x5D060C7D,
+ 0x8AEA7157,
+ 0x58DBEF0A,
+ 0xECFB8504,
+ 0xDF1CBA64,
+ 0xA85521AB,
+ 0x04507A33,
+ 0xAD33170D,
+ 0x8AAAC42D,
+ 0x15728E5A,
+ 0x98FA0510,
+ 0x15D22618,
+ 0xEA956AE5,
+ 0x3995497C,
+ 0x95581718,
+ 0xDE2BCBF6,
+ 0x6F4C52C9,
+ 0xB5C55DF0,
+ 0xEC07A28F,
+ 0x9B2783A2,
+ 0x180E8603,
+ 0xE39E772C,
+ 0x2E36CE3B,
+ 0x32905E46,
+ 0xCA18217C,
+ 0xF1746C08,
+ 0x4ABC9804,
+ 0x670C354E,
+ 0x7096966D,
+ 0x9ED52907,
+ 0x208552BB,
+ 0x1C62F356,
+ 0xDCA3AD96,
+ 0x83655D23,
+ 0xFD24CF5F,
+ 0x69163FA8,
+ 0x1C55D39A,
+ 0x98DA4836,
+ 0xA163BF05,
+ 0xC2007CB8,
+ 0xECE45B3D,
+ 0x49286651,
+ 0x7C4B1FE6,
+ 0xAE9F2411,
+ 0x5A899FA5,
+ 0xEE386BFB,
+ 0xF406B7ED,
+ 0x0BFF5CB6,
+ 0xA637ED6B,
+ 0xF44C42E9,
+ 0x625E7EC6,
+ 0xE485B576,
+ 0x6D51C245,
+ 0x4FE1356D,
+ 0xF25F1437,
+ 0x302B0A6D,
+ 0xCD3A431B,
+ 0xEF9519B3,
+ 0x8E3404DD,
+ 0x514A0879,
+ 0x3B139B22,
+ 0x020BBEA6,
+ 0x8A67CC74,
+ 0x29024E08,
+ 0x80DC1CD1,
+ 0xC4C6628B,
+ 0x2168C234,
+ 0xC90FDAA2,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+
+static __u32 dh_a[] = {
+ 8,
+ 0xdf367516,
+ 0x86459caa,
+ 0xe2d459a4,
+ 0xd910dae0,
+ 0x8a8b5e37,
+ 0x67ab31c6,
+ 0xf0b55ea9,
+ 0x440051d6,
+};
+
+static __u32 dh_b[] = {
+ 8,
+ 0xded92656,
+ 0xe07a048a,
+ 0x6fa452cd,
+ 0x2df89d30,
+ 0xc75f1b0f,
+ 0x8ce3578f,
+ 0x7980a324,
+ 0x5daec786,
+};
+
+static __u32 dh_g[] = {
+ 1,
+ 2,
+};
+
+int main(void)
+{
+ int i;
+ __u32 *k;
+ k = dwc_modpow(NULL, dh_g, dh_a, dh_p);
+
+ printf("\n\n");
+ for (i=0; i<k[0]; i++) {
+ __u32 word32 = k[k[0] - i];
+ __u16 l = word32 & 0xffff;
+ __u16 m = (word32 & 0xffff0000) >> 16;
+ printf("%04x %04x ", m, l);
+ if (!((i + 1)%13)) printf("\n");
+ }
+ printf("\n\n");
+
+ if ((k[0] == 0x60) && (k[1] == 0x28e490e5) && (k[0x60] == 0x5a0d3d4e)) {
+ printf("PASS\n\n");
+ }
+ else {
+ printf("FAIL\n\n");
+ }
+
+}
+
+#endif /* UNITTEST */
+
+#endif /* CONFIG_MACH_IPMATE */
+
+#endif /*DWC_CRYPTOLIB */
diff --git a/drivers/usb/dwc_otg/dwc_modpow.h b/drivers/usb/dwc_otg/dwc_modpow.h
new file mode 100644
index 0000000..64f00c2
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_modpow.h
@@ -0,0 +1,34 @@
+/*
+ * dwc_modpow.h
+ * See dwc_modpow.c for license and changes
+ */
+#ifndef _DWC_MODPOW_H
+#define _DWC_MODPOW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "dwc_os.h"
+
+/** @file
+ *
+ * This file defines the module exponentiation function which is only used
+ * internally by the DWC UWB modules for calculation of PKs during numeric
+ * association. The routine is taken from the PUTTY, an open source terminal
+ * emulator. The PUTTY License is preserved in the dwc_modpow.c file.
+ *
+ */
+
+typedef uint32_t BignumInt;
+typedef uint64_t BignumDblInt;
+typedef BignumInt *Bignum;
+
+/* Compute modular exponentiaion */
+extern Bignum dwc_modpow(void *mem_ctx, Bignum base_in, Bignum exp, Bignum mod);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LINUX_BIGNUM_H */
diff --git a/drivers/usb/dwc_otg/dwc_notifier.c b/drivers/usb/dwc_otg/dwc_notifier.c
new file mode 100644
index 0000000..d3dadce
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_notifier.c
@@ -0,0 +1,319 @@
+#ifdef DWC_NOTIFYLIB
+
+#include "dwc_notifier.h"
+#include "dwc_list.h"
+
+typedef struct dwc_observer {
+ void *observer;
+ dwc_notifier_callback_t callback;
+ void *data;
+ char *notification;
+ DWC_CIRCLEQ_ENTRY(dwc_observer) list_entry;
+} observer_t;
+
+DWC_CIRCLEQ_HEAD(observer_queue, dwc_observer);
+
+typedef struct dwc_notifier {
+ void *mem_ctx;
+ void *object;
+ struct observer_queue observers;
+ DWC_CIRCLEQ_ENTRY(dwc_notifier) list_entry;
+} notifier_t;
+
+DWC_CIRCLEQ_HEAD(notifier_queue, dwc_notifier);
+
+typedef struct manager {
+ void *mem_ctx;
+ void *wkq_ctx;
+ dwc_workq_t *wq;
+// dwc_mutex_t *mutex;
+ struct notifier_queue notifiers;
+} manager_t;
+
+static manager_t *manager = NULL;
+
+static int create_manager(void *mem_ctx, void *wkq_ctx)
+{
+ manager = dwc_alloc(mem_ctx, sizeof(manager_t));
+ if (!manager) {
+ return -DWC_E_NO_MEMORY;
+ }
+
+ DWC_CIRCLEQ_INIT(&manager->notifiers);
+
+ manager->wq = dwc_workq_alloc(wkq_ctx, "DWC Notification WorkQ");
+ if (!manager->wq) {
+ return -DWC_E_NO_MEMORY;
+ }
+
+ return 0;
+}
+
+static void free_manager(void)
+{
+ dwc_workq_free(manager->wq);
+
+ /* All notifiers must have unregistered themselves before this module
+ * can be removed. Hitting this assertion indicates a programmer
+ * error. */
+ DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&manager->notifiers),
+ "Notification manager being freed before all notifiers have been removed");
+ dwc_free(manager->mem_ctx, manager);
+}
+
+#ifdef DEBUG
+static void dump_manager(void)
+{
+ notifier_t *n;
+ observer_t *o;
+
+ DWC_ASSERT(manager, "Notification manager not found");
+
+ DWC_DEBUG("List of all notifiers and observers:\n");
+ DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
+ DWC_DEBUG("Notifier %p has observers:\n", n->object);
+ DWC_CIRCLEQ_FOREACH(o, &n->observers, list_entry) {
+ DWC_DEBUG(" %p watching %s\n", o->observer, o->notification);
+ }
+ }
+}
+#else
+#define dump_manager(...)
+#endif
+
+static observer_t *alloc_observer(void *mem_ctx, void *observer, char *notification,
+ dwc_notifier_callback_t callback, void *data)
+{
+ observer_t *new_observer = dwc_alloc(mem_ctx, sizeof(observer_t));
+
+ if (!new_observer) {
+ return NULL;
+ }
+
+ DWC_CIRCLEQ_INIT_ENTRY(new_observer, list_entry);
+ new_observer->observer = observer;
+ new_observer->notification = notification;
+ new_observer->callback = callback;
+ new_observer->data = data;
+ return new_observer;
+}
+
+static void free_observer(void *mem_ctx, observer_t *observer)
+{
+ dwc_free(mem_ctx, observer);
+}
+
+static notifier_t *alloc_notifier(void *mem_ctx, void *object)
+{
+ notifier_t *notifier;
+
+ if (!object) {
+ return NULL;
+ }
+
+ notifier = dwc_alloc(mem_ctx, sizeof(notifier_t));
+ if (!notifier) {
+ return NULL;
+ }
+
+ DWC_CIRCLEQ_INIT(¬ifier->observers);
+ DWC_CIRCLEQ_INIT_ENTRY(notifier, list_entry);
+
+ notifier->mem_ctx = mem_ctx;
+ notifier->object = object;
+ return notifier;
+}
+
+static void free_notifier(notifier_t *notifier)
+{
+ observer_t *observer;
+
+ DWC_CIRCLEQ_FOREACH(observer, ¬ifier->observers, list_entry) {
+ free_observer(notifier->mem_ctx, observer);
+ }
+
+ dwc_free(notifier->mem_ctx, notifier);
+}
+
+static notifier_t *find_notifier(void *object)
+{
+ notifier_t *notifier;
+
+ DWC_ASSERT(manager, "Notification manager not found");
+
+ if (!object) {
+ return NULL;
+ }
+
+ DWC_CIRCLEQ_FOREACH(notifier, &manager->notifiers, list_entry) {
+ if (notifier->object == object) {
+ return notifier;
+ }
+ }
+
+ return NULL;
+}
+
+int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx)
+{
+ return create_manager(mem_ctx, wkq_ctx);
+}
+
+void dwc_free_notification_manager(void)
+{
+ free_manager();
+}
+
+dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object)
+{
+ notifier_t *notifier;
+
+ DWC_ASSERT(manager, "Notification manager not found");
+
+ notifier = find_notifier(object);
+ if (notifier) {
+ DWC_ERROR("Notifier %p is already registered\n", object);
+ return NULL;
+ }
+
+ notifier = alloc_notifier(mem_ctx, object);
+ if (!notifier) {
+ return NULL;
+ }
+
+ DWC_CIRCLEQ_INSERT_TAIL(&manager->notifiers, notifier, list_entry);
+
+ DWC_INFO("Notifier %p registered", object);
+ dump_manager();
+
+ return notifier;
+}
+
+void dwc_unregister_notifier(dwc_notifier_t *notifier)
+{
+ DWC_ASSERT(manager, "Notification manager not found");
+
+ if (!DWC_CIRCLEQ_EMPTY(¬ifier->observers)) {
+ observer_t *o;
+
+ DWC_ERROR("Notifier %p has active observers when removing\n", notifier->object);
+ DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) {
+ DWC_DEBUG(" %p watching %s\n", o->observer, o->notification);
+ }
+
+ DWC_ASSERT(DWC_CIRCLEQ_EMPTY(¬ifier->observers),
+ "Notifier %p has active observers when removing", notifier);
+ }
+
+ DWC_CIRCLEQ_REMOVE_INIT(&manager->notifiers, notifier, list_entry);
+ free_notifier(notifier);
+
+ DWC_INFO("Notifier unregistered");
+ dump_manager();
+}
+
+/* Add an observer to observe the notifier for a particular state, event, or notification. */
+int dwc_add_observer(void *observer, void *object, char *notification,
+ dwc_notifier_callback_t callback, void *data)
+{
+ notifier_t *notifier = find_notifier(object);
+ observer_t *new_observer;
+
+ if (!notifier) {
+ DWC_ERROR("Notifier %p is not found when adding observer\n", object);
+ return -DWC_E_INVALID;
+ }
+
+ new_observer = alloc_observer(notifier->mem_ctx, observer, notification, callback, data);
+ if (!new_observer) {
+ return -DWC_E_NO_MEMORY;
+ }
+
+ DWC_CIRCLEQ_INSERT_TAIL(¬ifier->observers, new_observer, list_entry);
+
+ DWC_INFO("Added observer %p to notifier %p observing notification %s, callback=%p, data=%p",
+ observer, object, notification, callback, data);
+
+ dump_manager();
+ return 0;
+}
+
+int dwc_remove_observer(void *observer)
+{
+ notifier_t *n;
+
+ DWC_ASSERT(manager, "Notification manager not found");
+
+ DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
+ observer_t *o;
+ observer_t *o2;
+
+ DWC_CIRCLEQ_FOREACH_SAFE(o, o2, &n->observers, list_entry) {
+ if (o->observer == observer) {
+ DWC_CIRCLEQ_REMOVE_INIT(&n->observers, o, list_entry);
+ DWC_INFO("Removing observer %p from notifier %p watching notification %s:",
+ o->observer, n->object, o->notification);
+ free_observer(n->mem_ctx, o);
+ }
+ }
+ }
+
+ dump_manager();
+ return 0;
+}
+
+typedef struct callback_data {
+ void *mem_ctx;
+ dwc_notifier_callback_t cb;
+ void *observer;
+ void *data;
+ void *object;
+ char *notification;
+ void *notification_data;
+} cb_data_t;
+
+static void cb_task(void *data)
+{
+ cb_data_t *cb = (cb_data_t *)data;
+
+ cb->cb(cb->object, cb->notification, cb->observer, cb->notification_data, cb->data);
+ dwc_free(cb->mem_ctx, cb);
+}
+
+void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data)
+{
+ observer_t *o;
+
+ DWC_ASSERT(manager, "Notification manager not found");
+
+ DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) {
+ int len = DWC_STRLEN(notification);
+
+ if (DWC_STRLEN(o->notification) != len) {
+ continue;
+ }
+
+ if (DWC_STRNCMP(o->notification, notification, len) == 0) {
+ cb_data_t *cb_data = dwc_alloc(notifier->mem_ctx, sizeof(cb_data_t));
+
+ if (!cb_data) {
+ DWC_ERROR("Failed to allocate callback data\n");
+ return;
+ }
+
+ cb_data->mem_ctx = notifier->mem_ctx;
+ cb_data->cb = o->callback;
+ cb_data->observer = o->observer;
+ cb_data->data = o->data;
+ cb_data->object = notifier->object;
+ cb_data->notification = notification;
+ cb_data->notification_data = notification_data;
+ DWC_DEBUG("Observer found %p for notification %s\n", o->observer, notification);
+ DWC_WORKQ_SCHEDULE(manager->wq, cb_task, cb_data,
+ "Notify callback from %p for Notification %s, to observer %p",
+ cb_data->object, notification, cb_data->observer);
+ }
+ }
+}
+
+#endif /* DWC_NOTIFYLIB */
diff --git a/drivers/usb/dwc_otg/dwc_notifier.h b/drivers/usb/dwc_otg/dwc_notifier.h
new file mode 100644
index 0000000..4a8cdfe
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_notifier.h
@@ -0,0 +1,122 @@
+
+#ifndef __DWC_NOTIFIER_H__
+#define __DWC_NOTIFIER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "dwc_os.h"
+
+/** @file
+ *
+ * A simple implementation of the Observer pattern. Any "module" can
+ * register as an observer or notifier. The notion of "module" is abstract and
+ * can mean anything used to identify either an observer or notifier. Usually
+ * it will be a pointer to a data structure which contains some state, ie an
+ * object.
+ *
+ * Before any notifiers can be added, the global notification manager must be
+ * brought up with dwc_alloc_notification_manager().
+ * dwc_free_notification_manager() will bring it down and free all resources.
+ * These would typically be called upon module load and unload. The
+ * notification manager is a single global instance that handles all registered
+ * observable modules and observers so this should be done only once.
+ *
+ * A module can be observable by using Notifications to publicize some general
+ * information about it's state or operation. It does not care who listens, or
+ * even if anyone listens, or what they do with the information. The observable
+ * modules do not need to know any information about it's observers or their
+ * interface, or their state or data.
+ *
+ * Any module can register to emit Notifications. It should publish a list of
+ * notifications that it can emit and their behavior, such as when they will get
+ * triggered, and what information will be provided to the observer. Then it
+ * should register itself as an observable module. See dwc_register_notifier().
+ *
+ * Any module can observe any observable, registered module, provided it has a
+ * handle to the other module and knows what notifications to observe. See
+ * dwc_add_observer().
+ *
+ * A function of type dwc_notifier_callback_t is called whenever a notification
+ * is triggered with one or more observers observing it. This function is
+ * called in it's own process so it may sleep or block if needed. It is
+ * guaranteed to be called sometime after the notification has occurred and will
+ * be called once per each time the notification is triggered. It will NOT be
+ * called in the same process context used to trigger the notification.
+ *
+ * @section Limitiations
+ *
+ * Keep in mind that Notifications that can be triggered in rapid sucession may
+ * schedule too many processes too handle. Be aware of this limitation when
+ * designing to use notifications, and only add notifications for appropriate
+ * observable information.
+ *
+ * Also Notification callbacks are not synchronous. If you need to synchronize
+ * the behavior between module/observer you must use other means. And perhaps
+ * that will mean Notifications are not the proper solution.
+ */
+
+struct dwc_notifier;
+typedef struct dwc_notifier dwc_notifier_t;
+
+/** The callback function must be of this type.
+ *
+ * @param object This is the object that is being observed.
+ * @param notification This is the notification that was triggered.
+ * @param observer This is the observer
+ * @param notification_data This is notification-specific data that the notifier
+ * has included in this notification. The value of this should be published in
+ * the documentation of the observable module with the notifications.
+ * @param user_data This is any custom data that the observer provided when
+ * adding itself as an observer to the notification. */
+typedef void (*dwc_notifier_callback_t)(void *object, char *notification, void *observer,
+ void *notification_data, void *user_data);
+
+/** Brings up the notification manager. */
+extern int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx);
+/** Brings down the notification manager. */
+extern void dwc_free_notification_manager(void);
+
+/** This function registers an observable module. A dwc_notifier_t object is
+ * returned to the observable module. This is an opaque object that is used by
+ * the observable module to trigger notifications. This object should only be
+ * accessible to functions that are authorized to trigger notifications for this
+ * module. Observers do not need this object. */
+extern dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object);
+
+/** This function unregisters an observable module. All observers have to be
+ * removed prior to unregistration. */
+extern void dwc_unregister_notifier(dwc_notifier_t *notifier);
+
+/** Add a module as an observer to the observable module. The observable module
+ * needs to have previously registered with the notification manager.
+ *
+ * @param observer The observer module
+ * @param object The module to observe
+ * @param notification The notification to observe
+ * @param callback The callback function to call
+ * @param user_data Any additional user data to pass into the callback function */
+extern int dwc_add_observer(void *observer, void *object, char *notification,
+ dwc_notifier_callback_t callback, void *user_data);
+
+/** Removes the specified observer from all notifications that it is currently
+ * observing. */
+extern int dwc_remove_observer(void *observer);
+
+/** This function triggers a Notification. It should be called by the
+ * observable module, or any module or library which the observable module
+ * allows to trigger notification on it's behalf. Such as the dwc_cc_t.
+ *
+ * dwc_notify is a non-blocking function. Callbacks are scheduled called in
+ * their own process context for each trigger. Callbacks can be blocking.
+ * dwc_notify can be called from interrupt context if needed.
+ *
+ */
+void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DWC_NOTIFIER_H__ */
diff --git a/drivers/usb/dwc_otg/dwc_os.h b/drivers/usb/dwc_otg/dwc_os.h
new file mode 100644
index 0000000..fa9d5c6
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_os.h
@@ -0,0 +1,1237 @@
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_os.h $
+ * $Revision: #14 $
+ * $Date: 2010/11/04 $
+ * $Change: 1621695 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifndef _DWC_OS_H_
+#define _DWC_OS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file
+ *
+ * DWC portability library, low level os-wrapper functions
+ *
+ */
+
+/* These basic types need to be defined by some OS header file or custom header
+ * file for your specific target architecture.
+ *
+ * uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t
+ *
+ * Any custom or alternate header file must be added and enabled here.
+ */
+
+#ifdef DWC_LINUX
+# include <linux/types.h>
+# ifdef CONFIG_DEBUG_MUTEXES
+# include <linux/mutex.h>
+# endif
+# include <linux/errno.h>
+# include <stdarg.h>
+#endif
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+# include <os_dep.h>
+#endif
+
+
+/** @name Primitive Types and Values */
+
+/** We define a boolean type for consistency. Can be either YES or NO */
+typedef uint8_t dwc_bool_t;
+#define YES 1
+#define NO 0
+
+#ifdef DWC_LINUX
+
+/** @name Error Codes */
+#define DWC_E_INVALID EINVAL
+#define DWC_E_NO_MEMORY ENOMEM
+#define DWC_E_NO_DEVICE ENODEV
+#define DWC_E_NOT_SUPPORTED EOPNOTSUPP
+#define DWC_E_TIMEOUT ETIMEDOUT
+#define DWC_E_BUSY EBUSY
+#define DWC_E_AGAIN EAGAIN
+#define DWC_E_RESTART ERESTART
+#define DWC_E_ABORT ECONNABORTED
+#define DWC_E_SHUTDOWN ESHUTDOWN
+#define DWC_E_NO_DATA ENODATA
+#define DWC_E_DISCONNECT ECONNRESET
+#define DWC_E_UNKNOWN EINVAL
+#define DWC_E_NO_STREAM_RES ENOSR
+#define DWC_E_COMMUNICATION ECOMM
+#define DWC_E_OVERFLOW EOVERFLOW
+#define DWC_E_PROTOCOL EPROTO
+#define DWC_E_IN_PROGRESS EINPROGRESS
+#define DWC_E_PIPE EPIPE
+#define DWC_E_IO EIO
+#define DWC_E_NO_SPACE ENOSPC
+
+#else
+
+/** @name Error Codes */
+#define DWC_E_INVALID 1001
+#define DWC_E_NO_MEMORY 1002
+#define DWC_E_NO_DEVICE 1003
+#define DWC_E_NOT_SUPPORTED 1004
+#define DWC_E_TIMEOUT 1005
+#define DWC_E_BUSY 1006
+#define DWC_E_AGAIN 1007
+#define DWC_E_RESTART 1008
+#define DWC_E_ABORT 1009
+#define DWC_E_SHUTDOWN 1010
+#define DWC_E_NO_DATA 1011
+#define DWC_E_DISCONNECT 2000
+#define DWC_E_UNKNOWN 3000
+#define DWC_E_NO_STREAM_RES 4001
+#define DWC_E_COMMUNICATION 4002
+#define DWC_E_OVERFLOW 4003
+#define DWC_E_PROTOCOL 4004
+#define DWC_E_IN_PROGRESS 4005
+#define DWC_E_PIPE 4006
+#define DWC_E_IO 4007
+#define DWC_E_NO_SPACE 4008
+
+#endif
+
+
+/** @name Tracing/Logging Functions
+ *
+ * These function provide the capability to add tracing, debugging, and error
+ * messages, as well exceptions as assertions. The WUDEV uses these
+ * extensively. These could be logged to the main console, the serial port, an
+ * internal buffer, etc. These functions could also be no-op if they are too
+ * expensive on your system. By default undefining the DEBUG macro already
+ * no-ops some of these functions. */
+
+/** Returns non-zero if in interrupt context. */
+extern dwc_bool_t DWC_IN_IRQ(void);
+#define dwc_in_irq DWC_IN_IRQ
+
+/** Returns "IRQ" if DWC_IN_IRQ is true. */
+static inline char *dwc_irq(void) {
+ return DWC_IN_IRQ() ? "IRQ" : "";
+}
+
+/** Returns non-zero if in bottom-half context. */
+extern dwc_bool_t DWC_IN_BH(void);
+#define dwc_in_bh DWC_IN_BH
+
+/** Returns "BH" if DWC_IN_BH is true. */
+static inline char *dwc_bh(void) {
+ return DWC_IN_BH() ? "BH" : "";
+}
+
+/**
+ * A vprintf() clone. Just call vprintf if you've got it.
+ */
+extern void DWC_VPRINTF(char *format, va_list args);
+#define dwc_vprintf DWC_VPRINTF
+
+/**
+ * A vsnprintf() clone. Just call vprintf if you've got it.
+ */
+extern int DWC_VSNPRINTF(char *str, int size, char *format, va_list args);
+#define dwc_vsnprintf DWC_VSNPRINTF
+
+/**
+ * printf() clone. Just call printf if you've go it.
+ */
+extern void DWC_PRINTF(char *format, ...)
+/* This provides compiler level static checking of the parameters if you're
+ * using GCC. */
+#ifdef __GNUC__
+ __attribute__ ((format(printf, 1, 2)));
+#else
+ ;
+#endif
+#define dwc_printf DWC_PRINTF
+
+/**
+ * sprintf() clone. Just call sprintf if you've got it.
+ */
+extern int DWC_SPRINTF(char *string, char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format(printf, 2, 3)));
+#else
+ ;
+#endif
+#define dwc_sprintf DWC_SPRINTF
+
+/**
+ * snprintf() clone. Just call snprintf if you've got it.
+ */
+extern int DWC_SNPRINTF(char *string, int size, char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format(printf, 3, 4)));
+#else
+ ;
+#endif
+#define dwc_snprintf DWC_SNPRINTF
+
+/**
+ * Prints a WARNING message. On systems that don't differentiate between
+ * warnings and regular log messages, just print it. Indicates that something
+ * may be wrong with the driver. Works like printf().
+ *
+ * Use the DWC_WARN macro to call this function.
+ */
+extern void __DWC_WARN(char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format(printf, 1, 2)));
+#else
+ ;
+#endif
+
+/**
+ * Prints an error message. On systems that don't differentiate between errors
+ * and regular log messages, just print it. Indicates that something went wrong
+ * with the driver. Works like printf().
+ *
+ * Use the DWC_ERROR macro to call this function.
+ */
+extern void __DWC_ERROR(char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format(printf, 1, 2)));
+#else
+ ;
+#endif
+
+/**
+ * Prints an exception error message and takes some user-defined action such as
+ * print out a backtrace or trigger a breakpoint. Indicates that something went
+ * abnormally wrong with the driver such as programmer error, or other
+ * exceptional condition. It should not be ignored so even on systems without
+ * printing capability, some action should be taken to notify the developer of
+ * it. Works like printf().
+ */
+extern void DWC_EXCEPTION(char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format(printf, 1, 2)));
+#else
+ ;
+#endif
+#define dwc_exception DWC_EXCEPTION
+
+#ifdef DEBUG
+/**
+ * Prints out a debug message. Used for logging/trace messages.
+ *
+ * Use the DWC_DEBUG macro to call this function
+ */
+extern void __DWC_DEBUG(char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format(printf, 1, 2)));
+#else
+ ;
+#endif
+#else
+#define __DWC_DEBUG(...)
+#endif
+
+/**
+ * Prints out a Debug message.
+ */
+#define DWC_DEBUG(_format, _args...) __DWC_DEBUG("DEBUG:%s:%s: " _format "\n", \
+ __func__, dwc_irq(), ## _args)
+#define dwc_debug DWC_DEBUG
+/**
+ * Prints out an informative message.
+ */
+#define DWC_INFO(_format, _args...) DWC_PRINTF("INFO:%s: " _format "\n", \
+ dwc_irq(), ## _args)
+#define dwc_info DWC_INFO
+/**
+ * Prints out a warning message.
+ */
+#define DWC_WARN(_format, _args...) __DWC_WARN("WARN:%s:%s:%d: " _format "\n", \
+ dwc_irq(), __func__, __LINE__, ## _args)
+#define dwc_warn DWC_WARN
+/**
+ * Prints out an error message.
+ */
+#define DWC_ERROR(_format, _args...) __DWC_ERROR("ERROR:%s:%s:%d: " _format "\n", \
+ dwc_irq(), __func__, __LINE__, ## _args)
+#define dwc_error DWC_ERROR
+
+#define DWC_PROTO_ERROR(_format, _args...) __DWC_WARN("ERROR:%s:%s:%d: " _format "\n", \
+ dwc_irq(), __func__, __LINE__, ## _args)
+#define dwc_proto_error DWC_PROTO_ERROR
+
+#ifdef DEBUG
+/** Prints out a exception error message if the _expr expression fails. Disabled
+ * if DEBUG is not enabled. */
+#define DWC_ASSERT(_expr, _format, _args...) do { \
+ if (!(_expr)) { DWC_EXCEPTION("%s:%s:%d: " _format "\n", dwc_irq(), \
+ __FILE__, __LINE__, ## _args); } \
+ } while (0)
+#else
+#define DWC_ASSERT(_x...)
+#endif
+#define dwc_assert DWC_ASSERT
+
+
+/** @name Byte Ordering
+ * The following functions are for conversions between processor's byte ordering
+ * and specific ordering you want.
+ */
+
+/** Converts 32 bit data in CPU byte ordering to little endian. */
+extern uint32_t DWC_CPU_TO_LE32(uint32_t *p);
+#define dwc_cpu_to_le32 DWC_CPU_TO_LE32
+
+/** Converts 32 bit data in CPU byte orderint to big endian. */
+extern uint32_t DWC_CPU_TO_BE32(uint32_t *p);
+#define dwc_cpu_to_be32 DWC_CPU_TO_BE32
+
+/** Converts 32 bit little endian data to CPU byte ordering. */
+extern uint32_t DWC_LE32_TO_CPU(uint32_t *p);
+#define dwc_le32_to_cpu DWC_LE32_TO_CPU
+
+/** Converts 32 bit big endian data to CPU byte ordering. */
+extern uint32_t DWC_BE32_TO_CPU(uint32_t *p);
+#define dwc_be32_to_cpu DWC_BE32_TO_CPU
+
+/** Converts 16 bit data in CPU byte ordering to little endian. */
+extern uint16_t DWC_CPU_TO_LE16(uint16_t *p);
+#define dwc_cpu_to_le16 DWC_CPU_TO_LE16
+
+/** Converts 16 bit data in CPU byte orderint to big endian. */
+extern uint16_t DWC_CPU_TO_BE16(uint16_t *p);
+#define dwc_cpu_to_be16 DWC_CPU_TO_BE16
+
+/** Converts 16 bit little endian data to CPU byte ordering. */
+extern uint16_t DWC_LE16_TO_CPU(uint16_t *p);
+#define dwc_le16_to_cpu DWC_LE16_TO_CPU
+
+/** Converts 16 bit bi endian data to CPU byte ordering. */
+extern uint16_t DWC_BE16_TO_CPU(uint16_t *p);
+#define dwc_be16_to_cpu DWC_BE16_TO_CPU
+
+
+/** @name Register Read/Write
+ *
+ * The following six functions should be implemented to read/write registers of
+ * 32-bit and 64-bit sizes. All modules use this to read/write register values.
+ * The reg value is a pointer to the register calculated from the void *base
+ * variable passed into the driver when it is started. */
+
+#ifdef DWC_LINUX
+/* Linux doesn't need any extra parameters for register read/write, so we
+ * just throw away the IO context parameter.
+ */
+/** Reads the content of a 32-bit register. */
+extern uint32_t DWC_READ_REG32(uint32_t volatile *reg);
+#define dwc_read_reg32(_ctx_,_reg_) DWC_READ_REG32(_reg_)
+
+/** Reads the content of a 64-bit register. */
+extern uint64_t DWC_READ_REG64(uint64_t volatile *reg);
+#define dwc_read_reg64(_ctx_,_reg_) DWC_READ_REG64(_reg_)
+
+/** Writes to a 32-bit register. */
+extern void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value);
+#define dwc_write_reg32(_ctx_,_reg_,_val_) DWC_WRITE_REG32(_reg_, _val_)
+
+/** Writes to a 64-bit register. */
+extern void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value);
+#define dwc_write_reg64(_ctx_,_reg_,_val_) DWC_WRITE_REG64(_reg_, _val_)
+
+/**
+ * Modify bit values in a register. Using the
+ * algorithm: (reg_contents & ~clear_mask) | set_mask.
+ */
+extern void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask);
+#define dwc_modify_reg32(_ctx_,_reg_,_cmsk_,_smsk_) DWC_MODIFY_REG32(_reg_,_cmsk_,_smsk_)
+extern void DWC_MODIFY_REG64(uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask);
+#define dwc_modify_reg64(_ctx_,_reg_,_cmsk_,_smsk_) DWC_MODIFY_REG64(_reg_,_cmsk_,_smsk_)
+
+#endif /* DWC_LINUX */
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+typedef struct dwc_ioctx {
+ struct device *dev;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+} dwc_ioctx_t;
+
+/** BSD needs two extra parameters for register read/write, so we pass
+ * them in using the IO context parameter.
+ */
+/** Reads the content of a 32-bit register. */
+extern uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg);
+#define dwc_read_reg32 DWC_READ_REG32
+
+/** Reads the content of a 64-bit register. */
+extern uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg);
+#define dwc_read_reg64 DWC_READ_REG64
+
+/** Writes to a 32-bit register. */
+extern void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value);
+#define dwc_write_reg32 DWC_WRITE_REG32
+
+/** Writes to a 64-bit register. */
+extern void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value);
+#define dwc_write_reg64 DWC_WRITE_REG64
+
+/**
+ * Modify bit values in a register. Using the
+ * algorithm: (reg_contents & ~clear_mask) | set_mask.
+ */
+extern void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask);
+#define dwc_modify_reg32 DWC_MODIFY_REG32
+extern void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask);
+#define dwc_modify_reg64 DWC_MODIFY_REG64
+
+#endif /* DWC_FREEBSD || DWC_NETBSD */
+
+/** @cond */
+
+/** @name Some convenience MACROS used internally. Define DWC_DEBUG_REGS to log the
+ * register writes. */
+
+#ifdef DWC_LINUX
+
+# ifdef DWC_DEBUG_REGS
+
+#define dwc_define_read_write_reg_n(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \
+ return DWC_READ_REG32(&container->regs->_reg[num]); \
+} \
+static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \
+ DWC_DEBUG("WRITING %8s[%d]: %p: %08x", #_reg, num, \
+ &(((uint32_t*)container->regs->_reg)[num]), data); \
+ DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \
+}
+
+#define dwc_define_read_write_reg(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg(_container_type *container) { \
+ return DWC_READ_REG32(&container->regs->_reg); \
+} \
+static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \
+ DWC_DEBUG("WRITING %11s: %p: %08x", #_reg, &container->regs->_reg, data); \
+ DWC_WRITE_REG32(&container->regs->_reg, data); \
+}
+
+# else /* DWC_DEBUG_REGS */
+
+#define dwc_define_read_write_reg_n(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \
+ return DWC_READ_REG32(&container->regs->_reg[num]); \
+} \
+static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \
+ DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \
+}
+
+#define dwc_define_read_write_reg(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg(_container_type *container) { \
+ return DWC_READ_REG32(&container->regs->_reg); \
+} \
+static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \
+ DWC_WRITE_REG32(&container->regs->_reg, data); \
+}
+
+# endif /* DWC_DEBUG_REGS */
+
+#endif /* DWC_LINUX */
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+
+# ifdef DWC_DEBUG_REGS
+
+#define dwc_define_read_write_reg_n(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg##_n(void *io_ctx, _container_type *container, int num) { \
+ return DWC_READ_REG32(io_ctx, &container->regs->_reg[num]); \
+} \
+static inline void dwc_write_##_reg##_n(void *io_ctx, _container_type *container, int num, uint32_t data) { \
+ DWC_DEBUG("WRITING %8s[%d]: %p: %08x", #_reg, num, \
+ &(((uint32_t*)container->regs->_reg)[num]), data); \
+ DWC_WRITE_REG32(io_ctx, &(((uint32_t*)container->regs->_reg)[num]), data); \
+}
+
+#define dwc_define_read_write_reg(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg(void *io_ctx, _container_type *container) { \
+ return DWC_READ_REG32(io_ctx, &container->regs->_reg); \
+} \
+static inline void dwc_write_##_reg(void *io_ctx, _container_type *container, uint32_t data) { \
+ DWC_DEBUG("WRITING %11s: %p: %08x", #_reg, &container->regs->_reg, data); \
+ DWC_WRITE_REG32(io_ctx, &container->regs->_reg, data); \
+}
+
+# else /* DWC_DEBUG_REGS */
+
+#define dwc_define_read_write_reg_n(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg##_n(void *io_ctx, _container_type *container, int num) { \
+ return DWC_READ_REG32(io_ctx, &container->regs->_reg[num]); \
+} \
+static inline void dwc_write_##_reg##_n(void *io_ctx, _container_type *container, int num, uint32_t data) { \
+ DWC_WRITE_REG32(io_ctx, &(((uint32_t*)container->regs->_reg)[num]), data); \
+}
+
+#define dwc_define_read_write_reg(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg(void *io_ctx, _container_type *container) { \
+ return DWC_READ_REG32(io_ctx, &container->regs->_reg); \
+} \
+static inline void dwc_write_##_reg(void *io_ctx, _container_type *container, uint32_t data) { \
+ DWC_WRITE_REG32(io_ctx, &container->regs->_reg, data); \
+}
+
+# endif /* DWC_DEBUG_REGS */
+
+#endif /* DWC_FREEBSD || DWC_NETBSD */
+
+/** @endcond */
+
+
+#ifdef DWC_CRYPTOLIB
+/** @name Crypto Functions
+ *
+ * These are the low-level cryptographic functions used by the driver. */
+
+/** Perform AES CBC */
+extern int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out);
+#define dwc_aes_cbc DWC_AES_CBC
+
+/** Fill the provided buffer with random bytes. These should be cryptographic grade random numbers. */
+extern void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length);
+#define dwc_random_bytes DWC_RANDOM_BYTES
+
+/** Perform the SHA-256 hash function */
+extern int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out);
+#define dwc_sha256 DWC_SHA256
+
+/** Calculated the HMAC-SHA256 */
+extern int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t *out);
+#define dwc_hmac_sha256 DWC_HMAC_SHA256
+
+#endif /* DWC_CRYPTOLIB */
+
+
+/** @name Memory Allocation
+ *
+ * These function provide access to memory allocation. There are only 2 DMA
+ * functions and 3 Regular memory functions that need to be implemented. None
+ * of the memory debugging routines need to be implemented. The allocation
+ * routines all ZERO the contents of the memory.
+ *
+ * Defining DWC_DEBUG_MEMORY turns on memory debugging and statistic gathering.
+ * This checks for memory leaks, keeping track of alloc/free pairs. It also
+ * keeps track of how much memory the driver is using at any given time. */
+
+#define DWC_PAGE_SIZE 4096
+#define DWC_PAGE_OFFSET(addr) (((uint32_t)addr) & 0xfff)
+#define DWC_PAGE_ALIGNED(addr) ((((uint32_t)addr) & 0xfff) == 0)
+
+#define DWC_INVALID_DMA_ADDR 0x0
+
+#ifdef DWC_LINUX
+/** Type for a DMA address */
+typedef dma_addr_t dwc_dma_t;
+#endif
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+typedef bus_addr_t dwc_dma_t;
+#endif
+
+#ifdef DWC_FREEBSD
+typedef struct dwc_dmactx {
+ struct device *dev;
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+ bus_addr_t dma_paddr;
+ void *dma_vaddr;
+} dwc_dmactx_t;
+#endif
+
+#ifdef DWC_NETBSD
+typedef struct dwc_dmactx {
+ struct device *dev;
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+ bus_dma_segment_t segs[1];
+ int nsegs;
+ bus_addr_t dma_paddr;
+ void *dma_vaddr;
+} dwc_dmactx_t;
+#endif
+
+/* @todo these functions will be added in the future */
+#if 0
+/**
+ * Creates a DMA pool from which you can allocate DMA buffers. Buffers
+ * allocated from this pool will be guaranteed to meet the size, alignment, and
+ * boundary requirements specified.
+ *
+ * @param[in] size Specifies the size of the buffers that will be allocated from
+ * this pool.
+ * @param[in] align Specifies the byte alignment requirements of the buffers
+ * allocated from this pool. Must be a power of 2.
+ * @param[in] boundary Specifies the N-byte boundary that buffers allocated from
+ * this pool must not cross.
+ *
+ * @returns A pointer to an internal opaque structure which is not to be
+ * accessed outside of these library functions. Use this handle to specify
+ * which pools to allocate/free DMA buffers from and also to destroy the pool,
+ * when you are done with it.
+ */
+extern dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size, uint32_t align, uint32_t boundary);
+
+/**
+ * Destroy a DMA pool. All buffers allocated from that pool must be freed first.
+ */
+extern void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool);
+
+/**
+ * Allocate a buffer from the specified DMA pool and zeros its contents.
+ */
+extern void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr);
+
+/**
+ * Free a previously allocated buffer from the DMA pool.
+ */
+extern void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr);
+#endif
+
+/** Allocates a DMA capable buffer and zeroes its contents. */
+extern void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr);
+
+/** Allocates a DMA capable buffer and zeroes its contents in atomic contest */
+extern void *__DWC_DMA_ALLOC_ATOMIC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr);
+
+/** Frees a previously allocated buffer. */
+extern void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr);
+
+/** Allocates a block of memory and zeroes its contents. */
+extern void *__DWC_ALLOC(void *mem_ctx, uint32_t size);
+
+/** Allocates a block of memory and zeroes its contents, in an atomic manner
+ * which can be used inside interrupt context. The size should be sufficiently
+ * small, a few KB at most, such that failures are not likely to occur. Can just call
+ * __DWC_ALLOC if it is atomic. */
+extern void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size);
+
+/** Frees a previously allocated buffer. */
+extern void __DWC_FREE(void *mem_ctx, void *addr);
+
+#ifndef DWC_DEBUG_MEMORY
+
+#define DWC_ALLOC(_size_) __DWC_ALLOC(NULL, _size_)
+#define DWC_ALLOC_ATOMIC(_size_) __DWC_ALLOC_ATOMIC(NULL, _size_)
+#define DWC_FREE(_addr_) __DWC_FREE(NULL, _addr_)
+
+# ifdef DWC_LINUX
+#define DWC_DMA_ALLOC(_size_,_dma_) __DWC_DMA_ALLOC(NULL, _size_, _dma_)
+#define DWC_DMA_ALLOC_ATOMIC(_size_,_dma_) __DWC_DMA_ALLOC_ATOMIC(NULL, _size_,_dma_)
+#define DWC_DMA_FREE(_size_,_virt_,_dma_) __DWC_DMA_FREE(NULL, _size_, _virt_, _dma_)
+# endif
+
+# if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+#define DWC_DMA_ALLOC __DWC_DMA_ALLOC
+#define DWC_DMA_FREE __DWC_DMA_FREE
+# endif
+
+#else /* DWC_DEBUG_MEMORY */
+
+extern void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line);
+extern void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func, int line);
+extern void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line);
+extern void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
+ char const *func, int line);
+extern void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
+ char const *func, int line);
+extern void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr,
+ dwc_dma_t dma_addr, char const *func, int line);
+
+extern int dwc_memory_debug_start(void *mem_ctx);
+extern void dwc_memory_debug_stop(void);
+extern void dwc_memory_debug_report(void);
+
+#define DWC_ALLOC(_size_) dwc_alloc_debug(NULL, _size_, __func__, __LINE__)
+#define DWC_ALLOC_ATOMIC(_size_) dwc_alloc_atomic_debug(NULL, _size_, \
+ __func__, __LINE__)
+#define DWC_FREE(_addr_) dwc_free_debug(NULL, _addr_, __func__, __LINE__)
+
+# ifdef DWC_LINUX
+#define DWC_DMA_ALLOC(_size_,_dma_) dwc_dma_alloc_debug(NULL, _size_, \
+ _dma_, __func__, __LINE__)
+#define DWC_DMA_ALLOC_ATOMIC(_size_,_dma_) dwc_dma_alloc_atomic_debug(NULL, _size_, \
+ _dma_, __func__, __LINE__)
+#define DWC_DMA_FREE(_size_,_virt_,_dma_) dwc_dma_free_debug(NULL, _size_, \
+ _virt_, _dma_, __func__, __LINE__)
+# endif
+
+# if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+#define DWC_DMA_ALLOC(_ctx_,_size_,_dma_) dwc_dma_alloc_debug(_ctx_, _size_, \
+ _dma_, __func__, __LINE__)
+#define DWC_DMA_FREE(_ctx_,_size_,_virt_,_dma_) dwc_dma_free_debug(_ctx_, _size_, \
+ _virt_, _dma_, __func__, __LINE__)
+# endif
+
+#endif /* DWC_DEBUG_MEMORY */
+
+#define dwc_alloc(_ctx_,_size_) DWC_ALLOC(_size_)
+#define dwc_alloc_atomic(_ctx_,_size_) DWC_ALLOC_ATOMIC(_size_)
+#define dwc_free(_ctx_,_addr_) DWC_FREE(_addr_)
+
+#ifdef DWC_LINUX
+/* Linux doesn't need any extra parameters for DMA buffer allocation, so we
+ * just throw away the DMA context parameter.
+ */
+#define dwc_dma_alloc(_ctx_,_size_,_dma_) DWC_DMA_ALLOC(_size_, _dma_)
+#define dwc_dma_alloc_atomic(_ctx_,_size_,_dma_) DWC_DMA_ALLOC_ATOMIC(_size_, _dma_)
+#define dwc_dma_free(_ctx_,_size_,_virt_,_dma_) DWC_DMA_FREE(_size_, _virt_, _dma_)
+#endif
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+/** BSD needs several extra parameters for DMA buffer allocation, so we pass
+ * them in using the DMA context parameter.
+ */
+#define dwc_dma_alloc DWC_DMA_ALLOC
+#define dwc_dma_free DWC_DMA_FREE
+#endif
+
+
+/** @name Memory and String Processing */
+
+/** memset() clone */
+extern void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size);
+#define dwc_memset DWC_MEMSET
+
+/** memcpy() clone */
+extern void *DWC_MEMCPY(void *dest, void const *src, uint32_t size);
+#define dwc_memcpy DWC_MEMCPY
+
+/** memmove() clone */
+extern void *DWC_MEMMOVE(void *dest, void *src, uint32_t size);
+#define dwc_memmove DWC_MEMMOVE
+
+/** memcmp() clone */
+extern int DWC_MEMCMP(void *m1, void *m2, uint32_t size);
+#define dwc_memcmp DWC_MEMCMP
+
+/** strcmp() clone */
+extern int DWC_STRCMP(void *s1, void *s2);
+#define dwc_strcmp DWC_STRCMP
+
+/** strncmp() clone */
+extern int DWC_STRNCMP(void *s1, void *s2, uint32_t size);
+#define dwc_strncmp DWC_STRNCMP
+
+/** strlen() clone, for NULL terminated ASCII strings */
+extern int DWC_STRLEN(char const *str);
+#define dwc_strlen DWC_STRLEN
+
+/** strcpy() clone, for NULL terminated ASCII strings */
+extern char *DWC_STRCPY(char *to, const char *from);
+#define dwc_strcpy DWC_STRCPY
+
+/** strdup() clone. If you wish to use memory allocation debugging, this
+ * implementation of strdup should use the DWC_* memory routines instead of
+ * calling a predefined strdup. Otherwise the memory allocated by this routine
+ * will not be seen by the debugging routines. */
+extern char *DWC_STRDUP(char const *str);
+#define dwc_strdup(_ctx_,_str_) DWC_STRDUP(_str_)
+
+/** NOT an atoi() clone. Read the description carefully. Returns an integer
+ * converted from the string str in base 10 unless the string begins with a "0x"
+ * in which case it is base 16. String must be a NULL terminated sequence of
+ * ASCII characters and may optionally begin with whitespace, a + or -, and a
+ * "0x" prefix if base 16. The remaining characters must be valid digits for
+ * the number and end with a NULL character. If any invalid characters are
+ * encountered or it returns with a negative error code and the results of the
+ * conversion are undefined. On sucess it returns 0. Overflow conditions are
+ * undefined. An example implementation using atoi() can be referenced from the
+ * Linux implementation. */
+extern int DWC_ATOI(const char *str, int32_t *value);
+#define dwc_atoi DWC_ATOI
+
+/** Same as above but for unsigned. */
+extern int DWC_ATOUI(const char *str, uint32_t *value);
+#define dwc_atoui DWC_ATOUI
+
+#ifdef DWC_UTFLIB
+/** This routine returns a UTF16LE unicode encoded string from a UTF8 string. */
+extern int DWC_UTF8_TO_UTF16LE(uint8_t const *utf8string, uint16_t *utf16string, unsigned len);
+#define dwc_utf8_to_utf16le DWC_UTF8_TO_UTF16LE
+#endif
+
+
+/** @name Wait queues
+ *
+ * Wait queues provide a means of synchronizing between threads or processes. A
+ * process can block on a waitq if some condition is not true, waiting for it to
+ * become true. When the waitq is triggered all waiting process will get
+ * unblocked and the condition will be check again. Waitqs should be triggered
+ * every time a condition can potentially change.*/
+struct dwc_waitq;
+
+/** Type for a waitq */
+typedef struct dwc_waitq dwc_waitq_t;
+
+/** The type of waitq condition callback function. This is called every time
+ * condition is evaluated. */
+typedef int (*dwc_waitq_condition_t)(void *data);
+
+/** Allocate a waitq */
+extern dwc_waitq_t *DWC_WAITQ_ALLOC(void);
+#define dwc_waitq_alloc(_ctx_) DWC_WAITQ_ALLOC()
+
+/** Free a waitq */
+extern void DWC_WAITQ_FREE(dwc_waitq_t *wq);
+#define dwc_waitq_free DWC_WAITQ_FREE
+
+/** Check the condition and if it is false, block on the waitq. When unblocked, check the
+ * condition again. The function returns when the condition becomes true. The return value
+ * is 0 on condition true, DWC_WAITQ_ABORTED on abort or killed, or DWC_WAITQ_UNKNOWN on error. */
+extern int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data);
+#define dwc_waitq_wait DWC_WAITQ_WAIT
+
+/** Check the condition and if it is false, block on the waitq. When unblocked,
+ * check the condition again. The function returns when the condition become
+ * true or the timeout has passed. The return value is 0 on condition true or
+ * DWC_TIMED_OUT on timeout, or DWC_WAITQ_ABORTED, or DWC_WAITQ_UNKNOWN on
+ * error. */
+extern int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
+ void *data, int32_t msecs);
+#define dwc_waitq_wait_timeout DWC_WAITQ_WAIT_TIMEOUT
+
+/** Trigger a waitq, unblocking all processes. This should be called whenever a condition
+ * has potentially changed. */
+extern void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq);
+#define dwc_waitq_trigger DWC_WAITQ_TRIGGER
+
+/** Unblock all processes waiting on the waitq with an ABORTED result. */
+extern void DWC_WAITQ_ABORT(dwc_waitq_t *wq);
+#define dwc_waitq_abort DWC_WAITQ_ABORT
+
+
+/** @name Threads
+ *
+ * A thread must be explicitly stopped. It must check DWC_THREAD_SHOULD_STOP
+ * whenever it is woken up, and then return. The DWC_THREAD_STOP function
+ * returns the value from the thread.
+ */
+
+struct dwc_thread;
+
+/** Type for a thread */
+typedef struct dwc_thread dwc_thread_t;
+
+/** The thread function */
+typedef int (*dwc_thread_function_t)(void *data);
+
+/** Create a thread and start it running the thread_function. Returns a handle
+ * to the thread */
+extern dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data);
+#define dwc_thread_run(_ctx_,_func_,_name_,_data_) DWC_THREAD_RUN(_func_, _name_, _data_)
+
+/** Stops a thread. Return the value returned by the thread. Or will return
+ * DWC_ABORT if the thread never started. */
+extern int DWC_THREAD_STOP(dwc_thread_t *thread);
+#define dwc_thread_stop DWC_THREAD_STOP
+
+/** Signifies to the thread that it must stop. */
+#ifdef DWC_LINUX
+/* Linux doesn't need any parameters for kthread_should_stop() */
+extern dwc_bool_t DWC_THREAD_SHOULD_STOP(void);
+#define dwc_thread_should_stop(_thrd_) DWC_THREAD_SHOULD_STOP()
+
+/* No thread_exit function in Linux */
+#define dwc_thread_exit(_thrd_)
+#endif
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+/** BSD needs the thread pointer for kthread_suspend_check() */
+extern dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread);
+#define dwc_thread_should_stop DWC_THREAD_SHOULD_STOP
+
+/** The thread must call this to exit. */
+extern void DWC_THREAD_EXIT(dwc_thread_t *thread);
+#define dwc_thread_exit DWC_THREAD_EXIT
+#endif
+
+
+/** @name Work queues
+ *
+ * Workqs are used to queue a callback function to be called at some later time,
+ * in another thread. */
+struct dwc_workq;
+
+/** Type for a workq */
+typedef struct dwc_workq dwc_workq_t;
+
+/** The type of the callback function to be called. */
+typedef void (*dwc_work_callback_t)(void *data);
+
+/** Allocate a workq */
+extern dwc_workq_t *DWC_WORKQ_ALLOC(char *name);
+#define dwc_workq_alloc(_ctx_,_name_) DWC_WORKQ_ALLOC(_name_)
+
+/** Free a workq. All work must be completed before being freed. */
+extern void DWC_WORKQ_FREE(dwc_workq_t *workq);
+#define dwc_workq_free DWC_WORKQ_FREE
+
+/** Schedule a callback on the workq, passing in data. The function will be
+ * scheduled at some later time. */
+extern void DWC_WORKQ_SCHEDULE(dwc_workq_t *workq, dwc_work_callback_t cb,
+ void *data, char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format(printf, 4, 5)));
+#else
+ ;
+#endif
+#define dwc_workq_schedule DWC_WORKQ_SCHEDULE
+
+/** Schedule a callback on the workq, that will be called until at least
+ * given number miliseconds have passed. */
+extern void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *workq, dwc_work_callback_t cb,
+ void *data, uint32_t time, char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format(printf, 5, 6)));
+#else
+ ;
+#endif
+#define dwc_workq_schedule_delayed DWC_WORKQ_SCHEDULE_DELAYED
+
+/** The number of processes in the workq */
+extern int DWC_WORKQ_PENDING(dwc_workq_t *workq);
+#define dwc_workq_pending DWC_WORKQ_PENDING
+
+/** Blocks until all the work in the workq is complete or timed out. Returns <
+ * 0 on timeout. */
+extern int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout);
+#define dwc_workq_wait_work_done DWC_WORKQ_WAIT_WORK_DONE
+
+
+/** @name Tasklets
+ *
+ */
+struct dwc_tasklet;
+
+/** Type for a tasklet */
+typedef struct dwc_tasklet dwc_tasklet_t;
+
+/** The type of the callback function to be called */
+typedef void (*dwc_tasklet_callback_t)(void *data);
+
+/** Allocates a tasklet */
+extern dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data);
+#define dwc_task_alloc(_ctx_,_name_,_cb_,_data_) DWC_TASK_ALLOC(_name_, _cb_, _data_)
+
+/** Frees a tasklet */
+extern void DWC_TASK_FREE(dwc_tasklet_t *task);
+#define dwc_task_free DWC_TASK_FREE
+
+/** Schedules a tasklet to run */
+extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task);
+#define dwc_task_schedule DWC_TASK_SCHEDULE
+
+
+/** @name Timer
+ *
+ * Callbacks must be small and atomic.
+ */
+struct dwc_timer;
+
+/** Type for a timer */
+typedef struct dwc_timer dwc_timer_t;
+
+/** The type of the callback function to be called */
+typedef void (*dwc_timer_callback_t)(void *data);
+
+/** Allocates a timer */
+extern dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data);
+#define dwc_timer_alloc(_ctx_,_name_,_cb_,_data_) DWC_TIMER_ALLOC(_name_,_cb_,_data_)
+
+/** Frees a timer */
+extern void DWC_TIMER_FREE(dwc_timer_t *timer);
+#define dwc_timer_free DWC_TIMER_FREE
+
+/** Schedules the timer to run at time ms from now. And will repeat at every
+ * repeat_interval msec therafter
+ *
+ * Modifies a timer that is still awaiting execution to a new expiration time.
+ * The mod_time is added to the old time. */
+extern void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time);
+#define dwc_timer_schedule DWC_TIMER_SCHEDULE
+
+/** Disables the timer from execution. */
+extern void DWC_TIMER_CANCEL(dwc_timer_t *timer);
+#define dwc_timer_cancel DWC_TIMER_CANCEL
+
+
+/** @name Spinlocks
+ *
+ * These locks are used when the work between the lock/unlock is atomic and
+ * short. Interrupts are also disabled during the lock/unlock and thus they are
+ * suitable to lock between interrupt/non-interrupt context. They also lock
+ * between processes if you have multiple CPUs or Preemption. If you don't have
+ * multiple CPUS or Preemption, then the you can simply implement the
+ * DWC_SPINLOCK and DWC_SPINUNLOCK to disable and enable interrupts. Because
+ * the work between the lock/unlock is atomic, the process context will never
+ * change, and so you never have to lock between processes. */
+
+struct dwc_spinlock;
+
+/** Type for a spinlock */
+typedef struct dwc_spinlock dwc_spinlock_t;
+
+/** Type for the 'flags' argument to spinlock funtions */
+typedef unsigned long dwc_irqflags_t;
+
+/** Returns an initialized lock variable. This function should allocate and
+ * initialize the OS-specific data structure used for locking. This data
+ * structure is to be used for the DWC_LOCK and DWC_UNLOCK functions and should
+ * be freed by the DWC_FREE_LOCK when it is no longer used. */
+extern dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void);
+#define dwc_spinlock_alloc(_ctx_) DWC_SPINLOCK_ALLOC()
+
+/** Frees an initialized lock variable. */
+extern void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock);
+#define dwc_spinlock_free(_ctx_,_lock_) DWC_SPINLOCK_FREE(_lock_)
+
+/** Disables interrupts and blocks until it acquires the lock.
+ *
+ * @param lock Pointer to the spinlock.
+ * @param flags Unsigned long for irq flags storage.
+ */
+extern void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags);
+#define dwc_spinlock_irqsave DWC_SPINLOCK_IRQSAVE
+
+/** Re-enables the interrupt and releases the lock.
+ *
+ * @param lock Pointer to the spinlock.
+ * @param flags Unsigned long for irq flags storage. Must be the same as was
+ * passed into DWC_LOCK.
+ */
+extern void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags);
+#define dwc_spinunlock_irqrestore DWC_SPINUNLOCK_IRQRESTORE
+
+/** Blocks until it acquires the lock.
+ *
+ * @param lock Pointer to the spinlock.
+ */
+extern void DWC_SPINLOCK(dwc_spinlock_t *lock);
+#define dwc_spinlock DWC_SPINLOCK
+
+/** Releases the lock.
+ *
+ * @param lock Pointer to the spinlock.
+ */
+extern void DWC_SPINUNLOCK(dwc_spinlock_t *lock);
+#define dwc_spinunlock DWC_SPINUNLOCK
+
+
+/** @name Mutexes
+ *
+ * Unlike spinlocks Mutexes lock only between processes and the work between the
+ * lock/unlock CAN block, therefore it CANNOT be called from interrupt context.
+ */
+
+struct dwc_mutex;
+
+/** Type for a mutex */
+typedef struct dwc_mutex dwc_mutex_t;
+
+/* For Linux Mutex Debugging make it inline because the debugging routines use
+ * the symbol to determine recursive locking. This makes it falsely think
+ * recursive locking occurs. */
+#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)
+#define DWC_MUTEX_ALLOC_LINUX_DEBUG(__mutexp) ({ \
+ __mutexp = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mutex)); \
+ mutex_init((struct mutex *)__mutexp); \
+})
+#endif
+
+/** Allocate a mutex */
+extern dwc_mutex_t *DWC_MUTEX_ALLOC(void);
+#define dwc_mutex_alloc(_ctx_) DWC_MUTEX_ALLOC()
+
+/* For memory leak debugging when using Linux Mutex Debugging */
+#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)
+#define DWC_MUTEX_FREE(__mutexp) do { \
+ mutex_destroy((struct mutex *)__mutexp); \
+ DWC_FREE(__mutexp); \
+} while(0)
+#else
+/** Free a mutex */
+extern void DWC_MUTEX_FREE(dwc_mutex_t *mutex);
+#define dwc_mutex_free(_ctx_,_mutex_) DWC_MUTEX_FREE(_mutex_)
+#endif
+
+/** Lock a mutex */
+extern void DWC_MUTEX_LOCK(dwc_mutex_t *mutex);
+#define dwc_mutex_lock DWC_MUTEX_LOCK
+
+/** Non-blocking lock returns 1 on successful lock. */
+extern int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex);
+#define dwc_mutex_trylock DWC_MUTEX_TRYLOCK
+
+/** Unlock a mutex */
+extern void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex);
+#define dwc_mutex_unlock DWC_MUTEX_UNLOCK
+
+
+/** @name Time */
+
+/** Microsecond delay.
+ *
+ * @param usecs Microseconds to delay.
+ */
+extern void DWC_UDELAY(uint32_t usecs);
+#define dwc_udelay DWC_UDELAY
+
+/** Millisecond delay.
+ *
+ * @param msecs Milliseconds to delay.
+ */
+extern void DWC_MDELAY(uint32_t msecs);
+#define dwc_mdelay DWC_MDELAY
+
+/** Non-busy waiting.
+ * Sleeps for specified number of milliseconds.
+ *
+ * @param msecs Milliseconds to sleep.
+ */
+extern void DWC_MSLEEP(uint32_t msecs);
+#define dwc_msleep DWC_MSLEEP
+
+/**
+ * Returns number of milliseconds since boot.
+ */
+extern uint32_t DWC_TIME(void);
+#define dwc_time DWC_TIME
+
+
+
+
+/* @mainpage DWC Portability and Common Library
+ *
+ * This is the documentation for the DWC Portability and Common Library.
+ *
+ * @section intro Introduction
+ *
+ * The DWC Portability library consists of wrapper calls and data structures to
+ * all low-level functions which are typically provided by the OS. The WUDEV
+ * driver uses only these functions. In order to port the WUDEV driver, only
+ * the functions in this library need to be re-implemented, with the same
+ * behavior as documented here.
+ *
+ * The Common library consists of higher level functions, which rely only on
+ * calling the functions from the DWC Portability library. These common
+ * routines are shared across modules. Some of the common libraries need to be
+ * used directly by the driver programmer when porting WUDEV. Such as the
+ * parameter and notification libraries.
+ *
+ * @section low Portability Library OS Wrapper Functions
+ *
+ * Any function starting with DWC and in all CAPS is a low-level OS-wrapper that
+ * needs to be implemented when porting, for example DWC_MUTEX_ALLOC(). All of
+ * these functions are included in the dwc_os.h file.
+ *
+ * There are many functions here covering a wide array of OS services. Please
+ * see dwc_os.h for details, and implementation notes for each function.
+ *
+ * @section common Common Library Functions
+ *
+ * Any function starting with dwc and in all lowercase is a common library
+ * routine. These functions have a portable implementation and do not need to
+ * be reimplemented when porting. The common routines can be used by any
+ * driver, and some must be used by the end user to control the drivers. For
+ * example, you must use the Parameter common library in order to set the
+ * parameters in the WUDEV module.
+ *
+ * The common libraries consist of the following:
+ *
+ * - Connection Contexts - Used internally and can be used by end-user. See dwc_cc.h
+ * - Parameters - Used internally and can be used by end-user. See dwc_params.h
+ * - Notifications - Used internally and can be used by end-user. See dwc_notifier.h
+ * - Lists - Used internally and can be used by end-user. See dwc_list.h
+ * - Memory Debugging - Used internally and can be used by end-user. See dwc_os.h
+ * - Modpow - Used internally only. See dwc_modpow.h
+ * - DH - Used internally only. See dwc_dh.h
+ * - Crypto - Used internally only. See dwc_crypto.h
+ *
+ *
+ * @section prereq Prerequistes For dwc_os.h
+ * @subsection types Data Types
+ *
+ * The dwc_os.h file assumes that several low-level data types are pre defined for the
+ * compilation environment. These data types are:
+ *
+ * - uint8_t - unsigned 8-bit data type
+ * - int8_t - signed 8-bit data type
+ * - uint16_t - unsigned 16-bit data type
+ * - int16_t - signed 16-bit data type
+ * - uint32_t - unsigned 32-bit data type
+ * - int32_t - signed 32-bit data type
+ * - uint64_t - unsigned 64-bit data type
+ * - int64_t - signed 64-bit data type
+ *
+ * Ensure that these are defined before using dwc_os.h. The easiest way to do
+ * that is to modify the top of the file to include the appropriate header.
+ * This is already done for the Linux environment. If the DWC_LINUX macro is
+ * defined, the correct header will be added. A standard header <stdint.h> is
+ * also used for environments where standard C headers are available.
+ *
+ * @subsection stdarg Variable Arguments
+ *
+ * Variable arguments are provided by a standard C header <stdarg.h>. it is
+ * available in Both the Linux and ANSI C enviornment. An equivalent must be
+ * provided in your enviornment in order to use dwc_os.h with the debug and
+ * tracing message functionality.
+ *
+ * @subsection thread Threading
+ *
+ * WUDEV Core must be run on an operating system that provides for multiple
+ * threads/processes. Threading can be implemented in many ways, even in
+ * embedded systems without an operating system. At the bare minimum, the
+ * system should be able to start any number of processes at any time to handle
+ * special work. It need not be a pre-emptive system. Process context can
+ * change upon a call to a blocking function. The hardware interrupt context
+ * that calls the module's ISR() function must be differentiable from process
+ * context, even if your processes are impemented via a hardware interrupt.
+ * Further locking mechanism between process must exist (or be implemented), and
+ * process context must have a way to disable interrupts for a period of time to
+ * lock them out. If all of this exists, the functions in dwc_os.h related to
+ * threading should be able to be implemented with the defined behavior.
+ *
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_OS_H_ */
diff --git a/drivers/usb/dwc_otg/dwc_otg_adp.c b/drivers/usb/dwc_otg/dwc_otg_adp.c
new file mode 100644
index 0000000..0877472
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_adp.c
@@ -0,0 +1,854 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_adp.c $
+ * $Revision: #12 $
+ * $Date: 2011/10/26 $
+ * $Change: 1873028 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#include "dwc_os.h"
+#include "dwc_otg_regs.h"
+#include "dwc_otg_cil.h"
+#include "dwc_otg_adp.h"
+
+/** @file
+ *
+ * This file contains the most of the Attach Detect Protocol implementation for
+ * the driver to support OTG Rev2.0.
+ *
+ */
+
+void dwc_otg_adp_write_reg(dwc_otg_core_if_t * core_if, uint32_t value)
+{
+ adpctl_data_t adpctl;
+
+ adpctl.d32 = value;
+ adpctl.b.ar = 0x2;
+
+ DWC_WRITE_REG32(&core_if->core_global_regs->adpctl, adpctl.d32);
+
+ while (adpctl.b.ar) {
+ adpctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->adpctl);
+ }
+
+}
+
+/**
+ * Function is called to read ADP registers
+ */
+uint32_t dwc_otg_adp_read_reg(dwc_otg_core_if_t * core_if)
+{
+ adpctl_data_t adpctl;
+
+ adpctl.d32 = 0;
+ adpctl.b.ar = 0x1;
+
+ DWC_WRITE_REG32(&core_if->core_global_regs->adpctl, adpctl.d32);
+
+ while (adpctl.b.ar) {
+ adpctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->adpctl);
+ }
+
+ return adpctl.d32;
+}
+
+/**
+ * Function is called to read ADPCTL register and filter Write-clear bits
+ */
+uint32_t dwc_otg_adp_read_reg_filter(dwc_otg_core_if_t * core_if)
+{
+ adpctl_data_t adpctl;
+
+ adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+ adpctl.b.adp_tmout_int = 0;
+ adpctl.b.adp_prb_int = 0;
+ adpctl.b.adp_tmout_int = 0;
+
+ return adpctl.d32;
+}
+
+/**
+ * Function is called to write ADP registers
+ */
+void dwc_otg_adp_modify_reg(dwc_otg_core_if_t * core_if, uint32_t clr,
+ uint32_t set)
+{
+ dwc_otg_adp_write_reg(core_if,
+ (dwc_otg_adp_read_reg(core_if) & (~clr)) | set);
+}
+
+static void adp_sense_timeout(void *ptr)
+{
+ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
+ core_if->adp.sense_timer_started = 0;
+ DWC_PRINTF("ADP SENSE TIMEOUT\n");
+ if (core_if->adp_enable) {
+ dwc_otg_adp_sense_stop(core_if);
+ dwc_otg_adp_probe_start(core_if);
+ }
+}
+
+/**
+ * This function is called when the ADP vbus timer expires. Timeout is 1.1s.
+ */
+static void adp_vbuson_timeout(void *ptr)
+{
+ gpwrdn_data_t gpwrdn;
+ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
+ hprt0_data_t hprt0 = {.d32 = 0 };
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ DWC_PRINTF("%s: 1.1 seconds expire after turning on VBUS\n",__FUNCTION__);
+ if (core_if) {
+ core_if->adp.vbuson_timer_started = 0;
+ /* Turn off vbus */
+ hprt0.b.prtpwr = 1;
+ DWC_MODIFY_REG32(core_if->host_if->hprt0, hprt0.d32, 0);
+ gpwrdn.d32 = 0;
+
+ /* Power off the core */
+ if (core_if->power_down == 2) {
+ /* Enable Wakeup Logic */
+// gpwrdn.b.wkupactiv = 1;
+ gpwrdn.b.pmuactv = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
+ gpwrdn.d32);
+
+ /* Suspend the Phy Clock */
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
+
+ /* Switch on VDD */
+// gpwrdn.b.wkupactiv = 1;
+ gpwrdn.b.pmuactv = 1;
+ gpwrdn.b.pwrdnrstn = 1;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
+ gpwrdn.d32);
+ } else {
+ /* Enable Power Down Logic */
+ gpwrdn.b.pmuintsel = 1;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ }
+
+ /* Power off the core */
+ if (core_if->power_down == 2) {
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn,
+ gpwrdn.d32, 0);
+ }
+
+ /* Unmask SRP detected interrupt from Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.srp_det_msk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+
+ dwc_otg_adp_probe_start(core_if);
+ dwc_otg_dump_global_registers(core_if);
+ dwc_otg_dump_host_registers(core_if);
+ }
+
+}
+
+/**
+ * Start the ADP Initial Probe timer to detect if Port Connected interrupt is
+ * not asserted within 1.1 seconds.
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+void dwc_otg_adp_vbuson_timer_start(dwc_otg_core_if_t * core_if)
+{
+ core_if->adp.vbuson_timer_started = 1;
+ if (core_if->adp.vbuson_timer)
+ {
+ DWC_PRINTF("SCHEDULING VBUSON TIMER\n");
+ /* 1.1 secs + 60ms necessary for cil_hcd_start*/
+ DWC_TIMER_SCHEDULE(core_if->adp.vbuson_timer, 1160);
+ } else {
+ DWC_WARN("VBUSON_TIMER = %p\n",core_if->adp.vbuson_timer);
+ }
+}
+
+#if 0
+/**
+ * Masks all DWC OTG core interrupts
+ *
+ */
+static void mask_all_interrupts(dwc_otg_core_if_t * core_if)
+{
+ int i;
+ gahbcfg_data_t ahbcfg = {.d32 = 0 };
+
+ /* Mask Host Interrupts */
+
+ /* Clear and disable HCINTs */
+ for (i = 0; i < core_if->core_params->host_channels; i++) {
+ DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcintmsk, 0);
+ DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcint, 0xFFFFFFFF);
+
+ }
+
+ /* Clear and disable HAINT */
+ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haintmsk, 0x0000);
+ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haint, 0xFFFFFFFF);
+
+ /* Mask Device Interrupts */
+ if (!core_if->multiproc_int_enable) {
+ /* Clear and disable IN Endpoint interrupts */
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->diepmsk, 0);
+ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->
+ diepint, 0xFFFFFFFF);
+ }
+
+ /* Clear and disable OUT Endpoint interrupts */
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->doepmsk, 0);
+ for (i = 0; i <= core_if->dev_if->num_out_eps; i++) {
+ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->
+ doepint, 0xFFFFFFFF);
+ }
+
+ /* Clear and disable DAINT */
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daint,
+ 0xFFFFFFFF);
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daintmsk, 0);
+ } else {
+ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
+ diepeachintmsk[i], 0);
+ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->
+ diepint, 0xFFFFFFFF);
+ }
+
+ for (i = 0; i < core_if->dev_if->num_out_eps; ++i) {
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
+ doepeachintmsk[i], 0);
+ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->
+ doepint, 0xFFFFFFFF);
+ }
+
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachintmsk,
+ 0);
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachint,
+ 0xFFFFFFFF);
+
+ }
+
+ /* Disable interrupts */
+ ahbcfg.b.glblintrmsk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0);
+
+ /* Disable all interrupts. */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0);
+
+ /* Clear any pending interrupts */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+ /* Clear any pending OTG Interrupts */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, 0xFFFFFFFF);
+}
+
+/**
+ * Unmask Port Connection Detected interrupt
+ *
+ */
+static void unmask_conn_det_intr(dwc_otg_core_if_t * core_if)
+{
+ gintmsk_data_t gintmsk = {.d32 = 0,.b.portintr = 1 };
+
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32);
+}
+#endif
+
+/**
+ * Starts the ADP Probing
+ *
+ * @param core_if the pointer to core_if structure.
+ */
+uint32_t dwc_otg_adp_probe_start(dwc_otg_core_if_t * core_if)
+{
+
+ adpctl_data_t adpctl = {.d32 = 0};
+ gpwrdn_data_t gpwrdn;
+#if 0
+ adpctl_data_t adpctl_int = {.d32 = 0, .b.adp_prb_int = 1,
+ .b.adp_sns_int = 1, b.adp_tmout_int};
+#endif
+ dwc_otg_disable_global_interrupts(core_if);
+ DWC_PRINTF("ADP Probe Start\n");
+ core_if->adp.probe_enabled = 1;
+
+ adpctl.b.adpres = 1;
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+ while (adpctl.b.adpres) {
+ adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+ }
+
+ adpctl.d32 = 0;
+ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+
+ /* In Host mode unmask SRP detected interrupt */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.sts_chngint_msk = 1;
+ if (!gpwrdn.b.idsts) {
+ gpwrdn.b.srp_det_msk = 1;
+ }
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+
+ adpctl.b.adp_tmout_int_msk = 1;
+ adpctl.b.adp_prb_int_msk = 1;
+ adpctl.b.prb_dschg = 1;
+ adpctl.b.prb_delta = 1;
+ adpctl.b.prb_per = 1;
+ adpctl.b.adpen = 1;
+ adpctl.b.enaprb = 1;
+
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+ DWC_PRINTF("ADP Probe Finish\n");
+ return 0;
+}
+
+/**
+ * Starts the ADP Sense timer to detect if ADP Sense interrupt is not asserted
+ * within 3 seconds.
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+void dwc_otg_adp_sense_timer_start(dwc_otg_core_if_t * core_if)
+{
+ core_if->adp.sense_timer_started = 1;
+ DWC_TIMER_SCHEDULE(core_if->adp.sense_timer, 3000 /* 3 secs */ );
+}
+
+/**
+ * Starts the ADP Sense
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+uint32_t dwc_otg_adp_sense_start(dwc_otg_core_if_t * core_if)
+{
+ adpctl_data_t adpctl;
+
+ DWC_PRINTF("ADP Sense Start\n");
+
+ /* Unmask ADP sense interrupt and mask all other from the core */
+ adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if);
+ adpctl.b.adp_sns_int_msk = 1;
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+ dwc_otg_disable_global_interrupts(core_if); // vahrama
+
+ /* Set ADP reset bit*/
+ adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if);
+ adpctl.b.adpres = 1;
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+ while (adpctl.b.adpres) {
+ adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+ }
+
+ adpctl.b.adpres = 0;
+ adpctl.b.adpen = 1;
+ adpctl.b.enasns = 1;
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+ dwc_otg_adp_sense_timer_start(core_if);
+
+ return 0;
+}
+
+/**
+ * Stops the ADP Probing
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+uint32_t dwc_otg_adp_probe_stop(dwc_otg_core_if_t * core_if)
+{
+
+ adpctl_data_t adpctl;
+ DWC_PRINTF("Stop ADP probe\n");
+ core_if->adp.probe_enabled = 0;
+ core_if->adp.probe_counter = 0;
+ adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+
+ adpctl.b.adpen = 0;
+ adpctl.b.adp_prb_int = 1;
+ adpctl.b.adp_tmout_int = 1;
+ adpctl.b.adp_sns_int = 1;
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+ return 0;
+}
+
+/**
+ * Stops the ADP Sensing
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+uint32_t dwc_otg_adp_sense_stop(dwc_otg_core_if_t * core_if)
+{
+ adpctl_data_t adpctl;
+
+ core_if->adp.sense_enabled = 0;
+
+ adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if);
+ adpctl.b.enasns = 0;
+ adpctl.b.adp_sns_int = 1;
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+ return 0;
+}
+
+/**
+ * Called to turn on the VBUS after initial ADP probe in host mode.
+ * If port power was already enabled in cil_hcd_start function then
+ * only schedule a timer.
+ *
+ * @param core_if the pointer to core_if structure.
+ */
+void dwc_otg_adp_turnon_vbus(dwc_otg_core_if_t * core_if)
+{
+ hprt0_data_t hprt0 = {.d32 = 0 };
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ DWC_PRINTF("Turn on VBUS for 1.1s, port power is %d\n", hprt0.b.prtpwr);
+
+ if (hprt0.b.prtpwr == 0) {
+ hprt0.b.prtpwr = 1;
+ //DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ }
+
+ dwc_otg_adp_vbuson_timer_start(core_if);
+}
+
+/**
+ * Called right after driver is loaded
+ * to perform initial actions for ADP
+ *
+ * @param core_if the pointer to core_if structure.
+ * @param is_host - flag for current mode of operation either from GINTSTS or GPWRDN
+ */
+void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host)
+{
+ gpwrdn_data_t gpwrdn;
+
+ DWC_PRINTF("ADP Initial Start\n");
+ core_if->adp.adp_started = 1;
+
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+ dwc_otg_disable_global_interrupts(core_if);
+ if (is_host) {
+ DWC_PRINTF("HOST MODE\n");
+ /* Enable Power Down Logic Interrupt*/
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ /* Initialize first ADP probe to obtain Ramp Time value */
+ core_if->adp.initial_probe = 1;
+ dwc_otg_adp_probe_start(core_if);
+ } else {
+ gotgctl_data_t gotgctl;
+ gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+ DWC_PRINTF("DEVICE MODE\n");
+ if (gotgctl.b.bsesvld == 0) {
+ /* Enable Power Down Logic Interrupt*/
+ gpwrdn.d32 = 0;
+ DWC_PRINTF("VBUS is not valid - start ADP probe\n");
+ gpwrdn.b.pmuintsel = 1;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ core_if->adp.initial_probe = 1;
+ dwc_otg_adp_probe_start(core_if);
+ } else {
+ DWC_PRINTF("VBUS is valid - initialize core as a Device\n");
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+ dwc_otg_dump_global_registers(core_if);
+ dwc_otg_dump_dev_registers(core_if);
+ }
+ }
+}
+
+void dwc_otg_adp_init(dwc_otg_core_if_t * core_if)
+{
+ core_if->adp.adp_started = 0;
+ core_if->adp.initial_probe = 0;
+ core_if->adp.probe_timer_values[0] = -1;
+ core_if->adp.probe_timer_values[1] = -1;
+ core_if->adp.probe_enabled = 0;
+ core_if->adp.sense_enabled = 0;
+ core_if->adp.sense_timer_started = 0;
+ core_if->adp.vbuson_timer_started = 0;
+ core_if->adp.probe_counter = 0;
+ core_if->adp.gpwrdn = 0;
+ core_if->adp.attached = DWC_OTG_ADP_UNKOWN;
+ /* Initialize timers */
+ core_if->adp.sense_timer =
+ DWC_TIMER_ALLOC("ADP SENSE TIMER", adp_sense_timeout, core_if);
+ core_if->adp.vbuson_timer =
+ DWC_TIMER_ALLOC("ADP VBUS ON TIMER", adp_vbuson_timeout, core_if);
+ if (!core_if->adp.sense_timer || !core_if->adp.vbuson_timer)
+ {
+ DWC_ERROR("Could not allocate memory for ADP timers\n");
+ }
+}
+
+void dwc_otg_adp_remove(dwc_otg_core_if_t * core_if)
+{
+ gpwrdn_data_t gpwrdn = { .d32 = 0 };
+ gpwrdn.b.pmuintsel = 1;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ if (core_if->adp.probe_enabled)
+ dwc_otg_adp_probe_stop(core_if);
+ if (core_if->adp.sense_enabled)
+ dwc_otg_adp_sense_stop(core_if);
+ if (core_if->adp.sense_timer_started)
+ DWC_TIMER_CANCEL(core_if->adp.sense_timer);
+ if (core_if->adp.vbuson_timer_started)
+ DWC_TIMER_CANCEL(core_if->adp.vbuson_timer);
+ DWC_TIMER_FREE(core_if->adp.sense_timer);
+ DWC_TIMER_FREE(core_if->adp.vbuson_timer);
+}
+
+/////////////////////////////////////////////////////////////////////
+////////////// ADP Interrupt Handlers ///////////////////////////////
+/////////////////////////////////////////////////////////////////////
+/**
+ * This function sets Ramp Timer values
+ */
+static uint32_t set_timer_value(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ if (core_if->adp.probe_timer_values[0] == -1) {
+ core_if->adp.probe_timer_values[0] = val;
+ core_if->adp.probe_timer_values[1] = -1;
+ return 1;
+ } else {
+ core_if->adp.probe_timer_values[1] =
+ core_if->adp.probe_timer_values[0];
+ core_if->adp.probe_timer_values[0] = val;
+ return 0;
+ }
+}
+
+/**
+ * This function compares Ramp Timer values
+ */
+static uint32_t compare_timer_values(dwc_otg_core_if_t * core_if)
+{
+ uint32_t diff;
+ if (core_if->adp.probe_timer_values[0]>=core_if->adp.probe_timer_values[1])
+ diff = core_if->adp.probe_timer_values[0]-core_if->adp.probe_timer_values[1];
+ else
+ diff = core_if->adp.probe_timer_values[1]-core_if->adp.probe_timer_values[0];
+ if(diff < 2) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/**
+ * This function handles ADP Probe Interrupts
+ */
+static int32_t dwc_otg_adp_handle_prb_intr(dwc_otg_core_if_t * core_if,
+ uint32_t val)
+{
+ adpctl_data_t adpctl = {.d32 = 0 };
+ gpwrdn_data_t gpwrdn, temp;
+ adpctl.d32 = val;
+
+ temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ core_if->adp.probe_counter++;
+ core_if->adp.gpwrdn = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ if (adpctl.b.rtim == 0 && !temp.b.idsts){
+ DWC_PRINTF("RTIM value is 0\n");
+ goto exit;
+ }
+ if (set_timer_value(core_if, adpctl.b.rtim) &&
+ core_if->adp.initial_probe) {
+ core_if->adp.initial_probe = 0;
+ dwc_otg_adp_probe_stop(core_if);
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+ /* check which value is for device mode and which for Host mode */
+ if (!temp.b.idsts) { /* considered host mode value is 0 */
+ /*
+ * Turn on VBUS after initial ADP probe.
+ */
+ core_if->op_state = A_HOST;
+ dwc_otg_enable_global_interrupts(core_if);
+ DWC_SPINUNLOCK(core_if->lock);
+ cil_hcd_start(core_if);
+ dwc_otg_adp_turnon_vbus(core_if);
+ DWC_SPINLOCK(core_if->lock);
+ } else {
+ /*
+ * Initiate SRP after initial ADP probe.
+ */
+ dwc_otg_enable_global_interrupts(core_if);
+ dwc_otg_initiate_srp(core_if);
+ }
+ } else if (core_if->adp.probe_counter > 2){
+ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ if (compare_timer_values(core_if)) {
+ DWC_PRINTF("Difference in timer values !!! \n");
+// core_if->adp.attached = DWC_OTG_ADP_ATTACHED;
+ dwc_otg_adp_probe_stop(core_if);
+
+ /* Power on the core */
+ if (core_if->power_down == 2) {
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ }
+
+ /* check which value is for device mode and which for Host mode */
+ if (!temp.b.idsts) { /* considered host mode value is 0 */
+ /* Disable Interrupt from Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, gpwrdn.d32, 0);
+
+ /*
+ * Initialize the Core for Host mode.
+ */
+ core_if->op_state = A_HOST;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_hcd_start(core_if);
+ } else {
+ gotgctl_data_t gotgctl;
+ /* Mask SRP detected interrupt from Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.srp_det_msk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, gpwrdn.d32, 0);
+
+ /* Disable Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, gpwrdn.d32, 0);
+
+ /*
+ * Initialize the Core for Device mode.
+ */
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+
+ gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+ if (!gotgctl.b.bsesvld) {
+ dwc_otg_initiate_srp(core_if);
+ }
+ }
+ }
+ if (core_if->power_down == 2) {
+ if (gpwrdn.b.bsessvld) {
+ /* Mask SRP detected interrupt from Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.srp_det_msk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Disable Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /*
+ * Initialize the Core for Device mode.
+ */
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+ }
+ }
+ }
+exit:
+ /* Clear interrupt */
+ adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+ adpctl.b.adp_prb_int = 1;
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+ return 0;
+}
+
+/**
+ * This function hadles ADP Sense Interrupt
+ */
+static int32_t dwc_otg_adp_handle_sns_intr(dwc_otg_core_if_t * core_if)
+{
+ adpctl_data_t adpctl;
+ /* Stop ADP Sense timer */
+ DWC_TIMER_CANCEL(core_if->adp.sense_timer);
+
+ /* Restart ADP Sense timer */
+ dwc_otg_adp_sense_timer_start(core_if);
+
+ /* Clear interrupt */
+ adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+ adpctl.b.adp_sns_int = 1;
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+ return 0;
+}
+
+/**
+ * This function handles ADP Probe Interrupts
+ */
+static int32_t dwc_otg_adp_handle_prb_tmout_intr(dwc_otg_core_if_t * core_if,
+ uint32_t val)
+{
+ adpctl_data_t adpctl = {.d32 = 0 };
+ adpctl.d32 = val;
+ set_timer_value(core_if, adpctl.b.rtim);
+
+ /* Clear interrupt */
+ adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+ adpctl.b.adp_tmout_int = 1;
+ dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+ return 0;
+}
+
+/**
+ * ADP Interrupt handler.
+ *
+ */
+int32_t dwc_otg_adp_handle_intr(dwc_otg_core_if_t * core_if)
+{
+ int retval = 0;
+ adpctl_data_t adpctl = {.d32 = 0};
+
+ adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+ DWC_PRINTF("ADPCTL = %08x\n",adpctl.d32);
+
+ if (adpctl.b.adp_sns_int & adpctl.b.adp_sns_int_msk) {
+ DWC_PRINTF("ADP Sense interrupt\n");
+ retval |= dwc_otg_adp_handle_sns_intr(core_if);
+ }
+ if (adpctl.b.adp_tmout_int & adpctl.b.adp_tmout_int_msk) {
+ DWC_PRINTF("ADP timeout interrupt\n");
+ retval |= dwc_otg_adp_handle_prb_tmout_intr(core_if, adpctl.d32);
+ }
+ if (adpctl.b.adp_prb_int & adpctl.b.adp_prb_int_msk) {
+ DWC_PRINTF("ADP Probe interrupt\n");
+ adpctl.b.adp_prb_int = 1;
+ retval |= dwc_otg_adp_handle_prb_intr(core_if, adpctl.d32);
+ }
+
+// dwc_otg_adp_modify_reg(core_if, adpctl.d32, 0);
+ //dwc_otg_adp_write_reg(core_if, adpctl.d32);
+ DWC_PRINTF("RETURN FROM ADP ISR\n");
+
+ return retval;
+}
+
+/**
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_adp_handle_srp_intr(dwc_otg_core_if_t * core_if)
+{
+
+#ifndef DWC_HOST_ONLY
+ hprt0_data_t hprt0;
+ gpwrdn_data_t gpwrdn;
+ DWC_DEBUGPL(DBG_ANY, "++ Power Down Logic Session Request Interrupt++\n");
+
+ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ /* check which value is for device mode and which for Host mode */
+ if (!gpwrdn.b.idsts) { /* considered host mode value is 0 */
+ DWC_PRINTF("SRP: Host mode\n");
+
+ if (core_if->adp_enable) {
+ dwc_otg_adp_probe_stop(core_if);
+
+ /* Power on the core */
+ if (core_if->power_down == 2) {
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ }
+
+ core_if->op_state = A_HOST;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_hcd_start(core_if);
+ }
+
+ /* Turn on the port power bit. */
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtpwr = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+ /* Start the Connection timer. So a message can be displayed
+ * if connect does not occur within 10 seconds. */
+ cil_hcd_session_start(core_if);
+ } else {
+ DWC_PRINTF("SRP: Device mode %s\n", __FUNCTION__);
+ if (core_if->adp_enable) {
+ dwc_otg_adp_probe_stop(core_if);
+
+ /* Power on the core */
+ if (core_if->power_down == 2) {
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ }
+
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 0;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
+ gpwrdn.d32);
+
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+ }
+ }
+#endif
+ return 1;
+}
diff --git a/drivers/usb/dwc_otg/dwc_otg_adp.h b/drivers/usb/dwc_otg/dwc_otg_adp.h
new file mode 100644
index 0000000..d8c3f85
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_adp.h
@@ -0,0 +1,80 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_adp.h $
+ * $Revision: #7 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871159 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#ifndef __DWC_OTG_ADP_H__
+#define __DWC_OTG_ADP_H__
+
+/**
+ * @file
+ *
+ * This file contains the Attach Detect Protocol interfaces and defines
+ * (functions) and structures for Linux.
+ *
+ */
+
+#define DWC_OTG_ADP_UNATTACHED 0
+#define DWC_OTG_ADP_ATTACHED 1
+#define DWC_OTG_ADP_UNKOWN 2
+
+typedef struct dwc_otg_adp {
+ uint32_t adp_started;
+ uint32_t initial_probe;
+ int32_t probe_timer_values[2];
+ uint32_t probe_enabled;
+ uint32_t sense_enabled;
+ dwc_timer_t *sense_timer;
+ uint32_t sense_timer_started;
+ dwc_timer_t *vbuson_timer;
+ uint32_t vbuson_timer_started;
+ uint32_t attached;
+ uint32_t probe_counter;
+ uint32_t gpwrdn;
+} dwc_otg_adp_t;
+
+/**
+ * Attach Detect Protocol functions
+ */
+
+extern void dwc_otg_adp_write_reg(dwc_otg_core_if_t * core_if, uint32_t value);
+extern uint32_t dwc_otg_adp_read_reg(dwc_otg_core_if_t * core_if);
+extern uint32_t dwc_otg_adp_probe_start(dwc_otg_core_if_t * core_if);
+extern uint32_t dwc_otg_adp_sense_start(dwc_otg_core_if_t * core_if);
+extern uint32_t dwc_otg_adp_probe_stop(dwc_otg_core_if_t * core_if);
+extern uint32_t dwc_otg_adp_sense_stop(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host);
+extern void dwc_otg_adp_init(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_adp_remove(dwc_otg_core_if_t * core_if);
+extern int32_t dwc_otg_adp_handle_intr(dwc_otg_core_if_t * core_if);
+extern int32_t dwc_otg_adp_handle_srp_intr(dwc_otg_core_if_t * core_if);
+
+#endif //__DWC_OTG_ADP_H__
diff --git a/drivers/usb/dwc_otg/dwc_otg_attr.c b/drivers/usb/dwc_otg/dwc_otg_attr.c
new file mode 100644
index 0000000..17b0c23
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_attr.c
@@ -0,0 +1,1433 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.c $
+ * $Revision: #44 $
+ * $Date: 2010/11/29 $
+ * $Change: 1636033 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ *
+ * The diagnostic interface will provide access to the controller for
+ * bringing up the hardware and testing. The Linux driver attributes
+ * feature will be used to provide the Linux Diagnostic
+ * Interface. These attributes are accessed through sysfs.
+ */
+
+/** @page "Linux Module Attributes"
+ *
+ * The Linux module attributes feature is used to provide the Linux
+ * Diagnostic Interface. These attributes are accessed through sysfs.
+ * The diagnostic interface will provide access to the controller for
+ * bringing up the hardware and testing.
+
+ The following table shows the attributes.
+ <table>
+ <tr>
+ <td><b> Name</b></td>
+ <td><b> Description</b></td>
+ <td><b> Access</b></td>
+ </tr>
+
+ <tr>
+ <td> mode </td>
+ <td> Returns the current mode: 0 for device mode, 1 for host mode</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hnpcapable </td>
+ <td> Gets or sets the "HNP-capable" bit in the Core USB Configuraton Register.
+ Read returns the current value.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> srpcapable </td>
+ <td> Gets or sets the "SRP-capable" bit in the Core USB Configuraton Register.
+ Read returns the current value.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> hsic_connect </td>
+ <td> Gets or sets the "HSIC-Connect" bit in the GLPMCFG Register.
+ Read returns the current value.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> inv_sel_hsic </td>
+ <td> Gets or sets the "Invert Select HSIC" bit in the GLPMFG Register.
+ Read returns the current value.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> hnp </td>
+ <td> Initiates the Host Negotiation Protocol. Read returns the status.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> srp </td>
+ <td> Initiates the Session Request Protocol. Read returns the status.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> buspower </td>
+ <td> Gets or sets the Power State of the bus (0 - Off or 1 - On)</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> bussuspend </td>
+ <td> Suspends the USB bus.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> busconnected </td>
+ <td> Gets the connection status of the bus</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> gotgctl </td>
+ <td> Gets or sets the Core Control Status Register.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> gusbcfg </td>
+ <td> Gets or sets the Core USB Configuration Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> grxfsiz </td>
+ <td> Gets or sets the Receive FIFO Size Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> gnptxfsiz </td>
+ <td> Gets or sets the non-periodic Transmit Size Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> gpvndctl </td>
+ <td> Gets or sets the PHY Vendor Control Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> ggpio </td>
+ <td> Gets the value in the lower 16-bits of the General Purpose IO Register
+ or sets the upper 16 bits.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> guid </td>
+ <td> Gets or sets the value of the User ID Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> gsnpsid </td>
+ <td> Gets the value of the Synopsys ID Regester</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> devspeed </td>
+ <td> Gets or sets the device speed setting in the DCFG register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> enumspeed </td>
+ <td> Gets the device enumeration Speed.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hptxfsiz </td>
+ <td> Gets the value of the Host Periodic Transmit FIFO</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hprt0 </td>
+ <td> Gets or sets the value in the Host Port Control and Status Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> regoffset </td>
+ <td> Sets the register offset for the next Register Access</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> regvalue </td>
+ <td> Gets or sets the value of the register at the offset in the regoffset attribute.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> remote_wakeup </td>
+ <td> On read, shows the status of Remote Wakeup. On write, initiates a remote
+ wakeup of the host. When bit 0 is 1 and Remote Wakeup is enabled, the Remote
+ Wakeup signalling bit in the Device Control Register is set for 1
+ milli-second.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> rem_wakeup_pwrdn </td>
+ <td> On read, shows the status core - hibernated or not. On write, initiates
+ a remote wakeup of the device from Hibernation. </td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> mode_ch_tim_en </td>
+ <td> This bit is used to enable or disable the host core to wait for 200 PHY
+ clock cycles at the end of Resume to change the opmode signal to the PHY to 00
+ after Suspend or LPM. </td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> fr_interval </td>
+ <td> On read, shows the value of HFIR Frame Interval. On write, dynamically
+ reload HFIR register during runtime. The application can write a value to this
+ register only after the Port Enable bit of the Host Port Control and Status
+ register (HPRT.PrtEnaPort) has been set </td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> disconnect_us </td>
+ <td> On read, shows the status of disconnect_device_us. On write, sets disconnect_us
+ which causes soft disconnect for 100us. Applicable only for device mode of operation.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> regdump </td>
+ <td> Dumps the contents of core registers.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> spramdump </td>
+ <td> Dumps the contents of core registers.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hcddump </td>
+ <td> Dumps the current HCD state.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hcd_frrem </td>
+ <td> Shows the average value of the Frame Remaining
+ field in the Host Frame Number/Frame Remaining register when an SOF interrupt
+ occurs. This can be used to determine the average interrupt latency. Also
+ shows the average Frame Remaining value for start_transfer and the "a" and
+ "b" sample points. The "a" and "b" sample points may be used during debugging
+ bto determine how long it takes to execute a section of the HCD code.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> rd_reg_test </td>
+ <td> Displays the time required to read the GNPTXFSIZ register many times
+ (the output shows the number of times the register is read).
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> wr_reg_test </td>
+ <td> Displays the time required to write the GNPTXFSIZ register many times
+ (the output shows the number of times the register is written).
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> lpm_response </td>
+ <td> Gets or sets lpm_response mode. Applicable only in device mode.
+ <td> Write</td>
+ </tr>
+
+ <tr>
+ <td> sleep_status </td>
+ <td> Shows sleep status of device.
+ <td> Read</td>
+ </tr>
+
+ </table>
+
+ Example usage:
+ To get the current mode:
+ cat /sys/devices/lm0/mode
+
+ To power down the USB:
+ echo 0 > /sys/devices/lm0/buspower
+ */
+
+#include "dwc_otg_os_dep.h"
+#include "dwc_os.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_attr.h"
+#include "dwc_otg_core_if.h"
+#include "dwc_otg_pcd_if.h"
+#include "dwc_otg_hcd_if.h"
+
+/*
+ * MACROs for defining sysfs attribute
+ */
+#ifdef LM_INTERFACE
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
+ uint32_t val; \
+ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+ return sprintf (buf, "%s = 0x%x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
+ uint32_t set = simple_strtoul(buf, NULL, 16); \
+ dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\
+ return count; \
+}
+
+#elif defined(PCI_INTERFACE)
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \
+ uint32_t val; \
+ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+ return sprintf (buf, "%s = 0x%x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \
+ uint32_t set = simple_strtoul(buf, NULL, 16); \
+ dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\
+ return count; \
+}
+#else
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev); \
+ uint32_t val; \
+ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+ return sprintf (buf, "%s = 0x%x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev); \
+ uint32_t set = simple_strtoul(buf, NULL, 16); \
+ dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\
+ return count; \
+}
+
+
+#endif
+
+/*
+ * MACROs for defining sysfs attribute for 32-bit registers
+ */
+#ifdef LM_INTERFACE
+#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
+ uint32_t val; \
+ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
+ uint32_t val = simple_strtoul(buf, NULL, 16); \
+ dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \
+ return count; \
+}
+#elif defined(PCI_INTERFACE)
+#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \
+ uint32_t val; \
+ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \
+ uint32_t val = simple_strtoul(buf, NULL, 16); \
+ dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \
+ return count; \
+}
+#else
+#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev); \
+ uint32_t val; \
+ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev); \
+ uint32_t val = simple_strtoul(buf, NULL, 16); \
+ dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \
+ return count; \
+}
+#endif
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_RW(_otg_attr_name_,_string_) \
+DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
+DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store);
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_RO(_otg_attr_name_,_string_) \
+DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL);
+
+#define DWC_OTG_DEVICE_ATTR_REG32_RW(_otg_attr_name_,_addr_,_string_) \
+DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
+DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store);
+
+#define DWC_OTG_DEVICE_ATTR_REG32_RO(_otg_attr_name_,_addr_,_string_) \
+DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL);
+
+/** @name Functions for Show/Store of Attributes */
+/**@{*/
+
+/**
+ * Show the register offset of the Register Access.
+ */
+static ssize_t regoffset_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ return snprintf(buf, sizeof("0xFFFFFFFF\n") + 1, "0x%08x\n",
+ otg_dev->os_dep.reg_offset);
+}
+
+/**
+ * Set the register offset for the next Register Access Read/Write
+ */
+static ssize_t regoffset_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ uint32_t offset = simple_strtoul(buf, NULL, 16);
+#ifdef LM_INTERFACE
+ if (offset < SZ_256K) {
+#elif defined(PCI_INTERFACE)
+ if (offset < 0x00040000) {
+#else
+ if (offset < 0x00040000) {
+#endif
+ otg_dev->os_dep.reg_offset = offset;
+ } else {
+ dev_err(_dev, "invalid offset\n");
+ }
+
+ return count;
+}
+
+DEVICE_ATTR(regoffset, S_IRUGO | S_IWUSR, regoffset_show, regoffset_store);
+
+/**
+ * Show the value of the register at the offset in the reg_offset
+ * attribute.
+ */
+static ssize_t regvalue_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ uint32_t val;
+ volatile uint32_t *addr;
+
+ if (otg_dev->os_dep.reg_offset != 0xFFFFFFFF && 0 != otg_dev->os_dep.base) {
+ /* Calculate the address */
+ addr = (uint32_t *) (otg_dev->os_dep.reg_offset +
+ (uint8_t *) otg_dev->os_dep.base);
+ val = DWC_READ_REG32(addr);
+ return snprintf(buf,
+ sizeof("Reg@0xFFFFFFFF = 0xFFFFFFFF\n") + 1,
+ "Reg@0x%06x = 0x%08x\n", otg_dev->os_dep.reg_offset,
+ val);
+ } else {
+ dev_err(_dev, "Invalid offset (0x%0x)\n", otg_dev->os_dep.reg_offset);
+ return sprintf(buf, "invalid offset\n");
+ }
+}
+
+/**
+ * Store the value in the register at the offset in the reg_offset
+ * attribute.
+ *
+ */
+static ssize_t regvalue_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ volatile uint32_t *addr;
+ uint32_t val = simple_strtoul(buf, NULL, 16);
+ //dev_dbg(_dev, "Offset=0x%08x Val=0x%08x\n", otg_dev->reg_offset, val);
+ if (otg_dev->os_dep.reg_offset != 0xFFFFFFFF && 0 != otg_dev->os_dep.base) {
+ /* Calculate the address */
+ addr = (uint32_t *) (otg_dev->os_dep.reg_offset +
+ (uint8_t *) otg_dev->os_dep.base);
+ DWC_WRITE_REG32(addr, val);
+ } else {
+ dev_err(_dev, "Invalid Register Offset (0x%08x)\n",
+ otg_dev->os_dep.reg_offset);
+ }
+ return count;
+}
+
+DEVICE_ATTR(regvalue, S_IRUGO | S_IWUSR, regvalue_show, regvalue_store);
+
+/*
+ * Attributes
+ */
+DWC_OTG_DEVICE_ATTR_BITFIELD_RO(mode, "Mode");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hnpcapable, "HNPCapable");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(srpcapable, "SRPCapable");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hsic_connect, "HSIC Connect");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(inv_sel_hsic, "Invert Select HSIC");
+
+//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(buspower,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode");
+//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(bussuspend,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RO(busconnected, "Bus Connected");
+
+DWC_OTG_DEVICE_ATTR_REG32_RW(gotgctl, 0, "GOTGCTL");
+DWC_OTG_DEVICE_ATTR_REG32_RW(gusbcfg,
+ &(otg_dev->core_if->core_global_regs->gusbcfg),
+ "GUSBCFG");
+DWC_OTG_DEVICE_ATTR_REG32_RW(grxfsiz,
+ &(otg_dev->core_if->core_global_regs->grxfsiz),
+ "GRXFSIZ");
+DWC_OTG_DEVICE_ATTR_REG32_RW(gnptxfsiz,
+ &(otg_dev->core_if->core_global_regs->gnptxfsiz),
+ "GNPTXFSIZ");
+DWC_OTG_DEVICE_ATTR_REG32_RW(gpvndctl,
+ &(otg_dev->core_if->core_global_regs->gpvndctl),
+ "GPVNDCTL");
+DWC_OTG_DEVICE_ATTR_REG32_RW(ggpio,
+ &(otg_dev->core_if->core_global_regs->ggpio),
+ "GGPIO");
+DWC_OTG_DEVICE_ATTR_REG32_RW(guid, &(otg_dev->core_if->core_global_regs->guid),
+ "GUID");
+DWC_OTG_DEVICE_ATTR_REG32_RO(gsnpsid,
+ &(otg_dev->core_if->core_global_regs->gsnpsid),
+ "GSNPSID");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(devspeed, "Device Speed");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RO(enumspeed, "Device Enumeration Speed");
+
+DWC_OTG_DEVICE_ATTR_REG32_RO(hptxfsiz,
+ &(otg_dev->core_if->core_global_regs->hptxfsiz),
+ "HPTXFSIZ");
+DWC_OTG_DEVICE_ATTR_REG32_RW(hprt0, otg_dev->core_if->host_if->hprt0, "HPRT0");
+
+/**
+ * @todo Add code to initiate the HNP.
+ */
+/**
+ * Show the HNP status bit
+ */
+static ssize_t hnp_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+ return sprintf(buf, "HstNegScs = 0x%x\n",
+ dwc_otg_get_hnpstatus(otg_dev->core_if));
+}
+
+/**
+ * Set the HNP Request bit
+ */
+static ssize_t hnp_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+ uint32_t in = simple_strtoul(buf, NULL, 16);
+ dwc_otg_set_hnpreq(otg_dev->core_if, in);
+ return count;
+}
+
+DEVICE_ATTR(hnp, 0644, hnp_show, hnp_store);
+
+/**
+ * @todo Add code to initiate the SRP.
+ */
+/**
+ * Show the SRP status bit
+ */
+static ssize_t srp_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_HOST_ONLY
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+ return sprintf(buf, "SesReqScs = 0x%x\n",
+ dwc_otg_get_srpstatus(otg_dev->core_if));
+#else
+ return sprintf(buf, "Host Only Mode!\n");
+#endif
+}
+
+/**
+ * Set the SRP Request bit
+ */
+static ssize_t srp_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifndef DWC_HOST_ONLY
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+ dwc_otg_pcd_initiate_srp(otg_dev->pcd);
+#endif
+ return count;
+}
+
+DEVICE_ATTR(srp, 0644, srp_show, srp_store);
+
+/**
+ * @todo Need to do more for power on/off?
+ */
+/**
+ * Show the Bus Power status
+ */
+static ssize_t buspower_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+ return sprintf(buf, "Bus Power = 0x%x\n",
+ dwc_otg_get_prtpower(otg_dev->core_if));
+}
+
+/**
+ * Set the Bus Power status
+ */
+static ssize_t buspower_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+ uint32_t on = simple_strtoul(buf, NULL, 16);
+ dwc_otg_set_prtpower(otg_dev->core_if, on);
+ return count;
+}
+
+DEVICE_ATTR(buspower, 0644, buspower_show, buspower_store);
+
+/**
+ * @todo Need to do more for suspend?
+ */
+/**
+ * Show the Bus Suspend status
+ */
+static ssize_t bussuspend_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ return sprintf(buf, "Bus Suspend = 0x%x\n",
+ dwc_otg_get_prtsuspend(otg_dev->core_if));
+}
+
+/**
+ * Set the Bus Suspend status
+ */
+static ssize_t bussuspend_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ uint32_t in = simple_strtoul(buf, NULL, 16);
+ dwc_otg_set_prtsuspend(otg_dev->core_if, in);
+ return count;
+}
+
+DEVICE_ATTR(bussuspend, 0644, bussuspend_show, bussuspend_store);
+
+/**
+ * Show the Mode Change Ready Timer status
+ */
+static ssize_t mode_ch_tim_en_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ return sprintf(buf, "Mode Change Ready Timer Enable = 0x%x\n",
+ dwc_otg_get_mode_ch_tim(otg_dev->core_if));
+}
+
+/**
+ * Set the Mode Change Ready Timer status
+ */
+static ssize_t mode_ch_tim_en_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ uint32_t in = simple_strtoul(buf, NULL, 16);
+ dwc_otg_set_mode_ch_tim(otg_dev->core_if, in);
+ return count;
+}
+
+DEVICE_ATTR(mode_ch_tim_en, 0644, mode_ch_tim_en_show, mode_ch_tim_en_store);
+
+/**
+ * Show the value of HFIR Frame Interval bitfield
+ */
+static ssize_t fr_interval_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ return sprintf(buf, "Frame Interval = 0x%x\n",
+ dwc_otg_get_fr_interval(otg_dev->core_if));
+}
+
+/**
+ * Set the HFIR Frame Interval value
+ */
+static ssize_t fr_interval_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ uint32_t in = simple_strtoul(buf, NULL, 10);
+ dwc_otg_set_fr_interval(otg_dev->core_if, in);
+ return count;
+}
+
+DEVICE_ATTR(fr_interval, 0644, fr_interval_show, fr_interval_store);
+
+/**
+ * Show the status of Remote Wakeup.
+ */
+static ssize_t remote_wakeup_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_HOST_ONLY
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ return sprintf(buf,
+ "Remote Wakeup Sig = %d Enabled = %d LPM Remote Wakeup = %d\n",
+ dwc_otg_get_remotewakesig(otg_dev->core_if),
+ dwc_otg_pcd_get_rmwkup_enable(otg_dev->pcd),
+ dwc_otg_get_lpm_remotewakeenabled(otg_dev->core_if));
+#else
+ return sprintf(buf, "Host Only Mode!\n");
+#endif /* DWC_HOST_ONLY */
+}
+
+/**
+ * Initiate a remote wakeup of the host. The Device control register
+ * Remote Wakeup Signal bit is written if the PCD Remote wakeup enable
+ * flag is set.
+ *
+ */
+static ssize_t remote_wakeup_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifndef DWC_HOST_ONLY
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ uint32_t val = simple_strtoul(buf, NULL, 16);
+
+ if (val & 1) {
+ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 1);
+ } else {
+ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 0);
+ }
+#endif /* DWC_HOST_ONLY */
+ return count;
+}
+
+DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR, remote_wakeup_show,
+ remote_wakeup_store);
+
+/**
+ * Show the whether core is hibernated or not.
+ */
+static ssize_t rem_wakeup_pwrdn_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_HOST_ONLY
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+ if (dwc_otg_get_core_state(otg_dev->core_if)) {
+ DWC_PRINTF("Core is in hibernation\n");
+ } else {
+ DWC_PRINTF("Core is not in hibernation\n");
+ }
+#endif /* DWC_HOST_ONLY */
+ return 0;
+}
+
+extern int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if,
+ int rem_wakeup, int reset);
+
+/**
+ * Initiate a remote wakeup of the device to exit from hibernation.
+ */
+static ssize_t rem_wakeup_pwrdn_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifndef DWC_HOST_ONLY
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+ dwc_otg_device_hibernation_restore(otg_dev->core_if, 1, 0);
+#endif
+ return count;
+}
+
+DEVICE_ATTR(rem_wakeup_pwrdn, S_IRUGO | S_IWUSR, rem_wakeup_pwrdn_show,
+ rem_wakeup_pwrdn_store);
+
+static ssize_t disconnect_us(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+
+#ifndef DWC_HOST_ONLY
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+ uint32_t val = simple_strtoul(buf, NULL, 16);
+ DWC_PRINTF("The Passed value is %04x\n", val);
+
+ dwc_otg_pcd_disconnect_us(otg_dev->pcd, 50);
+
+#endif /* DWC_HOST_ONLY */
+ return count;
+}
+
+DEVICE_ATTR(disconnect_us, S_IWUSR, 0, disconnect_us);
+
+/**
+ * Dump global registers and either host or device registers (depending on the
+ * current mode of the core).
+ */
+static ssize_t regdump_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ dwc_otg_dump_global_registers(otg_dev->core_if);
+ if (dwc_otg_is_host_mode(otg_dev->core_if)) {
+ dwc_otg_dump_host_registers(otg_dev->core_if);
+ } else {
+ dwc_otg_dump_dev_registers(otg_dev->core_if);
+
+ }
+ return sprintf(buf, "Register Dump\n");
+}
+
+DEVICE_ATTR(regdump, S_IRUGO | S_IWUSR, regdump_show, 0);
+
+/**
+ * Dump global registers and either host or device registers (depending on the
+ * current mode of the core).
+ */
+static ssize_t spramdump_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ dwc_otg_dump_spram(otg_dev->core_if);
+
+ return sprintf(buf, "SPRAM Dump\n");
+}
+
+DEVICE_ATTR(spramdump, S_IRUGO | S_IWUSR, spramdump_show, 0);
+
+/**
+ * Dump the current hcd state.
+ */
+static ssize_t hcddump_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_DEVICE_ONLY
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ dwc_otg_hcd_dump_state(otg_dev->hcd);
+#endif /* DWC_DEVICE_ONLY */
+ return sprintf(buf, "HCD Dump\n");
+}
+
+DEVICE_ATTR(hcddump, S_IRUGO | S_IWUSR, hcddump_show, 0);
+
+/**
+ * Dump the average frame remaining at SOF. This can be used to
+ * determine average interrupt latency. Frame remaining is also shown for
+ * start transfer and two additional sample points.
+ */
+static ssize_t hcd_frrem_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_DEVICE_ONLY
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ dwc_otg_hcd_dump_frrem(otg_dev->hcd);
+#endif /* DWC_DEVICE_ONLY */
+ return sprintf(buf, "HCD Dump Frame Remaining\n");
+}
+
+DEVICE_ATTR(hcd_frrem, S_IRUGO | S_IWUSR, hcd_frrem_show, 0);
+
+/**
+ * Displays the time required to read the GNPTXFSIZ register many times (the
+ * output shows the number of times the register is read).
+ */
+#define RW_REG_COUNT 10000000
+#define MSEC_PER_JIFFIE 1000/HZ
+static ssize_t rd_reg_test_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+ int i;
+ int time;
+ int start_jiffies;
+
+ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n",
+ HZ, MSEC_PER_JIFFIE, loops_per_jiffy);
+ start_jiffies = jiffies;
+ for (i = 0; i < RW_REG_COUNT; i++) {
+ dwc_otg_get_gnptxfsiz(otg_dev->core_if);
+ }
+ time = jiffies - start_jiffies;
+ return sprintf(buf,
+ "Time to read GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n",
+ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
+}
+
+DEVICE_ATTR(rd_reg_test, S_IRUGO | S_IWUSR, rd_reg_test_show, 0);
+
+/**
+ * Displays the time required to write the GNPTXFSIZ register many times (the
+ * output shows the number of times the register is written).
+ */
+static ssize_t wr_reg_test_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+
+ uint32_t reg_val;
+ int i;
+ int time;
+ int start_jiffies;
+
+ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n",
+ HZ, MSEC_PER_JIFFIE, loops_per_jiffy);
+ reg_val = dwc_otg_get_gnptxfsiz(otg_dev->core_if);
+ start_jiffies = jiffies;
+ for (i = 0; i < RW_REG_COUNT; i++) {
+ dwc_otg_set_gnptxfsiz(otg_dev->core_if, reg_val);
+ }
+ time = jiffies - start_jiffies;
+ return sprintf(buf,
+ "Time to write GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n",
+ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
+}
+
+DEVICE_ATTR(wr_reg_test, S_IRUGO | S_IWUSR, wr_reg_test_show, 0);
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+
+/**
+* Show the lpm_response attribute.
+*/
+static ssize_t lpmresp_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+
+ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if))
+ return sprintf(buf, "** LPM is DISABLED **\n");
+
+ if (!dwc_otg_is_device_mode(otg_dev->core_if)) {
+ return sprintf(buf, "** Current mode is not device mode\n");
+ }
+ return sprintf(buf, "lpm_response = %d\n",
+ dwc_otg_get_lpmresponse(otg_dev->core_if));
+}
+
+/**
+* Store the lpm_response attribute.
+*/
+static ssize_t lpmresp_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+
+ uint32_t val = simple_strtoul(buf, NULL, 16);
+
+ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) {
+ return 0;
+ }
+
+ if (!dwc_otg_is_device_mode(otg_dev->core_if)) {
+ return 0;
+ }
+
+ dwc_otg_set_lpmresponse(otg_dev->core_if, val);
+ return count;
+}
+
+DEVICE_ATTR(lpm_response, S_IRUGO | S_IWUSR, lpmresp_show, lpmresp_store);
+
+/**
+* Show the sleep_status attribute.
+*/
+static ssize_t sleepstatus_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+
+ return sprintf(buf, "Sleep Status = %d\n",
+ dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if));
+}
+
+/**
+ * Store the sleep_status attribure.
+ */
+static ssize_t sleepstatus_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#ifdef LM_INTERFACE
+ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev);
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = dev_get_platdata(_dev);
+#endif
+
+
+ dwc_otg_core_if_t *core_if = otg_dev->core_if;
+
+ if (dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if)) {
+ if (dwc_otg_is_host_mode(core_if)) {
+
+ DWC_PRINTF("Host initiated resume\n");
+ dwc_otg_set_prtresume(otg_dev->core_if, 1);
+ }
+ }
+
+ return count;
+}
+
+DEVICE_ATTR(sleep_status, S_IRUGO | S_IWUSR, sleepstatus_show,
+ sleepstatus_store);
+
+#endif /* CONFIG_USB_DWC_OTG_LPM_ENABLE */
+
+/**@}*/
+
+/**
+ * Create the device files
+ */
+void dwc_otg_attr_create(
+#ifdef LM_INTERFACE
+ struct lm_device *dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *dev
+#else
+// struct device *dev
+ struct platform_device *dev
+#endif
+
+ )
+{
+ int error;
+
+ error = device_create_file(&dev->dev, &dev_attr_regoffset);
+ error = device_create_file(&dev->dev, &dev_attr_regvalue);
+ error = device_create_file(&dev->dev, &dev_attr_mode);
+ error = device_create_file(&dev->dev, &dev_attr_hnpcapable);
+ error = device_create_file(&dev->dev, &dev_attr_srpcapable);
+ error = device_create_file(&dev->dev, &dev_attr_hsic_connect);
+ error = device_create_file(&dev->dev, &dev_attr_inv_sel_hsic);
+ error = device_create_file(&dev->dev, &dev_attr_hnp);
+ error = device_create_file(&dev->dev, &dev_attr_srp);
+ error = device_create_file(&dev->dev, &dev_attr_buspower);
+ error = device_create_file(&dev->dev, &dev_attr_bussuspend);
+ error = device_create_file(&dev->dev, &dev_attr_mode_ch_tim_en);
+ error = device_create_file(&dev->dev, &dev_attr_fr_interval);
+ error = device_create_file(&dev->dev, &dev_attr_busconnected);
+ error = device_create_file(&dev->dev, &dev_attr_gotgctl);
+ error = device_create_file(&dev->dev, &dev_attr_gusbcfg);
+ error = device_create_file(&dev->dev, &dev_attr_grxfsiz);
+ error = device_create_file(&dev->dev, &dev_attr_gnptxfsiz);
+ error = device_create_file(&dev->dev, &dev_attr_gpvndctl);
+ error = device_create_file(&dev->dev, &dev_attr_ggpio);
+ error = device_create_file(&dev->dev, &dev_attr_guid);
+ error = device_create_file(&dev->dev, &dev_attr_gsnpsid);
+ error = device_create_file(&dev->dev, &dev_attr_devspeed);
+ error = device_create_file(&dev->dev, &dev_attr_enumspeed);
+ error = device_create_file(&dev->dev, &dev_attr_hptxfsiz);
+ error = device_create_file(&dev->dev, &dev_attr_hprt0);
+ error = device_create_file(&dev->dev, &dev_attr_remote_wakeup);
+ error = device_create_file(&dev->dev, &dev_attr_rem_wakeup_pwrdn);
+ error = device_create_file(&dev->dev, &dev_attr_disconnect_us);
+ error = device_create_file(&dev->dev, &dev_attr_regdump);
+ error = device_create_file(&dev->dev, &dev_attr_spramdump);
+ error = device_create_file(&dev->dev, &dev_attr_hcddump);
+ error = device_create_file(&dev->dev, &dev_attr_hcd_frrem);
+ error = device_create_file(&dev->dev, &dev_attr_rd_reg_test);
+ error = device_create_file(&dev->dev, &dev_attr_wr_reg_test);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ error = device_create_file(&dev->dev, &dev_attr_lpm_response);
+ error = device_create_file(&dev->dev, &dev_attr_sleep_status);
+#endif
+}
+
+/**
+ * Remove the device files
+ */
+void dwc_otg_attr_remove(
+#ifdef LM_INTERFACE
+ struct lm_device *dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *dev
+#else
+ //struct device *dev
+ struct platform_device *dev
+#endif
+
+ )
+{
+ device_remove_file(&dev->dev, &dev_attr_regoffset);
+ device_remove_file(&dev->dev, &dev_attr_regvalue);
+ device_remove_file(&dev->dev, &dev_attr_mode);
+ device_remove_file(&dev->dev, &dev_attr_hnpcapable);
+ device_remove_file(&dev->dev, &dev_attr_srpcapable);
+ device_remove_file(&dev->dev, &dev_attr_hsic_connect);
+ device_remove_file(&dev->dev, &dev_attr_inv_sel_hsic);
+ device_remove_file(&dev->dev, &dev_attr_hnp);
+ device_remove_file(&dev->dev, &dev_attr_srp);
+ device_remove_file(&dev->dev, &dev_attr_buspower);
+ device_remove_file(&dev->dev, &dev_attr_bussuspend);
+ device_remove_file(&dev->dev, &dev_attr_mode_ch_tim_en);
+ device_remove_file(&dev->dev, &dev_attr_fr_interval);
+ device_remove_file(&dev->dev, &dev_attr_busconnected);
+ device_remove_file(&dev->dev, &dev_attr_gotgctl);
+ device_remove_file(&dev->dev, &dev_attr_gusbcfg);
+ device_remove_file(&dev->dev, &dev_attr_grxfsiz);
+ device_remove_file(&dev->dev, &dev_attr_gnptxfsiz);
+ device_remove_file(&dev->dev, &dev_attr_gpvndctl);
+ device_remove_file(&dev->dev, &dev_attr_ggpio);
+ device_remove_file(&dev->dev, &dev_attr_guid);
+ device_remove_file(&dev->dev, &dev_attr_gsnpsid);
+ device_remove_file(&dev->dev, &dev_attr_devspeed);
+ device_remove_file(&dev->dev, &dev_attr_enumspeed);
+ device_remove_file(&dev->dev, &dev_attr_hptxfsiz);
+ device_remove_file(&dev->dev, &dev_attr_hprt0);
+ device_remove_file(&dev->dev, &dev_attr_remote_wakeup);
+ device_remove_file(&dev->dev, &dev_attr_rem_wakeup_pwrdn);
+ device_remove_file(&dev->dev, &dev_attr_disconnect_us);
+ device_remove_file(&dev->dev, &dev_attr_regdump);
+ device_remove_file(&dev->dev, &dev_attr_spramdump);
+ device_remove_file(&dev->dev, &dev_attr_hcddump);
+ device_remove_file(&dev->dev, &dev_attr_hcd_frrem);
+ device_remove_file(&dev->dev, &dev_attr_rd_reg_test);
+ device_remove_file(&dev->dev, &dev_attr_wr_reg_test);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ device_remove_file(&dev->dev, &dev_attr_lpm_response);
+ device_remove_file(&dev->dev, &dev_attr_sleep_status);
+#endif
+}
diff --git a/drivers/usb/dwc_otg/dwc_otg_attr.h b/drivers/usb/dwc_otg/dwc_otg_attr.h
new file mode 100644
index 0000000..9479e6c
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_attr.h
@@ -0,0 +1,89 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.h $
+ * $Revision: #13 $
+ * $Date: 2010/06/21 $
+ * $Change: 1532021 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_OTG_ATTR_H__)
+#define __DWC_OTG_ATTR_H__
+
+/** @file
+ * This file contains the interface to the Linux device attributes.
+ */
+extern struct device_attribute dev_attr_regoffset;
+extern struct device_attribute dev_attr_regvalue;
+
+extern struct device_attribute dev_attr_mode;
+extern struct device_attribute dev_attr_hnpcapable;
+extern struct device_attribute dev_attr_srpcapable;
+extern struct device_attribute dev_attr_hnp;
+extern struct device_attribute dev_attr_srp;
+extern struct device_attribute dev_attr_buspower;
+extern struct device_attribute dev_attr_bussuspend;
+extern struct device_attribute dev_attr_mode_ch_tim_en;
+extern struct device_attribute dev_attr_fr_interval;
+extern struct device_attribute dev_attr_busconnected;
+extern struct device_attribute dev_attr_gotgctl;
+extern struct device_attribute dev_attr_gusbcfg;
+extern struct device_attribute dev_attr_grxfsiz;
+extern struct device_attribute dev_attr_gnptxfsiz;
+extern struct device_attribute dev_attr_gpvndctl;
+extern struct device_attribute dev_attr_ggpio;
+extern struct device_attribute dev_attr_guid;
+extern struct device_attribute dev_attr_gsnpsid;
+extern struct device_attribute dev_attr_devspeed;
+extern struct device_attribute dev_attr_enumspeed;
+extern struct device_attribute dev_attr_hptxfsiz;
+extern struct device_attribute dev_attr_hprt0;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+extern struct device_attribute dev_attr_lpm_response;
+extern struct device_attribute devi_attr_sleep_status;
+#endif
+
+void dwc_otg_attr_create(
+#ifdef LM_INTERFACE
+ struct lm_device *dev
+#elif PCI_INTERFACE
+ struct pci_dev *dev
+#else
+ struct platform_device *dev
+#endif
+ );
+
+void dwc_otg_attr_remove(
+#ifdef LM_INTERFACE
+ struct lm_device *dev
+#elif PCI_INTERFACE
+ struct pci_dev *dev
+#else
+ struct platform_device *dev
+#endif
+ );
+#endif
diff --git a/drivers/usb/dwc_otg/dwc_otg_cfi.c b/drivers/usb/dwc_otg/dwc_otg_cfi.c
new file mode 100644
index 0000000..9c6fe15
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_cfi.c
@@ -0,0 +1,1876 @@
+/* ==========================================================================
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ *
+ * This file contains the most of the CFI(Core Feature Interface)
+ * implementation for the OTG.
+ */
+
+#ifdef DWC_UTE_CFI
+
+#include "dwc_otg_pcd.h"
+#include "dwc_otg_cfi.h"
+
+/** This definition should actually migrate to the Portability Library */
+#define DWC_CONSTANT_CPU_TO_LE16(x) (x)
+
+extern dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex);
+
+static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen);
+static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen,
+ struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *ctrl_req);
+static int cfi_set_feature_value(struct dwc_otg_pcd *pcd);
+static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *req);
+static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *req);
+static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *req);
+static int cfi_preproc_reset(struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *req);
+static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep);
+
+static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if);
+static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue);
+static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue);
+
+static uint8_t resize_fifos(dwc_otg_core_if_t * core_if);
+
+/** This is the header of the all features descriptor */
+static cfi_all_features_header_t all_props_desc_header = {
+ .wVersion = DWC_CONSTANT_CPU_TO_LE16(0x100),
+ .wCoreID = DWC_CONSTANT_CPU_TO_LE16(CFI_CORE_ID_OTG),
+ .wNumFeatures = DWC_CONSTANT_CPU_TO_LE16(9),
+};
+
+/** This is an array of statically allocated feature descriptors */
+static cfi_feature_desc_header_t prop_descs[] = {
+
+ /* FT_ID_DMA_MODE */
+ {
+ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_MODE),
+ .bmAttributes = CFI_FEATURE_ATTR_RW,
+ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(1),
+ },
+
+ /* FT_ID_DMA_BUFFER_SETUP */
+ {
+ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFFER_SETUP),
+ .bmAttributes = CFI_FEATURE_ATTR_RW,
+ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
+ },
+
+ /* FT_ID_DMA_BUFF_ALIGN */
+ {
+ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFF_ALIGN),
+ .bmAttributes = CFI_FEATURE_ATTR_RW,
+ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
+ },
+
+ /* FT_ID_DMA_CONCAT_SETUP */
+ {
+ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CONCAT_SETUP),
+ .bmAttributes = CFI_FEATURE_ATTR_RW,
+ //.wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
+ },
+
+ /* FT_ID_DMA_CIRCULAR */
+ {
+ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CIRCULAR),
+ .bmAttributes = CFI_FEATURE_ATTR_RW,
+ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
+ },
+
+ /* FT_ID_THRESHOLD_SETUP */
+ {
+ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_THRESHOLD_SETUP),
+ .bmAttributes = CFI_FEATURE_ATTR_RW,
+ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
+ },
+
+ /* FT_ID_DFIFO_DEPTH */
+ {
+ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DFIFO_DEPTH),
+ .bmAttributes = CFI_FEATURE_ATTR_RO,
+ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
+ },
+
+ /* FT_ID_TX_FIFO_DEPTH */
+ {
+ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_TX_FIFO_DEPTH),
+ .bmAttributes = CFI_FEATURE_ATTR_RW,
+ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
+ },
+
+ /* FT_ID_RX_FIFO_DEPTH */
+ {
+ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_RX_FIFO_DEPTH),
+ .bmAttributes = CFI_FEATURE_ATTR_RW,
+ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
+ }
+};
+
+/** The table of feature names */
+cfi_string_t prop_name_table[] = {
+ {FT_ID_DMA_MODE, "dma_mode"},
+ {FT_ID_DMA_BUFFER_SETUP, "buffer_setup"},
+ {FT_ID_DMA_BUFF_ALIGN, "buffer_align"},
+ {FT_ID_DMA_CONCAT_SETUP, "concat_setup"},
+ {FT_ID_DMA_CIRCULAR, "buffer_circular"},
+ {FT_ID_THRESHOLD_SETUP, "threshold_setup"},
+ {FT_ID_DFIFO_DEPTH, "dfifo_depth"},
+ {FT_ID_TX_FIFO_DEPTH, "txfifo_depth"},
+ {FT_ID_RX_FIFO_DEPTH, "rxfifo_depth"},
+ {}
+};
+
+/************************************************************************/
+
+/**
+ * Returns the name of the feature by its ID
+ * or NULL if no featute ID matches.
+ *
+ */
+const uint8_t *get_prop_name(uint16_t prop_id, int *len)
+{
+ cfi_string_t *pstr;
+ *len = 0;
+
+ for (pstr = prop_name_table; pstr && pstr->s; pstr++) {
+ if (pstr->id == prop_id) {
+ *len = DWC_STRLEN(pstr->s);
+ return pstr->s;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * This function handles all CFI specific control requests.
+ *
+ * Return a negative value to stall the DCE.
+ */
+int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl)
+{
+ int retval = 0;
+ dwc_otg_pcd_ep_t *ep = NULL;
+ cfiobject_t *cfi = pcd->cfi;
+ struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd);
+ uint16_t wLen = DWC_LE16_TO_CPU(&ctrl->wLength);
+ uint16_t wValue = DWC_LE16_TO_CPU(&ctrl->wValue);
+ uint16_t wIndex = DWC_LE16_TO_CPU(&ctrl->wIndex);
+ uint32_t regaddr = 0;
+ uint32_t regval = 0;
+
+ /* Save this Control Request in the CFI object.
+ * The data field will be assigned in the data stage completion CB function.
+ */
+ cfi->ctrl_req = *ctrl;
+ cfi->ctrl_req.data = NULL;
+
+ cfi->need_gadget_att = 0;
+ cfi->need_status_in_complete = 0;
+
+ switch (ctrl->bRequest) {
+ case VEN_CORE_GET_FEATURES:
+ retval = cfi_core_features_buf(cfi->buf_in.buf, CFI_IN_BUF_LEN);
+ if (retval >= 0) {
+ //dump_msg(cfi->buf_in.buf, retval);
+ ep = &pcd->ep0;
+
+ retval = min((uint16_t) retval, wLen);
+ /* Transfer this buffer to the host through the EP0-IN EP */
+ ep->dwc_ep.dma_addr = cfi->buf_in.addr;
+ ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf;
+ ep->dwc_ep.xfer_buff = cfi->buf_in.buf;
+ ep->dwc_ep.xfer_len = retval;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ pcd->ep0_pending = 1;
+ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+ }
+ retval = 0;
+ break;
+
+ case VEN_CORE_GET_FEATURE:
+ CFI_INFO("VEN_CORE_GET_FEATURE\n");
+ retval = cfi_get_feature_value(cfi->buf_in.buf, CFI_IN_BUF_LEN,
+ pcd, ctrl);
+ if (retval >= 0) {
+ ep = &pcd->ep0;
+
+ retval = min((uint16_t) retval, wLen);
+ /* Transfer this buffer to the host through the EP0-IN EP */
+ ep->dwc_ep.dma_addr = cfi->buf_in.addr;
+ ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf;
+ ep->dwc_ep.xfer_buff = cfi->buf_in.buf;
+ ep->dwc_ep.xfer_len = retval;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ pcd->ep0_pending = 1;
+ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+ }
+ CFI_INFO("VEN_CORE_GET_FEATURE=%d\n", retval);
+ dump_msg(cfi->buf_in.buf, retval);
+ break;
+
+ case VEN_CORE_SET_FEATURE:
+ CFI_INFO("VEN_CORE_SET_FEATURE\n");
+ /* Set up an XFER to get the data stage of the control request,
+ * which is the new value of the feature to be modified.
+ */
+ ep = &pcd->ep0;
+ ep->dwc_ep.is_in = 0;
+ ep->dwc_ep.dma_addr = cfi->buf_out.addr;
+ ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf;
+ ep->dwc_ep.xfer_buff = cfi->buf_out.buf;
+ ep->dwc_ep.xfer_len = wLen;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ pcd->ep0_pending = 1;
+ /* Read the control write's data stage */
+ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+ retval = 0;
+ break;
+
+ case VEN_CORE_RESET_FEATURES:
+ CFI_INFO("VEN_CORE_RESET_FEATURES\n");
+ cfi->need_gadget_att = 1;
+ cfi->need_status_in_complete = 1;
+ retval = cfi_preproc_reset(pcd, ctrl);
+ CFI_INFO("VEN_CORE_RESET_FEATURES = (%d)\n", retval);
+ break;
+
+ case VEN_CORE_ACTIVATE_FEATURES:
+ CFI_INFO("VEN_CORE_ACTIVATE_FEATURES\n");
+ break;
+
+ case VEN_CORE_READ_REGISTER:
+ CFI_INFO("VEN_CORE_READ_REGISTER\n");
+ /* wValue optionally contains the HI WORD of the register offset and
+ * wIndex contains the LOW WORD of the register offset
+ */
+ if (wValue == 0) {
+ /* @TODO - MAS - fix the access to the base field */
+ regaddr = 0;
+ //regaddr = (uint32_t) pcd->otg_dev->os_dep.base;
+ //GET_CORE_IF(pcd)->co
+ regaddr |= wIndex;
+ } else {
+ regaddr = (wValue << 16) | wIndex;
+ }
+
+ /* Read a 32-bit value of the memory at the regaddr */
+ regval = DWC_READ_REG32((uint32_t *) regaddr);
+
+ ep = &pcd->ep0;
+ dwc_memcpy(cfi->buf_in.buf, ®val, sizeof(uint32_t));
+ ep->dwc_ep.is_in = 1;
+ ep->dwc_ep.dma_addr = cfi->buf_in.addr;
+ ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf;
+ ep->dwc_ep.xfer_buff = cfi->buf_in.buf;
+ ep->dwc_ep.xfer_len = wLen;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ pcd->ep0_pending = 1;
+ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+ cfi->need_gadget_att = 0;
+ retval = 0;
+ break;
+
+ case VEN_CORE_WRITE_REGISTER:
+ CFI_INFO("VEN_CORE_WRITE_REGISTER\n");
+ /* Set up an XFER to get the data stage of the control request,
+ * which is the new value of the register to be modified.
+ */
+ ep = &pcd->ep0;
+ ep->dwc_ep.is_in = 0;
+ ep->dwc_ep.dma_addr = cfi->buf_out.addr;
+ ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf;
+ ep->dwc_ep.xfer_buff = cfi->buf_out.buf;
+ ep->dwc_ep.xfer_len = wLen;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ pcd->ep0_pending = 1;
+ /* Read the control write's data stage */
+ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+ retval = 0;
+ break;
+
+ default:
+ retval = -DWC_E_NOT_SUPPORTED;
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * This function prepares the core features descriptors and copies its
+ * raw representation into the buffer <buf>.
+ *
+ * The buffer structure is as follows:
+ * all_features_header (8 bytes)
+ * features_#1 (8 bytes + feature name string length)
+ * features_#2 (8 bytes + feature name string length)
+ * .....
+ * features_#n - where n=the total count of feature descriptors
+ */
+static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen)
+{
+ cfi_feature_desc_header_t *prop_hdr = prop_descs;
+ cfi_feature_desc_header_t *prop;
+ cfi_all_features_header_t *all_props_hdr = &all_props_desc_header;
+ cfi_all_features_header_t *tmp;
+ uint8_t *tmpbuf = buf;
+ const uint8_t *pname = NULL;
+ int i, j, namelen = 0, totlen;
+
+ /* Prepare and copy the core features into the buffer */
+ CFI_INFO("%s:\n", __func__);
+
+ tmp = (cfi_all_features_header_t *) tmpbuf;
+ *tmp = *all_props_hdr;
+ tmpbuf += CFI_ALL_FEATURES_HDR_LEN;
+
+ j = sizeof(prop_descs) / sizeof(cfi_all_features_header_t);
+ for (i = 0; i < j; i++, prop_hdr++) {
+ pname = get_prop_name(prop_hdr->wFeatureID, &namelen);
+ prop = (cfi_feature_desc_header_t *) tmpbuf;
+ *prop = *prop_hdr;
+
+ prop->bNameLen = namelen;
+ prop->wLength =
+ DWC_CONSTANT_CPU_TO_LE16(CFI_FEATURE_DESC_HDR_LEN +
+ namelen);
+
+ tmpbuf += CFI_FEATURE_DESC_HDR_LEN;
+ dwc_memcpy(tmpbuf, pname, namelen);
+ tmpbuf += namelen;
+ }
+
+ totlen = tmpbuf - buf;
+
+ if (totlen > 0) {
+ tmp = (cfi_all_features_header_t *) buf;
+ tmp->wTotalLen = DWC_CONSTANT_CPU_TO_LE16(totlen);
+ }
+
+ return totlen;
+}
+
+/**
+ * This function releases all the dynamic memory in the CFI object.
+ */
+static void cfi_release(cfiobject_t * cfiobj)
+{
+ cfi_ep_t *cfiep;
+ dwc_list_link_t *tmp;
+
+ CFI_INFO("%s\n", __func__);
+
+ if (cfiobj->buf_in.buf) {
+ DWC_DMA_FREE(CFI_IN_BUF_LEN, cfiobj->buf_in.buf,
+ cfiobj->buf_in.addr);
+ cfiobj->buf_in.buf = NULL;
+ }
+
+ if (cfiobj->buf_out.buf) {
+ DWC_DMA_FREE(CFI_OUT_BUF_LEN, cfiobj->buf_out.buf,
+ cfiobj->buf_out.addr);
+ cfiobj->buf_out.buf = NULL;
+ }
+
+ /* Free the Buffer Setup values for each EP */
+ //list_for_each_entry(cfiep, &cfiobj->active_eps, lh) {
+ DWC_LIST_FOREACH(tmp, &cfiobj->active_eps) {
+ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+ cfi_free_ep_bs_dyn_data(cfiep);
+ }
+}
+
+/**
+ * This function frees the dynamically allocated EP buffer setup data.
+ */
+static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep)
+{
+ if (cfiep->bm_sg) {
+ DWC_FREE(cfiep->bm_sg);
+ cfiep->bm_sg = NULL;
+ }
+
+ if (cfiep->bm_align) {
+ DWC_FREE(cfiep->bm_align);
+ cfiep->bm_align = NULL;
+ }
+
+ if (cfiep->bm_concat) {
+ if (NULL != cfiep->bm_concat->wTxBytes) {
+ DWC_FREE(cfiep->bm_concat->wTxBytes);
+ cfiep->bm_concat->wTxBytes = NULL;
+ }
+ DWC_FREE(cfiep->bm_concat);
+ cfiep->bm_concat = NULL;
+ }
+}
+
+/**
+ * This function initializes the default values of the features
+ * for a specific endpoint and should be called only once when
+ * the EP is enabled first time.
+ */
+static int cfi_ep_init_defaults(struct dwc_otg_pcd *pcd, cfi_ep_t * cfiep)
+{
+ int retval = 0;
+
+ cfiep->bm_sg = DWC_ALLOC(sizeof(ddma_sg_buffer_setup_t));
+ if (NULL == cfiep->bm_sg) {
+ CFI_INFO("Failed to allocate memory for SG feature value\n");
+ return -DWC_E_NO_MEMORY;
+ }
+ dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t));
+
+ /* For the Concatenation feature's default value we do not allocate
+ * memory for the wTxBytes field - it will be done in the set_feature_value
+ * request handler.
+ */
+ cfiep->bm_concat = DWC_ALLOC(sizeof(ddma_concat_buffer_setup_t));
+ if (NULL == cfiep->bm_concat) {
+ CFI_INFO
+ ("Failed to allocate memory for CONCATENATION feature value\n");
+ DWC_FREE(cfiep->bm_sg);
+ return -DWC_E_NO_MEMORY;
+ }
+ dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t));
+
+ cfiep->bm_align = DWC_ALLOC(sizeof(ddma_align_buffer_setup_t));
+ if (NULL == cfiep->bm_align) {
+ CFI_INFO
+ ("Failed to allocate memory for Alignment feature value\n");
+ DWC_FREE(cfiep->bm_sg);
+ DWC_FREE(cfiep->bm_concat);
+ return -DWC_E_NO_MEMORY;
+ }
+ dwc_memset(cfiep->bm_align, 0, sizeof(ddma_align_buffer_setup_t));
+
+ return retval;
+}
+
+/**
+ * The callback function that notifies the CFI on the activation of
+ * an endpoint in the PCD. The following steps are done in this function:
+ *
+ * Create a dynamically allocated cfi_ep_t object (a CFI wrapper to the PCD's
+ * active endpoint)
+ * Create MAX_DMA_DESCS_PER_EP count DMA Descriptors for the EP
+ * Set the Buffer Mode to standard
+ * Initialize the default values for all EP modes (SG, Circular, Concat, Align)
+ * Add the cfi_ep_t object to the list of active endpoints in the CFI object
+ */
+static int cfi_ep_enable(struct cfiobject *cfi, struct dwc_otg_pcd *pcd,
+ struct dwc_otg_pcd_ep *ep)
+{
+ cfi_ep_t *cfiep;
+ int retval = -DWC_E_NOT_SUPPORTED;
+
+ CFI_INFO("%s: epname=%s; epnum=0x%02x\n", __func__,
+ "EP_" /*ep->ep.name */ , ep->desc->bEndpointAddress);
+ /* MAS - Check whether this endpoint already is in the list */
+ cfiep = get_cfi_ep_by_pcd_ep(cfi, ep);
+
+ if (NULL == cfiep) {
+ /* Allocate a cfi_ep_t object */
+ cfiep = DWC_ALLOC(sizeof(cfi_ep_t));
+ if (NULL == cfiep) {
+ CFI_INFO
+ ("Unable to allocate memory for <cfiep> in function %s\n",
+ __func__);
+ return -DWC_E_NO_MEMORY;
+ }
+ dwc_memset(cfiep, 0, sizeof(cfi_ep_t));
+
+ /* Save the dwc_otg_pcd_ep pointer in the cfiep object */
+ cfiep->ep = ep;
+
+ /* Allocate the DMA Descriptors chain of MAX_DMA_DESCS_PER_EP count */
+ ep->dwc_ep.descs =
+ DWC_DMA_ALLOC(MAX_DMA_DESCS_PER_EP *
+ sizeof(dwc_otg_dma_desc_t),
+ &ep->dwc_ep.descs_dma_addr);
+
+ if (NULL == ep->dwc_ep.descs) {
+ DWC_FREE(cfiep);
+ return -DWC_E_NO_MEMORY;
+ }
+
+ DWC_LIST_INIT(&cfiep->lh);
+
+ /* Set the buffer mode to BM_STANDARD. It will be modified
+ * when building descriptors for a specific buffer mode */
+ ep->dwc_ep.buff_mode = BM_STANDARD;
+
+ /* Create and initialize the default values for this EP's Buffer modes */
+ if ((retval = cfi_ep_init_defaults(pcd, cfiep)) < 0)
+ return retval;
+
+ /* Add the cfi_ep_t object to the CFI object's list of active endpoints */
+ DWC_LIST_INSERT_TAIL(&cfi->active_eps, &cfiep->lh);
+ retval = 0;
+ } else { /* The sought EP already is in the list */
+ CFI_INFO("%s: The sought EP already is in the list\n",
+ __func__);
+ }
+
+ return retval;
+}
+
+/**
+ * This function is called when the data stage of a 3-stage Control Write request
+ * is complete.
+ *
+ */
+static int cfi_ctrl_write_complete(struct cfiobject *cfi,
+ struct dwc_otg_pcd *pcd)
+{
+ uint32_t addr, reg_value;
+ uint16_t wIndex, wValue;
+ uint8_t bRequest;
+ uint8_t *buf = cfi->buf_out.buf;
+ //struct usb_ctrlrequest *ctrl_req = &cfi->ctrl_req_saved;
+ struct cfi_usb_ctrlrequest *ctrl_req = &cfi->ctrl_req;
+ int retval = -DWC_E_NOT_SUPPORTED;
+
+ CFI_INFO("%s\n", __func__);
+
+ bRequest = ctrl_req->bRequest;
+ wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex);
+ wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue);
+
+ /*
+ * Save the pointer to the data stage in the ctrl_req's <data> field.
+ * The request should be already saved in the command stage by now.
+ */
+ ctrl_req->data = cfi->buf_out.buf;
+ cfi->need_status_in_complete = 0;
+ cfi->need_gadget_att = 0;
+
+ switch (bRequest) {
+ case VEN_CORE_WRITE_REGISTER:
+ /* The buffer contains raw data of the new value for the register */
+ reg_value = *((uint32_t *) buf);
+ if (wValue == 0) {
+ addr = 0;
+ //addr = (uint32_t) pcd->otg_dev->os_dep.base;
+ addr += wIndex;
+ } else {
+ addr = (wValue << 16) | wIndex;
+ }
+
+ //writel(reg_value, addr);
+
+ retval = 0;
+ cfi->need_status_in_complete = 1;
+ break;
+
+ case VEN_CORE_SET_FEATURE:
+ /* The buffer contains raw data of the new value of the feature */
+ retval = cfi_set_feature_value(pcd);
+ if (retval < 0)
+ return retval;
+
+ cfi->need_status_in_complete = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * This function builds the DMA descriptors for the SG buffer mode.
+ */
+static void cfi_build_sg_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
+ dwc_otg_pcd_request_t * req)
+{
+ struct dwc_otg_pcd_ep *ep = cfiep->ep;
+ ddma_sg_buffer_setup_t *sgval = cfiep->bm_sg;
+ struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs;
+ struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs;
+ dma_addr_t buff_addr = req->dma;
+ int i;
+ uint32_t txsize, off;
+
+ txsize = sgval->wSize;
+ off = sgval->bOffset;
+
+// CFI_INFO("%s: %s TXSIZE=0x%08x; OFFSET=0x%08x\n",
+// __func__, cfiep->ep->ep.name, txsize, off);
+
+ for (i = 0; i < sgval->bCount; i++) {
+ desc->status.b.bs = BS_HOST_BUSY;
+ desc->buf = buff_addr;
+ desc->status.b.l = 0;
+ desc->status.b.ioc = 0;
+ desc->status.b.sp = 0;
+ desc->status.b.bytes = txsize;
+ desc->status.b.bs = BS_HOST_READY;
+
+ /* Set the next address of the buffer */
+ buff_addr += txsize + off;
+ desc_last = desc;
+ desc++;
+ }
+
+ /* Set the last, ioc and sp bits on the Last DMA Descriptor */
+ desc_last->status.b.l = 1;
+ desc_last->status.b.ioc = 1;
+ desc_last->status.b.sp = ep->dwc_ep.sent_zlp;
+ /* Save the last DMA descriptor pointer */
+ cfiep->dma_desc_last = desc_last;
+ cfiep->desc_count = sgval->bCount;
+}
+
+/**
+ * This function builds the DMA descriptors for the Concatenation buffer mode.
+ */
+static void cfi_build_concat_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
+ dwc_otg_pcd_request_t * req)
+{
+ struct dwc_otg_pcd_ep *ep = cfiep->ep;
+ ddma_concat_buffer_setup_t *concatval = cfiep->bm_concat;
+ struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs;
+ struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs;
+ dma_addr_t buff_addr = req->dma;
+ int i;
+ uint16_t *txsize;
+
+ txsize = concatval->wTxBytes;
+
+ for (i = 0; i < concatval->hdr.bDescCount; i++) {
+ desc->buf = buff_addr;
+ desc->status.b.bs = BS_HOST_BUSY;
+ desc->status.b.l = 0;
+ desc->status.b.ioc = 0;
+ desc->status.b.sp = 0;
+ desc->status.b.bytes = *txsize;
+ desc->status.b.bs = BS_HOST_READY;
+
+ txsize++;
+ /* Set the next address of the buffer */
+ buff_addr += UGETW(ep->desc->wMaxPacketSize);
+ desc_last = desc;
+ desc++;
+ }
+
+ /* Set the last, ioc and sp bits on the Last DMA Descriptor */
+ desc_last->status.b.l = 1;
+ desc_last->status.b.ioc = 1;
+ desc_last->status.b.sp = ep->dwc_ep.sent_zlp;
+ cfiep->dma_desc_last = desc_last;
+ cfiep->desc_count = concatval->hdr.bDescCount;
+}
+
+/**
+ * This function builds the DMA descriptors for the Circular buffer mode
+ */
+static void cfi_build_circ_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
+ dwc_otg_pcd_request_t * req)
+{
+ /* @todo: MAS - add implementation when this feature needs to be tested */
+}
+
+/**
+ * This function builds the DMA descriptors for the Alignment buffer mode
+ */
+static void cfi_build_align_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
+ dwc_otg_pcd_request_t * req)
+{
+ struct dwc_otg_pcd_ep *ep = cfiep->ep;
+ ddma_align_buffer_setup_t *alignval = cfiep->bm_align;
+ struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs;
+ dma_addr_t buff_addr = req->dma;
+
+ desc->status.b.bs = BS_HOST_BUSY;
+ desc->status.b.l = 1;
+ desc->status.b.ioc = 1;
+ desc->status.b.sp = ep->dwc_ep.sent_zlp;
+ desc->status.b.bytes = req->length;
+ /* Adjust the buffer alignment */
+ desc->buf = (buff_addr + alignval->bAlign);
+ desc->status.b.bs = BS_HOST_READY;
+ cfiep->dma_desc_last = desc;
+ cfiep->desc_count = 1;
+}
+
+/**
+ * This function builds the DMA descriptors chain for different modes of the
+ * buffer setup of an endpoint.
+ */
+static void cfi_build_descriptors(struct cfiobject *cfi,
+ struct dwc_otg_pcd *pcd,
+ struct dwc_otg_pcd_ep *ep,
+ dwc_otg_pcd_request_t * req)
+{
+ cfi_ep_t *cfiep;
+
+ /* Get the cfiep by the dwc_otg_pcd_ep */
+ cfiep = get_cfi_ep_by_pcd_ep(cfi, ep);
+ if (NULL == cfiep) {
+ CFI_INFO("%s: Unable to find a matching active endpoint\n",
+ __func__);
+ return;
+ }
+
+ cfiep->xfer_len = req->length;
+
+ /* Iterate through all the DMA descriptors */
+ switch (cfiep->ep->dwc_ep.buff_mode) {
+ case BM_SG:
+ cfi_build_sg_descs(cfi, cfiep, req);
+ break;
+
+ case BM_CONCAT:
+ cfi_build_concat_descs(cfi, cfiep, req);
+ break;
+
+ case BM_CIRCULAR:
+ cfi_build_circ_descs(cfi, cfiep, req);
+ break;
+
+ case BM_ALIGN:
+ cfi_build_align_descs(cfi, cfiep, req);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * Allocate DMA buffer for different Buffer modes.
+ */
+static void *cfi_ep_alloc_buf(struct cfiobject *cfi, struct dwc_otg_pcd *pcd,
+ struct dwc_otg_pcd_ep *ep, dma_addr_t * dma,
+ unsigned size, gfp_t flags)
+{
+ return DWC_DMA_ALLOC(size, dma);
+}
+
+/**
+ * This function initializes the CFI object.
+ */
+int init_cfi(cfiobject_t * cfiobj)
+{
+ CFI_INFO("%s\n", __func__);
+
+ /* Allocate a buffer for IN XFERs */
+ cfiobj->buf_in.buf =
+ DWC_DMA_ALLOC(CFI_IN_BUF_LEN, &cfiobj->buf_in.addr);
+ if (NULL == cfiobj->buf_in.buf) {
+ CFI_INFO("Unable to allocate buffer for INs\n");
+ return -DWC_E_NO_MEMORY;
+ }
+
+ /* Allocate a buffer for OUT XFERs */
+ cfiobj->buf_out.buf =
+ DWC_DMA_ALLOC(CFI_OUT_BUF_LEN, &cfiobj->buf_out.addr);
+ if (NULL == cfiobj->buf_out.buf) {
+ CFI_INFO("Unable to allocate buffer for OUT\n");
+ return -DWC_E_NO_MEMORY;
+ }
+
+ /* Initialize the callback function pointers */
+ cfiobj->ops.release = cfi_release;
+ cfiobj->ops.ep_enable = cfi_ep_enable;
+ cfiobj->ops.ctrl_write_complete = cfi_ctrl_write_complete;
+ cfiobj->ops.build_descriptors = cfi_build_descriptors;
+ cfiobj->ops.ep_alloc_buf = cfi_ep_alloc_buf;
+
+ /* Initialize the list of active endpoints in the CFI object */
+ DWC_LIST_INIT(&cfiobj->active_eps);
+
+ return 0;
+}
+
+/**
+ * This function reads the required feature's current value into the buffer
+ *
+ * @retval: Returns negative as error, or the data length of the feature
+ */
+static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen,
+ struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *ctrl_req)
+{
+ int retval = -DWC_E_NOT_SUPPORTED;
+ struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd);
+ uint16_t dfifo, rxfifo, txfifo;
+
+ switch (ctrl_req->wIndex) {
+ /* Whether the DDMA is enabled or not */
+ case FT_ID_DMA_MODE:
+ *buf = (coreif->dma_enable && coreif->dma_desc_enable) ? 1 : 0;
+ retval = 1;
+ break;
+
+ case FT_ID_DMA_BUFFER_SETUP:
+ retval = cfi_ep_get_sg_val(buf, pcd, ctrl_req);
+ break;
+
+ case FT_ID_DMA_BUFF_ALIGN:
+ retval = cfi_ep_get_align_val(buf, pcd, ctrl_req);
+ break;
+
+ case FT_ID_DMA_CONCAT_SETUP:
+ retval = cfi_ep_get_concat_val(buf, pcd, ctrl_req);
+ break;
+
+ case FT_ID_DMA_CIRCULAR:
+ CFI_INFO("GetFeature value (FT_ID_DMA_CIRCULAR)\n");
+ break;
+
+ case FT_ID_THRESHOLD_SETUP:
+ CFI_INFO("GetFeature value (FT_ID_THRESHOLD_SETUP)\n");
+ break;
+
+ case FT_ID_DFIFO_DEPTH:
+ dfifo = get_dfifo_size(coreif);
+ *((uint16_t *) buf) = dfifo;
+ retval = sizeof(uint16_t);
+ break;
+
+ case FT_ID_TX_FIFO_DEPTH:
+ retval = get_txfifo_size(pcd, ctrl_req->wValue);
+ if (retval >= 0) {
+ txfifo = retval;
+ *((uint16_t *) buf) = txfifo;
+ retval = sizeof(uint16_t);
+ }
+ break;
+
+ case FT_ID_RX_FIFO_DEPTH:
+ retval = get_rxfifo_size(coreif, ctrl_req->wValue);
+ if (retval >= 0) {
+ rxfifo = retval;
+ *((uint16_t *) buf) = rxfifo;
+ retval = sizeof(uint16_t);
+ }
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * This function resets the SG for the specified EP to its default value
+ */
+static int cfi_reset_sg_val(cfi_ep_t * cfiep)
+{
+ dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t));
+ return 0;
+}
+
+/**
+ * This function resets the Alignment for the specified EP to its default value
+ */
+static int cfi_reset_align_val(cfi_ep_t * cfiep)
+{
+ dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t));
+ return 0;
+}
+
+/**
+ * This function resets the Concatenation for the specified EP to its default value
+ * This function will also set the value of the wTxBytes field to NULL after
+ * freeing the memory previously allocated for this field.
+ */
+static int cfi_reset_concat_val(cfi_ep_t * cfiep)
+{
+ /* First we need to free the wTxBytes field */
+ if (cfiep->bm_concat->wTxBytes) {
+ DWC_FREE(cfiep->bm_concat->wTxBytes);
+ cfiep->bm_concat->wTxBytes = NULL;
+ }
+
+ dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t));
+ return 0;
+}
+
+/**
+ * This function resets all the buffer setups of the specified endpoint
+ */
+static int cfi_ep_reset_all_setup_vals(cfi_ep_t * cfiep)
+{
+ cfi_reset_sg_val(cfiep);
+ cfi_reset_align_val(cfiep);
+ cfi_reset_concat_val(cfiep);
+ return 0;
+}
+
+static int cfi_handle_reset_fifo_val(struct dwc_otg_pcd *pcd, uint8_t ep_addr,
+ uint8_t rx_rst, uint8_t tx_rst)
+{
+ int retval = -DWC_E_INVALID;
+ uint16_t tx_siz[15];
+ uint16_t rx_siz = 0;
+ dwc_otg_pcd_ep_t *ep = NULL;
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params;
+
+ if (rx_rst) {
+ rx_siz = params->dev_rx_fifo_size;
+ params->dev_rx_fifo_size = GET_CORE_IF(pcd)->init_rxfsiz;
+ }
+
+ if (tx_rst) {
+ if (ep_addr == 0) {
+ int i;
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+ tx_siz[i] =
+ core_if->core_params->dev_tx_fifo_size[i];
+ core_if->core_params->dev_tx_fifo_size[i] =
+ core_if->init_txfsiz[i];
+ }
+ } else {
+
+ ep = get_ep_by_addr(pcd, ep_addr);
+
+ if (NULL == ep) {
+ CFI_INFO
+ ("%s: Unable to get the endpoint addr=0x%02x\n",
+ __func__, ep_addr);
+ return -DWC_E_INVALID;
+ }
+
+ tx_siz[0] =
+ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num -
+ 1];
+ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] =
+ GET_CORE_IF(pcd)->init_txfsiz[ep->
+ dwc_ep.tx_fifo_num -
+ 1];
+ }
+ }
+
+ if (resize_fifos(GET_CORE_IF(pcd))) {
+ retval = 0;
+ } else {
+ CFI_INFO
+ ("%s: Error resetting the feature Reset All(FIFO size)\n",
+ __func__);
+ if (rx_rst) {
+ params->dev_rx_fifo_size = rx_siz;
+ }
+
+ if (tx_rst) {
+ if (ep_addr == 0) {
+ int i;
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps;
+ i++) {
+ core_if->
+ core_params->dev_tx_fifo_size[i] =
+ tx_siz[i];
+ }
+ } else {
+ params->dev_tx_fifo_size[ep->
+ dwc_ep.tx_fifo_num -
+ 1] = tx_siz[0];
+ }
+ }
+ retval = -DWC_E_INVALID;
+ }
+ return retval;
+}
+
+static int cfi_handle_reset_all(struct dwc_otg_pcd *pcd, uint8_t addr)
+{
+ int retval = 0;
+ cfi_ep_t *cfiep;
+ cfiobject_t *cfi = pcd->cfi;
+ dwc_list_link_t *tmp;
+
+ retval = cfi_handle_reset_fifo_val(pcd, addr, 1, 1);
+ if (retval < 0) {
+ return retval;
+ }
+
+ /* If the EP address is known then reset the features for only that EP */
+ if (addr) {
+ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
+ if (NULL == cfiep) {
+ CFI_INFO("%s: Error getting the EP address 0x%02x\n",
+ __func__, addr);
+ return -DWC_E_INVALID;
+ }
+ retval = cfi_ep_reset_all_setup_vals(cfiep);
+ cfiep->ep->dwc_ep.buff_mode = BM_STANDARD;
+ }
+ /* Otherwise (wValue == 0), reset all features of all EP's */
+ else {
+ /* Traverse all the active EP's and reset the feature(s) value(s) */
+ //list_for_each_entry(cfiep, &cfi->active_eps, lh) {
+ DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+ retval = cfi_ep_reset_all_setup_vals(cfiep);
+ cfiep->ep->dwc_ep.buff_mode = BM_STANDARD;
+ if (retval < 0) {
+ CFI_INFO
+ ("%s: Error resetting the feature Reset All\n",
+ __func__);
+ return retval;
+ }
+ }
+ }
+ return retval;
+}
+
+static int cfi_handle_reset_dma_buff_setup(struct dwc_otg_pcd *pcd,
+ uint8_t addr)
+{
+ int retval = 0;
+ cfi_ep_t *cfiep;
+ cfiobject_t *cfi = pcd->cfi;
+ dwc_list_link_t *tmp;
+
+ /* If the EP address is known then reset the features for only that EP */
+ if (addr) {
+ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
+ if (NULL == cfiep) {
+ CFI_INFO("%s: Error getting the EP address 0x%02x\n",
+ __func__, addr);
+ return -DWC_E_INVALID;
+ }
+ retval = cfi_reset_sg_val(cfiep);
+ }
+ /* Otherwise (wValue == 0), reset all features of all EP's */
+ else {
+ /* Traverse all the active EP's and reset the feature(s) value(s) */
+ //list_for_each_entry(cfiep, &cfi->active_eps, lh) {
+ DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+ retval = cfi_reset_sg_val(cfiep);
+ if (retval < 0) {
+ CFI_INFO
+ ("%s: Error resetting the feature Buffer Setup\n",
+ __func__);
+ return retval;
+ }
+ }
+ }
+ return retval;
+}
+
+static int cfi_handle_reset_concat_val(struct dwc_otg_pcd *pcd, uint8_t addr)
+{
+ int retval = 0;
+ cfi_ep_t *cfiep;
+ cfiobject_t *cfi = pcd->cfi;
+ dwc_list_link_t *tmp;
+
+ /* If the EP address is known then reset the features for only that EP */
+ if (addr) {
+ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
+ if (NULL == cfiep) {
+ CFI_INFO("%s: Error getting the EP address 0x%02x\n",
+ __func__, addr);
+ return -DWC_E_INVALID;
+ }
+ retval = cfi_reset_concat_val(cfiep);
+ }
+ /* Otherwise (wValue == 0), reset all features of all EP's */
+ else {
+ /* Traverse all the active EP's and reset the feature(s) value(s) */
+ //list_for_each_entry(cfiep, &cfi->active_eps, lh) {
+ DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+ retval = cfi_reset_concat_val(cfiep);
+ if (retval < 0) {
+ CFI_INFO
+ ("%s: Error resetting the feature Concatenation Value\n",
+ __func__);
+ return retval;
+ }
+ }
+ }
+ return retval;
+}
+
+static int cfi_handle_reset_align_val(struct dwc_otg_pcd *pcd, uint8_t addr)
+{
+ int retval = 0;
+ cfi_ep_t *cfiep;
+ cfiobject_t *cfi = pcd->cfi;
+ dwc_list_link_t *tmp;
+
+ /* If the EP address is known then reset the features for only that EP */
+ if (addr) {
+ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
+ if (NULL == cfiep) {
+ CFI_INFO("%s: Error getting the EP address 0x%02x\n",
+ __func__, addr);
+ return -DWC_E_INVALID;
+ }
+ retval = cfi_reset_align_val(cfiep);
+ }
+ /* Otherwise (wValue == 0), reset all features of all EP's */
+ else {
+ /* Traverse all the active EP's and reset the feature(s) value(s) */
+ //list_for_each_entry(cfiep, &cfi->active_eps, lh) {
+ DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+ retval = cfi_reset_align_val(cfiep);
+ if (retval < 0) {
+ CFI_INFO
+ ("%s: Error resetting the feature Aliignment Value\n",
+ __func__);
+ return retval;
+ }
+ }
+ }
+ return retval;
+
+}
+
+static int cfi_preproc_reset(struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *req)
+{
+ int retval = 0;
+
+ switch (req->wIndex) {
+ case 0:
+ /* Reset all features */
+ retval = cfi_handle_reset_all(pcd, req->wValue & 0xff);
+ break;
+
+ case FT_ID_DMA_BUFFER_SETUP:
+ /* Reset the SG buffer setup */
+ retval =
+ cfi_handle_reset_dma_buff_setup(pcd, req->wValue & 0xff);
+ break;
+
+ case FT_ID_DMA_CONCAT_SETUP:
+ /* Reset the Concatenation buffer setup */
+ retval = cfi_handle_reset_concat_val(pcd, req->wValue & 0xff);
+ break;
+
+ case FT_ID_DMA_BUFF_ALIGN:
+ /* Reset the Alignment buffer setup */
+ retval = cfi_handle_reset_align_val(pcd, req->wValue & 0xff);
+ break;
+
+ case FT_ID_TX_FIFO_DEPTH:
+ retval =
+ cfi_handle_reset_fifo_val(pcd, req->wValue & 0xff, 0, 1);
+ pcd->cfi->need_gadget_att = 0;
+ break;
+
+ case FT_ID_RX_FIFO_DEPTH:
+ retval = cfi_handle_reset_fifo_val(pcd, 0, 1, 0);
+ pcd->cfi->need_gadget_att = 0;
+ break;
+ default:
+ break;
+ }
+ return retval;
+}
+
+/**
+ * This function sets a new value for the SG buffer setup.
+ */
+static int cfi_ep_set_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd)
+{
+ uint8_t inaddr, outaddr;
+ cfi_ep_t *epin, *epout;
+ ddma_sg_buffer_setup_t *psgval;
+ uint32_t desccount, size;
+
+ CFI_INFO("%s\n", __func__);
+
+ psgval = (ddma_sg_buffer_setup_t *) buf;
+ desccount = (uint32_t) psgval->bCount;
+ size = (uint32_t) psgval->wSize;
+
+ /* Check the DMA descriptor count */
+ if ((desccount > MAX_DMA_DESCS_PER_EP) || (desccount == 0)) {
+ CFI_INFO
+ ("%s: The count of DMA Descriptors should be between 1 and %d\n",
+ __func__, MAX_DMA_DESCS_PER_EP);
+ return -DWC_E_INVALID;
+ }
+
+ /* Check the DMA descriptor count */
+
+ if (size == 0) {
+
+ CFI_INFO("%s: The transfer size should be at least 1 byte\n",
+ __func__);
+
+ return -DWC_E_INVALID;
+
+ }
+
+ inaddr = psgval->bInEndpointAddress;
+ outaddr = psgval->bOutEndpointAddress;
+
+ epin = get_cfi_ep_by_addr(pcd->cfi, inaddr);
+ epout = get_cfi_ep_by_addr(pcd->cfi, outaddr);
+
+ if (NULL == epin || NULL == epout) {
+ CFI_INFO
+ ("%s: Unable to get the endpoints inaddr=0x%02x outaddr=0x%02x\n",
+ __func__, inaddr, outaddr);
+ return -DWC_E_INVALID;
+ }
+
+ epin->ep->dwc_ep.buff_mode = BM_SG;
+ dwc_memcpy(epin->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t));
+
+ epout->ep->dwc_ep.buff_mode = BM_SG;
+ dwc_memcpy(epout->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t));
+
+ return 0;
+}
+
+/**
+ * This function sets a new value for the buffer Alignment setup.
+ */
+static int cfi_ep_set_alignment_val(uint8_t * buf, struct dwc_otg_pcd *pcd)
+{
+ cfi_ep_t *ep;
+ uint8_t addr;
+ ddma_align_buffer_setup_t *palignval;
+
+ palignval = (ddma_align_buffer_setup_t *) buf;
+ addr = palignval->bEndpointAddress;
+
+ ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+
+ if (NULL == ep) {
+ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
+ __func__, addr);
+ return -DWC_E_INVALID;
+ }
+
+ ep->ep->dwc_ep.buff_mode = BM_ALIGN;
+ dwc_memcpy(ep->bm_align, palignval, sizeof(ddma_align_buffer_setup_t));
+
+ return 0;
+}
+
+/**
+ * This function sets a new value for the Concatenation buffer setup.
+ */
+static int cfi_ep_set_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd)
+{
+ uint8_t addr;
+ cfi_ep_t *ep;
+ struct _ddma_concat_buffer_setup_hdr *pConcatValHdr;
+ uint16_t *pVals;
+ uint32_t desccount;
+ int i;
+ uint16_t mps;
+
+ pConcatValHdr = (struct _ddma_concat_buffer_setup_hdr *)buf;
+ desccount = (uint32_t) pConcatValHdr->bDescCount;
+ pVals = (uint16_t *) (buf + BS_CONCAT_VAL_HDR_LEN);
+
+ /* Check the DMA descriptor count */
+ if (desccount > MAX_DMA_DESCS_PER_EP) {
+ CFI_INFO("%s: Maximum DMA Descriptor count should be %d\n",
+ __func__, MAX_DMA_DESCS_PER_EP);
+ return -DWC_E_INVALID;
+ }
+
+ addr = pConcatValHdr->bEndpointAddress;
+ ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+ if (NULL == ep) {
+ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
+ __func__, addr);
+ return -DWC_E_INVALID;
+ }
+
+ mps = UGETW(ep->ep->desc->wMaxPacketSize);
+
+#if 0
+ for (i = 0; i < desccount; i++) {
+ CFI_INFO("%s: wTxSize[%d]=0x%04x\n", __func__, i, pVals[i]);
+ }
+ CFI_INFO("%s: epname=%s; mps=%d\n", __func__, ep->ep->ep.name, mps);
+#endif
+
+ /* Check the wTxSizes to be less than or equal to the mps */
+ for (i = 0; i < desccount; i++) {
+ if (pVals[i] > mps) {
+ CFI_INFO
+ ("%s: ERROR - the wTxSize[%d] should be <= MPS (wTxSize=%d)\n",
+ __func__, i, pVals[i]);
+ return -DWC_E_INVALID;
+ }
+ }
+
+ ep->ep->dwc_ep.buff_mode = BM_CONCAT;
+ dwc_memcpy(ep->bm_concat, pConcatValHdr, BS_CONCAT_VAL_HDR_LEN);
+
+ /* Free the previously allocated storage for the wTxBytes */
+ if (ep->bm_concat->wTxBytes) {
+ DWC_FREE(ep->bm_concat->wTxBytes);
+ }
+
+ /* Allocate a new storage for the wTxBytes field */
+ ep->bm_concat->wTxBytes =
+ DWC_ALLOC(sizeof(uint16_t) * pConcatValHdr->bDescCount);
+ if (NULL == ep->bm_concat->wTxBytes) {
+ CFI_INFO("%s: Unable to allocate memory\n", __func__);
+ return -DWC_E_NO_MEMORY;
+ }
+
+ /* Copy the new values into the wTxBytes filed */
+ dwc_memcpy(ep->bm_concat->wTxBytes, buf + BS_CONCAT_VAL_HDR_LEN,
+ sizeof(uint16_t) * pConcatValHdr->bDescCount);
+
+ return 0;
+}
+
+/**
+ * This function calculates the total of all FIFO sizes
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return The total of data FIFO sizes.
+ *
+ */
+static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_core_params_t *params = core_if->core_params;
+ uint16_t dfifo_total = 0;
+ int i;
+
+ /* The shared RxFIFO size */
+ dfifo_total =
+ params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size;
+
+ /* Add up each TxFIFO size to the total */
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+ dfifo_total += params->dev_tx_fifo_size[i];
+ }
+
+ return dfifo_total;
+}
+
+/**
+ * This function returns Rx FIFO size
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return The total of data FIFO sizes.
+ *
+ */
+static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue)
+{
+ switch (wValue >> 8) {
+ case 0:
+ return (core_if->pwron_rxfsiz <
+ 32768) ? core_if->pwron_rxfsiz : 32768;
+ break;
+ case 1:
+ return core_if->core_params->dev_rx_fifo_size;
+ break;
+ default:
+ return -DWC_E_INVALID;
+ break;
+ }
+}
+
+/**
+ * This function returns Tx FIFO size for IN EP
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return The total of data FIFO sizes.
+ *
+ */
+static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue)
+{
+ dwc_otg_pcd_ep_t *ep;
+
+ ep = get_ep_by_addr(pcd, wValue & 0xff);
+
+ if (NULL == ep) {
+ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
+ __func__, wValue & 0xff);
+ return -DWC_E_INVALID;
+ }
+
+ if (!ep->dwc_ep.is_in) {
+ CFI_INFO
+ ("%s: No Tx FIFO assingned to the Out endpoint addr=0x%02x\n",
+ __func__, wValue & 0xff);
+ return -DWC_E_INVALID;
+ }
+
+ switch (wValue >> 8) {
+ case 0:
+ return (GET_CORE_IF(pcd)->pwron_txfsiz
+ [ep->dwc_ep.tx_fifo_num - 1] <
+ 768) ? GET_CORE_IF(pcd)->pwron_txfsiz[ep->
+ dwc_ep.tx_fifo_num
+ - 1] : 32768;
+ break;
+ case 1:
+ return GET_CORE_IF(pcd)->core_params->
+ dev_tx_fifo_size[ep->dwc_ep.num - 1];
+ break;
+ default:
+ return -DWC_E_INVALID;
+ break;
+ }
+}
+
+/**
+ * This function checks if the submitted combination of
+ * device mode FIFO sizes is possible or not.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return 1 if possible, 0 otherwise.
+ *
+ */
+static uint8_t check_fifo_sizes(dwc_otg_core_if_t * core_if)
+{
+ uint16_t dfifo_actual = 0;
+ dwc_otg_core_params_t *params = core_if->core_params;
+ uint16_t start_addr = 0;
+ int i;
+
+ dfifo_actual =
+ params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size;
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+ dfifo_actual += params->dev_tx_fifo_size[i];
+ }
+
+ if (dfifo_actual > core_if->total_fifo_size) {
+ return 0;
+ }
+
+ if (params->dev_rx_fifo_size > 32768 || params->dev_rx_fifo_size < 16)
+ return 0;
+
+ if (params->dev_nperio_tx_fifo_size > 32768
+ || params->dev_nperio_tx_fifo_size < 16)
+ return 0;
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+
+ if (params->dev_tx_fifo_size[i] > 768
+ || params->dev_tx_fifo_size[i] < 4)
+ return 0;
+ }
+
+ if (params->dev_rx_fifo_size > core_if->pwron_rxfsiz)
+ return 0;
+ start_addr = params->dev_rx_fifo_size;
+
+ if (params->dev_nperio_tx_fifo_size > core_if->pwron_gnptxfsiz)
+ return 0;
+ start_addr += params->dev_nperio_tx_fifo_size;
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+
+ if (params->dev_tx_fifo_size[i] > core_if->pwron_txfsiz[i])
+ return 0;
+ start_addr += params->dev_tx_fifo_size[i];
+ }
+
+ return 1;
+}
+
+/**
+ * This function resizes Device mode FIFOs
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return 1 if successful, 0 otherwise
+ *
+ */
+static uint8_t resize_fifos(dwc_otg_core_if_t * core_if)
+{
+ int i = 0;
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ dwc_otg_core_params_t *params = core_if->core_params;
+ uint32_t rx_fifo_size;
+ fifosize_data_t nptxfifosize;
+ fifosize_data_t txfifosize[15];
+
+ uint32_t rx_fsz_bak;
+ uint32_t nptxfsz_bak;
+ uint32_t txfsz_bak[15];
+
+ uint16_t start_address;
+ uint8_t retval = 1;
+
+ if (!check_fifo_sizes(core_if)) {
+ return 0;
+ }
+
+ /* Configure data FIFO sizes */
+ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
+ rx_fsz_bak = DWC_READ_REG32(&global_regs->grxfsiz);
+ rx_fifo_size = params->dev_rx_fifo_size;
+ DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size);
+
+ /*
+ * Tx FIFOs These FIFOs are numbered from 1 to 15.
+ * Indexes of the FIFO size module parameters in the
+ * dev_tx_fifo_size array and the FIFO size registers in
+ * the dtxfsiz array run from 0 to 14.
+ */
+
+ /* Non-periodic Tx FIFO */
+ nptxfsz_bak = DWC_READ_REG32(&global_regs->gnptxfsiz);
+ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size;
+ start_address = 0x400;
+ nptxfifosize.b.startaddr = start_address;
+
+ DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32);
+
+ start_address += nptxfifosize.b.depth;
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+ txfsz_bak[i] = DWC_READ_REG32(&global_regs->dtxfsiz[i]);
+
+ txfifosize[i].b.depth = params->dev_tx_fifo_size[i];
+ txfifosize[i].b.startaddr = start_address;
+ DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
+ txfifosize[i].d32);
+
+ start_address += txfifosize[i].b.depth;
+ }
+
+ /** Check if register values are set correctly */
+ if (rx_fifo_size != DWC_READ_REG32(&global_regs->grxfsiz)) {
+ retval = 0;
+ }
+
+ if (nptxfifosize.d32 != DWC_READ_REG32(&global_regs->gnptxfsiz)) {
+ retval = 0;
+ }
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+ if (txfifosize[i].d32 !=
+ DWC_READ_REG32(&global_regs->dtxfsiz[i])) {
+ retval = 0;
+ }
+ }
+
+ /** If register values are not set correctly, reset old values */
+ if (retval == 0) {
+ DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fsz_bak);
+
+ /* Non-periodic Tx FIFO */
+ DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfsz_bak);
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+ DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
+ txfsz_bak[i]);
+ }
+ }
+ } else {
+ return 0;
+ }
+
+ /* Flush the FIFOs */
+ dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */
+ dwc_otg_flush_rx_fifo(core_if);
+
+ return retval;
+}
+
+/**
+ * This function sets a new value for the buffer Alignment setup.
+ */
+static int cfi_ep_set_tx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd)
+{
+ int retval;
+ uint32_t fsiz;
+ uint16_t size;
+ uint16_t ep_addr;
+ dwc_otg_pcd_ep_t *ep;
+ dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params;
+ tx_fifo_size_setup_t *ptxfifoval;
+
+ ptxfifoval = (tx_fifo_size_setup_t *) buf;
+ ep_addr = ptxfifoval->bEndpointAddress;
+ size = ptxfifoval->wDepth;
+
+ ep = get_ep_by_addr(pcd, ep_addr);
+
+ CFI_INFO
+ ("%s: Set Tx FIFO size: endpoint addr=0x%02x, depth=%d, FIFO Num=%d\n",
+ __func__, ep_addr, size, ep->dwc_ep.tx_fifo_num);
+
+ if (NULL == ep) {
+ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
+ __func__, ep_addr);
+ return -DWC_E_INVALID;
+ }
+
+ fsiz = params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1];
+ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = size;
+
+ if (resize_fifos(GET_CORE_IF(pcd))) {
+ retval = 0;
+ } else {
+ CFI_INFO
+ ("%s: Error setting the feature Tx FIFO Size for EP%d\n",
+ __func__, ep_addr);
+ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = fsiz;
+ retval = -DWC_E_INVALID;
+ }
+
+ return retval;
+}
+
+/**
+ * This function sets a new value for the buffer Alignment setup.
+ */
+static int cfi_set_rx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd)
+{
+ int retval;
+ uint32_t fsiz;
+ uint16_t size;
+ dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params;
+ rx_fifo_size_setup_t *prxfifoval;
+
+ prxfifoval = (rx_fifo_size_setup_t *) buf;
+ size = prxfifoval->wDepth;
+
+ fsiz = params->dev_rx_fifo_size;
+ params->dev_rx_fifo_size = size;
+
+ if (resize_fifos(GET_CORE_IF(pcd))) {
+ retval = 0;
+ } else {
+ CFI_INFO("%s: Error setting the feature Rx FIFO Size\n",
+ __func__);
+ params->dev_rx_fifo_size = fsiz;
+ retval = -DWC_E_INVALID;
+ }
+
+ return retval;
+}
+
+/**
+ * This function reads the SG of an EP's buffer setup into the buffer buf
+ */
+static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *req)
+{
+ int retval = -DWC_E_INVALID;
+ uint8_t addr;
+ cfi_ep_t *ep;
+
+ /* The Low Byte of the wValue contains a non-zero address of the endpoint */
+ addr = req->wValue & 0xFF;
+ if (addr == 0) /* The address should be non-zero */
+ return retval;
+
+ ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+ if (NULL == ep) {
+ CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n",
+ __func__, addr);
+ return retval;
+ }
+
+ dwc_memcpy(buf, ep->bm_sg, BS_SG_VAL_DESC_LEN);
+ retval = BS_SG_VAL_DESC_LEN;
+ return retval;
+}
+
+/**
+ * This function reads the Concatenation value of an EP's buffer mode into
+ * the buffer buf
+ */
+static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *req)
+{
+ int retval = -DWC_E_INVALID;
+ uint8_t addr;
+ cfi_ep_t *ep;
+ uint8_t desc_count;
+
+ /* The Low Byte of the wValue contains a non-zero address of the endpoint */
+ addr = req->wValue & 0xFF;
+ if (addr == 0) /* The address should be non-zero */
+ return retval;
+
+ ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+ if (NULL == ep) {
+ CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n",
+ __func__, addr);
+ return retval;
+ }
+
+ /* Copy the header to the buffer */
+ dwc_memcpy(buf, ep->bm_concat, BS_CONCAT_VAL_HDR_LEN);
+ /* Advance the buffer pointer by the header size */
+ buf += BS_CONCAT_VAL_HDR_LEN;
+
+ desc_count = ep->bm_concat->hdr.bDescCount;
+ /* Copy alll the wTxBytes to the buffer */
+ dwc_memcpy(buf, ep->bm_concat->wTxBytes, sizeof(uid16_t) * desc_count);
+
+ retval = BS_CONCAT_VAL_HDR_LEN + sizeof(uid16_t) * desc_count;
+ return retval;
+}
+
+/**
+ * This function reads the buffer Alignment value of an EP's buffer mode into
+ * the buffer buf
+ *
+ * @return The total number of bytes copied to the buffer or negative error code.
+ */
+static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+ struct cfi_usb_ctrlrequest *req)
+{
+ int retval = -DWC_E_INVALID;
+ uint8_t addr;
+ cfi_ep_t *ep;
+
+ /* The Low Byte of the wValue contains a non-zero address of the endpoint */
+ addr = req->wValue & 0xFF;
+ if (addr == 0) /* The address should be non-zero */
+ return retval;
+
+ ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+ if (NULL == ep) {
+ CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n",
+ __func__, addr);
+ return retval;
+ }
+
+ dwc_memcpy(buf, ep->bm_align, BS_ALIGN_VAL_HDR_LEN);
+ retval = BS_ALIGN_VAL_HDR_LEN;
+
+ return retval;
+}
+
+/**
+ * This function sets a new value for the specified feature
+ *
+ * @param pcd A pointer to the PCD object
+ *
+ * @return 0 if successful, negative error code otherwise to stall the DCE.
+ */
+static int cfi_set_feature_value(struct dwc_otg_pcd *pcd)
+{
+ int retval = -DWC_E_NOT_SUPPORTED;
+ uint16_t wIndex, wValue;
+ uint8_t bRequest;
+ struct dwc_otg_core_if *coreif;
+ cfiobject_t *cfi = pcd->cfi;
+ struct cfi_usb_ctrlrequest *ctrl_req;
+ uint8_t *buf;
+ ctrl_req = &cfi->ctrl_req;
+
+ buf = pcd->cfi->ctrl_req.data;
+
+ coreif = GET_CORE_IF(pcd);
+ bRequest = ctrl_req->bRequest;
+ wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex);
+ wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue);
+
+ /* See which feature is to be modified */
+ switch (wIndex) {
+ case FT_ID_DMA_BUFFER_SETUP:
+ /* Modify the feature */
+ if ((retval = cfi_ep_set_sg_val(buf, pcd)) < 0)
+ return retval;
+
+ /* And send this request to the gadget */
+ cfi->need_gadget_att = 1;
+ break;
+
+ case FT_ID_DMA_BUFF_ALIGN:
+ if ((retval = cfi_ep_set_alignment_val(buf, pcd)) < 0)
+ return retval;
+ cfi->need_gadget_att = 1;
+ break;
+
+ case FT_ID_DMA_CONCAT_SETUP:
+ /* Modify the feature */
+ if ((retval = cfi_ep_set_concat_val(buf, pcd)) < 0)
+ return retval;
+ cfi->need_gadget_att = 1;
+ break;
+
+ case FT_ID_DMA_CIRCULAR:
+ CFI_INFO("FT_ID_DMA_CIRCULAR\n");
+ break;
+
+ case FT_ID_THRESHOLD_SETUP:
+ CFI_INFO("FT_ID_THRESHOLD_SETUP\n");
+ break;
+
+ case FT_ID_DFIFO_DEPTH:
+ CFI_INFO("FT_ID_DFIFO_DEPTH\n");
+ break;
+
+ case FT_ID_TX_FIFO_DEPTH:
+ CFI_INFO("FT_ID_TX_FIFO_DEPTH\n");
+ if ((retval = cfi_ep_set_tx_fifo_val(buf, pcd)) < 0)
+ return retval;
+ cfi->need_gadget_att = 0;
+ break;
+
+ case FT_ID_RX_FIFO_DEPTH:
+ CFI_INFO("FT_ID_RX_FIFO_DEPTH\n");
+ if ((retval = cfi_set_rx_fifo_val(buf, pcd)) < 0)
+ return retval;
+ cfi->need_gadget_att = 0;
+ break;
+ }
+
+ return retval;
+}
+
+#endif //DWC_UTE_CFI
diff --git a/drivers/usb/dwc_otg/dwc_otg_cfi.h b/drivers/usb/dwc_otg/dwc_otg_cfi.h
new file mode 100644
index 0000000..cbccdab
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_cfi.h
@@ -0,0 +1,320 @@
+/* ==========================================================================
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_OTG_CFI_H__)
+#define __DWC_OTG_CFI_H__
+
+#include "dwc_otg_pcd.h"
+#include "dwc_cfi_common.h"
+
+/**
+ * @file
+ * This file contains the CFI related OTG PCD specific common constants,
+ * interfaces(functions and macros) and data structures.The CFI Protocol is an
+ * optional interface for internal testing purposes that a DUT may implement to
+ * support testing of configurable features.
+ *
+ */
+
+struct dwc_otg_pcd;
+struct dwc_otg_pcd_ep;
+
+/** OTG CFI Features (properties) ID constants */
+/** This is a request for all Core Features */
+#define FT_ID_DMA_MODE 0x0001
+#define FT_ID_DMA_BUFFER_SETUP 0x0002
+#define FT_ID_DMA_BUFF_ALIGN 0x0003
+#define FT_ID_DMA_CONCAT_SETUP 0x0004
+#define FT_ID_DMA_CIRCULAR 0x0005
+#define FT_ID_THRESHOLD_SETUP 0x0006
+#define FT_ID_DFIFO_DEPTH 0x0007
+#define FT_ID_TX_FIFO_DEPTH 0x0008
+#define FT_ID_RX_FIFO_DEPTH 0x0009
+
+/**********************************************************/
+#define CFI_INFO_DEF
+
+#ifdef CFI_INFO_DEF
+#define CFI_INFO(fmt...) DWC_PRINTF("CFI: " fmt);
+#else
+#define CFI_INFO(fmt...)
+#endif
+
+#define min(x,y) ({ \
+ x < y ? x : y; })
+
+#define max(x,y) ({ \
+ x > y ? x : y; })
+
+/**
+ * Descriptor DMA SG Buffer setup structure (SG buffer). This structure is
+ * also used for setting up a buffer for Circular DDMA.
+ */
+struct _ddma_sg_buffer_setup {
+#define BS_SG_VAL_DESC_LEN 6
+ /* The OUT EP address */
+ uint8_t bOutEndpointAddress;
+ /* The IN EP address */
+ uint8_t bInEndpointAddress;
+ /* Number of bytes to put between transfer segments (must be DWORD boundaries) */
+ uint8_t bOffset;
+ /* The number of transfer segments (a DMA descriptors per each segment) */
+ uint8_t bCount;
+ /* Size (in byte) of each transfer segment */
+ uint16_t wSize;
+} __attribute__ ((packed));
+typedef struct _ddma_sg_buffer_setup ddma_sg_buffer_setup_t;
+
+/** Descriptor DMA Concatenation Buffer setup structure */
+struct _ddma_concat_buffer_setup_hdr {
+#define BS_CONCAT_VAL_HDR_LEN 4
+ /* The endpoint for which the buffer is to be set up */
+ uint8_t bEndpointAddress;
+ /* The count of descriptors to be used */
+ uint8_t bDescCount;
+ /* The total size of the transfer */
+ uint16_t wSize;
+} __attribute__ ((packed));
+typedef struct _ddma_concat_buffer_setup_hdr ddma_concat_buffer_setup_hdr_t;
+
+/** Descriptor DMA Concatenation Buffer setup structure */
+struct _ddma_concat_buffer_setup {
+ /* The SG header */
+ ddma_concat_buffer_setup_hdr_t hdr;
+
+ /* The XFER sizes pointer (allocated dynamically) */
+ uint16_t *wTxBytes;
+} __attribute__ ((packed));
+typedef struct _ddma_concat_buffer_setup ddma_concat_buffer_setup_t;
+
+/** Descriptor DMA Alignment Buffer setup structure */
+struct _ddma_align_buffer_setup {
+#define BS_ALIGN_VAL_HDR_LEN 2
+ uint8_t bEndpointAddress;
+ uint8_t bAlign;
+} __attribute__ ((packed));
+typedef struct _ddma_align_buffer_setup ddma_align_buffer_setup_t;
+
+/** Transmit FIFO Size setup structure */
+struct _tx_fifo_size_setup {
+ uint8_t bEndpointAddress;
+ uint16_t wDepth;
+} __attribute__ ((packed));
+typedef struct _tx_fifo_size_setup tx_fifo_size_setup_t;
+
+/** Transmit FIFO Size setup structure */
+struct _rx_fifo_size_setup {
+ uint16_t wDepth;
+} __attribute__ ((packed));
+typedef struct _rx_fifo_size_setup rx_fifo_size_setup_t;
+
+/**
+ * struct cfi_usb_ctrlrequest - the CFI implementation of the struct usb_ctrlrequest
+ * This structure encapsulates the standard usb_ctrlrequest and adds a pointer
+ * to the data returned in the data stage of a 3-stage Control Write requests.
+ */
+struct cfi_usb_ctrlrequest {
+ uint8_t bRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+ uint8_t *data;
+} UPACKED;
+
+/*---------------------------------------------------------------------------*/
+
+/**
+ * The CFI wrapper of the enabled and activated dwc_otg_pcd_ep structures.
+ * This structure is used to store the buffer setup data for any
+ * enabled endpoint in the PCD.
+ */
+struct cfi_ep {
+ /* Entry for the list container */
+ dwc_list_link_t lh;
+ /* Pointer to the active PCD endpoint structure */
+ struct dwc_otg_pcd_ep *ep;
+ /* The last descriptor in the chain of DMA descriptors of the endpoint */
+ struct dwc_otg_dma_desc *dma_desc_last;
+ /* The SG feature value */
+ ddma_sg_buffer_setup_t *bm_sg;
+ /* The Circular feature value */
+ ddma_sg_buffer_setup_t *bm_circ;
+ /* The Concatenation feature value */
+ ddma_concat_buffer_setup_t *bm_concat;
+ /* The Alignment feature value */
+ ddma_align_buffer_setup_t *bm_align;
+ /* XFER length */
+ uint32_t xfer_len;
+ /*
+ * Count of DMA descriptors currently used.
+ * The total should not exceed the MAX_DMA_DESCS_PER_EP value
+ * defined in the dwc_otg_cil.h
+ */
+ uint32_t desc_count;
+};
+typedef struct cfi_ep cfi_ep_t;
+
+typedef struct cfi_dma_buff {
+#define CFI_IN_BUF_LEN 1024
+#define CFI_OUT_BUF_LEN 1024
+ dma_addr_t addr;
+ uint8_t *buf;
+} cfi_dma_buff_t;
+
+struct cfiobject;
+
+/**
+ * This is the interface for the CFI operations.
+ *
+ * @param ep_enable Called when any endpoint is enabled and activated.
+ * @param release Called when the CFI object is released and it needs to correctly
+ * deallocate the dynamic memory
+ * @param ctrl_write_complete Called when the data stage of the request is complete
+ */
+typedef struct cfi_ops {
+ int (*ep_enable) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd,
+ struct dwc_otg_pcd_ep * ep);
+ void *(*ep_alloc_buf) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd,
+ struct dwc_otg_pcd_ep * ep, dma_addr_t * dma,
+ unsigned size, gfp_t flags);
+ void (*release) (struct cfiobject * cfi);
+ int (*ctrl_write_complete) (struct cfiobject * cfi,
+ struct dwc_otg_pcd * pcd);
+ void (*build_descriptors) (struct cfiobject * cfi,
+ struct dwc_otg_pcd * pcd,
+ struct dwc_otg_pcd_ep * ep,
+ dwc_otg_pcd_request_t * req);
+} cfi_ops_t;
+
+struct cfiobject {
+ cfi_ops_t ops;
+ struct dwc_otg_pcd *pcd;
+ struct usb_gadget *gadget;
+
+ /* Buffers used to send/receive CFI-related request data */
+ cfi_dma_buff_t buf_in;
+ cfi_dma_buff_t buf_out;
+
+ /* CFI specific Control request wrapper */
+ struct cfi_usb_ctrlrequest ctrl_req;
+
+ /* The list of active EP's in the PCD of type cfi_ep_t */
+ dwc_list_link_t active_eps;
+
+ /* This flag shall control the propagation of a specific request
+ * to the gadget's processing routines.
+ * 0 - no gadget handling
+ * 1 - the gadget needs to know about this request (w/o completing a status
+ * phase - just return a 0 to the _setup callback)
+ */
+ uint8_t need_gadget_att;
+
+ /* Flag indicating whether the status IN phase needs to be
+ * completed by the PCD
+ */
+ uint8_t need_status_in_complete;
+};
+typedef struct cfiobject cfiobject_t;
+
+#define DUMP_MSG
+
+#if defined(DUMP_MSG)
+static inline void dump_msg(const u8 * buf, unsigned int length)
+{
+ unsigned int start, num, i;
+ char line[52], *p;
+
+ if (length >= 512)
+ return;
+
+ start = 0;
+ while (length > 0) {
+ num = min(length, 16u);
+ p = line;
+ for (i = 0; i < num; ++i) {
+ if (i == 8)
+ *p++ = ' ';
+ DWC_SPRINTF(p, " %02x", buf[i]);
+ p += 3;
+ }
+ *p = 0;
+ DWC_DEBUG("%6x: %s\n", start, line);
+ buf += num;
+ start += num;
+ length -= num;
+ }
+}
+#else
+static inline void dump_msg(const u8 * buf, unsigned int length)
+{
+}
+#endif
+
+/**
+ * This function returns a pointer to cfi_ep_t object with the addr address.
+ */
+static inline struct cfi_ep *get_cfi_ep_by_addr(struct cfiobject *cfi,
+ uint8_t addr)
+{
+ struct cfi_ep *pcfiep;
+ dwc_list_link_t *tmp;
+
+ DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+ pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+
+ if (pcfiep->ep->desc->bEndpointAddress == addr) {
+ return pcfiep;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * This function returns a pointer to cfi_ep_t object that matches
+ * the dwc_otg_pcd_ep object.
+ */
+static inline struct cfi_ep *get_cfi_ep_by_pcd_ep(struct cfiobject *cfi,
+ struct dwc_otg_pcd_ep *ep)
+{
+ struct cfi_ep *pcfiep = NULL;
+ dwc_list_link_t *tmp;
+
+ DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+ pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+ if (pcfiep->ep == ep) {
+ return pcfiep;
+ }
+ }
+ return NULL;
+}
+
+int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl);
+
+#endif /* (__DWC_OTG_CFI_H__) */
diff --git a/drivers/usb/dwc_otg/dwc_otg_cil.c b/drivers/usb/dwc_otg/dwc_otg_cil.c
new file mode 100644
index 0000000..84f1179
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_cil.c
@@ -0,0 +1,7049 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.c $
+ * $Revision: #189 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871160 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ *
+ * The Core Interface Layer provides basic services for accessing and
+ * managing the DWC_otg hardware. These services are used by both the
+ * Host Controller Driver and the Peripheral Controller Driver.
+ *
+ * The CIL manages the memory map for the core so that the HCD and PCD
+ * don't have to do this separately. It also handles basic tasks like
+ * reading/writing the registers and data FIFOs in the controller.
+ * Some of the data access functions provide encapsulation of several
+ * operations required to perform a task, such as writing multiple
+ * registers to start a transfer. Finally, the CIL performs basic
+ * services that are not specific to either the host or device modes
+ * of operation. These services include management of the OTG Host
+ * Negotiation Protocol (HNP) and Session Request Protocol (SRP). A
+ * Diagnostic API is also provided to allow testing of the controller
+ * hardware.
+ *
+ * The Core Interface Layer has the following requirements:
+ * - Provides basic controller operations.
+ * - Minimal use of OS services.
+ * - The OS services used will be abstracted by using inline functions
+ * or macros.
+ *
+ */
+
+#include "dwc_os.h"
+#include "dwc_otg_regs.h"
+#include "dwc_otg_cil.h"
+
+static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if);
+
+/**
+ * This function is called to initialize the DWC_otg CSR data
+ * structures. The register addresses in the device and host
+ * structures are initialized from the base address supplied by the
+ * caller. The calling function must make the OS calls to get the
+ * base address of the DWC_otg controller registers. The core_params
+ * argument holds the parameters that specify how the core should be
+ * configured.
+ *
+ * @param reg_base_addr Base address of DWC_otg core registers
+ *
+ */
+dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * reg_base_addr)
+{
+ dwc_otg_core_if_t *core_if = 0;
+ dwc_otg_dev_if_t *dev_if = 0;
+ dwc_otg_host_if_t *host_if = 0;
+ uint8_t *reg_base = (uint8_t *) reg_base_addr;
+ int i = 0;
+
+ DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, reg_base_addr);
+
+ core_if = DWC_ALLOC(sizeof(dwc_otg_core_if_t));
+
+ if (core_if == NULL) {
+ DWC_DEBUGPL(DBG_CIL,
+ "Allocation of dwc_otg_core_if_t failed\n");
+ return 0;
+ }
+ core_if->core_global_regs = (dwc_otg_core_global_regs_t *) reg_base;
+
+ /*
+ * Allocate the Device Mode structures.
+ */
+ dev_if = DWC_ALLOC(sizeof(dwc_otg_dev_if_t));
+
+ if (dev_if == NULL) {
+ DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_dev_if_t failed\n");
+ DWC_FREE(core_if);
+ return 0;
+ }
+
+ dev_if->dev_global_regs =
+ (dwc_otg_device_global_regs_t *) (reg_base +
+ DWC_DEV_GLOBAL_REG_OFFSET);
+
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ dev_if->in_ep_regs[i] = (dwc_otg_dev_in_ep_regs_t *)
+ (reg_base + DWC_DEV_IN_EP_REG_OFFSET +
+ (i * DWC_EP_REG_OFFSET));
+
+ dev_if->out_ep_regs[i] = (dwc_otg_dev_out_ep_regs_t *)
+ (reg_base + DWC_DEV_OUT_EP_REG_OFFSET +
+ (i * DWC_EP_REG_OFFSET));
+ DWC_DEBUGPL(DBG_CILV, "in_ep_regs[%d]->diepctl=%p\n",
+ i, &dev_if->in_ep_regs[i]->diepctl);
+ DWC_DEBUGPL(DBG_CILV, "out_ep_regs[%d]->doepctl=%p\n",
+ i, &dev_if->out_ep_regs[i]->doepctl);
+ }
+
+ dev_if->speed = 0; // unknown
+
+ core_if->dev_if = dev_if;
+
+ /*
+ * Allocate the Host Mode structures.
+ */
+ host_if = DWC_ALLOC(sizeof(dwc_otg_host_if_t));
+
+ if (host_if == NULL) {
+ DWC_DEBUGPL(DBG_CIL,
+ "Allocation of dwc_otg_host_if_t failed\n");
+ DWC_FREE(dev_if);
+ DWC_FREE(core_if);
+ return 0;
+ }
+
+ host_if->host_global_regs = (dwc_otg_host_global_regs_t *)
+ (reg_base + DWC_OTG_HOST_GLOBAL_REG_OFFSET);
+
+ host_if->hprt0 =
+ (uint32_t *) (reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET);
+
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ host_if->hc_regs[i] = (dwc_otg_hc_regs_t *)
+ (reg_base + DWC_OTG_HOST_CHAN_REGS_OFFSET +
+ (i * DWC_OTG_CHAN_REGS_OFFSET));
+ DWC_DEBUGPL(DBG_CILV, "hc_reg[%d]->hcchar=%p\n",
+ i, &host_if->hc_regs[i]->hcchar);
+ }
+
+ host_if->num_host_channels = MAX_EPS_CHANNELS;
+ core_if->host_if = host_if;
+
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ core_if->data_fifo[i] =
+ (uint32_t *) (reg_base + DWC_OTG_DATA_FIFO_OFFSET +
+ (i * DWC_OTG_DATA_FIFO_SIZE));
+ DWC_DEBUGPL(DBG_CILV, "data_fifo[%d]=0x%08lx\n",
+ i, (unsigned long)core_if->data_fifo[i]);
+ }
+
+ core_if->pcgcctl = (uint32_t *) (reg_base + DWC_OTG_PCGCCTL_OFFSET);
+
+ /* Initiate lx_state to L3 disconnected state */
+ core_if->lx_state = DWC_OTG_L3;
+ /*
+ * Store the contents of the hardware configuration registers here for
+ * easy access later.
+ */
+ core_if->hwcfg1.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->ghwcfg1);
+ core_if->hwcfg2.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->ghwcfg2);
+ core_if->hwcfg3.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->ghwcfg3);
+ core_if->hwcfg4.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->ghwcfg4);
+
+ /* Force host mode to get HPTXFSIZ exact power on value */
+ {
+ gusbcfg_data_t gusbcfg = {.d32 = 0 };
+ gusbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+ gusbcfg.b.force_host_mode = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32);
+ dwc_mdelay(100);
+ core_if->hptxfsiz.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz);
+ gusbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+ gusbcfg.b.force_host_mode = 0;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32);
+ dwc_mdelay(100);
+ }
+
+ DWC_DEBUGPL(DBG_CILV, "hwcfg1=%08x\n", core_if->hwcfg1.d32);
+ DWC_DEBUGPL(DBG_CILV, "hwcfg2=%08x\n", core_if->hwcfg2.d32);
+ DWC_DEBUGPL(DBG_CILV, "hwcfg3=%08x\n", core_if->hwcfg3.d32);
+ DWC_DEBUGPL(DBG_CILV, "hwcfg4=%08x\n", core_if->hwcfg4.d32);
+
+ core_if->hcfg.d32 =
+ DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+ core_if->dcfg.d32 =
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+
+ DWC_DEBUGPL(DBG_CILV, "hcfg=%08x\n", core_if->hcfg.d32);
+ DWC_DEBUGPL(DBG_CILV, "dcfg=%08x\n", core_if->dcfg.d32);
+
+ DWC_DEBUGPL(DBG_CILV, "op_mode=%0x\n", core_if->hwcfg2.b.op_mode);
+ DWC_DEBUGPL(DBG_CILV, "arch=%0x\n", core_if->hwcfg2.b.architecture);
+ DWC_DEBUGPL(DBG_CILV, "num_dev_ep=%d\n", core_if->hwcfg2.b.num_dev_ep);
+ DWC_DEBUGPL(DBG_CILV, "num_host_chan=%d\n",
+ core_if->hwcfg2.b.num_host_chan);
+ DWC_DEBUGPL(DBG_CILV, "nonperio_tx_q_depth=0x%0x\n",
+ core_if->hwcfg2.b.nonperio_tx_q_depth);
+ DWC_DEBUGPL(DBG_CILV, "host_perio_tx_q_depth=0x%0x\n",
+ core_if->hwcfg2.b.host_perio_tx_q_depth);
+ DWC_DEBUGPL(DBG_CILV, "dev_token_q_depth=0x%0x\n",
+ core_if->hwcfg2.b.dev_token_q_depth);
+
+ DWC_DEBUGPL(DBG_CILV, "Total FIFO SZ=%d\n",
+ core_if->hwcfg3.b.dfifo_depth);
+ DWC_DEBUGPL(DBG_CILV, "xfer_size_cntr_width=%0x\n",
+ core_if->hwcfg3.b.xfer_size_cntr_width);
+
+ /*
+ * Set the SRP sucess bit for FS-I2c
+ */
+ core_if->srp_success = 0;
+ core_if->srp_timer_started = 0;
+
+ /*
+ * Create new workqueue and init works
+ */
+ core_if->wq_otg = DWC_WORKQ_ALLOC("dwc_otg");
+ if (core_if->wq_otg == 0) {
+ DWC_WARN("DWC_WORKQ_ALLOC failed\n");
+ DWC_FREE(host_if);
+ DWC_FREE(dev_if);
+ DWC_FREE(core_if);
+ return 0;
+ }
+
+ core_if->snpsid = DWC_READ_REG32(&core_if->core_global_regs->gsnpsid);
+
+ DWC_PRINTF("Core Release: %x.%x%x%x\n",
+ (core_if->snpsid >> 12 & 0xF),
+ (core_if->snpsid >> 8 & 0xF),
+ (core_if->snpsid >> 4 & 0xF), (core_if->snpsid & 0xF));
+
+ core_if->wkp_timer = DWC_TIMER_ALLOC("Wake Up Timer",
+ w_wakeup_detected, core_if);
+ if (core_if->wkp_timer == 0) {
+ DWC_WARN("DWC_TIMER_ALLOC failed\n");
+ DWC_FREE(host_if);
+ DWC_FREE(dev_if);
+ DWC_WORKQ_FREE(core_if->wq_otg);
+ DWC_FREE(core_if);
+ return 0;
+ }
+
+ if (dwc_otg_setup_params(core_if)) {
+ DWC_WARN("Error while setting core params\n");
+ }
+
+ core_if->hibernation_suspend = 0;
+
+ /** ADP initialization */
+ dwc_otg_adp_init(core_if);
+
+ return core_if;
+}
+
+/**
+ * This function frees the structures allocated by dwc_otg_cil_init().
+ *
+ * @param core_if The core interface pointer returned from
+ * dwc_otg_cil_init().
+ *
+ */
+void dwc_otg_cil_remove(dwc_otg_core_if_t * core_if)
+{
+ /* Disable all interrupts */
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, 1, 0);
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0);
+
+ if (core_if->wq_otg) {
+ DWC_WORKQ_WAIT_WORK_DONE(core_if->wq_otg, 500);
+ DWC_WORKQ_FREE(core_if->wq_otg);
+ }
+ if (core_if->dev_if) {
+ DWC_FREE(core_if->dev_if);
+ }
+ if (core_if->host_if) {
+ DWC_FREE(core_if->host_if);
+ }
+
+ /** Remove ADP Stuff */
+ dwc_otg_adp_remove(core_if);
+ if (core_if->core_params) {
+ DWC_FREE(core_if->core_params);
+ }
+ if (core_if->wkp_timer) {
+ DWC_TIMER_FREE(core_if->wkp_timer);
+ }
+ if (core_if->srp_timer) {
+ DWC_TIMER_FREE(core_if->srp_timer);
+ }
+ DWC_FREE(core_if);
+}
+
+/**
+ * This function enables the controller's Global Interrupt in the AHB Config
+ * register.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * core_if)
+{
+ gahbcfg_data_t ahbcfg = {.d32 = 0 };
+ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, 0, ahbcfg.d32);
+}
+
+/**
+ * This function disables the controller's Global Interrupt in the AHB Config
+ * register.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * core_if)
+{
+ gahbcfg_data_t ahbcfg = {.d32 = 0 };
+ ahbcfg.b.glblintrmsk = 1; /* Disable interrupts */
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0);
+}
+
+/**
+ * This function initializes the commmon interrupts, used in both
+ * device and host modes.
+ *
+ * @param core_if Programming view of the DWC_otg controller
+ *
+ */
+static void dwc_otg_enable_common_interrupts(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ /* Clear any pending OTG Interrupts */
+ DWC_WRITE_REG32(&global_regs->gotgint, 0xFFFFFFFF);
+
+ /* Clear any pending interrupts */
+ DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF);
+
+ /*
+ * Enable the interrupts in the GINTMSK.
+ */
+ intr_mask.b.modemismatch = 1;
+ intr_mask.b.otgintr = 1;
+
+ if (!core_if->dma_enable) {
+ intr_mask.b.rxstsqlvl = 1;
+ }
+
+ intr_mask.b.conidstschng = 1;
+ intr_mask.b.wkupintr = 1;
+ intr_mask.b.disconnect = 0;
+ intr_mask.b.usbsuspend = 1;
+ intr_mask.b.sessreqintr = 1;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ if (core_if->core_params->lpm_enable) {
+ intr_mask.b.lpmtranrcvd = 1;
+ }
+#endif
+ DWC_WRITE_REG32(&global_regs->gintmsk, intr_mask.d32);
+}
+
+/*
+ * The restore operation is modified to support Synopsys Emulated Powerdown and
+ * Hibernation. This function is for exiting from Device mode hibernation by
+ * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
+ * @param core_if Programming view of DWC_otg controller.
+ * @param rem_wakeup - indicates whether resume is initiated by Device or Host.
+ * @param reset - indicates whether resume is initiated by Reset.
+ */
+int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if,
+ int rem_wakeup, int reset)
+{
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ dctl_data_t dctl = {.d32 = 0 };
+
+ int timeout = 2000;
+
+ if (!core_if->hibernation_suspend) {
+ DWC_PRINTF("Already exited from Hibernation\n");
+ return 1;
+ }
+
+ DWC_DEBUGPL(DBG_PCD, "%s called\n", __FUNCTION__);
+ /* Switch-on voltage to the core */
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Reset core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Assert Restore signal */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.restore = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Disable power clamps */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ if (rem_wakeup) {
+ dwc_udelay(70);
+ }
+
+ /* Deassert Reset core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Mask interrupts from gpwrdn */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.connect_det_msk = 1;
+ gpwrdn.b.srp_det_msk = 1;
+ gpwrdn.b.disconn_det_msk = 1;
+ gpwrdn.b.rst_det_msk = 1;
+ gpwrdn.b.lnstchng_msk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Indicates that we are going out from hibernation */
+ core_if->hibernation_suspend = 0;
+
+ /*
+ * Set Restore Essential Regs bit in PCGCCTL register, restore_mode = 1
+ * indicates restore from remote_wakeup
+ */
+ restore_essential_regs(core_if, rem_wakeup, 0);
+
+ /*
+ * Wait a little for seeing new value of variable hibernation_suspend if
+ * Restore done interrupt received before polling
+ */
+ dwc_udelay(10);
+
+ if (core_if->hibernation_suspend == 0) {
+ /*
+ * Wait For Restore_done Interrupt. This mechanism of polling the
+ * interrupt is introduced to avoid any possible race conditions
+ */
+ do {
+ gintsts_data_t gintsts;
+ gintsts.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+ if (gintsts.b.restoredone) {
+ gintsts.d32 = 0;
+ gintsts.b.restoredone = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->
+ gintsts, gintsts.d32);
+ DWC_PRINTF("Restore Done Interrupt seen\n");
+ break;
+ }
+ dwc_udelay(10);
+ } while (--timeout);
+ if (!timeout) {
+ DWC_PRINTF("Restore Done interrupt wasn't generated here\n");
+ }
+ }
+ /* Clear all pending interupts */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+ /* De-assert Restore */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.restore = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ if (!rem_wakeup) {
+ pcgcctl.d32 = 0;
+ pcgcctl.b.rstpdwnmodule = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+ }
+
+ /* Restore GUSBCFG and DCFG */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg,
+ core_if->gr_backup->gusbcfg_local);
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg,
+ core_if->dr_backup->dcfg);
+
+ /* De-assert Wakeup Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ if (!rem_wakeup) {
+ /* Set Device programming done bit */
+ dctl.b.pwronprgdone = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+ } else {
+ /* Start Remote Wakeup Signaling */
+ dctl.d32 = core_if->dr_backup->dctl;
+ dctl.b.rmtwkupsig = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
+ }
+
+ dwc_mdelay(2);
+ /* Clear all pending interupts */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+ /* Restore global registers */
+ dwc_otg_restore_global_regs(core_if);
+ /* Restore device global registers */
+ dwc_otg_restore_dev_regs(core_if, rem_wakeup);
+
+ if (rem_wakeup) {
+ dwc_mdelay(7);
+ dctl.d32 = 0;
+ dctl.b.rmtwkupsig = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0);
+ }
+
+ core_if->hibernation_suspend = 0;
+ /* The core will be in ON STATE */
+ core_if->lx_state = DWC_OTG_L0;
+ DWC_PRINTF("Hibernation recovery completes here\n");
+
+ return 1;
+}
+
+/*
+ * The restore operation is modified to support Synopsys Emulated Powerdown and
+ * Hibernation. This function is for exiting from Host mode hibernation by
+ * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
+ * @param core_if Programming view of DWC_otg controller.
+ * @param rem_wakeup - indicates whether resume is initiated by Device or Host.
+ * @param reset - indicates whether resume is initiated by Reset.
+ */
+int dwc_otg_host_hibernation_restore(dwc_otg_core_if_t * core_if,
+ int rem_wakeup, int reset)
+{
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ hprt0_data_t hprt0 = {.d32 = 0 };
+
+ int timeout = 2000;
+
+ DWC_DEBUGPL(DBG_HCD, "%s called\n", __FUNCTION__);
+ /* Switch-on voltage to the core */
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Reset core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Assert Restore signal */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.restore = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Disable power clamps */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ if (!rem_wakeup) {
+ dwc_udelay(50);
+ }
+
+ /* Deassert Reset core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ gpwrdn.d32 = 0;
+ gpwrdn.b.connect_det_msk = 1;
+ gpwrdn.b.srp_det_msk = 1;
+ gpwrdn.b.disconn_det_msk = 1;
+ gpwrdn.b.rst_det_msk = 1;
+ gpwrdn.b.lnstchng_msk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Indicates that we are going out from hibernation */
+ core_if->hibernation_suspend = 0;
+
+ /* Set Restore Essential Regs bit in PCGCCTL register */
+ restore_essential_regs(core_if, rem_wakeup, 1);
+
+ /* Wait a little for seeing new value of variable hibernation_suspend if
+ * Restore done interrupt received before polling */
+ dwc_udelay(10);
+
+ if (core_if->hibernation_suspend == 0) {
+ /* Wait For Restore_done Interrupt. This mechanism of polling the
+ * interrupt is introduced to avoid any possible race conditions
+ */
+ do {
+ gintsts_data_t gintsts;
+ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+ if (gintsts.b.restoredone) {
+ gintsts.d32 = 0;
+ gintsts.b.restoredone = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+ DWC_DEBUGPL(DBG_HCD,"Restore Done Interrupt seen\n");
+ break;
+ }
+ dwc_udelay(10);
+ } while (--timeout);
+ if (!timeout) {
+ DWC_WARN("Restore Done interrupt wasn't generated\n");
+ }
+ }
+
+ /* Set the flag's value to 0 again after receiving restore done interrupt */
+ core_if->hibernation_suspend = 0;
+
+ /* This step is not described in functional spec but if not wait for this
+ * delay, mismatch interrupts occurred because just after restore core is
+ * in Device mode(gintsts.curmode == 0) */
+ dwc_mdelay(100);
+
+ /* Clear all pending interrupts */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+ /* De-assert Restore */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.restore = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Restore GUSBCFG and HCFG */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg,
+ core_if->gr_backup->gusbcfg_local);
+ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg,
+ core_if->hr_backup->hcfg_local);
+
+ /* De-assert Wakeup Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Start the Resume operation by programming HPRT0 */
+ hprt0.d32 = core_if->hr_backup->hprt0_local;
+ hprt0.b.prtpwr = 1;
+ hprt0.b.prtena = 0;
+ hprt0.b.prtsusp = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+ DWC_PRINTF("Resume Starts Now\n");
+ if (!reset) { // Indicates it is Resume Operation
+ hprt0.d32 = core_if->hr_backup->hprt0_local;
+ hprt0.b.prtres = 1;
+ hprt0.b.prtpwr = 1;
+ hprt0.b.prtena = 0;
+ hprt0.b.prtsusp = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+ if (!rem_wakeup)
+ hprt0.b.prtres = 0;
+ /* Wait for Resume time and then program HPRT again */
+ dwc_mdelay(100);
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+ } else { // Indicates it is Reset Operation
+ hprt0.d32 = core_if->hr_backup->hprt0_local;
+ hprt0.b.prtrst = 1;
+ hprt0.b.prtpwr = 1;
+ hprt0.b.prtena = 0;
+ hprt0.b.prtsusp = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ /* Wait for Reset time and then program HPRT again */
+ dwc_mdelay(60);
+ hprt0.b.prtrst = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ }
+ /* Clear all interrupt status */
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtconndet = 1;
+ hprt0.b.prtenchng = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+ /* Clear all pending interupts */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+ /* Restore global registers */
+ dwc_otg_restore_global_regs(core_if);
+ /* Restore host global registers */
+ dwc_otg_restore_host_regs(core_if, reset);
+
+ /* The core will be in ON STATE */
+ core_if->lx_state = DWC_OTG_L0;
+ DWC_PRINTF("Hibernation recovery is complete here\n");
+ return 0;
+}
+
+/** Saves some register values into system memory. */
+int dwc_otg_save_global_regs(dwc_otg_core_if_t * core_if)
+{
+ struct dwc_otg_global_regs_backup *gr;
+ int i;
+
+ gr = core_if->gr_backup;
+ if (!gr) {
+ gr = DWC_ALLOC(sizeof(*gr));
+ if (!gr) {
+ return -DWC_E_NO_MEMORY;
+ }
+ core_if->gr_backup = gr;
+ }
+
+ gr->gotgctl_local = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+ gr->gintmsk_local = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+ gr->gahbcfg_local = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
+ gr->gusbcfg_local = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+ gr->grxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+ gr->gnptxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz);
+ gr->hptxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ gr->glpmcfg_local = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+#endif
+ gr->gi2cctl_local = DWC_READ_REG32(&core_if->core_global_regs->gi2cctl);
+ gr->pcgcctl_local = DWC_READ_REG32(core_if->pcgcctl);
+ gr->gdfifocfg_local =
+ DWC_READ_REG32(&core_if->core_global_regs->gdfifocfg);
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ gr->dtxfsiz_local[i] =
+ DWC_READ_REG32(&(core_if->core_global_regs->dtxfsiz[i]));
+ }
+
+ DWC_DEBUGPL(DBG_ANY, "===========Backing Global registers==========\n");
+ DWC_DEBUGPL(DBG_ANY, "Backed up gotgctl = %08x\n", gr->gotgctl_local);
+ DWC_DEBUGPL(DBG_ANY, "Backed up gintmsk = %08x\n", gr->gintmsk_local);
+ DWC_DEBUGPL(DBG_ANY, "Backed up gahbcfg = %08x\n", gr->gahbcfg_local);
+ DWC_DEBUGPL(DBG_ANY, "Backed up gusbcfg = %08x\n", gr->gusbcfg_local);
+ DWC_DEBUGPL(DBG_ANY, "Backed up grxfsiz = %08x\n", gr->grxfsiz_local);
+ DWC_DEBUGPL(DBG_ANY, "Backed up gnptxfsiz = %08x\n",
+ gr->gnptxfsiz_local);
+ DWC_DEBUGPL(DBG_ANY, "Backed up hptxfsiz = %08x\n",
+ gr->hptxfsiz_local);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ DWC_DEBUGPL(DBG_ANY, "Backed up glpmcfg = %08x\n", gr->glpmcfg_local);
+#endif
+ DWC_DEBUGPL(DBG_ANY, "Backed up gi2cctl = %08x\n", gr->gi2cctl_local);
+ DWC_DEBUGPL(DBG_ANY, "Backed up pcgcctl = %08x\n", gr->pcgcctl_local);
+ DWC_DEBUGPL(DBG_ANY,"Backed up gdfifocfg = %08x\n",gr->gdfifocfg_local);
+
+ return 0;
+}
+
+/** Saves GINTMSK register before setting the msk bits. */
+int dwc_otg_save_gintmsk_reg(dwc_otg_core_if_t * core_if)
+{
+ struct dwc_otg_global_regs_backup *gr;
+
+ gr = core_if->gr_backup;
+ if (!gr) {
+ gr = DWC_ALLOC(sizeof(*gr));
+ if (!gr) {
+ return -DWC_E_NO_MEMORY;
+ }
+ core_if->gr_backup = gr;
+ }
+
+ gr->gintmsk_local = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+
+ DWC_DEBUGPL(DBG_ANY,"=============Backing GINTMSK registers============\n");
+ DWC_DEBUGPL(DBG_ANY, "Backed up gintmsk = %08x\n", gr->gintmsk_local);
+
+ return 0;
+}
+
+int dwc_otg_save_dev_regs(dwc_otg_core_if_t * core_if)
+{
+ struct dwc_otg_dev_regs_backup *dr;
+ int i;
+
+ dr = core_if->dr_backup;
+ if (!dr) {
+ dr = DWC_ALLOC(sizeof(*dr));
+ if (!dr) {
+ return -DWC_E_NO_MEMORY;
+ }
+ core_if->dr_backup = dr;
+ }
+
+ dr->dcfg = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+ dr->dctl = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+ dr->daintmsk =
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk);
+ dr->diepmsk =
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->diepmsk);
+ dr->doepmsk =
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->doepmsk);
+
+ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+ dr->diepctl[i] =
+ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl);
+ dr->dieptsiz[i] =
+ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->dieptsiz);
+ dr->diepdma[i] =
+ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepdma);
+ }
+
+ DWC_DEBUGPL(DBG_ANY,
+ "=============Backing Host registers==============\n");
+ DWC_DEBUGPL(DBG_ANY, "Backed up dcfg = %08x\n", dr->dcfg);
+ DWC_DEBUGPL(DBG_ANY, "Backed up dctl = %08x\n", dr->dctl);
+ DWC_DEBUGPL(DBG_ANY, "Backed up daintmsk = %08x\n",
+ dr->daintmsk);
+ DWC_DEBUGPL(DBG_ANY, "Backed up diepmsk = %08x\n", dr->diepmsk);
+ DWC_DEBUGPL(DBG_ANY, "Backed up doepmsk = %08x\n", dr->doepmsk);
+ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+ DWC_DEBUGPL(DBG_ANY, "Backed up diepctl[%d] = %08x\n", i,
+ dr->diepctl[i]);
+ DWC_DEBUGPL(DBG_ANY, "Backed up dieptsiz[%d] = %08x\n",
+ i, dr->dieptsiz[i]);
+ DWC_DEBUGPL(DBG_ANY, "Backed up diepdma[%d] = %08x\n", i,
+ dr->diepdma[i]);
+ }
+
+ return 0;
+}
+
+int dwc_otg_save_host_regs(dwc_otg_core_if_t * core_if)
+{
+ struct dwc_otg_host_regs_backup *hr;
+ int i;
+
+ hr = core_if->hr_backup;
+ if (!hr) {
+ hr = DWC_ALLOC(sizeof(*hr));
+ if (!hr) {
+ return -DWC_E_NO_MEMORY;
+ }
+ core_if->hr_backup = hr;
+ }
+
+ hr->hcfg_local =
+ DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+ hr->haintmsk_local =
+ DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk);
+ for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) {
+ hr->hcintmsk_local[i] =
+ DWC_READ_REG32(&core_if->host_if->hc_regs[i]->hcintmsk);
+ }
+ hr->hprt0_local = DWC_READ_REG32(core_if->host_if->hprt0);
+ hr->hfir_local =
+ DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir);
+
+ DWC_DEBUGPL(DBG_ANY,
+ "=============Backing Host registers===============\n");
+ DWC_DEBUGPL(DBG_ANY, "Backed up hcfg = %08x\n",
+ hr->hcfg_local);
+ DWC_DEBUGPL(DBG_ANY, "Backed up haintmsk = %08x\n", hr->haintmsk_local);
+ for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) {
+ DWC_DEBUGPL(DBG_ANY, "Backed up hcintmsk[%02d]=%08x\n", i,
+ hr->hcintmsk_local[i]);
+ }
+ DWC_DEBUGPL(DBG_ANY, "Backed up hprt0 = %08x\n",
+ hr->hprt0_local);
+ DWC_DEBUGPL(DBG_ANY, "Backed up hfir = %08x\n",
+ hr->hfir_local);
+
+ return 0;
+}
+
+int dwc_otg_restore_global_regs(dwc_otg_core_if_t *core_if)
+{
+ struct dwc_otg_global_regs_backup *gr;
+ int i;
+
+ gr = core_if->gr_backup;
+ if (!gr) {
+ return -DWC_E_INVALID;
+ }
+
+ DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, gr->gotgctl_local);
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gr->gintmsk_local);
+ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gr->gusbcfg_local);
+ DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gr->gahbcfg_local);
+ DWC_WRITE_REG32(&core_if->core_global_regs->grxfsiz, gr->grxfsiz_local);
+ DWC_WRITE_REG32(&core_if->core_global_regs->gnptxfsiz,
+ gr->gnptxfsiz_local);
+ DWC_WRITE_REG32(&core_if->core_global_regs->hptxfsiz,
+ gr->hptxfsiz_local);
+ DWC_WRITE_REG32(&core_if->core_global_regs->gdfifocfg,
+ gr->gdfifocfg_local);
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ DWC_WRITE_REG32(&core_if->core_global_regs->dtxfsiz[i],
+ gr->dtxfsiz_local[i]);
+ }
+
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+ DWC_WRITE_REG32(core_if->host_if->hprt0, 0x0000100A);
+ DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg,
+ (gr->gahbcfg_local));
+ return 0;
+}
+
+int dwc_otg_restore_dev_regs(dwc_otg_core_if_t * core_if, int rem_wakeup)
+{
+ struct dwc_otg_dev_regs_backup *dr;
+ int i;
+
+ dr = core_if->dr_backup;
+
+ if (!dr) {
+ return -DWC_E_INVALID;
+ }
+
+ if (!rem_wakeup)
+ {
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dr->dctl);
+ }
+
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daintmsk, dr->daintmsk);
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->diepmsk, dr->diepmsk);
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->doepmsk, dr->doepmsk);
+
+ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl, dr->diepctl[i]);
+ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->dieptsiz, dr->dieptsiz[i]);
+ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->diepdma, dr->diepdma[i]);
+ }
+
+ return 0;
+}
+
+int dwc_otg_restore_host_regs(dwc_otg_core_if_t * core_if, int reset)
+{
+ struct dwc_otg_host_regs_backup *hr;
+ int i;
+ hr = core_if->hr_backup;
+
+ if (!hr) {
+ return -DWC_E_INVALID;
+ }
+
+ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hr->hcfg_local);
+ //if (!reset)
+ //{
+ // DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hfir, hr->hfir_local);
+ //}
+
+ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haintmsk,
+ hr->haintmsk_local);
+ for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) {
+ DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcintmsk,
+ hr->hcintmsk_local[i]);
+ }
+
+ return 0;
+}
+
+int restore_lpm_i2c_regs(dwc_otg_core_if_t * core_if)
+{
+ struct dwc_otg_global_regs_backup *gr;
+
+ gr = core_if->gr_backup;
+
+ /* Restore values for LPM and I2C */
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, gr->glpmcfg_local);
+#endif
+ DWC_WRITE_REG32(&core_if->core_global_regs->gi2cctl, gr->gi2cctl_local);
+
+ return 0;
+}
+
+int restore_essential_regs(dwc_otg_core_if_t * core_if, int rmode, int is_host)
+{
+ struct dwc_otg_global_regs_backup *gr;
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ gahbcfg_data_t gahbcfg = {.d32 = 0 };
+ gusbcfg_data_t gusbcfg = {.d32 = 0 };
+ gintmsk_data_t gintmsk = {.d32 = 0 };
+
+ /* Restore LPM and I2C registers */
+ restore_lpm_i2c_regs(core_if);
+
+ /* Set PCGCCTL to 0 */
+ DWC_WRITE_REG32(core_if->pcgcctl, 0x00000000);
+
+ gr = core_if->gr_backup;
+ /* Load restore values for [31:14] bits */
+ DWC_WRITE_REG32(core_if->pcgcctl,
+ ((gr->pcgcctl_local & 0xffffc000) | 0x00020000));
+
+ /* Umnask global Interrupt in GAHBCFG and restore it */
+ gahbcfg.d32 = gr->gahbcfg_local;
+ gahbcfg.b.glblintrmsk = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gahbcfg.d32);
+
+ /* Clear all pending interupts */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+ /* Unmask restore done interrupt */
+ gintmsk.b.restoredone = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32);
+
+ /* Restore GUSBCFG and HCFG/DCFG */
+ gusbcfg.d32 = core_if->gr_backup->gusbcfg_local;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32);
+
+ if (is_host) {
+ hcfg_data_t hcfg = {.d32 = 0 };
+ hcfg.d32 = core_if->hr_backup->hcfg_local;
+ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg,
+ hcfg.d32);
+
+ /* Load restore values for [31:14] bits */
+ pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
+ pcgcctl.d32 = gr->pcgcctl_local | 0x00020000;
+
+ if (rmode)
+ pcgcctl.b.restoremode = 1;
+ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+ dwc_udelay(10);
+
+ /* Load restore values for [31:14] bits and set EssRegRestored bit */
+ pcgcctl.d32 = gr->pcgcctl_local | 0xffffc000;
+ pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
+ pcgcctl.b.ess_reg_restored = 1;
+ if (rmode)
+ pcgcctl.b.restoremode = 1;
+ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+ } else {
+ dcfg_data_t dcfg = {.d32 = 0 };
+ dcfg.d32 = core_if->dr_backup->dcfg;
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+ /* Load restore values for [31:14] bits */
+ pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
+ pcgcctl.d32 = gr->pcgcctl_local | 0x00020000;
+ if (!rmode) {
+ pcgcctl.d32 |= 0x208;
+ }
+ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+ dwc_udelay(10);
+
+ /* Load restore values for [31:14] bits */
+ pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
+ pcgcctl.d32 = gr->pcgcctl_local | 0x00020000;
+ pcgcctl.b.ess_reg_restored = 1;
+ if (!rmode)
+ pcgcctl.d32 |= 0x208;
+ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+ }
+
+ return 0;
+}
+
+/**
+ * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY
+ * type.
+ */
+static void init_fslspclksel(dwc_otg_core_if_t * core_if)
+{
+ uint32_t val;
+ hcfg_data_t hcfg;
+
+ if (((core_if->hwcfg2.b.hs_phy_type == 2) &&
+ (core_if->hwcfg2.b.fs_phy_type == 1) &&
+ (core_if->core_params->ulpi_fs_ls)) ||
+ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
+ /* Full speed PHY */
+ val = DWC_HCFG_48_MHZ;
+ } else {
+ /* High speed PHY running at full speed or high speed */
+ val = DWC_HCFG_30_60_MHZ;
+ }
+
+ DWC_DEBUGPL(DBG_CIL, "Initializing HCFG.FSLSPClkSel to 0x%1x\n", val);
+ hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+ hcfg.b.fslspclksel = val;
+ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32);
+}
+
+/**
+ * Initializes the DevSpd field of the DCFG register depending on the PHY type
+ * and the enumeration speed of the device.
+ */
+static void init_devspd(dwc_otg_core_if_t * core_if)
+{
+ uint32_t val;
+ dcfg_data_t dcfg;
+
+ if (((core_if->hwcfg2.b.hs_phy_type == 2) &&
+ (core_if->hwcfg2.b.fs_phy_type == 1) &&
+ (core_if->core_params->ulpi_fs_ls)) ||
+ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
+ /* Full speed PHY */
+ val = 0x3;
+ } else if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
+ /* High speed PHY running at full speed */
+ val = 0x1;
+ } else {
+ /* High speed PHY running at high speed */
+ val = 0x0;
+ }
+
+ DWC_DEBUGPL(DBG_CIL, "Initializing DCFG.DevSpd to 0x%1x\n", val);
+
+ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+ dcfg.b.devspd = val;
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
+}
+
+/**
+ * This function calculates the number of IN EPS
+ * using GHWCFG1 and GHWCFG2 registers values
+ *
+ * @param core_if Programming view of the DWC_otg controller
+ */
+static uint32_t calc_num_in_eps(dwc_otg_core_if_t * core_if)
+{
+ uint32_t num_in_eps = 0;
+ uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep;
+ uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 3;
+ uint32_t num_tx_fifos = core_if->hwcfg4.b.num_in_eps;
+ int i;
+
+ for (i = 0; i < num_eps; ++i) {
+ if (!(hwcfg1 & 0x1))
+ num_in_eps++;
+
+ hwcfg1 >>= 2;
+ }
+
+ if (core_if->hwcfg4.b.ded_fifo_en) {
+ num_in_eps =
+ (num_in_eps > num_tx_fifos) ? num_tx_fifos : num_in_eps;
+ }
+
+ return num_in_eps;
+}
+
+/**
+ * This function calculates the number of OUT EPS
+ * using GHWCFG1 and GHWCFG2 registers values
+ *
+ * @param core_if Programming view of the DWC_otg controller
+ */
+static uint32_t calc_num_out_eps(dwc_otg_core_if_t * core_if)
+{
+ uint32_t num_out_eps = 0;
+ uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep;
+ uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 2;
+ int i;
+
+ for (i = 0; i < num_eps; ++i) {
+ if (!(hwcfg1 & 0x1))
+ num_out_eps++;
+
+ hwcfg1 >>= 2;
+ }
+ return num_out_eps;
+}
+
+/**
+ * This function initializes the DWC_otg controller registers and
+ * prepares the core for device mode or host mode operation.
+ *
+ * @param core_if Programming view of the DWC_otg controller
+ *
+ */
+void dwc_otg_core_init(dwc_otg_core_if_t * core_if)
+{
+ int i = 0;
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ gahbcfg_data_t ahbcfg = {.d32 = 0 };
+ gusbcfg_data_t usbcfg = {.d32 = 0 };
+ gi2cctl_data_t i2cctl = {.d32 = 0 };
+
+ DWC_DEBUGPL(DBG_CILV, "dwc_otg_core_init(%p)\n", core_if);
+
+ /* Common Initialization */
+ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+
+ /* Program the ULPI External VBUS bit if needed */
+ usbcfg.b.ulpi_ext_vbus_drv =
+ (core_if->core_params->phy_ulpi_ext_vbus ==
+ DWC_PHY_ULPI_EXTERNAL_VBUS) ? 1 : 0;
+
+ /* Set external TS Dline pulsing */
+ usbcfg.b.term_sel_dl_pulse =
+ (core_if->core_params->ts_dline == 1) ? 1 : 0;
+ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+
+ /* Reset the Controller */
+ dwc_otg_core_reset(core_if);
+
+ core_if->adp_enable = core_if->core_params->adp_supp_enable;
+ core_if->power_down = core_if->core_params->power_down;
+ core_if->otg_sts = 0;
+
+ /* Initialize parameters from Hardware configuration registers. */
+ dev_if->num_in_eps = calc_num_in_eps(core_if);
+ dev_if->num_out_eps = calc_num_out_eps(core_if);
+
+ DWC_DEBUGPL(DBG_CIL, "num_dev_perio_in_ep=%d\n",
+ core_if->hwcfg4.b.num_dev_perio_in_ep);
+
+ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) {
+ dev_if->perio_tx_fifo_size[i] =
+ DWC_READ_REG32(&global_regs->dtxfsiz[i]) >> 16;
+ DWC_DEBUGPL(DBG_CIL, "Periodic Tx FIFO SZ #%d=0x%0x\n",
+ i, dev_if->perio_tx_fifo_size[i]);
+ }
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+ dev_if->tx_fifo_size[i] =
+ DWC_READ_REG32(&global_regs->dtxfsiz[i]) >> 16;
+ DWC_DEBUGPL(DBG_CIL, "Tx FIFO SZ #%d=0x%0x\n",
+ i, dev_if->tx_fifo_size[i]);
+ }
+
+ core_if->total_fifo_size = core_if->hwcfg3.b.dfifo_depth;
+ core_if->rx_fifo_size = DWC_READ_REG32(&global_regs->grxfsiz);
+ core_if->nperio_tx_fifo_size =
+ DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16;
+
+ DWC_DEBUGPL(DBG_CIL, "Total FIFO SZ=%d\n", core_if->total_fifo_size);
+ DWC_DEBUGPL(DBG_CIL, "Rx FIFO SZ=%d\n", core_if->rx_fifo_size);
+ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO SZ=%d\n",
+ core_if->nperio_tx_fifo_size);
+
+ /* This programming sequence needs to happen in FS mode before any other
+ * programming occurs */
+ if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) &&
+ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
+ /* If FS mode with FS PHY */
+
+ /* core_init() is now called on every switch so only call the
+ * following for the first time through. */
+ if (!core_if->phy_init_done) {
+ core_if->phy_init_done = 1;
+ DWC_DEBUGPL(DBG_CIL, "FS_PHY detected\n");
+ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+ usbcfg.b.physel = 1;
+ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+
+ /* Reset after a PHY select */
+ dwc_otg_core_reset(core_if);
+ }
+
+ /* Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also
+ * do this on HNP Dev/Host mode switches (done in dev_init and
+ * host_init). */
+ if (dwc_otg_is_host_mode(core_if)) {
+ init_fslspclksel(core_if);
+ } else {
+ init_devspd(core_if);
+ }
+
+ if (core_if->core_params->i2c_enable) {
+ DWC_DEBUGPL(DBG_CIL, "FS_PHY Enabling I2c\n");
+ /* Program GUSBCFG.OtgUtmifsSel to I2C */
+ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+ usbcfg.b.otgutmifssel = 1;
+ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+
+ /* Program GI2CCTL.I2CEn */
+ i2cctl.d32 = DWC_READ_REG32(&global_regs->gi2cctl);
+ i2cctl.b.i2cdevaddr = 1;
+ i2cctl.b.i2cen = 0;
+ DWC_WRITE_REG32(&global_regs->gi2cctl, i2cctl.d32);
+ i2cctl.b.i2cen = 1;
+ DWC_WRITE_REG32(&global_regs->gi2cctl, i2cctl.d32);
+ }
+
+ } /* endif speed == DWC_SPEED_PARAM_FULL */
+ else {
+ /* High speed PHY. */
+ if (!core_if->phy_init_done) {
+ core_if->phy_init_done = 1;
+ /* HS PHY parameters. These parameters are preserved
+ * during soft reset so only program the first time. Do
+ * a soft reset immediately after setting phyif. */
+
+ if (core_if->core_params->phy_type == 2) {
+ /* ULPI interface */
+ usbcfg.b.ulpi_utmi_sel = 1;
+ usbcfg.b.phyif = 0;
+ usbcfg.b.ddrsel =
+ core_if->core_params->phy_ulpi_ddr;
+ } else if (core_if->core_params->phy_type == 1) {
+ /* UTMI+ interface */
+ usbcfg.b.ulpi_utmi_sel = 0;
+ if (core_if->core_params->phy_utmi_width == 16) {
+ usbcfg.b.phyif = 1;
+
+ } else {
+ usbcfg.b.phyif = 0;
+ }
+ } else {
+ DWC_ERROR("FS PHY TYPE\n");
+ }
+ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+ /* Reset after setting the PHY parameters */
+ dwc_otg_core_reset(core_if);
+ }
+ }
+
+ if ((core_if->hwcfg2.b.hs_phy_type == 2) &&
+ (core_if->hwcfg2.b.fs_phy_type == 1) &&
+ (core_if->core_params->ulpi_fs_ls)) {
+ DWC_DEBUGPL(DBG_CIL, "Setting ULPI FSLS\n");
+ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+ usbcfg.b.ulpi_fsls = 1;
+ usbcfg.b.ulpi_clk_sus_m = 1;
+ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+ } else {
+ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+ usbcfg.b.ulpi_fsls = 0;
+ usbcfg.b.ulpi_clk_sus_m = 0;
+ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+ }
+
+ /* Program the GAHBCFG Register. */
+ switch (core_if->hwcfg2.b.architecture) {
+
+ case DWC_SLAVE_ONLY_ARCH:
+ DWC_DEBUGPL(DBG_CIL, "Slave Only Mode\n");
+ ahbcfg.b.nptxfemplvl_txfemplvl =
+ DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY;
+ ahbcfg.b.ptxfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY;
+ core_if->dma_enable = 0;
+ core_if->dma_desc_enable = 0;
+ break;
+
+ case DWC_EXT_DMA_ARCH:
+ DWC_DEBUGPL(DBG_CIL, "External DMA Mode\n");
+ {
+ uint8_t brst_sz = core_if->core_params->dma_burst_size;
+ ahbcfg.b.hburstlen = 0;
+ while (brst_sz > 1) {
+ ahbcfg.b.hburstlen++;
+ brst_sz >>= 1;
+ }
+ }
+ core_if->dma_enable = (core_if->core_params->dma_enable != 0);
+ core_if->dma_desc_enable =
+ (core_if->core_params->dma_desc_enable != 0);
+ break;
+
+ case DWC_INT_DMA_ARCH:
+ DWC_DEBUGPL(DBG_CIL, "Internal DMA Mode\n");
+ /* Old value was DWC_GAHBCFG_INT_DMA_BURST_INCR - done for
+ Host mode ISOC in issue fix - vahrama */
+ ahbcfg.b.hburstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR16;
+ core_if->dma_enable = (core_if->core_params->dma_enable != 0);
+ core_if->dma_desc_enable =
+ (core_if->core_params->dma_desc_enable != 0);
+ break;
+
+ }
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable) {
+ DWC_PRINTF("Using Descriptor DMA mode\n");
+ } else {
+ DWC_PRINTF("Using Buffer DMA mode\n");
+
+ }
+ } else {
+ DWC_PRINTF("Using Slave mode\n");
+ core_if->dma_desc_enable = 0;
+ }
+
+ if (core_if->core_params->ahb_single) {
+ ahbcfg.b.ahbsingle = 1;
+ }
+
+ ahbcfg.b.dmaenable = core_if->dma_enable;
+ DWC_WRITE_REG32(&global_regs->gahbcfg, ahbcfg.d32);
+
+ core_if->en_multiple_tx_fifo = core_if->hwcfg4.b.ded_fifo_en;
+
+ core_if->pti_enh_enable = core_if->core_params->pti_enable != 0;
+ core_if->multiproc_int_enable = core_if->core_params->mpi_enable;
+ DWC_PRINTF("Periodic Transfer Interrupt Enhancement - %s\n",
+ ((core_if->pti_enh_enable) ? "enabled" : "disabled"));
+ DWC_PRINTF("Multiprocessor Interrupt Enhancement - %s\n",
+ ((core_if->multiproc_int_enable) ? "enabled" : "disabled"));
+
+ /*
+ * Program the GUSBCFG register.
+ */
+ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+
+ switch (core_if->hwcfg2.b.op_mode) {
+ case DWC_MODE_HNP_SRP_CAPABLE:
+ usbcfg.b.hnpcap = (core_if->core_params->otg_cap ==
+ DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE);
+ usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
+ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+ break;
+
+ case DWC_MODE_SRP_ONLY_CAPABLE:
+ usbcfg.b.hnpcap = 0;
+ usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
+ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+ break;
+
+ case DWC_MODE_NO_HNP_SRP_CAPABLE:
+ usbcfg.b.hnpcap = 0;
+ usbcfg.b.srpcap = 0;
+ break;
+
+ case DWC_MODE_SRP_CAPABLE_DEVICE:
+ usbcfg.b.hnpcap = 0;
+ usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
+ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+ break;
+
+ case DWC_MODE_NO_SRP_CAPABLE_DEVICE:
+ usbcfg.b.hnpcap = 0;
+ usbcfg.b.srpcap = 0;
+ break;
+
+ case DWC_MODE_SRP_CAPABLE_HOST:
+ usbcfg.b.hnpcap = 0;
+ usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
+ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+ break;
+
+ case DWC_MODE_NO_SRP_CAPABLE_HOST:
+ usbcfg.b.hnpcap = 0;
+ usbcfg.b.srpcap = 0;
+ break;
+ }
+
+ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ if (core_if->core_params->lpm_enable) {
+ glpmcfg_data_t lpmcfg = {.d32 = 0 };
+
+ /* To enable LPM support set lpm_cap_en bit */
+ lpmcfg.b.lpm_cap_en = 1;
+
+ /* Make AppL1Res ACK */
+ lpmcfg.b.appl_resp = 1;
+
+ /* Retry 3 times */
+ lpmcfg.b.retry_count = 3;
+
+ DWC_MODIFY_REG32(&core_if->core_global_regs->glpmcfg,
+ 0, lpmcfg.d32);
+
+ }
+#endif
+ if (core_if->core_params->ic_usb_cap) {
+ gusbcfg_data_t gusbcfg = {.d32 = 0 };
+ gusbcfg.b.ic_usb_cap = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gusbcfg,
+ 0, gusbcfg.d32);
+ }
+ {
+ gotgctl_data_t gotgctl = {.d32 = 0 };
+ gotgctl.b.otgver = core_if->core_params->otg_ver;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl, 0,
+ gotgctl.d32);
+ /* Set OTG version supported */
+ core_if->otg_ver = core_if->core_params->otg_ver;
+ DWC_PRINTF("OTG VER PARAM: %d, OTG VER FLAG: %d\n",
+ core_if->core_params->otg_ver, core_if->otg_ver);
+ }
+
+
+ /* Enable common interrupts */
+ dwc_otg_enable_common_interrupts(core_if);
+
+ /* Do device or host intialization based on mode during PCD
+ * and HCD initialization */
+ if (dwc_otg_is_host_mode(core_if)) {
+ DWC_DEBUGPL(DBG_ANY, "Host Mode\n");
+ core_if->op_state = A_HOST;
+ } else {
+ DWC_DEBUGPL(DBG_ANY, "Device Mode\n");
+ core_if->op_state = B_PERIPHERAL;
+#ifdef DWC_DEVICE_ONLY
+ dwc_otg_core_dev_init(core_if);
+#endif
+ }
+}
+
+/**
+ * This function enables the Device mode interrupts.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ */
+void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * core_if)
+{
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+
+ DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__);
+
+ /* Disable all interrupts. */
+ DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+ /* Clear any pending interrupts */
+ DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF);
+
+ /* Enable the common interrupts */
+ dwc_otg_enable_common_interrupts(core_if);
+
+ /* Enable interrupts */
+ intr_mask.b.usbreset = 1;
+ intr_mask.b.enumdone = 1;
+ /* Disable Disconnect interrupt in Device mode */
+ intr_mask.b.disconnect = 0;
+
+ if (!core_if->multiproc_int_enable) {
+ intr_mask.b.inepintr = 1;
+ intr_mask.b.outepintr = 1;
+ }
+
+ intr_mask.b.erlysuspend = 1;
+
+ if (core_if->en_multiple_tx_fifo == 0) {
+ intr_mask.b.epmismatch = 1;
+ }
+
+ //intr_mask.b.incomplisoout = 1;
+ intr_mask.b.incomplisoin = 1;
+
+/* Enable the ignore frame number for ISOC xfers - MAS */
+/* Disable to support high bandwith ISOC transfers - manukz */
+#if 0
+#ifdef DWC_UTE_PER_IO
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable) {
+ dctl_data_t dctl1 = {.d32 = 0 };
+ dctl1.b.ifrmnum = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+ dctl, 0, dctl1.d32);
+ DWC_DEBUG("----Enabled Ignore frame number (0x%08x)",
+ DWC_READ_REG32(&core_if->dev_if->
+ dev_global_regs->dctl));
+ }
+ }
+#endif
+#endif
+#ifdef DWC_EN_ISOC
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable == 0) {
+ if (core_if->pti_enh_enable) {
+ dctl_data_t dctl = {.d32 = 0 };
+ dctl.b.ifrmnum = 1;
+ DWC_MODIFY_REG32(&core_if->
+ dev_if->dev_global_regs->dctl,
+ 0, dctl.d32);
+ } else {
+ intr_mask.b.incomplisoin = 1;
+ intr_mask.b.incomplisoout = 1;
+ }
+ }
+ } else {
+ intr_mask.b.incomplisoin = 1;
+ intr_mask.b.incomplisoout = 1;
+ }
+#endif /* DWC_EN_ISOC */
+
+ /** @todo NGS: Should this be a module parameter? */
+#ifdef USE_PERIODIC_EP
+ intr_mask.b.isooutdrop = 1;
+ intr_mask.b.eopframe = 1;
+ intr_mask.b.incomplisoin = 1;
+ intr_mask.b.incomplisoout = 1;
+#endif
+
+ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
+
+ DWC_DEBUGPL(DBG_CIL, "%s() gintmsk=%0x\n", __func__,
+ DWC_READ_REG32(&global_regs->gintmsk));
+}
+
+/**
+ * This function initializes the DWC_otg controller registers for
+ * device mode.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ */
+void dwc_otg_core_dev_init(dwc_otg_core_if_t * core_if)
+{
+ int i;
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ dwc_otg_core_params_t *params = core_if->core_params;
+ dcfg_data_t dcfg = {.d32 = 0 };
+ depctl_data_t diepctl = {.d32 = 0 };
+ grstctl_t resetctl = {.d32 = 0 };
+ uint32_t rx_fifo_size;
+ fifosize_data_t nptxfifosize;
+ fifosize_data_t txfifosize;
+ dthrctl_data_t dthrctl;
+ fifosize_data_t ptxfifosize;
+ uint16_t rxfsiz, nptxfsiz;
+ gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
+ hwcfg3_data_t hwcfg3 = {.d32 = 0 };
+
+ /* Restart the Phy Clock */
+ DWC_WRITE_REG32(core_if->pcgcctl, 0);
+
+ /* Device configuration register */
+ init_devspd(core_if);
+ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+ dcfg.b.descdma = (core_if->dma_desc_enable) ? 1 : 0;
+ dcfg.b.perfrint = DWC_DCFG_FRAME_INTERVAL_80;
+ /* Enable Device OUT NAK in case of DDMA mode*/
+ if (core_if->core_params->dev_out_nak) {
+ dcfg.b.endevoutnak = 1;
+ }
+
+ if (core_if->core_params->cont_on_bna) {
+ dctl_data_t dctl = {.d32 = 0 };
+ dctl.b.encontonbna = 1;
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, 0, dctl.d32);
+ }
+
+
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+ /* Configure data FIFO sizes */
+ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
+ DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n",
+ core_if->total_fifo_size);
+ DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n",
+ params->dev_rx_fifo_size);
+ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n",
+ params->dev_nperio_tx_fifo_size);
+
+ /* Rx FIFO */
+ DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->grxfsiz));
+
+#ifdef DWC_UTE_CFI
+ core_if->pwron_rxfsiz = DWC_READ_REG32(&global_regs->grxfsiz);
+ core_if->init_rxfsiz = params->dev_rx_fifo_size;
+#endif
+ rx_fifo_size = params->dev_rx_fifo_size;
+ DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size);
+
+ DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->grxfsiz));
+
+ /** Set Periodic Tx FIFO Mask all bits 0 */
+ core_if->p_tx_msk = 0;
+
+ /** Set Tx FIFO Mask all bits 0 */
+ core_if->tx_msk = 0;
+
+ if (core_if->en_multiple_tx_fifo == 0) {
+ /* Non-periodic Tx FIFO */
+ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size;
+ nptxfifosize.b.startaddr = 0x400;
+
+ DWC_WRITE_REG32(&global_regs->gnptxfsiz,
+ nptxfifosize.d32);
+
+ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+ /**@todo NGS: Fix Periodic FIFO Sizing! */
+ /*
+ * Periodic Tx FIFOs These FIFOs are numbered from 1 to 15.
+ * Indexes of the FIFO size module parameters in the
+ * dev_perio_tx_fifo_size array and the FIFO size registers in
+ * the dptxfsiz array run from 0 to 14.
+ */
+ /** @todo Finish debug of this */
+ ptxfifosize.b.startaddr =
+ nptxfifosize.b.startaddr + nptxfifosize.b.depth;
+ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) {
+ ptxfifosize.b.depth =
+ params->dev_perio_tx_fifo_size[i];
+ DWC_DEBUGPL(DBG_CIL,
+ "initial dtxfsiz[%d]=%08x\n", i,
+ DWC_READ_REG32(&global_regs->dtxfsiz
+ [i]));
+ DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
+ ptxfifosize.d32);
+ DWC_DEBUGPL(DBG_CIL, "new dtxfsiz[%d]=%08x\n",
+ i,
+ DWC_READ_REG32(&global_regs->dtxfsiz
+ [i]));
+ ptxfifosize.b.startaddr += ptxfifosize.b.depth;
+ }
+ } else {
+ /*
+ * Tx FIFOs These FIFOs are numbered from 1 to 15.
+ * Indexes of the FIFO size module parameters in the
+ * dev_tx_fifo_size array and the FIFO size registers in
+ * the dtxfsiz array run from 0 to 14.
+ */
+
+ /* Non-periodic Tx FIFO */
+ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+#ifdef DWC_UTE_CFI
+ core_if->pwron_gnptxfsiz =
+ (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16);
+ core_if->init_gnptxfsiz =
+ params->dev_nperio_tx_fifo_size;
+#endif
+ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size;
+ nptxfifosize.b.startaddr = 0x400;
+
+ DWC_WRITE_REG32(&global_regs->gnptxfsiz,
+ nptxfifosize.d32);
+
+ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+ txfifosize.b.startaddr =
+ nptxfifosize.b.startaddr + nptxfifosize.b.depth;
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+
+ txfifosize.b.depth =
+ params->dev_tx_fifo_size[i];
+
+ DWC_DEBUGPL(DBG_CIL,
+ "initial dtxfsiz[%d]=%08x\n",
+ i,
+ DWC_READ_REG32(&global_regs->dtxfsiz
+ [i]));
+
+#ifdef DWC_UTE_CFI
+ core_if->pwron_txfsiz[i] =
+ (DWC_READ_REG32
+ (&global_regs->dtxfsiz[i]) >> 16);
+ core_if->init_txfsiz[i] =
+ params->dev_tx_fifo_size[i];
+#endif
+ DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
+ txfifosize.d32);
+
+ DWC_DEBUGPL(DBG_CIL,
+ "new dtxfsiz[%d]=%08x\n",
+ i,
+ DWC_READ_REG32(&global_regs->dtxfsiz
+ [i]));
+
+ txfifosize.b.startaddr += txfifosize.b.depth;
+ }
+ /* Calculating DFIFOCFG for Device mode to include RxFIFO and NPTXFIFO */
+ gdfifocfg.d32 = DWC_READ_REG32(&global_regs->gdfifocfg);
+ hwcfg3.d32 = DWC_READ_REG32(&global_regs->ghwcfg3);
+ gdfifocfg.b.gdfifocfg = (DWC_READ_REG32(&global_regs->ghwcfg3) >> 16);
+ DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32);
+ rxfsiz = (DWC_READ_REG32(&global_regs->grxfsiz) & 0x0000ffff);
+ nptxfsiz = (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16);
+ gdfifocfg.b.epinfobase = rxfsiz + nptxfsiz;
+ DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32);
+ }
+ }
+
+ /* Flush the FIFOs */
+ dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */
+ dwc_otg_flush_rx_fifo(core_if);
+
+ /* Flush the Learning Queue. */
+ resetctl.b.intknqflsh = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32);
+
+ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) {
+ core_if->start_predict = 0;
+ for (i = 0; i<= core_if->dev_if->num_in_eps; ++i) {
+ core_if->nextep_seq[i] = 0xff; // 0xff - EP not active
+ }
+ core_if->nextep_seq[0] = 0;
+ core_if->first_in_nextep_seq = 0;
+ diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl);
+ diepctl.b.nextep = 0;
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
+
+ /* Update IN Endpoint Mismatch Count by active IN NP EP count + 1 */
+ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+ dcfg.b.epmscnt = 2;
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+ DWC_DEBUGPL(DBG_CILV,"%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+ __func__, core_if->first_in_nextep_seq);
+ for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+ DWC_DEBUGPL(DBG_CILV, "%2d ", core_if->nextep_seq[i]);
+ }
+ DWC_DEBUGPL(DBG_CILV,"\n");
+ }
+
+ /* Clear all pending Device Interrupts */
+ /** @todo - if the condition needed to be checked
+ * or in any case all pending interrutps should be cleared?
+ */
+ if (core_if->multiproc_int_enable) {
+ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+ DWC_WRITE_REG32(&dev_if->
+ dev_global_regs->diepeachintmsk[i], 0);
+ }
+
+ for (i = 0; i < core_if->dev_if->num_out_eps; ++i) {
+ DWC_WRITE_REG32(&dev_if->
+ dev_global_regs->doepeachintmsk[i], 0);
+ }
+
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->deachint, 0xFFFFFFFF);
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->deachintmsk, 0);
+ } else {
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->diepmsk, 0);
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->doepmsk, 0);
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->daint, 0xFFFFFFFF);
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->daintmsk, 0);
+ }
+
+ for (i = 0; i <= dev_if->num_in_eps; i++) {
+ depctl_data_t depctl;
+ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+ if (depctl.b.epena) {
+ depctl.d32 = 0;
+ depctl.b.epdis = 1;
+ depctl.b.snak = 1;
+ } else {
+ depctl.d32 = 0;
+ }
+
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32);
+
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->dieptsiz, 0);
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepdma, 0);
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepint, 0xFF);
+ }
+
+ for (i = 0; i <= dev_if->num_out_eps; i++) {
+ depctl_data_t depctl;
+ depctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl);
+ if (depctl.b.epena) {
+ depctl.d32 = 0;
+ depctl.b.epdis = 1;
+ depctl.b.snak = 1;
+ } else {
+ depctl.d32 = 0;
+ }
+
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, depctl.d32);
+
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doeptsiz, 0);
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepdma, 0);
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepint, 0xFF);
+ }
+
+ if (core_if->en_multiple_tx_fifo && core_if->dma_enable) {
+ dev_if->non_iso_tx_thr_en = params->thr_ctl & 0x1;
+ dev_if->iso_tx_thr_en = (params->thr_ctl >> 1) & 0x1;
+ dev_if->rx_thr_en = (params->thr_ctl >> 2) & 0x1;
+
+ dev_if->rx_thr_length = params->rx_thr_length;
+ dev_if->tx_thr_length = params->tx_thr_length;
+
+ dev_if->setup_desc_index = 0;
+
+ dthrctl.d32 = 0;
+ dthrctl.b.non_iso_thr_en = dev_if->non_iso_tx_thr_en;
+ dthrctl.b.iso_thr_en = dev_if->iso_tx_thr_en;
+ dthrctl.b.tx_thr_len = dev_if->tx_thr_length;
+ dthrctl.b.rx_thr_en = dev_if->rx_thr_en;
+ dthrctl.b.rx_thr_len = dev_if->rx_thr_length;
+ dthrctl.b.ahb_thr_ratio = params->ahb_thr_ratio;
+
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->dtknqr3_dthrctl,
+ dthrctl.d32);
+
+ DWC_DEBUGPL(DBG_CIL,
+ "Non ISO Tx Thr - %d\nISO Tx Thr - %d\nRx Thr - %d\nTx Thr Len - %d\nRx Thr Len - %d\n",
+ dthrctl.b.non_iso_thr_en, dthrctl.b.iso_thr_en,
+ dthrctl.b.rx_thr_en, dthrctl.b.tx_thr_len,
+ dthrctl.b.rx_thr_len);
+
+ }
+
+ dwc_otg_enable_device_interrupts(core_if);
+
+ {
+ diepmsk_data_t msk = {.d32 = 0 };
+ msk.b.txfifoundrn = 1;
+ if (core_if->multiproc_int_enable) {
+ DWC_MODIFY_REG32(&dev_if->
+ dev_global_regs->diepeachintmsk[0],
+ msk.d32, msk.d32);
+ } else {
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->diepmsk,
+ msk.d32, msk.d32);
+ }
+ }
+
+ if (core_if->multiproc_int_enable) {
+ /* Set NAK on Babble */
+ dctl_data_t dctl = {.d32 = 0 };
+ dctl.b.nakonbble = 1;
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, 0, dctl.d32);
+ }
+
+ if (core_if->snpsid >= OTG_CORE_REV_2_94a) {
+ dctl_data_t dctl = {.d32 = 0 };
+ dctl.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dctl);
+ dctl.b.sftdiscon = 0;
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl, dctl.d32);
+ }
+}
+
+/**
+ * This function enables the Host mode interrupts.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ */
+void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__);
+
+ /* Disable all interrupts. */
+ DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+ /* Clear any pending interrupts. */
+ DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF);
+
+ /* Enable the common interrupts */
+ dwc_otg_enable_common_interrupts(core_if);
+
+ /*
+ * Enable host mode interrupts without disturbing common
+ * interrupts.
+ */
+
+ intr_mask.b.disconnect = 1;
+ intr_mask.b.portintr = 1;
+ intr_mask.b.hcintr = 1;
+
+ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
+}
+
+/**
+ * This function disables the Host Mode interrupts.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ */
+void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_DEBUGPL(DBG_CILV, "%s()\n", __func__);
+
+ /*
+ * Disable host mode interrupts without disturbing common
+ * interrupts.
+ */
+ intr_mask.b.sofintr = 1;
+ intr_mask.b.portintr = 1;
+ intr_mask.b.hcintr = 1;
+ intr_mask.b.ptxfempty = 1;
+ intr_mask.b.nptxfempty = 1;
+
+ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, 0);
+}
+
+/**
+ * This function initializes the DWC_otg controller registers for
+ * host mode.
+ *
+ * This function flushes the Tx and Rx FIFOs and it flushes any entries in the
+ * request queues. Host channels are reset to ensure that they are ready for
+ * performing transfers.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ */
+void dwc_otg_core_host_init(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ dwc_otg_host_if_t *host_if = core_if->host_if;
+ dwc_otg_core_params_t *params = core_if->core_params;
+ hprt0_data_t hprt0 = {.d32 = 0 };
+ fifosize_data_t nptxfifosize;
+ fifosize_data_t ptxfifosize;
+ uint16_t rxfsiz, nptxfsiz, hptxfsiz;
+ gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
+ int i;
+ hcchar_data_t hcchar;
+ hcfg_data_t hcfg;
+ hfir_data_t hfir;
+ dwc_otg_hc_regs_t *hc_regs;
+ int num_channels;
+ gotgctl_data_t gotgctl = {.d32 = 0 };
+
+ DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, core_if);
+
+ /* Restart the Phy Clock */
+ DWC_WRITE_REG32(core_if->pcgcctl, 0);
+
+ /* Initialize Host Configuration Register */
+ init_fslspclksel(core_if);
+ if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
+ hcfg.d32 = DWC_READ_REG32(&host_if->host_global_regs->hcfg);
+ hcfg.b.fslssupp = 1;
+ DWC_WRITE_REG32(&host_if->host_global_regs->hcfg, hcfg.d32);
+
+ }
+
+ /* This bit allows dynamic reloading of the HFIR register
+ * during runtime. This bit needs to be programmed during
+ * initial configuration and its value must not be changed
+ * during runtime.*/
+ if (core_if->core_params->reload_ctl == 1) {
+ hfir.d32 = DWC_READ_REG32(&host_if->host_global_regs->hfir);
+ hfir.b.hfirrldctrl = 1;
+ DWC_WRITE_REG32(&host_if->host_global_regs->hfir, hfir.d32);
+ }
+
+ if (core_if->core_params->dma_desc_enable) {
+ uint8_t op_mode = core_if->hwcfg2.b.op_mode;
+ if (!
+ (core_if->hwcfg4.b.desc_dma
+ && (core_if->snpsid >= OTG_CORE_REV_2_90a)
+ && ((op_mode == DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
+ || (op_mode == DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG)
+ || (op_mode ==
+ DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG)
+ || (op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)
+ || (op_mode ==
+ DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST)))) {
+
+ DWC_ERROR("Host can't operate in Descriptor DMA mode.\n"
+ "Either core version is below 2.90a or "
+ "GHWCFG2, GHWCFG4 registers' values do not allow Descriptor DMA in host mode.\n"
+ "To run the driver in Buffer DMA host mode set dma_desc_enable "
+ "module parameter to 0.\n");
+ return;
+ }
+ hcfg.d32 = DWC_READ_REG32(&host_if->host_global_regs->hcfg);
+ hcfg.b.descdma = 1;
+ DWC_WRITE_REG32(&host_if->host_global_regs->hcfg, hcfg.d32);
+ }
+
+ /* Configure data FIFO sizes */
+ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
+ DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n",
+ core_if->total_fifo_size);
+ DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n",
+ params->host_rx_fifo_size);
+ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n",
+ params->host_nperio_tx_fifo_size);
+ DWC_DEBUGPL(DBG_CIL, "P Tx FIFO Size=%d\n",
+ params->host_perio_tx_fifo_size);
+
+ /* Rx FIFO */
+ DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->grxfsiz));
+ DWC_WRITE_REG32(&global_regs->grxfsiz,
+ params->host_rx_fifo_size);
+ DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->grxfsiz));
+
+ /* Non-periodic Tx FIFO */
+ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->gnptxfsiz));
+ nptxfifosize.b.depth = params->host_nperio_tx_fifo_size;
+ nptxfifosize.b.startaddr = 0x400;
+
+ DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32);
+ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+ /* Periodic Tx FIFO */
+ DWC_DEBUGPL(DBG_CIL, "initial hptxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->hptxfsiz));
+ ptxfifosize.b.depth = params->host_perio_tx_fifo_size;
+ ptxfifosize.b.startaddr = 0xC00;
+ DWC_WRITE_REG32(&global_regs->hptxfsiz, ptxfifosize.d32);
+ DWC_DEBUGPL(DBG_CIL, "new hptxfsiz=%08x\n",
+ DWC_READ_REG32(&global_regs->hptxfsiz));
+
+ if (core_if->en_multiple_tx_fifo) {
+ /* Global DFIFOCFG calculation for Host mode - include RxFIFO, NPTXFIFO and HPTXFIFO */
+ gdfifocfg.d32 = DWC_READ_REG32(&global_regs->gdfifocfg);
+ rxfsiz = (DWC_READ_REG32(&global_regs->grxfsiz) & 0x0000ffff);
+ nptxfsiz = (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16);
+ hptxfsiz = (DWC_READ_REG32(&global_regs->hptxfsiz) >> 16);
+ gdfifocfg.b.epinfobase = rxfsiz + nptxfsiz + hptxfsiz;
+ DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32);
+ }
+ }
+
+ /* TODO - check this */
+ /* Clear Host Set HNP Enable in the OTG Control Register */
+ gotgctl.b.hstsethnpen = 1;
+ DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
+ /* Make sure the FIFOs are flushed. */
+ dwc_otg_flush_tx_fifo(core_if, 0x10 /* all TX FIFOs */ );
+ dwc_otg_flush_rx_fifo(core_if);
+
+ /* Clear Host Set HNP Enable in the OTG Control Register */
+ gotgctl.b.hstsethnpen = 1;
+ DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
+
+ if (!core_if->core_params->dma_desc_enable) {
+ /* Flush out any leftover queued requests. */
+ num_channels = core_if->core_params->host_channels;
+
+ for (i = 0; i < num_channels; i++) {
+ hc_regs = core_if->host_if->hc_regs[i];
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcchar.b.chen = 0;
+ hcchar.b.chdis = 1;
+ hcchar.b.epdir = 0;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+ }
+
+ /* Halt all channels to put them into a known state. */
+ for (i = 0; i < num_channels; i++) {
+ int count = 0;
+ hc_regs = core_if->host_if->hc_regs[i];
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcchar.b.chen = 1;
+ hcchar.b.chdis = 1;
+ hcchar.b.epdir = 0;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+ DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d\n", __func__, i);
+ do {
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ if (++count > 1000) {
+ DWC_ERROR
+ ("%s: Unable to clear halt on channel %d\n",
+ __func__, i);
+ break;
+ }
+ dwc_udelay(1);
+ } while (hcchar.b.chen);
+ }
+ }
+
+ /* Turn on the vbus power. */
+ DWC_PRINTF("Init: Port Power? op_state=%d\n", core_if->op_state);
+ if (core_if->op_state == A_HOST) {
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ DWC_PRINTF("Init: Power Port (%d)\n", hprt0.b.prtpwr);
+ if (hprt0.b.prtpwr == 0) {
+ hprt0.b.prtpwr = 1;
+ DWC_WRITE_REG32(host_if->hprt0, hprt0.d32);
+ }
+ }
+
+ dwc_otg_enable_host_interrupts(core_if);
+}
+
+/**
+ * Prepares a host channel for transferring packets to/from a specific
+ * endpoint. The HCCHARn register is set up with the characteristics specified
+ * in _hc. Host channel interrupts that may need to be serviced while this
+ * transfer is in progress are enabled.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ * @param hc Information needed to initialize the host channel
+ */
+void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+ uint32_t intr_enable;
+ hcintmsk_data_t hc_intr_mask;
+ gintmsk_data_t gintmsk = {.d32 = 0 };
+ hcchar_data_t hcchar;
+ hcsplt_data_t hcsplt;
+
+ uint8_t hc_num = hc->hc_num;
+ dwc_otg_host_if_t *host_if = core_if->host_if;
+ dwc_otg_hc_regs_t *hc_regs = host_if->hc_regs[hc_num];
+
+ /* Clear old interrupt conditions for this host channel. */
+ hc_intr_mask.d32 = 0xFFFFFFFF;
+ hc_intr_mask.b.reserved14_31 = 0;
+ DWC_WRITE_REG32(&hc_regs->hcint, hc_intr_mask.d32);
+
+ /* Enable channel interrupts required for this transfer. */
+ hc_intr_mask.d32 = 0;
+ hc_intr_mask.b.chhltd = 1;
+ if (core_if->dma_enable) {
+ /* For Descriptor DMA mode core halts the channel on AHB error. Interrupt is not required */
+ if (!core_if->dma_desc_enable)
+ hc_intr_mask.b.ahberr = 1;
+ else {
+ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+ hc_intr_mask.b.xfercompl = 1;
+ }
+
+ if (hc->error_state && !hc->do_split &&
+ hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+ hc_intr_mask.b.ack = 1;
+ if (hc->ep_is_in) {
+ hc_intr_mask.b.datatglerr = 1;
+ if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) {
+ hc_intr_mask.b.nak = 1;
+ }
+ }
+ }
+ } else {
+ switch (hc->ep_type) {
+ case DWC_OTG_EP_TYPE_CONTROL:
+ case DWC_OTG_EP_TYPE_BULK:
+ hc_intr_mask.b.xfercompl = 1;
+ hc_intr_mask.b.stall = 1;
+ hc_intr_mask.b.xacterr = 1;
+ hc_intr_mask.b.datatglerr = 1;
+ if (hc->ep_is_in) {
+ hc_intr_mask.b.bblerr = 1;
+ } else {
+ hc_intr_mask.b.nak = 1;
+ hc_intr_mask.b.nyet = 1;
+ if (hc->do_ping) {
+ hc_intr_mask.b.ack = 1;
+ }
+ }
+
+ if (hc->do_split) {
+ hc_intr_mask.b.nak = 1;
+ if (hc->complete_split) {
+ hc_intr_mask.b.nyet = 1;
+ } else {
+ hc_intr_mask.b.ack = 1;
+ }
+ }
+
+ if (hc->error_state) {
+ hc_intr_mask.b.ack = 1;
+ }
+ break;
+ case DWC_OTG_EP_TYPE_INTR:
+ hc_intr_mask.b.xfercompl = 1;
+ hc_intr_mask.b.nak = 1;
+ hc_intr_mask.b.stall = 1;
+ hc_intr_mask.b.xacterr = 1;
+ hc_intr_mask.b.datatglerr = 1;
+ hc_intr_mask.b.frmovrun = 1;
+
+ if (hc->ep_is_in) {
+ hc_intr_mask.b.bblerr = 1;
+ }
+ if (hc->error_state) {
+ hc_intr_mask.b.ack = 1;
+ }
+ if (hc->do_split) {
+ if (hc->complete_split) {
+ hc_intr_mask.b.nyet = 1;
+ } else {
+ hc_intr_mask.b.ack = 1;
+ }
+ }
+ break;
+ case DWC_OTG_EP_TYPE_ISOC:
+ hc_intr_mask.b.xfercompl = 1;
+ hc_intr_mask.b.frmovrun = 1;
+ hc_intr_mask.b.ack = 1;
+
+ if (hc->ep_is_in) {
+ hc_intr_mask.b.xacterr = 1;
+ hc_intr_mask.b.bblerr = 1;
+ }
+ break;
+ }
+ }
+ DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32);
+
+ /* Enable the top level host channel interrupt. */
+ intr_enable = (1 << hc_num);
+ DWC_MODIFY_REG32(&host_if->host_global_regs->haintmsk, 0, intr_enable);
+
+ /* Make sure host channel interrupts are enabled. */
+ gintmsk.b.hcintr = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
+
+ /*
+ * Program the HCCHARn register with the endpoint characteristics for
+ * the current transfer.
+ */
+ hcchar.d32 = 0;
+ hcchar.b.devaddr = hc->dev_addr;
+ hcchar.b.epnum = hc->ep_num;
+ hcchar.b.epdir = hc->ep_is_in;
+ hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW);
+ hcchar.b.eptype = hc->ep_type;
+ hcchar.b.mps = hc->max_packet;
+
+ DWC_WRITE_REG32(&host_if->hc_regs[hc_num]->hcchar, hcchar.d32);
+
+ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+ DWC_DEBUGPL(DBG_HCDV, " Dev Addr: %d\n", hcchar.b.devaddr);
+ DWC_DEBUGPL(DBG_HCDV, " Ep Num: %d\n", hcchar.b.epnum);
+ DWC_DEBUGPL(DBG_HCDV, " Is In: %d\n", hcchar.b.epdir);
+ DWC_DEBUGPL(DBG_HCDV, " Is Low Speed: %d\n", hcchar.b.lspddev);
+ DWC_DEBUGPL(DBG_HCDV, " Ep Type: %d\n", hcchar.b.eptype);
+ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps);
+ DWC_DEBUGPL(DBG_HCDV, " Multi Cnt: %d\n", hcchar.b.multicnt);
+
+ /*
+ * Program the HCSPLIT register for SPLITs
+ */
+ hcsplt.d32 = 0;
+ if (hc->do_split) {
+ DWC_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n",
+ hc->hc_num,
+ hc->complete_split ? "CSPLIT" : "SSPLIT");
+ hcsplt.b.compsplt = hc->complete_split;
+ hcsplt.b.xactpos = hc->xact_pos;
+ hcsplt.b.hubaddr = hc->hub_addr;
+ hcsplt.b.prtaddr = hc->port_addr;
+ DWC_DEBUGPL(DBG_HCDV, " comp split %d\n", hc->complete_split);
+ DWC_DEBUGPL(DBG_HCDV, " xact pos %d\n", hc->xact_pos);
+ DWC_DEBUGPL(DBG_HCDV, " hub addr %d\n", hc->hub_addr);
+ DWC_DEBUGPL(DBG_HCDV, " port addr %d\n", hc->port_addr);
+ DWC_DEBUGPL(DBG_HCDV, " is_in %d\n", hc->ep_is_in);
+ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps);
+ DWC_DEBUGPL(DBG_HCDV, " xferlen: %d\n", hc->xfer_len);
+ }
+ DWC_WRITE_REG32(&host_if->hc_regs[hc_num]->hcsplt, hcsplt.d32);
+
+}
+
+/**
+ * Attempts to halt a host channel. This function should only be called in
+ * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under
+ * normal circumstances in DMA mode, the controller halts the channel when the
+ * transfer is complete or a condition occurs that requires application
+ * intervention.
+ *
+ * In slave mode, checks for a free request queue entry, then sets the Channel
+ * Enable and Channel Disable bits of the Host Channel Characteristics
+ * register of the specified channel to intiate the halt. If there is no free
+ * request queue entry, sets only the Channel Disable bit of the HCCHARn
+ * register to flush requests for this channel. In the latter case, sets a
+ * flag to indicate that the host channel needs to be halted when a request
+ * queue slot is open.
+ *
+ * In DMA mode, always sets the Channel Enable and Channel Disable bits of the
+ * HCCHARn register. The controller ensures there is space in the request
+ * queue before submitting the halt request.
+ *
+ * Some time may elapse before the core flushes any posted requests for this
+ * host channel and halts. The Channel Halted interrupt handler completes the
+ * deactivation of the host channel.
+ *
+ * @param core_if Controller register interface.
+ * @param hc Host channel to halt.
+ * @param halt_status Reason for halting the channel.
+ */
+void dwc_otg_hc_halt(dwc_otg_core_if_t * core_if,
+ dwc_hc_t * hc, dwc_otg_halt_status_e halt_status)
+{
+ gnptxsts_data_t nptxsts;
+ hptxsts_data_t hptxsts;
+ hcchar_data_t hcchar;
+ dwc_otg_hc_regs_t *hc_regs;
+ dwc_otg_core_global_regs_t *global_regs;
+ dwc_otg_host_global_regs_t *host_global_regs;
+
+ hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+ global_regs = core_if->core_global_regs;
+ host_global_regs = core_if->host_if->host_global_regs;
+
+ DWC_ASSERT(!(halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS),
+ "halt_status = %d\n", halt_status);
+
+ if (halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
+ halt_status == DWC_OTG_HC_XFER_AHB_ERR) {
+ /*
+ * Disable all channel interrupts except Ch Halted. The QTD
+ * and QH state associated with this transfer has been cleared
+ * (in the case of URB_DEQUEUE), so the channel needs to be
+ * shut down carefully to prevent crashes.
+ */
+ hcintmsk_data_t hcintmsk;
+ hcintmsk.d32 = 0;
+ hcintmsk.b.chhltd = 1;
+ DWC_WRITE_REG32(&hc_regs->hcintmsk, hcintmsk.d32);
+
+ /*
+ * Make sure no other interrupts besides halt are currently
+ * pending. Handling another interrupt could cause a crash due
+ * to the QTD and QH state.
+ */
+ DWC_WRITE_REG32(&hc_regs->hcint, ~hcintmsk.d32);
+
+ /*
+ * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
+ * even if the channel was already halted for some other
+ * reason.
+ */
+ hc->halt_status = halt_status;
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ if (hcchar.b.chen == 0) {
+ /*
+ * The channel is either already halted or it hasn't
+ * started yet. In DMA mode, the transfer may halt if
+ * it finishes normally or a condition occurs that
+ * requires driver intervention. Don't want to halt
+ * the channel again. In either Slave or DMA mode,
+ * it's possible that the transfer has been assigned
+ * to a channel, but not started yet when an URB is
+ * dequeued. Don't want to halt a channel that hasn't
+ * started yet.
+ */
+ return;
+ }
+ }
+ if (hc->halt_pending) {
+ /*
+ * A halt has already been issued for this channel. This might
+ * happen when a transfer is aborted by a higher level in
+ * the stack.
+ */
+#ifdef DEBUG
+ DWC_PRINTF
+ ("*** %s: Channel %d, _hc->halt_pending already set ***\n",
+ __func__, hc->hc_num);
+
+#endif
+ return;
+ }
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* No need to set the bit in DDMA for disabling the channel */
+ //TODO check it everywhere channel is disabled
+ if (!core_if->core_params->dma_desc_enable)
+ hcchar.b.chen = 1;
+ hcchar.b.chdis = 1;
+
+ if (!core_if->dma_enable) {
+ /* Check for space in the request queue to issue the halt. */
+ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+ hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
+ nptxsts.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+ if (nptxsts.b.nptxqspcavail == 0) {
+ hcchar.b.chen = 0;
+ }
+ } else {
+ hptxsts.d32 =
+ DWC_READ_REG32(&host_global_regs->hptxsts);
+ if ((hptxsts.b.ptxqspcavail == 0)
+ || (core_if->queuing_high_bandwidth)) {
+ hcchar.b.chen = 0;
+ }
+ }
+ }
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+ hc->halt_status = halt_status;
+
+ if (hcchar.b.chen) {
+ hc->halt_pending = 1;
+ hc->halt_on_queue = 0;
+ } else {
+ hc->halt_on_queue = 1;
+ }
+
+ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+ DWC_DEBUGPL(DBG_HCDV, " hcchar: 0x%08x\n", hcchar.d32);
+ DWC_DEBUGPL(DBG_HCDV, " halt_pending: %d\n", hc->halt_pending);
+ DWC_DEBUGPL(DBG_HCDV, " halt_on_queue: %d\n", hc->halt_on_queue);
+ DWC_DEBUGPL(DBG_HCDV, " halt_status: %d\n", hc->halt_status);
+
+ return;
+}
+
+/**
+ * Clears the transfer state for a host channel. This function is normally
+ * called after a transfer is done and the host channel is being released.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param hc Identifies the host channel to clean up.
+ */
+void dwc_otg_hc_cleanup(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+ dwc_otg_hc_regs_t *hc_regs;
+
+ hc->xfer_started = 0;
+
+ /*
+ * Clear channel interrupt enables and any unhandled channel interrupt
+ * conditions.
+ */
+ hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0);
+ DWC_WRITE_REG32(&hc_regs->hcint, 0xFFFFFFFF);
+#ifdef DEBUG
+ DWC_TIMER_CANCEL(core_if->hc_xfer_timer[hc->hc_num]);
+#endif
+}
+
+/**
+ * Sets the channel property that indicates in which frame a periodic transfer
+ * should occur. This is always set to the _next_ frame. This function has no
+ * effect on non-periodic transfers.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param hc Identifies the host channel to set up and its properties.
+ * @param hcchar Current value of the HCCHAR register for the specified host
+ * channel.
+ */
+static inline void hc_set_even_odd_frame(dwc_otg_core_if_t * core_if,
+ dwc_hc_t * hc, hcchar_data_t * hcchar)
+{
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ hfnum_data_t hfnum;
+ hfnum.d32 =
+ DWC_READ_REG32(&core_if->host_if->host_global_regs->hfnum);
+
+ /* 1 if _next_ frame is odd, 0 if it's even */
+ hcchar->b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1;
+#ifdef DEBUG
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR && hc->do_split
+ && !hc->complete_split) {
+ switch (hfnum.b.frnum & 0x7) {
+ case 7:
+ core_if->hfnum_7_samples++;
+ core_if->hfnum_7_frrem_accum += hfnum.b.frrem;
+ break;
+ case 0:
+ core_if->hfnum_0_samples++;
+ core_if->hfnum_0_frrem_accum += hfnum.b.frrem;
+ break;
+ default:
+ core_if->hfnum_other_samples++;
+ core_if->hfnum_other_frrem_accum +=
+ hfnum.b.frrem;
+ break;
+ }
+ }
+#endif
+ }
+}
+
+#ifdef DEBUG
+void hc_xfer_timeout(void *ptr)
+{
+ hc_xfer_info_t *xfer_info = NULL;
+ int hc_num = 0;
+
+ if (ptr)
+ xfer_info = (hc_xfer_info_t *) ptr;
+
+ if (!xfer_info->hc) {
+ DWC_ERROR("xfer_info->hc = %p\n", xfer_info->hc);
+ return;
+ }
+
+ hc_num = xfer_info->hc->hc_num;
+ DWC_WARN("%s: timeout on channel %d\n", __func__, hc_num);
+ DWC_WARN(" start_hcchar_val 0x%08x\n",
+ xfer_info->core_if->start_hcchar_val[hc_num]);
+}
+#endif
+
+void ep_xfer_timeout(void *ptr)
+{
+ ep_xfer_info_t *xfer_info = NULL;
+ int ep_num = 0;
+ dctl_data_t dctl = {.d32 = 0 };
+ gintsts_data_t gintsts = {.d32 = 0 };
+ gintmsk_data_t gintmsk = {.d32 = 0 };
+
+ if (ptr)
+ xfer_info = (ep_xfer_info_t *) ptr;
+
+ if (!xfer_info->ep) {
+ DWC_ERROR("xfer_info->ep = %p\n", xfer_info->ep);
+ return;
+ }
+
+ ep_num = xfer_info->ep->num;
+ DWC_WARN("%s: timeout on endpoit %d\n", __func__, ep_num);
+ /* Put the sate to 2 as it was time outed */
+ xfer_info->state = 2;
+
+ dctl.d32 = DWC_READ_REG32(&xfer_info->core_if->
+ dev_if->dev_global_regs->dctl);
+ gintsts.d32 = DWC_READ_REG32(&xfer_info->core_if->
+ core_global_regs->gintsts);
+ gintmsk.d32 = DWC_READ_REG32(&xfer_info->core_if->
+ core_global_regs->gintmsk);
+
+ if (!gintmsk.b.goutnakeff) {
+ /* Unmask it */
+ gintmsk.b.goutnakeff = 1;
+ DWC_WRITE_REG32(&xfer_info->core_if->
+ core_global_regs->gintmsk, gintmsk.d32);
+
+ }
+
+ if (!gintsts.b.goutnakeff) {
+ dctl.b.sgoutnak = 1;
+ }
+ DWC_WRITE_REG32(&xfer_info->core_if->dev_if->
+ dev_global_regs->dctl, dctl.d32);
+
+}
+
+void set_pid_isoc(dwc_hc_t * hc)
+{
+ /* Set up the initial PID for the transfer. */
+ if (hc->speed == DWC_OTG_EP_SPEED_HIGH) {
+ if (hc->ep_is_in) {
+ if (hc->multi_count == 1) {
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+ } else if (hc->multi_count == 2) {
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
+ } else {
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA2;
+ }
+ } else {
+ if (hc->multi_count == 1) {
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+ } else {
+ hc->data_pid_start = DWC_OTG_HC_PID_MDATA;
+ }
+ }
+ } else {
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+ }
+}
+
+/**
+ * This function does the setup for a data transfer for a host channel and
+ * starts the transfer. May be called in either Slave mode or DMA mode. In
+ * Slave mode, the caller must ensure that there is sufficient space in the
+ * request queue and Tx Data FIFO.
+ *
+ * For an OUT transfer in Slave mode, it loads a data packet into the
+ * appropriate FIFO. If necessary, additional data packets will be loaded in
+ * the Host ISR.
+ *
+ * For an IN transfer in Slave mode, a data packet is requested. The data
+ * packets are unloaded from the Rx FIFO in the Host ISR. If necessary,
+ * additional data packets are requested in the Host ISR.
+ *
+ * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ
+ * register along with a packet count of 1 and the channel is enabled. This
+ * causes a single PING transaction to occur. Other fields in HCTSIZ are
+ * simply set to 0 since no data transfer occurs in this case.
+ *
+ * For a PING transfer in DMA mode, the HCTSIZ register is initialized with
+ * all the information required to perform the subsequent data transfer. In
+ * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the
+ * controller performs the entire PING protocol, then starts the data
+ * transfer.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param hc Information needed to initialize the host channel. The xfer_len
+ * value may be reduced to accommodate the max widths of the XferSize and
+ * PktCnt fields in the HCTSIZn register. The multi_count value may be changed
+ * to reflect the final xfer_len value.
+ */
+void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+ hcchar_data_t hcchar;
+ hctsiz_data_t hctsiz;
+ uint16_t num_packets;
+ uint32_t max_hc_xfer_size = core_if->core_params->max_transfer_size;
+ uint16_t max_hc_pkt_count = core_if->core_params->max_packet_count;
+ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+
+ hctsiz.d32 = 0;
+
+ if (hc->do_ping) {
+ if (!core_if->dma_enable) {
+ dwc_otg_hc_do_ping(core_if, hc);
+ hc->xfer_started = 1;
+ return;
+ } else {
+ hctsiz.b.dopng = 1;
+ }
+ }
+
+ if (hc->do_split) {
+ num_packets = 1;
+
+ if (hc->complete_split && !hc->ep_is_in) {
+ /* For CSPLIT OUT Transfer, set the size to 0 so the
+ * core doesn't expect any data written to the FIFO */
+ hc->xfer_len = 0;
+ } else if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) {
+ hc->xfer_len = hc->max_packet;
+ } else if (!hc->ep_is_in && (hc->xfer_len > 188)) {
+ hc->xfer_len = 188;
+ }
+
+ hctsiz.b.xfersize = hc->xfer_len;
+ } else {
+ /*
+ * Ensure that the transfer length and packet count will fit
+ * in the widths allocated for them in the HCTSIZn register.
+ */
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ /*
+ * Make sure the transfer size is no larger than one
+ * (micro)frame's worth of data. (A check was done
+ * when the periodic transfer was accepted to ensure
+ * that a (micro)frame's worth of data can be
+ * programmed into a channel.)
+ */
+ uint32_t max_periodic_len =
+ hc->multi_count * hc->max_packet;
+ if (hc->xfer_len > max_periodic_len) {
+ hc->xfer_len = max_periodic_len;
+ } else {
+ }
+ } else if (hc->xfer_len > max_hc_xfer_size) {
+ /* Make sure that xfer_len is a multiple of max packet size. */
+ hc->xfer_len = max_hc_xfer_size - hc->max_packet + 1;
+ }
+
+ if (hc->xfer_len > 0) {
+ num_packets =
+ (hc->xfer_len + hc->max_packet -
+ 1) / hc->max_packet;
+ if (num_packets > max_hc_pkt_count) {
+ num_packets = max_hc_pkt_count;
+ hc->xfer_len = num_packets * hc->max_packet;
+ }
+ } else {
+ /* Need 1 packet for transfer length of 0. */
+ num_packets = 1;
+ }
+
+ if (hc->ep_is_in) {
+ /* Always program an integral # of max packets for IN transfers. */
+ hc->xfer_len = num_packets * hc->max_packet;
+ }
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ /*
+ * Make sure that the multi_count field matches the
+ * actual transfer length.
+ */
+ hc->multi_count = num_packets;
+ }
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+ set_pid_isoc(hc);
+
+ hctsiz.b.xfersize = hc->xfer_len;
+ }
+
+ hc->start_pkt_count = num_packets;
+ hctsiz.b.pktcnt = num_packets;
+ hctsiz.b.pid = hc->data_pid_start;
+ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+ DWC_DEBUGPL(DBG_HCDV, " Xfer Size: %d\n", hctsiz.b.xfersize);
+ DWC_DEBUGPL(DBG_HCDV, " Num Pkts: %d\n", hctsiz.b.pktcnt);
+ DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid);
+
+ if (core_if->dma_enable) {
+ dwc_dma_t dma_addr;
+ if (hc->align_buff) {
+ dma_addr = hc->align_buff;
+ } else {
+ dma_addr = ((unsigned long)hc->xfer_buff & 0xffffffff);
+ }
+ DWC_WRITE_REG32(&hc_regs->hcdma, dma_addr);
+ }
+
+ /* Start the split */
+ if (hc->do_split) {
+ hcsplt_data_t hcsplt;
+ hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
+ hcsplt.b.spltena = 1;
+ DWC_WRITE_REG32(&hc_regs->hcsplt, hcsplt.d32);
+ }
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcchar.b.multicnt = hc->multi_count;
+ hc_set_even_odd_frame(core_if, hc, &hcchar);
+#ifdef DEBUG
+ core_if->start_hcchar_val[hc->hc_num] = hcchar.d32;
+ if (hcchar.b.chdis) {
+ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n",
+ __func__, hc->hc_num, hcchar.d32);
+ }
+#endif
+
+ /* Set host channel enable after all other setup is complete. */
+ hcchar.b.chen = 1;
+ hcchar.b.chdis = 0;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+ hc->xfer_started = 1;
+ hc->requests++;
+
+ if (!core_if->dma_enable && !hc->ep_is_in && hc->xfer_len > 0) {
+ /* Load OUT packet into the appropriate Tx FIFO. */
+ dwc_otg_hc_write_packet(core_if, hc);
+ }
+#ifdef DEBUG
+ if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) {
+ core_if->hc_xfer_info[hc->hc_num].core_if = core_if;
+ core_if->hc_xfer_info[hc->hc_num].hc = hc;
+
+ /* Start a timer for this transfer. */
+ DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000);
+ }
+#endif
+}
+
+/**
+ * This function does the setup for a data transfer for a host channel
+ * and starts the transfer in Descriptor DMA mode.
+ *
+ * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set.
+ * Sets PID and NTD values. For periodic transfers
+ * initializes SCHED_INFO field with micro-frame bitmap.
+ *
+ * Initializes HCDMA register with descriptor list address and CTD value
+ * then starts the transfer via enabling the channel.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param hc Information needed to initialize the host channel.
+ */
+void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+ hcchar_data_t hcchar;
+ hctsiz_data_t hctsiz;
+ hcdma_data_t hcdma;
+
+ hctsiz.d32 = 0;
+
+ if (hc->do_ping)
+ hctsiz.b_ddma.dopng = 1;
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+ set_pid_isoc(hc);
+
+ /* Packet Count and Xfer Size are not used in Descriptor DMA mode */
+ hctsiz.b_ddma.pid = hc->data_pid_start;
+ hctsiz.b_ddma.ntd = hc->ntd - 1; /* 0 - 1 descriptor, 1 - 2 descriptors, etc. */
+ hctsiz.b_ddma.schinfo = hc->schinfo; /* Non-zero only for high-speed interrupt endpoints */
+
+ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+ DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid);
+ DWC_DEBUGPL(DBG_HCDV, " NTD: %d\n", hctsiz.b_ddma.ntd);
+
+ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+ hcdma.d32 = 0;
+ hcdma.b.dma_addr = ((uint32_t) hc->desc_list_addr) >> 11;
+
+ /* Always start from first descriptor. */
+ hcdma.b.ctd = 0;
+ DWC_WRITE_REG32(&hc_regs->hcdma, hcdma.d32);
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcchar.b.multicnt = hc->multi_count;
+
+#ifdef DEBUG
+ core_if->start_hcchar_val[hc->hc_num] = hcchar.d32;
+ if (hcchar.b.chdis) {
+ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n",
+ __func__, hc->hc_num, hcchar.d32);
+ }
+#endif
+
+ /* Set host channel enable after all other setup is complete. */
+ hcchar.b.chen = 1;
+ hcchar.b.chdis = 0;
+
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+ hc->xfer_started = 1;
+ hc->requests++;
+
+#ifdef DEBUG
+ if ((hc->ep_type != DWC_OTG_EP_TYPE_INTR)
+ && (hc->ep_type != DWC_OTG_EP_TYPE_ISOC)) {
+ core_if->hc_xfer_info[hc->hc_num].core_if = core_if;
+ core_if->hc_xfer_info[hc->hc_num].hc = hc;
+ /* Start a timer for this transfer. */
+ DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000);
+ }
+#endif
+
+}
+
+/**
+ * This function continues a data transfer that was started by previous call
+ * to <code>dwc_otg_hc_start_transfer</code>. The caller must ensure there is
+ * sufficient space in the request queue and Tx Data FIFO. This function
+ * should only be called in Slave mode. In DMA mode, the controller acts
+ * autonomously to complete transfers programmed to a host channel.
+ *
+ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO
+ * if there is any data remaining to be queued. For an IN transfer, another
+ * data packet is always requested. For the SETUP phase of a control transfer,
+ * this function does nothing.
+ *
+ * @return 1 if a new request is queued, 0 if no more requests are required
+ * for this transfer.
+ */
+int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+
+ if (hc->do_split) {
+ /* SPLITs always queue just once per channel */
+ return 0;
+ } else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
+ /* SETUPs are queued only once since they can't be NAKed. */
+ return 0;
+ } else if (hc->ep_is_in) {
+ /*
+ * Always queue another request for other IN transfers. If
+ * back-to-back INs are issued and NAKs are received for both,
+ * the driver may still be processing the first NAK when the
+ * second NAK is received. When the interrupt handler clears
+ * the NAK interrupt for the first NAK, the second NAK will
+ * not be seen. So we can't depend on the NAK interrupt
+ * handler to requeue a NAKed request. Instead, IN requests
+ * are issued each time this function is called. When the
+ * transfer completes, the extra requests for the channel will
+ * be flushed.
+ */
+ hcchar_data_t hcchar;
+ dwc_otg_hc_regs_t *hc_regs =
+ core_if->host_if->hc_regs[hc->hc_num];
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hc_set_even_odd_frame(core_if, hc, &hcchar);
+ hcchar.b.chen = 1;
+ hcchar.b.chdis = 0;
+ DWC_DEBUGPL(DBG_HCDV, " IN xfer: hcchar = 0x%08x\n",
+ hcchar.d32);
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+ hc->requests++;
+ return 1;
+ } else {
+ /* OUT transfers. */
+ if (hc->xfer_count < hc->xfer_len) {
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ hcchar_data_t hcchar;
+ dwc_otg_hc_regs_t *hc_regs;
+ hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hc_set_even_odd_frame(core_if, hc, &hcchar);
+ }
+
+ /* Load OUT packet into the appropriate Tx FIFO. */
+ dwc_otg_hc_write_packet(core_if, hc);
+ hc->requests++;
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+/**
+ * Starts a PING transfer. This function should only be called in Slave mode.
+ * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled.
+ */
+void dwc_otg_hc_do_ping(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+ hcchar_data_t hcchar;
+ hctsiz_data_t hctsiz;
+ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+
+ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+
+ hctsiz.d32 = 0;
+ hctsiz.b.dopng = 1;
+ hctsiz.b.pktcnt = 1;
+ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcchar.b.chen = 1;
+ hcchar.b.chdis = 0;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+}
+
+/*
+ * This function writes a packet into the Tx FIFO associated with the Host
+ * Channel. For a channel associated with a non-periodic EP, the non-periodic
+ * Tx FIFO is written. For a channel associated with a periodic EP, the
+ * periodic Tx FIFO is written. This function should only be called in Slave
+ * mode.
+ *
+ * Upon return the xfer_buff and xfer_count fields in _hc are incremented by
+ * then number of bytes written to the Tx FIFO.
+ */
+void dwc_otg_hc_write_packet(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+ uint32_t i;
+ uint32_t remaining_count;
+ uint32_t byte_count;
+ uint32_t dword_count;
+
+ uint32_t *data_buff = (uint32_t *) (hc->xfer_buff);
+ uint32_t *data_fifo = core_if->data_fifo[hc->hc_num];
+
+ remaining_count = hc->xfer_len - hc->xfer_count;
+ if (remaining_count > hc->max_packet) {
+ byte_count = hc->max_packet;
+ } else {
+ byte_count = remaining_count;
+ }
+
+ dword_count = (byte_count + 3) / 4;
+
+ if ((((unsigned long)data_buff) & 0x3) == 0) {
+ /* xfer_buff is DWORD aligned. */
+ for (i = 0; i < dword_count; i++, data_buff++) {
+ DWC_WRITE_REG32(data_fifo, *data_buff);
+ }
+ } else {
+ /* xfer_buff is not DWORD aligned. */
+ for (i = 0; i < dword_count; i++, data_buff++) {
+ uint32_t data;
+ data =
+ (data_buff[0] | data_buff[1] << 8 | data_buff[2] <<
+ 16 | data_buff[3] << 24);
+ DWC_WRITE_REG32(data_fifo, data);
+ }
+ }
+
+ hc->xfer_count += byte_count;
+ hc->xfer_buff += byte_count;
+}
+
+/**
+ * Gets the current USB frame number. This is the frame number from the last
+ * SOF packet.
+ */
+uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * core_if)
+{
+ dsts_data_t dsts;
+ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+ /* read current frame/microframe number from DSTS register */
+ return dsts.b.soffn;
+}
+
+/**
+ * Calculates and gets the frame Interval value of HFIR register according PHY
+ * type and speed.The application can modify a value of HFIR register only after
+ * the Port Enable bit of the Host Port Control and Status register
+ * (HPRT.PrtEnaPort) has been set.
+*/
+
+uint32_t calc_frame_interval(dwc_otg_core_if_t * core_if)
+{
+ gusbcfg_data_t usbcfg;
+ hwcfg2_data_t hwcfg2;
+ hprt0_data_t hprt0;
+ int clock = 60; // default value
+ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+ hwcfg2.d32 = DWC_READ_REG32(&core_if->core_global_regs->ghwcfg2);
+ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+ if (!usbcfg.b.physel && usbcfg.b.ulpi_utmi_sel && !usbcfg.b.phyif)
+ clock = 60;
+ if (usbcfg.b.physel && hwcfg2.b.fs_phy_type == 3)
+ clock = 48;
+ if (!usbcfg.b.phylpwrclksel && !usbcfg.b.physel &&
+ !usbcfg.b.ulpi_utmi_sel && usbcfg.b.phyif)
+ clock = 30;
+ if (!usbcfg.b.phylpwrclksel && !usbcfg.b.physel &&
+ !usbcfg.b.ulpi_utmi_sel && !usbcfg.b.phyif)
+ clock = 60;
+ if (usbcfg.b.phylpwrclksel && !usbcfg.b.physel &&
+ !usbcfg.b.ulpi_utmi_sel && usbcfg.b.phyif)
+ clock = 48;
+ if (usbcfg.b.physel && !usbcfg.b.phyif && hwcfg2.b.fs_phy_type == 2)
+ clock = 48;
+ if (usbcfg.b.physel && hwcfg2.b.fs_phy_type == 1)
+ clock = 48;
+ if (hprt0.b.prtspd == 0)
+ /* High speed case */
+ return 125 * clock;
+ else
+ /* FS/LS case */
+ return 1000 * clock;
+}
+
+/**
+ * This function reads a setup packet from the Rx FIFO into the destination
+ * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl)
+ * Interrupt routine when a SETUP packet has been received in Slave mode.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dest Destination buffer for packet data.
+ */
+void dwc_otg_read_setup_packet(dwc_otg_core_if_t * core_if, uint32_t * dest)
+{
+ /* Get the 8 bytes of a setup transaction data */
+
+ /* Pop 2 DWORDS off the receive data FIFO into memory */
+ dest[0] = DWC_READ_REG32(core_if->data_fifo[0]);
+ dest[1] = DWC_READ_REG32(core_if->data_fifo[0]);
+}
+
+/**
+ * This function enables EP0 OUT to receive SETUP packets and configures EP0
+ * IN for transmitting packets. It is normally called when the
+ * "Enumeration Done" interrupt occurs.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP0 data.
+ */
+void dwc_otg_ep0_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ dsts_data_t dsts;
+ depctl_data_t diepctl;
+ depctl_data_t doepctl;
+ dctl_data_t dctl = {.d32 = 0 };
+
+ /* Read the Device Status and Endpoint 0 Control registers */
+ dsts.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dsts);
+ diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl);
+ doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl);
+
+ /* Set the MPS of the IN EP based on the enumeration speed */
+ switch (dsts.b.enumspd) {
+ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+ diepctl.b.mps = DWC_DEP0CTL_MPS_64;
+ break;
+ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+ diepctl.b.mps = DWC_DEP0CTL_MPS_8;
+ break;
+ }
+
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
+
+ /* Enable OUT EP for receive */
+ doepctl.b.epena = 1;
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);
+
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n",
+ DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl));
+ DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n",
+ DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl));
+#endif
+ dctl.b.cgnpinnak = 1;
+
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+ DWC_DEBUGPL(DBG_PCDV, "dctl=%0x\n",
+ DWC_READ_REG32(&dev_if->dev_global_regs->dctl));
+
+}
+
+/**
+ * This function activates an EP. The Device EP control register for
+ * the EP is configured as defined in the ep structure. Note: This
+ * function is not used for EP0.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to activate.
+ */
+void dwc_otg_ep_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ depctl_data_t depctl;
+ volatile uint32_t *addr;
+ daint_data_t daintmsk = {.d32 = 0 };
+ dcfg_data_t dcfg;
+ uint8_t i;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s() EP%d-%s\n", __func__, ep->num,
+ (ep->is_in ? "IN" : "OUT"));
+
+#ifdef DWC_UTE_PER_IO
+ ep->xiso_frame_num = 0xFFFFFFFF;
+ ep->xiso_active_xfers = 0;
+ ep->xiso_queued_xfers = 0;
+#endif
+ /* Read DEPCTLn register */
+ if (ep->is_in == 1) {
+ addr = &dev_if->in_ep_regs[ep->num]->diepctl;
+ daintmsk.ep.in = 1 << ep->num;
+ } else {
+ addr = &dev_if->out_ep_regs[ep->num]->doepctl;
+ daintmsk.ep.out = 1 << ep->num;
+ }
+
+ /* If the EP is already active don't change the EP Control
+ * register. */
+ depctl.d32 = DWC_READ_REG32(addr);
+ if (!depctl.b.usbactep) {
+ depctl.b.mps = ep->maxpacket;
+ depctl.b.eptype = ep->type;
+ depctl.b.txfnum = ep->tx_fifo_num;
+
+ if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ depctl.b.setd0pid = 1; // ???
+ } else {
+ depctl.b.setd0pid = 1;
+ }
+ depctl.b.usbactep = 1;
+
+ /* Update nextep_seq array and EPMSCNT in DCFG*/
+ if (!(depctl.b.eptype & 1) && (ep->is_in == 1)) { // NP IN EP
+ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+ if (core_if->nextep_seq[i] == core_if->first_in_nextep_seq)
+ break;
+ }
+ core_if->nextep_seq[i] = ep->num;
+ core_if->nextep_seq[ep->num] = core_if->first_in_nextep_seq;
+ depctl.b.nextep = core_if->nextep_seq[ep->num];
+ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+ dcfg.b.epmscnt++;
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+ DWC_DEBUGPL(DBG_PCDV,"%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+ __func__, core_if->first_in_nextep_seq);
+ for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+ DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]);
+ }
+
+ }
+
+
+ DWC_WRITE_REG32(addr, depctl.d32);
+ DWC_DEBUGPL(DBG_PCDV, "DEPCTL=%08x\n", DWC_READ_REG32(addr));
+ }
+
+ /* Enable the Interrupt for this EP */
+ if (core_if->multiproc_int_enable) {
+ if (ep->is_in == 1) {
+ diepmsk_data_t diepmsk = {.d32 = 0 };
+ diepmsk.b.xfercompl = 1;
+ diepmsk.b.timeout = 1;
+ diepmsk.b.epdisabled = 1;
+ diepmsk.b.ahberr = 1;
+ diepmsk.b.intknepmis = 1;
+ if (!core_if->en_multiple_tx_fifo && core_if->dma_enable)
+ diepmsk.b.intknepmis = 0;
+ diepmsk.b.txfifoundrn = 1; //?????
+ if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ diepmsk.b.nak = 1;
+ }
+
+
+
+/*
+ if (core_if->dma_desc_enable) {
+ diepmsk.b.bna = 1;
+ }
+*/
+/*
+ if (core_if->dma_enable) {
+ doepmsk.b.nak = 1;
+ }
+*/
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->
+ diepeachintmsk[ep->num], diepmsk.d32);
+
+ } else {
+ doepmsk_data_t doepmsk = {.d32 = 0 };
+ doepmsk.b.xfercompl = 1;
+ doepmsk.b.ahberr = 1;
+ doepmsk.b.epdisabled = 1;
+ if (ep->type == DWC_OTG_EP_TYPE_ISOC)
+ doepmsk.b.outtknepdis = 1;
+
+/*
+
+ if (core_if->dma_desc_enable) {
+ doepmsk.b.bna = 1;
+ }
+*/
+/*
+ doepmsk.b.babble = 1;
+ doepmsk.b.nyet = 1;
+ doepmsk.b.nak = 1;
+*/
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->
+ doepeachintmsk[ep->num], doepmsk.d32);
+ }
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->deachintmsk,
+ 0, daintmsk.d32);
+ } else {
+ if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ if (ep->is_in)
+ {
+ diepmsk_data_t diepmsk = {.d32 = 0 };
+ diepmsk.b.nak = 1;
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->diepmsk, 0, diepmsk.d32);
+ } else {
+ doepmsk_data_t doepmsk = {.d32 = 0 };
+ doepmsk.b.outtknepdis = 1;
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->doepmsk, 0, doepmsk.d32);
+ }
+ }
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->daintmsk,
+ 0, daintmsk.d32);
+ }
+
+ DWC_DEBUGPL(DBG_PCDV, "DAINTMSK=%0x\n",
+ DWC_READ_REG32(&dev_if->dev_global_regs->daintmsk));
+
+ ep->stall_clear_flag = 0;
+
+ return;
+}
+
+/**
+ * This function deactivates an EP. This is done by clearing the USB Active
+ * EP bit in the Device EP control register. Note: This function is not used
+ * for EP0. EP0 cannot be deactivated.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to deactivate.
+ */
+void dwc_otg_ep_deactivate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ depctl_data_t depctl = {.d32 = 0 };
+ volatile uint32_t *addr;
+ daint_data_t daintmsk = {.d32 = 0 };
+ dcfg_data_t dcfg;
+ uint8_t i = 0;
+
+#ifdef DWC_UTE_PER_IO
+ ep->xiso_frame_num = 0xFFFFFFFF;
+ ep->xiso_active_xfers = 0;
+ ep->xiso_queued_xfers = 0;
+#endif
+
+ /* Read DEPCTLn register */
+ if (ep->is_in == 1) {
+ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
+ daintmsk.ep.in = 1 << ep->num;
+ } else {
+ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
+ daintmsk.ep.out = 1 << ep->num;
+ }
+
+ depctl.d32 = DWC_READ_REG32(addr);
+
+ depctl.b.usbactep = 0;
+
+ /* Update nextep_seq array and EPMSCNT in DCFG*/
+ if (!(depctl.b.eptype & 1) && ep->is_in == 1) { // NP EP IN
+ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+ if (core_if->nextep_seq[i] == ep->num)
+ break;
+ }
+ core_if->nextep_seq[i] = core_if->nextep_seq[ep->num];
+ if (core_if->first_in_nextep_seq == ep->num)
+ core_if->first_in_nextep_seq = i;
+ core_if->nextep_seq[ep->num] = 0xff;
+ depctl.b.nextep = 0;
+ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+ dcfg.b.epmscnt--;
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+ DWC_DEBUGPL(DBG_PCDV,"%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+ __func__, core_if->first_in_nextep_seq);
+ for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+ DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]);
+ }
+ }
+
+ if (ep->is_in == 1)
+ depctl.b.txfnum = 0;
+
+ if (core_if->dma_desc_enable)
+ depctl.b.epdis = 1;
+
+ DWC_WRITE_REG32(addr, depctl.d32);
+ depctl.d32 = DWC_READ_REG32(addr);
+ if (core_if->dma_enable && ep->type == DWC_OTG_EP_TYPE_ISOC && depctl.b.epena)
+ {
+ depctl_data_t depctl = {.d32 = 0};
+ if (ep->is_in)
+ {
+ diepint_data_t diepint = {.d32 = 0};
+
+ depctl.b.snak = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->diepctl, depctl.d32);
+ do
+ {
+ dwc_udelay(10);
+ diepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+ in_ep_regs[ep->num]->diepint);
+ } while (!diepint.b.inepnakeff);
+ diepint.b.inepnakeff = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->
+ in_ep_regs[ep->num]->diepint, diepint.d32);
+ depctl.d32 = 0;
+ depctl.b.epdis = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->diepctl, depctl.d32);
+ do
+ {
+ dwc_udelay(10);
+ diepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+ in_ep_regs[ep->num]->diepint);
+ } while (!diepint.b.epdisabled);
+ diepint.b.epdisabled = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->diepint, diepint.d32);
+ } else {
+ dctl_data_t dctl = {.d32 = 0};
+ gintmsk_data_t gintsts = {.d32 = 0};
+ doepint_data_t doepint = {.d32 = 0};
+ dctl.b.sgoutnak = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+ do
+ {
+ dwc_udelay(10);
+ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+ } while (!gintsts.b.goutnakeff);
+ gintsts.d32 = 0;
+ gintsts.b.goutnakeff = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ depctl.d32 = 0;
+ depctl.b.epdis = 1;
+ depctl.b.snak = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->doepctl, depctl.d32);
+ do
+ {
+ dwc_udelay(10);
+ doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+ out_ep_regs[ep->num]->doepint);
+ } while (!doepint.b.epdisabled);
+
+ doepint.b.epdisabled = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->doepint, doepint.d32);
+
+ dctl.d32 = 0;
+ dctl.b.cgoutnak = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+ }
+ }
+
+ /* Disable the Interrupt for this EP */
+ if (core_if->multiproc_int_enable) {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->deachintmsk,
+ daintmsk.d32, 0);
+
+ if (ep->is_in == 1) {
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
+ diepeachintmsk[ep->num], 0);
+ } else {
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
+ doepeachintmsk[ep->num], 0);
+ }
+ } else {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->daintmsk,
+ daintmsk.d32, 0);
+ }
+
+}
+
+/**
+ * This function initializes dma descriptor chain.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ */
+static void init_dma_desc_chain(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ dwc_otg_dev_dma_desc_t *dma_desc;
+ uint32_t offset;
+ uint32_t xfer_est;
+ int i;
+ unsigned maxxfer_local, total_len;
+
+ if (!ep->is_in && ep->type == DWC_OTG_EP_TYPE_INTR &&
+ (ep->maxpacket%4)) {
+ maxxfer_local = ep->maxpacket;
+ total_len = ep->xfer_len;
+ } else {
+ maxxfer_local = ep->maxxfer;
+ total_len = ep->total_len;
+ }
+
+ ep->desc_cnt = (total_len / maxxfer_local) +
+ ((total_len % maxxfer_local) ? 1 : 0);
+
+ if (!ep->desc_cnt)
+ ep->desc_cnt = 1;
+
+ if (ep->desc_cnt > MAX_DMA_DESC_CNT)
+ ep->desc_cnt = MAX_DMA_DESC_CNT;
+
+ dma_desc = ep->desc_addr;
+ if (maxxfer_local == ep->maxpacket) {
+ if ((total_len % maxxfer_local) &&
+ (total_len/maxxfer_local < MAX_DMA_DESC_CNT)) {
+ xfer_est = (ep->desc_cnt - 1) * maxxfer_local +
+ (total_len % maxxfer_local);
+ } else
+ xfer_est = ep->desc_cnt * maxxfer_local;
+ }
+ else
+ xfer_est = total_len;
+ offset = 0;
+ for (i = 0; i < ep->desc_cnt; ++i) {
+ /** DMA Descriptor Setup */
+ if (xfer_est > maxxfer_local) {
+ dma_desc->status.b.bs = BS_HOST_BUSY;
+ dma_desc->status.b.l = 0;
+ dma_desc->status.b.ioc = 0;
+ dma_desc->status.b.sp = 0;
+ dma_desc->status.b.bytes = maxxfer_local;
+ dma_desc->buf = ep->dma_addr + offset;
+ dma_desc->status.b.sts = 0;
+ dma_desc->status.b.bs = BS_HOST_READY;
+
+ xfer_est -= maxxfer_local;
+ offset += maxxfer_local;
+ } else {
+ dma_desc->status.b.bs = BS_HOST_BUSY;
+ dma_desc->status.b.l = 1;
+ dma_desc->status.b.ioc = 1;
+ if (ep->is_in) {
+ dma_desc->status.b.sp =
+ (xfer_est %
+ ep->maxpacket) ? 1 : ((ep->
+ sent_zlp) ? 1 : 0);
+ dma_desc->status.b.bytes = xfer_est;
+ } else {
+ if (maxxfer_local == ep->maxpacket)
+ dma_desc->status.b.bytes = xfer_est;
+ else
+ dma_desc->status.b.bytes =
+ xfer_est + ((4 - (xfer_est & 0x3)) & 0x3);
+ }
+
+ dma_desc->buf = ep->dma_addr + offset;
+ dma_desc->status.b.sts = 0;
+ dma_desc->status.b.bs = BS_HOST_READY;
+ }
+ dma_desc++;
+ }
+}
+/**
+ * This function is called when to write ISOC data into appropriate dedicated
+ * periodic FIFO.
+ */
+static int32_t write_isoc_tx_fifo(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep)
+{
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ dwc_otg_dev_in_ep_regs_t *ep_regs;
+ dtxfsts_data_t txstatus = {.d32 = 0 };
+ uint32_t len = 0;
+ int epnum = dwc_ep->num;
+ int dwords;
+
+ DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", epnum);
+
+ ep_regs = core_if->dev_if->in_ep_regs[epnum];
+
+ len = dwc_ep->xfer_len - dwc_ep->xfer_count;
+
+ if (len > dwc_ep->maxpacket) {
+ len = dwc_ep->maxpacket;
+ }
+
+ dwords = (len + 3) / 4;
+
+ /* While there is space in the queue and space in the FIFO and
+ * More data to tranfer, Write packets to the Tx FIFO */
+ txstatus.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
+ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32);
+
+ while (txstatus.b.txfspcavail > dwords &&
+ dwc_ep->xfer_count < dwc_ep->xfer_len &&
+ dwc_ep->xfer_len != 0) {
+ /* Write the FIFO */
+ dwc_otg_ep_write_packet(core_if, dwc_ep, 0);
+
+ len = dwc_ep->xfer_len - dwc_ep->xfer_count;
+ if (len > dwc_ep->maxpacket) {
+ len = dwc_ep->maxpacket;
+ }
+
+ dwords = (len + 3) / 4;
+ txstatus.d32 =
+ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
+ DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum,
+ txstatus.d32);
+ }
+
+ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum,
+ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts));
+
+ return 1;
+}
+/**
+ * This function does the setup for a data transfer for an EP and
+ * starts the transfer. For an IN transfer, the packets will be
+ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers,
+ * the packets are unloaded from the Rx FIFO in the ISR. the ISR.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ */
+
+void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ depctl_data_t depctl;
+ deptsiz_data_t deptsiz;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__);
+ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d "
+ "xfer_buff=%p start_xfer_buff=%p, total_len = %d\n",
+ ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len,
+ ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff,
+ ep->total_len);
+ /* IN endpoint */
+ if (ep->is_in == 1) {
+ dwc_otg_dev_in_ep_regs_t *in_regs =
+ core_if->dev_if->in_ep_regs[ep->num];
+
+ gnptxsts_data_t gtxstatus;
+
+ gtxstatus.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->gnptxsts);
+
+ if (core_if->en_multiple_tx_fifo == 0
+ && gtxstatus.b.nptxqspcavail == 0
+ && !core_if->dma_enable) {
+#ifdef DEBUG
+ DWC_PRINTF("TX Queue Full (0x%0x)\n", gtxstatus.d32);
+#endif
+ return;
+ }
+
+ depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl));
+ deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz));
+
+ if (ep->maxpacket > ep->maxxfer / MAX_PKT_CNT)
+ ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ?
+ ep->maxxfer : (ep->total_len - ep->xfer_len);
+ else
+ ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len - ep->xfer_len)) ?
+ MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len);
+
+
+ /* Zero Length Packet? */
+ if ((ep->xfer_len - ep->xfer_count) == 0) {
+ deptsiz.b.xfersize = 0;
+ deptsiz.b.pktcnt = 1;
+ } else {
+ /* Program the transfer size and packet count
+ * as follows: xfersize = N * maxpacket +
+ * short_packet pktcnt = N + (short_packet
+ * exist ? 1 : 0)
+ */
+ deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count;
+ deptsiz.b.pktcnt =
+ (ep->xfer_len - ep->xfer_count - 1 +
+ ep->maxpacket) / ep->maxpacket;
+ if (deptsiz.b.pktcnt > MAX_PKT_CNT) {
+ deptsiz.b.pktcnt = MAX_PKT_CNT;
+ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
+ }
+ if (ep->type == DWC_OTG_EP_TYPE_ISOC)
+ deptsiz.b.mc = deptsiz.b.pktcnt;
+ }
+
+ /* Write the DMA register */
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable == 0) {
+ if (ep->type != DWC_OTG_EP_TYPE_ISOC)
+ deptsiz.b.mc = 1;
+ DWC_WRITE_REG32(&in_regs->dieptsiz,
+ deptsiz.d32);
+ DWC_WRITE_REG32(&(in_regs->diepdma),
+ (uint32_t) ep->dma_addr);
+ } else {
+#ifdef DWC_UTE_CFI
+ /* The descriptor chain should be already initialized by now */
+ if (ep->buff_mode != BM_STANDARD) {
+ DWC_WRITE_REG32(&in_regs->diepdma,
+ ep->descs_dma_addr);
+ } else {
+#endif
+ init_dma_desc_chain(core_if, ep);
+ /** DIEPDMAn Register write */
+ DWC_WRITE_REG32(&in_regs->diepdma,
+ ep->dma_desc_addr);
+#ifdef DWC_UTE_CFI
+ }
+#endif
+ }
+ } else {
+ DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
+ if (ep->type != DWC_OTG_EP_TYPE_ISOC) {
+ /**
+ * Enable the Non-Periodic Tx FIFO empty interrupt,
+ * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode,
+ * the data will be written into the fifo by the ISR.
+ */
+ if (core_if->en_multiple_tx_fifo == 0) {
+ intr_mask.b.nptxfempty = 1;
+ DWC_MODIFY_REG32
+ (&core_if->core_global_regs->gintmsk,
+ intr_mask.d32, intr_mask.d32);
+ } else {
+ /* Enable the Tx FIFO Empty Interrupt for this EP */
+ if (ep->xfer_len > 0) {
+ uint32_t fifoemptymsk = 0;
+ fifoemptymsk = 1 << ep->num;
+ DWC_MODIFY_REG32
+ (&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+ 0, fifoemptymsk);
+
+ }
+ }
+ } else {
+ write_isoc_tx_fifo(core_if, ep);
+ }
+ }
+ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
+ depctl.b.nextep = core_if->nextep_seq[ep->num];
+
+ if (ep->type == DWC_OTG_EP_TYPE_ISOC)
+ {
+ dsts_data_t dsts = {.d32 = 0};
+ if (ep->bInterval == 1) {
+ dsts.d32 =
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+ ep->frame_num = dsts.b.soffn + ep->bInterval;
+ if (ep->frame_num > 0x3FFF)
+ {
+ ep->frm_overrun = 1;
+ ep->frame_num &= 0x3FFF;
+ } else
+ ep->frm_overrun = 0;
+ if (ep->frame_num & 0x1) {
+ depctl.b.setd1pid = 1;
+ } else {
+ depctl.b.setd0pid = 1;
+ }
+ }
+ }
+ /* EP enable, IN data in FIFO */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
+
+ } else {
+ /* OUT endpoint */
+ dwc_otg_dev_out_ep_regs_t *out_regs =
+ core_if->dev_if->out_ep_regs[ep->num];
+
+ depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl));
+ deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz));
+
+ if (!core_if->dma_desc_enable) {
+ if (ep->maxpacket > ep->maxxfer / MAX_PKT_CNT)
+ ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ?
+ ep->maxxfer : (ep->total_len - ep->xfer_len);
+ else
+ ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len
+ - ep->xfer_len)) ? MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len);
+ }
+
+ /* Program the transfer size and packet count as follows:
+ *
+ * pktcnt = N
+ * xfersize = N * maxpacket
+ */
+ if ((ep->xfer_len - ep->xfer_count) == 0) {
+ /* Zero Length Packet */
+ deptsiz.b.xfersize = ep->maxpacket;
+ deptsiz.b.pktcnt = 1;
+ } else {
+ deptsiz.b.pktcnt =
+ (ep->xfer_len - ep->xfer_count +
+ (ep->maxpacket - 1)) / ep->maxpacket;
+ if (deptsiz.b.pktcnt > MAX_PKT_CNT) {
+ deptsiz.b.pktcnt = MAX_PKT_CNT;
+ }
+ if (!core_if->dma_desc_enable) {
+ ep->xfer_len =
+ deptsiz.b.pktcnt * ep->maxpacket + ep->xfer_count;
+ }
+ deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count;
+ }
+
+ DWC_DEBUGPL(DBG_PCDV, "ep%d xfersize=%d pktcnt=%d\n",
+ ep->num, deptsiz.b.xfersize, deptsiz.b.pktcnt);
+
+ if (core_if->dma_enable) {
+ if (!core_if->dma_desc_enable) {
+ DWC_WRITE_REG32(&out_regs->doeptsiz,
+ deptsiz.d32);
+
+ DWC_WRITE_REG32(&(out_regs->doepdma),
+ (uint32_t) ep->dma_addr);
+ } else {
+#ifdef DWC_UTE_CFI
+ /* The descriptor chain should be already initialized by now */
+ if (ep->buff_mode != BM_STANDARD) {
+ DWC_WRITE_REG32(&out_regs->doepdma,
+ ep->descs_dma_addr);
+ } else {
+#endif
+ /** This is used for interrupt out transfers*/
+ if (!ep->xfer_len)
+ ep->xfer_len = ep->total_len;
+ init_dma_desc_chain(core_if, ep);
+
+ if (core_if->core_params->dev_out_nak) {
+ if (ep->type == DWC_OTG_EP_TYPE_BULK) {
+ deptsiz.b.pktcnt = (ep->total_len +
+ (ep->maxpacket - 1)) / ep->maxpacket;
+ deptsiz.b.xfersize = ep->total_len;
+ /* Remember initial value of doeptsiz */
+ core_if->start_doeptsiz_val[ep->num] = deptsiz.d32;
+ DWC_WRITE_REG32(&out_regs->doeptsiz,
+ deptsiz.d32);
+ }
+ }
+ /** DOEPDMAn Register write */
+ DWC_WRITE_REG32(&out_regs->doepdma,
+ ep->dma_desc_addr);
+#ifdef DWC_UTE_CFI
+ }
+#endif
+ }
+ } else {
+ DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
+ }
+
+ if (ep->type == DWC_OTG_EP_TYPE_ISOC)
+ {
+ dsts_data_t dsts = {.d32 = 0};
+ if (ep->bInterval == 1) {
+ dsts.d32 =
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+ ep->frame_num = dsts.b.soffn + ep->bInterval;
+ if (ep->frame_num > 0x3FFF)
+ {
+ ep->frm_overrun = 1;
+ ep->frame_num &= 0x3FFF;
+ } else
+ ep->frm_overrun = 0;
+
+ if (ep->frame_num & 0x1) {
+ depctl.b.setd1pid = 1;
+ } else {
+ depctl.b.setd0pid = 1;
+ }
+ }
+ }
+
+ /* EP enable */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+
+ DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32);
+
+ DWC_DEBUGPL(DBG_PCD, "DOEPCTL=%08x DOEPTSIZ=%08x\n",
+ DWC_READ_REG32(&out_regs->doepctl),
+ DWC_READ_REG32(&out_regs->doeptsiz));
+ DWC_DEBUGPL(DBG_PCD, "DAINTMSK=%08x GINTMSK=%08x\n",
+ DWC_READ_REG32(&core_if->dev_if->
+ dev_global_regs->daintmsk),
+ DWC_READ_REG32(&core_if->
+ core_global_regs->gintmsk));
+
+
+ /* Timer is scheduling only for out bulk transfers for
+ * "Device DDMA OUT NAK Enhancement" feature to inform user
+ * about received data payload in case of timeout
+ */
+ if (core_if->core_params->dev_out_nak) {
+ if (ep->type == DWC_OTG_EP_TYPE_BULK) {
+ core_if->ep_xfer_info[ep->num].core_if = core_if;
+ core_if->ep_xfer_info[ep->num].ep = ep;
+ core_if->ep_xfer_info[ep->num].state = 1;
+
+ /* Start a timer for this transfer. */
+ DWC_TIMER_SCHEDULE(core_if->ep_xfer_timer[ep->num], 10000);
+ }
+ }
+ }
+}
+
+/**
+ * This function setup a zero length transfer in Buffer DMA and
+ * Slave modes for usb requests with zero field set
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+
+ depctl_data_t depctl;
+ deptsiz_data_t deptsiz;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__);
+ DWC_PRINTF("zero length transfer is called\n");
+
+ /* IN endpoint */
+ if (ep->is_in == 1) {
+ dwc_otg_dev_in_ep_regs_t *in_regs =
+ core_if->dev_if->in_ep_regs[ep->num];
+
+ depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl));
+ deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz));
+
+ deptsiz.b.xfersize = 0;
+ deptsiz.b.pktcnt = 1;
+
+ /* Write the DMA register */
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable == 0) {
+ deptsiz.b.mc = 1;
+ DWC_WRITE_REG32(&in_regs->dieptsiz,
+ deptsiz.d32);
+ DWC_WRITE_REG32(&(in_regs->diepdma),
+ (uint32_t) ep->dma_addr);
+ }
+ } else {
+ DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
+ /**
+ * Enable the Non-Periodic Tx FIFO empty interrupt,
+ * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode,
+ * the data will be written into the fifo by the ISR.
+ */
+ if (core_if->en_multiple_tx_fifo == 0) {
+ intr_mask.b.nptxfempty = 1;
+ DWC_MODIFY_REG32(&core_if->
+ core_global_regs->gintmsk,
+ intr_mask.d32, intr_mask.d32);
+ } else {
+ /* Enable the Tx FIFO Empty Interrupt for this EP */
+ if (ep->xfer_len > 0) {
+ uint32_t fifoemptymsk = 0;
+ fifoemptymsk = 1 << ep->num;
+ DWC_MODIFY_REG32(&core_if->
+ dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+ 0, fifoemptymsk);
+ }
+ }
+ }
+
+ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
+ depctl.b.nextep = core_if->nextep_seq[ep->num];
+ /* EP enable, IN data in FIFO */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
+
+ } else {
+ /* OUT endpoint */
+ dwc_otg_dev_out_ep_regs_t *out_regs =
+ core_if->dev_if->out_ep_regs[ep->num];
+
+ depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl));
+ deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz));
+
+ /* Zero Length Packet */
+ deptsiz.b.xfersize = ep->maxpacket;
+ deptsiz.b.pktcnt = 1;
+
+ if (core_if->dma_enable) {
+ if (!core_if->dma_desc_enable) {
+ DWC_WRITE_REG32(&out_regs->doeptsiz,
+ deptsiz.d32);
+
+ DWC_WRITE_REG32(&(out_regs->doepdma),
+ (uint32_t) ep->dma_addr);
+ }
+ } else {
+ DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
+ }
+
+ /* EP enable */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+
+ DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32);
+
+ }
+}
+
+/**
+ * This function does the setup for a data transfer for EP0 and starts
+ * the transfer. For an IN transfer, the packets will be loaded into
+ * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are
+ * unloaded from the Rx FIFO in the ISR.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP0 data.
+ */
+void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ depctl_data_t depctl;
+ deptsiz0_data_t deptsiz;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ dwc_otg_dev_dma_desc_t *dma_desc;
+
+ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d "
+ "xfer_buff=%p start_xfer_buff=%p \n",
+ ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len,
+ ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff);
+
+ ep->total_len = ep->xfer_len;
+
+ /* IN endpoint */
+ if (ep->is_in == 1) {
+ dwc_otg_dev_in_ep_regs_t *in_regs =
+ core_if->dev_if->in_ep_regs[0];
+
+ gnptxsts_data_t gtxstatus;
+
+ gtxstatus.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->gnptxsts);
+
+ if (core_if->en_multiple_tx_fifo == 0
+ && gtxstatus.b.nptxqspcavail == 0
+ && !core_if->dma_enable) {
+#ifdef DEBUG
+ deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz);
+ DWC_DEBUGPL(DBG_PCD, "DIEPCTL0=%0x\n",
+ DWC_READ_REG32(&in_regs->diepctl));
+ DWC_DEBUGPL(DBG_PCD, "DIEPTSIZ0=%0x (sz=%d, pcnt=%d)\n",
+ deptsiz.d32,
+ deptsiz.b.xfersize, deptsiz.b.pktcnt);
+ DWC_PRINTF("TX Queue or FIFO Full (0x%0x)\n",
+ gtxstatus.d32);
+#endif
+ return;
+ }
+
+ depctl.d32 = DWC_READ_REG32(&in_regs->diepctl);
+ deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz);
+
+ /* Zero Length Packet? */
+ if (ep->xfer_len == 0) {
+ deptsiz.b.xfersize = 0;
+ deptsiz.b.pktcnt = 1;
+ } else {
+ /* Program the transfer size and packet count
+ * as follows: xfersize = N * maxpacket +
+ * short_packet pktcnt = N + (short_packet
+ * exist ? 1 : 0)
+ */
+ if (ep->xfer_len > ep->maxpacket) {
+ ep->xfer_len = ep->maxpacket;
+ deptsiz.b.xfersize = ep->maxpacket;
+ } else {
+ deptsiz.b.xfersize = ep->xfer_len;
+ }
+ deptsiz.b.pktcnt = 1;
+
+ }
+ DWC_DEBUGPL(DBG_PCDV,
+ "IN len=%d xfersize=%d pktcnt=%d [%08x]\n",
+ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt,
+ deptsiz.d32);
+
+ /* Write the DMA register */
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable == 0) {
+ DWC_WRITE_REG32(&in_regs->dieptsiz,
+ deptsiz.d32);
+
+ DWC_WRITE_REG32(&(in_regs->diepdma),
+ (uint32_t) ep->dma_addr);
+ } else {
+ dma_desc = core_if->dev_if->in_desc_addr;
+
+ /** DMA Descriptor Setup */
+ dma_desc->status.b.bs = BS_HOST_BUSY;
+ dma_desc->status.b.l = 1;
+ dma_desc->status.b.ioc = 1;
+ dma_desc->status.b.sp =
+ (ep->xfer_len == ep->maxpacket) ? 0 : 1;
+ dma_desc->status.b.bytes = ep->xfer_len;
+ dma_desc->buf = ep->dma_addr;
+ dma_desc->status.b.sts = 0;
+ dma_desc->status.b.bs = BS_HOST_READY;
+
+ /** DIEPDMA0 Register write */
+ DWC_WRITE_REG32(&in_regs->diepdma,
+ core_if->
+ dev_if->dma_in_desc_addr);
+ }
+ } else {
+ DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
+ }
+
+ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
+ depctl.b.nextep = core_if->nextep_seq[ep->num];
+ /* EP enable, IN data in FIFO */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
+
+ /**
+ * Enable the Non-Periodic Tx FIFO empty interrupt, the
+ * data will be written into the fifo by the ISR.
+ */
+ if (!core_if->dma_enable) {
+ if (core_if->en_multiple_tx_fifo == 0) {
+ intr_mask.b.nptxfempty = 1;
+ DWC_MODIFY_REG32(&core_if->
+ core_global_regs->gintmsk,
+ intr_mask.d32, intr_mask.d32);
+ } else {
+ /* Enable the Tx FIFO Empty Interrupt for this EP */
+ if (ep->xfer_len > 0) {
+ uint32_t fifoemptymsk = 0;
+ fifoemptymsk |= 1 << ep->num;
+ DWC_MODIFY_REG32(&core_if->
+ dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+ 0, fifoemptymsk);
+ }
+ }
+ }
+ } else {
+ /* OUT endpoint */
+ dwc_otg_dev_out_ep_regs_t *out_regs =
+ core_if->dev_if->out_ep_regs[0];
+
+ depctl.d32 = DWC_READ_REG32(&out_regs->doepctl);
+ deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz);
+
+ /* Program the transfer size and packet count as follows:
+ * xfersize = N * (maxpacket + 4 - (maxpacket % 4))
+ * pktcnt = N */
+ /* Zero Length Packet */
+ deptsiz.b.xfersize = ep->maxpacket;
+ deptsiz.b.pktcnt = 1;
+
+ DWC_DEBUGPL(DBG_PCDV, "len=%d xfersize=%d pktcnt=%d\n",
+ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt);
+
+ if (core_if->dma_enable) {
+ if (!core_if->dma_desc_enable) {
+ DWC_WRITE_REG32(&out_regs->doeptsiz,
+ deptsiz.d32);
+
+ DWC_WRITE_REG32(&(out_regs->doepdma),
+ (uint32_t) ep->dma_addr);
+ } else {
+ dma_desc = core_if->dev_if->out_desc_addr;
+
+ /** DMA Descriptor Setup */
+ dma_desc->status.b.bs = BS_HOST_BUSY;
+ dma_desc->status.b.l = 1;
+ dma_desc->status.b.ioc = 1;
+ dma_desc->status.b.bytes = ep->maxpacket;
+ dma_desc->buf = ep->dma_addr;
+ dma_desc->status.b.sts = 0;
+ dma_desc->status.b.bs = BS_HOST_READY;
+
+ /** DOEPDMA0 Register write */
+ DWC_WRITE_REG32(&out_regs->doepdma,
+ core_if->
+ dev_if->dma_out_desc_addr);
+ }
+ } else {
+ DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
+ }
+
+ /* EP enable */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ DWC_WRITE_REG32(&(out_regs->doepctl), depctl.d32);
+ }
+}
+
+/**
+ * This function continues control IN transfers started by
+ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a
+ * single packet. NOTE: The DIEPCTL0/DOEPCTL0 registers only have one
+ * bit for the packet count.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP0 data.
+ */
+void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ depctl_data_t depctl;
+ deptsiz0_data_t deptsiz;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ dwc_otg_dev_dma_desc_t *dma_desc;
+
+ if (ep->is_in == 1) {
+ dwc_otg_dev_in_ep_regs_t *in_regs =
+ core_if->dev_if->in_ep_regs[0];
+ gnptxsts_data_t tx_status = {.d32 = 0 };
+
+ tx_status.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->gnptxsts);
+ /** @todo Should there be check for room in the Tx
+ * Status Queue. If not remove the code above this comment. */
+
+ depctl.d32 = DWC_READ_REG32(&in_regs->diepctl);
+ deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz);
+
+ /* Program the transfer size and packet count
+ * as follows: xfersize = N * maxpacket +
+ * short_packet pktcnt = N + (short_packet
+ * exist ? 1 : 0)
+ */
+
+ if (core_if->dma_desc_enable == 0) {
+ deptsiz.b.xfersize =
+ (ep->total_len - ep->xfer_count) >
+ ep->maxpacket ? ep->maxpacket : (ep->total_len -
+ ep->xfer_count);
+ deptsiz.b.pktcnt = 1;
+ if (core_if->dma_enable == 0) {
+ ep->xfer_len += deptsiz.b.xfersize;
+ } else {
+ ep->xfer_len = deptsiz.b.xfersize;
+ }
+ DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
+ } else {
+ ep->xfer_len =
+ (ep->total_len - ep->xfer_count) >
+ ep->maxpacket ? ep->maxpacket : (ep->total_len -
+ ep->xfer_count);
+
+ dma_desc = core_if->dev_if->in_desc_addr;
+
+ /** DMA Descriptor Setup */
+ dma_desc->status.b.bs = BS_HOST_BUSY;
+ dma_desc->status.b.l = 1;
+ dma_desc->status.b.ioc = 1;
+ dma_desc->status.b.sp =
+ (ep->xfer_len == ep->maxpacket) ? 0 : 1;
+ dma_desc->status.b.bytes = ep->xfer_len;
+ dma_desc->buf = ep->dma_addr;
+ dma_desc->status.b.sts = 0;
+ dma_desc->status.b.bs = BS_HOST_READY;
+
+ /** DIEPDMA0 Register write */
+ DWC_WRITE_REG32(&in_regs->diepdma,
+ core_if->dev_if->dma_in_desc_addr);
+ }
+
+ DWC_DEBUGPL(DBG_PCDV,
+ "IN len=%d xfersize=%d pktcnt=%d [%08x]\n",
+ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt,
+ deptsiz.d32);
+
+ /* Write the DMA register */
+ if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) {
+ if (core_if->dma_desc_enable == 0)
+ DWC_WRITE_REG32(&(in_regs->diepdma),
+ (uint32_t) ep->dma_addr);
+ }
+ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
+ depctl.b.nextep = core_if->nextep_seq[ep->num];
+ /* EP enable, IN data in FIFO */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
+
+ /**
+ * Enable the Non-Periodic Tx FIFO empty interrupt, the
+ * data will be written into the fifo by the ISR.
+ */
+ if (!core_if->dma_enable) {
+ if (core_if->en_multiple_tx_fifo == 0) {
+ /* First clear it from GINTSTS */
+ intr_mask.b.nptxfempty = 1;
+ DWC_MODIFY_REG32(&core_if->
+ core_global_regs->gintmsk,
+ intr_mask.d32, intr_mask.d32);
+
+ } else {
+ /* Enable the Tx FIFO Empty Interrupt for this EP */
+ if (ep->xfer_len > 0) {
+ uint32_t fifoemptymsk = 0;
+ fifoemptymsk |= 1 << ep->num;
+ DWC_MODIFY_REG32(&core_if->
+ dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+ 0, fifoemptymsk);
+ }
+ }
+ }
+ } else {
+ dwc_otg_dev_out_ep_regs_t *out_regs =
+ core_if->dev_if->out_ep_regs[0];
+
+ depctl.d32 = DWC_READ_REG32(&out_regs->doepctl);
+ deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz);
+
+ /* Program the transfer size and packet count
+ * as follows: xfersize = N * maxpacket +
+ * short_packet pktcnt = N + (short_packet
+ * exist ? 1 : 0)
+ */
+ deptsiz.b.xfersize = ep->maxpacket;
+ deptsiz.b.pktcnt = 1;
+
+ if (core_if->dma_desc_enable == 0) {
+ DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
+ } else {
+ dma_desc = core_if->dev_if->out_desc_addr;
+
+ /** DMA Descriptor Setup */
+ dma_desc->status.b.bs = BS_HOST_BUSY;
+ dma_desc->status.b.l = 1;
+ dma_desc->status.b.ioc = 1;
+ dma_desc->status.b.bytes = ep->maxpacket;
+ dma_desc->buf = ep->dma_addr;
+ dma_desc->status.b.sts = 0;
+ dma_desc->status.b.bs = BS_HOST_READY;
+
+ /** DOEPDMA0 Register write */
+ DWC_WRITE_REG32(&out_regs->doepdma,
+ core_if->dev_if->dma_out_desc_addr);
+ }
+
+ DWC_DEBUGPL(DBG_PCDV,
+ "IN len=%d xfersize=%d pktcnt=%d [%08x]\n",
+ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt,
+ deptsiz.d32);
+
+ /* Write the DMA register */
+ if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) {
+ if (core_if->dma_desc_enable == 0)
+ DWC_WRITE_REG32(&(out_regs->doepdma),
+ (uint32_t) ep->dma_addr);
+
+ }
+
+ /* EP enable, IN data in FIFO */
+ depctl.b.cnak = 1;
+ depctl.b.epena = 1;
+ DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32);
+
+ }
+}
+
+#ifdef DEBUG
+void dump_msg(const u8 * buf, unsigned int length)
+{
+ unsigned int start, num, i;
+ char line[52], *p;
+
+ if (length >= 512)
+ return;
+ start = 0;
+ while (length > 0) {
+ num = length < 16u ? length : 16u;
+ p = line;
+ for (i = 0; i < num; ++i) {
+ if (i == 8)
+ *p++ = ' ';
+ DWC_SPRINTF(p, " %02x", buf[i]);
+ p += 3;
+ }
+ *p = 0;
+ DWC_PRINTF("%6x: %s\n", start, line);
+ buf += num;
+ start += num;
+ length -= num;
+ }
+}
+#else
+static inline void dump_msg(const u8 * buf, unsigned int length)
+{
+}
+#endif
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the
+ * EP. For non-periodic EPs the non-periodic Tx FIFO is written. For
+ * periodic EPs the periodic Tx FIFO associated with the EP is written
+ * with all packets for the next micro-frame.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to write packet for.
+ * @param dma Indicates if DMA is being used.
+ */
+void dwc_otg_ep_write_packet(dwc_otg_core_if_t * core_if, dwc_ep_t * ep,
+ int dma)
+{
+ /**
+ * The buffer is padded to DWORD on a per packet basis in
+ * slave/dma mode if the MPS is not DWORD aligned. The last
+ * packet, if short, is also padded to a multiple of DWORD.
+ *
+ * ep->xfer_buff always starts DWORD aligned in memory and is a
+ * multiple of DWORD in length
+ *
+ * ep->xfer_len can be any number of bytes
+ *
+ * ep->xfer_count is a multiple of ep->maxpacket until the last
+ * packet
+ *
+ * FIFO access is DWORD */
+
+ uint32_t i;
+ uint32_t byte_count;
+ uint32_t dword_count;
+ uint32_t *fifo;
+ uint32_t *data_buff = (uint32_t *) ep->xfer_buff;
+
+ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p)\n", __func__, core_if,
+ ep);
+ if (ep->xfer_count >= ep->xfer_len) {
+ DWC_WARN("%s() No data for EP%d!!!\n", __func__, ep->num);
+ return;
+ }
+
+ /* Find the byte length of the packet either short packet or MPS */
+ if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket) {
+ byte_count = ep->xfer_len - ep->xfer_count;
+ } else {
+ byte_count = ep->maxpacket;
+ }
+
+ /* Find the DWORD length, padded by extra bytes as neccessary if MPS
+ * is not a multiple of DWORD */
+ dword_count = (byte_count + 3) / 4;
+
+#ifdef VERBOSE
+ dump_msg(ep->xfer_buff, byte_count);
+#endif
+
+ /**@todo NGS Where are the Periodic Tx FIFO addresses
+ * intialized? What should this be? */
+
+ fifo = core_if->data_fifo[ep->num];
+
+ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "fifo=%p buff=%p *p=%08x bc=%d\n",
+ fifo, data_buff, *data_buff, byte_count);
+
+ if (!dma) {
+ for (i = 0; i < dword_count; i++, data_buff++) {
+ DWC_WRITE_REG32(fifo, *data_buff);
+ }
+ }
+
+ ep->xfer_count += byte_count;
+ ep->xfer_buff += byte_count;
+ ep->dma_addr += byte_count;
+}
+
+/**
+ * Set the EP STALL.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to set the stall on.
+ */
+void dwc_otg_ep_set_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ depctl_data_t depctl;
+ volatile uint32_t *depctl_addr;
+
+ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num,
+ (ep->is_in ? "IN" : "OUT"));
+
+ if (ep->is_in == 1) {
+ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl);
+ depctl.d32 = DWC_READ_REG32(depctl_addr);
+
+ /* set the disable and stall bits */
+ if (depctl.b.epena) {
+ depctl.b.epdis = 1;
+ }
+ depctl.b.stall = 1;
+ DWC_WRITE_REG32(depctl_addr, depctl.d32);
+ } else {
+ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl);
+ depctl.d32 = DWC_READ_REG32(depctl_addr);
+
+ /* set the stall bit */
+ depctl.b.stall = 1;
+ DWC_WRITE_REG32(depctl_addr, depctl.d32);
+ }
+
+ DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", DWC_READ_REG32(depctl_addr));
+
+ return;
+}
+
+/**
+ * Clear the EP STALL.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to clear stall from.
+ */
+void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ depctl_data_t depctl;
+ volatile uint32_t *depctl_addr;
+
+ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num,
+ (ep->is_in ? "IN" : "OUT"));
+
+ if (ep->is_in == 1) {
+ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl);
+ } else {
+ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl);
+ }
+
+ depctl.d32 = DWC_READ_REG32(depctl_addr);
+
+ /* clear the stall bits */
+ depctl.b.stall = 0;
+
+ /*
+ * USB Spec 9.4.5: For endpoints using data toggle, regardless
+ * of whether an endpoint has the Halt feature set, a
+ * ClearFeature(ENDPOINT_HALT) request always results in the
+ * data toggle being reinitialized to DATA0.
+ */
+ if (ep->type == DWC_OTG_EP_TYPE_INTR ||
+ ep->type == DWC_OTG_EP_TYPE_BULK) {
+ depctl.b.setd0pid = 1; /* DATA0 */
+ }
+
+ DWC_WRITE_REG32(depctl_addr, depctl.d32);
+ DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", DWC_READ_REG32(depctl_addr));
+ return;
+}
+
+/**
+ * This function reads a packet from the Rx FIFO into the destination
+ * buffer. To read SETUP data use dwc_otg_read_setup_packet.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dest Destination buffer for the packet.
+ * @param bytes Number of bytes to copy to the destination.
+ */
+void dwc_otg_read_packet(dwc_otg_core_if_t * core_if,
+ uint8_t * dest, uint16_t bytes)
+{
+ int i;
+ int word_count = (bytes + 3) / 4;
+
+ volatile uint32_t *fifo = core_if->data_fifo[0];
+ uint32_t *data_buff = (uint32_t *) dest;
+
+ /**
+ * @todo Account for the case where _dest is not dword aligned. This
+ * requires reading data from the FIFO into a uint32_t temp buffer,
+ * then moving it into the data buffer.
+ */
+
+ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p,%d)\n", __func__,
+ core_if, dest, bytes);
+
+ for (i = 0; i < word_count; i++, data_buff++) {
+ *data_buff = DWC_READ_REG32(fifo);
+ }
+
+ return;
+}
+
+/**
+ * This functions reads the device registers and prints them
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * core_if)
+{
+ int i;
+ volatile uint32_t *addr;
+
+ DWC_PRINTF("Device Global Registers\n");
+ addr = &core_if->dev_if->dev_global_regs->dcfg;
+ DWC_PRINTF("DCFG @0x%08lX : 0x%08X\n", (unsigned long) addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->dev_global_regs->dctl;
+ DWC_PRINTF("DCTL @0x%08lX : 0x%08X\n", (unsigned long) addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->dev_global_regs->dsts;
+ DWC_PRINTF("DSTS @0x%08lX : 0x%08X\n", (unsigned long) addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->dev_global_regs->diepmsk;
+ DWC_PRINTF("DIEPMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->dev_global_regs->doepmsk;
+ DWC_PRINTF("DOEPMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->dev_global_regs->daint;
+ DWC_PRINTF("DAINT @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->dev_global_regs->daintmsk;
+ DWC_PRINTF("DAINTMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->dev_global_regs->dtknqr1;
+ DWC_PRINTF("DTKNQR1 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ if (core_if->hwcfg2.b.dev_token_q_depth > 6) {
+ addr = &core_if->dev_if->dev_global_regs->dtknqr2;
+ DWC_PRINTF("DTKNQR2 @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ }
+
+ addr = &core_if->dev_if->dev_global_regs->dvbusdis;
+ DWC_PRINTF("DVBUSID @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+
+ addr = &core_if->dev_if->dev_global_regs->dvbuspulse;
+ DWC_PRINTF("DVBUSPULSE @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+
+ addr = &core_if->dev_if->dev_global_regs->dtknqr3_dthrctl;
+ DWC_PRINTF("DTKNQR3_DTHRCTL @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+
+ if (core_if->hwcfg2.b.dev_token_q_depth > 22) {
+ addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk;
+ DWC_PRINTF("DTKNQR4 @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ }
+
+ addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk;
+ DWC_PRINTF("FIFOEMPMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+
+ if (core_if->hwcfg2.b.multi_proc_int) {
+
+ addr = &core_if->dev_if->dev_global_regs->deachint;
+ DWC_PRINTF("DEACHINT @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->dev_global_regs->deachintmsk;
+ DWC_PRINTF("DEACHINTMSK @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+
+ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+ addr =
+ &core_if->dev_if->dev_global_regs->
+ diepeachintmsk[i];
+ DWC_PRINTF("DIEPEACHINTMSK[%d] @0x%08lX : 0x%08X\n",
+ i, (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ }
+
+ for (i = 0; i <= core_if->dev_if->num_out_eps; i++) {
+ addr =
+ &core_if->dev_if->dev_global_regs->
+ doepeachintmsk[i];
+ DWC_PRINTF("DOEPEACHINTMSK[%d] @0x%08lX : 0x%08X\n",
+ i, (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ }
+ }
+
+ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+ DWC_PRINTF("Device IN EP %d Registers\n", i);
+ addr = &core_if->dev_if->in_ep_regs[i]->diepctl;
+ DWC_PRINTF("DIEPCTL @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->in_ep_regs[i]->diepint;
+ DWC_PRINTF("DIEPINT @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->in_ep_regs[i]->dieptsiz;
+ DWC_PRINTF("DIETSIZ @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->in_ep_regs[i]->diepdma;
+ DWC_PRINTF("DIEPDMA @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->in_ep_regs[i]->dtxfsts;
+ DWC_PRINTF("DTXFSTS @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->in_ep_regs[i]->diepdmab;
+ DWC_PRINTF("DIEPDMAB @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, 0 /*DWC_READ_REG32(addr) */ );
+ }
+
+ for (i = 0; i <= core_if->dev_if->num_out_eps; i++) {
+ DWC_PRINTF("Device OUT EP %d Registers\n", i);
+ addr = &core_if->dev_if->out_ep_regs[i]->doepctl;
+ DWC_PRINTF("DOEPCTL @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->out_ep_regs[i]->doepint;
+ DWC_PRINTF("DOEPINT @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->out_ep_regs[i]->doeptsiz;
+ DWC_PRINTF("DOETSIZ @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->dev_if->out_ep_regs[i]->doepdma;
+ DWC_PRINTF("DOEPDMA @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ if (core_if->dma_enable) { /* Don't access this register in SLAVE mode */
+ addr = &core_if->dev_if->out_ep_regs[i]->doepdmab;
+ DWC_PRINTF("DOEPDMAB @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ }
+
+ }
+}
+
+/**
+ * This functions reads the SPRAM and prints its content
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_dump_spram(dwc_otg_core_if_t * core_if)
+{
+ volatile uint8_t *addr, *start_addr, *end_addr;
+
+ DWC_PRINTF("SPRAM Data:\n");
+ start_addr = (void *)core_if->core_global_regs;
+ DWC_PRINTF("Base Address: 0x%8lX\n", (unsigned long)start_addr);
+ start_addr += 0x00028000;
+ end_addr = (void *)core_if->core_global_regs;
+ end_addr += 0x000280e0;
+
+ for (addr = start_addr; addr < end_addr; addr += 16) {
+ DWC_PRINTF
+ ("0x%8lX:\t%2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X\n",
+ (unsigned long)addr, addr[0], addr[1], addr[2], addr[3],
+ addr[4], addr[5], addr[6], addr[7], addr[8], addr[9],
+ addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]
+ );
+ }
+
+ return;
+}
+
+/**
+ * This function reads the host registers and prints them
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_dump_host_registers(dwc_otg_core_if_t * core_if)
+{
+ int i;
+ volatile uint32_t *addr;
+
+ DWC_PRINTF("Host Global Registers\n");
+ addr = &core_if->host_if->host_global_regs->hcfg;
+ DWC_PRINTF("HCFG @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->host_if->host_global_regs->hfir;
+ DWC_PRINTF("HFIR @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->host_if->host_global_regs->hfnum;
+ DWC_PRINTF("HFNUM @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->host_if->host_global_regs->hptxsts;
+ DWC_PRINTF("HPTXSTS @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->host_if->host_global_regs->haint;
+ DWC_PRINTF("HAINT @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->host_if->host_global_regs->haintmsk;
+ DWC_PRINTF("HAINTMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ if (core_if->dma_desc_enable) {
+ addr = &core_if->host_if->host_global_regs->hflbaddr;
+ DWC_PRINTF("HFLBADDR @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ }
+
+ addr = core_if->host_if->hprt0;
+ DWC_PRINTF("HPRT0 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+
+ for (i = 0; i < core_if->core_params->host_channels; i++) {
+ DWC_PRINTF("Host Channel %d Specific Registers\n", i);
+ addr = &core_if->host_if->hc_regs[i]->hcchar;
+ DWC_PRINTF("HCCHAR @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->host_if->hc_regs[i]->hcsplt;
+ DWC_PRINTF("HCSPLT @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->host_if->hc_regs[i]->hcint;
+ DWC_PRINTF("HCINT @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->host_if->hc_regs[i]->hcintmsk;
+ DWC_PRINTF("HCINTMSK @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->host_if->hc_regs[i]->hctsiz;
+ DWC_PRINTF("HCTSIZ @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->host_if->hc_regs[i]->hcdma;
+ DWC_PRINTF("HCDMA @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ if (core_if->dma_desc_enable) {
+ addr = &core_if->host_if->hc_regs[i]->hcdmab;
+ DWC_PRINTF("HCDMAB @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ }
+
+ }
+ return;
+}
+
+/**
+ * This function reads the core global registers and prints them
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_dump_global_registers(dwc_otg_core_if_t * core_if)
+{
+ int i, ep_num;
+ volatile uint32_t *addr;
+ char *txfsiz;
+
+ DWC_PRINTF("Core Global Registers\n");
+ addr = &core_if->core_global_regs->gotgctl;
+ DWC_PRINTF("GOTGCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gotgint;
+ DWC_PRINTF("GOTGINT @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gahbcfg;
+ DWC_PRINTF("GAHBCFG @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gusbcfg;
+ DWC_PRINTF("GUSBCFG @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->grstctl;
+ DWC_PRINTF("GRSTCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gintsts;
+ DWC_PRINTF("GINTSTS @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gintmsk;
+ DWC_PRINTF("GINTMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->grxstsr;
+ DWC_PRINTF("GRXSTSR @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->grxfsiz;
+ DWC_PRINTF("GRXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gnptxfsiz;
+ DWC_PRINTF("GNPTXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gnptxsts;
+ DWC_PRINTF("GNPTXSTS @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gi2cctl;
+ DWC_PRINTF("GI2CCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gpvndctl;
+ DWC_PRINTF("GPVNDCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->ggpio;
+ DWC_PRINTF("GGPIO @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->guid;
+ DWC_PRINTF("GUID @0x%08lX : 0x%08X\n",
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gsnpsid;
+ DWC_PRINTF("GSNPSID @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->ghwcfg1;
+ DWC_PRINTF("GHWCFG1 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->ghwcfg2;
+ DWC_PRINTF("GHWCFG2 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->ghwcfg3;
+ DWC_PRINTF("GHWCFG3 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->ghwcfg4;
+ DWC_PRINTF("GHWCFG4 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->glpmcfg;
+ DWC_PRINTF("GLPMCFG @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gpwrdn;
+ DWC_PRINTF("GPWRDN @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->gdfifocfg;
+ DWC_PRINTF("GDFIFOCFG @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+ addr = &core_if->core_global_regs->adpctl;
+ DWC_PRINTF("ADPCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ dwc_otg_adp_read_reg(core_if));
+ addr = &core_if->core_global_regs->hptxfsiz;
+ DWC_PRINTF("HPTXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+
+ if (core_if->en_multiple_tx_fifo == 0) {
+ ep_num = core_if->hwcfg4.b.num_dev_perio_in_ep;
+ txfsiz = "DPTXFSIZ";
+ } else {
+ ep_num = core_if->hwcfg4.b.num_in_eps;
+ txfsiz = "DIENPTXF";
+ }
+ for (i = 0; i < ep_num; i++) {
+ addr = &core_if->core_global_regs->dtxfsiz[i];
+ DWC_PRINTF("%s[%d] @0x%08lX : 0x%08X\n", txfsiz, i + 1,
+ (unsigned long)addr, DWC_READ_REG32(addr));
+ }
+ addr = core_if->pcgcctl;
+ DWC_PRINTF("PCGCCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
+ DWC_READ_REG32(addr));
+}
+
+/**
+ * Flush a Tx FIFO.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param num Tx FIFO to flush.
+ */
+void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * core_if, const int num)
+{
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ volatile grstctl_t greset = {.d32 = 0 };
+ int count = 0;
+
+ DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "Flush Tx FIFO %d\n", num);
+
+ greset.b.txfflsh = 1;
+ greset.b.txfnum = num;
+ DWC_WRITE_REG32(&global_regs->grstctl, greset.d32);
+
+ do {
+ greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
+ if (++count > 10000) {
+ DWC_WARN("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
+ __func__, greset.d32,
+ DWC_READ_REG32(&global_regs->gnptxsts));
+ break;
+ }
+ dwc_udelay(1);
+ } while (greset.b.txfflsh == 1);
+
+ /* Wait for 3 PHY Clocks */
+ dwc_udelay(1);
+}
+
+/**
+ * Flush Rx FIFO.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ volatile grstctl_t greset = {.d32 = 0 };
+ int count = 0;
+
+ DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "%s\n", __func__);
+ /*
+ *
+ */
+ greset.b.rxfflsh = 1;
+ DWC_WRITE_REG32(&global_regs->grstctl, greset.d32);
+
+ do {
+ greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
+ if (++count > 10000) {
+ DWC_WARN("%s() HANG! GRSTCTL=%0x\n", __func__,
+ greset.d32);
+ break;
+ }
+ dwc_udelay(1);
+ } while (greset.b.rxfflsh == 1);
+
+ /* Wait for 3 PHY Clocks */
+ dwc_udelay(1);
+}
+
+/**
+ * Do core a soft reset of the core. Be careful with this because it
+ * resets all the internal state machines of the core.
+ */
+void dwc_otg_core_reset(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ volatile grstctl_t greset = {.d32 = 0 };
+ int count = 0;
+
+ DWC_DEBUGPL(DBG_CILV, "%s\n", __func__);
+ /* Wait for AHB master IDLE state. */
+ do {
+ dwc_udelay(10);
+ greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
+ if (++count > 100000) {
+ DWC_WARN("%s() HANG! AHB Idle GRSTCTL=%0x\n", __func__,
+ greset.d32);
+ return;
+ }
+ }
+ while (greset.b.ahbidle == 0);
+
+ /* Core Soft Reset */
+ count = 0;
+ greset.b.csftrst = 1;
+ DWC_WRITE_REG32(&global_regs->grstctl, greset.d32);
+ do {
+ greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
+ if (++count > 10000) {
+ DWC_WARN("%s() HANG! Soft Reset GRSTCTL=%0x\n",
+ __func__, greset.d32);
+ break;
+ }
+ dwc_udelay(1);
+ }
+ while (greset.b.csftrst == 1);
+
+ /* Wait for 3 PHY Clocks */
+ dwc_mdelay(100);
+}
+
+uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if)
+{
+ return (dwc_otg_mode(_core_if) != DWC_HOST_MODE);
+}
+
+uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if)
+{
+ return (dwc_otg_mode(_core_if) == DWC_HOST_MODE);
+}
+
+/**
+ * Register HCD callbacks. The callbacks are used to start and stop
+ * the HCD for interrupt processing.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param cb the HCD callback structure.
+ * @param p pointer to be passed to callback function (usb_hcd*).
+ */
+void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * core_if,
+ dwc_otg_cil_callbacks_t * cb, void *p)
+{
+ core_if->hcd_cb = cb;
+ cb->p = p;
+}
+
+/**
+ * Register PCD callbacks. The callbacks are used to start and stop
+ * the PCD for interrupt processing.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param cb the PCD callback structure.
+ * @param p pointer to be passed to callback function (pcd*).
+ */
+void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * core_if,
+ dwc_otg_cil_callbacks_t * cb, void *p)
+{
+ core_if->pcd_cb = cb;
+ cb->p = p;
+}
+
+#ifdef DWC_EN_ISOC
+
+/**
+ * This function writes isoc data per 1 (micro)frame into tx fifo
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void write_isoc_frame_data(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ dwc_otg_dev_in_ep_regs_t *ep_regs;
+ dtxfsts_data_t txstatus = {.d32 = 0 };
+ uint32_t len = 0;
+ uint32_t dwords;
+
+ ep->xfer_len = ep->data_per_frame;
+ ep->xfer_count = 0;
+
+ ep_regs = core_if->dev_if->in_ep_regs[ep->num];
+
+ len = ep->xfer_len - ep->xfer_count;
+
+ if (len > ep->maxpacket) {
+ len = ep->maxpacket;
+ }
+
+ dwords = (len + 3) / 4;
+
+ /* While there is space in the queue and space in the FIFO and
+ * More data to tranfer, Write packets to the Tx FIFO */
+ txstatus.d32 =
+ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]->dtxfsts);
+ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", ep->num, txstatus.d32);
+
+ while (txstatus.b.txfspcavail > dwords &&
+ ep->xfer_count < ep->xfer_len && ep->xfer_len != 0) {
+ /* Write the FIFO */
+ dwc_otg_ep_write_packet(core_if, ep, 0);
+
+ len = ep->xfer_len - ep->xfer_count;
+ if (len > ep->maxpacket) {
+ len = ep->maxpacket;
+ }
+
+ dwords = (len + 3) / 4;
+ txstatus.d32 =
+ DWC_READ_REG32(&core_if->dev_if->
+ in_ep_regs[ep->num]->dtxfsts);
+ DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", ep->num,
+ txstatus.d32);
+ }
+}
+
+/**
+ * This function initializes a descriptor chain for Isochronous transfer
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if,
+ dwc_ep_t * ep)
+{
+ deptsiz_data_t deptsiz = {.d32 = 0 };
+ depctl_data_t depctl = {.d32 = 0 };
+ dsts_data_t dsts = {.d32 = 0 };
+ volatile uint32_t *addr;
+
+ if (ep->is_in) {
+ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
+ } else {
+ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
+ }
+
+ ep->xfer_len = ep->data_per_frame;
+ ep->xfer_count = 0;
+ ep->xfer_buff = ep->cur_pkt_addr;
+ ep->dma_addr = ep->cur_pkt_dma_addr;
+
+ if (ep->is_in) {
+ /* Program the transfer size and packet count
+ * as follows: xfersize = N * maxpacket +
+ * short_packet pktcnt = N + (short_packet
+ * exist ? 1 : 0)
+ */
+ deptsiz.b.xfersize = ep->xfer_len;
+ deptsiz.b.pktcnt =
+ (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
+ deptsiz.b.mc = deptsiz.b.pktcnt;
+ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz,
+ deptsiz.d32);
+
+ /* Write the DMA register */
+ if (core_if->dma_enable) {
+ DWC_WRITE_REG32(&
+ (core_if->dev_if->in_ep_regs[ep->num]->
+ diepdma), (uint32_t) ep->dma_addr);
+ }
+ } else {
+ deptsiz.b.pktcnt =
+ (ep->xfer_len + (ep->maxpacket - 1)) / ep->maxpacket;
+ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
+
+ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->
+ doeptsiz, deptsiz.d32);
+
+ if (core_if->dma_enable) {
+ DWC_WRITE_REG32(&
+ (core_if->dev_if->out_ep_regs[ep->num]->
+ doepdma), (uint32_t) ep->dma_addr);
+ }
+ }
+
+ /** Enable endpoint, clear nak */
+
+ depctl.d32 = 0;
+ if (ep->bInterval == 1) {
+ dsts.d32 =
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+ ep->next_frame = dsts.b.soffn + ep->bInterval;
+
+ if (ep->next_frame & 0x1) {
+ depctl.b.setd1pid = 1;
+ } else {
+ depctl.b.setd0pid = 1;
+ }
+ } else {
+ ep->next_frame += ep->bInterval;
+
+ if (ep->next_frame & 0x1) {
+ depctl.b.setd1pid = 1;
+ } else {
+ depctl.b.setd0pid = 1;
+ }
+ }
+ depctl.b.epena = 1;
+ depctl.b.cnak = 1;
+
+ DWC_MODIFY_REG32(addr, 0, depctl.d32);
+ depctl.d32 = DWC_READ_REG32(addr);
+
+ if (ep->is_in && core_if->dma_enable == 0) {
+ write_isoc_frame_data(core_if, ep);
+ }
+
+}
+#endif /* DWC_EN_ISOC */
+
+static void dwc_otg_set_uninitialized(int32_t * p, int size)
+{
+ int i;
+ for (i = 0; i < size; i++) {
+ p[i] = -1;
+ }
+}
+
+static int dwc_otg_param_initialized(int32_t val)
+{
+ return val != -1;
+}
+
+static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if)
+{
+ int i;
+ core_if->core_params = DWC_ALLOC(sizeof(*core_if->core_params));
+ if (!core_if->core_params) {
+ return -DWC_E_NO_MEMORY;
+ }
+ dwc_otg_set_uninitialized((int32_t *) core_if->core_params,
+ sizeof(*core_if->core_params) /
+ sizeof(int32_t));
+ DWC_PRINTF("Setting default values for core params\n");
+ dwc_otg_set_param_otg_cap(core_if, dwc_param_otg_cap_default);
+ dwc_otg_set_param_dma_enable(core_if, dwc_param_dma_enable_default);
+ dwc_otg_set_param_dma_desc_enable(core_if,
+ dwc_param_dma_desc_enable_default);
+ dwc_otg_set_param_opt(core_if, dwc_param_opt_default);
+ dwc_otg_set_param_dma_burst_size(core_if,
+ dwc_param_dma_burst_size_default);
+ dwc_otg_set_param_host_support_fs_ls_low_power(core_if,
+ dwc_param_host_support_fs_ls_low_power_default);
+ dwc_otg_set_param_enable_dynamic_fifo(core_if,
+ dwc_param_enable_dynamic_fifo_default);
+ dwc_otg_set_param_data_fifo_size(core_if,
+ dwc_param_data_fifo_size_default);
+ dwc_otg_set_param_dev_rx_fifo_size(core_if,
+ dwc_param_dev_rx_fifo_size_default);
+ dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if,
+ dwc_param_dev_nperio_tx_fifo_size_default);
+ dwc_otg_set_param_host_rx_fifo_size(core_if,
+ dwc_param_host_rx_fifo_size_default);
+ dwc_otg_set_param_host_nperio_tx_fifo_size(core_if,
+ dwc_param_host_nperio_tx_fifo_size_default);
+ dwc_otg_set_param_host_perio_tx_fifo_size(core_if,
+ dwc_param_host_perio_tx_fifo_size_default);
+ dwc_otg_set_param_max_transfer_size(core_if,
+ dwc_param_max_transfer_size_default);
+ dwc_otg_set_param_max_packet_count(core_if,
+ dwc_param_max_packet_count_default);
+ dwc_otg_set_param_host_channels(core_if,
+ dwc_param_host_channels_default);
+ dwc_otg_set_param_dev_endpoints(core_if,
+ dwc_param_dev_endpoints_default);
+ dwc_otg_set_param_phy_type(core_if, dwc_param_phy_type_default);
+ dwc_otg_set_param_speed(core_if, dwc_param_speed_default);
+ dwc_otg_set_param_host_ls_low_power_phy_clk(core_if,
+ dwc_param_host_ls_low_power_phy_clk_default);
+ dwc_otg_set_param_phy_ulpi_ddr(core_if, dwc_param_phy_ulpi_ddr_default);
+ dwc_otg_set_param_phy_ulpi_ext_vbus(core_if,
+ dwc_param_phy_ulpi_ext_vbus_default);
+ dwc_otg_set_param_phy_utmi_width(core_if,
+ dwc_param_phy_utmi_width_default);
+ dwc_otg_set_param_ts_dline(core_if, dwc_param_ts_dline_default);
+ dwc_otg_set_param_i2c_enable(core_if, dwc_param_i2c_enable_default);
+ dwc_otg_set_param_ulpi_fs_ls(core_if, dwc_param_ulpi_fs_ls_default);
+ dwc_otg_set_param_en_multiple_tx_fifo(core_if,
+ dwc_param_en_multiple_tx_fifo_default);
+ for (i = 0; i < 15; i++) {
+ dwc_otg_set_param_dev_perio_tx_fifo_size(core_if,
+ dwc_param_dev_perio_tx_fifo_size_default,
+ i);
+ }
+
+ for (i = 0; i < 15; i++) {
+ dwc_otg_set_param_dev_tx_fifo_size(core_if,
+ dwc_param_dev_tx_fifo_size_default,
+ i);
+ }
+ dwc_otg_set_param_thr_ctl(core_if, dwc_param_thr_ctl_default);
+ dwc_otg_set_param_mpi_enable(core_if, dwc_param_mpi_enable_default);
+ dwc_otg_set_param_pti_enable(core_if, dwc_param_pti_enable_default);
+ dwc_otg_set_param_lpm_enable(core_if, dwc_param_lpm_enable_default);
+ dwc_otg_set_param_ic_usb_cap(core_if, dwc_param_ic_usb_cap_default);
+ dwc_otg_set_param_tx_thr_length(core_if,
+ dwc_param_tx_thr_length_default);
+ dwc_otg_set_param_rx_thr_length(core_if,
+ dwc_param_rx_thr_length_default);
+ dwc_otg_set_param_ahb_thr_ratio(core_if,
+ dwc_param_ahb_thr_ratio_default);
+ dwc_otg_set_param_power_down(core_if, dwc_param_power_down_default);
+ dwc_otg_set_param_reload_ctl(core_if, dwc_param_reload_ctl_default);
+ dwc_otg_set_param_dev_out_nak(core_if, dwc_param_dev_out_nak_default);
+ dwc_otg_set_param_cont_on_bna(core_if, dwc_param_cont_on_bna_default);
+ dwc_otg_set_param_ahb_single(core_if, dwc_param_ahb_single_default);
+ dwc_otg_set_param_otg_ver(core_if, dwc_param_otg_ver_default);
+ dwc_otg_set_param_adp_enable(core_if, dwc_param_adp_enable_default);
+ return 0;
+}
+
+uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if)
+{
+ return core_if->dma_enable;
+}
+
+/* Checks if the parameter is outside of its valid range of values */
+#define DWC_OTG_PARAM_TEST(_param_, _low_, _high_) \
+ (((_param_) < (_low_)) || \
+ ((_param_) > (_high_)))
+
+/* Parameter access functions */
+int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int valid;
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 2)) {
+ DWC_WARN("Wrong value for otg_cap parameter\n");
+ DWC_WARN("otg_cap parameter must be 0,1 or 2\n");
+ retval = -DWC_E_INVALID;
+ goto out;
+ }
+
+ valid = 1;
+ switch (val) {
+ case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE:
+ if (core_if->hwcfg2.b.op_mode !=
+ DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
+ valid = 0;
+ break;
+ case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE:
+ if ((core_if->hwcfg2.b.op_mode !=
+ DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
+ && (core_if->hwcfg2.b.op_mode !=
+ DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG)
+ && (core_if->hwcfg2.b.op_mode !=
+ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE)
+ && (core_if->hwcfg2.b.op_mode !=
+ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) {
+ valid = 0;
+ }
+ break;
+ case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE:
+ /* always valid */
+ break;
+ }
+ if (!valid) {
+ if (dwc_otg_param_initialized(core_if->core_params->otg_cap)) {
+ DWC_ERROR
+ ("%d invalid for otg_cap paremter. Check HW configuration.\n",
+ val);
+ }
+ val =
+ (((core_if->hwcfg2.b.op_mode ==
+ DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
+ || (core_if->hwcfg2.b.op_mode ==
+ DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG)
+ || (core_if->hwcfg2.b.op_mode ==
+ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE)
+ || (core_if->hwcfg2.b.op_mode ==
+ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) ?
+ DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE :
+ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->otg_cap = val;
+out:
+ return retval;
+}
+
+int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->otg_cap;
+}
+
+int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong value for opt parameter\n");
+ return -DWC_E_INVALID;
+ }
+ core_if->core_params->opt = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->opt;
+}
+
+int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong value for dma enable\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val == 1) && (core_if->hwcfg2.b.architecture == 0)) {
+ if (dwc_otg_param_initialized(core_if->core_params->dma_enable)) {
+ DWC_ERROR
+ ("%d invalid for dma_enable paremter. Check HW configuration.\n",
+ val);
+ }
+ val = 0;
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->dma_enable = val;
+ if (val == 0) {
+ dwc_otg_set_param_dma_desc_enable(core_if, 0);
+ }
+ return retval;
+}
+
+int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->dma_enable;
+}
+
+int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong value for dma_enable\n");
+ DWC_WARN("dma_desc_enable must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val == 1)
+ && ((dwc_otg_get_param_dma_enable(core_if) == 0)
+ || (core_if->hwcfg4.b.desc_dma == 0))) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->dma_desc_enable)) {
+ DWC_ERROR
+ ("%d invalid for dma_desc_enable paremter. Check HW configuration.\n",
+ val);
+ }
+ val = 0;
+ retval = -DWC_E_INVALID;
+ }
+ core_if->core_params->dma_desc_enable = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->dma_desc_enable;
+}
+
+int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong value for host_support_fs_low_power\n");
+ DWC_WARN("host_support_fs_low_power must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+ core_if->core_params->host_support_fs_ls_low_power = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t *
+ core_if)
+{
+ return core_if->core_params->host_support_fs_ls_low_power;
+}
+
+int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong value for enable_dynamic_fifo\n");
+ DWC_WARN("enable_dynamic_fifo must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val == 1) && (core_if->hwcfg2.b.dynamic_fifo == 0)) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->enable_dynamic_fifo)) {
+ DWC_ERROR
+ ("%d invalid for enable_dynamic_fifo paremter. Check HW configuration.\n",
+ val);
+ }
+ val = 0;
+ retval = -DWC_E_INVALID;
+ }
+ core_if->core_params->enable_dynamic_fifo = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->enable_dynamic_fifo;
+}
+
+int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 32, 32768)) {
+ DWC_WARN("Wrong value for data_fifo_size\n");
+ DWC_WARN("data_fifo_size must be 32-32768\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val > core_if->hwcfg3.b.dfifo_depth) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->data_fifo_size)) {
+ DWC_ERROR
+ ("%d invalid for data_fifo_size parameter. Check HW configuration.\n",
+ val);
+ }
+ val = core_if->hwcfg3.b.dfifo_depth;
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->data_fifo_size = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->data_fifo_size;
+}
+
+int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+ DWC_WARN("Wrong value for dev_rx_fifo_size\n");
+ DWC_WARN("dev_rx_fifo_size must be 16-32768\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val > DWC_READ_REG32(&core_if->core_global_regs->grxfsiz)) {
+ if (dwc_otg_param_initialized(core_if->core_params->dev_rx_fifo_size)) {
+ DWC_WARN("%d invalid for dev_rx_fifo_size parameter\n", val);
+ }
+ val = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->dev_rx_fifo_size = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->dev_rx_fifo_size;
+}
+
+int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+ DWC_WARN("Wrong value for dev_nperio_tx_fifo\n");
+ DWC_WARN("dev_nperio_tx_fifo must be 16-32768\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val > (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> 16)) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->dev_nperio_tx_fifo_size)) {
+ DWC_ERROR
+ ("%d invalid for dev_nperio_tx_fifo_size. Check HW configuration.\n",
+ val);
+ }
+ val =
+ (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >>
+ 16);
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->dev_nperio_tx_fifo_size = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->dev_nperio_tx_fifo_size;
+}
+
+int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+ DWC_WARN("Wrong value for host_rx_fifo_size\n");
+ DWC_WARN("host_rx_fifo_size must be 16-32768\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val > DWC_READ_REG32(&core_if->core_global_regs->grxfsiz)) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->host_rx_fifo_size)) {
+ DWC_ERROR
+ ("%d invalid for host_rx_fifo_size. Check HW configuration.\n",
+ val);
+ }
+ val = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->host_rx_fifo_size = val;
+ return retval;
+
+}
+
+int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->host_rx_fifo_size;
+}
+
+int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+ DWC_WARN("Wrong value for host_nperio_tx_fifo_size\n");
+ DWC_WARN("host_nperio_tx_fifo_size must be 16-32768\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val > (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> 16)) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->host_nperio_tx_fifo_size)) {
+ DWC_ERROR
+ ("%d invalid for host_nperio_tx_fifo_size. Check HW configuration.\n",
+ val);
+ }
+ val =
+ (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >>
+ 16);
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->host_nperio_tx_fifo_size = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->host_nperio_tx_fifo_size;
+}
+
+int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+ DWC_WARN("Wrong value for host_perio_tx_fifo_size\n");
+ DWC_WARN("host_perio_tx_fifo_size must be 16-32768\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val >
+ ((core_if->hptxfsiz.d32)>> 16)) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->host_perio_tx_fifo_size)) {
+ DWC_ERROR
+ ("%d invalid for host_perio_tx_fifo_size. Check HW configuration.\n",
+ val);
+ }
+ val = (core_if->hptxfsiz.d32) >> 16;
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->host_perio_tx_fifo_size = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->host_perio_tx_fifo_size;
+}
+
+int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 2047, 524288)) {
+ DWC_WARN("Wrong value for max_transfer_size\n");
+ DWC_WARN("max_transfer_size must be 2047-524288\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val >= (1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11))) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->max_transfer_size)) {
+ DWC_ERROR
+ ("%d invalid for max_transfer_size. Check HW configuration.\n",
+ val);
+ }
+ val =
+ ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 11)) -
+ 1);
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->max_transfer_size = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->max_transfer_size;
+}
+
+int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 15, 511)) {
+ DWC_WARN("Wrong value for max_packet_count\n");
+ DWC_WARN("max_packet_count must be 15-511\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val > (1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4))) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->max_packet_count)) {
+ DWC_ERROR
+ ("%d invalid for max_packet_count. Check HW configuration.\n",
+ val);
+ }
+ val =
+ ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1);
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->max_packet_count = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->max_packet_count;
+}
+
+int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 1, 16)) {
+ DWC_WARN("Wrong value for host_channels\n");
+ DWC_WARN("host_channels must be 1-16\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val > (core_if->hwcfg2.b.num_host_chan + 1)) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->host_channels)) {
+ DWC_ERROR
+ ("%d invalid for host_channels. Check HW configurations.\n",
+ val);
+ }
+ val = (core_if->hwcfg2.b.num_host_chan + 1);
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->host_channels = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->host_channels;
+}
+
+int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 1, 15)) {
+ DWC_WARN("Wrong value for dev_endpoints\n");
+ DWC_WARN("dev_endpoints must be 1-15\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val > (core_if->hwcfg2.b.num_dev_ep)) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->dev_endpoints)) {
+ DWC_ERROR
+ ("%d invalid for dev_endpoints. Check HW configurations.\n",
+ val);
+ }
+ val = core_if->hwcfg2.b.num_dev_ep;
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->dev_endpoints = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->dev_endpoints;
+}
+
+int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ int valid = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 2)) {
+ DWC_WARN("Wrong value for phy_type\n");
+ DWC_WARN("phy_type must be 0,1 or 2\n");
+ return -DWC_E_INVALID;
+ }
+#ifndef NO_FS_PHY_HW_CHECKS
+ if ((val == DWC_PHY_TYPE_PARAM_UTMI) &&
+ ((core_if->hwcfg2.b.hs_phy_type == 1) ||
+ (core_if->hwcfg2.b.hs_phy_type == 3))) {
+ valid = 1;
+ } else if ((val == DWC_PHY_TYPE_PARAM_ULPI) &&
+ ((core_if->hwcfg2.b.hs_phy_type == 2) ||
+ (core_if->hwcfg2.b.hs_phy_type == 3))) {
+ valid = 1;
+ } else if ((val == DWC_PHY_TYPE_PARAM_FS) &&
+ (core_if->hwcfg2.b.fs_phy_type == 1)) {
+ valid = 1;
+ }
+ if (!valid) {
+ if (dwc_otg_param_initialized(core_if->core_params->phy_type)) {
+ DWC_ERROR
+ ("%d invalid for phy_type. Check HW configurations.\n",
+ val);
+ }
+ if (core_if->hwcfg2.b.hs_phy_type) {
+ if ((core_if->hwcfg2.b.hs_phy_type == 3) ||
+ (core_if->hwcfg2.b.hs_phy_type == 1)) {
+ val = DWC_PHY_TYPE_PARAM_UTMI;
+ } else {
+ val = DWC_PHY_TYPE_PARAM_ULPI;
+ }
+ }
+ retval = -DWC_E_INVALID;
+ }
+#endif
+ core_if->core_params->phy_type = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->phy_type;
+}
+
+int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong value for speed parameter\n");
+ DWC_WARN("max_speed parameter must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+ if ((val == 0)
+ && dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS) {
+ if (dwc_otg_param_initialized(core_if->core_params->speed)) {
+ DWC_ERROR
+ ("%d invalid for speed paremter. Check HW configuration.\n",
+ val);
+ }
+ val =
+ (dwc_otg_get_param_phy_type(core_if) ==
+ DWC_PHY_TYPE_PARAM_FS ? 1 : 0);
+ retval = -DWC_E_INVALID;
+ }
+ core_if->core_params->speed = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->speed;
+}
+
+int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN
+ ("Wrong value for host_ls_low_power_phy_clk parameter\n");
+ DWC_WARN("host_ls_low_power_phy_clk must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val == DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ)
+ && (dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS)) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->host_ls_low_power_phy_clk)) {
+ DWC_ERROR
+ ("%d invalid for host_ls_low_power_phy_clk. Check HW configuration.\n",
+ val);
+ }
+ val =
+ (dwc_otg_get_param_phy_type(core_if) ==
+ DWC_PHY_TYPE_PARAM_FS) ?
+ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ :
+ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ;
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->host_ls_low_power_phy_clk = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->host_ls_low_power_phy_clk;
+}
+
+int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong value for phy_ulpi_ddr\n");
+ DWC_WARN("phy_upli_ddr must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ core_if->core_params->phy_ulpi_ddr = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->phy_ulpi_ddr;
+}
+
+int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong valaue for phy_ulpi_ext_vbus\n");
+ DWC_WARN("phy_ulpi_ext_vbus must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ core_if->core_params->phy_ulpi_ext_vbus = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->phy_ulpi_ext_vbus;
+}
+
+int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 8, 8) && DWC_OTG_PARAM_TEST(val, 16, 16)) {
+ DWC_WARN("Wrong valaue for phy_utmi_width\n");
+ DWC_WARN("phy_utmi_width must be 8 or 16\n");
+ return -DWC_E_INVALID;
+ }
+
+ core_if->core_params->phy_utmi_width = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->phy_utmi_width;
+}
+
+int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong valaue for ulpi_fs_ls\n");
+ DWC_WARN("ulpi_fs_ls must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ core_if->core_params->ulpi_fs_ls = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->ulpi_fs_ls;
+}
+
+int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong valaue for ts_dline\n");
+ DWC_WARN("ts_dline must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ core_if->core_params->ts_dline = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->ts_dline;
+}
+
+int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong valaue for i2c_enable\n");
+ DWC_WARN("i2c_enable must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+#ifndef NO_FS_PHY_HW_CHECK
+ if (val == 1 && core_if->hwcfg3.b.i2c == 0) {
+ if (dwc_otg_param_initialized(core_if->core_params->i2c_enable)) {
+ DWC_ERROR
+ ("%d invalid for i2c_enable. Check HW configuration.\n",
+ val);
+ }
+ val = 0;
+ retval = -DWC_E_INVALID;
+ }
+#endif
+
+ core_if->core_params->i2c_enable = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->i2c_enable;
+}
+
+int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+ int32_t val, int fifo_num)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 4, 768)) {
+ DWC_WARN("Wrong value for dev_perio_tx_fifo_size\n");
+ DWC_WARN("dev_perio_tx_fifo_size must be 4-768\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val >
+ (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]))) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->dev_perio_tx_fifo_size[fifo_num])) {
+ DWC_ERROR
+ ("`%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n",
+ val, fifo_num);
+ }
+ val = (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]));
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->dev_perio_tx_fifo_size[fifo_num] = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+ int fifo_num)
+{
+ return core_if->core_params->dev_perio_tx_fifo_size[fifo_num];
+}
+
+int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if,
+ int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong valaue for en_multiple_tx_fifo,\n");
+ DWC_WARN("en_multiple_tx_fifo must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val == 1 && core_if->hwcfg4.b.ded_fifo_en == 0) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->en_multiple_tx_fifo)) {
+ DWC_ERROR
+ ("%d invalid for parameter en_multiple_tx_fifo. Check HW configuration.\n",
+ val);
+ }
+ val = 0;
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->en_multiple_tx_fifo = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->en_multiple_tx_fifo;
+}
+
+int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val,
+ int fifo_num)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 4, 768)) {
+ DWC_WARN("Wrong value for dev_tx_fifo_size\n");
+ DWC_WARN("dev_tx_fifo_size must be 4-768\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val >
+ (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]))) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->dev_tx_fifo_size[fifo_num])) {
+ DWC_ERROR
+ ("`%d' invalid for parameter `dev_tx_fifo_size_%d'. Check HW configuration.\n",
+ val, fifo_num);
+ }
+ val = (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]));
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->dev_tx_fifo_size[fifo_num] = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if,
+ int fifo_num)
+{
+ return core_if->core_params->dev_tx_fifo_size[fifo_num];
+}
+
+int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 7)) {
+ DWC_WARN("Wrong value for thr_ctl\n");
+ DWC_WARN("thr_ctl must be 0-7\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val != 0) &&
+ (!dwc_otg_get_param_dma_enable(core_if) ||
+ !core_if->hwcfg4.b.ded_fifo_en)) {
+ if (dwc_otg_param_initialized(core_if->core_params->thr_ctl)) {
+ DWC_ERROR
+ ("%d invalid for parameter thr_ctl. Check HW configuration.\n",
+ val);
+ }
+ val = 0;
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->thr_ctl = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_thr_ctl(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->thr_ctl;
+}
+
+int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("Wrong value for lpm_enable\n");
+ DWC_WARN("lpm_enable must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val && !core_if->hwcfg3.b.otg_lpm_en) {
+ if (dwc_otg_param_initialized(core_if->core_params->lpm_enable)) {
+ DWC_ERROR
+ ("%d invalid for parameter lpm_enable. Check HW configuration.\n",
+ val);
+ }
+ val = 0;
+ retval = -DWC_E_INVALID;
+ }
+
+ core_if->core_params->lpm_enable = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->lpm_enable;
+}
+
+int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 8, 128)) {
+ DWC_WARN("Wrong valaue for tx_thr_length\n");
+ DWC_WARN("tx_thr_length must be 8 - 128\n");
+ return -DWC_E_INVALID;
+ }
+
+ core_if->core_params->tx_thr_length = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_tx_thr_length(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->tx_thr_length;
+}
+
+int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 8, 128)) {
+ DWC_WARN("Wrong valaue for rx_thr_length\n");
+ DWC_WARN("rx_thr_length must be 8 - 128\n");
+ return -DWC_E_INVALID;
+ }
+
+ core_if->core_params->rx_thr_length = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_rx_thr_length(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->rx_thr_length;
+}
+
+int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ if (DWC_OTG_PARAM_TEST(val, 1, 1) &&
+ DWC_OTG_PARAM_TEST(val, 4, 4) &&
+ DWC_OTG_PARAM_TEST(val, 8, 8) &&
+ DWC_OTG_PARAM_TEST(val, 16, 16) &&
+ DWC_OTG_PARAM_TEST(val, 32, 32) &&
+ DWC_OTG_PARAM_TEST(val, 64, 64) &&
+ DWC_OTG_PARAM_TEST(val, 128, 128) &&
+ DWC_OTG_PARAM_TEST(val, 256, 256)) {
+ DWC_WARN("`%d' invalid for parameter `dma_burst_size'\n", val);
+ return -DWC_E_INVALID;
+ }
+ core_if->core_params->dma_burst_size = val;
+ return 0;
+}
+
+int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->dma_burst_size;
+}
+
+int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("`%d' invalid for parameter `pti_enable'\n", val);
+ return -DWC_E_INVALID;
+ }
+ if (val && (core_if->snpsid < OTG_CORE_REV_2_72a)) {
+ if (dwc_otg_param_initialized(core_if->core_params->pti_enable)) {
+ DWC_ERROR
+ ("%d invalid for parameter pti_enable. Check HW configuration.\n",
+ val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+ core_if->core_params->pti_enable = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->pti_enable;
+}
+
+int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("`%d' invalid for parameter `mpi_enable'\n", val);
+ return -DWC_E_INVALID;
+ }
+ if (val && (core_if->hwcfg2.b.multi_proc_int == 0)) {
+ if (dwc_otg_param_initialized(core_if->core_params->mpi_enable)) {
+ DWC_ERROR
+ ("%d invalid for parameter mpi_enable. Check HW configuration.\n",
+ val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+ core_if->core_params->mpi_enable = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->mpi_enable;
+}
+
+int dwc_otg_set_param_adp_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("`%d' invalid for parameter `adp_enable'\n", val);
+ return -DWC_E_INVALID;
+ }
+ if (val && (core_if->hwcfg3.b.adp_supp == 0)) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->adp_supp_enable)) {
+ DWC_ERROR
+ ("%d invalid for parameter adp_enable. Check HW configuration.\n",
+ val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+ core_if->core_params->adp_supp_enable = val;
+ /*Set OTG version 2.0 in case of enabling ADP*/
+ if (val)
+ dwc_otg_set_param_otg_ver(core_if, 1);
+
+ return retval;
+}
+
+int32_t dwc_otg_get_param_adp_enable(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->adp_supp_enable;
+}
+
+int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("`%d' invalid for parameter `ic_usb_cap'\n", val);
+ DWC_WARN("ic_usb_cap must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val && (core_if->hwcfg2.b.otg_enable_ic_usb == 0)) {
+ if (dwc_otg_param_initialized(core_if->core_params->ic_usb_cap)) {
+ DWC_ERROR
+ ("%d invalid for parameter ic_usb_cap. Check HW configuration.\n",
+ val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+ core_if->core_params->ic_usb_cap = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->ic_usb_cap;
+}
+
+int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ int valid = 1;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 3)) {
+ DWC_WARN("`%d' invalid for parameter `ahb_thr_ratio'\n", val);
+ DWC_WARN("ahb_thr_ratio must be 0 - 3\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (val
+ && (core_if->snpsid < OTG_CORE_REV_2_81a
+ || !dwc_otg_get_param_thr_ctl(core_if))) {
+ valid = 0;
+ } else if (val
+ && ((dwc_otg_get_param_tx_thr_length(core_if) / (1 << val)) <
+ 4)) {
+ valid = 0;
+ }
+ if (valid == 0) {
+ if (dwc_otg_param_initialized
+ (core_if->core_params->ahb_thr_ratio)) {
+ DWC_ERROR
+ ("%d invalid for parameter ahb_thr_ratio. Check HW configuration.\n",
+ val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+
+ core_if->core_params->ahb_thr_ratio = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->ahb_thr_ratio;
+}
+
+int dwc_otg_set_param_power_down(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ int valid = 1;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 2)) {
+ DWC_WARN("`%d' invalid for parameter `power_down'\n", val);
+ DWC_WARN("power_down must be 0 - 2\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val == 2) && (core_if->snpsid < OTG_CORE_REV_2_91a)) {
+ valid = 0;
+ }
+ if (valid == 0) {
+ if (dwc_otg_param_initialized(core_if->core_params->power_down)) {
+ DWC_ERROR
+ ("%d invalid for parameter power_down. Check HW configuration.\n",
+ val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+ core_if->core_params->power_down = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_power_down(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->power_down;
+}
+
+int dwc_otg_set_param_reload_ctl(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ int valid = 1;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("`%d' invalid for parameter `reload_ctl'\n", val);
+ DWC_WARN("reload_ctl must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val == 1) && (core_if->snpsid < OTG_CORE_REV_2_92a)) {
+ valid = 0;
+ }
+ if (valid == 0) {
+ if (dwc_otg_param_initialized(core_if->core_params->reload_ctl)) {
+ DWC_ERROR("%d invalid for parameter reload_ctl."
+ "Check HW configuration.\n", val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+ core_if->core_params->reload_ctl = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_reload_ctl(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->reload_ctl;
+}
+
+int dwc_otg_set_param_dev_out_nak(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ int valid = 1;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("`%d' invalid for parameter `dev_out_nak'\n", val);
+ DWC_WARN("dev_out_nak must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val == 1) && ((core_if->snpsid < OTG_CORE_REV_2_93a) ||
+ !(core_if->core_params->dma_desc_enable))) {
+ valid = 0;
+ }
+ if (valid == 0) {
+ if (dwc_otg_param_initialized(core_if->core_params->dev_out_nak)) {
+ DWC_ERROR("%d invalid for parameter dev_out_nak."
+ "Check HW configuration.\n", val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+ core_if->core_params->dev_out_nak = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_dev_out_nak(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->dev_out_nak;
+}
+
+int dwc_otg_set_param_cont_on_bna(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ int valid = 1;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("`%d' invalid for parameter `cont_on_bna'\n", val);
+ DWC_WARN("cont_on_bna must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val == 1) && ((core_if->snpsid < OTG_CORE_REV_2_94a) ||
+ !(core_if->core_params->dma_desc_enable))) {
+ valid = 0;
+ }
+ if (valid == 0) {
+ if (dwc_otg_param_initialized(core_if->core_params->cont_on_bna)) {
+ DWC_ERROR("%d invalid for parameter cont_on_bna."
+ "Check HW configuration.\n", val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+ core_if->core_params->cont_on_bna = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_cont_on_bna(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->cont_on_bna;
+}
+
+int dwc_otg_set_param_ahb_single(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+ int valid = 1;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("`%d' invalid for parameter `ahb_single'\n", val);
+ DWC_WARN("ahb_single must be 0 or 1\n");
+ return -DWC_E_INVALID;
+ }
+
+ if ((val == 1) && (core_if->snpsid < OTG_CORE_REV_2_94a)) {
+ valid = 0;
+ }
+ if (valid == 0) {
+ if (dwc_otg_param_initialized(core_if->core_params->ahb_single)) {
+ DWC_ERROR("%d invalid for parameter ahb_single."
+ "Check HW configuration.\n", val);
+ }
+ retval = -DWC_E_INVALID;
+ val = 0;
+ }
+ core_if->core_params->ahb_single = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_ahb_single(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->ahb_single;
+}
+
+int dwc_otg_set_param_otg_ver(dwc_otg_core_if_t * core_if, int32_t val)
+{
+ int retval = 0;
+
+ if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+ DWC_WARN("`%d' invalid for parameter `otg_ver'\n", val);
+ DWC_WARN
+ ("otg_ver must be 0(for OTG 1.3 support) or 1(for OTG 2.0 support)\n");
+ return -DWC_E_INVALID;
+ }
+
+ core_if->core_params->otg_ver = val;
+ return retval;
+}
+
+int32_t dwc_otg_get_param_otg_ver(dwc_otg_core_if_t * core_if)
+{
+ return core_if->core_params->otg_ver;
+}
+
+uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if)
+{
+ gotgctl_data_t otgctl;
+ otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+ return otgctl.b.hstnegscs;
+}
+
+uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if)
+{
+ gotgctl_data_t otgctl;
+ otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+ return otgctl.b.sesreqscs;
+}
+
+void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ if(core_if->otg_ver == 0) {
+ gotgctl_data_t otgctl;
+ otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+ otgctl.b.hnpreq = val;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, otgctl.d32);
+ } else {
+ core_if->otg_sts = val;
+ }
+}
+
+uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if)
+{
+ return core_if->snpsid;
+}
+
+uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if)
+{
+ gintsts_data_t gintsts;
+ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+ return gintsts.b.curmode;
+}
+
+uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if)
+{
+ gusbcfg_data_t usbcfg;
+ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+ return usbcfg.b.hnpcap;
+}
+
+void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ gusbcfg_data_t usbcfg;
+ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+ usbcfg.b.hnpcap = val;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, usbcfg.d32);
+}
+
+uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if)
+{
+ gusbcfg_data_t usbcfg;
+ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+ return usbcfg.b.srpcap;
+}
+
+void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ gusbcfg_data_t usbcfg;
+ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+ usbcfg.b.srpcap = val;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, usbcfg.d32);
+}
+
+uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if)
+{
+ dcfg_data_t dcfg;
+ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+ return dcfg.b.devspd;
+}
+
+void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ dcfg_data_t dcfg;
+ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+ dcfg.b.devspd = val;
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
+}
+
+uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if)
+{
+ hprt0_data_t hprt0;
+ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+ return hprt0.b.prtconnsts;
+}
+
+uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if)
+{
+ dsts_data_t dsts;
+ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+ return dsts.b.enumspd;
+}
+
+uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if)
+{
+ hprt0_data_t hprt0;
+ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+ return hprt0.b.prtpwr;
+
+}
+
+uint32_t dwc_otg_get_core_state(dwc_otg_core_if_t * core_if)
+{
+ return core_if->hibernation_suspend;
+}
+
+void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ hprt0_data_t hprt0;
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtpwr = val;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+}
+
+uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if)
+{
+ hprt0_data_t hprt0;
+ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+ return hprt0.b.prtsusp;
+
+}
+
+void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ hprt0_data_t hprt0;
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtsusp = val;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+}
+
+uint32_t dwc_otg_get_fr_interval(dwc_otg_core_if_t * core_if)
+{
+ hfir_data_t hfir;
+ hfir.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir);
+ return hfir.b.frint;
+
+}
+
+void dwc_otg_set_fr_interval(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ hfir_data_t hfir;
+ uint32_t fram_int;
+ fram_int = calc_frame_interval(core_if);
+ hfir.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir);
+ if (!core_if->core_params->reload_ctl) {
+ DWC_WARN("\nCannot reload HFIR register.HFIR.HFIRRldCtrl bit is"
+ "not set to 1.\nShould load driver with reload_ctl=1"
+ " module parameter\n");
+ return;
+ }
+ switch (fram_int) {
+ case 3750:
+ if ((val < 3350) || (val > 4150)) {
+ DWC_WARN("HFIR interval for HS core and 30 MHz"
+ "clock freq should be from 3350 to 4150\n");
+ return;
+ }
+ break;
+ case 30000:
+ if ((val < 26820) || (val > 33180)) {
+ DWC_WARN("HFIR interval for FS/LS core and 30 MHz"
+ "clock freq should be from 26820 to 33180\n");
+ return;
+ }
+ break;
+ case 6000:
+ if ((val < 5360) || (val > 6640)) {
+ DWC_WARN("HFIR interval for HS core and 48 MHz"
+ "clock freq should be from 5360 to 6640\n");
+ return;
+ }
+ break;
+ case 48000:
+ if ((val < 42912) || (val > 53088)) {
+ DWC_WARN("HFIR interval for FS/LS core and 48 MHz"
+ "clock freq should be from 42912 to 53088\n");
+ return;
+ }
+ break;
+ case 7500:
+ if ((val < 6700) || (val > 8300)) {
+ DWC_WARN("HFIR interval for HS core and 60 MHz"
+ "clock freq should be from 6700 to 8300\n");
+ return;
+ }
+ break;
+ case 60000:
+ if ((val < 53640) || (val > 65536)) {
+ DWC_WARN("HFIR interval for FS/LS core and 60 MHz"
+ "clock freq should be from 53640 to 65536\n");
+ return;
+ }
+ break;
+ default:
+ DWC_WARN("Unknown frame interval\n");
+ return;
+ break;
+
+ }
+ hfir.b.frint = val;
+ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hfir, hfir.d32);
+}
+
+uint32_t dwc_otg_get_mode_ch_tim(dwc_otg_core_if_t * core_if)
+{
+ hcfg_data_t hcfg;
+ hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+ return hcfg.b.modechtimen;
+
+}
+
+void dwc_otg_set_mode_ch_tim(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ hcfg_data_t hcfg;
+ hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+ hcfg.b.modechtimen = val;
+ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32);
+}
+
+void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ hprt0_data_t hprt0;
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtres = val;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+}
+
+uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if)
+{
+ dctl_data_t dctl;
+ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+ return dctl.b.rmtwkupsig;
+}
+
+uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if)
+{
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+
+ DWC_ASSERT(!
+ ((core_if->lx_state == DWC_OTG_L1) ^ lpmcfg.b.prt_sleep_sts),
+ "lx_state = %d, lmpcfg.prt_sleep_sts = %d\n",
+ core_if->lx_state, lpmcfg.b.prt_sleep_sts);
+
+ return lpmcfg.b.prt_sleep_sts;
+}
+
+uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if)
+{
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ return lpmcfg.b.rem_wkup_en;
+}
+
+uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if)
+{
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ return lpmcfg.b.appl_resp;
+}
+
+void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ lpmcfg.b.appl_resp = val;
+ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+}
+
+uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if)
+{
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ return lpmcfg.b.hsic_connect;
+}
+
+void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ lpmcfg.b.hsic_connect = val;
+ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+}
+
+uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if)
+{
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ return lpmcfg.b.inv_sel_hsic;
+
+}
+
+void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ lpmcfg.b.inv_sel_hsic = val;
+ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+}
+
+uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if)
+{
+ return DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+}
+
+void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, val);
+}
+
+uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if)
+{
+ return DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+}
+
+void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, val);
+}
+
+uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if)
+{
+ return DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+}
+
+void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ DWC_WRITE_REG32(&core_if->core_global_regs->grxfsiz, val);
+}
+
+uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if)
+{
+ return DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz);
+}
+
+void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ DWC_WRITE_REG32(&core_if->core_global_regs->gnptxfsiz, val);
+}
+
+uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if)
+{
+ return DWC_READ_REG32(&core_if->core_global_regs->gpvndctl);
+}
+
+void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ DWC_WRITE_REG32(&core_if->core_global_regs->gpvndctl, val);
+}
+
+uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if)
+{
+ return DWC_READ_REG32(&core_if->core_global_regs->ggpio);
+}
+
+void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, val);
+}
+
+uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if)
+{
+ return DWC_READ_REG32(core_if->host_if->hprt0);
+
+}
+
+void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ DWC_WRITE_REG32(core_if->host_if->hprt0, val);
+}
+
+uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if)
+{
+ return DWC_READ_REG32(&core_if->core_global_regs->guid);
+}
+
+void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+ DWC_WRITE_REG32(&core_if->core_global_regs->guid, val);
+}
+
+uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if)
+{
+ return DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz);
+}
+
+uint16_t dwc_otg_get_otg_version(dwc_otg_core_if_t * core_if)
+{
+ return ((core_if->otg_ver == 1) ? (uint16_t)0x0200 : (uint16_t)0x0103);
+}
+
+/**
+ * Start the SRP timer to detect when the SRP does not complete within
+ * 6 seconds.
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+void dwc_otg_pcd_start_srp_timer(dwc_otg_core_if_t * core_if)
+{
+ core_if->srp_timer_started = 1;
+ DWC_TIMER_SCHEDULE(core_if->srp_timer, 6000 /* 6 secs */ );
+}
+
+void dwc_otg_initiate_srp(dwc_otg_core_if_t * core_if)
+{
+ uint32_t *addr = (uint32_t *) & (core_if->core_global_regs->gotgctl);
+ gotgctl_data_t mem;
+ gotgctl_data_t val;
+
+ val.d32 = DWC_READ_REG32(addr);
+ if (val.b.sesreq) {
+ DWC_ERROR("Session Request Already active!\n");
+ return;
+ }
+
+ DWC_INFO("Session Request Initated\n"); //NOTICE
+ mem.d32 = DWC_READ_REG32(addr);
+ mem.b.sesreq = 1;
+ DWC_WRITE_REG32(addr, mem.d32);
+
+ /* Start the SRP timer */
+ dwc_otg_pcd_start_srp_timer(core_if);
+ return;
+}
diff --git a/drivers/usb/dwc_otg/dwc_otg_cil.h b/drivers/usb/dwc_otg/dwc_otg_cil.h
new file mode 100644
index 0000000..9957861
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_cil.h
@@ -0,0 +1,1453 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.h $
+ * $Revision: #122 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871160 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_CIL_H__)
+#define __DWC_CIL_H__
+
+#include "dwc_list.h"
+#include "dwc_otg_dbg.h"
+#include "dwc_otg_regs.h"
+
+#include "dwc_otg_core_if.h"
+#include "dwc_otg_adp.h"
+
+/**
+ * @file
+ * This file contains the interface to the Core Interface Layer.
+ */
+
+#ifdef DWC_UTE_CFI
+
+#define MAX_DMA_DESCS_PER_EP 256
+
+/**
+ * Enumeration for the data buffer mode
+ */
+typedef enum _data_buffer_mode {
+ BM_STANDARD = 0, /* data buffer is in normal mode */
+ BM_SG = 1, /* data buffer uses the scatter/gather mode */
+ BM_CONCAT = 2, /* data buffer uses the concatenation mode */
+ BM_CIRCULAR = 3, /* data buffer uses the circular DMA mode */
+ BM_ALIGN = 4 /* data buffer is in buffer alignment mode */
+} data_buffer_mode_e;
+#endif //DWC_UTE_CFI
+
+/** Macros defined for DWC OTG HW Release version */
+
+#define OTG_CORE_REV_2_60a 0x4F54260A
+#define OTG_CORE_REV_2_71a 0x4F54271A
+#define OTG_CORE_REV_2_72a 0x4F54272A
+#define OTG_CORE_REV_2_80a 0x4F54280A
+#define OTG_CORE_REV_2_81a 0x4F54281A
+#define OTG_CORE_REV_2_90a 0x4F54290A
+#define OTG_CORE_REV_2_91a 0x4F54291A
+#define OTG_CORE_REV_2_92a 0x4F54292A
+#define OTG_CORE_REV_2_93a 0x4F54293A
+#define OTG_CORE_REV_2_94a 0x4F54294A
+
+/**
+ * Information for each ISOC packet.
+ */
+typedef struct iso_pkt_info {
+ uint32_t offset;
+ uint32_t length;
+ int32_t status;
+} iso_pkt_info_t;
+
+/**
+ * The <code>dwc_ep</code> structure represents the state of a single
+ * endpoint when acting in device mode. It contains the data items
+ * needed for an endpoint to be activated and transfer packets.
+ */
+typedef struct dwc_ep {
+ /** EP number used for register address lookup */
+ uint8_t num;
+ /** EP direction 0 = OUT */
+ unsigned is_in:1;
+ /** EP active. */
+ unsigned active:1;
+
+ /**
+ * Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use non-periodic
+ * Tx FIFO. If dedicated Tx FIFOs are enabled Tx FIFO # FOR IN EPs*/
+ unsigned tx_fifo_num:4;
+ /** EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR */
+ unsigned type:2;
+#define DWC_OTG_EP_TYPE_CONTROL 0
+#define DWC_OTG_EP_TYPE_ISOC 1
+#define DWC_OTG_EP_TYPE_BULK 2
+#define DWC_OTG_EP_TYPE_INTR 3
+
+ /** DATA start PID for INTR and BULK EP */
+ unsigned data_pid_start:1;
+ /** Frame (even/odd) for ISOC EP */
+ unsigned even_odd_frame:1;
+ /** Max Packet bytes */
+ unsigned maxpacket:11;
+
+ /** Max Transfer size */
+ uint32_t maxxfer;
+
+ /** @name Transfer state */
+ /** @{ */
+
+ /**
+ * Pointer to the beginning of the transfer buffer -- do not modify
+ * during transfer.
+ */
+
+ dwc_dma_t dma_addr;
+
+ dwc_dma_t dma_desc_addr;
+ dwc_otg_dev_dma_desc_t *desc_addr;
+
+ uint8_t *start_xfer_buff;
+ /** pointer to the transfer buffer */
+ uint8_t *xfer_buff;
+ /** Number of bytes to transfer */
+ unsigned xfer_len:19;
+ /** Number of bytes transferred. */
+ unsigned xfer_count:19;
+ /** Sent ZLP */
+ unsigned sent_zlp:1;
+ /** Total len for control transfer */
+ unsigned total_len:19;
+
+ /** stall clear flag */
+ unsigned stall_clear_flag:1;
+
+#ifdef DWC_UTE_CFI
+ /* The buffer mode */
+ data_buffer_mode_e buff_mode;
+
+ /* The chain of DMA descriptors.
+ * MAX_DMA_DESCS_PER_EP will be allocated for each active EP.
+ */
+ dwc_otg_dma_desc_t *descs;
+
+ /* The DMA address of the descriptors chain start */
+ dma_addr_t descs_dma_addr;
+ /** This variable stores the length of the last enqueued request */
+ uint32_t cfi_req_len;
+#endif //DWC_UTE_CFI
+
+/** Max DMA Descriptor count for any EP */
+#define MAX_DMA_DESC_CNT 256
+ /** Allocated DMA Desc count */
+ uint32_t desc_cnt;
+
+ /** bInterval */
+ uint32_t bInterval;
+ /** Next frame num to setup next ISOC transfer */
+ uint32_t frame_num;
+ /** Indicates SOF number overrun in DSTS */
+ uint8_t frm_overrun;
+
+#ifdef DWC_UTE_PER_IO
+ /** Next frame num for which will be setup DMA Desc */
+ uint32_t xiso_frame_num;
+ /** bInterval */
+ uint32_t xiso_bInterval;
+ /** Count of currently active transfers - shall be either 0 or 1 */
+ int xiso_active_xfers;
+ int xiso_queued_xfers;
+#endif
+#ifdef DWC_EN_ISOC
+ /**
+ * Variables specific for ISOC EPs
+ *
+ */
+ /** DMA addresses of ISOC buffers */
+ dwc_dma_t dma_addr0;
+ dwc_dma_t dma_addr1;
+
+ dwc_dma_t iso_dma_desc_addr;
+ dwc_otg_dev_dma_desc_t *iso_desc_addr;
+
+ /** pointer to the transfer buffers */
+ uint8_t *xfer_buff0;
+ uint8_t *xfer_buff1;
+
+ /** number of ISOC Buffer is processing */
+ uint32_t proc_buf_num;
+ /** Interval of ISOC Buffer processing */
+ uint32_t buf_proc_intrvl;
+ /** Data size for regular frame */
+ uint32_t data_per_frame;
+
+ /* todo - pattern data support is to be implemented in the future */
+ /** Data size for pattern frame */
+ uint32_t data_pattern_frame;
+ /** Frame number of pattern data */
+ uint32_t sync_frame;
+
+ /** ISO Packet number per frame */
+ uint32_t pkt_per_frm;
+ /** Next frame num for which will be setup DMA Desc */
+ uint32_t next_frame;
+ /** Number of packets per buffer processing */
+ uint32_t pkt_cnt;
+ /** Info for all isoc packets */
+ iso_pkt_info_t *pkt_info;
+ /** current pkt number */
+ uint32_t cur_pkt;
+ /** current pkt number */
+ uint8_t *cur_pkt_addr;
+ /** current pkt number */
+ uint32_t cur_pkt_dma_addr;
+#endif /* DWC_EN_ISOC */
+
+/** @} */
+} dwc_ep_t;
+
+/*
+ * Reasons for halting a host channel.
+ */
+typedef enum dwc_otg_halt_status {
+ DWC_OTG_HC_XFER_NO_HALT_STATUS,
+ DWC_OTG_HC_XFER_COMPLETE,
+ DWC_OTG_HC_XFER_URB_COMPLETE,
+ DWC_OTG_HC_XFER_ACK,
+ DWC_OTG_HC_XFER_NAK,
+ DWC_OTG_HC_XFER_NYET,
+ DWC_OTG_HC_XFER_STALL,
+ DWC_OTG_HC_XFER_XACT_ERR,
+ DWC_OTG_HC_XFER_FRAME_OVERRUN,
+ DWC_OTG_HC_XFER_BABBLE_ERR,
+ DWC_OTG_HC_XFER_DATA_TOGGLE_ERR,
+ DWC_OTG_HC_XFER_AHB_ERR,
+ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
+ DWC_OTG_HC_XFER_URB_DEQUEUE
+} dwc_otg_halt_status_e;
+
+/**
+ * Host channel descriptor. This structure represents the state of a single
+ * host channel when acting in host mode. It contains the data items needed to
+ * transfer packets to an endpoint via a host channel.
+ */
+typedef struct dwc_hc {
+ /** Host channel number used for register address lookup */
+ uint8_t hc_num;
+
+ /** Device to access */
+ unsigned dev_addr:7;
+
+ /** EP to access */
+ unsigned ep_num:4;
+
+ /** EP direction. 0: OUT, 1: IN */
+ unsigned ep_is_in:1;
+
+ /**
+ * EP speed.
+ * One of the following values:
+ * - DWC_OTG_EP_SPEED_LOW
+ * - DWC_OTG_EP_SPEED_FULL
+ * - DWC_OTG_EP_SPEED_HIGH
+ */
+ unsigned speed:2;
+#define DWC_OTG_EP_SPEED_LOW 0
+#define DWC_OTG_EP_SPEED_FULL 1
+#define DWC_OTG_EP_SPEED_HIGH 2
+
+ /**
+ * Endpoint type.
+ * One of the following values:
+ * - DWC_OTG_EP_TYPE_CONTROL: 0
+ * - DWC_OTG_EP_TYPE_ISOC: 1
+ * - DWC_OTG_EP_TYPE_BULK: 2
+ * - DWC_OTG_EP_TYPE_INTR: 3
+ */
+ unsigned ep_type:2;
+
+ /** Max packet size in bytes */
+ unsigned max_packet:11;
+
+ /**
+ * PID for initial transaction.
+ * 0: DATA0,<br>
+ * 1: DATA2,<br>
+ * 2: DATA1,<br>
+ * 3: MDATA (non-Control EP),
+ * SETUP (Control EP)
+ */
+ unsigned data_pid_start:2;
+#define DWC_OTG_HC_PID_DATA0 0
+#define DWC_OTG_HC_PID_DATA2 1
+#define DWC_OTG_HC_PID_DATA1 2
+#define DWC_OTG_HC_PID_MDATA 3
+#define DWC_OTG_HC_PID_SETUP 3
+
+ /** Number of periodic transactions per (micro)frame */
+ unsigned multi_count:2;
+
+ /** @name Transfer State */
+ /** @{ */
+
+ /** Pointer to the current transfer buffer position. */
+ uint8_t *xfer_buff;
+ /**
+ * In Buffer DMA mode this buffer will be used
+ * if xfer_buff is not DWORD aligned.
+ */
+ dwc_dma_t align_buff;
+ /** Total number of bytes to transfer. */
+ uint32_t xfer_len;
+ /** Number of bytes transferred so far. */
+ uint32_t xfer_count;
+ /** Packet count at start of transfer.*/
+ uint16_t start_pkt_count;
+
+ /**
+ * Flag to indicate whether the transfer has been started. Set to 1 if
+ * it has been started, 0 otherwise.
+ */
+ uint8_t xfer_started;
+
+ /**
+ * Set to 1 to indicate that a PING request should be issued on this
+ * channel. If 0, process normally.
+ */
+ uint8_t do_ping;
+
+ /**
+ * Set to 1 to indicate that the error count for this transaction is
+ * non-zero. Set to 0 if the error count is 0.
+ */
+ uint8_t error_state;
+
+ /**
+ * Set to 1 to indicate that this channel should be halted the next
+ * time a request is queued for the channel. This is necessary in
+ * slave mode if no request queue space is available when an attempt
+ * is made to halt the channel.
+ */
+ uint8_t halt_on_queue;
+
+ /**
+ * Set to 1 if the host channel has been halted, but the core is not
+ * finished flushing queued requests. Otherwise 0.
+ */
+ uint8_t halt_pending;
+
+ /**
+ * Reason for halting the host channel.
+ */
+ dwc_otg_halt_status_e halt_status;
+
+ /*
+ * Split settings for the host channel
+ */
+ uint8_t do_split; /**< Enable split for the channel */
+ uint8_t complete_split; /**< Enable complete split */
+ uint8_t hub_addr; /**< Address of high speed hub */
+
+ uint8_t port_addr; /**< Port of the low/full speed device */
+ /** Split transaction position
+ * One of the following values:
+ * - DWC_HCSPLIT_XACTPOS_MID
+ * - DWC_HCSPLIT_XACTPOS_BEGIN
+ * - DWC_HCSPLIT_XACTPOS_END
+ * - DWC_HCSPLIT_XACTPOS_ALL */
+ uint8_t xact_pos;
+
+ /** Set when the host channel does a short read. */
+ uint8_t short_read;
+
+ /**
+ * Number of requests issued for this channel since it was assigned to
+ * the current transfer (not counting PINGs).
+ */
+ uint8_t requests;
+
+ /**
+ * Queue Head for the transfer being processed by this channel.
+ */
+ struct dwc_otg_qh *qh;
+
+ /** @} */
+
+ /** Entry in list of host channels. */
+ DWC_CIRCLEQ_ENTRY(dwc_hc) hc_list_entry;
+
+ /** @name Descriptor DMA support */
+ /** @{ */
+
+ /** Number of Transfer Descriptors */
+ uint16_t ntd;
+
+ /** Descriptor List DMA address */
+ dwc_dma_t desc_list_addr;
+
+ /** Scheduling micro-frame bitmap. */
+ uint8_t schinfo;
+
+ /** @} */
+} dwc_hc_t;
+
+/**
+ * The following parameters may be specified when starting the module. These
+ * parameters define how the DWC_otg controller should be configured.
+ */
+typedef struct dwc_otg_core_params {
+ int32_t opt;
+
+ /**
+ * Specifies the OTG capabilities. The driver will automatically
+ * detect the value for this parameter if none is specified.
+ * 0 - HNP and SRP capable (default)
+ * 1 - SRP Only capable
+ * 2 - No HNP/SRP capable
+ */
+ int32_t otg_cap;
+
+ /**
+ * Specifies whether to use slave or DMA mode for accessing the data
+ * FIFOs. The driver will automatically detect the value for this
+ * parameter if none is specified.
+ * 0 - Slave
+ * 1 - DMA (default, if available)
+ */
+ int32_t dma_enable;
+
+ /**
+ * When DMA mode is enabled specifies whether to use address DMA or DMA
+ * Descriptor mode for accessing the data FIFOs in device mode. The driver
+ * will automatically detect the value for this if none is specified.
+ * 0 - address DMA
+ * 1 - DMA Descriptor(default, if available)
+ */
+ int32_t dma_desc_enable;
+ /** The DMA Burst size (applicable only for External DMA
+ * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32)
+ */
+ int32_t dma_burst_size; /* Translate this to GAHBCFG values */
+
+ /**
+ * Specifies the maximum speed of operation in host and device mode.
+ * The actual speed depends on the speed of the attached device and
+ * the value of phy_type. The actual speed depends on the speed of the
+ * attached device.
+ * 0 - High Speed (default)
+ * 1 - Full Speed
+ */
+ int32_t speed;
+ /** Specifies whether low power mode is supported when attached
+ * to a Full Speed or Low Speed device in host mode.
+ * 0 - Don't support low power mode (default)
+ * 1 - Support low power mode
+ */
+ int32_t host_support_fs_ls_low_power;
+
+ /** Specifies the PHY clock rate in low power mode when connected to a
+ * Low Speed device in host mode. This parameter is applicable only if
+ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
+ * then defaults to 6 MHZ otherwise 48 MHZ.
+ *
+ * 0 - 48 MHz
+ * 1 - 6 MHz
+ */
+ int32_t host_ls_low_power_phy_clk;
+
+ /**
+ * 0 - Use cC FIFO size parameters
+ * 1 - Allow dynamic FIFO sizing (default)
+ */
+ int32_t enable_dynamic_fifo;
+
+ /** Total number of 4-byte words in the data FIFO memory. This
+ * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic
+ * Tx FIFOs.
+ * 32 to 32768 (default 8192)
+ * Note: The total FIFO memory depth in the FPGA configuration is 8192.
+ */
+ int32_t data_fifo_size;
+
+ /** Number of 4-byte words in the Rx FIFO in device mode when dynamic
+ * FIFO sizing is enabled.
+ * 16 to 32768 (default 1064)
+ */
+ int32_t dev_rx_fifo_size;
+
+ /** Number of 4-byte words in the non-periodic Tx FIFO in device mode
+ * when dynamic FIFO sizing is enabled.
+ * 16 to 32768 (default 1024)
+ */
+ int32_t dev_nperio_tx_fifo_size;
+
+ /** Number of 4-byte words in each of the periodic Tx FIFOs in device
+ * mode when dynamic FIFO sizing is enabled.
+ * 4 to 768 (default 256)
+ */
+ uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS];
+
+ /** Number of 4-byte words in the Rx FIFO in host mode when dynamic
+ * FIFO sizing is enabled.
+ * 16 to 32768 (default 1024)
+ */
+ int32_t host_rx_fifo_size;
+
+ /** Number of 4-byte words in the non-periodic Tx FIFO in host mode
+ * when Dynamic FIFO sizing is enabled in the core.
+ * 16 to 32768 (default 1024)
+ */
+ int32_t host_nperio_tx_fifo_size;
+
+ /** Number of 4-byte words in the host periodic Tx FIFO when dynamic
+ * FIFO sizing is enabled.
+ * 16 to 32768 (default 1024)
+ */
+ int32_t host_perio_tx_fifo_size;
+
+ /** The maximum transfer size supported in bytes.
+ * 2047 to 65,535 (default 65,535)
+ */
+ int32_t max_transfer_size;
+
+ /** The maximum number of packets in a transfer.
+ * 15 to 511 (default 511)
+ */
+ int32_t max_packet_count;
+
+ /** The number of host channel registers to use.
+ * 1 to 16 (default 12)
+ * Note: The FPGA configuration supports a maximum of 12 host channels.
+ */
+ int32_t host_channels;
+
+ /** The number of endpoints in addition to EP0 available for device
+ * mode operations.
+ * 1 to 15 (default 6 IN and OUT)
+ * Note: The FPGA configuration supports a maximum of 6 IN and OUT
+ * endpoints in addition to EP0.
+ */
+ int32_t dev_endpoints;
+
+ /**
+ * Specifies the type of PHY interface to use. By default, the driver
+ * will automatically detect the phy_type.
+ *
+ * 0 - Full Speed PHY
+ * 1 - UTMI+ (default)
+ * 2 - ULPI
+ */
+ int32_t phy_type;
+
+ /**
+ * Specifies the UTMI+ Data Width. This parameter is
+ * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI
+ * PHY_TYPE, this parameter indicates the data width between
+ * the MAC and the ULPI Wrapper.) Also, this parameter is
+ * applicable only if the OTG_HSPHY_WIDTH cC parameter was set
+ * to "8 and 16 bits", meaning that the core has been
+ * configured to work at either data path width.
+ *
+ * 8 or 16 bits (default 16)
+ */
+ int32_t phy_utmi_width;
+
+ /**
+ * Specifies whether the ULPI operates at double or single
+ * data rate. This parameter is only applicable if PHY_TYPE is
+ * ULPI.
+ *
+ * 0 - single data rate ULPI interface with 8 bit wide data
+ * bus (default)
+ * 1 - double data rate ULPI interface with 4 bit wide data
+ * bus
+ */
+ int32_t phy_ulpi_ddr;
+
+ /**
+ * Specifies whether to use the internal or external supply to
+ * drive the vbus with a ULPI phy.
+ */
+ int32_t phy_ulpi_ext_vbus;
+
+ /**
+ * Specifies whether to use the I2Cinterface for full speed PHY. This
+ * parameter is only applicable if PHY_TYPE is FS.
+ * 0 - No (default)
+ * 1 - Yes
+ */
+ int32_t i2c_enable;
+
+ int32_t ulpi_fs_ls;
+
+ int32_t ts_dline;
+
+ /**
+ * Specifies whether dedicated transmit FIFOs are
+ * enabled for non periodic IN endpoints in device mode
+ * 0 - No
+ * 1 - Yes
+ */
+ int32_t en_multiple_tx_fifo;
+
+ /** Number of 4-byte words in each of the Tx FIFOs in device
+ * mode when dynamic FIFO sizing is enabled.
+ * 4 to 768 (default 256)
+ */
+ uint32_t dev_tx_fifo_size[MAX_TX_FIFOS];
+
+ /** Thresholding enable flag-
+ * bit 0 - enable non-ISO Tx thresholding
+ * bit 1 - enable ISO Tx thresholding
+ * bit 2 - enable Rx thresholding
+ */
+ uint32_t thr_ctl;
+
+ /** Thresholding length for Tx
+ * FIFOs in 32 bit DWORDs
+ */
+ uint32_t tx_thr_length;
+
+ /** Thresholding length for Rx
+ * FIFOs in 32 bit DWORDs
+ */
+ uint32_t rx_thr_length;
+
+ /**
+ * Specifies whether LPM (Link Power Management) support is enabled
+ */
+ int32_t lpm_enable;
+
+ /** Per Transfer Interrupt
+ * mode enable flag
+ * 1 - Enabled
+ * 0 - Disabled
+ */
+ int32_t pti_enable;
+
+ /** Multi Processor Interrupt
+ * mode enable flag
+ * 1 - Enabled
+ * 0 - Disabled
+ */
+ int32_t mpi_enable;
+
+ /** IS_USB Capability
+ * 1 - Enabled
+ * 0 - Disabled
+ */
+ int32_t ic_usb_cap;
+
+ /** AHB Threshold Ratio
+ * 2'b00 AHB Threshold = MAC Threshold
+ * 2'b01 AHB Threshold = 1/2 MAC Threshold
+ * 2'b10 AHB Threshold = 1/4 MAC Threshold
+ * 2'b11 AHB Threshold = 1/8 MAC Threshold
+ */
+ int32_t ahb_thr_ratio;
+
+ /** ADP Support
+ * 1 - Enabled
+ * 0 - Disabled
+ */
+ int32_t adp_supp_enable;
+
+ /** HFIR Reload Control
+ * 0 - The HFIR cannot be reloaded dynamically.
+ * 1 - Allow dynamic reloading of the HFIR register during runtime.
+ */
+ int32_t reload_ctl;
+
+ /** DCFG: Enable device Out NAK
+ * 0 - The core does not set NAK after Bulk Out transfer complete.
+ * 1 - The core sets NAK after Bulk OUT transfer complete.
+ */
+ int32_t dev_out_nak;
+
+ /** DCFG: Enable Continue on BNA
+ * After receiving BNA interrupt the core disables the endpoint,when the
+ * endpoint is re-enabled by the application the core starts processing
+ * 0 - from the DOEPDMA descriptor
+ * 1 - from the descriptor which received the BNA.
+ */
+ int32_t cont_on_bna;
+
+ /** GAHBCFG: AHB Single Support
+ * This bit when programmed supports SINGLE transfers for remainder
+ * data in a transfer for DMA mode of operation.
+ * 0 - in this case the remainder data will be sent using INCR burst size.
+ * 1 - in this case the remainder data will be sent using SINGLE burst size.
+ */
+ int32_t ahb_single;
+
+ /** Core Power down mode
+ * 0 - No Power Down is enabled
+ * 1 - Reserved
+ * 2 - Complete Power Down (Hibernation)
+ */
+ int32_t power_down;
+
+ /** OTG revision supported
+ * 0 - OTG 1.3 revision
+ * 1 - OTG 2.0 revision
+ */
+ int32_t otg_ver;
+
+} dwc_otg_core_params_t;
+
+#ifdef DEBUG
+struct dwc_otg_core_if;
+typedef struct hc_xfer_info {
+ struct dwc_otg_core_if *core_if;
+ dwc_hc_t *hc;
+} hc_xfer_info_t;
+#endif
+
+typedef struct ep_xfer_info {
+ struct dwc_otg_core_if *core_if;
+ dwc_ep_t *ep;
+ uint8_t state;
+} ep_xfer_info_t;
+/*
+ * Device States
+ */
+typedef enum dwc_otg_lx_state {
+ /** On state */
+ DWC_OTG_L0,
+ /** LPM sleep state*/
+ DWC_OTG_L1,
+ /** USB suspend state*/
+ DWC_OTG_L2,
+ /** Off state*/
+ DWC_OTG_L3
+} dwc_otg_lx_state_e;
+
+struct dwc_otg_global_regs_backup {
+ uint32_t gotgctl_local;
+ uint32_t gintmsk_local;
+ uint32_t gahbcfg_local;
+ uint32_t gusbcfg_local;
+ uint32_t grxfsiz_local;
+ uint32_t gnptxfsiz_local;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ uint32_t glpmcfg_local;
+#endif
+ uint32_t gi2cctl_local;
+ uint32_t hptxfsiz_local;
+ uint32_t pcgcctl_local;
+ uint32_t gdfifocfg_local;
+ uint32_t dtxfsiz_local[MAX_EPS_CHANNELS];
+ uint32_t gpwrdn_local;
+};
+
+struct dwc_otg_host_regs_backup {
+ uint32_t hcfg_local;
+ uint32_t haintmsk_local;
+ uint32_t hcintmsk_local[MAX_EPS_CHANNELS];
+ uint32_t hprt0_local;
+ uint32_t hfir_local;
+};
+
+struct dwc_otg_dev_regs_backup {
+ uint32_t dcfg;
+ uint32_t dctl;
+ uint32_t daintmsk;
+ uint32_t diepmsk;
+ uint32_t doepmsk;
+ uint32_t diepctl[MAX_EPS_CHANNELS];
+ uint32_t dieptsiz[MAX_EPS_CHANNELS];
+ uint32_t diepdma[MAX_EPS_CHANNELS];
+};
+/**
+ * The <code>dwc_otg_core_if</code> structure contains information needed to manage
+ * the DWC_otg controller acting in either host or device mode. It
+ * represents the programming view of the controller as a whole.
+ */
+struct dwc_otg_core_if {
+ /** Parameters that define how the core should be configured.*/
+ dwc_otg_core_params_t *core_params;
+
+ /** Core Global registers starting at offset 000h. */
+ dwc_otg_core_global_regs_t *core_global_regs;
+
+ /** Device-specific information */
+ dwc_otg_dev_if_t *dev_if;
+ /** Host-specific information */
+ dwc_otg_host_if_t *host_if;
+
+ /** Value from SNPSID register */
+ uint32_t snpsid;
+
+ /*
+ * Set to 1 if the core PHY interface bits in USBCFG have been
+ * initialized.
+ */
+ uint8_t phy_init_done;
+
+ /*
+ * SRP Success flag, set by srp success interrupt in FS I2C mode
+ */
+ uint8_t srp_success;
+ uint8_t srp_timer_started;
+ /** Timer for SRP. If it expires before SRP is successful
+ * clear the SRP. */
+ dwc_timer_t *srp_timer;
+
+#ifdef DWC_DEV_SRPCAP
+ /* This timer is needed to power on the hibernated host core if SRP is not
+ * initiated on connected SRP capable device for limited period of time
+ */
+ uint8_t pwron_timer_started;
+ dwc_timer_t *pwron_timer;
+#endif
+ /* Common configuration information */
+ /** Power and Clock Gating Control Register */
+ volatile uint32_t *pcgcctl;
+#define DWC_OTG_PCGCCTL_OFFSET 0xE00
+
+ /** Push/pop addresses for endpoints or host channels.*/
+ uint32_t *data_fifo[MAX_EPS_CHANNELS];
+#define DWC_OTG_DATA_FIFO_OFFSET 0x1000
+#define DWC_OTG_DATA_FIFO_SIZE 0x1000
+
+ /** Total RAM for FIFOs (Bytes) */
+ uint16_t total_fifo_size;
+ /** Size of Rx FIFO (Bytes) */
+ uint16_t rx_fifo_size;
+ /** Size of Non-periodic Tx FIFO (Bytes) */
+ uint16_t nperio_tx_fifo_size;
+
+ /** 1 if DMA is enabled, 0 otherwise. */
+ uint8_t dma_enable;
+
+ /** 1 if DMA descriptor is enabled, 0 otherwise. */
+ uint8_t dma_desc_enable;
+
+ /** 1 if PTI Enhancement mode is enabled, 0 otherwise. */
+ uint8_t pti_enh_enable;
+
+ /** 1 if MPI Enhancement mode is enabled, 0 otherwise. */
+ uint8_t multiproc_int_enable;
+
+ /** 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */
+ uint8_t en_multiple_tx_fifo;
+
+ /** Set to 1 if multiple packets of a high-bandwidth transfer is in
+ * process of being queued */
+ uint8_t queuing_high_bandwidth;
+
+ /** Hardware Configuration -- stored here for convenience.*/
+ hwcfg1_data_t hwcfg1;
+ hwcfg2_data_t hwcfg2;
+ hwcfg3_data_t hwcfg3;
+ hwcfg4_data_t hwcfg4;
+ fifosize_data_t hptxfsiz;
+
+ /** Host and Device Configuration -- stored here for convenience.*/
+ hcfg_data_t hcfg;
+ dcfg_data_t dcfg;
+
+ /** The operational State, during transations
+ * (a_host>>a_peripherial and b_device=>b_host) this may not
+ * match the core but allows the software to determine
+ * transitions.
+ */
+ uint8_t op_state;
+
+ /**
+ * Set to 1 if the HCD needs to be restarted on a session request
+ * interrupt. This is required if no connector ID status change has
+ * occurred since the HCD was last disconnected.
+ */
+ uint8_t restart_hcd_on_session_req;
+
+ /** HCD callbacks */
+ /** A-Device is a_host */
+#define A_HOST (1)
+ /** A-Device is a_suspend */
+#define A_SUSPEND (2)
+ /** A-Device is a_peripherial */
+#define A_PERIPHERAL (3)
+ /** B-Device is operating as a Peripheral. */
+#define B_PERIPHERAL (4)
+ /** B-Device is operating as a Host. */
+#define B_HOST (5)
+
+ /** HCD callbacks */
+ struct dwc_otg_cil_callbacks *hcd_cb;
+ /** PCD callbacks */
+ struct dwc_otg_cil_callbacks *pcd_cb;
+
+ /** Device mode Periodic Tx FIFO Mask */
+ uint32_t p_tx_msk;
+ /** Device mode Periodic Tx FIFO Mask */
+ uint32_t tx_msk;
+
+ /** Workqueue object used for handling several interrupts */
+ dwc_workq_t *wq_otg;
+
+ /** Timer object used for handling "Wakeup Detected" Interrupt */
+ dwc_timer_t *wkp_timer;
+ /** This arrays used for debug purposes for DEV OUT NAK enhancement */
+ uint32_t start_doeptsiz_val[MAX_EPS_CHANNELS];
+ ep_xfer_info_t ep_xfer_info[MAX_EPS_CHANNELS];
+ dwc_timer_t *ep_xfer_timer[MAX_EPS_CHANNELS];
+#ifdef DEBUG
+ uint32_t start_hcchar_val[MAX_EPS_CHANNELS];
+
+ hc_xfer_info_t hc_xfer_info[MAX_EPS_CHANNELS];
+ dwc_timer_t *hc_xfer_timer[MAX_EPS_CHANNELS];
+
+ uint32_t hfnum_7_samples;
+ uint64_t hfnum_7_frrem_accum;
+ uint32_t hfnum_0_samples;
+ uint64_t hfnum_0_frrem_accum;
+ uint32_t hfnum_other_samples;
+ uint64_t hfnum_other_frrem_accum;
+#endif
+
+#ifdef DWC_UTE_CFI
+ uint16_t pwron_rxfsiz;
+ uint16_t pwron_gnptxfsiz;
+ uint16_t pwron_txfsiz[15];
+
+ uint16_t init_rxfsiz;
+ uint16_t init_gnptxfsiz;
+ uint16_t init_txfsiz[15];
+#endif
+
+ /** Lx state of device */
+ dwc_otg_lx_state_e lx_state;
+
+ /** Saved Core Global registers */
+ struct dwc_otg_global_regs_backup *gr_backup;
+ /** Saved Host registers */
+ struct dwc_otg_host_regs_backup *hr_backup;
+ /** Saved Device registers */
+ struct dwc_otg_dev_regs_backup *dr_backup;
+
+ /** Power Down Enable */
+ uint32_t power_down;
+
+ /** ADP support Enable */
+ uint32_t adp_enable;
+
+ /** ADP structure object */
+ dwc_otg_adp_t adp;
+
+ /** hibernation/suspend flag */
+ int hibernation_suspend;
+
+ /** OTG revision supported */
+ uint32_t otg_ver;
+
+ /** OTG status flag used for HNP polling */
+ uint8_t otg_sts;
+
+ /** Pointer to either hcd->lock or pcd->lock */
+ dwc_spinlock_t *lock;
+
+ /** Start predict NextEP based on Learning Queue if equal 1,
+ * also used as counter of disabled NP IN EP's */
+ uint8_t start_predict;
+
+ /** NextEp sequence, including EP0: nextep_seq[] = EP if non-periodic and
+ * active, 0xff otherwise */
+ uint8_t nextep_seq[MAX_EPS_CHANNELS];
+
+ /** Index of fisrt EP in nextep_seq array which should be re-enabled **/
+ uint8_t first_in_nextep_seq;
+
+ /** Frame number while entering to ISR - needed for ISOCs **/
+ uint32_t frame_num;
+
+};
+
+#ifdef DEBUG
+/*
+ * This function is called when transfer is timed out.
+ */
+extern void hc_xfer_timeout(void *ptr);
+#endif
+
+/*
+ * This function is called when transfer is timed out on endpoint.
+ */
+extern void ep_xfer_timeout(void *ptr);
+
+/*
+ * The following functions are functions for works
+ * using during handling some interrupts
+ */
+extern void w_conn_id_status_change(void *p);
+
+extern void w_wakeup_detected(void *p);
+
+/** Saves global register values into system memory. */
+extern int dwc_otg_save_global_regs(dwc_otg_core_if_t * core_if);
+/** Saves device register values into system memory. */
+extern int dwc_otg_save_dev_regs(dwc_otg_core_if_t * core_if);
+/** Saves host register values into system memory. */
+extern int dwc_otg_save_host_regs(dwc_otg_core_if_t * core_if);
+/** Restore global register values. */
+extern int dwc_otg_restore_global_regs(dwc_otg_core_if_t * core_if);
+/** Restore host register values. */
+extern int dwc_otg_restore_host_regs(dwc_otg_core_if_t * core_if, int reset);
+/** Restore device register values. */
+extern int dwc_otg_restore_dev_regs(dwc_otg_core_if_t * core_if,
+ int rem_wakeup);
+extern int restore_lpm_i2c_regs(dwc_otg_core_if_t * core_if);
+extern int restore_essential_regs(dwc_otg_core_if_t * core_if, int rmode,
+ int is_host);
+
+extern int dwc_otg_host_hibernation_restore(dwc_otg_core_if_t * core_if,
+ int restore_mode, int reset);
+extern int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if,
+ int rem_wakeup, int reset);
+
+/*
+ * The following functions support initialization of the CIL driver component
+ * and the DWC_otg controller.
+ */
+extern void dwc_otg_core_host_init(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_core_dev_init(dwc_otg_core_if_t * _core_if);
+
+/** @name Device CIL Functions
+ * The following functions support managing the DWC_otg controller in device
+ * mode.
+ */
+/**@{*/
+extern void dwc_otg_wakeup(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_read_setup_packet(dwc_otg_core_if_t * _core_if,
+ uint32_t * _dest);
+extern uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_ep0_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
+extern void dwc_otg_ep_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
+extern void dwc_otg_ep_deactivate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
+extern void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * _core_if,
+ dwc_ep_t * _ep);
+extern void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * _core_if,
+ dwc_ep_t * _ep);
+extern void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * _core_if,
+ dwc_ep_t * _ep);
+extern void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * _core_if,
+ dwc_ep_t * _ep);
+extern void dwc_otg_ep_write_packet(dwc_otg_core_if_t * _core_if,
+ dwc_ep_t * _ep, int _dma);
+extern void dwc_otg_ep_set_stall(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
+extern void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * _core_if,
+ dwc_ep_t * _ep);
+extern void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * _core_if);
+
+#ifdef DWC_EN_ISOC
+extern void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if,
+ dwc_ep_t * ep);
+extern void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if,
+ dwc_ep_t * ep);
+#endif /* DWC_EN_ISOC */
+/**@}*/
+
+/** @name Host CIL Functions
+ * The following functions support managing the DWC_otg controller in host
+ * mode.
+ */
+/**@{*/
+extern void dwc_otg_hc_init(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc);
+extern void dwc_otg_hc_halt(dwc_otg_core_if_t * _core_if,
+ dwc_hc_t * _hc, dwc_otg_halt_status_e _halt_status);
+extern void dwc_otg_hc_cleanup(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc);
+extern void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * _core_if,
+ dwc_hc_t * _hc);
+extern int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * _core_if,
+ dwc_hc_t * _hc);
+extern void dwc_otg_hc_do_ping(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc);
+extern void dwc_otg_hc_write_packet(dwc_otg_core_if_t * _core_if,
+ dwc_hc_t * _hc);
+extern void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * _core_if);
+
+extern void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if,
+ dwc_hc_t * hc);
+
+extern uint32_t calc_frame_interval(dwc_otg_core_if_t * core_if);
+
+/* Macro used to clear one channel interrupt */
+#define clear_hc_int(_hc_regs_, _intr_) \
+do { \
+ hcint_data_t hcint_clear = {.d32 = 0}; \
+ hcint_clear.b._intr_ = 1; \
+ DWC_WRITE_REG32(&(_hc_regs_)->hcint, hcint_clear.d32); \
+} while (0)
+
+/*
+ * Macro used to disable one channel interrupt. Channel interrupts are
+ * disabled when the channel is halted or released by the interrupt handler.
+ * There is no need to handle further interrupts of that type until the
+ * channel is re-assigned. In fact, subsequent handling may cause crashes
+ * because the channel structures are cleaned up when the channel is released.
+ */
+#define disable_hc_int(_hc_regs_, _intr_) \
+do { \
+ hcintmsk_data_t hcintmsk = {.d32 = 0}; \
+ hcintmsk.b._intr_ = 1; \
+ DWC_MODIFY_REG32(&(_hc_regs_)->hcintmsk, hcintmsk.d32, 0); \
+} while (0)
+
+/**
+ * This function Reads HPRT0 in preparation to modify. It keeps the
+ * WC bits 0 so that if they are read as 1, they won't clear when you
+ * write it back
+ */
+static inline uint32_t dwc_otg_read_hprt0(dwc_otg_core_if_t * _core_if)
+{
+ hprt0_data_t hprt0;
+ hprt0.d32 = DWC_READ_REG32(_core_if->host_if->hprt0);
+ hprt0.b.prtena = 0;
+ hprt0.b.prtconndet = 0;
+ hprt0.b.prtenchng = 0;
+ hprt0.b.prtovrcurrchng = 0;
+ return hprt0.d32;
+}
+
+/**@}*/
+
+/** @name Common CIL Functions
+ * The following functions support managing the DWC_otg controller in either
+ * device or host mode.
+ */
+/**@{*/
+
+extern void dwc_otg_read_packet(dwc_otg_core_if_t * core_if,
+ uint8_t * dest, uint16_t bytes);
+
+extern void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * _core_if, const int _num);
+extern void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_core_reset(dwc_otg_core_if_t * _core_if);
+
+/**
+ * This function returns the Core Interrupt register.
+ */
+static inline uint32_t dwc_otg_read_core_intr(dwc_otg_core_if_t * core_if)
+{
+ return (DWC_READ_REG32(&core_if->core_global_regs->gintsts) &
+ DWC_READ_REG32(&core_if->core_global_regs->gintmsk));
+}
+
+/**
+ * This function returns the OTG Interrupt register.
+ */
+static inline uint32_t dwc_otg_read_otg_intr(dwc_otg_core_if_t * core_if)
+{
+ return (DWC_READ_REG32(&core_if->core_global_regs->gotgint));
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the IN endpoint interrupt bits.
+ */
+static inline uint32_t dwc_otg_read_dev_all_in_ep_intr(dwc_otg_core_if_t *
+ core_if)
+{
+
+ uint32_t v;
+
+ if (core_if->multiproc_int_enable) {
+ v = DWC_READ_REG32(&core_if->dev_if->
+ dev_global_regs->deachint) &
+ DWC_READ_REG32(&core_if->
+ dev_if->dev_global_regs->deachintmsk);
+ } else {
+ v = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daint) &
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk);
+ }
+ return (v & 0xffff);
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the OUT endpoint interrupt bits.
+ */
+static inline uint32_t dwc_otg_read_dev_all_out_ep_intr(dwc_otg_core_if_t *
+ core_if)
+{
+ uint32_t v;
+
+ if (core_if->multiproc_int_enable) {
+ v = DWC_READ_REG32(&core_if->dev_if->
+ dev_global_regs->deachint) &
+ DWC_READ_REG32(&core_if->
+ dev_if->dev_global_regs->deachintmsk);
+ } else {
+ v = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daint) &
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk);
+ }
+
+ return ((v & 0xffff0000) >> 16);
+}
+
+/**
+ * This function returns the Device IN EP Interrupt register
+ */
+static inline uint32_t dwc_otg_read_dev_in_ep_intr(dwc_otg_core_if_t * core_if,
+ dwc_ep_t * ep)
+{
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ uint32_t v, msk, emp;
+
+ if (core_if->multiproc_int_enable) {
+ msk =
+ DWC_READ_REG32(&dev_if->
+ dev_global_regs->diepeachintmsk[ep->num]);
+ emp =
+ DWC_READ_REG32(&dev_if->
+ dev_global_regs->dtknqr4_fifoemptymsk);
+ msk |= ((emp >> ep->num) & 0x1) << 7;
+ v = DWC_READ_REG32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
+ } else {
+ msk = DWC_READ_REG32(&dev_if->dev_global_regs->diepmsk);
+ emp =
+ DWC_READ_REG32(&dev_if->
+ dev_global_regs->dtknqr4_fifoemptymsk);
+ msk |= ((emp >> ep->num) & 0x1) << 7;
+ v = DWC_READ_REG32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
+ }
+
+ return v;
+}
+
+/**
+ * This function returns the Device OUT EP Interrupt register
+ */
+static inline uint32_t dwc_otg_read_dev_out_ep_intr(dwc_otg_core_if_t *
+ _core_if, dwc_ep_t * _ep)
+{
+ dwc_otg_dev_if_t *dev_if = _core_if->dev_if;
+ uint32_t v;
+ doepmsk_data_t msk = {.d32 = 0 };
+
+ if (_core_if->multiproc_int_enable) {
+ msk.d32 =
+ DWC_READ_REG32(&dev_if->
+ dev_global_regs->doepeachintmsk[_ep->num]);
+ if (_core_if->pti_enh_enable) {
+ msk.b.pktdrpsts = 1;
+ }
+ v = DWC_READ_REG32(&dev_if->
+ out_ep_regs[_ep->num]->doepint) & msk.d32;
+ } else {
+ msk.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->doepmsk);
+ if (_core_if->pti_enh_enable) {
+ msk.b.pktdrpsts = 1;
+ }
+ v = DWC_READ_REG32(&dev_if->
+ out_ep_regs[_ep->num]->doepint) & msk.d32;
+ }
+ return v;
+}
+
+/**
+ * This function returns the Host All Channel Interrupt register
+ */
+static inline uint32_t dwc_otg_read_host_all_channels_intr(dwc_otg_core_if_t *
+ _core_if)
+{
+ return (DWC_READ_REG32(&_core_if->host_if->host_global_regs->haint));
+}
+
+static inline uint32_t dwc_otg_read_host_channel_intr(dwc_otg_core_if_t *
+ _core_if, dwc_hc_t * _hc)
+{
+ return (DWC_READ_REG32
+ (&_core_if->host_if->hc_regs[_hc->hc_num]->hcint));
+}
+
+/**
+ * This function returns the mode of the operation, host or device.
+ *
+ * @return 0 - Device Mode, 1 - Host Mode
+ */
+static inline uint32_t dwc_otg_mode(dwc_otg_core_if_t * _core_if)
+{
+ return (DWC_READ_REG32(&_core_if->core_global_regs->gintsts) & 0x1);
+}
+
+/**@}*/
+
+/**
+ * DWC_otg CIL callback structure. This structure allows the HCD and
+ * PCD to register functions used for starting and stopping the PCD
+ * and HCD for role change on for a DRD.
+ */
+typedef struct dwc_otg_cil_callbacks {
+ /** Start function for role change */
+ int (*start) (void *_p);
+ /** Stop Function for role change */
+ int (*stop) (void *_p);
+ /** Disconnect Function for role change */
+ int (*disconnect) (void *_p);
+ /** Resume/Remote wakeup Function */
+ int (*resume_wakeup) (void *_p);
+ /** Suspend function */
+ int (*suspend) (void *_p);
+ /** Session Start (SRP) */
+ int (*session_start) (void *_p);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ /** Sleep (switch to L0 state) */
+ int (*sleep) (void *_p);
+#endif
+ /** Pointer passed to start() and stop() */
+ void *p;
+} dwc_otg_cil_callbacks_t;
+
+extern void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * _core_if,
+ dwc_otg_cil_callbacks_t * _cb,
+ void *_p);
+extern void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * _core_if,
+ dwc_otg_cil_callbacks_t * _cb,
+ void *_p);
+
+void dwc_otg_initiate_srp(dwc_otg_core_if_t * core_if);
+
+//////////////////////////////////////////////////////////////////////
+/** Start the HCD. Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_start(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->start) {
+ core_if->hcd_cb->start(core_if->hcd_cb->p);
+ }
+}
+
+/** Stop the HCD. Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_stop(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->stop) {
+ core_if->hcd_cb->stop(core_if->hcd_cb->p);
+ }
+}
+
+/** Disconnect the HCD. Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_disconnect(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->disconnect) {
+ core_if->hcd_cb->disconnect(core_if->hcd_cb->p);
+ }
+}
+
+/** Inform the HCD the a New Session has begun. Helper function for
+ * using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_session_start(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->session_start) {
+ core_if->hcd_cb->session_start(core_if->hcd_cb->p);
+ }
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/**
+ * Inform the HCD about LPM sleep.
+ * Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_sleep(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->sleep) {
+ core_if->hcd_cb->sleep(core_if->hcd_cb->p);
+ }
+}
+#endif
+
+/** Resume the HCD. Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_resume(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->hcd_cb && core_if->hcd_cb->resume_wakeup) {
+ core_if->hcd_cb->resume_wakeup(core_if->hcd_cb->p);
+ }
+}
+
+/** Start the PCD. Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_pcd_start(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->start) {
+ core_if->pcd_cb->start(core_if->pcd_cb->p);
+ }
+}
+
+/** Stop the PCD. Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_pcd_stop(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->stop) {
+ core_if->pcd_cb->stop(core_if->pcd_cb->p);
+ }
+}
+
+/** Suspend the PCD. Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_pcd_suspend(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->suspend) {
+ core_if->pcd_cb->suspend(core_if->pcd_cb->p);
+ }
+}
+
+/** Resume the PCD. Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_pcd_resume(dwc_otg_core_if_t * core_if)
+{
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/drivers/usb/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/dwc_otg/dwc_otg_cil_intr.c
new file mode 100644
index 0000000..0581b49
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_cil_intr.c
@@ -0,0 +1,1393 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $
+ * $Revision: #31 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871286 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ *
+ * The Core Interface Layer provides basic services for accessing and
+ * managing the DWC_otg hardware. These services are used by both the
+ * Host Controller Driver and the Peripheral Controller Driver.
+ *
+ * This file contains the Common Interrupt handlers.
+ */
+#include "dwc_os.h"
+#include "dwc_otg_regs.h"
+#include "dwc_otg_cil.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_pcd.h"
+#include "dwc_otg_hcd.h"
+
+#ifdef DEBUG
+inline const char *op_state_str(dwc_otg_core_if_t * core_if)
+{
+ return (core_if->op_state == A_HOST ? "a_host" :
+ (core_if->op_state == A_SUSPEND ? "a_suspend" :
+ (core_if->op_state == A_PERIPHERAL ? "a_peripheral" :
+ (core_if->op_state == B_PERIPHERAL ? "b_peripheral" :
+ (core_if->op_state == B_HOST ? "b_host" : "unknown")))));
+}
+#endif
+
+/** This function will log a debug message
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_mode_mismatch_intr(dwc_otg_core_if_t * core_if)
+{
+ gintsts_data_t gintsts;
+ DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n",
+ dwc_otg_mode(core_if) ? "Host" : "Device");
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.modemismatch = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+ return 1;
+}
+
+/**
+ * This function handles the OTG Interrupts. It reads the OTG
+ * Interrupt Register (GOTGINT) to determine what interrupt has
+ * occurred.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ gotgint_data_t gotgint;
+ gotgctl_data_t gotgctl;
+ gintmsk_data_t gintmsk;
+ gpwrdn_data_t gpwrdn;
+
+ gotgint.d32 = DWC_READ_REG32(&global_regs->gotgint);
+ gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
+ DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32,
+ op_state_str(core_if));
+
+ if (gotgint.b.sesenddet) {
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+ "Session End Detected++ (%s)\n",
+ op_state_str(core_if));
+ gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
+
+ if (core_if->op_state == B_HOST) {
+ cil_pcd_start(core_if);
+ core_if->op_state = B_PERIPHERAL;
+ } else {
+ /* If not B_HOST and Device HNP still set. HNP
+ * Did not succeed!*/
+ if (gotgctl.b.devhnpen) {
+ DWC_DEBUGPL(DBG_ANY, "Session End Detected\n");
+ __DWC_ERROR("Device Not Connected/Responding!\n");
+ }
+
+ /* If Session End Detected the B-Cable has
+ * been disconnected. */
+ /* Reset PCD and Gadget driver to a
+ * clean state. */
+ core_if->lx_state = DWC_OTG_L0;
+ DWC_SPINUNLOCK(core_if->lock);
+ cil_pcd_stop(core_if);
+ DWC_SPINLOCK(core_if->lock);
+
+ if (core_if->adp_enable) {
+ if (core_if->power_down == 2) {
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->
+ core_global_regs->
+ gpwrdn, gpwrdn.d32, 0);
+ }
+
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+
+ dwc_otg_adp_sense_start(core_if);
+ }
+ }
+
+ gotgctl.d32 = 0;
+ gotgctl.b.devhnpen = 1;
+ DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
+ }
+ if (gotgint.b.sesreqsucstschng) {
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+ "Session Reqeust Success Status Change++\n");
+ gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
+ if (gotgctl.b.sesreqscs) {
+
+ if ((core_if->core_params->phy_type ==
+ DWC_PHY_TYPE_PARAM_FS) && (core_if->core_params->i2c_enable)) {
+ core_if->srp_success = 1;
+ } else {
+ DWC_SPINUNLOCK(core_if->lock);
+ cil_pcd_resume(core_if);
+ DWC_SPINLOCK(core_if->lock);
+ /* Clear Session Request */
+ gotgctl.d32 = 0;
+ gotgctl.b.sesreq = 1;
+ DWC_MODIFY_REG32(&global_regs->gotgctl,
+ gotgctl.d32, 0);
+ }
+ }
+ }
+ if (gotgint.b.hstnegsucstschng) {
+ /* Print statements during the HNP interrupt handling
+ * can cause it to fail.*/
+ gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
+ if (gotgctl.b.hstnegscs) {
+ if (dwc_otg_is_host_mode(core_if)) {
+ core_if->op_state = B_HOST;
+ /*
+ * Need to disable SOF interrupt immediately.
+ * When switching from device to host, the PCD
+ * interrupt handler won't handle the
+ * interrupt if host mode is already set. The
+ * HCD interrupt handler won't get called if
+ * the HCD state is HALT. This means that the
+ * interrupt does not get handled and Linux
+ * complains loudly.
+ */
+ gintmsk.d32 = 0;
+ gintmsk.b.sofintr = 1;
+ DWC_MODIFY_REG32(&global_regs->gintmsk,
+ gintmsk.d32, 0);
+ /* Call callback function with spin lock released */
+ DWC_SPINUNLOCK(core_if->lock);
+ cil_pcd_stop(core_if);
+ /*
+ * Initialize the Core for Host mode.
+ */
+ cil_hcd_start(core_if);
+ DWC_SPINLOCK(core_if->lock);
+ core_if->op_state = B_HOST;
+ }
+ } else {
+ gotgctl.d32 = 0;
+ gotgctl.b.hnpreq = 1;
+ gotgctl.b.devhnpen = 1;
+ DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
+ DWC_DEBUGPL(DBG_ANY, "HNP Failed\n");
+ __DWC_ERROR("Device Not Connected/Responding\n");
+ }
+ }
+ if (gotgint.b.hstnegdet) {
+ /* The disconnect interrupt is set at the same time as
+ * Host Negotiation Detected. During the mode
+ * switch all interrupts are cleared so the disconnect
+ * interrupt handler will not get executed.
+ */
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+ "Host Negotiation Detected++ (%s)\n",
+ (dwc_otg_is_host_mode(core_if) ? "Host" :
+ "Device"));
+ if (dwc_otg_is_device_mode(core_if)) {
+ DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n",
+ core_if->op_state);
+ DWC_SPINUNLOCK(core_if->lock);
+ cil_hcd_disconnect(core_if);
+ cil_pcd_start(core_if);
+ DWC_SPINLOCK(core_if->lock);
+ core_if->op_state = A_PERIPHERAL;
+ } else {
+ /*
+ * Need to disable SOF interrupt immediately. When
+ * switching from device to host, the PCD interrupt
+ * handler won't handle the interrupt if host mode is
+ * already set. The HCD interrupt handler won't get
+ * called if the HCD state is HALT. This means that
+ * the interrupt does not get handled and Linux
+ * complains loudly.
+ */
+ gintmsk.d32 = 0;
+ gintmsk.b.sofintr = 1;
+ DWC_MODIFY_REG32(&global_regs->gintmsk, gintmsk.d32, 0);
+ DWC_SPINUNLOCK(core_if->lock);
+ cil_pcd_stop(core_if);
+ cil_hcd_start(core_if);
+ DWC_SPINLOCK(core_if->lock);
+ core_if->op_state = A_HOST;
+ }
+ }
+ if (gotgint.b.adevtoutchng) {
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+ "A-Device Timeout Change++\n");
+ }
+ if (gotgint.b.debdone) {
+ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Debounce Done++\n");
+ }
+
+ /* Clear GOTGINT */
+ DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, gotgint.d32);
+
+ return 1;
+}
+
+void w_conn_id_status_change(void *p)
+{
+ dwc_otg_core_if_t *core_if = p;
+ uint32_t count = 0;
+ gotgctl_data_t gotgctl = {.d32 = 0 };
+
+ gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+ DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32);
+ DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts);
+
+ /* B-Device connector (Device Mode) */
+ if (gotgctl.b.conidsts) {
+ /* Wait for switch to device mode. */
+ while (!dwc_otg_is_device_mode(core_if)) {
+ DWC_PRINTF("Waiting for Peripheral Mode, Mode=%s\n",
+ (dwc_otg_is_host_mode(core_if) ? "Host" :
+ "Peripheral"));
+ dwc_mdelay(100);
+ if (++count > 10000)
+ break;
+ }
+ DWC_ASSERT(++count < 10000,
+ "Connection id status change timed out");
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+ } else {
+ /* A-Device connector (Host Mode) */
+ while (!dwc_otg_is_host_mode(core_if)) {
+ DWC_PRINTF("Waiting for Host Mode, Mode=%s\n",
+ (dwc_otg_is_host_mode(core_if) ? "Host" :
+ "Peripheral"));
+ dwc_mdelay(100);
+ if (++count > 10000)
+ break;
+ }
+ DWC_ASSERT(++count < 10000,
+ "Connection id status change timed out");
+ core_if->op_state = A_HOST;
+ /*
+ * Initialize the Core for Host mode.
+ */
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_hcd_start(core_if);
+ }
+}
+
+/**
+ * This function handles the Connector ID Status Change Interrupt. It
+ * reads the OTG Interrupt Register (GOTCTL) to determine whether this
+ * is a Device to Host Mode transition or a Host Mode to Device
+ * Transition.
+ *
+ * This only occurs when the cable is connected/removed from the PHY
+ * connector.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t * core_if)
+{
+
+ /*
+ * Need to disable SOF interrupt immediately. If switching from device
+ * to host, the PCD interrupt handler won't handle the interrupt if
+ * host mode is already set. The HCD interrupt handler won't get
+ * called if the HCD state is HALT. This means that the interrupt does
+ * not get handled and Linux complains loudly.
+ */
+ gintmsk_data_t gintmsk = {.d32 = 0 };
+ gintsts_data_t gintsts = {.d32 = 0 };
+
+ gintmsk.b.sofintr = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
+
+ DWC_DEBUGPL(DBG_CIL,
+ " ++Connector ID Status Change Interrupt++ (%s)\n",
+ (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"));
+
+ if (core_if->lock)
+ DWC_SPINUNLOCK(core_if->lock);
+
+ /*
+ * Need to schedule a work, as there are possible DELAY function calls
+ * Release lock before scheduling workq as it holds spinlock during scheduling
+ */
+
+ DWC_WORKQ_SCHEDULE(core_if->wq_otg, w_conn_id_status_change,
+ core_if, "connection id status change");
+ if (core_if->lock)
+ DWC_SPINLOCK(core_if->lock);
+
+ /* Set flag and clear interrupt */
+ gintsts.b.conidstschng = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that a device is initiating the Session
+ * Request Protocol to request the host to turn on bus power so a new
+ * session can begin. The handler responds by turning on bus power. If
+ * the DWC_otg controller is in low power mode, the handler brings the
+ * controller out of low power mode before turning on bus power.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t * core_if)
+{
+ gintsts_data_t gintsts;
+
+#ifndef DWC_HOST_ONLY
+ DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n");
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ DWC_PRINTF("SRP: Device mode\n");
+ } else {
+ hprt0_data_t hprt0;
+ DWC_PRINTF("SRP: Host mode\n");
+
+ /* Turn on the port power bit. */
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtpwr = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+ /* Start the Connection timer. So a message can be displayed
+ * if connect does not occur within 10 seconds. */
+ cil_hcd_session_start(core_if);
+ }
+#endif
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.sessreqintr = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+void w_wakeup_detected(void *p)
+{
+ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) p;
+ /*
+ * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
+ * so that OPT tests pass with all PHYs).
+ */
+ hprt0_data_t hprt0 = {.d32 = 0 };
+#if 0
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ /* Restart the Phy Clock */
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+ dwc_udelay(10);
+#endif //0
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ DWC_DEBUGPL(DBG_ANY, "Resume: HPRT0=%0x\n", hprt0.d32);
+// dwc_mdelay(70);
+ hprt0.b.prtres = 0; /* Resume */
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ DWC_DEBUGPL(DBG_ANY, "Clear Resume: HPRT0=%0x\n",
+ DWC_READ_REG32(core_if->host_if->hprt0));
+
+ cil_hcd_resume(core_if);
+
+ /** Change to L0 state*/
+ core_if->lx_state = DWC_OTG_L0;
+}
+
+/**
+ * This interrupt indicates that the DWC_otg controller has detected a
+ * resume or remote wakeup sequence. If the DWC_otg controller is in
+ * low power mode, the handler must brings the controller out of low
+ * power mode. The controller automatically begins resume
+ * signaling. The handler schedules a time to stop resume signaling.
+ */
+int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t * core_if)
+{
+ gintsts_data_t gintsts;
+
+ DWC_DEBUGPL(DBG_ANY,
+ "++Resume and Remote Wakeup Detected Interrupt++\n");
+
+ DWC_PRINTF("%s lxstate = %d\n", __func__, core_if->lx_state);
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ dctl_data_t dctl = {.d32 = 0 };
+ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n",
+ DWC_READ_REG32(&core_if->dev_if->
+ dev_global_regs->dsts));
+ if (core_if->lx_state == DWC_OTG_L2) {
+#ifdef PARTIAL_POWER_DOWN
+ if (core_if->hwcfg4.b.power_optimiz) {
+ pcgcctl_data_t power = {.d32 = 0 };
+
+ power.d32 = DWC_READ_REG32(core_if->pcgcctl);
+ DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n",
+ power.d32);
+
+ power.b.stoppclk = 0;
+ DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
+
+ power.b.pwrclmp = 0;
+ DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
+
+ power.b.rstpdwnmodule = 0;
+ DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
+ }
+#endif
+ /* Clear the Remote Wakeup Signaling */
+ dctl.b.rmtwkupsig = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->
+ dev_global_regs->dctl, dctl.d32, 0);
+
+ DWC_SPINUNLOCK(core_if->lock);
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+ }
+ DWC_SPINLOCK(core_if->lock);
+ } else {
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ lpmcfg.b.hird_thres &= (~(1 << 4));
+ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg,
+ lpmcfg.d32);
+ }
+ /** Change to L0 state*/
+ core_if->lx_state = DWC_OTG_L0;
+ } else {
+ if (core_if->lx_state != DWC_OTG_L1) {
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+
+ /* Restart the Phy Clock */
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+ DWC_TIMER_SCHEDULE(core_if->wkp_timer, 71);
+ } else {
+ /** Change to L0 state*/
+ core_if->lx_state = DWC_OTG_L0;
+ }
+ }
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.wkupintr = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that the Wakeup Logic has detected a
+ * Device disconnect.
+ */
+static int32_t dwc_otg_handle_pwrdn_disconnect_intr(dwc_otg_core_if_t *core_if)
+{
+ gpwrdn_data_t gpwrdn = { .d32 = 0 };
+ gpwrdn_data_t gpwrdn_temp = { .d32 = 0 };
+ gpwrdn_temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+
+ DWC_PRINTF("%s called\n", __FUNCTION__);
+
+ if (!core_if->hibernation_suspend) {
+ DWC_PRINTF("Already exited from Hibernation\n");
+ return 1;
+ }
+
+ /* Switch on the voltage to the core */
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Reset the core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Disable power clamps*/
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Remove reset the core signal */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ core_if->hibernation_suspend = 0;
+
+ /* Disable PMU */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ if (gpwrdn_temp.b.idsts) {
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+ } else {
+ core_if->op_state = A_HOST;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_hcd_start(core_if);
+ }
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that the Wakeup Logic has detected a
+ * remote wakeup sequence.
+ */
+static int32_t dwc_otg_handle_pwrdn_wakeup_detected_intr(dwc_otg_core_if_t * core_if)
+{
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ DWC_DEBUGPL(DBG_ANY,
+ "++Powerdown Remote Wakeup Detected Interrupt++\n");
+
+ if (!core_if->hibernation_suspend) {
+ DWC_PRINTF("Already exited from Hibernation\n");
+ return 1;
+ }
+
+ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ if (gpwrdn.b.idsts) { // Device Mode
+ if ((core_if->power_down == 2)
+ && (core_if->hibernation_suspend == 1)) {
+ dwc_otg_device_hibernation_restore(core_if, 0, 0);
+ }
+ } else {
+ if ((core_if->power_down == 2)
+ && (core_if->hibernation_suspend == 1)) {
+ dwc_otg_host_hibernation_restore(core_if, 1, 0);
+ }
+ }
+ return 1;
+}
+
+static int32_t dwc_otg_handle_pwrdn_idsts_change(dwc_otg_device_t *otg_dev)
+{
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ gpwrdn_data_t gpwrdn_temp = {.d32 = 0 };
+ dwc_otg_core_if_t *core_if = otg_dev->core_if;
+
+ DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__);
+ gpwrdn_temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ if (core_if->power_down == 2)
+ {
+ if (!core_if->hibernation_suspend) {
+ DWC_PRINTF("Already exited from Hibernation\n");
+ return 1;
+ }
+ DWC_DEBUGPL(DBG_ANY, "Exit from hibernation on ID sts change\n");
+ /* Switch on the voltage to the core */
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Reset the core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Disable power clamps */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Remove reset the core signal */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /*Indicates that we are exiting from hibernation */
+ core_if->hibernation_suspend = 0;
+
+ /* Disable PMU */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ gpwrdn.d32 = core_if->gr_backup->gpwrdn_local;
+ if (gpwrdn.b.dis_vbus == 1) {
+ gpwrdn.d32 = 0;
+ gpwrdn.b.dis_vbus = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ }
+
+ if (gpwrdn_temp.b.idsts) {
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+ } else {
+ core_if->op_state = A_HOST;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_hcd_start(core_if);
+ }
+ }
+
+ if (core_if->adp_enable)
+ {
+ uint8_t is_host = 0;
+ DWC_SPINUNLOCK(core_if->lock);
+ /* Change the core_if's lock to hcd/pcd lock depend on mode? */
+#ifndef DWC_HOST_ONLY
+ if (gpwrdn_temp.b.idsts)
+ core_if->lock = otg_dev->pcd->lock;
+#endif
+#ifndef DWC_DEVICE_ONLY
+ if (!gpwrdn_temp.b.idsts) {
+ core_if->lock = otg_dev->hcd->lock;
+ is_host = 1;
+ }
+#endif
+ DWC_PRINTF("RESTART ADP\n");
+ if (core_if->adp.probe_enabled)
+ dwc_otg_adp_probe_stop(core_if);
+ if (core_if->adp.sense_enabled)
+ dwc_otg_adp_sense_stop(core_if);
+ if (core_if->adp.sense_timer_started)
+ DWC_TIMER_CANCEL(core_if->adp.sense_timer);
+ if (core_if->adp.vbuson_timer_started)
+ DWC_TIMER_CANCEL(core_if->adp.vbuson_timer);
+ core_if->adp.probe_timer_values[0] = -1;
+ core_if->adp.probe_timer_values[1] = -1;
+ core_if->adp.sense_timer_started = 0;
+ core_if->adp.vbuson_timer_started = 0;
+ core_if->adp.probe_counter = 0;
+ core_if->adp.gpwrdn = 0;
+
+ /* Disable PMU and restart ADP */
+ gpwrdn_temp.d32 = 0;
+ gpwrdn_temp.b.pmuactv = 1;
+ gpwrdn_temp.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ DWC_PRINTF("Check point 1\n");
+ dwc_mdelay(110);
+ dwc_otg_adp_start(core_if, is_host);
+ DWC_SPINLOCK(core_if->lock);
+ }
+
+
+ return 1;
+}
+
+static int32_t dwc_otg_handle_pwrdn_session_change(dwc_otg_core_if_t * core_if)
+{
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ int32_t otg_cap_param = core_if->core_params->otg_cap;
+ DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__);
+
+ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ if (core_if->power_down == 2) {
+ if (!core_if->hibernation_suspend) {
+ DWC_PRINTF("Already exited from Hibernation\n");
+ return 1;
+ }
+
+ if ((otg_cap_param != DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ||
+ otg_cap_param != DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE) &&
+ gpwrdn.b.bsessvld == 0) {
+ /* Save gpwrdn register for further usage if stschng interrupt */
+ core_if->gr_backup->gpwrdn_local =
+ DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ /*Exit from ISR and wait for stschng interrupt with bsessvld = 1 */
+ return 1;
+ }
+
+ /* Switch on the voltage to the core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Reset the core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Disable power clamps */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Remove reset the core signal */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /*Indicates that we are exiting from hibernation */
+ core_if->hibernation_suspend = 0;
+
+ /* Disable PMU */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+
+ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ||
+ otg_cap_param == DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE) {
+ /*
+ * Initiate SRP after initial ADP probe.
+ */
+ dwc_otg_initiate_srp(core_if);
+ }
+ }
+
+ return 1;
+}
+/**
+ * This interrupt indicates that the Wakeup Logic has detected a
+ * status change either on IDDIG or BSessVld.
+ */
+static uint32_t dwc_otg_handle_pwrdn_stschng_intr(dwc_otg_device_t *otg_dev)
+{
+ int retval;
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ gpwrdn_data_t gpwrdn_temp = {.d32 = 0 };
+ dwc_otg_core_if_t *core_if = otg_dev->core_if;
+
+ DWC_PRINTF("%s called\n", __FUNCTION__);
+
+ if (core_if->power_down == 2) {
+ if (core_if->hibernation_suspend <= 0) {
+ DWC_PRINTF("Already exited from Hibernation\n");
+ return 1;
+ } else
+ gpwrdn_temp.d32 = core_if->gr_backup->gpwrdn_local;
+
+ } else {
+ gpwrdn_temp.d32 = core_if->adp.gpwrdn;
+ }
+
+ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+
+ if (gpwrdn.b.idsts ^ gpwrdn_temp.b.idsts) {
+ retval = dwc_otg_handle_pwrdn_idsts_change(otg_dev);
+ } else if (gpwrdn.b.bsessvld ^ gpwrdn_temp.b.bsessvld) {
+ retval = dwc_otg_handle_pwrdn_session_change(core_if);
+ }
+
+ return retval;
+}
+
+/**
+ * This interrupt indicates that the Wakeup Logic has detected a
+ * SRP.
+ */
+static int32_t dwc_otg_handle_pwrdn_srp_intr(dwc_otg_core_if_t * core_if)
+{
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+
+ DWC_PRINTF("%s called\n", __FUNCTION__);
+
+ if (!core_if->hibernation_suspend) {
+ DWC_PRINTF("Already exited from Hibernation\n");
+ return 1;
+ }
+#ifdef DWC_DEV_SRPCAP
+ if (core_if->pwron_timer_started) {
+ core_if->pwron_timer_started = 0;
+ DWC_TIMER_CANCEL(core_if->pwron_timer);
+ }
+#endif
+
+ /* Switch on the voltage to the core */
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Reset the core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Disable power clamps */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Remove reset the core signal */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Indicates that we are exiting from hibernation */
+ core_if->hibernation_suspend = 0;
+
+ /* Disable PMU */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Programm Disable VBUS to 0 */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.dis_vbus = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /*Initialize the core as Host */
+ core_if->op_state = A_HOST;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_hcd_start(core_if);
+
+ return 1;
+}
+
+/** This interrupt indicates that restore command after Hibernation
+ * was completed by the core. */
+int32_t dwc_otg_handle_restore_done_intr(dwc_otg_core_if_t * core_if)
+{
+ pcgcctl_data_t pcgcctl;
+ DWC_DEBUGPL(DBG_ANY, "++Restore Done Interrupt++\n");
+
+ //TODO De-assert restore signal. 8.a
+ pcgcctl.d32 = DWC_READ_REG32(core_if->pcgcctl);
+ if (pcgcctl.b.restoremode == 1) {
+ gintmsk_data_t gintmsk = {.d32 = 0 };
+ /*
+ * If restore mode is Remote Wakeup,
+ * unmask Remote Wakeup interrupt.
+ */
+ gintmsk.b.wkupintr = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
+ 0, gintmsk.d32);
+ }
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that a device has been disconnected from
+ * the root port.
+ */
+int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t * core_if)
+{
+ gintsts_data_t gintsts;
+
+ DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n",
+ (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"),
+ op_state_str(core_if));
+
+/** @todo Consolidate this if statement. */
+#ifndef DWC_HOST_ONLY
+ if (core_if->op_state == B_HOST) {
+ /* If in device mode Disconnect and stop the HCD, then
+ * start the PCD. */
+ DWC_SPINUNLOCK(core_if->lock);
+ cil_hcd_disconnect(core_if);
+ cil_pcd_start(core_if);
+ DWC_SPINLOCK(core_if->lock);
+ core_if->op_state = B_PERIPHERAL;
+ } else if (dwc_otg_is_device_mode(core_if)) {
+ gotgctl_data_t gotgctl = {.d32 = 0 };
+ gotgctl.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+ if (gotgctl.b.hstsethnpen == 1) {
+ /* Do nothing, if HNP in process the OTG
+ * interrupt "Host Negotiation Detected"
+ * interrupt will do the mode switch.
+ */
+ } else if (gotgctl.b.devhnpen == 0) {
+ /* If in device mode Disconnect and stop the HCD, then
+ * start the PCD. */
+ DWC_SPINUNLOCK(core_if->lock);
+ cil_hcd_disconnect(core_if);
+ cil_pcd_start(core_if);
+ DWC_SPINLOCK(core_if->lock);
+ core_if->op_state = B_PERIPHERAL;
+ } else {
+ DWC_DEBUGPL(DBG_ANY, "!a_peripheral && !devhnpen\n");
+ }
+ } else {
+ if (core_if->op_state == A_HOST) {
+ /* A-Cable still connected but device disconnected. */
+ cil_hcd_disconnect(core_if);
+ if (core_if->adp_enable) {
+ gpwrdn_data_t gpwrdn = { .d32 = 0 };
+ cil_hcd_stop(core_if);
+ /* Enable Power Down Logic */
+ gpwrdn.b.pmuintsel = 1;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_otg_adp_probe_start(core_if);
+
+ /* Power off the core */
+ if (core_if->power_down == 2) {
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->
+ core_global_regs->
+ gpwrdn, gpwrdn.d32, 0);
+ }
+ }
+ }
+ }
+#endif
+ /* Change to L3(OFF) state */
+ core_if->lx_state = DWC_OTG_L3;
+
+ gintsts.d32 = 0;
+ gintsts.b.disconnect = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+ return 1;
+}
+
+/**
+ * This interrupt indicates that SUSPEND state has been detected on
+ * the USB.
+ *
+ * For HNP the USB Suspend interrupt signals the change from
+ * "a_peripheral" to "a_host".
+ *
+ * When power management is enabled the core will be put in low power
+ * mode.
+ */
+int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t * core_if)
+{
+ dsts_data_t dsts;
+ gintsts_data_t gintsts;
+ dcfg_data_t dcfg;
+
+ DWC_DEBUGPL(DBG_ANY, "USB SUSPEND\n");
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ /* Check the Device status register to determine if the Suspend
+ * state is active. */
+ dsts.d32 =
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32);
+ DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d "
+ "HWCFG4.power Optimize=%d\n",
+ dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz);
+
+#ifdef PARTIAL_POWER_DOWN
+/** @todo Add a module parameter for power management. */
+
+ if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) {
+ pcgcctl_data_t power = {.d32 = 0 };
+ DWC_DEBUGPL(DBG_CIL, "suspend\n");
+
+ power.b.pwrclmp = 1;
+ DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
+
+ power.b.rstpdwnmodule = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32);
+
+ power.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32);
+
+ } else {
+ DWC_DEBUGPL(DBG_ANY, "disconnect?\n");
+ }
+#endif
+ /* PCD callback for suspend. Release the lock inside of callback function */
+ cil_pcd_suspend(core_if);
+ if (core_if->power_down == 2)
+ {
+ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+ DWC_DEBUGPL(DBG_ANY,"lx_state = %08x\n",core_if->lx_state);
+ DWC_DEBUGPL(DBG_ANY," device address = %08d\n",dcfg.b.devaddr);
+
+ if (core_if->lx_state != DWC_OTG_L3 && dcfg.b.devaddr) {
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ gusbcfg_data_t gusbcfg = {.d32 = 0 };
+
+ /* Change to L2(suspend) state */
+ core_if->lx_state = DWC_OTG_L2;
+
+ /* Clear interrupt in gintsts */
+ gintsts.d32 = 0;
+ gintsts.b.usbsuspend = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->
+ gintsts, gintsts.d32);
+ DWC_PRINTF("Start of hibernation completed\n");
+ dwc_otg_save_global_regs(core_if);
+ dwc_otg_save_dev_regs(core_if);
+
+ gusbcfg.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->
+ gusbcfg);
+ if (gusbcfg.b.ulpi_utmi_sel == 1) {
+ /* ULPI interface */
+ /* Suspend the Phy Clock */
+ pcgcctl.d32 = 0;
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, 0,
+ pcgcctl.d32);
+ dwc_udelay(10);
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->
+ core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ } else {
+ /* UTMI+ Interface */
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->
+ core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, 0,
+ pcgcctl.d32);
+ dwc_udelay(10);
+ }
+
+ /* Set flag to indicate that we are in hibernation */
+ core_if->hibernation_suspend = 1;
+ /* Enable interrupts from wake up logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Unmask device mode interrupts in GPWRDN */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.rst_det_msk = 1;
+ gpwrdn.b.lnstchng_msk = 1;
+ gpwrdn.b.sts_chngint_msk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Enable Power Down Clamp */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Switch off VDD */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+
+ /* Save gpwrdn register for further usage if stschng interrupt */
+ core_if->gr_backup->gpwrdn_local =
+ DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ DWC_PRINTF("Hibernation completed\n");
+
+ return 1;
+ }
+ }
+ } else {
+ if (core_if->op_state == A_PERIPHERAL) {
+ DWC_DEBUGPL(DBG_ANY, "a_peripheral->a_host\n");
+ /* Clear the a_peripheral flag, back to a_host. */
+ DWC_SPINUNLOCK(core_if->lock);
+ cil_pcd_stop(core_if);
+ cil_hcd_start(core_if);
+ DWC_SPINLOCK(core_if->lock);
+ core_if->op_state = A_HOST;
+ }
+ }
+
+ /* Change to L2(suspend) state */
+ core_if->lx_state = DWC_OTG_L2;
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.usbsuspend = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/**
+ * This function hadles LPM transaction received interrupt.
+ */
+static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if)
+{
+ glpmcfg_data_t lpmcfg;
+ gintsts_data_t gintsts;
+
+ if (!core_if->core_params->lpm_enable) {
+ DWC_PRINTF("Unexpected LPM interrupt\n");
+ }
+
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ DWC_PRINTF("LPM config register = 0x%08x\n", lpmcfg.d32);
+
+ if (dwc_otg_is_host_mode(core_if)) {
+ cil_hcd_sleep(core_if);
+ } else {
+ lpmcfg.b.hird_thres |= (1 << 4);
+ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg,
+ lpmcfg.d32);
+ }
+
+ /* Examine prt_sleep_sts after TL1TokenTetry period max (10 us) */
+ dwc_udelay(10);
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ if (lpmcfg.b.prt_sleep_sts) {
+ /* Save the current state */
+ core_if->lx_state = DWC_OTG_L1;
+ }
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.lpmtranrcvd = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+ return 1;
+}
+#endif /* CONFIG_USB_DWC_OTG_LPM */
+
+/**
+ * This function returns the Core Interrupt register.
+ */
+static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if)
+{
+ gahbcfg_data_t gahbcfg = {.d32 = 0 };
+ gintsts_data_t gintsts;
+ gintmsk_data_t gintmsk;
+ gintmsk_data_t gintmsk_common = {.d32 = 0 };
+ gintmsk_common.b.wkupintr = 1;
+ gintmsk_common.b.sessreqintr = 1;
+ gintmsk_common.b.conidstschng = 1;
+ gintmsk_common.b.otgintr = 1;
+ gintmsk_common.b.modemismatch = 1;
+ gintmsk_common.b.disconnect = 1;
+ gintmsk_common.b.usbsuspend = 1;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ gintmsk_common.b.lpmtranrcvd = 1;
+#endif
+ gintmsk_common.b.restoredone = 1;
+ /** @todo: The port interrupt occurs while in device
+ * mode. Added code to CIL to clear the interrupt for now!
+ */
+ gintmsk_common.b.portintr = 1;
+
+ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+ gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+ gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
+
+#ifdef DEBUG
+ /* if any common interrupts set */
+ if (gintsts.d32 & gintmsk_common.d32) {
+ DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n",
+ gintsts.d32, gintmsk.d32);
+ }
+#endif
+ if (gahbcfg.b.glblintrmsk)
+ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
+ else
+ return 0;
+
+}
+
+/* MACRO for clearing interupt bits in GPWRDN register */
+#define CLEAR_GPWRDN_INTR(__core_if,__intr) \
+do { \
+ gpwrdn_data_t gpwrdn = {.d32=0}; \
+ gpwrdn.b.__intr = 1; \
+ DWC_MODIFY_REG32(&__core_if->core_global_regs->gpwrdn, \
+ 0, gpwrdn.d32); \
+} while (0)
+
+/**
+ * Common interrupt handler.
+ *
+ * The common interrupts are those that occur in both Host and Device mode.
+ * This handler handles the following interrupts:
+ * - Mode Mismatch Interrupt
+ * - Disconnect Interrupt
+ * - OTG Interrupt
+ * - Connector ID Status Change Interrupt
+ * - Session Request Interrupt.
+ * - Resume / Remote Wakeup Detected Interrupt.
+ * - LPM Transaction Received Interrupt
+ * - ADP Transaction Received Interrupt
+ *
+ */
+int32_t dwc_otg_handle_common_intr(void *dev)
+{
+ int retval = 0;
+ gintsts_data_t gintsts;
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ dwc_otg_device_t *otg_dev = dev;
+ dwc_otg_core_if_t *core_if = otg_dev->core_if;
+ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+ if (dwc_otg_is_device_mode(core_if))
+ core_if->frame_num = dwc_otg_get_frame_number(core_if);
+
+ if (core_if->lock)
+ DWC_SPINLOCK(core_if->lock);
+
+ if (core_if->hibernation_suspend <= 0) {
+ gintsts.d32 = dwc_otg_read_common_intr(core_if);
+
+ if (gintsts.b.modemismatch) {
+ retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
+ }
+ if (gintsts.b.otgintr) {
+ retval |= dwc_otg_handle_otg_intr(core_if);
+ }
+ if (gintsts.b.conidstschng) {
+ retval |= dwc_otg_handle_conn_id_status_change_intr(core_if);
+ }
+ if (gintsts.b.disconnect) {
+ retval |= dwc_otg_handle_disconnect_intr(core_if);
+ }
+ if (gintsts.b.sessreqintr) {
+ retval |= dwc_otg_handle_session_req_intr(core_if);
+ }
+ if (gintsts.b.wkupintr) {
+ retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
+ }
+ if (gintsts.b.usbsuspend) {
+ retval |= dwc_otg_handle_usb_suspend_intr(core_if);
+ }
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ if (gintsts.b.lpmtranrcvd) {
+ retval |= dwc_otg_handle_lpm_intr(core_if);
+ }
+#endif
+ if (gintsts.b.restoredone) {
+ gintsts.d32 = 0;
+ if (core_if->power_down == 2)
+ core_if->hibernation_suspend = -1;
+ gintsts.b.restoredone = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
+ DWC_PRINTF(" --Restore done interrupt received-- \n");
+ retval |= 1;
+ }
+ if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) {
+ /* The port interrupt occurs while in device mode with HPRT0
+ * Port Enable/Disable.
+ */
+ gintsts.d32 = 0;
+ gintsts.b.portintr = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
+ retval |= 1;
+
+ }
+ } else {
+ DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32);
+
+ if (gpwrdn.b.disconn_det && gpwrdn.b.disconn_det_msk) {
+ CLEAR_GPWRDN_INTR(core_if, disconn_det);
+ if (gpwrdn.b.linestate == 0) {
+ dwc_otg_handle_pwrdn_disconnect_intr(core_if);
+ } else {
+ DWC_PRINTF("Disconnect detected while linestate is not 0\n");
+ }
+
+ retval |= 1;
+ }
+ if (gpwrdn.b.lnstschng && gpwrdn.b.lnstchng_msk) {
+ CLEAR_GPWRDN_INTR(core_if, lnstschng);
+ /* remote wakeup from hibernation */
+ if (gpwrdn.b.linestate == 2 || gpwrdn.b.linestate == 1) {
+ dwc_otg_handle_pwrdn_wakeup_detected_intr(core_if);
+ } else {
+ DWC_PRINTF("gpwrdn.linestate = %d\n", gpwrdn.b.linestate);
+ }
+ retval |= 1;
+ }
+ if (gpwrdn.b.rst_det && gpwrdn.b.rst_det_msk) {
+ CLEAR_GPWRDN_INTR(core_if, rst_det);
+ if (gpwrdn.b.linestate == 0) {
+ DWC_PRINTF("Reset detected\n");
+ retval |= dwc_otg_device_hibernation_restore(core_if, 0, 1);
+ }
+ }
+ if (gpwrdn.b.srp_det && gpwrdn.b.srp_det_msk) {
+ CLEAR_GPWRDN_INTR(core_if, srp_det);
+ dwc_otg_handle_pwrdn_srp_intr(core_if);
+ retval |= 1;
+ }
+ }
+ /* Handle ADP interrupt here */
+ if (gpwrdn.b.adp_int) {
+ DWC_PRINTF("ADP interrupt\n");
+ CLEAR_GPWRDN_INTR(core_if, adp_int);
+ dwc_otg_adp_handle_intr(core_if);
+ retval |= 1;
+ }
+ if (gpwrdn.b.sts_chngint && gpwrdn.b.sts_chngint_msk) {
+ DWC_PRINTF("STS CHNG interrupt asserted\n");
+ CLEAR_GPWRDN_INTR(core_if, sts_chngint);
+ dwc_otg_handle_pwrdn_stschng_intr(otg_dev);
+
+ retval |= 1;
+ }
+ if (core_if->lock)
+ DWC_SPINUNLOCK(core_if->lock);
+
+ return retval;
+}
diff --git a/drivers/usb/dwc_otg/dwc_otg_core_if.h b/drivers/usb/dwc_otg/dwc_otg_core_if.h
new file mode 100644
index 0000000..30f52fe
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_core_if.h
@@ -0,0 +1,699 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_core_if.h $
+ * $Revision: #12 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871159 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#if !defined(__DWC_CORE_IF_H__)
+#define __DWC_CORE_IF_H__
+
+#include "dwc_os.h"
+
+/** @file
+ * This file defines DWC_OTG Core API
+ */
+
+struct dwc_otg_core_if;
+typedef struct dwc_otg_core_if dwc_otg_core_if_t;
+
+/** Maximum number of Periodic FIFOs */
+#define MAX_PERIO_FIFOS 15
+/** Maximum number of Periodic FIFOs */
+#define MAX_TX_FIFOS 15
+
+/** Maximum number of Endpoints/HostChannels */
+#define MAX_EPS_CHANNELS 16
+
+extern dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * _reg_base_addr);
+extern void dwc_otg_core_init(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_cil_remove(dwc_otg_core_if_t * _core_if);
+
+extern void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * _core_if);
+
+extern uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if);
+extern uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if);
+
+extern uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if);
+
+/** This function should be called on every hardware interrupt. */
+extern int32_t dwc_otg_handle_common_intr(void *otg_dev);
+
+/** @name OTG Core Parameters */
+/** @{ */
+
+/**
+ * Specifies the OTG capabilities. The driver will automatically
+ * detect the value for this parameter if none is specified.
+ * 0 - HNP and SRP capable (default)
+ * 1 - SRP Only capable
+ * 2 - No HNP/SRP capable
+ */
+extern int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if);
+#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0
+#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1
+#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2
+#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE
+
+extern int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if);
+#define dwc_param_opt_default 1
+
+/**
+ * Specifies whether to use slave or DMA mode for accessing the data
+ * FIFOs. The driver will automatically detect the value for this
+ * parameter if none is specified.
+ * 0 - Slave
+ * 1 - DMA (default, if available)
+ */
+extern int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_dma_enable_default 1
+
+/**
+ * When DMA mode is enabled specifies whether to use
+ * address DMA or DMA Descritor mode for accessing the data
+ * FIFOs in device mode. The driver will automatically detect
+ * the value for this parameter if none is specified.
+ * 0 - address DMA
+ * 1 - DMA Descriptor(default, if available)
+ */
+extern int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_dma_desc_enable_default 0
+
+/** The DMA Burst size (applicable only for External DMA
+ * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32)
+ */
+extern int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if);
+#define dwc_param_dma_burst_size_default 32
+
+/**
+ * Specifies the maximum speed of operation in host and device mode.
+ * The actual speed depends on the speed of the attached device and
+ * the value of phy_type. The actual speed depends on the speed of the
+ * attached device.
+ * 0 - High Speed (default)
+ * 1 - Full Speed
+ */
+extern int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if);
+#define dwc_param_speed_default 0
+#define DWC_SPEED_PARAM_HIGH 0
+#define DWC_SPEED_PARAM_FULL 1
+
+/** Specifies whether low power mode is supported when attached
+ * to a Full Speed or Low Speed device in host mode.
+ * 0 - Don't support low power mode (default)
+ * 1 - Support low power mode
+ */
+extern int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t *
+ core_if, int32_t val);
+extern int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t
+ * core_if);
+#define dwc_param_host_support_fs_ls_low_power_default 0
+
+/** Specifies the PHY clock rate in low power mode when connected to a
+ * Low Speed device in host mode. This parameter is applicable only if
+ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
+ * then defaults to 6 MHZ otherwise 48 MHZ.
+ *
+ * 0 - 48 MHz
+ * 1 - 6 MHz
+ */
+extern int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t *
+ core_if, int32_t val);
+extern int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t *
+ core_if);
+#define dwc_param_host_ls_low_power_phy_clk_default 0
+#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0
+#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1
+
+/**
+ * 0 - Use cC FIFO size parameters
+ * 1 - Allow dynamic FIFO sizing (default)
+ */
+extern int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t *
+ core_if);
+#define dwc_param_enable_dynamic_fifo_default 1
+
+/** Total number of 4-byte words in the data FIFO memory. This
+ * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic
+ * Tx FIFOs.
+ * 32 to 32768 (default 8192)
+ * Note: The total FIFO memory depth in the FPGA configuration is 8192.
+ */
+extern int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if);
+#define dwc_param_data_fifo_size_default 8192
+
+/** Number of 4-byte words in the Rx FIFO in device mode when dynamic
+ * FIFO sizing is enabled.
+ * 16 to 32768 (default 1064)
+ */
+extern int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if);
+#define dwc_param_dev_rx_fifo_size_default 1064
+
+/** Number of 4-byte words in the non-periodic Tx FIFO in device mode
+ * when dynamic FIFO sizing is enabled.
+ * 16 to 32768 (default 1024)
+ */
+extern int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t *
+ core_if, int32_t val);
+extern int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t *
+ core_if);
+#define dwc_param_dev_nperio_tx_fifo_size_default 1024
+
+/** Number of 4-byte words in each of the periodic Tx FIFOs in device
+ * mode when dynamic FIFO sizing is enabled.
+ * 4 to 768 (default 256)
+ */
+extern int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+ int32_t val, int fifo_num);
+extern int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t *
+ core_if, int fifo_num);
+#define dwc_param_dev_perio_tx_fifo_size_default 256
+
+/** Number of 4-byte words in the Rx FIFO in host mode when dynamic
+ * FIFO sizing is enabled.
+ * 16 to 32768 (default 1024)
+ */
+extern int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if);
+#define dwc_param_host_rx_fifo_size_default 776
+
+
+/** Number of 4-byte words in the non-periodic Tx FIFO in host mode
+ * when Dynamic FIFO sizing is enabled in the core.
+ * 16 to 32768 (default 1024)
+ */
+extern int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t *
+ core_if, int32_t val);
+extern int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t *
+ core_if);
+#define dwc_param_host_nperio_tx_fifo_size_default 512
+
+/** Number of 4-byte words in the host periodic Tx FIFO when dynamic
+ * FIFO sizing is enabled.
+ * 16 to 32768 (default 1024)
+ */
+extern int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t *
+ core_if, int32_t val);
+extern int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t *
+ core_if);
+#define dwc_param_host_perio_tx_fifo_size_default 768
+
+/** The maximum transfer size supported in bytes.
+ * 2047 to 65,535 (default 65,535)
+ */
+extern int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if);
+#define dwc_param_max_transfer_size_default 65535
+
+/** The maximum number of packets in a transfer.
+ * 15 to 511 (default 511)
+ */
+extern int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if);
+#define dwc_param_max_packet_count_default 511
+
+/** The number of host channel registers to use.
+ * 1 to 16 (default 12)
+ * Note: The FPGA configuration supports a maximum of 12 host channels.
+ */
+extern int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if);
+#define dwc_param_host_channels_default 14
+
+/** The number of endpoints in addition to EP0 available for device
+ * mode operations.
+ * 1 to 15 (default 6 IN and OUT)
+ * Note: The FPGA configuration supports a maximum of 6 IN and OUT
+ * endpoints in addition to EP0.
+ */
+extern int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if);
+#define dwc_param_dev_endpoints_default 7
+
+/**
+ * Specifies the type of PHY interface to use. By default, the driver
+ * will automatically detect the phy_type.
+ *
+ * 0 - Full Speed PHY
+ * 1 - UTMI+ (default)
+ * 2 - ULPI
+ */
+extern int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if);
+#define DWC_PHY_TYPE_PARAM_FS 0
+#define DWC_PHY_TYPE_PARAM_UTMI 1
+#define DWC_PHY_TYPE_PARAM_ULPI 2
+#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI
+
+/**
+ * Specifies the UTMI+ Data Width. This parameter is
+ * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI
+ * PHY_TYPE, this parameter indicates the data width between
+ * the MAC and the ULPI Wrapper.) Also, this parameter is
+ * applicable only if the OTG_HSPHY_WIDTH cC parameter was set
+ * to "8 and 16 bits", meaning that the core has been
+ * configured to work at either data path width.
+ *
+ * 8 or 16 bits (default 16)
+ */
+extern int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if);
+#define dwc_param_phy_utmi_width_default 16
+
+/**
+ * Specifies whether the ULPI operates at double or single
+ * data rate. This parameter is only applicable if PHY_TYPE is
+ * ULPI.
+ *
+ * 0 - single data rate ULPI interface with 8 bit wide data
+ * bus (default)
+ * 1 - double data rate ULPI interface with 4 bit wide data
+ * bus
+ */
+extern int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if);
+#define dwc_param_phy_ulpi_ddr_default 0
+
+/**
+ * Specifies whether to use the internal or external supply to
+ * drive the vbus with a ULPI phy.
+ */
+extern int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if);
+#define DWC_PHY_ULPI_INTERNAL_VBUS 0
+#define DWC_PHY_ULPI_EXTERNAL_VBUS 1
+#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS
+
+/**
+ * Specifies whether to use the I2Cinterface for full speed PHY. This
+ * parameter is only applicable if PHY_TYPE is FS.
+ * 0 - No (default)
+ * 1 - Yes
+ */
+extern int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_i2c_enable_default 0
+
+extern int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if);
+#define dwc_param_ulpi_fs_ls_default 0
+
+extern int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if);
+#define dwc_param_ts_dline_default 0
+
+/**
+ * Specifies whether dedicated transmit FIFOs are
+ * enabled for non periodic IN endpoints in device mode
+ * 0 - No
+ * 1 - Yes
+ */
+extern int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t *
+ core_if);
+#define dwc_param_en_multiple_tx_fifo_default 1
+
+/** Number of 4-byte words in each of the Tx FIFOs in device
+ * mode when dynamic FIFO sizing is enabled.
+ * 4 to 768 (default 256)
+ */
+extern int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if,
+ int fifo_num, int32_t val);
+extern int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if,
+ int fifo_num);
+#define dwc_param_dev_tx_fifo_size_default 768
+
+/** Thresholding enable flag-
+ * bit 0 - enable non-ISO Tx thresholding
+ * bit 1 - enable ISO Tx thresholding
+ * bit 2 - enable Rx thresholding
+ */
+extern int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_thr_ctl(dwc_otg_core_if_t * core_if, int fifo_num);
+#define dwc_param_thr_ctl_default 0
+
+/** Thresholding length for Tx
+ * FIFOs in 32 bit DWORDs
+ */
+extern int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_tx_thr_length(dwc_otg_core_if_t * core_if);
+#define dwc_param_tx_thr_length_default 64
+
+/** Thresholding length for Rx
+ * FIFOs in 32 bit DWORDs
+ */
+extern int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_rx_thr_length(dwc_otg_core_if_t * core_if);
+#define dwc_param_rx_thr_length_default 64
+
+/**
+ * Specifies whether LPM (Link Power Management) support is enabled
+ */
+extern int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_lpm_enable_default 1
+
+/**
+ * Specifies whether PTI enhancement is enabled
+ */
+extern int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_pti_enable_default 0
+
+/**
+ * Specifies whether MPI enhancement is enabled
+ */
+extern int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_mpi_enable_default 0
+
+/**
+ * Specifies whether ADP capability is enabled
+ */
+extern int dwc_otg_set_param_adp_enable(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_adp_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_adp_enable_default 0
+
+/**
+ * Specifies whether IC_USB capability is enabled
+ */
+
+extern int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if);
+#define dwc_param_ic_usb_cap_default 0
+
+extern int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if);
+#define dwc_param_ahb_thr_ratio_default 0
+
+extern int dwc_otg_set_param_power_down(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_power_down(dwc_otg_core_if_t * core_if);
+#define dwc_param_power_down_default 0
+
+extern int dwc_otg_set_param_reload_ctl(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_reload_ctl(dwc_otg_core_if_t * core_if);
+#define dwc_param_reload_ctl_default 0
+
+extern int dwc_otg_set_param_dev_out_nak(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_dev_out_nak(dwc_otg_core_if_t * core_if);
+#define dwc_param_dev_out_nak_default 0
+
+extern int dwc_otg_set_param_cont_on_bna(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_cont_on_bna(dwc_otg_core_if_t * core_if);
+#define dwc_param_cont_on_bna_default 0
+
+extern int dwc_otg_set_param_ahb_single(dwc_otg_core_if_t * core_if,
+ int32_t val);
+extern int32_t dwc_otg_get_param_ahb_single(dwc_otg_core_if_t * core_if);
+#define dwc_param_ahb_single_default 0
+
+extern int dwc_otg_set_param_otg_ver(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_otg_ver(dwc_otg_core_if_t * core_if);
+#define dwc_param_otg_ver_default 0
+
+/** @} */
+
+/** @name Access to registers and bit-fields */
+
+/**
+ * Dump core registers and SPRAM
+ */
+extern void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_dump_spram(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_dump_host_registers(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_dump_global_registers(dwc_otg_core_if_t * _core_if);
+
+/**
+ * Get host negotiation status.
+ */
+extern uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get srp status
+ */
+extern uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if);
+
+/**
+ * Set hnpreq bit in the GOTGCTL register.
+ */
+extern void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get Content of SNPSID register.
+ */
+extern uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get current mode.
+ * Returns 0 if in device mode, and 1 if in host mode.
+ */
+extern uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of hnpcapable field in the GUSBCFG register
+ */
+extern uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of hnpcapable field in the GUSBCFG register
+ */
+extern void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of srpcapable field in the GUSBCFG register
+ */
+extern uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of srpcapable field in the GUSBCFG register
+ */
+extern void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of devspeed field in the DCFG register
+ */
+extern uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of devspeed field in the DCFG register
+ */
+extern void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get the value of busconnected field from the HPRT0 register
+ */
+extern uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if);
+
+/**
+ * Gets the device enumeration Speed.
+ */
+extern uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of prtpwr field from the HPRT0 register
+ */
+extern uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of flag indicating core state - hibernated or not
+ */
+extern uint32_t dwc_otg_get_core_state(dwc_otg_core_if_t * core_if);
+
+/**
+ * Set value of prtpwr field from the HPRT0 register
+ */
+extern void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of prtsusp field from the HPRT0 regsiter
+ */
+extern uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of prtpwr field from the HPRT0 register
+ */
+extern void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of ModeChTimEn field from the HCFG regsiter
+ */
+extern uint32_t dwc_otg_get_mode_ch_tim(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of ModeChTimEn field from the HCFG regsiter
+ */
+extern void dwc_otg_set_mode_ch_tim(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of Fram Interval field from the HFIR regsiter
+ */
+extern uint32_t dwc_otg_get_fr_interval(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of Frame Interval field from the HFIR regsiter
+ */
+extern void dwc_otg_set_fr_interval(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Set value of prtres field from the HPRT0 register
+ *FIXME Remove?
+ */
+extern void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of rmtwkupsig bit in DCTL register
+ */
+extern uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of prt_sleep_sts field from the GLPMCFG register
+ */
+extern uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of rem_wkup_en field from the GLPMCFG register
+ */
+extern uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of appl_resp field from the GLPMCFG register
+ */
+extern uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of appl_resp field from the GLPMCFG register
+ */
+extern void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of hsic_connect field from the GLPMCFG register
+ */
+extern uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of hsic_connect field from the GLPMCFG register
+ */
+extern void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of inv_sel_hsic field from the GLPMCFG register.
+ */
+extern uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of inv_sel_hsic field from the GLPMFG register.
+ */
+extern void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/*
+ * Some functions for accessing registers
+ */
+
+/**
+ * GOTGCTL register
+ */
+extern uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GUSBCFG register
+ */
+extern uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GRXFSIZ register
+ */
+extern uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GNPTXFSIZ register
+ */
+extern uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val);
+
+extern uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GGPIO register
+ */
+extern uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GUID register
+ */
+extern uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * HPRT0 register
+ */
+extern uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GHPTXFSIZE
+ */
+extern uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if);
+
+/** @} */
+
+#endif /* __DWC_CORE_IF_H__ */
diff --git a/drivers/usb/dwc_otg/dwc_otg_dbg.h b/drivers/usb/dwc_otg/dwc_otg_dbg.h
new file mode 100644
index 0000000..e81d093
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_dbg.h
@@ -0,0 +1,113 @@
+/* ==========================================================================
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#ifndef __DWC_OTG_DBG_H__
+#define __DWC_OTG_DBG_H__
+
+/** @file
+ * This file defines debug levels.
+ * Debugging support vanishes in non-debug builds.
+ */
+
+/**
+ * The Debug Level bit-mask variable.
+ */
+extern uint32_t g_dbg_lvl;
+/**
+ * Set the Debug Level variable.
+ */
+static inline uint32_t SET_DEBUG_LEVEL(const uint32_t new)
+{
+ uint32_t old = g_dbg_lvl;
+ g_dbg_lvl = new;
+ return old;
+}
+
+/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */
+#define DBG_CIL (0x2)
+/** When debug level has the DBG_CILV bit set, display CIL Verbose debug
+ * messages */
+#define DBG_CILV (0x20)
+/** When debug level has the DBG_PCD bit set, display PCD (Device) debug
+ * messages */
+#define DBG_PCD (0x4)
+/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug
+ * messages */
+#define DBG_PCDV (0x40)
+/** When debug level has the DBG_HCD bit set, display Host debug messages */
+#define DBG_HCD (0x8)
+/** When debug level has the DBG_HCDV bit set, display Verbose Host debug
+ * messages */
+#define DBG_HCDV (0x80)
+/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host
+ * mode. */
+#define DBG_HCD_URB (0x800)
+
+/** When debug level has any bit set, display debug messages */
+#define DBG_ANY (0xFF)
+
+/** All debug messages off */
+#define DBG_OFF 0
+
+/** Prefix string for DWC_DEBUG print macros. */
+#define USB_DWC "DWC_otg: "
+
+/**
+ * Print a debug message when the Global debug level variable contains
+ * the bit defined in <code>lvl</code>.
+ *
+ * @param[in] lvl - Debug level, use one of the DBG_ constants above.
+ * @param[in] x - like printf
+ *
+ * Example:<p>
+ * <code>
+ * DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr);
+ * </code>
+ * <br>
+ * results in:<br>
+ * <code>
+ * usb-DWC_otg: dwc_otg_cil_init(ca867000)
+ * </code>
+ */
+#ifdef DEBUG
+
+# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)__DWC_DEBUG(USB_DWC x ); }while(0)
+# define DWC_DEBUGP(x...) DWC_DEBUGPL(DBG_ANY, x )
+
+# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl)
+
+#else
+
+# define DWC_DEBUGPL(lvl, x...) do{}while(0)
+# define DWC_DEBUGP(x...)
+
+# define CHK_DEBUG_LEVEL(level) (0)
+
+#endif /*DEBUG*/
+#endif
diff --git a/drivers/usb/dwc_otg/dwc_otg_driver.c b/drivers/usb/dwc_otg/dwc_otg_driver.c
new file mode 100644
index 0000000..2afd1bc
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_driver.c
@@ -0,0 +1,1893 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.c $
+ * $Revision: #91 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871159 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ * The dwc_otg_driver module provides the initialization and cleanup entry
+ * points for the DWC_otg driver. This module will be dynamically installed
+ * after Linux is booted using the insmod command. When the module is
+ * installed, the dwc_otg_driver_init function is called. When the module is
+ * removed (using rmmod), the dwc_otg_driver_cleanup function is called.
+ *
+ * This module also defines a data structure for the dwc_otg_driver, which is
+ * used in conjunction with the standard ARM lm_device structure. These
+ * structures allow the OTG driver to comply with the standard Linux driver
+ * model in which devices and drivers are registered with a bus driver. This
+ * has the benefit that Linux can expose attributes of the driver and device
+ * in its special sysfs file system. Users can then read or write files in
+ * this file system to perform diagnostics on the driver components or the
+ * device.
+ */
+
+#include "dwc_otg_os_dep.h"
+#include "dwc_os.h"
+#include "dwc_otg_dbg.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_attr.h"
+#include "dwc_otg_core_if.h"
+#include "dwc_otg_pcd_if.h"
+#include "dwc_otg_hcd_if.h"
+#include <mach/reset.h>
+#include <mach/hardware.h>
+
+#include <linux/clk.h>
+#include <mach/comcerto-2000/pm.h>
+
+/* USB 3.0 clock */
+static struct clk *usb2_clk;
+
+extern void dwc_otg_host_port_suspend(struct usb_hcd *hcd);
+extern void dwc_otg_host_port_resume(struct usb_hcd *hcd);
+
+
+#define DWC_DRIVER_VERSION "2.94a 27-OCT-2011"
+#define DWC_DRIVER_DESC "HS OTG USB Controller driver"
+
+static const char dwc_driver_name[] = "dwc_otg";
+
+extern int pcd_init(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#else
+ struct platform_device *_dev
+#endif
+ );
+extern int hcd_init(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#else
+ struct platform_device *_dev
+#endif
+ );
+
+extern int pcd_remove(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#else
+ struct platform_device *_dev
+#endif
+ );
+
+extern void hcd_remove(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#else
+ struct platform_device *_dev
+#endif
+ );
+
+extern void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host);
+
+/*-------------------------------------------------------------------------*/
+/* Encapsulate the module parameter settings */
+
+struct dwc_otg_driver_module_params {
+ int32_t opt;
+ int32_t otg_cap;
+ int32_t dma_enable;
+ int32_t dma_desc_enable;
+ int32_t dma_burst_size;
+ int32_t speed;
+ int32_t host_support_fs_ls_low_power;
+ int32_t host_ls_low_power_phy_clk;
+ int32_t enable_dynamic_fifo;
+ int32_t data_fifo_size;
+ int32_t dev_rx_fifo_size;
+ int32_t dev_nperio_tx_fifo_size;
+ uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS];
+ int32_t host_rx_fifo_size;
+ int32_t host_nperio_tx_fifo_size;
+ int32_t host_perio_tx_fifo_size;
+ int32_t max_transfer_size;
+ int32_t max_packet_count;
+ int32_t host_channels;
+ int32_t dev_endpoints;
+ int32_t phy_type;
+ int32_t phy_utmi_width;
+ int32_t phy_ulpi_ddr;
+ int32_t phy_ulpi_ext_vbus;
+ int32_t i2c_enable;
+ int32_t ulpi_fs_ls;
+ int32_t ts_dline;
+ int32_t en_multiple_tx_fifo;
+ uint32_t dev_tx_fifo_size[MAX_TX_FIFOS];
+ uint32_t thr_ctl;
+ uint32_t tx_thr_length;
+ uint32_t rx_thr_length;
+ int32_t pti_enable;
+ int32_t mpi_enable;
+ int32_t lpm_enable;
+ int32_t ic_usb_cap;
+ int32_t ahb_thr_ratio;
+ int32_t power_down;
+ int32_t reload_ctl;
+ int32_t dev_out_nak;
+ int32_t cont_on_bna;
+ int32_t ahb_single;
+ int32_t otg_ver;
+ int32_t adp_enable;
+};
+
+static struct dwc_otg_driver_module_params dwc_otg_module_params = {
+ .opt = -1,
+ .otg_cap = -1,
+ .dma_enable = -1,
+ .dma_desc_enable = -1,
+ .dma_burst_size = -1,
+ .speed = -1,
+ .host_support_fs_ls_low_power = -1,
+ .host_ls_low_power_phy_clk = -1,
+ .enable_dynamic_fifo = -1,
+ .data_fifo_size = -1,
+ .dev_rx_fifo_size = -1,
+ .dev_nperio_tx_fifo_size = -1,
+ .dev_perio_tx_fifo_size = {
+ /* dev_perio_tx_fifo_size_1 */
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1
+ /* 15 */
+ },
+ .host_rx_fifo_size = -1,
+ .host_nperio_tx_fifo_size = -1,
+ .host_perio_tx_fifo_size = -1,
+ .max_transfer_size = -1,
+ .max_packet_count = -1,
+ .host_channels = -1,
+ .dev_endpoints = -1,
+ .phy_type = -1,
+ .phy_utmi_width = -1,
+ .phy_ulpi_ddr = -1,
+ .phy_ulpi_ext_vbus = -1,
+ .i2c_enable = -1,
+ .ulpi_fs_ls = -1,
+ .ts_dline = -1,
+ .en_multiple_tx_fifo = -1,
+ .dev_tx_fifo_size = {
+ /* dev_tx_fifo_size */
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1
+ /* 15 */
+ },
+ .thr_ctl = -1,
+ .tx_thr_length = -1,
+ .rx_thr_length = -1,
+ .pti_enable = -1,
+ .mpi_enable = -1,
+ .lpm_enable = -1,
+ .ic_usb_cap = -1,
+ .ahb_thr_ratio = -1,
+ .power_down = -1,
+ .reload_ctl = -1,
+ .dev_out_nak = -1,
+ .cont_on_bna = -1,
+ .ahb_single = -1,
+ .otg_ver = -1,
+ .adp_enable = -1,
+};
+
+/**
+ * This function shows the Driver Version.
+ */
+static ssize_t version_show(struct device_driver *dev, char *buf)
+{
+ return snprintf(buf, sizeof(DWC_DRIVER_VERSION) + 2, "%s\n",
+ DWC_DRIVER_VERSION);
+}
+
+static DRIVER_ATTR(version, S_IRUGO, version_show, NULL);
+
+/**
+ * Global Debug Level Mask.
+ */
+uint32_t g_dbg_lvl = 0; /* OFF */
+
+/**
+ * This function shows the driver Debug Level.
+ */
+static ssize_t dbg_level_show(struct device_driver *drv, char *buf)
+{
+ return sprintf(buf, "0x%0x\n", g_dbg_lvl);
+}
+
+/**
+ * This function stores the driver Debug Level.
+ */
+static ssize_t dbg_level_store(struct device_driver *drv, const char *buf,
+ size_t count)
+{
+ g_dbg_lvl = simple_strtoul(buf, NULL, 16);
+ return count;
+}
+
+static DRIVER_ATTR(debuglevel, S_IRUGO | S_IWUSR, dbg_level_show,
+ dbg_level_store);
+
+/**
+ * This function is called during module intialization
+ * to pass module parameters to the DWC_OTG CORE.
+ */
+static int set_parameters(dwc_otg_core_if_t * core_if)
+{
+ int retval = 0;
+ int i;
+
+ if (dwc_otg_module_params.otg_cap != -1) {
+ retval +=
+ dwc_otg_set_param_otg_cap(core_if,
+ dwc_otg_module_params.otg_cap);
+ }
+ if (dwc_otg_module_params.dma_enable != -1) {
+ retval +=
+ dwc_otg_set_param_dma_enable(core_if,
+ dwc_otg_module_params.
+ dma_enable);
+ }
+ if (dwc_otg_module_params.dma_desc_enable != -1) {
+ retval +=
+ dwc_otg_set_param_dma_desc_enable(core_if,
+ dwc_otg_module_params.
+ dma_desc_enable);
+ }
+ if (dwc_otg_module_params.opt != -1) {
+ retval +=
+ dwc_otg_set_param_opt(core_if, dwc_otg_module_params.opt);
+ }
+ if (dwc_otg_module_params.dma_burst_size != -1) {
+ retval +=
+ dwc_otg_set_param_dma_burst_size(core_if,
+ dwc_otg_module_params.
+ dma_burst_size);
+ }
+ if (dwc_otg_module_params.host_support_fs_ls_low_power != -1) {
+ retval +=
+ dwc_otg_set_param_host_support_fs_ls_low_power(core_if,
+ dwc_otg_module_params.
+ host_support_fs_ls_low_power);
+ }
+ if (dwc_otg_module_params.enable_dynamic_fifo != -1) {
+ retval +=
+ dwc_otg_set_param_enable_dynamic_fifo(core_if,
+ dwc_otg_module_params.
+ enable_dynamic_fifo);
+ }
+ if (dwc_otg_module_params.data_fifo_size != -1) {
+ retval +=
+ dwc_otg_set_param_data_fifo_size(core_if,
+ dwc_otg_module_params.
+ data_fifo_size);
+ }
+ if (dwc_otg_module_params.dev_rx_fifo_size != -1) {
+ retval +=
+ dwc_otg_set_param_dev_rx_fifo_size(core_if,
+ dwc_otg_module_params.
+ dev_rx_fifo_size);
+ }
+ if (dwc_otg_module_params.dev_nperio_tx_fifo_size != -1) {
+ retval +=
+ dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if,
+ dwc_otg_module_params.
+ dev_nperio_tx_fifo_size);
+ }
+ if (dwc_otg_module_params.host_rx_fifo_size != -1) {
+ retval +=
+ dwc_otg_set_param_host_rx_fifo_size(core_if,
+ dwc_otg_module_params.host_rx_fifo_size);
+ }
+ if (dwc_otg_module_params.host_nperio_tx_fifo_size != -1) {
+ retval +=
+ dwc_otg_set_param_host_nperio_tx_fifo_size(core_if,
+ dwc_otg_module_params.
+ host_nperio_tx_fifo_size);
+ }
+ if (dwc_otg_module_params.host_perio_tx_fifo_size != -1) {
+ retval +=
+ dwc_otg_set_param_host_perio_tx_fifo_size(core_if,
+ dwc_otg_module_params.
+ host_perio_tx_fifo_size);
+ }
+ if (dwc_otg_module_params.max_transfer_size != -1) {
+ retval +=
+ dwc_otg_set_param_max_transfer_size(core_if,
+ dwc_otg_module_params.
+ max_transfer_size);
+ }
+ if (dwc_otg_module_params.max_packet_count != -1) {
+ retval +=
+ dwc_otg_set_param_max_packet_count(core_if,
+ dwc_otg_module_params.
+ max_packet_count);
+ }
+ if (dwc_otg_module_params.host_channels != -1) {
+ retval +=
+ dwc_otg_set_param_host_channels(core_if,
+ dwc_otg_module_params.
+ host_channels);
+ }
+ if (dwc_otg_module_params.dev_endpoints != -1) {
+ retval +=
+ dwc_otg_set_param_dev_endpoints(core_if,
+ dwc_otg_module_params.
+ dev_endpoints);
+ }
+ if (dwc_otg_module_params.phy_type != -1) {
+ retval +=
+ dwc_otg_set_param_phy_type(core_if,
+ dwc_otg_module_params.phy_type);
+ }
+ if (dwc_otg_module_params.speed != -1) {
+ retval +=
+ dwc_otg_set_param_speed(core_if,
+ dwc_otg_module_params.speed);
+ }
+ if (dwc_otg_module_params.host_ls_low_power_phy_clk != -1) {
+ retval +=
+ dwc_otg_set_param_host_ls_low_power_phy_clk(core_if,
+ dwc_otg_module_params.
+ host_ls_low_power_phy_clk);
+ }
+ if (dwc_otg_module_params.phy_ulpi_ddr != -1) {
+ retval +=
+ dwc_otg_set_param_phy_ulpi_ddr(core_if,
+ dwc_otg_module_params.
+ phy_ulpi_ddr);
+ }
+ if (dwc_otg_module_params.phy_ulpi_ext_vbus != -1) {
+ retval +=
+ dwc_otg_set_param_phy_ulpi_ext_vbus(core_if,
+ dwc_otg_module_params.
+ phy_ulpi_ext_vbus);
+ }
+ if (dwc_otg_module_params.phy_utmi_width != -1) {
+ retval +=
+ dwc_otg_set_param_phy_utmi_width(core_if,
+ dwc_otg_module_params.
+ phy_utmi_width);
+ }
+ if (dwc_otg_module_params.ulpi_fs_ls != -1) {
+ retval +=
+ dwc_otg_set_param_ulpi_fs_ls(core_if,
+ dwc_otg_module_params.ulpi_fs_ls);
+ }
+ if (dwc_otg_module_params.ts_dline != -1) {
+ retval +=
+ dwc_otg_set_param_ts_dline(core_if,
+ dwc_otg_module_params.ts_dline);
+ }
+ if (dwc_otg_module_params.i2c_enable != -1) {
+ retval +=
+ dwc_otg_set_param_i2c_enable(core_if,
+ dwc_otg_module_params.
+ i2c_enable);
+ }
+ if (dwc_otg_module_params.en_multiple_tx_fifo != -1) {
+ retval +=
+ dwc_otg_set_param_en_multiple_tx_fifo(core_if,
+ dwc_otg_module_params.
+ en_multiple_tx_fifo);
+ }
+
+ for (i = 0; i < 7; i++) {
+ if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) {
+ retval +=
+ dwc_otg_set_param_dev_perio_tx_fifo_size(core_if,
+ dwc_otg_module_params.
+ dev_perio_tx_fifo_size
+ [i], i);
+ }
+ }
+
+ for (i = 0; i < 7; i++) {
+ if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) {
+ retval += dwc_otg_set_param_dev_tx_fifo_size(core_if,
+ dwc_otg_module_params.
+ dev_tx_fifo_size
+ [i], i);
+ }
+ }
+ if (dwc_otg_module_params.thr_ctl != -1) {
+ retval +=
+ dwc_otg_set_param_thr_ctl(core_if,
+ dwc_otg_module_params.thr_ctl);
+ }
+ if (dwc_otg_module_params.mpi_enable != -1) {
+ retval +=
+ dwc_otg_set_param_mpi_enable(core_if,
+ dwc_otg_module_params.
+ mpi_enable);
+ }
+ if (dwc_otg_module_params.pti_enable != -1) {
+ retval +=
+ dwc_otg_set_param_pti_enable(core_if,
+ dwc_otg_module_params.
+ pti_enable);
+ }
+ if (dwc_otg_module_params.lpm_enable != -1) {
+ retval +=
+ dwc_otg_set_param_lpm_enable(core_if,
+ dwc_otg_module_params.
+ lpm_enable);
+ }
+ if (dwc_otg_module_params.ic_usb_cap != -1) {
+ retval +=
+ dwc_otg_set_param_ic_usb_cap(core_if,
+ dwc_otg_module_params.
+ ic_usb_cap);
+ }
+ if (dwc_otg_module_params.tx_thr_length != -1) {
+ retval +=
+ dwc_otg_set_param_tx_thr_length(core_if,
+ dwc_otg_module_params.tx_thr_length);
+ }
+ if (dwc_otg_module_params.rx_thr_length != -1) {
+ retval +=
+ dwc_otg_set_param_rx_thr_length(core_if,
+ dwc_otg_module_params.
+ rx_thr_length);
+ }
+ if (dwc_otg_module_params.ahb_thr_ratio != -1) {
+ retval +=
+ dwc_otg_set_param_ahb_thr_ratio(core_if,
+ dwc_otg_module_params.ahb_thr_ratio);
+ }
+ if (dwc_otg_module_params.power_down != -1) {
+ retval +=
+ dwc_otg_set_param_power_down(core_if,
+ dwc_otg_module_params.power_down);
+ }
+ if (dwc_otg_module_params.reload_ctl != -1) {
+ retval +=
+ dwc_otg_set_param_reload_ctl(core_if,
+ dwc_otg_module_params.reload_ctl);
+ }
+
+ if (dwc_otg_module_params.dev_out_nak != -1) {
+ retval +=
+ dwc_otg_set_param_dev_out_nak(core_if,
+ dwc_otg_module_params.dev_out_nak);
+ }
+
+ if (dwc_otg_module_params.cont_on_bna != -1) {
+ retval +=
+ dwc_otg_set_param_cont_on_bna(core_if,
+ dwc_otg_module_params.cont_on_bna);
+ }
+
+ if (dwc_otg_module_params.ahb_single != -1) {
+ retval +=
+ dwc_otg_set_param_ahb_single(core_if,
+ dwc_otg_module_params.ahb_single);
+ }
+
+ if (dwc_otg_module_params.otg_ver != -1) {
+ retval +=
+ dwc_otg_set_param_otg_ver(core_if,
+ dwc_otg_module_params.otg_ver);
+ }
+ if (dwc_otg_module_params.adp_enable != -1) {
+ retval +=
+ dwc_otg_set_param_adp_enable(core_if,
+ dwc_otg_module_params.
+ adp_enable);
+ }
+ return retval;
+}
+
+
+/**
+ * Platform specific initialization for USB 2 PHY
+ */
+static void comcerto_usb2_phy_init(void)
+{
+ uint32_t rd_data;
+
+#define COMCERTO_USB0_PHY_CTRL_REG0 (COMCERTO_DWC1_CFG_BASE + 0x000)
+#define COMCERTO_USB0_DWC_CFG_REGF (USB2_PHY_BASE + 0x03C)
+
+
+ /* take device out of reset , enable clock. */
+ /* Programming the USB0 PHY CONTROL REGISTER */
+ /* Bit[2]:usb0_vbusvldextsel - The internal Session Valid comparator is used.
+ * Bit[3]:usb0_vbusvldext - The VBUS signal is not valid, and the pull-up resistor on D+ is disabled.
+ * Bit[4]:usb0_otgdisable - TThe OTG block is powered up.
+ * Bit[17:16]:usb0_refclkdiv - Reference Clock Frequency Select, 48 MHz
+ * Bit[21:20]:usb0_refclksel - The XO block uses an external, 2.5 V clock supplied on the XO pin.
+ */
+ if(HAL_get_ref_clk() == REF_CLK_24MHZ)
+ writel(0x00210000, COMCERTO_USB0_PHY_CTRL_REG0); //24MHz ref clk
+ else
+ writel(0x00220000, COMCERTO_USB0_PHY_CTRL_REG0); //48MHz ref clk
+
+ /* Programming the IDSEL values to USB 2.0 Controller @ DWC_CFG_REGF register */
+ /* Configuring the usb2 controller to select the ID value from register */
+ rd_data = readl(COMCERTO_USB0_DWC_CFG_REGF);
+
+ /* Bit[8]:usb0_id_sel - Selects from the register bit
+ * Bit[9]:usb1_id_sel - Selects from the register bit
+ */
+ rd_data = ((rd_data & 0xFFFF11FF)| 0x00001100); //For Host mode.
+ writel (rd_data, COMCERTO_USB0_DWC_CFG_REGF);
+
+
+ /* Programming USB2.0 controller with scale down value. */
+ /* Configuring the usb2 controller in scaledown disable mode */
+ rd_data = readl(COMCERTO_USB0_DWC_CFG_REGF);
+ rd_data = ((rd_data & 0xFFFFFFF0)| 0x0);
+ writel (rd_data, COMCERTO_USB0_DWC_CFG_REGF);
+}
+
+
+
+/**
+ * Platform specific initialization for Dwc OTG Controller
+ */
+static void comcerto_start_dwc_otg(void)
+{
+ /* Get the FAST-UART clk structure from DUS ,To be used for UART0/UART1 initilization */
+ usb2_clk = clk_get(NULL,"usb0");
+
+ /* Enable the Clock */
+ if (clk_enable(usb2_clk)){
+ pr_err("comcerto_start_dwc_otg:Unable to enable the usb2 clock \n");
+ }
+
+ /* USB 2.0 PHY Initialization */
+ comcerto_usb2_phy_init();
+
+ /* Continuing resets on usb2 controller+phy blocks */
+
+ /* APPLYING THE RESET TO USB2 UTMI */
+ c2000_block_reset(COMPONENT_UTMI_USB0, 1);
+
+ /* APPLYING THE RESET TO USB2 PHY */
+ c2000_block_reset(COMPONENT_USB0_PHY, 1);
+
+ /* APLLYING RESET TO USB2 AXI RESET */
+ c2000_block_reset(COMPONENT_AXI_USB0, 1);
+
+ /* Releasing the (POR) Power on Reset to usb2 nano phy block */
+ c2000_block_reset(COMPONENT_USB0_PHY, 0);
+
+ /* Releasing the UTMI Reset to usb2 controller block */
+ c2000_block_reset(COMPONENT_UTMI_USB0, 0);
+
+ /* Releasing the AHB Reset to usb2 controller block -- Bit[4]-USB3,Bit[3]-USB2 */
+ c2000_block_reset(COMPONENT_AXI_USB0, 0);
+}
+
+
+/**
+ * Platform specific Shutdown for Dwc OTG Controller
+ */
+static void comcerto_stop_dwc_otg(void)
+{
+ /* APPLYING THE RESET TO USB2 UTMI */
+ c2000_block_reset(COMPONENT_UTMI_USB0, 1);
+
+ /* APPLYING THE RESET TO USB2 PHY */
+ c2000_block_reset(COMPONENT_USB0_PHY, 1);
+
+ /* APLLYING RESET TO USB2 AXI RESET */
+ c2000_block_reset(COMPONENT_AXI_USB0, 1);
+
+ /* Disable the Clock */
+ clk_disable(usb2_clk);
+
+ /* Release the clock */
+ clk_put(usb2_clk);
+}
+
+
+/**
+ * This function is the top level interrupt handler for the Common
+ * (Device and host modes) interrupts.
+ */
+static irqreturn_t dwc_otg_common_irq(int irq, void *dev)
+{
+ int32_t retval = IRQ_NONE;
+
+ retval = dwc_otg_handle_common_intr(dev);
+ if (retval != 0) {
+ S3C2410X_CLEAR_EINTPEND();
+ }
+ return IRQ_RETVAL(retval);
+}
+
+/**
+ * This function is called when a lm_device is unregistered with the
+ * dwc_otg_driver. This happens, for example, when the rmmod command is
+ * executed. The device may or may not be electrically present. If it is
+ * present, the driver stops device processing. Any resources used on behalf
+ * of this device are freed.
+ *
+ * @param _dev
+ */
+static void dwc_otg_driver_remove(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#else
+ struct platform_device *_dev
+#endif
+ )
+{
+#ifdef LM_INTERFACE
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
+#endif
+
+ DWC_DEBUGPL(DBG_ANY, "%s(%p)\n", __func__, _dev);
+
+ if (!otg_dev) {
+ /* Memory allocation for the dwc_otg_device failed. */
+ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__);
+ return;
+ }
+#ifndef DWC_DEVICE_ONLY
+ if (otg_dev->hcd) {
+ hcd_remove(_dev);
+ } else {
+ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__);
+ return;
+ }
+#endif
+
+#ifndef DWC_HOST_ONLY
+ if (otg_dev->pcd) {
+ pcd_remove(_dev);
+ } else {
+ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->pcd NULL!\n", __func__);
+ return;
+ }
+#endif
+ /*
+ * Free the IRQ
+ */
+ if (otg_dev->common_irq_installed) {
+ free_irq(otg_dev->irq, otg_dev);
+ } else {
+ DWC_DEBUGPL(DBG_ANY, "%s: There is no installed irq!\n", __func__);
+ return;
+ }
+
+ if (otg_dev->core_if) {
+ dwc_otg_cil_remove(otg_dev->core_if);
+ } else {
+ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->core_if NULL!\n", __func__);
+ return;
+ }
+
+ /*
+ * Remove the device attributes
+ */
+ dwc_otg_attr_remove(_dev);
+
+ /*
+ * Return the memory.
+ */
+ if (otg_dev->os_dep.base) {
+ iounmap(otg_dev->os_dep.base);
+ }
+
+ /* Do the Platform specific shutdown */
+ comcerto_stop_dwc_otg();
+
+ DWC_FREE(otg_dev);
+
+ /*
+ * Clear the drvdata pointer.
+ */
+#ifdef LM_INTERFACE
+ lm_set_drvdata(_dev, 0);
+#elif defined(PCI_INTERFACE)
+ release_mem_region(otg_dev->os_dep.rsrc_start, otg_dev->os_dep.rsrc_len);
+ pci_set_drvdata(_dev, 0);
+#else
+ platform_set_drvdata(_dev, 0);
+#endif
+}
+
+/**
+ * This function is called when an lm_device is bound to a
+ * dwc_otg_driver. It creates the driver components required to
+ * control the device (CIL, HCD, and PCD) and it initializes the
+ * device. The driver components are stored in a dwc_otg_device
+ * structure. A reference to the dwc_otg_device is saved in the
+ * lm_device. This allows the driver to access the dwc_otg_device
+ * structure on subsequent calls to driver methods for this device.
+ *
+ * @param _dev Bus device
+ */
+static int dwc_otg_driver_probe(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev,
+ const struct pci_device_id *id
+#else
+ struct platform_device *_dev
+#endif
+ )
+{
+ int retval = 0;
+ dwc_otg_device_t *dwc_otg_device;
+
+ dev_dbg(&_dev->dev, "dwc_otg_driver_probe(%p)\n", _dev);
+#ifdef LM_INTERFACE
+ dev_dbg(&_dev->dev, "start=0x%08x\n", (unsigned)_dev->resource.start);
+#elif defined(PCI_INTERFACE)
+ if (!id) {
+ DWC_ERROR("Invalid pci_device_id %p", id);
+ return -EINVAL;
+ }
+
+ if (!_dev || (pci_enable_device(_dev) < 0)) {
+ DWC_ERROR("Invalid pci_device %p", _dev);
+ return -ENODEV;
+ }
+ dev_dbg(&_dev->dev, "start=0x%08x\n", (unsigned)pci_resource_start(_dev,0));
+ /* other stuff needed as well? */
+#else
+ dev_dbg(&_dev->dev, "start=0x%08x\n", (unsigned)_dev->resource->start);
+#endif
+
+ dwc_otg_device = DWC_ALLOC(sizeof(dwc_otg_device_t));
+
+ if (!dwc_otg_device) {
+ dev_err(&_dev->dev, "kmalloc of dwc_otg_device failed\n");
+ return -ENOMEM;
+ }
+
+ memset(dwc_otg_device, 0, sizeof(*dwc_otg_device));
+ dwc_otg_device->os_dep.reg_offset = 0xFFFFFFFF;
+
+ /* Platform Specific initialization */
+ comcerto_start_dwc_otg();
+
+ dwc_otg_device->os_dep.parent= &_dev->dev;
+
+ /*
+ * Map the DWC_otg Core memory into virtual address space.
+ */
+#ifdef LM_INTERFACE
+ dwc_otg_device->os_dep.base = ioremap(_dev->resource.start, SZ_256K);
+
+ if (!dwc_otg_device->os_dep.base) {
+ dev_err(&_dev->dev, "ioremap() failed\n");
+ DWC_FREE(dwc_otg_device);
+ return -ENOMEM;
+ }
+ dev_dbg(&_dev->dev, "base=0x%08x\n",
+ (unsigned)dwc_otg_device->os_dep.base);
+#elif defined(PCI_INTERFACE)
+ _dev->current_state = PCI_D0;
+ _dev->dev.power.power_state = PMSG_ON;
+
+ if (!_dev->irq) {
+ DWC_ERROR("Found HC with no IRQ. Check BIOS/PCI %s setup!",
+ pci_name(_dev));
+ iounmap(dwc_otg_device->os_dep.base);
+ DWC_FREE(dwc_otg_device);
+ return -ENODEV;
+ }
+
+ dwc_otg_device->os_dep.rsrc_start = pci_resource_start(_dev, 0);
+ dwc_otg_device->os_dep.rsrc_len = pci_resource_len(_dev, 0);
+ DWC_DEBUGPL(DBG_ANY, "PCI resource: start=%08x, len=%08x\n",
+ (unsigned)dwc_otg_device->os_dep.rsrc_start,
+ (unsigned)dwc_otg_device->os_dep.rsrc_len);
+ if (!request_mem_region
+ (dwc_otg_device->os_dep.rsrc_start, dwc_otg_device->os_dep.rsrc_len,
+ "dwc_otg")) {
+ dev_dbg(&_dev->dev, "error requesting memory\n");
+ iounmap(dwc_otg_device->os_dep.base);
+ DWC_FREE(dwc_otg_device);
+ return -EFAULT;
+ }
+
+ dwc_otg_device->os_dep.base =
+ ioremap_nocache(dwc_otg_device->os_dep.rsrc_start,
+ dwc_otg_device->os_dep.rsrc_len);
+ if (dwc_otg_device->os_dep.base == NULL) {
+ dev_dbg(&_dev->dev, "error mapping memory\n");
+ release_mem_region(dwc_otg_device->os_dep.rsrc_start,
+ dwc_otg_device->os_dep.rsrc_len);
+ iounmap(dwc_otg_device->os_dep.base);
+ DWC_FREE(dwc_otg_device);
+ return -EFAULT;
+ }
+ dev_dbg(&_dev->dev, "base=0x%p (before adjust) \n",
+ dwc_otg_device->os_dep.base);
+ dwc_otg_device->os_dep.base = (char *)dwc_otg_device->os_dep.base;
+ dev_dbg(&_dev->dev, "base=0x%p (after adjust) \n",
+ dwc_otg_device->os_dep.base);
+ dev_dbg(&_dev->dev, "%s: mapped PA 0x%x to VA 0x%p\n", __func__,
+ (unsigned)dwc_otg_device->os_dep.rsrc_start,
+ dwc_otg_device->os_dep.base);
+
+ pci_set_master(_dev);
+ pci_set_drvdata(_dev, dwc_otg_device);
+#else
+ dwc_otg_device->os_dep.base = ioremap(_dev->resource->start, SZ_256K);
+
+ if (!dwc_otg_device->os_dep.base) {
+ dev_err(&_dev->dev, "ioremap() failed\n");
+ DWC_FREE(dwc_otg_device);
+ return -ENOMEM;
+ }
+ dev_dbg(&_dev->dev, "base=0x%08x\n",
+ (unsigned)dwc_otg_device->os_dep.base);
+#endif
+
+ /*
+ * Retrieve the memory and IRQ resources.
+ */
+ dwc_otg_device->irq = platform_get_irq(_dev, 0);
+ if (dwc_otg_device->irq <= 0) {
+ dev_err(&_dev->dev, "no device irq\n");
+ retval = -EINVAL;
+ goto fail;
+ }
+
+ /*
+ * Initialize driver data to point to the global DWC_otg
+ * Device structure.
+ */
+#ifdef LM_INTERFACE
+ lm_set_drvdata(_dev, dwc_otg_device);
+#else
+ platform_set_drvdata(_dev, dwc_otg_device);
+ _dev->dev.platform_data = (void *) dwc_otg_device;
+#endif
+ dev_dbg(&_dev->dev, "dwc_otg_device=0x%p\n", dwc_otg_device);
+
+ dwc_otg_device->core_if = dwc_otg_cil_init(dwc_otg_device->os_dep.base);
+ if (!dwc_otg_device->core_if) {
+ dev_err(&_dev->dev, "CIL initialization failed!\n");
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+ /*
+ * Attempt to ensure this device is really a DWC_otg Controller.
+ * Read and verify the SNPSID register contents. The value should be
+ * 0x45F42XXX, which corresponds to "OT2", as in "OTG version 2.XX".
+ */
+
+ if ((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) !=
+ 0x4F542000) {
+ dev_err(&_dev->dev, "Bad value for SNPSID: 0x%08x\n",
+ dwc_otg_get_gsnpsid(dwc_otg_device->core_if));
+ retval = -EINVAL;
+ goto fail;
+ }
+
+ /*
+ * Validate parameter values.
+ */
+ if (set_parameters(dwc_otg_device->core_if)) {
+ retval = -EINVAL;
+ goto fail;
+ }
+
+ /*
+ * Create Device Attributes in sysfs
+ */
+ dwc_otg_attr_create(_dev);
+
+ /*
+ * Disable the global interrupt until all the interrupt
+ * handlers are installed.
+ */
+ dwc_otg_disable_global_interrupts(dwc_otg_device->core_if);
+
+ /*
+ * Install the interrupt handler for the common interrupts before
+ * enabling common interrupts in core_init below.
+ */
+ DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n",
+ dwc_otg_device->irq);
+ retval = request_irq(dwc_otg_device->irq, dwc_otg_common_irq,
+ IRQF_SHARED | IRQF_DISABLED | IRQ_LEVEL, "dwc_otg",
+ dwc_otg_device);
+ if (retval) {
+ DWC_ERROR("request of irq%d failed\n", dwc_otg_device->irq);
+ retval = -EBUSY;
+ goto fail;
+ } else {
+ dwc_otg_device->common_irq_installed = 1;
+ }
+
+#ifdef LM_INTERFACE
+ set_irq_type(_dev->irq, IRQT_LOW);
+#endif
+ /*
+ * Initialize the DWC_otg core.
+ */
+ dwc_otg_core_init(dwc_otg_device->core_if);
+
+#ifndef DWC_HOST_ONLY
+ /*
+ * Initialize the PCD
+ */
+ retval = pcd_init(_dev);
+ if (retval != 0) {
+ DWC_ERROR("pcd_init failed\n");
+ dwc_otg_device->pcd = NULL;
+ goto fail;
+ }
+#endif
+#ifndef DWC_DEVICE_ONLY
+ /*
+ * Initialize the HCD
+ */
+ retval = hcd_init(_dev);
+ if (retval != 0) {
+ DWC_ERROR("hcd_init failed\n");
+ dwc_otg_device->hcd = NULL;
+ goto fail;
+ }
+#endif
+#ifdef PCI_INTERFACE
+ pci_set_drvdata(_dev, dwc_otg_device);
+ dwc_otg_device->os_dep.pcidev = _dev;
+#endif
+
+ /*
+ * Enable the global interrupt after all the interrupt
+ * handlers are installed if there is no ADP support else
+ * perform initial actions required for Internal ADP logic.
+ */
+ if (!dwc_otg_get_param_adp_enable(dwc_otg_device->core_if))
+ dwc_otg_enable_global_interrupts(dwc_otg_device->core_if);
+ else
+ dwc_otg_adp_start(dwc_otg_device->core_if,
+ dwc_otg_is_host_mode(dwc_otg_device->core_if));
+
+ return 0;
+
+fail:
+ dwc_otg_driver_remove(_dev);
+ return retval;
+}
+
+#if 0
+/**
+ * This structure defines the methods to be called by a bus driver
+ * during the lifecycle of a device on that bus. Both drivers and
+ * devices are registered with a bus driver. The bus driver matches
+ * devices to drivers based on information in the device and driver
+ * structures.
+ *
+ * The probe function is called when the bus driver matches a device
+ * to this driver. The remove function is called when a device is
+ * unregistered with the bus driver.
+ */
+#ifdef LM_INTERFACE
+static struct lm_driver dwc_otg_driver = {
+ .drv = {.name = (char *)dwc_driver_name,},
+ .probe = dwc_otg_driver_probe,
+ .remove = dwc_otg_driver_remove,
+};
+#elif defined(PCI_INTERFACE)
+static const struct pci_device_id pci_ids[] = { {
+ PCI_DEVICE(0x16c3, 0xabcd),
+ .driver_data =
+ (unsigned long)0xdeadbeef,
+ }, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver dwc_otg_driver = {
+ .name = "dwc_otg",
+ .id_table = pci_ids,
+
+ .probe = dwc_otg_driver_probe,
+ .remove = dwc_otg_driver_remove,
+
+ .driver = {
+ .name = (char *)dwc_driver_name,
+ },
+};
+#endif
+
+/**
+ * This function is called when the dwc_otg_driver is installed with the
+ * insmod command. It registers the dwc_otg_driver structure with the
+ * appropriate bus driver. This will cause the dwc_otg_driver_probe function
+ * to be called. In addition, the bus driver will automatically expose
+ * attributes defined for the device and driver in the special sysfs file
+ * system.
+ *
+ * @return
+ */
+static int __init dwc_otg_driver_init(void)
+{
+ int retval = 0;
+ int error;
+ printk(KERN_INFO "%s: version %s\n", dwc_driver_name,
+ DWC_DRIVER_VERSION);
+#ifdef LM_INTERFACE
+ retval = lm_driver_register(&dwc_otg_driver);
+#elif defined(PCI_INTERFACE)
+ retval = pci_register_driver(&dwc_otg_driver);
+#endif
+ if (retval < 0) {
+ printk(KERN_ERR "%s retval=%d\n", __func__, retval);
+ return retval;
+ }
+#ifdef LM_INTERFACE
+ error = driver_create_file(&dwc_otg_driver.drv, &driver_attr_version);
+ error = driver_create_file(&dwc_otg_driver.drv, &driver_attr_debuglevel);
+#elif defined(PCI_INTERFACE)
+ error = driver_create_file(&dwc_otg_driver.driver, &driver_attr_version);
+ error = driver_create_file(&dwc_otg_driver.driver, &driver_attr_debuglevel);
+#endif
+ return retval;
+}
+
+module_init(dwc_otg_driver_init);
+
+/**
+ * This function is called when the driver is removed from the kernel
+ * with the rmmod command. The driver unregisters itself with its bus
+ * driver.
+ *
+ */
+static void __exit dwc_otg_driver_cleanup(void)
+{
+ printk(KERN_DEBUG "dwc_otg_driver_cleanup()\n");
+
+#ifdef LM_INTERFACE
+ driver_remove_file(&dwc_otg_driver.drv, &driver_attr_debuglevel);
+ driver_remove_file(&dwc_otg_driver.drv, &driver_attr_version);
+ lm_driver_unregister(&dwc_otg_driver);
+#elif defined(PCI_INTERFACE)
+ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel);
+ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version);
+ pci_unregister_driver(&dwc_otg_driver);
+#endif
+
+ printk(KERN_INFO "%s module removed\n", dwc_driver_name);
+}
+
+module_exit(dwc_otg_driver_cleanup);
+#else
+
+/*
+ * Platform Specific Controller Suspend Routiene.
+ * It does a Port suspend, and gate the clock (Power Off L1).
+ * Controller and PHY reset (Power Off L2) functionality
+ * is not supported
+ */
+int comcerto_usb2_bus_suspend(struct platform_device * pd, pm_message_t state)
+{
+ int error_status = 0, val = 0;
+ struct usb_hcd *hcd = NULL;
+
+ /* Check for the Bit Mask bit for USB2, if not enabled
+ * then we are not going suspend the USB2 device , as by
+ * this device , we will wake from resume.
+ */
+ if ( !(host_utilpe_shared_pmu_bitmask & USB2p0_IRQ )){
+
+ /* We will return here.
+ * Not prepared yet for suspend , so that device suspend
+ * will not occur.
+ */
+ return error_status;
+ }
+
+ hcd = (struct usb_hcd *) platform_get_drvdata(pd);
+
+ /* Do the port suspend for USB 2.0 Controller */
+ dwc_otg_host_port_suspend(hcd);
+
+ for (val = 0 ; val < 50 ; val++)
+ udelay(1000);
+
+ /* Disable the Clock */
+ clk_disable(usb2_clk);
+
+ /* PM Performance Enhancement : USB0 PD */
+ /* Common Block Power-Down Control and powering down all analog blocks */
+ writel(0x01220040, COMCERTO_USB0_PHY_CTRL_REG0);
+
+ return error_status;
+}
+
+/*
+ * Platform Specific Controller Resume Routiene.
+ * It does a Port resume, and enable the clock (Power Off L1).
+ * Resume from (Power Off L2) is not supported
+ */
+int comcerto_usb2_bus_resume(struct platform_device *pd)
+{
+ int error_status = 0;
+ struct usb_hcd *hcd = NULL;
+
+
+ /* Check for the Bit Mask bit for USB2, if not enabled
+ * then we are not going suspend the USB2 device , as by
+ * this device , we will wake from resume.
+ */
+ if ( host_utilpe_shared_pmu_bitmask & USB2p0_IRQ ){
+
+ /* We will return here.
+ * Not prepared yet for suspend , so that device suspend
+ * will not occur.
+ */
+ return error_status;
+ }
+
+ /* PM Performance Enhancement : USB0 PD */
+ /* Common Block Power-Down Control and powering down all analog blocks */
+ writel(0x00220000, COMCERTO_USB0_PHY_CTRL_REG0);
+
+ /* Enable the Clock */
+ if (clk_enable(usb2_clk)){
+ pr_err("comcerto_usb2_bus_resume_dummy:Unable to enable the usb2 clock \n");
+ }
+
+ hcd = (struct usb_hcd *) platform_get_drvdata(pd);
+
+ dwc_otg_host_port_resume(hcd);
+
+ return error_status;
+}
+
+/**
+ * This structure defines the methods to be called by a bus driver
+ * during the lifecycle of a device on that bus. Both drivers and
+ * devices are registered with a bus driver. The bus driver matches
+ * devices to drivers based on information in the device and driver
+ * structures.
+ *
+ * The probe function is called when the bus driver matches a device
+ * to this driver. The remove function is called when a device is
+ * unregistered with the bus driver.
+ */
+static struct platform_driver dwc_otg_driver = {
+ .driver = {
+ .name = (char *)dwc_driver_name,
+ },
+ .probe = dwc_otg_driver_probe,
+ .remove = dwc_otg_driver_remove,
+
+ .suspend = comcerto_usb2_bus_suspend,
+ .resume = comcerto_usb2_bus_resume,
+};
+
+/**
+ * This function is called when the dwc_otg_driver is installed with the
+ * insmod command. It registers the dwc_otg_driver structure with the
+ * appropriate bus driver. This will cause the dwc_otg_driver_probe function
+ * to be called. In addition, the bus driver will automatically expose
+ * attributes defined for the device and driver in the special sysfs file
+ * system.
+ *
+ * @return
+ */
+static int __init dwc_otg_driver_init(void)
+{
+ int retval = 0;
+ int error;
+
+ printk(KERN_INFO "%s: version %s\n", dwc_driver_name, DWC_DRIVER_VERSION);
+
+ retval = platform_driver_register(&dwc_otg_driver);
+ if (retval) {
+ printk(KERN_ERR "%s retval=%d\n", __func__, retval);
+ return retval;
+ }
+
+ error = driver_create_file(&dwc_otg_driver.driver, &driver_attr_version);
+ error = driver_create_file(&dwc_otg_driver.driver, &driver_attr_debuglevel);
+
+ return retval;
+}
+module_init(dwc_otg_driver_init);
+
+/**
+ * This function is called when the driver is removed from the kernel
+ * with the rmmod command. The driver unregisters itself with its bus
+ * driver.
+ *
+ */
+static void __exit dwc_otg_driver_cleanup(void)
+{
+ printk(KERN_DEBUG "dwc_otg_driver_cleanup()\n");
+
+ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel);
+ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version);
+
+ platform_driver_unregister(&dwc_otg_driver);
+
+ printk(KERN_INFO "%s module removed\n", dwc_driver_name);
+}
+module_exit(dwc_otg_driver_cleanup);
+
+#endif
+
+MODULE_DESCRIPTION(DWC_DRIVER_DESC);
+MODULE_AUTHOR("Synopsys Inc.");
+MODULE_LICENSE("GPL");
+
+module_param_named(otg_cap, dwc_otg_module_params.otg_cap, int, 0444);
+MODULE_PARM_DESC(otg_cap, "OTG Capabilities 0=HNP&SRP 1=SRP Only 2=None");
+module_param_named(opt, dwc_otg_module_params.opt, int, 0444);
+MODULE_PARM_DESC(opt, "OPT Mode");
+module_param_named(dma_enable, dwc_otg_module_params.dma_enable, int, 0444);
+MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled");
+
+module_param_named(dma_desc_enable, dwc_otg_module_params.dma_desc_enable, int,
+ 0444);
+MODULE_PARM_DESC(dma_desc_enable,
+ "DMA Desc Mode 0=Address DMA 1=DMA Descriptor enabled");
+
+module_param_named(dma_burst_size, dwc_otg_module_params.dma_burst_size, int,
+ 0444);
+MODULE_PARM_DESC(dma_burst_size,
+ "DMA Burst Size 1, 4, 8, 16, 32, 64, 128, 256");
+module_param_named(speed, dwc_otg_module_params.speed, int, 0444);
+MODULE_PARM_DESC(speed, "Speed 0=High Speed 1=Full Speed");
+module_param_named(host_support_fs_ls_low_power,
+ dwc_otg_module_params.host_support_fs_ls_low_power, int,
+ 0444);
+MODULE_PARM_DESC(host_support_fs_ls_low_power,
+ "Support Low Power w/FS or LS 0=Support 1=Don't Support");
+module_param_named(host_ls_low_power_phy_clk,
+ dwc_otg_module_params.host_ls_low_power_phy_clk, int, 0444);
+MODULE_PARM_DESC(host_ls_low_power_phy_clk,
+ "Low Speed Low Power Clock 0=48Mhz 1=6Mhz");
+module_param_named(enable_dynamic_fifo,
+ dwc_otg_module_params.enable_dynamic_fifo, int, 0444);
+MODULE_PARM_DESC(enable_dynamic_fifo, "0=cC Setting 1=Allow Dynamic Sizing");
+module_param_named(data_fifo_size, dwc_otg_module_params.data_fifo_size, int,
+ 0444);
+MODULE_PARM_DESC(data_fifo_size,
+ "Total number of words in the data FIFO memory 32-32768");
+module_param_named(dev_rx_fifo_size, dwc_otg_module_params.dev_rx_fifo_size,
+ int, 0444);
+MODULE_PARM_DESC(dev_rx_fifo_size, "Number of words in the Rx FIFO 16-32768");
+module_param_named(dev_nperio_tx_fifo_size,
+ dwc_otg_module_params.dev_nperio_tx_fifo_size, int, 0444);
+MODULE_PARM_DESC(dev_nperio_tx_fifo_size,
+ "Number of words in the non-periodic Tx FIFO 16-32768");
+module_param_named(dev_perio_tx_fifo_size_1,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[0], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_1,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_2,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[1], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_2,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_3,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[2], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_3,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_4,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[3], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_4,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_5,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[4], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_5,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_6,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[5], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_6,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_7,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[6], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_7,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_8,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[7], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_8,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_9,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[8], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_9,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_10,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[9], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_10,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_11,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[10], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_11,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_12,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[11], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_12,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_13,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[12], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_13,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_14,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[13], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_14,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_15,
+ dwc_otg_module_params.dev_perio_tx_fifo_size[14], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_15,
+ "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(host_rx_fifo_size, dwc_otg_module_params.host_rx_fifo_size,
+ int, 0444);
+MODULE_PARM_DESC(host_rx_fifo_size, "Number of words in the Rx FIFO 16-32768");
+module_param_named(host_nperio_tx_fifo_size,
+ dwc_otg_module_params.host_nperio_tx_fifo_size, int, 0444);
+MODULE_PARM_DESC(host_nperio_tx_fifo_size,
+ "Number of words in the non-periodic Tx FIFO 16-32768");
+module_param_named(host_perio_tx_fifo_size,
+ dwc_otg_module_params.host_perio_tx_fifo_size, int, 0444);
+MODULE_PARM_DESC(host_perio_tx_fifo_size,
+ "Number of words in the host periodic Tx FIFO 16-32768");
+module_param_named(max_transfer_size, dwc_otg_module_params.max_transfer_size,
+ int, 0444);
+/** @todo Set the max to 512K, modify checks */
+MODULE_PARM_DESC(max_transfer_size,
+ "The maximum transfer size supported in bytes 2047-65535");
+module_param_named(max_packet_count, dwc_otg_module_params.max_packet_count,
+ int, 0444);
+MODULE_PARM_DESC(max_packet_count,
+ "The maximum number of packets in a transfer 15-511");
+module_param_named(host_channels, dwc_otg_module_params.host_channels, int,
+ 0444);
+MODULE_PARM_DESC(host_channels,
+ "The number of host channel registers to use 1-16");
+module_param_named(dev_endpoints, dwc_otg_module_params.dev_endpoints, int,
+ 0444);
+MODULE_PARM_DESC(dev_endpoints,
+ "The number of endpoints in addition to EP0 available for device mode 1-15");
+module_param_named(phy_type, dwc_otg_module_params.phy_type, int, 0444);
+MODULE_PARM_DESC(phy_type, "0=Reserved 1=UTMI+ 2=ULPI");
+module_param_named(phy_utmi_width, dwc_otg_module_params.phy_utmi_width, int,
+ 0444);
+MODULE_PARM_DESC(phy_utmi_width, "Specifies the UTMI+ Data Width 8 or 16 bits");
+module_param_named(phy_ulpi_ddr, dwc_otg_module_params.phy_ulpi_ddr, int, 0444);
+MODULE_PARM_DESC(phy_ulpi_ddr,
+ "ULPI at double or single data rate 0=Single 1=Double");
+module_param_named(phy_ulpi_ext_vbus, dwc_otg_module_params.phy_ulpi_ext_vbus,
+ int, 0444);
+MODULE_PARM_DESC(phy_ulpi_ext_vbus,
+ "ULPI PHY using internal or external vbus 0=Internal");
+module_param_named(i2c_enable, dwc_otg_module_params.i2c_enable, int, 0444);
+MODULE_PARM_DESC(i2c_enable, "FS PHY Interface");
+module_param_named(ulpi_fs_ls, dwc_otg_module_params.ulpi_fs_ls, int, 0444);
+MODULE_PARM_DESC(ulpi_fs_ls, "ULPI PHY FS/LS mode only");
+module_param_named(ts_dline, dwc_otg_module_params.ts_dline, int, 0444);
+MODULE_PARM_DESC(ts_dline, "Term select Dline pulsing for all PHYs");
+module_param_named(debug, g_dbg_lvl, int, 0444);
+MODULE_PARM_DESC(debug, "");
+
+module_param_named(en_multiple_tx_fifo,
+ dwc_otg_module_params.en_multiple_tx_fifo, int, 0444);
+MODULE_PARM_DESC(en_multiple_tx_fifo,
+ "Dedicated Non Periodic Tx FIFOs 0=disabled 1=enabled");
+module_param_named(dev_tx_fifo_size_1,
+ dwc_otg_module_params.dev_tx_fifo_size[0], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_1, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_2,
+ dwc_otg_module_params.dev_tx_fifo_size[1], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_2, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_3,
+ dwc_otg_module_params.dev_tx_fifo_size[2], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_3, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_4,
+ dwc_otg_module_params.dev_tx_fifo_size[3], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_4, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_5,
+ dwc_otg_module_params.dev_tx_fifo_size[4], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_5, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_6,
+ dwc_otg_module_params.dev_tx_fifo_size[5], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_6, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_7,
+ dwc_otg_module_params.dev_tx_fifo_size[6], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_7, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_8,
+ dwc_otg_module_params.dev_tx_fifo_size[7], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_8, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_9,
+ dwc_otg_module_params.dev_tx_fifo_size[8], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_9, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_10,
+ dwc_otg_module_params.dev_tx_fifo_size[9], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_10, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_11,
+ dwc_otg_module_params.dev_tx_fifo_size[10], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_11, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_12,
+ dwc_otg_module_params.dev_tx_fifo_size[11], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_12, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_13,
+ dwc_otg_module_params.dev_tx_fifo_size[12], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_13, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_14,
+ dwc_otg_module_params.dev_tx_fifo_size[13], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_14, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_15,
+ dwc_otg_module_params.dev_tx_fifo_size[14], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_15, "Number of words in the Tx FIFO 4-768");
+
+module_param_named(thr_ctl, dwc_otg_module_params.thr_ctl, int, 0444);
+MODULE_PARM_DESC(thr_ctl,
+ "Thresholding enable flag bit 0 - non ISO Tx thr., 1 - ISO Tx thr., 2 - Rx thr.- bit 0=disabled 1=enabled");
+module_param_named(tx_thr_length, dwc_otg_module_params.tx_thr_length, int,
+ 0444);
+MODULE_PARM_DESC(tx_thr_length, "Tx Threshold length in 32 bit DWORDs");
+module_param_named(rx_thr_length, dwc_otg_module_params.rx_thr_length, int,
+ 0444);
+MODULE_PARM_DESC(rx_thr_length, "Rx Threshold length in 32 bit DWORDs");
+
+module_param_named(pti_enable, dwc_otg_module_params.pti_enable, int, 0444);
+module_param_named(mpi_enable, dwc_otg_module_params.mpi_enable, int, 0444);
+module_param_named(lpm_enable, dwc_otg_module_params.lpm_enable, int, 0444);
+MODULE_PARM_DESC(lpm_enable, "LPM Enable 0=LPM Disabled 1=LPM Enabled");
+module_param_named(ic_usb_cap, dwc_otg_module_params.ic_usb_cap, int, 0444);
+MODULE_PARM_DESC(ic_usb_cap,
+ "IC_USB Capability 0=IC_USB Disabled 1=IC_USB Enabled");
+module_param_named(ahb_thr_ratio, dwc_otg_module_params.ahb_thr_ratio, int,
+ 0444);
+MODULE_PARM_DESC(ahb_thr_ratio, "AHB Threshold Ratio");
+module_param_named(power_down, dwc_otg_module_params.power_down, int, 0444);
+MODULE_PARM_DESC(power_down, "Power Down Mode");
+module_param_named(reload_ctl, dwc_otg_module_params.reload_ctl, int, 0444);
+MODULE_PARM_DESC(reload_ctl, "HFIR Reload Control");
+module_param_named(dev_out_nak, dwc_otg_module_params.dev_out_nak, int, 0444);
+MODULE_PARM_DESC(dev_out_nak, "Enable Device OUT NAK");
+module_param_named(cont_on_bna, dwc_otg_module_params.cont_on_bna, int, 0444);
+MODULE_PARM_DESC(cont_on_bna, "Enable Enable Continue on BNA");
+module_param_named(ahb_single, dwc_otg_module_params.ahb_single, int, 0444);
+MODULE_PARM_DESC(ahb_single, "Enable AHB Single Support");
+module_param_named(adp_enable, dwc_otg_module_params.adp_enable, int, 0444);
+MODULE_PARM_DESC(adp_enable, "ADP Enable 0=ADP Disabled 1=ADP Enabled");
+module_param_named(otg_ver, dwc_otg_module_params.otg_ver, int, 0444);
+MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0");
+
+/** @page "Module Parameters"
+ *
+ * The following parameters may be specified when starting the module.
+ * These parameters define how the DWC_otg controller should be
+ * configured. Parameter values are passed to the CIL initialization
+ * function dwc_otg_cil_init
+ *
+ * Example: <code>modprobe dwc_otg speed=1 otg_cap=1</code>
+ *
+
+ <table>
+ <tr><td>Parameter Name</td><td>Meaning</td></tr>
+
+ <tr>
+ <td>otg_cap</td>
+ <td>Specifies the OTG capabilities. The driver will automatically detect the
+ value for this parameter if none is specified.
+ - 0: HNP and SRP capable (default, if available)
+ - 1: SRP Only capable
+ - 2: No HNP/SRP capable
+ </td></tr>
+
+ <tr>
+ <td>dma_enable</td>
+ <td>Specifies whether to use slave or DMA mode for accessing the data FIFOs.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: Slave
+ - 1: DMA (default, if available)
+ </td></tr>
+
+ <tr>
+ <td>dma_burst_size</td>
+ <td>The DMA Burst size (applicable only for External DMA Mode).
+ - Values: 1, 4, 8 16, 32, 64, 128, 256 (default 32)
+ </td></tr>
+
+ <tr>
+ <td>speed</td>
+ <td>Specifies the maximum speed of operation in host and device mode. The
+ actual speed depends on the speed of the attached device and the value of
+ phy_type.
+ - 0: High Speed (default)
+ - 1: Full Speed
+ </td></tr>
+
+ <tr>
+ <td>host_support_fs_ls_low_power</td>
+ <td>Specifies whether low power mode is supported when attached to a Full
+ Speed or Low Speed device in host mode.
+ - 0: Don't support low power mode (default)
+ - 1: Support low power mode
+ </td></tr>
+
+ <tr>
+ <td>host_ls_low_power_phy_clk</td>
+ <td>Specifies the PHY clock rate in low power mode when connected to a Low
+ Speed device in host mode. This parameter is applicable only if
+ HOST_SUPPORT_FS_LS_LOW_POWER is enabled.
+ - 0: 48 MHz (default)
+ - 1: 6 MHz
+ </td></tr>
+
+ <tr>
+ <td>enable_dynamic_fifo</td>
+ <td> Specifies whether FIFOs may be resized by the driver software.
+ - 0: Use cC FIFO size parameters
+ - 1: Allow dynamic FIFO sizing (default)
+ </td></tr>
+
+ <tr>
+ <td>data_fifo_size</td>
+ <td>Total number of 4-byte words in the data FIFO memory. This memory
+ includes the Rx FIFO, non-periodic Tx FIFO, and periodic Tx FIFOs.
+ - Values: 32 to 32768 (default 8192)
+
+ Note: The total FIFO memory depth in the FPGA configuration is 8192.
+ </td></tr>
+
+ <tr>
+ <td>dev_rx_fifo_size</td>
+ <td>Number of 4-byte words in the Rx FIFO in device mode when dynamic
+ FIFO sizing is enabled.
+ - Values: 16 to 32768 (default 1064)
+ </td></tr>
+
+ <tr>
+ <td>dev_nperio_tx_fifo_size</td>
+ <td>Number of 4-byte words in the non-periodic Tx FIFO in device mode when
+ dynamic FIFO sizing is enabled.
+ - Values: 16 to 32768 (default 1024)
+ </td></tr>
+
+ <tr>
+ <td>dev_perio_tx_fifo_size_n (n = 1 to 15)</td>
+ <td>Number of 4-byte words in each of the periodic Tx FIFOs in device mode
+ when dynamic FIFO sizing is enabled.
+ - Values: 4 to 768 (default 256)
+ </td></tr>
+
+ <tr>
+ <td>host_rx_fifo_size</td>
+ <td>Number of 4-byte words in the Rx FIFO in host mode when dynamic FIFO
+ sizing is enabled.
+ - Values: 16 to 32768 (default 1024)
+ </td></tr>
+
+ <tr>
+ <td>host_nperio_tx_fifo_size</td>
+ <td>Number of 4-byte words in the non-periodic Tx FIFO in host mode when
+ dynamic FIFO sizing is enabled in the core.
+ - Values: 16 to 32768 (default 1024)
+ </td></tr>
+
+ <tr>
+ <td>host_perio_tx_fifo_size</td>
+ <td>Number of 4-byte words in the host periodic Tx FIFO when dynamic FIFO
+ sizing is enabled.
+ - Values: 16 to 32768 (default 1024)
+ </td></tr>
+
+ <tr>
+ <td>max_transfer_size</td>
+ <td>The maximum transfer size supported in bytes.
+ - Values: 2047 to 65,535 (default 65,535)
+ </td></tr>
+
+ <tr>
+ <td>max_packet_count</td>
+ <td>The maximum number of packets in a transfer.
+ - Values: 15 to 511 (default 511)
+ </td></tr>
+
+ <tr>
+ <td>host_channels</td>
+ <td>The number of host channel registers to use.
+ - Values: 1 to 16 (default 12)
+
+ Note: The FPGA configuration supports a maximum of 12 host channels.
+ </td></tr>
+
+ <tr>
+ <td>dev_endpoints</td>
+ <td>The number of endpoints in addition to EP0 available for device mode
+ operations.
+ - Values: 1 to 15 (default 6 IN and OUT)
+
+ Note: The FPGA configuration supports a maximum of 6 IN and OUT endpoints in
+ addition to EP0.
+ </td></tr>
+
+ <tr>
+ <td>phy_type</td>
+ <td>Specifies the type of PHY interface to use. By default, the driver will
+ automatically detect the phy_type.
+ - 0: Full Speed
+ - 1: UTMI+ (default, if available)
+ - 2: ULPI
+ </td></tr>
+
+ <tr>
+ <td>phy_utmi_width</td>
+ <td>Specifies the UTMI+ Data Width. This parameter is applicable for a
+ phy_type of UTMI+. Also, this parameter is applicable only if the
+ OTG_HSPHY_WIDTH cC parameter was set to "8 and 16 bits", meaning that the
+ core has been configured to work at either data path width.
+ - Values: 8 or 16 bits (default 16)
+ </td></tr>
+
+ <tr>
+ <td>phy_ulpi_ddr</td>
+ <td>Specifies whether the ULPI operates at double or single data rate. This
+ parameter is only applicable if phy_type is ULPI.
+ - 0: single data rate ULPI interface with 8 bit wide data bus (default)
+ - 1: double data rate ULPI interface with 4 bit wide data bus
+ </td></tr>
+
+ <tr>
+ <td>i2c_enable</td>
+ <td>Specifies whether to use the I2C interface for full speed PHY. This
+ parameter is only applicable if PHY_TYPE is FS.
+ - 0: Disabled (default)
+ - 1: Enabled
+ </td></tr>
+
+ <tr>
+ <td>ulpi_fs_ls</td>
+ <td>Specifies whether to use ULPI FS/LS mode only.
+ - 0: Disabled (default)
+ - 1: Enabled
+ </td></tr>
+
+ <tr>
+ <td>ts_dline</td>
+ <td>Specifies whether term select D-Line pulsing for all PHYs is enabled.
+ - 0: Disabled (default)
+ - 1: Enabled
+ </td></tr>
+
+ <tr>
+ <td>en_multiple_tx_fifo</td>
+ <td>Specifies whether dedicatedto tx fifos are enabled for non periodic IN EPs.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: Disabled
+ - 1: Enabled (default, if available)
+ </td></tr>
+
+ <tr>
+ <td>dev_tx_fifo_size_n (n = 1 to 15)</td>
+ <td>Number of 4-byte words in each of the Tx FIFOs in device mode
+ when dynamic FIFO sizing is enabled.
+ - Values: 4 to 768 (default 256)
+ </td></tr>
+
+ <tr>
+ <td>tx_thr_length</td>
+ <td>Transmit Threshold length in 32 bit double words
+ - Values: 8 to 128 (default 64)
+ </td></tr>
+
+ <tr>
+ <td>rx_thr_length</td>
+ <td>Receive Threshold length in 32 bit double words
+ - Values: 8 to 128 (default 64)
+ </td></tr>
+
+<tr>
+ <td>thr_ctl</td>
+ <td>Specifies whether to enable Thresholding for Device mode. Bits 0, 1, 2 of
+ this parmater specifies if thresholding is enabled for non-Iso Tx, Iso Tx and
+ Rx transfers accordingly.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - Values: 0 to 7 (default 0)
+ Bit values indicate:
+ - 0: Thresholding disabled
+ - 1: Thresholding enabled
+ </td></tr>
+
+<tr>
+ <td>dma_desc_enable</td>
+ <td>Specifies whether to enable Descriptor DMA mode.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: Descriptor DMA disabled
+ - 1: Descriptor DMA (default, if available)
+ </td></tr>
+
+<tr>
+ <td>mpi_enable</td>
+ <td>Specifies whether to enable MPI enhancement mode.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: MPI disabled (default)
+ - 1: MPI enable
+ </td></tr>
+
+<tr>
+ <td>pti_enable</td>
+ <td>Specifies whether to enable PTI enhancement support.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: PTI disabled (default)
+ - 1: PTI enable
+ </td></tr>
+
+<tr>
+ <td>lpm_enable</td>
+ <td>Specifies whether to enable LPM support.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: LPM disabled
+ - 1: LPM enable (default, if available)
+ </td></tr>
+
+<tr>
+ <td>ic_usb_cap</td>
+ <td>Specifies whether to enable IC_USB capability.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: IC_USB disabled (default, if available)
+ - 1: IC_USB enable
+ </td></tr>
+
+<tr>
+ <td>ahb_thr_ratio</td>
+ <td>Specifies AHB Threshold ratio.
+ - Values: 0 to 3 (default 0)
+ </td></tr>
+
+<tr>
+ <td>power_down</td>
+ <td>Specifies Power Down(Hibernation) Mode.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: Power Down disabled (default)
+ - 2: Power Down enabled
+ </td></tr>
+
+ <tr>
+ <td>reload_ctl</td>
+ <td>Specifies whether dynamic reloading of the HFIR register is allowed during
+ run time. The driver will automatically detect the value for this parameter if
+ none is specified. In case the HFIR value is reloaded when HFIR.RldCtrl == 1'b0
+ the core might misbehave.
+ - 0: Reload Control disabled (default)
+ - 1: Reload Control enabled
+ </td></tr>
+
+ <tr>
+ <td>dev_out_nak</td>
+ <td>Specifies whether Device OUT NAK enhancement enabled or no.
+ The driver will automatically detect the value for this parameter if
+ none is specified. This parameter is valid only when OTG_EN_DESC_DMA == 1Â’b1.
+ - 0: The core does not set NAK after Bulk OUT transfer complete (default)
+ - 1: The core sets NAK after Bulk OUT transfer complete
+ </td></tr>
+
+ <tr>
+ <td>cont_on_bna</td>
+ <td>Specifies whether Enable Continue on BNA enabled or no.
+ After receiving BNA interrupt the core disables the endpoint,when the
+ endpoint is re-enabled by the application the
+ - 0: Core starts processing from the DOEPDMA descriptor (default)
+ - 1: Core starts processing from the descriptor which received the BNA.
+ This parameter is valid only when OTG_EN_DESC_DMA == 1Â’b1.
+ </td></tr>
+
+ <tr>
+ <td>ahb_single</td>
+ <td>This bit when programmed supports SINGLE transfers for remainder data
+ in a transfer for DMA mode of operation.
+ - 0: The remainder data will be sent using INCR burst size (default)
+ - 1: The remainder data will be sent using SINGLE burst size.
+ </td></tr>
+
+<tr>
+ <td>adp_enable</td>
+ <td>Specifies whether ADP feature is enabled.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: ADP feature disabled (default)
+ - 1: ADP feature enabled
+ </td></tr>
+
+ <tr>
+ <td>otg_ver</td>
+ <td>Specifies whether OTG is performing as USB OTG Revision 2.0 or Revision 1.3
+ USB OTG device.
+ - 0: OTG 2.0 support disabled (default)
+ - 1: OTG 2.0 support enabled
+ </td></tr>
+
+*/
diff --git a/drivers/usb/dwc_otg/dwc_otg_driver.h b/drivers/usb/dwc_otg/dwc_otg_driver.h
new file mode 100644
index 0000000..7e3cf23
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_driver.h
@@ -0,0 +1,88 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.h $
+ * $Revision: #19 $
+ * $Date: 2010/11/15 $
+ * $Change: 1627671 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#ifndef __DWC_OTG_DRIVER_H__
+#define __DWC_OTG_DRIVER_H__
+
+/** @file
+ * This file contains the interface to the Linux driver.
+ */
+#include "dwc_otg_os_dep.h"
+#include "dwc_otg_core_if.h"
+
+/* Type declarations */
+struct dwc_otg_pcd;
+struct dwc_otg_hcd;
+
+/**
+ * This structure is a wrapper that encapsulates the driver components used to
+ * manage a single DWC_otg controller.
+ */
+typedef struct dwc_otg_device {
+ /** Structure containing OS-dependent stuff. KEEP THIS STRUCT AT THE
+ * VERY BEGINNING OF THE DEVICE STRUCT. OSes such as FreeBSD and NetBSD
+ * require this. */
+ struct os_dependent os_dep;
+
+ /** Pointer to the core interface structure. */
+ dwc_otg_core_if_t *core_if;
+
+ /** Pointer to the PCD structure. */
+ struct dwc_otg_pcd *pcd;
+
+ /** Pointer to the HCD structure. */
+ struct dwc_otg_hcd *hcd;
+
+ /** Flag to indicate whether the common IRQ handler is installed. */
+ uint8_t common_irq_installed;
+
+ /* Interrupt request number. */
+ unsigned int irq;
+} dwc_otg_device_t;
+
+/*We must clear S3C24XX_EINTPEND external interrupt register
+ * because after clearing in this register trigerred IRQ from
+ * H/W core in kernel interrupt can be occured again before OTG
+ * handlers clear all IRQ sources of Core registers because of
+ * timing latencies and Low Level IRQ Type.
+ */
+#ifdef CONFIG_MACH_IPMATE
+#define S3C2410X_CLEAR_EINTPEND() \
+do { \
+ __raw_writel(1UL << 11,S3C24XX_EINTPEND); \
+} while (0)
+#else
+#define S3C2410X_CLEAR_EINTPEND() do { } while (0)
+#endif
+
+#endif
diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd.c b/drivers/usb/dwc_otg/dwc_otg_hcd.c
new file mode 100644
index 0000000..d49f475
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd.c
@@ -0,0 +1,3348 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.c $
+ * $Revision: #104 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871159 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+/** @file
+ * This file implements HCD Core. All code in this file is portable and doesn't
+ * use any OS specific functions.
+ * Interface provided by HCD Core is defined in <code><hcd_if.h></code>
+ * header file.
+ */
+
+#include "dwc_otg_hcd.h"
+#include "dwc_otg_regs.h"
+
+dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void)
+{
+ return DWC_ALLOC(sizeof(dwc_otg_hcd_t));
+}
+
+/**
+ * Connection timeout function. An OTG host is required to display a
+ * message if the device does not connect within 10 seconds.
+ */
+void dwc_otg_hcd_connect_timeout(void *ptr)
+{
+ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, ptr);
+ DWC_PRINTF("Connect Timeout\n");
+ __DWC_ERROR("Device Not Connected/Responding\n");
+}
+
+#ifdef DEBUG
+static void dump_channel_info(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ if (qh->channel != NULL) {
+ dwc_hc_t *hc = qh->channel;
+ dwc_list_link_t *item;
+ dwc_otg_qh_t *qh_item;
+ int num_channels = hcd->core_if->core_params->host_channels;
+ int i;
+
+ dwc_otg_hc_regs_t *hc_regs;
+ hcchar_data_t hcchar;
+ hcsplt_data_t hcsplt;
+ hctsiz_data_t hctsiz;
+ uint32_t hcdma;
+
+ hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num];
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
+ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+ hcdma = DWC_READ_REG32(&hc_regs->hcdma);
+
+ DWC_PRINTF(" Assigned to channel %p:\n", hc);
+ DWC_PRINTF(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32,
+ hcsplt.d32);
+ DWC_PRINTF(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32,
+ hcdma);
+ DWC_PRINTF(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
+ hc->dev_addr, hc->ep_num, hc->ep_is_in);
+ DWC_PRINTF(" ep_type: %d\n", hc->ep_type);
+ DWC_PRINTF(" max_packet: %d\n", hc->max_packet);
+ DWC_PRINTF(" data_pid_start: %d\n", hc->data_pid_start);
+ DWC_PRINTF(" xfer_started: %d\n", hc->xfer_started);
+ DWC_PRINTF(" halt_status: %d\n", hc->halt_status);
+ DWC_PRINTF(" xfer_buff: %p\n", hc->xfer_buff);
+ DWC_PRINTF(" xfer_len: %d\n", hc->xfer_len);
+ DWC_PRINTF(" qh: %p\n", hc->qh);
+ DWC_PRINTF(" NP inactive sched:\n");
+ DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_inactive) {
+ qh_item =
+ DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry);
+ DWC_PRINTF(" %p\n", qh_item);
+ }
+ DWC_PRINTF(" NP active sched:\n");
+ DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_active) {
+ qh_item =
+ DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry);
+ DWC_PRINTF(" %p\n", qh_item);
+ }
+ DWC_PRINTF(" Channels: \n");
+ for (i = 0; i < num_channels; i++) {
+ dwc_hc_t *hc = hcd->hc_ptr_array[i];
+ DWC_PRINTF(" %2d: %p\n", i, hc);
+ }
+ }
+}
+#endif /* DEBUG */
+
+/**
+ * Work queue function for starting the HCD when A-Cable is connected.
+ * The hcd_start() must be called in a process context.
+ */
+static void hcd_start_func(void *_vp)
+{
+ dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) _vp;
+
+ DWC_DEBUGPL(DBG_HCDV, "%s() %p\n", __func__, hcd);
+ if (hcd) {
+ hcd->fops->start(hcd);
+ }
+}
+
+static void del_xfer_timers(dwc_otg_hcd_t * hcd)
+{
+#ifdef DEBUG
+ int i;
+ int num_channels = hcd->core_if->core_params->host_channels;
+ for (i = 0; i < num_channels; i++) {
+ DWC_TIMER_CANCEL(hcd->core_if->hc_xfer_timer[i]);
+ }
+#endif
+}
+
+static void del_timers(dwc_otg_hcd_t * hcd)
+{
+ del_xfer_timers(hcd);
+ DWC_TIMER_CANCEL(hcd->conn_timer);
+}
+
+/**
+ * Processes all the URBs in a single list of QHs. Completes them with
+ * -ETIMEDOUT and frees the QTD.
+ */
+static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
+{
+ dwc_list_link_t *qh_item;
+ dwc_otg_qh_t *qh;
+ dwc_otg_qtd_t *qtd, *qtd_tmp;
+
+ DWC_LIST_FOREACH(qh_item, qh_list) {
+ qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry);
+ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp,
+ &qh->qtd_list, qtd_list_entry) {
+ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+ if (qtd->urb != NULL) {
+ hcd->fops->complete(hcd, qtd->urb->priv,
+ qtd->urb, -DWC_E_TIMEOUT);
+ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+ }
+
+ }
+ }
+}
+
+/**
+ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic
+ * and periodic schedules. The QTD associated with each URB is removed from
+ * the schedule and freed. This function may be called when a disconnect is
+ * detected or when the HCD is being stopped.
+ */
+static void kill_all_urbs(dwc_otg_hcd_t * hcd)
+{
+ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive);
+ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned);
+ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued);
+}
+
+/**
+ * Start the connection timer. An OTG host is required to display a
+ * message if the device does not connect within 10 seconds. The
+ * timer is deleted if a port connect interrupt occurs before the
+ * timer expires.
+ */
+static void dwc_otg_hcd_start_connect_timer(dwc_otg_hcd_t * hcd)
+{
+ DWC_TIMER_SCHEDULE(hcd->conn_timer, 10000 /* 10 secs */ );
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int32_t dwc_otg_hcd_session_start_cb(void *p)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd;
+ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p);
+ dwc_otg_hcd = p;
+ dwc_otg_hcd_start_connect_timer(dwc_otg_hcd);
+ return 1;
+}
+
+/**
+ * HCD Callback function for starting the HCD when A-Cable is
+ * connected.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int32_t dwc_otg_hcd_start_cb(void *p)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = p;
+ dwc_otg_core_if_t *core_if;
+ hprt0_data_t hprt0;
+
+ core_if = dwc_otg_hcd->core_if;
+
+ if (core_if->op_state == B_HOST) {
+ /*
+ * Reset the port. During a HNP mode switch the reset
+ * needs to occur within 1ms and have a duration of at
+ * least 50ms.
+ */
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtrst = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ }
+ DWC_WORKQ_SCHEDULE_DELAYED(core_if->wq_otg,
+ hcd_start_func, dwc_otg_hcd, 50,
+ "start hcd");
+
+ return 1;
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int32_t dwc_otg_hcd_disconnect_cb(void *p)
+{
+ gintsts_data_t intr;
+ dwc_otg_hcd_t *dwc_otg_hcd = p;
+
+ /*
+ * Set status flags for the hub driver.
+ */
+ dwc_otg_hcd->flags.b.port_connect_status_change = 1;
+ dwc_otg_hcd->flags.b.port_connect_status = 0;
+
+ /*
+ * Shutdown any transfers in process by clearing the Tx FIFO Empty
+ * interrupt mask and status bits and disabling subsequent host
+ * channel interrupts.
+ */
+ intr.d32 = 0;
+ intr.b.nptxfempty = 1;
+ intr.b.ptxfempty = 1;
+ intr.b.hcintr = 1;
+ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk,
+ intr.d32, 0);
+ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintsts,
+ intr.d32, 0);
+
+ del_timers(dwc_otg_hcd);
+
+ /*
+ * Turn off the vbus power only if the core has transitioned to device
+ * mode. If still in host mode, need to keep power on to detect a
+ * reconnection.
+ */
+ if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) {
+ if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) {
+ hprt0_data_t hprt0 = {.d32 = 0 };
+ DWC_PRINTF("Disconnect: PortPower off\n");
+ hprt0.b.prtpwr = 0;
+ DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0,
+ hprt0.d32);
+ }
+
+ dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if);
+ }
+
+ /* Respond with an error status to all URBs in the schedule. */
+ kill_all_urbs(dwc_otg_hcd);
+
+ if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) {
+ /* Clean up any host channels that were in use. */
+ int num_channels;
+ int i;
+ dwc_hc_t *channel;
+ dwc_otg_hc_regs_t *hc_regs;
+ hcchar_data_t hcchar;
+
+ num_channels = dwc_otg_hcd->core_if->core_params->host_channels;
+
+ if (!dwc_otg_hcd->core_if->dma_enable) {
+ /* Flush out any channel requests in slave mode. */
+ for (i = 0; i < num_channels; i++) {
+ channel = dwc_otg_hcd->hc_ptr_array[i];
+ if (DWC_CIRCLEQ_EMPTY_ENTRY
+ (channel, hc_list_entry)) {
+ hc_regs =
+ dwc_otg_hcd->core_if->
+ host_if->hc_regs[i];
+ hcchar.d32 =
+ DWC_READ_REG32(&hc_regs->hcchar);
+ if (hcchar.b.chen) {
+ hcchar.b.chen = 0;
+ hcchar.b.chdis = 1;
+ hcchar.b.epdir = 0;
+ DWC_WRITE_REG32
+ (&hc_regs->hcchar,
+ hcchar.d32);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < num_channels; i++) {
+ channel = dwc_otg_hcd->hc_ptr_array[i];
+ if (DWC_CIRCLEQ_EMPTY_ENTRY(channel, hc_list_entry)) {
+ hc_regs =
+ dwc_otg_hcd->core_if->host_if->hc_regs[i];
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ if (hcchar.b.chen) {
+ /* Halt the channel. */
+ hcchar.b.chdis = 1;
+ DWC_WRITE_REG32(&hc_regs->hcchar,
+ hcchar.d32);
+ }
+
+ dwc_otg_hc_cleanup(dwc_otg_hcd->core_if,
+ channel);
+ DWC_CIRCLEQ_INSERT_TAIL
+ (&dwc_otg_hcd->free_hc_list, channel,
+ hc_list_entry);
+ /*
+ * Added for Descriptor DMA to prevent channel double cleanup
+ * in release_channel_ddma(). Which called from ep_disable
+ * when device disconnect.
+ */
+ channel->qh = NULL;
+ }
+ }
+ }
+
+ if (dwc_otg_hcd->fops->disconnect) {
+ dwc_otg_hcd->fops->disconnect(dwc_otg_hcd);
+ }
+
+ return 1;
+}
+
+/**
+ * HCD Callback function for stopping the HCD.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int32_t dwc_otg_hcd_stop_cb(void *p)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = p;
+
+ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p);
+ dwc_otg_hcd_stop(dwc_otg_hcd);
+ return 1;
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/**
+ * HCD Callback function for sleep of HCD.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int dwc_otg_hcd_sleep_cb(void *p)
+{
+ dwc_otg_hcd_t *hcd = p;
+
+ dwc_otg_hcd_free_hc_from_lpm(hcd);
+
+ return 0;
+}
+#endif
+
+/**
+ * HCD Callback function for Remote Wakeup.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int dwc_otg_hcd_rem_wakeup_cb(void *p)
+{
+ dwc_otg_hcd_t *hcd = p;
+
+ if (hcd->core_if->lx_state == DWC_OTG_L2) {
+ hcd->flags.b.port_suspend_change = 1;
+ }
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ else {
+ hcd->flags.b.port_l1_change = 1;
+ }
+#endif
+ return 0;
+}
+
+/**
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ */
+void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd)
+{
+ hprt0_data_t hprt0 = {.d32 = 0 };
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n");
+
+ /*
+ * The root hub should be disconnected before this function is called.
+ * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue)
+ * and the QH lists (via ..._hcd_endpoint_disable).
+ */
+
+ /* Turn off all host-specific interrupts. */
+ dwc_otg_disable_host_interrupts(hcd->core_if);
+
+ /* Turn off the vbus power */
+ DWC_PRINTF("PortPower off\n");
+ hprt0.b.prtpwr = 0;
+ DWC_WRITE_REG32(hcd->core_if->host_if->hprt0, hprt0.d32);
+ dwc_mdelay(1);
+}
+
+int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd,
+ dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle,
+ int atomic_alloc)
+{
+ dwc_irqflags_t flags;
+ int retval = 0;
+ dwc_otg_qtd_t *qtd;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ if (!hcd->flags.b.port_connect_status) {
+ /* No longer connected. */
+ DWC_ERROR("Not connected\n");
+ return -DWC_E_NO_DEVICE;
+ }
+
+ qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc);
+ if (qtd == NULL) {
+ DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n");
+ return -DWC_E_NO_MEMORY;
+ }
+
+ retval =
+ dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc);
+ if (retval < 0) {
+ DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. "
+ "Error status %d\n", retval);
+ dwc_otg_hcd_qtd_free(qtd);
+ } else {
+ qtd->qh = *ep_handle;
+ }
+ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
+ if (!intr_mask.b.sofintr && retval == 0) {
+ dwc_otg_transaction_type_e tr_type;
+ if ((qtd->qh->ep_type == UE_BULK)
+ && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) {
+ /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */
+ return 0;
+ }
+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+ tr_type = dwc_otg_hcd_select_transactions(hcd);
+ if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+ dwc_otg_hcd_queue_transactions(hcd, tr_type);
+ }
+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+ }
+
+ return retval;
+}
+
+int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
+ dwc_otg_hcd_urb_t * dwc_otg_urb)
+{
+ dwc_otg_qh_t *qh;
+ dwc_otg_qtd_t *urb_qtd;
+
+ urb_qtd = dwc_otg_urb->qtd;
+ qh = urb_qtd->qh;
+#ifdef DEBUG
+ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+ if (urb_qtd->in_process) {
+ dump_channel_info(hcd, qh);
+ }
+ }
+#endif
+ if (urb_qtd->in_process && qh->channel) {
+ /* The QTD is in process (it has been assigned to a channel). */
+ if (hcd->flags.b.port_connect_status) {
+ /*
+ * If still connected (i.e. in host mode), halt the
+ * channel so it can be used for other transfers. If
+ * no longer connected, the host registers can't be
+ * written to halt the channel since the core is in
+ * device mode.
+ */
+ dwc_otg_hc_halt(hcd->core_if, qh->channel,
+ DWC_OTG_HC_XFER_URB_DEQUEUE);
+ }
+ }
+
+ /*
+ * Free the QTD and clean up the associated QH. Leave the QH in the
+ * schedule if it has any remaining QTDs.
+ */
+
+ if (!hcd->core_if->dma_desc_enable) {
+ uint8_t b = urb_qtd->in_process;
+ dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh);
+ if (b) {
+ dwc_otg_hcd_qh_deactivate(hcd, qh, 0);
+ qh->channel = NULL;
+ } else if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ }
+ } else {
+ dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh);
+ }
+ return 0;
+}
+
+int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle,
+ int retry)
+{
+ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+ int retval = 0;
+ dwc_irqflags_t flags;
+
+ if (retry < 0) {
+ retval = -DWC_E_INVALID;
+ goto done;
+ }
+
+ if (!qh) {
+ retval = -DWC_E_INVALID;
+ goto done;
+ }
+
+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+
+ while (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list) && retry) {
+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+ retry--;
+ dwc_msleep(5);
+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+ }
+
+ dwc_otg_hcd_qh_remove(hcd, qh);
+
+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+ /*
+ * Split dwc_otg_hcd_qh_remove_and_free() into qh_remove
+ * and qh_free to prevent stack dump on DWC_DMA_FREE() with
+ * irq_disabled (spinlock_irqsave) in dwc_otg_hcd_desc_list_free()
+ * and dwc_otg_hcd_frame_list_alloc().
+ */
+ dwc_otg_hcd_qh_free(hcd, qh);
+
+done:
+ return retval;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+int dwc_otg_hcd_endpoint_reset(dwc_otg_hcd_t * hcd, void *ep_handle)
+{
+ int retval = 0;
+ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+ if (!qh)
+ return -DWC_E_INVALID;
+
+ qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+ return retval;
+}
+#endif
+
+/**
+ * HCD Callback structure for handling mode switching.
+ */
+static dwc_otg_cil_callbacks_t hcd_cil_callbacks = {
+ .start = dwc_otg_hcd_start_cb,
+ .stop = dwc_otg_hcd_stop_cb,
+ .disconnect = dwc_otg_hcd_disconnect_cb,
+ .session_start = dwc_otg_hcd_session_start_cb,
+ .resume_wakeup = dwc_otg_hcd_rem_wakeup_cb,
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ .sleep = dwc_otg_hcd_sleep_cb,
+#endif
+ .p = 0,
+};
+
+/**
+ * Reset tasklet function
+ */
+static void reset_tasklet_func(void *data)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *) data;
+ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+ hprt0_data_t hprt0;
+
+ DWC_DEBUGPL(DBG_HCDV, "USB RESET tasklet called\n");
+
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtrst = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ dwc_mdelay(60);
+
+ hprt0.b.prtrst = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ dwc_otg_hcd->flags.b.port_reset_change = 1;
+}
+
+static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
+{
+ dwc_list_link_t *item;
+ dwc_otg_qh_t *qh;
+ dwc_irqflags_t flags;
+
+ if (!qh_list->next) {
+ /* The list hasn't been initialized yet. */
+ return;
+ }
+ /*
+ * Hold spinlock here. Not needed in that case if bellow
+ * function is being called from ISR
+ */
+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+ /* Ensure there are no QTDs or URBs left. */
+ kill_urbs_in_qh_list(hcd, qh_list);
+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+
+ DWC_LIST_FOREACH(item, qh_list) {
+ qh = DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry);
+ dwc_otg_hcd_qh_remove_and_free(hcd, qh);
+ }
+}
+
+/**
+ * Exit from Hibernation if Host did not detect SRP from connected SRP capable
+ * Device during SRP time by host power up.
+ */
+void dwc_otg_hcd_power_up(void *ptr)
+{
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
+
+ DWC_PRINTF("%s called\n", __FUNCTION__);
+
+ if (!core_if->hibernation_suspend) {
+ DWC_PRINTF("Already exited from Hibernation\n");
+ return;
+ }
+
+ /* Switch on the voltage to the core */
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Reset the core */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Disable power clamps */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ /* Remove reset the core signal */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnrstn = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ core_if->hibernation_suspend = 0;
+
+ /* Disable PMU */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+ dwc_udelay(10);
+
+ /* Enable VBUS */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.dis_vbus = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+ core_if->op_state = A_HOST;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_hcd_start(core_if);
+}
+
+/**
+ * Frees secondary storage associated with the dwc_otg_hcd structure contained
+ * in the struct usb_hcd field.
+ */
+static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+ int i;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n");
+
+ del_timers(dwc_otg_hcd);
+
+ /* Free memory for QH/QTD lists */
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned);
+ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued);
+
+ /* Free memory for the host channels. */
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i];
+
+#ifdef DEBUG
+ if (dwc_otg_hcd->core_if->hc_xfer_timer[i]) {
+ DWC_TIMER_FREE(dwc_otg_hcd->core_if->hc_xfer_timer[i]);
+ }
+#endif
+ if (hc != NULL) {
+ DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n",
+ i, hc);
+ DWC_FREE(hc);
+ }
+ }
+
+ if (dwc_otg_hcd->core_if->dma_enable) {
+ if (dwc_otg_hcd->status_buf_dma) {
+ DWC_DMA_FREE(DWC_OTG_HCD_STATUS_BUF_SIZE,
+ dwc_otg_hcd->status_buf,
+ dwc_otg_hcd->status_buf_dma);
+ }
+ } else if (dwc_otg_hcd->status_buf != NULL) {
+ DWC_FREE(dwc_otg_hcd->status_buf);
+ }
+ DWC_SPINLOCK_FREE(dwc_otg_hcd->lock);
+ /* Set core_if's lock pointer to NULL */
+ dwc_otg_hcd->core_if->lock = NULL;
+
+ DWC_TIMER_FREE(dwc_otg_hcd->conn_timer);
+ DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet);
+
+#ifdef DWC_DEV_SRPCAP
+ if (dwc_otg_hcd->core_if->power_down == 2 &&
+ dwc_otg_hcd->core_if->pwron_timer) {
+ DWC_TIMER_FREE(dwc_otg_hcd->core_if->pwron_timer);
+ }
+#endif
+ DWC_FREE(dwc_otg_hcd);
+}
+
+int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
+{
+ int retval = 0;
+ int num_channels;
+ int i;
+ dwc_hc_t *channel;
+
+ hcd->lock = DWC_SPINLOCK_ALLOC();
+ if (!hcd->lock) {
+ DWC_ERROR("Could not allocate lock for pcd");
+ DWC_FREE(hcd);
+ retval = -DWC_E_NO_MEMORY;
+ goto out;
+ }
+ hcd->core_if = core_if;
+
+ /* Register the HCD CIL Callbacks */
+ dwc_otg_cil_register_hcd_callbacks(hcd->core_if,
+ &hcd_cil_callbacks, hcd);
+
+ /* Initialize the non-periodic schedule. */
+ DWC_LIST_INIT(&hcd->non_periodic_sched_inactive);
+ DWC_LIST_INIT(&hcd->non_periodic_sched_active);
+
+ /* Initialize the periodic schedule. */
+ DWC_LIST_INIT(&hcd->periodic_sched_inactive);
+ DWC_LIST_INIT(&hcd->periodic_sched_ready);
+ DWC_LIST_INIT(&hcd->periodic_sched_assigned);
+ DWC_LIST_INIT(&hcd->periodic_sched_queued);
+
+ /*
+ * Create a host channel descriptor for each host channel implemented
+ * in the controller. Initialize the channel descriptor array.
+ */
+ DWC_CIRCLEQ_INIT(&hcd->free_hc_list);
+ num_channels = hcd->core_if->core_params->host_channels;
+ DWC_MEMSET(hcd->hc_ptr_array, 0, sizeof(hcd->hc_ptr_array));
+ for (i = 0; i < num_channels; i++) {
+ channel = DWC_ALLOC(sizeof(dwc_hc_t));
+ if (channel == NULL) {
+ retval = -DWC_E_NO_MEMORY;
+ DWC_ERROR("%s: host channel allocation failed\n",
+ __func__);
+ dwc_otg_hcd_free(hcd);
+ goto out;
+ }
+ channel->hc_num = i;
+ hcd->hc_ptr_array[i] = channel;
+#ifdef DEBUG
+ hcd->core_if->hc_xfer_timer[i] =
+ DWC_TIMER_ALLOC("hc timer", hc_xfer_timeout,
+ &hcd->core_if->hc_xfer_info[i]);
+#endif
+ DWC_DEBUGPL(DBG_HCDV, "HCD Added channel #%d, hc=%p\n", i,
+ channel);
+ }
+
+ /* Initialize the Connection timeout timer. */
+ hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer",
+ dwc_otg_hcd_connect_timeout, 0);
+
+ /* Initialize reset tasklet. */
+ hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd);
+#ifdef DWC_DEV_SRPCAP
+ if (hcd->core_if->power_down == 2) {
+ /* Initialize Power on timer for Host power up in case hibernation */
+ hcd->core_if->pwron_timer = DWC_TIMER_ALLOC("PWRON TIMER",
+ dwc_otg_hcd_power_up, core_if);
+ }
+#endif
+
+ /*
+ * Allocate space for storing data on status transactions. Normally no
+ * data is sent, but this space acts as a bit bucket. This must be
+ * done after usb_add_hcd since that function allocates the DMA buffer
+ * pool.
+ */
+ if (hcd->core_if->dma_enable) {
+ hcd->status_buf =
+ DWC_DMA_ALLOC(DWC_OTG_HCD_STATUS_BUF_SIZE,
+ &hcd->status_buf_dma);
+ } else {
+ hcd->status_buf = DWC_ALLOC(DWC_OTG_HCD_STATUS_BUF_SIZE);
+ }
+ if (!hcd->status_buf) {
+ retval = -DWC_E_NO_MEMORY;
+ DWC_ERROR("%s: status_buf allocation failed\n", __func__);
+ dwc_otg_hcd_free(hcd);
+ goto out;
+ }
+
+ hcd->otg_port = 1;
+ hcd->frame_list = NULL;
+ hcd->frame_list_dma = 0;
+ hcd->periodic_qh_count = 0;
+out:
+ return retval;
+}
+
+void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd)
+{
+ /* Turn off all host-specific interrupts. */
+ dwc_otg_disable_host_interrupts(hcd->core_if);
+
+ dwc_otg_hcd_free(hcd);
+}
+
+/**
+ * Initializes dynamic portions of the DWC_otg HCD state.
+ */
+static void dwc_otg_hcd_reinit(dwc_otg_hcd_t * hcd)
+{
+ int num_channels;
+ int i;
+ dwc_hc_t *channel;
+ dwc_hc_t *channel_tmp;
+
+ hcd->flags.d32 = 0;
+
+ hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
+ hcd->non_periodic_channels = 0;
+ hcd->periodic_channels = 0;
+
+ /*
+ * Put all channels in the free channel list and clean up channel
+ * states.
+ */
+ DWC_CIRCLEQ_FOREACH_SAFE(channel, channel_tmp,
+ &hcd->free_hc_list, hc_list_entry) {
+ DWC_CIRCLEQ_REMOVE(&hcd->free_hc_list, channel, hc_list_entry);
+ }
+
+ num_channels = hcd->core_if->core_params->host_channels;
+ for (i = 0; i < num_channels; i++) {
+ channel = hcd->hc_ptr_array[i];
+ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, channel,
+ hc_list_entry);
+ dwc_otg_hc_cleanup(hcd->core_if, channel);
+ }
+
+ /* Initialize the DWC core for host mode operation. */
+ dwc_otg_core_host_init(hcd->core_if);
+
+ /* Set core_if's lock pointer to the hcd->lock */
+ hcd->core_if->lock = hcd->lock;
+}
+
+/**
+ * Assigns transactions from a QTD to a free host channel and initializes the
+ * host channel to perform the transactions. The host channel is removed from
+ * the free list.
+ *
+ * @param hcd The HCD state structure.
+ * @param qh Transactions from the first QTD for this QH are selected and
+ * assigned to a free host channel.
+ */
+static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ dwc_hc_t *hc;
+ dwc_otg_qtd_t *qtd;
+ dwc_otg_hcd_urb_t *urb;
+ void* ptr = NULL;
+
+ DWC_DEBUGPL(DBG_HCDV, "%s(%p,%p)\n", __func__, hcd, qh);
+
+ hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list);
+
+ /* Remove the host channel from the free list. */
+ DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry);
+
+ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+
+ urb = qtd->urb;
+ qh->channel = hc;
+
+ qtd->in_process = 1;
+
+ /*
+ * Use usb_pipedevice to determine device address. This address is
+ * 0 before the SET_ADDRESS command and the correct address afterward.
+ */
+ hc->dev_addr = dwc_otg_hcd_get_dev_addr(&urb->pipe_info);
+ hc->ep_num = dwc_otg_hcd_get_ep_num(&urb->pipe_info);
+ hc->speed = qh->dev_speed;
+ hc->max_packet = dwc_max_packet(qh->maxp);
+
+ hc->xfer_started = 0;
+ hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS;
+ hc->error_state = (qtd->error_count > 0);
+ hc->halt_on_queue = 0;
+ hc->halt_pending = 0;
+ hc->requests = 0;
+
+ /*
+ * The following values may be modified in the transfer type section
+ * below. The xfer_len value may be reduced when the transfer is
+ * started to accommodate the max widths of the XferSize and PktCnt
+ * fields in the HCTSIZn register.
+ */
+
+ hc->ep_is_in = (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) != 0);
+ if (hc->ep_is_in) {
+ hc->do_ping = 0;
+ } else {
+ hc->do_ping = qh->ping_state;
+ }
+
+ hc->data_pid_start = qh->data_toggle;
+ hc->multi_count = 1;
+
+ if (hcd->core_if->dma_enable) {
+ hc->xfer_buff = (uint8_t *) urb->dma + urb->actual_length;
+
+ /* For non-dword aligned case */
+ if (((unsigned long)hc->xfer_buff & 0x3)
+ && !hcd->core_if->dma_desc_enable) {
+ ptr = (uint8_t *) urb->buf + urb->actual_length;
+ }
+ } else {
+ hc->xfer_buff = (uint8_t *) urb->buf + urb->actual_length;
+ }
+ hc->xfer_len = urb->length - urb->actual_length;
+ hc->xfer_count = 0;
+
+ /*
+ * Set the split attributes
+ */
+ hc->do_split = 0;
+ if (qh->do_split) {
+ uint32_t hub_addr, port_addr;
+ hc->do_split = 1;
+ hc->xact_pos = qtd->isoc_split_pos;
+ hc->complete_split = qtd->complete_split;
+ hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr);
+ hc->hub_addr = (uint8_t) hub_addr;
+ hc->port_addr = (uint8_t) port_addr;
+ }
+
+ switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) {
+ case UE_CONTROL:
+ hc->ep_type = DWC_OTG_EP_TYPE_CONTROL;
+ switch (qtd->control_phase) {
+ case DWC_OTG_CONTROL_SETUP:
+ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction\n");
+ hc->do_ping = 0;
+ hc->ep_is_in = 0;
+ hc->data_pid_start = DWC_OTG_HC_PID_SETUP;
+ if (hcd->core_if->dma_enable) {
+ hc->xfer_buff = (uint8_t *) urb->setup_dma;
+ } else {
+ hc->xfer_buff = (uint8_t *) urb->setup_packet;
+ }
+ hc->xfer_len = 8;
+ ptr = NULL;
+ break;
+ case DWC_OTG_CONTROL_DATA:
+ DWC_DEBUGPL(DBG_HCDV, " Control data transaction\n");
+ hc->data_pid_start = qtd->data_toggle;
+ break;
+ case DWC_OTG_CONTROL_STATUS:
+ /*
+ * Direction is opposite of data direction or IN if no
+ * data.
+ */
+ DWC_DEBUGPL(DBG_HCDV, " Control status transaction\n");
+ if (urb->length == 0) {
+ hc->ep_is_in = 1;
+ } else {
+ hc->ep_is_in =
+ dwc_otg_hcd_is_pipe_out(&urb->pipe_info);
+ }
+ if (hc->ep_is_in) {
+ hc->do_ping = 0;
+ }
+
+ hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
+
+ hc->xfer_len = 0;
+ if (hcd->core_if->dma_enable) {
+ hc->xfer_buff = (uint8_t *) hcd->status_buf_dma;
+ } else {
+ hc->xfer_buff = (uint8_t *) hcd->status_buf;
+ }
+ ptr = NULL;
+ break;
+ }
+ break;
+ case UE_BULK:
+ hc->ep_type = DWC_OTG_EP_TYPE_BULK;
+ break;
+ case UE_INTERRUPT:
+ hc->ep_type = DWC_OTG_EP_TYPE_INTR;
+ break;
+ case UE_ISOCHRONOUS:
+ {
+ struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+
+ hc->ep_type = DWC_OTG_EP_TYPE_ISOC;
+
+ if (hcd->core_if->dma_desc_enable)
+ break;
+
+ frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
+
+ frame_desc->status = 0;
+
+ if (hcd->core_if->dma_enable) {
+ hc->xfer_buff = (uint8_t *) urb->dma;
+ } else {
+ hc->xfer_buff = (uint8_t *) urb->buf;
+ }
+ hc->xfer_buff +=
+ frame_desc->offset + qtd->isoc_split_offset;
+ hc->xfer_len =
+ frame_desc->length - qtd->isoc_split_offset;
+
+ /* For non-dword aligned buffers */
+ if (((unsigned long)hc->xfer_buff & 0x3)
+ && hcd->core_if->dma_enable) {
+ ptr =
+ (uint8_t *) urb->buf + frame_desc->offset +
+ qtd->isoc_split_offset;
+ } else
+ ptr = NULL;
+
+ if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) {
+ if (hc->xfer_len <= 188) {
+ hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL;
+ } else {
+ hc->xact_pos =
+ DWC_HCSPLIT_XACTPOS_BEGIN;
+ }
+ }
+ }
+ break;
+ }
+ /* non DWORD-aligned buffer case */
+ if (ptr) {
+ uint32_t buf_size;
+ if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+ buf_size = hcd->core_if->core_params->max_transfer_size;
+ } else {
+ buf_size = 4096;
+ }
+ if (!qh->dw_align_buf) {
+ qh->dw_align_buf = DWC_DMA_ALLOC_ATOMIC(buf_size,
+ &qh->dw_align_buf_dma);
+ if (!qh->dw_align_buf) {
+ DWC_ERROR
+ ("%s: Failed to allocate memory to handle "
+ "non-dword aligned buffer case\n",
+ __func__);
+ return;
+ }
+ }
+ if (!hc->ep_is_in) {
+ dwc_memcpy(qh->dw_align_buf, ptr, hc->xfer_len);
+ }
+ hc->align_buff = qh->dw_align_buf_dma;
+ } else {
+ hc->align_buff = 0;
+ }
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ /*
+ * This value may be modified when the transfer is started to
+ * reflect the actual transfer length.
+ */
+ hc->multi_count = dwc_hb_mult(qh->maxp);
+ }
+
+ if (hcd->core_if->dma_desc_enable)
+ hc->desc_list_addr = qh->desc_list_dma;
+
+ dwc_otg_hc_init(hcd->core_if, hc);
+ hc->qh = qh;
+}
+
+/**
+ * This function selects transactions from the HCD transfer schedule and
+ * assigns them to available host channels. It is called from HCD interrupt
+ * handler functions.
+ *
+ * @param hcd The HCD state structure.
+ *
+ * @return The types of new transactions that were assigned to host channels.
+ */
+dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
+{
+ dwc_list_link_t *qh_ptr;
+ dwc_otg_qh_t *qh;
+ int num_channels;
+ dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE;
+
+#ifdef DEBUG_SOF
+ DWC_DEBUGPL(DBG_HCD, " Select Transactions\n");
+#endif
+
+ /* Process entries in the periodic ready list. */
+ qh_ptr = DWC_LIST_FIRST(&hcd->periodic_sched_ready);
+
+ while (qh_ptr != &hcd->periodic_sched_ready &&
+ !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
+
+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+ assign_and_init_hc(hcd, qh);
+
+ /*
+ * Move the QH from the periodic ready schedule to the
+ * periodic assigned schedule.
+ */
+ qh_ptr = DWC_LIST_NEXT(qh_ptr);
+ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
+ &qh->qh_list_entry);
+
+ ret_val = DWC_OTG_TRANSACTION_PERIODIC;
+ }
+
+ /*
+ * Process entries in the inactive portion of the non-periodic
+ * schedule. Some free host channels may not be used if they are
+ * reserved for periodic transfers.
+ */
+ qh_ptr = hcd->non_periodic_sched_inactive.next;
+ num_channels = hcd->core_if->core_params->host_channels;
+ while (qh_ptr != &hcd->non_periodic_sched_inactive &&
+ (hcd->non_periodic_channels <
+ num_channels - hcd->periodic_channels) &&
+ !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
+
+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+
+ assign_and_init_hc(hcd, qh);
+
+ /*
+ * Move the QH from the non-periodic inactive schedule to the
+ * non-periodic active schedule.
+ */
+ qh_ptr = DWC_LIST_NEXT(qh_ptr);
+ DWC_LIST_MOVE_HEAD(&hcd->non_periodic_sched_active,
+ &qh->qh_list_entry);
+
+ if (ret_val == DWC_OTG_TRANSACTION_NONE) {
+ ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
+ } else {
+ ret_val = DWC_OTG_TRANSACTION_ALL;
+ }
+
+ hcd->non_periodic_channels++;
+ }
+
+ return ret_val;
+}
+
+/**
+ * Attempts to queue a single transaction request for a host channel
+ * associated with either a periodic or non-periodic transfer. This function
+ * assumes that there is space available in the appropriate request queue. For
+ * an OUT transfer or SETUP transaction in Slave mode, it checks whether space
+ * is available in the appropriate Tx FIFO.
+ *
+ * @param hcd The HCD state structure.
+ * @param hc Host channel descriptor associated with either a periodic or
+ * non-periodic transfer.
+ * @param fifo_dwords_avail Number of DWORDs available in the periodic Tx
+ * FIFO for periodic transfers or the non-periodic Tx FIFO for non-periodic
+ * transfers.
+ *
+ * @return 1 if a request is queued and more requests may be needed to
+ * complete the transfer, 0 if no more requests are required for this
+ * transfer, -1 if there is insufficient space in the Tx FIFO.
+ */
+static int queue_transaction(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc, uint16_t fifo_dwords_avail)
+{
+ int retval;
+
+ if (hcd->core_if->dma_enable) {
+ if (hcd->core_if->dma_desc_enable) {
+ if (!hc->xfer_started
+ || (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)) {
+ dwc_otg_hcd_start_xfer_ddma(hcd, hc->qh);
+ hc->qh->ping_state = 0;
+ }
+ } else if (!hc->xfer_started) {
+ dwc_otg_hc_start_transfer(hcd->core_if, hc);
+ hc->qh->ping_state = 0;
+ }
+ retval = 0;
+ } else if (hc->halt_pending) {
+ /* Don't queue a request if the channel has been halted. */
+ retval = 0;
+ } else if (hc->halt_on_queue) {
+ dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status);
+ retval = 0;
+ } else if (hc->do_ping) {
+ if (!hc->xfer_started) {
+ dwc_otg_hc_start_transfer(hcd->core_if, hc);
+ }
+ retval = 0;
+ } else if (!hc->ep_is_in || hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
+ if ((fifo_dwords_avail * 4) >= hc->max_packet) {
+ if (!hc->xfer_started) {
+ dwc_otg_hc_start_transfer(hcd->core_if, hc);
+ retval = 1;
+ } else {
+ retval =
+ dwc_otg_hc_continue_transfer(hcd->core_if,
+ hc);
+ }
+ } else {
+ retval = -1;
+ }
+ } else {
+ if (!hc->xfer_started) {
+ dwc_otg_hc_start_transfer(hcd->core_if, hc);
+ retval = 1;
+ } else {
+ retval = dwc_otg_hc_continue_transfer(hcd->core_if, hc);
+ }
+ }
+
+ return retval;
+}
+
+/**
+ * Processes periodic channels for the next frame and queues transactions for
+ * these channels to the DWC_otg controller. After queueing transactions, the
+ * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions
+ * to queue as Periodic Tx FIFO or request queue space becomes available.
+ * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled.
+ */
+static void process_periodic_channels(dwc_otg_hcd_t * hcd)
+{
+ hptxsts_data_t tx_status;
+ dwc_list_link_t *qh_ptr;
+ dwc_otg_qh_t *qh;
+ int status;
+ int no_queue_space = 0;
+ int no_fifo_space = 0;
+
+ dwc_otg_host_global_regs_t *host_regs;
+ host_regs = hcd->core_if->host_if->host_global_regs;
+
+ DWC_DEBUGPL(DBG_HCDV, "Queue periodic transactions\n");
+#ifdef DEBUG
+ tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts);
+ DWC_DEBUGPL(DBG_HCDV,
+ " P Tx Req Queue Space Avail (before queue): %d\n",
+ tx_status.b.ptxqspcavail);
+ DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (before queue): %d\n",
+ tx_status.b.ptxfspcavail);
+#endif
+
+ qh_ptr = hcd->periodic_sched_assigned.next;
+ while (qh_ptr != &hcd->periodic_sched_assigned) {
+ tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts);
+ if (tx_status.b.ptxqspcavail == 0) {
+ no_queue_space = 1;
+ break;
+ }
+
+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+
+ /*
+ * Set a flag if we're queuing high-bandwidth in slave mode.
+ * The flag prevents any halts to get into the request queue in
+ * the middle of multiple high-bandwidth packets getting queued.
+ */
+ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) {
+ hcd->core_if->queuing_high_bandwidth = 1;
+ }
+ status =
+ queue_transaction(hcd, qh->channel,
+ tx_status.b.ptxfspcavail);
+ if (status < 0) {
+ no_fifo_space = 1;
+ break;
+ }
+
+ /*
+ * In Slave mode, stay on the current transfer until there is
+ * nothing more to do or the high-bandwidth request count is
+ * reached. In DMA mode, only need to queue one request. The
+ * controller automatically handles multiple packets for
+ * high-bandwidth transfers.
+ */
+ if (hcd->core_if->dma_enable || status == 0 ||
+ qh->channel->requests == qh->channel->multi_count) {
+ qh_ptr = qh_ptr->next;
+ /*
+ * Move the QH from the periodic assigned schedule to
+ * the periodic queued schedule.
+ */
+ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_queued,
+ &qh->qh_list_entry);
+
+ /* done queuing high bandwidth */
+ hcd->core_if->queuing_high_bandwidth = 0;
+ }
+ }
+
+ if (!hcd->core_if->dma_enable) {
+ dwc_otg_core_global_regs_t *global_regs;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ global_regs = hcd->core_if->core_global_regs;
+ intr_mask.b.ptxfempty = 1;
+#ifdef DEBUG
+ tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts);
+ DWC_DEBUGPL(DBG_HCDV,
+ " P Tx Req Queue Space Avail (after queue): %d\n",
+ tx_status.b.ptxqspcavail);
+ DWC_DEBUGPL(DBG_HCDV,
+ " P Tx FIFO Space Avail (after queue): %d\n",
+ tx_status.b.ptxfspcavail);
+#endif
+ if (!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned) ||
+ no_queue_space || no_fifo_space) {
+ /*
+ * May need to queue more transactions as the request
+ * queue or Tx FIFO empties. Enable the periodic Tx
+ * FIFO empty interrupt. (Always use the half-empty
+ * level to ensure that new requests are loaded as
+ * soon as possible.)
+ */
+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0,
+ intr_mask.d32);
+ } else {
+ /*
+ * Disable the Tx FIFO empty interrupt since there are
+ * no more transactions that need to be queued right
+ * now. This function is called from interrupt
+ * handlers to queue more transactions as transfer
+ * states change.
+ */
+ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32,
+ 0);
+ }
+ }
+}
+
+/**
+ * Processes active non-periodic channels and queues transactions for these
+ * channels to the DWC_otg controller. After queueing transactions, the NP Tx
+ * FIFO Empty interrupt is enabled if there are more transactions to queue as
+ * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx
+ * FIFO Empty interrupt is disabled.
+ */
+static void process_non_periodic_channels(dwc_otg_hcd_t * hcd)
+{
+ gnptxsts_data_t tx_status;
+ dwc_list_link_t *orig_qh_ptr;
+ dwc_otg_qh_t *qh;
+ int status;
+ int no_queue_space = 0;
+ int no_fifo_space = 0;
+ int more_to_do = 0;
+
+ dwc_otg_core_global_regs_t *global_regs =
+ hcd->core_if->core_global_regs;
+
+ DWC_DEBUGPL(DBG_HCDV, "Queue non-periodic transactions\n");
+#ifdef DEBUG
+ tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+ DWC_DEBUGPL(DBG_HCDV,
+ " NP Tx Req Queue Space Avail (before queue): %d\n",
+ tx_status.b.nptxqspcavail);
+ DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (before queue): %d\n",
+ tx_status.b.nptxfspcavail);
+#endif
+ /*
+ * Keep track of the starting point. Skip over the start-of-list
+ * entry.
+ */
+ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) {
+ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
+ }
+ orig_qh_ptr = hcd->non_periodic_qh_ptr;
+
+ /*
+ * Process once through the active list or until no more space is
+ * available in the request queue or the Tx FIFO.
+ */
+ do {
+ tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+ if (!hcd->core_if->dma_enable && tx_status.b.nptxqspcavail == 0) {
+ no_queue_space = 1;
+ break;
+ }
+
+ qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t,
+ qh_list_entry);
+ status =
+ queue_transaction(hcd, qh->channel,
+ tx_status.b.nptxfspcavail);
+
+ if (status > 0) {
+ more_to_do = 1;
+ } else if (status < 0) {
+ no_fifo_space = 1;
+ break;
+ }
+
+ /* Advance to next QH, skipping start-of-list entry. */
+ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
+ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) {
+ hcd->non_periodic_qh_ptr =
+ hcd->non_periodic_qh_ptr->next;
+ }
+
+ } while (hcd->non_periodic_qh_ptr != orig_qh_ptr);
+
+ if (!hcd->core_if->dma_enable) {
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ intr_mask.b.nptxfempty = 1;
+
+#ifdef DEBUG
+ tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+ DWC_DEBUGPL(DBG_HCDV,
+ " NP Tx Req Queue Space Avail (after queue): %d\n",
+ tx_status.b.nptxqspcavail);
+ DWC_DEBUGPL(DBG_HCDV,
+ " NP Tx FIFO Space Avail (after queue): %d\n",
+ tx_status.b.nptxfspcavail);
+#endif
+ if (more_to_do || no_queue_space || no_fifo_space) {
+ /*
+ * May need to queue more transactions as the request
+ * queue or Tx FIFO empties. Enable the non-periodic
+ * Tx FIFO empty interrupt. (Always use the half-empty
+ * level to ensure that new requests are loaded as
+ * soon as possible.)
+ */
+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0,
+ intr_mask.d32);
+ } else {
+ /*
+ * Disable the Tx FIFO empty interrupt since there are
+ * no more transactions that need to be queued right
+ * now. This function is called from interrupt
+ * handlers to queue more transactions as transfer
+ * states change.
+ */
+ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32,
+ 0);
+ }
+ }
+}
+
+/**
+ * This function processes the currently active host channels and queues
+ * transactions for these channels to the DWC_otg controller. It is called
+ * from HCD interrupt handler functions.
+ *
+ * @param hcd The HCD state structure.
+ * @param tr_type The type(s) of transactions to queue (non-periodic,
+ * periodic, or both).
+ */
+void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
+ dwc_otg_transaction_type_e tr_type)
+{
+#ifdef DEBUG_SOF
+ DWC_DEBUGPL(DBG_HCD, "Queue Transactions\n");
+#endif
+ /* Process host channels associated with periodic transfers. */
+ if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC ||
+ tr_type == DWC_OTG_TRANSACTION_ALL) &&
+ !DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) {
+
+ process_periodic_channels(hcd);
+ }
+
+ /* Process host channels associated with non-periodic transfers. */
+ if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC ||
+ tr_type == DWC_OTG_TRANSACTION_ALL) {
+ if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active)) {
+ process_non_periodic_channels(hcd);
+ } else {
+ /*
+ * Ensure NP Tx FIFO empty interrupt is disabled when
+ * there are no non-periodic transfers to process.
+ */
+ gintmsk_data_t gintmsk = {.d32 = 0 };
+ gintmsk.b.nptxfempty = 1;
+ DWC_MODIFY_REG32(&hcd->core_if->
+ core_global_regs->gintmsk, gintmsk.d32,
+ 0);
+ }
+ }
+}
+
+#ifdef DWC_HS_ELECT_TST
+/*
+ * Quick and dirty hack to implement the HS Electrical Test
+ * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature.
+ *
+ * This code was copied from our userspace app "hset". It sends a
+ * Get Device Descriptor control sequence in two parts, first the
+ * Setup packet by itself, followed some time later by the In and
+ * Ack packets. Rather than trying to figure out how to add this
+ * functionality to the normal driver code, we just hijack the
+ * hardware, using these two function to drive the hardware
+ * directly.
+ */
+
+static dwc_otg_core_global_regs_t *global_regs;
+static dwc_otg_host_global_regs_t *hc_global_regs;
+static dwc_otg_hc_regs_t *hc_regs;
+static uint32_t *data_fifo;
+
+static void do_setup(void)
+{
+ gintsts_data_t gintsts;
+ hctsiz_data_t hctsiz;
+ hcchar_data_t hcchar;
+ haint_data_t haint;
+ hcint_data_t hcint;
+
+ /* Enable HAINTs */
+ DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0001);
+
+ /* Enable HCINTs */
+ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x04a3);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Read HAINT */
+ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+ /* Read HCINT */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+ /* Read HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* Clear HCINT */
+ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+ /* Clear HAINT */
+ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+ /* Clear GINTSTS */
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /*
+ * Send Setup packet (Get Device Descriptor)
+ */
+
+ /* Make sure channel is disabled */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ if (hcchar.b.chen) {
+ hcchar.b.chdis = 1;
+// hcchar.b.chen = 1;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+ //sleep(1);
+ dwc_mdelay(1000);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Read HAINT */
+ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+ /* Read HCINT */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+ /* Read HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* Clear HCINT */
+ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+ /* Clear HAINT */
+ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+ /* Clear GINTSTS */
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ }
+
+ /* Set HCTSIZ */
+ hctsiz.d32 = 0;
+ hctsiz.b.xfersize = 8;
+ hctsiz.b.pktcnt = 1;
+ hctsiz.b.pid = DWC_OTG_HC_PID_SETUP;
+ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+ /* Set HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
+ hcchar.b.epdir = 0;
+ hcchar.b.epnum = 0;
+ hcchar.b.mps = 8;
+ hcchar.b.chen = 1;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+ /* Fill FIFO with Setup data for Get Device Descriptor */
+ data_fifo = (uint32_t *) ((char *)global_regs + 0x1000);
+ DWC_WRITE_REG32(data_fifo++, 0x01000680);
+ DWC_WRITE_REG32(data_fifo++, 0x00080000);
+
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Wait for host channel interrupt */
+ do {
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+ } while (gintsts.b.hcintr == 0);
+
+ /* Disable HCINTs */
+ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x0000);
+
+ /* Disable HAINTs */
+ DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0000);
+
+ /* Read HAINT */
+ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+ /* Read HCINT */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+ /* Read HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* Clear HCINT */
+ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+ /* Clear HAINT */
+ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+ /* Clear GINTSTS */
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+}
+
+static void do_in_ack(void)
+{
+ gintsts_data_t gintsts;
+ hctsiz_data_t hctsiz;
+ hcchar_data_t hcchar;
+ haint_data_t haint;
+ hcint_data_t hcint;
+ host_grxsts_data_t grxsts;
+
+ /* Enable HAINTs */
+ DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0001);
+
+ /* Enable HCINTs */
+ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x04a3);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Read HAINT */
+ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+ /* Read HCINT */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+ /* Read HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* Clear HCINT */
+ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+ /* Clear HAINT */
+ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+ /* Clear GINTSTS */
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /*
+ * Receive Control In packet
+ */
+
+ /* Make sure channel is disabled */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ if (hcchar.b.chen) {
+ hcchar.b.chdis = 1;
+ hcchar.b.chen = 1;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+ //sleep(1);
+ dwc_mdelay(1000);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Read HAINT */
+ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+ /* Read HCINT */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+ /* Read HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* Clear HCINT */
+ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+ /* Clear HAINT */
+ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+ /* Clear GINTSTS */
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ }
+
+ /* Set HCTSIZ */
+ hctsiz.d32 = 0;
+ hctsiz.b.xfersize = 8;
+ hctsiz.b.pktcnt = 1;
+ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1;
+ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+ /* Set HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
+ hcchar.b.epdir = 1;
+ hcchar.b.epnum = 0;
+ hcchar.b.mps = 8;
+ hcchar.b.chen = 1;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Wait for receive status queue interrupt */
+ do {
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+ } while (gintsts.b.rxstsqlvl == 0);
+
+ /* Read RXSTS */
+ grxsts.d32 = DWC_READ_REG32(&global_regs->grxstsp);
+
+ /* Clear RXSTSQLVL in GINTSTS */
+ gintsts.d32 = 0;
+ gintsts.b.rxstsqlvl = 1;
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ switch (grxsts.b.pktsts) {
+ case DWC_GRXSTS_PKTSTS_IN:
+ /* Read the data into the host buffer */
+ if (grxsts.b.bcnt > 0) {
+ int i;
+ int word_count = (grxsts.b.bcnt + 3) / 4;
+
+ data_fifo = (uint32_t *) ((char *)global_regs + 0x1000);
+
+ for (i = 0; i < word_count; i++) {
+ (void)DWC_READ_REG32(data_fifo++);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Wait for receive status queue interrupt */
+ do {
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+ } while (gintsts.b.rxstsqlvl == 0);
+
+ /* Read RXSTS */
+ grxsts.d32 = DWC_READ_REG32(&global_regs->grxstsp);
+
+ /* Clear RXSTSQLVL in GINTSTS */
+ gintsts.d32 = 0;
+ gintsts.b.rxstsqlvl = 1;
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ switch (grxsts.b.pktsts) {
+ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
+ break;
+
+ default:
+ break;
+ }
+
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Wait for host channel interrupt */
+ do {
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+ } while (gintsts.b.hcintr == 0);
+
+ /* Read HAINT */
+ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+ /* Read HCINT */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+ /* Read HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* Clear HCINT */
+ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+ /* Clear HAINT */
+ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+ /* Clear GINTSTS */
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+// usleep(100000);
+// mdelay(100);
+ dwc_mdelay(1);
+
+ /*
+ * Send handshake packet
+ */
+
+ /* Read HAINT */
+ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+ /* Read HCINT */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+ /* Read HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* Clear HCINT */
+ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+ /* Clear HAINT */
+ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+ /* Clear GINTSTS */
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Make sure channel is disabled */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ if (hcchar.b.chen) {
+ hcchar.b.chdis = 1;
+ hcchar.b.chen = 1;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+ //sleep(1);
+ dwc_mdelay(1000);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Read HAINT */
+ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+ /* Read HCINT */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+ /* Read HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* Clear HCINT */
+ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+ /* Clear HAINT */
+ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+ /* Clear GINTSTS */
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ }
+
+ /* Set HCTSIZ */
+ hctsiz.d32 = 0;
+ hctsiz.b.xfersize = 0;
+ hctsiz.b.pktcnt = 1;
+ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1;
+ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+ /* Set HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
+ hcchar.b.epdir = 0;
+ hcchar.b.epnum = 0;
+ hcchar.b.mps = 8;
+ hcchar.b.chen = 1;
+ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+ /* Wait for host channel interrupt */
+ do {
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+ } while (gintsts.b.hcintr == 0);
+
+ /* Disable HCINTs */
+ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x0000);
+
+ /* Disable HAINTs */
+ DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0000);
+
+ /* Read HAINT */
+ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+ /* Read HCINT */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+ /* Read HCCHAR */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+ /* Clear HCINT */
+ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+ /* Clear HAINT */
+ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+ /* Clear GINTSTS */
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ /* Read GINTSTS */
+ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+}
+#endif
+
+/** Handles hub class-specific requests. */
+int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd,
+ uint16_t typeReq,
+ uint16_t wValue,
+ uint16_t wIndex, uint8_t * buf, uint16_t wLength)
+{
+ int retval = 0;
+
+ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+ usb_hub_descriptor_t *hub_desc;
+ hprt0_data_t hprt0 = {.d32 = 0 };
+
+ uint32_t port_status;
+
+ switch (typeReq) {
+ case UCR_CLEAR_HUB_FEATURE:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "ClearHubFeature 0x%x\n", wValue);
+ switch (wValue) {
+ case UHF_C_HUB_LOCAL_POWER:
+ case UHF_C_HUB_OVER_CURRENT:
+ /* Nothing required here */
+ break;
+ default:
+ retval = -DWC_E_INVALID;
+ DWC_ERROR("DWC OTG HCD - "
+ "ClearHubFeature request %xh unknown\n",
+ wValue);
+ }
+ break;
+ case UCR_CLEAR_PORT_FEATURE:
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ if (wValue != UHF_PORT_L1)
+#endif
+ if (!wIndex || wIndex > 1)
+ goto error;
+
+ switch (wValue) {
+ case UHF_PORT_ENABLE:
+ DWC_DEBUGPL(DBG_ANY, "DWC OTG HCD HUB CONTROL - "
+ "ClearPortFeature USB_PORT_FEAT_ENABLE\n");
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtena = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ break;
+ case UHF_PORT_SUSPEND:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
+
+ if (core_if->power_down == 2) {
+ dwc_otg_host_hibernation_restore(core_if, 0, 0);
+ } else {
+ DWC_WRITE_REG32(core_if->pcgcctl, 0);
+ dwc_mdelay(5);
+
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtres = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ hprt0.b.prtsusp = 0;
+ /* Clear Resume bit */
+ dwc_mdelay(100);
+ hprt0.b.prtres = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ }
+ break;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ case UHF_PORT_L1:
+ {
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ glpmcfg_data_t lpmcfg = {.d32 = 0 };
+
+ lpmcfg.d32 =
+ DWC_READ_REG32(&core_if->
+ core_global_regs->glpmcfg);
+ lpmcfg.b.en_utmi_sleep = 0;
+ lpmcfg.b.hird_thres &= (~(1 << 4));
+ lpmcfg.b.prt_sleep_sts = 1;
+ DWC_WRITE_REG32(&core_if->
+ core_global_regs->glpmcfg,
+ lpmcfg.d32);
+
+ /* Clear Enbl_L1Gating bit. */
+ pcgcctl.b.enbl_sleep_gating = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32,
+ 0);
+
+ dwc_mdelay(5);
+
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtres = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0,
+ hprt0.d32);
+ /* This bit will be cleared in wakeup interrupt handle */
+ break;
+ }
+#endif
+ case UHF_PORT_POWER:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "ClearPortFeature USB_PORT_FEAT_POWER\n");
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtpwr = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ break;
+ case UHF_PORT_INDICATOR:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "ClearPortFeature USB_PORT_FEAT_INDICATOR\n");
+ /* Port inidicator not supported */
+ break;
+ case UHF_C_PORT_CONNECTION:
+ /* Clears drivers internal connect status change
+ * flag */
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n");
+ dwc_otg_hcd->flags.b.port_connect_status_change = 0;
+ break;
+ case UHF_C_PORT_RESET:
+ /* Clears the driver's internal Port Reset Change
+ * flag */
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "ClearPortFeature USB_PORT_FEAT_C_RESET\n");
+ dwc_otg_hcd->flags.b.port_reset_change = 0;
+ break;
+ case UHF_C_PORT_ENABLE:
+ /* Clears the driver's internal Port
+ * Enable/Disable Change flag */
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n");
+ dwc_otg_hcd->flags.b.port_enable_change = 0;
+ break;
+ case UHF_C_PORT_SUSPEND:
+ /* Clears the driver's internal Port Suspend
+ * Change flag, which is set when resume signaling on
+ * the host port is complete */
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n");
+ dwc_otg_hcd->flags.b.port_suspend_change = 0;
+ break;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ case UHF_C_PORT_L1:
+ dwc_otg_hcd->flags.b.port_l1_change = 0;
+ break;
+#endif
+ case UHF_C_PORT_OVER_CURRENT:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n");
+ dwc_otg_hcd->flags.b.port_over_current_change = 0;
+ break;
+ default:
+ retval = -DWC_E_INVALID;
+ DWC_ERROR("DWC OTG HCD - "
+ "ClearPortFeature request %xh "
+ "unknown or unsupported\n", wValue);
+ }
+ break;
+ case UCR_GET_HUB_DESCRIPTOR:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "GetHubDescriptor\n");
+ hub_desc = (usb_hub_descriptor_t *) buf;
+ hub_desc->bDescLength = 9;
+ hub_desc->bDescriptorType = 0x29;
+ hub_desc->bNbrPorts = 1;
+ USETW(hub_desc->wHubCharacteristics, 0x08);
+ hub_desc->bPwrOn2PwrGood = 1;
+ hub_desc->bHubContrCurrent = 0;
+ hub_desc->DeviceRemovable[0] = 0;
+ hub_desc->DeviceRemovable[1] = 0xff;
+ break;
+ case UCR_GET_HUB_STATUS:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "GetHubStatus\n");
+ DWC_MEMSET(buf, 0, 4);
+ break;
+ case UCR_GET_PORT_STATUS:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "GetPortStatus wIndex = 0x%04x FLAGS=0x%08x\n",
+ wIndex, dwc_otg_hcd->flags.d32);
+ if (!wIndex || wIndex > 1)
+ goto error;
+
+ port_status = 0;
+
+ if (dwc_otg_hcd->flags.b.port_connect_status_change)
+ port_status |= (1 << UHF_C_PORT_CONNECTION);
+
+ if (dwc_otg_hcd->flags.b.port_enable_change)
+ port_status |= (1 << UHF_C_PORT_ENABLE);
+
+ if (dwc_otg_hcd->flags.b.port_suspend_change)
+ port_status |= (1 << UHF_C_PORT_SUSPEND);
+
+ if (dwc_otg_hcd->flags.b.port_l1_change)
+ port_status |= (1 << UHF_C_PORT_L1);
+
+ if (dwc_otg_hcd->flags.b.port_reset_change) {
+ port_status |= (1 << UHF_C_PORT_RESET);
+ }
+
+ if (dwc_otg_hcd->flags.b.port_over_current_change) {
+ DWC_WARN("Overcurrent change detected\n");
+ port_status |= (1 << UHF_C_PORT_OVER_CURRENT);
+ }
+
+ if (!dwc_otg_hcd->flags.b.port_connect_status) {
+ /*
+ * The port is disconnected, which means the core is
+ * either in device mode or it soon will be. Just
+ * return 0's for the remainder of the port status
+ * since the port register can't be read if the core
+ * is in device mode.
+ */
+ *((__le32 *) buf) = dwc_cpu_to_le32(&port_status);
+ break;
+ }
+
+ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+ DWC_DEBUGPL(DBG_HCDV, " HPRT0: 0x%08x\n", hprt0.d32);
+
+ if (hprt0.b.prtconnsts)
+ port_status |= (1 << UHF_PORT_CONNECTION);
+
+ if (hprt0.b.prtena)
+ port_status |= (1 << UHF_PORT_ENABLE);
+
+ if (hprt0.b.prtsusp)
+ port_status |= (1 << UHF_PORT_SUSPEND);
+
+ if (hprt0.b.prtovrcurract)
+ port_status |= (1 << UHF_PORT_OVER_CURRENT);
+
+ if (hprt0.b.prtrst)
+ port_status |= (1 << UHF_PORT_RESET);
+
+ if (hprt0.b.prtpwr)
+ port_status |= (1 << UHF_PORT_POWER);
+
+ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED)
+ port_status |= (1 << UHF_PORT_HIGH_SPEED);
+ else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED)
+ port_status |= (1 << UHF_PORT_LOW_SPEED);
+
+ if (hprt0.b.prttstctl)
+ port_status |= (1 << UHF_PORT_TEST);
+ if (dwc_otg_get_lpm_portsleepstatus(dwc_otg_hcd->core_if)) {
+ port_status |= (1 << UHF_PORT_L1);
+ }
+ /*
+ For Synopsys HW emulation of Power down wkup_control asserts the
+ hreset_n and prst_n on suspned. This causes the HPRT0 to be zero.
+ We intentionally tell the software that port is in L2Suspend state.
+ Only for STE.
+ */
+ if ((core_if->power_down == 2)
+ && (core_if->hibernation_suspend == 1)) {
+ port_status |= (1 << UHF_PORT_SUSPEND);
+ }
+ /* USB_PORT_FEAT_INDICATOR unsupported always 0 */
+
+ *((__le32 *) buf) = dwc_cpu_to_le32(&port_status);
+
+ break;
+ case UCR_SET_HUB_FEATURE:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "SetHubFeature\n");
+ /* No HUB features supported */
+ break;
+ case UCR_SET_PORT_FEATURE:
+ if (wValue != UHF_PORT_TEST && (!wIndex || wIndex > 1))
+ goto error;
+
+ if (!dwc_otg_hcd->flags.b.port_connect_status) {
+ /*
+ * The port is disconnected, which means the core is
+ * either in device mode or it soon will be. Just
+ * return without doing anything since the port
+ * register can't be written if the core is in device
+ * mode.
+ */
+ break;
+ }
+
+ switch (wValue) {
+ case UHF_PORT_SUSPEND:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "SetPortFeature - USB_PORT_FEAT_SUSPEND\n");
+ if (dwc_otg_hcd_otg_port(dwc_otg_hcd) != wIndex) {
+ goto error;
+ }
+ if (core_if->power_down == 2) {
+ int timeout = 300;
+ dwc_irqflags_t flags;
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ gusbcfg_data_t gusbcfg = {.d32 = 0 };
+#ifdef DWC_DEV_SRPCAP
+ int32_t otg_cap_param = core_if->core_params->otg_cap;
+#endif
+ DWC_PRINTF("Preparing for complete power-off\n");
+
+ /* Save registers before hibernation */
+ dwc_otg_save_global_regs(core_if);
+ dwc_otg_save_host_regs(core_if);
+
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtsusp = 1;
+ hprt0.b.prtena = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ /* Spin hprt0.b.prtsusp to became 1 */
+ do {
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ if (hprt0.b.prtsusp) {
+ break;
+ }
+ dwc_mdelay(1);
+ } while (--timeout);
+ if (!timeout) {
+ DWC_WARN("Suspend wasn't genereted\n");
+ }
+ dwc_udelay(10);
+
+ /*
+ * We need to disable interrupts to prevent servicing of any IRQ
+ * during going to hibernation
+ */
+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
+ core_if->lx_state = DWC_OTG_L2;
+#ifdef DWC_DEV_SRPCAP
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtpwr = 0;
+ hprt0.b.prtena = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0,
+ hprt0.d32);
+#endif
+ gusbcfg.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->
+ gusbcfg);
+ if (gusbcfg.b.ulpi_utmi_sel == 1) {
+ /* ULPI interface */
+ /* Suspend the Phy Clock */
+ pcgcctl.d32 = 0;
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, 0,
+ pcgcctl.d32);
+ dwc_udelay(10);
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->
+ core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ } else {
+ /* UTMI+ Interface */
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->
+ core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
+ dwc_udelay(10);
+ }
+#ifdef DWC_DEV_SRPCAP
+ gpwrdn.d32 = 0;
+ gpwrdn.b.dis_vbus = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+#endif
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ gpwrdn.d32 = 0;
+#ifdef DWC_DEV_SRPCAP
+ gpwrdn.b.srp_det_msk = 1;
+#endif
+ gpwrdn.b.disconn_det_msk = 1;
+ gpwrdn.b.lnstchng_msk = 1;
+ gpwrdn.b.sts_chngint_msk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Enable Power Down Clamp and all interrupts in GPWRDN */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnclmp = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+ dwc_udelay(10);
+
+ /* Switch off VDD */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+
+#ifdef DWC_DEV_SRPCAP
+ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE)
+ {
+ core_if->pwron_timer_started = 1;
+ DWC_TIMER_SCHEDULE(core_if->pwron_timer, 6000 /* 6 secs */ );
+ }
+#endif
+ /* Save gpwrdn register for further usage if stschng interrupt */
+ core_if->gr_backup->gpwrdn_local =
+ DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+
+ /* Set flag to indicate that we are in hibernation */
+ core_if->hibernation_suspend = 1;
+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock,flags);
+
+ DWC_PRINTF("Host hibernation completed\n");
+ // Exit from case statement
+ break;
+
+ }
+ if (dwc_otg_hcd_otg_port(dwc_otg_hcd) == wIndex &&
+ dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) {
+ gotgctl_data_t gotgctl = {.d32 = 0 };
+ gotgctl.b.hstsethnpen = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gotgctl, 0, gotgctl.d32);
+ core_if->op_state = A_SUSPEND;
+ }
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtsusp = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ {
+ dwc_irqflags_t flags;
+ /* Update lx_state */
+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
+ core_if->lx_state = DWC_OTG_L2;
+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
+ }
+ /* Suspend the Phy Clock */
+ {
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, 0,
+ pcgcctl.d32);
+ dwc_udelay(10);
+ }
+
+ /* For HNP the bus must be suspended for at least 200ms. */
+ if (dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) {
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+ dwc_mdelay(200);
+ }
+
+ /** @todo - check how sw can wait for 1 sec to check asesvld??? */
+#if 0 //vahrama !!!!!!!!!!!!!!!!!!
+ if (core_if->adp_enable) {
+ gotgctl_data_t gotgctl = {.d32 = 0 };
+ gpwrdn_data_t gpwrdn;
+
+ while (gotgctl.b.asesvld == 1) {
+ gotgctl.d32 =
+ DWC_READ_REG32(&core_if->
+ core_global_regs->
+ gotgctl);
+ dwc_mdelay(100);
+ }
+
+ /* Enable Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+
+ /* Unmask SRP detected interrupt from Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.srp_det_msk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, 0, gpwrdn.d32);
+
+ dwc_otg_adp_probe_start(core_if);
+ }
+#endif
+ break;
+ case UHF_PORT_POWER:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "SetPortFeature - USB_PORT_FEAT_POWER\n");
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtpwr = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ break;
+ case UHF_PORT_RESET:
+ if ((core_if->power_down == 2)
+ && (core_if->hibernation_suspend == 1)) {
+ /* If we are going to exit from Hibernated
+ * state via USB RESET.
+ */
+ dwc_otg_host_hibernation_restore(core_if, 0, 1);
+ } else {
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+
+ DWC_DEBUGPL(DBG_HCD,
+ "DWC OTG HCD HUB CONTROL - "
+ "SetPortFeature - USB_PORT_FEAT_RESET\n");
+ {
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ pcgcctl.b.enbl_sleep_gating = 1;
+ pcgcctl.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+ DWC_WRITE_REG32(core_if->pcgcctl, 0);
+ }
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ {
+ glpmcfg_data_t lpmcfg;
+ lpmcfg.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ if (lpmcfg.b.prt_sleep_sts) {
+ lpmcfg.b.en_utmi_sleep = 0;
+ lpmcfg.b.hird_thres &= (~(1 << 4));
+ DWC_WRITE_REG32
+ (&core_if->core_global_regs->glpmcfg,
+ lpmcfg.d32);
+ dwc_mdelay(1);
+ }
+ }
+#endif
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ /* Clear suspend bit if resetting from suspended state. */
+ hprt0.b.prtsusp = 0;
+ /* When B-Host the Port reset bit is set in
+ * the Start HCD Callback function, so that
+ * the reset is started within 1ms of the HNP
+ * success interrupt. */
+ if (!dwc_otg_hcd_is_b_host(dwc_otg_hcd)) {
+ hprt0.b.prtpwr = 1;
+ hprt0.b.prtrst = 1;
+ DWC_PRINTF("Indeed it is in host mode hprt0 = %08x\n",hprt0.d32);
+ DWC_WRITE_REG32(core_if->host_if->hprt0,
+ hprt0.d32);
+ }
+ /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
+ dwc_mdelay(60);
+ hprt0.b.prtrst = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ core_if->lx_state = DWC_OTG_L0; /* Now back to the on state */
+ }
+ break;
+#ifdef DWC_HS_ELECT_TST
+ case UHF_PORT_TEST:
+ {
+ uint32_t t;
+ gintmsk_data_t gintmsk;
+
+ t = (wIndex >> 8); /* MSB wIndex USB */
+ DWC_DEBUGPL(DBG_HCD,
+ "DWC OTG HCD HUB CONTROL - "
+ "SetPortFeature - USB_PORT_FEAT_TEST %d\n",
+ t);
+ DWC_WARN("USB_PORT_FEAT_TEST %d\n", t);
+ if (t < 6) {
+ hprt0.d32 = dwc_otg_read_hprt0(core_if);
+ hprt0.b.prttstctl = t;
+ DWC_WRITE_REG32(core_if->host_if->hprt0,
+ hprt0.d32);
+ } else {
+ /* Setup global vars with reg addresses (quick and
+ * dirty hack, should be cleaned up)
+ */
+ global_regs = core_if->core_global_regs;
+ hc_global_regs =
+ core_if->host_if->host_global_regs;
+ hc_regs =
+ (dwc_otg_hc_regs_t *) ((char *)
+ global_regs +
+ 0x500);
+ data_fifo =
+ (uint32_t *) ((char *)global_regs +
+ 0x1000);
+
+ if (t == 6) { /* HS_HOST_PORT_SUSPEND_RESUME */
+ /* Save current interrupt mask */
+ gintmsk.d32 =
+ DWC_READ_REG32
+ (&global_regs->gintmsk);
+
+ /* Disable all interrupts while we muck with
+ * the hardware directly
+ */
+ DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+ /* 15 second delay per the test spec */
+ dwc_mdelay(15000);
+
+ /* Drive suspend on the root port */
+ hprt0.d32 =
+ dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtsusp = 1;
+ hprt0.b.prtres = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+ /* 15 second delay per the test spec */
+ dwc_mdelay(15000);
+
+ /* Drive resume on the root port */
+ hprt0.d32 =
+ dwc_otg_read_hprt0(core_if);
+ hprt0.b.prtsusp = 0;
+ hprt0.b.prtres = 1;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+ dwc_mdelay(100);
+
+ /* Clear the resume bit */
+ hprt0.b.prtres = 0;
+ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+ /* Restore interrupts */
+ DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32);
+ } else if (t == 7) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */
+ /* Save current interrupt mask */
+ gintmsk.d32 =
+ DWC_READ_REG32
+ (&global_regs->gintmsk);
+
+ /* Disable all interrupts while we muck with
+ * the hardware directly
+ */
+ DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+ /* 15 second delay per the test spec */
+ dwc_mdelay(15000);
+
+ /* Send the Setup packet */
+ do_setup();
+
+ /* 15 second delay so nothing else happens for awhile */
+ dwc_mdelay(15000);
+
+ /* Restore interrupts */
+ DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32);
+ } else if (t == 8) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */
+ /* Save current interrupt mask */
+ gintmsk.d32 =
+ DWC_READ_REG32
+ (&global_regs->gintmsk);
+
+ /* Disable all interrupts while we muck with
+ * the hardware directly
+ */
+ DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+ /* Send the Setup packet */
+ do_setup();
+
+ /* 15 second delay so nothing else happens for awhile */
+ dwc_mdelay(15000);
+
+ /* Send the In and Ack packets */
+ do_in_ack();
+
+ /* 15 second delay so nothing else happens for awhile */
+ dwc_mdelay(15000);
+
+ /* Restore interrupts */
+ DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32);
+ }
+ }
+ break;
+ }
+#endif /* DWC_HS_ELECT_TST */
+
+ case UHF_PORT_INDICATOR:
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+ "SetPortFeature - USB_PORT_FEAT_INDICATOR\n");
+ /* Not supported */
+ break;
+ default:
+ retval = -DWC_E_INVALID;
+ DWC_ERROR("DWC OTG HCD - "
+ "SetPortFeature request %xh "
+ "unknown or unsupported\n", wValue);
+ break;
+ }
+ break;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ case UCR_SET_AND_TEST_PORT_FEATURE:
+ if (wValue != UHF_PORT_L1) {
+ goto error;
+ }
+ {
+ int portnum, hird, devaddr, remwake;
+ glpmcfg_data_t lpmcfg;
+ uint32_t time_usecs;
+ gintsts_data_t gintsts;
+ gintmsk_data_t gintmsk;
+
+ if (!dwc_otg_get_param_lpm_enable(core_if)) {
+ goto error;
+ }
+ if (wValue != UHF_PORT_L1 || wLength != 1) {
+ goto error;
+ }
+ /* Check if the port currently is in SLEEP state */
+ lpmcfg.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ if (lpmcfg.b.prt_sleep_sts) {
+ DWC_INFO("Port is already in sleep mode\n");
+ buf[0] = 0; /* Return success */
+ break;
+ }
+
+ portnum = wIndex & 0xf;
+ hird = (wIndex >> 4) & 0xf;
+ devaddr = (wIndex >> 8) & 0x7f;
+ remwake = (wIndex >> 15);
+
+ if (portnum != 1) {
+ retval = -DWC_E_INVALID;
+ DWC_WARN
+ ("Wrong port number(%d) in SetandTestPortFeature request\n",
+ portnum);
+ break;
+ }
+
+ DWC_PRINTF
+ ("SetandTestPortFeature request: portnum = %d, hird = %d, devaddr = %d, rewake = %d\n",
+ portnum, hird, devaddr, remwake);
+ /* Disable LPM interrupt */
+ gintmsk.d32 = 0;
+ gintmsk.b.lpmtranrcvd = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
+ gintmsk.d32, 0);
+
+ if (dwc_otg_hcd_send_lpm
+ (dwc_otg_hcd, devaddr, hird, remwake)) {
+ retval = -DWC_E_INVALID;
+ break;
+ }
+
+ time_usecs = 10 * (lpmcfg.b.retry_count + 1);
+ /* We will consider timeout if time_usecs microseconds pass,
+ * and we don't receive LPM transaction status.
+ * After receiving non-error responce(ACK/NYET/STALL) from device,
+ * core will set lpmtranrcvd bit.
+ */
+ do {
+ gintsts.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+ if (gintsts.b.lpmtranrcvd) {
+ break;
+ }
+ dwc_udelay(1);
+ } while (--time_usecs);
+ /* lpm_int bit will be cleared in LPM interrupt handler */
+
+ /* Now fill status
+ * 0x00 - Success
+ * 0x10 - NYET
+ * 0x11 - Timeout
+ */
+ if (!gintsts.b.lpmtranrcvd) {
+ buf[0] = 0x3; /* Completion code is Timeout */
+ dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd);
+ } else {
+ lpmcfg.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ if (lpmcfg.b.lpm_resp == 0x3) {
+ /* ACK responce from the device */
+ buf[0] = 0x00; /* Success */
+ } else if (lpmcfg.b.lpm_resp == 0x2) {
+ /* NYET responce from the device */
+ buf[0] = 0x2;
+ } else {
+ /* Otherwise responce with Timeout */
+ buf[0] = 0x3;
+ }
+ }
+ DWC_PRINTF("Device responce to LPM trans is %x\n",
+ lpmcfg.b.lpm_resp);
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0,
+ gintmsk.d32);
+
+ break;
+ }
+#endif /* CONFIG_USB_DWC_OTG_LPM */
+ default:
+error:
+ retval = -DWC_E_INVALID;
+ DWC_WARN("DWC OTG HCD - "
+ "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n",
+ typeReq, wIndex, wValue);
+ break;
+ }
+
+ return retval;
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/** Returns index of host channel to perform LPM transaction. */
+int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd, uint8_t devaddr)
+{
+ dwc_otg_core_if_t *core_if = hcd->core_if;
+ dwc_hc_t *hc;
+ hcchar_data_t hcchar;
+ gintmsk_data_t gintmsk = {.d32 = 0 };
+
+ if (DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
+ DWC_PRINTF("No free channel to select for LPM transaction\n");
+ return -1;
+ }
+
+ hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list);
+
+ /* Mask host channel interrupts. */
+ gintmsk.b.hcintr = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
+
+ /* Fill fields that core needs for LPM transaction */
+ hcchar.b.devaddr = devaddr;
+ hcchar.b.epnum = 0;
+ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
+ hcchar.b.mps = 64;
+ hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW);
+ hcchar.b.epdir = 0; /* OUT */
+ DWC_WRITE_REG32(&core_if->host_if->hc_regs[hc->hc_num]->hcchar,
+ hcchar.d32);
+
+ /* Remove the host channel from the free list. */
+ DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry);
+
+ DWC_PRINTF("hcnum = %d devaddr = %d\n", hc->hc_num, devaddr);
+
+ return hc->hc_num;
+}
+
+/** Release hc after performing LPM transaction */
+void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd)
+{
+ dwc_hc_t *hc;
+ glpmcfg_data_t lpmcfg;
+ uint8_t hc_num;
+
+ lpmcfg.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->glpmcfg);
+ hc_num = lpmcfg.b.lpm_chan_index;
+
+ hc = hcd->hc_ptr_array[hc_num];
+
+ DWC_PRINTF("Freeing channel %d after LPM\n", hc_num);
+ /* Return host channel to free list */
+ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
+}
+
+int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr, uint8_t hird,
+ uint8_t bRemoteWake)
+{
+ glpmcfg_data_t lpmcfg;
+ pcgcctl_data_t pcgcctl = {.d32 = 0 };
+ int channel;
+
+ channel = dwc_otg_hcd_get_hc_for_lpm_tran(hcd, devaddr);
+ if (channel < 0) {
+ return channel;
+ }
+
+ pcgcctl.b.enbl_sleep_gating = 1;
+ DWC_MODIFY_REG32(hcd->core_if->pcgcctl, 0, pcgcctl.d32);
+
+ /* Read LPM config register */
+ lpmcfg.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->glpmcfg);
+
+ /* Program LPM transaction fields */
+ lpmcfg.b.rem_wkup_en = bRemoteWake;
+ lpmcfg.b.hird = hird;
+ lpmcfg.b.hird_thres = 0x1c;
+ lpmcfg.b.lpm_chan_index = channel;
+ lpmcfg.b.en_utmi_sleep = 1;
+ /* Program LPM config register */
+ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+
+ /* Send LPM transaction */
+ lpmcfg.b.send_lpm = 1;
+ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+
+ return 0;
+}
+
+#endif /* CONFIG_USB_DWC_OTG_LPM */
+
+int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port)
+{
+ int retval;
+
+ if (port != 1) {
+ return -DWC_E_INVALID;
+ }
+
+ retval = (hcd->flags.b.port_connect_status_change ||
+ hcd->flags.b.port_reset_change ||
+ hcd->flags.b.port_enable_change ||
+ hcd->flags.b.port_suspend_change ||
+ hcd->flags.b.port_over_current_change);
+#ifdef DEBUG
+ if (retval) {
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB STATUS DATA:"
+ " Root port status changed\n");
+ DWC_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n",
+ hcd->flags.b.port_connect_status_change);
+ DWC_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n",
+ hcd->flags.b.port_reset_change);
+ DWC_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n",
+ hcd->flags.b.port_enable_change);
+ DWC_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n",
+ hcd->flags.b.port_suspend_change);
+ DWC_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n",
+ hcd->flags.b.port_over_current_change);
+ }
+#endif
+ return retval;
+}
+
+int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+ hfnum_data_t hfnum;
+ hfnum.d32 =
+ DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->
+ hfnum);
+
+#ifdef DEBUG_SOF
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n",
+ hfnum.b.frnum);
+#endif
+ return hfnum.b.frnum;
+}
+
+int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd,
+ struct dwc_otg_hcd_function_ops *fops)
+{
+ int retval = 0;
+
+ hcd->fops = fops;
+ if (!dwc_otg_is_device_mode(hcd->core_if) &&
+ (!hcd->core_if->adp_enable || hcd->core_if->adp.adp_started)) {
+ dwc_otg_hcd_reinit(hcd);
+ } else {
+ retval = -DWC_E_NO_DEVICE;
+ }
+
+ return retval;
+}
+
+void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd)
+{
+ return hcd->priv;
+}
+
+void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data)
+{
+ hcd->priv = priv_data;
+}
+
+uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd)
+{
+ return hcd->otg_port;
+}
+
+uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd)
+{
+ uint32_t is_b_host;
+ if (hcd->core_if->op_state == B_HOST) {
+ is_b_host = 1;
+ } else {
+ is_b_host = 0;
+ }
+
+ return is_b_host;
+}
+
+dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd,
+ int iso_desc_count, int atomic_alloc)
+{
+ dwc_otg_hcd_urb_t *dwc_otg_urb;
+ uint32_t size;
+
+ size =
+ sizeof(*dwc_otg_urb) +
+ iso_desc_count * sizeof(struct dwc_otg_hcd_iso_packet_desc);
+ if (atomic_alloc)
+ dwc_otg_urb = DWC_ALLOC_ATOMIC(size);
+ else
+ dwc_otg_urb = DWC_ALLOC(size);
+
+ dwc_otg_urb->packet_count = iso_desc_count;
+
+ return dwc_otg_urb;
+}
+
+void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * dwc_otg_urb,
+ uint8_t dev_addr, uint8_t ep_num,
+ uint8_t ep_type, uint8_t ep_dir, uint16_t mps)
+{
+ dwc_otg_hcd_fill_pipe(&dwc_otg_urb->pipe_info, dev_addr, ep_num,
+ ep_type, ep_dir, mps);
+#if 0
+ DWC_PRINTF
+ ("addr = %d, ep_num = %d, ep_dir = 0x%x, ep_type = 0x%x, mps = %d\n",
+ dev_addr, ep_num, ep_dir, ep_type, mps);
+#endif
+}
+
+void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * dwc_otg_urb,
+ void *urb_handle, void *buf, dwc_dma_t dma,
+ uint32_t buflen, void *setup_packet,
+ dwc_dma_t setup_dma, uint32_t flags,
+ uint16_t interval)
+{
+ dwc_otg_urb->priv = urb_handle;
+ dwc_otg_urb->buf = buf;
+ dwc_otg_urb->dma = dma;
+ dwc_otg_urb->length = buflen;
+ dwc_otg_urb->setup_packet = setup_packet;
+ dwc_otg_urb->setup_dma = setup_dma;
+ dwc_otg_urb->flags = flags;
+ dwc_otg_urb->interval = interval;
+ dwc_otg_urb->status = -DWC_E_IN_PROGRESS;
+}
+
+uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb)
+{
+ return dwc_otg_urb->status;
+}
+
+uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t * dwc_otg_urb)
+{
+ return dwc_otg_urb->actual_length;
+}
+
+uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t * dwc_otg_urb)
+{
+ return dwc_otg_urb->error_count;
+}
+
+void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb,
+ int desc_num, uint32_t offset,
+ uint32_t length)
+{
+ dwc_otg_urb->iso_descs[desc_num].offset = offset;
+ dwc_otg_urb->iso_descs[desc_num].length = length;
+}
+
+uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t * dwc_otg_urb,
+ int desc_num)
+{
+ return dwc_otg_urb->iso_descs[desc_num].status;
+}
+
+uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t *
+ dwc_otg_urb, int desc_num)
+{
+ return dwc_otg_urb->iso_descs[desc_num].actual_length;
+}
+
+int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd, void *ep_handle)
+{
+ int allocated = 0;
+ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+
+ if (qh) {
+ if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) {
+ allocated = 1;
+ }
+ }
+ return allocated;
+}
+
+int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle)
+{
+ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+ int freed = 0;
+ DWC_ASSERT(qh, "qh is not allocated\n");
+
+ if (DWC_LIST_EMPTY(&qh->qh_list_entry)) {
+ freed = 1;
+ }
+
+ return freed;
+}
+
+uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd, void *ep_handle)
+{
+ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+ DWC_ASSERT(qh, "qh is not allocated\n");
+ return qh->usecs;
+}
+
+void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd)
+{
+#ifdef DEBUG
+ int num_channels;
+ int i;
+ gnptxsts_data_t np_tx_status;
+ hptxsts_data_t p_tx_status;
+
+ num_channels = hcd->core_if->core_params->host_channels;
+ DWC_PRINTF("\n");
+ DWC_PRINTF
+ ("************************************************************\n");
+ DWC_PRINTF("HCD State:\n");
+ DWC_PRINTF(" Num channels: %d\n", num_channels);
+ for (i = 0; i < num_channels; i++) {
+ dwc_hc_t *hc = hcd->hc_ptr_array[i];
+ DWC_PRINTF(" Channel %d:\n", i);
+ DWC_PRINTF(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
+ hc->dev_addr, hc->ep_num, hc->ep_is_in);
+ DWC_PRINTF(" speed: %d\n", hc->speed);
+ DWC_PRINTF(" ep_type: %d\n", hc->ep_type);
+ DWC_PRINTF(" max_packet: %d\n", hc->max_packet);
+ DWC_PRINTF(" data_pid_start: %d\n", hc->data_pid_start);
+ DWC_PRINTF(" multi_count: %d\n", hc->multi_count);
+ DWC_PRINTF(" xfer_started: %d\n", hc->xfer_started);
+ DWC_PRINTF(" xfer_buff: %p\n", hc->xfer_buff);
+ DWC_PRINTF(" xfer_len: %d\n", hc->xfer_len);
+ DWC_PRINTF(" xfer_count: %d\n", hc->xfer_count);
+ DWC_PRINTF(" halt_on_queue: %d\n", hc->halt_on_queue);
+ DWC_PRINTF(" halt_pending: %d\n", hc->halt_pending);
+ DWC_PRINTF(" halt_status: %d\n", hc->halt_status);
+ DWC_PRINTF(" do_split: %d\n", hc->do_split);
+ DWC_PRINTF(" complete_split: %d\n", hc->complete_split);
+ DWC_PRINTF(" hub_addr: %d\n", hc->hub_addr);
+ DWC_PRINTF(" port_addr: %d\n", hc->port_addr);
+ DWC_PRINTF(" xact_pos: %d\n", hc->xact_pos);
+ DWC_PRINTF(" requests: %d\n", hc->requests);
+ DWC_PRINTF(" qh: %p\n", hc->qh);
+ if (hc->xfer_started) {
+ hfnum_data_t hfnum;
+ hcchar_data_t hcchar;
+ hctsiz_data_t hctsiz;
+ hcint_data_t hcint;
+ hcintmsk_data_t hcintmsk;
+ hfnum.d32 =
+ DWC_READ_REG32(&hcd->core_if->
+ host_if->host_global_regs->hfnum);
+ hcchar.d32 =
+ DWC_READ_REG32(&hcd->core_if->host_if->
+ hc_regs[i]->hcchar);
+ hctsiz.d32 =
+ DWC_READ_REG32(&hcd->core_if->host_if->
+ hc_regs[i]->hctsiz);
+ hcint.d32 =
+ DWC_READ_REG32(&hcd->core_if->host_if->
+ hc_regs[i]->hcint);
+ hcintmsk.d32 =
+ DWC_READ_REG32(&hcd->core_if->host_if->
+ hc_regs[i]->hcintmsk);
+ DWC_PRINTF(" hfnum: 0x%08x\n", hfnum.d32);
+ DWC_PRINTF(" hcchar: 0x%08x\n", hcchar.d32);
+ DWC_PRINTF(" hctsiz: 0x%08x\n", hctsiz.d32);
+ DWC_PRINTF(" hcint: 0x%08x\n", hcint.d32);
+ DWC_PRINTF(" hcintmsk: 0x%08x\n", hcintmsk.d32);
+ }
+ if (hc->xfer_started && hc->qh) {
+ dwc_otg_qtd_t *qtd;
+ dwc_otg_hcd_urb_t *urb;
+
+ DWC_CIRCLEQ_FOREACH(qtd, &hc->qh->qtd_list, qtd_list_entry) {
+ if (!qtd->in_process)
+ break;
+
+ urb = qtd->urb;
+ DWC_PRINTF(" URB Info:\n");
+ DWC_PRINTF(" qtd: %p, urb: %p\n", qtd, urb);
+ if (urb) {
+ DWC_PRINTF(" Dev: %d, EP: %d %s\n",
+ dwc_otg_hcd_get_dev_addr(&urb->
+ pipe_info),
+ dwc_otg_hcd_get_ep_num(&urb->
+ pipe_info),
+ dwc_otg_hcd_is_pipe_in(&urb->
+ pipe_info) ?
+ "IN" : "OUT");
+ DWC_PRINTF(" Max packet size: %d\n",
+ dwc_otg_hcd_get_mps(&urb->
+ pipe_info));
+ DWC_PRINTF(" transfer_buffer: %p\n",
+ urb->buf);
+ DWC_PRINTF(" transfer_dma: %p\n",
+ (void *)urb->dma);
+ DWC_PRINTF(" transfer_buffer_length: %d\n",
+ urb->length);
+ DWC_PRINTF(" actual_length: %d\n",
+ urb->actual_length);
+ }
+ }
+ }
+ }
+ DWC_PRINTF(" non_periodic_channels: %d\n", hcd->non_periodic_channels);
+ DWC_PRINTF(" periodic_channels: %d\n", hcd->periodic_channels);
+ DWC_PRINTF(" periodic_usecs: %d\n", hcd->periodic_usecs);
+ np_tx_status.d32 =
+ DWC_READ_REG32(&hcd->core_if->core_global_regs->gnptxsts);
+ DWC_PRINTF(" NP Tx Req Queue Space Avail: %d\n",
+ np_tx_status.b.nptxqspcavail);
+ DWC_PRINTF(" NP Tx FIFO Space Avail: %d\n",
+ np_tx_status.b.nptxfspcavail);
+ p_tx_status.d32 =
+ DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hptxsts);
+ DWC_PRINTF(" P Tx Req Queue Space Avail: %d\n",
+ p_tx_status.b.ptxqspcavail);
+ DWC_PRINTF(" P Tx FIFO Space Avail: %d\n", p_tx_status.b.ptxfspcavail);
+ dwc_otg_hcd_dump_frrem(hcd);
+ dwc_otg_dump_global_registers(hcd->core_if);
+ dwc_otg_dump_host_registers(hcd->core_if);
+ DWC_PRINTF
+ ("************************************************************\n");
+ DWC_PRINTF("\n");
+#endif
+}
+
+#ifdef DEBUG
+void dwc_print_setup_data(uint8_t * setup)
+{
+ int i;
+ if (CHK_DEBUG_LEVEL(DBG_HCD)) {
+ DWC_PRINTF("Setup Data = MSB ");
+ for (i = 7; i >= 0; i--)
+ DWC_PRINTF("%02x ", setup[i]);
+ DWC_PRINTF("\n");
+ DWC_PRINTF(" bmRequestType Tranfer = %s\n",
+ (setup[0] & 0x80) ? "Device-to-Host" :
+ "Host-to-Device");
+ DWC_PRINTF(" bmRequestType Type = ");
+ switch ((setup[0] & 0x60) >> 5) {
+ case 0:
+ DWC_PRINTF("Standard\n");
+ break;
+ case 1:
+ DWC_PRINTF("Class\n");
+ break;
+ case 2:
+ DWC_PRINTF("Vendor\n");
+ break;
+ case 3:
+ DWC_PRINTF("Reserved\n");
+ break;
+ }
+ DWC_PRINTF(" bmRequestType Recipient = ");
+ switch (setup[0] & 0x1f) {
+ case 0:
+ DWC_PRINTF("Device\n");
+ break;
+ case 1:
+ DWC_PRINTF("Interface\n");
+ break;
+ case 2:
+ DWC_PRINTF("Endpoint\n");
+ break;
+ case 3:
+ DWC_PRINTF("Other\n");
+ break;
+ default:
+ DWC_PRINTF("Reserved\n");
+ break;
+ }
+ DWC_PRINTF(" bRequest = 0x%0x\n", setup[1]);
+ DWC_PRINTF(" wValue = 0x%0x\n", *((uint16_t *) & setup[2]));
+ DWC_PRINTF(" wIndex = 0x%0x\n", *((uint16_t *) & setup[4]));
+ DWC_PRINTF(" wLength = 0x%0x\n\n", *((uint16_t *) & setup[6]));
+ }
+}
+#endif
+
+void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd)
+{
+#if 0
+ DWC_PRINTF("Frame remaining at SOF:\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->frrem_samples, hcd->frrem_accum,
+ (hcd->frrem_samples > 0) ?
+ hcd->frrem_accum / hcd->frrem_samples : 0);
+
+ DWC_PRINTF("\n");
+ DWC_PRINTF("Frame remaining at start_transfer (uframe 7):\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->core_if->hfnum_7_samples,
+ hcd->core_if->hfnum_7_frrem_accum,
+ (hcd->core_if->hfnum_7_samples >
+ 0) ? hcd->core_if->hfnum_7_frrem_accum /
+ hcd->core_if->hfnum_7_samples : 0);
+ DWC_PRINTF("Frame remaining at start_transfer (uframe 0):\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->core_if->hfnum_0_samples,
+ hcd->core_if->hfnum_0_frrem_accum,
+ (hcd->core_if->hfnum_0_samples >
+ 0) ? hcd->core_if->hfnum_0_frrem_accum /
+ hcd->core_if->hfnum_0_samples : 0);
+ DWC_PRINTF("Frame remaining at start_transfer (uframe 1-6):\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->core_if->hfnum_other_samples,
+ hcd->core_if->hfnum_other_frrem_accum,
+ (hcd->core_if->hfnum_other_samples >
+ 0) ? hcd->core_if->hfnum_other_frrem_accum /
+ hcd->core_if->hfnum_other_samples : 0);
+
+ DWC_PRINTF("\n");
+ DWC_PRINTF("Frame remaining at sample point A (uframe 7):\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->hfnum_7_samples_a, hcd->hfnum_7_frrem_accum_a,
+ (hcd->hfnum_7_samples_a > 0) ?
+ hcd->hfnum_7_frrem_accum_a / hcd->hfnum_7_samples_a : 0);
+ DWC_PRINTF("Frame remaining at sample point A (uframe 0):\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->hfnum_0_samples_a, hcd->hfnum_0_frrem_accum_a,
+ (hcd->hfnum_0_samples_a > 0) ?
+ hcd->hfnum_0_frrem_accum_a / hcd->hfnum_0_samples_a : 0);
+ DWC_PRINTF("Frame remaining at sample point A (uframe 1-6):\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->hfnum_other_samples_a, hcd->hfnum_other_frrem_accum_a,
+ (hcd->hfnum_other_samples_a > 0) ?
+ hcd->hfnum_other_frrem_accum_a /
+ hcd->hfnum_other_samples_a : 0);
+
+ DWC_PRINTF("\n");
+ DWC_PRINTF("Frame remaining at sample point B (uframe 7):\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->hfnum_7_samples_b, hcd->hfnum_7_frrem_accum_b,
+ (hcd->hfnum_7_samples_b > 0) ?
+ hcd->hfnum_7_frrem_accum_b / hcd->hfnum_7_samples_b : 0);
+ DWC_PRINTF("Frame remaining at sample point B (uframe 0):\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->hfnum_0_samples_b, hcd->hfnum_0_frrem_accum_b,
+ (hcd->hfnum_0_samples_b > 0) ?
+ hcd->hfnum_0_frrem_accum_b / hcd->hfnum_0_samples_b : 0);
+ DWC_PRINTF("Frame remaining at sample point B (uframe 1-6):\n");
+ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
+ hcd->hfnum_other_samples_b, hcd->hfnum_other_frrem_accum_b,
+ (hcd->hfnum_other_samples_b > 0) ?
+ hcd->hfnum_other_frrem_accum_b /
+ hcd->hfnum_other_samples_b : 0);
+#endif
+}
+
+#endif /* DWC_DEVICE_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd.h b/drivers/usb/dwc_otg/dwc_otg_hcd.h
new file mode 100644
index 0000000..58a1293
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd.h
@@ -0,0 +1,803 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $
+ * $Revision: #58 $
+ * $Date: 2011/09/15 $
+ * $Change: 1846647 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+#ifndef __DWC_HCD_H__
+#define __DWC_HCD_H__
+
+#include "dwc_otg_os_dep.h"
+#include "usb.h"
+#include "dwc_otg_hcd_if.h"
+#include "dwc_otg_core_if.h"
+#include "dwc_list.h"
+#include "dwc_otg_cil.h"
+
+/**
+ * @file
+ *
+ * This file contains the structures, constants, and interfaces for
+ * the Host Contoller Driver (HCD).
+ *
+ * The Host Controller Driver (HCD) is responsible for translating requests
+ * from the USB Driver into the appropriate actions on the DWC_otg controller.
+ * It isolates the USBD from the specifics of the controller by providing an
+ * API to the USBD.
+ */
+
+struct dwc_otg_hcd_pipe_info {
+ uint8_t dev_addr;
+ uint8_t ep_num;
+ uint8_t pipe_type;
+ uint8_t pipe_dir;
+ uint16_t mps;
+};
+
+struct dwc_otg_hcd_iso_packet_desc {
+ uint32_t offset;
+ uint32_t length;
+ uint32_t actual_length;
+ uint32_t status;
+};
+
+struct dwc_otg_qtd;
+
+struct dwc_otg_hcd_urb {
+ void *priv;
+ struct dwc_otg_qtd *qtd;
+ void *buf;
+ dwc_dma_t dma;
+ void *setup_packet;
+ dwc_dma_t setup_dma;
+ uint32_t length;
+ uint32_t actual_length;
+ uint32_t status;
+ uint32_t error_count;
+ uint32_t packet_count;
+ uint32_t flags;
+ uint16_t interval;
+ struct dwc_otg_hcd_pipe_info pipe_info;
+ struct dwc_otg_hcd_iso_packet_desc iso_descs[0];
+};
+
+static inline uint8_t dwc_otg_hcd_get_ep_num(struct dwc_otg_hcd_pipe_info *pipe)
+{
+ return pipe->ep_num;
+}
+
+static inline uint8_t dwc_otg_hcd_get_pipe_type(struct dwc_otg_hcd_pipe_info
+ *pipe)
+{
+ return pipe->pipe_type;
+}
+
+static inline uint16_t dwc_otg_hcd_get_mps(struct dwc_otg_hcd_pipe_info *pipe)
+{
+ return pipe->mps;
+}
+
+static inline uint8_t dwc_otg_hcd_get_dev_addr(struct dwc_otg_hcd_pipe_info
+ *pipe)
+{
+ return pipe->dev_addr;
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_isoc(struct dwc_otg_hcd_pipe_info
+ *pipe)
+{
+ return (pipe->pipe_type == UE_ISOCHRONOUS);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_int(struct dwc_otg_hcd_pipe_info
+ *pipe)
+{
+ return (pipe->pipe_type == UE_INTERRUPT);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_bulk(struct dwc_otg_hcd_pipe_info
+ *pipe)
+{
+ return (pipe->pipe_type == UE_BULK);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_control(struct dwc_otg_hcd_pipe_info
+ *pipe)
+{
+ return (pipe->pipe_type == UE_CONTROL);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_in(struct dwc_otg_hcd_pipe_info *pipe)
+{
+ return (pipe->pipe_dir == UE_DIR_IN);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_out(struct dwc_otg_hcd_pipe_info
+ *pipe)
+{
+ return (!dwc_otg_hcd_is_pipe_in(pipe));
+}
+
+static inline void dwc_otg_hcd_fill_pipe(struct dwc_otg_hcd_pipe_info *pipe,
+ uint8_t devaddr, uint8_t ep_num,
+ uint8_t pipe_type, uint8_t pipe_dir,
+ uint16_t mps)
+{
+ pipe->dev_addr = devaddr;
+ pipe->ep_num = ep_num;
+ pipe->pipe_type = pipe_type;
+ pipe->pipe_dir = pipe_dir;
+ pipe->mps = mps;
+}
+
+/**
+ * Phases for control transfers.
+ */
+typedef enum dwc_otg_control_phase {
+ DWC_OTG_CONTROL_SETUP,
+ DWC_OTG_CONTROL_DATA,
+ DWC_OTG_CONTROL_STATUS
+} dwc_otg_control_phase_e;
+
+/** Transaction types. */
+typedef enum dwc_otg_transaction_type {
+ DWC_OTG_TRANSACTION_NONE,
+ DWC_OTG_TRANSACTION_PERIODIC,
+ DWC_OTG_TRANSACTION_NON_PERIODIC,
+ DWC_OTG_TRANSACTION_ALL
+} dwc_otg_transaction_type_e;
+
+struct dwc_otg_qh;
+
+/**
+ * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
+ * interrupt, or isochronous transfer. A single QTD is created for each URB
+ * (of one of these types) submitted to the HCD. The transfer associated with
+ * a QTD may require one or multiple transactions.
+ *
+ * A QTD is linked to a Queue Head, which is entered in either the
+ * non-periodic or periodic schedule for execution. When a QTD is chosen for
+ * execution, some or all of its transactions may be executed. After
+ * execution, the state of the QTD is updated. The QTD may be retired if all
+ * its transactions are complete or if an error occurred. Otherwise, it
+ * remains in the schedule so more transactions can be executed later.
+ */
+typedef struct dwc_otg_qtd {
+ /**
+ * Determines the PID of the next data packet for the data phase of
+ * control transfers. Ignored for other transfer types.<br>
+ * One of the following values:
+ * - DWC_OTG_HC_PID_DATA0
+ * - DWC_OTG_HC_PID_DATA1
+ */
+ uint8_t data_toggle;
+
+ /** Current phase for control transfers (Setup, Data, or Status). */
+ dwc_otg_control_phase_e control_phase;
+
+ /** Keep track of the current split type
+ * for FS/LS endpoints on a HS Hub */
+ uint8_t complete_split;
+
+ /** How many bytes transferred during SSPLIT OUT */
+ uint32_t ssplit_out_xfer_count;
+
+ /**
+ * Holds the number of bus errors that have occurred for a transaction
+ * within this transfer.
+ */
+ uint8_t error_count;
+
+ /**
+ * Index of the next frame descriptor for an isochronous transfer. A
+ * frame descriptor describes the buffer position and length of the
+ * data to be transferred in the next scheduled (micro)frame of an
+ * isochronous transfer. It also holds status for that transaction.
+ * The frame index starts at 0.
+ */
+ uint16_t isoc_frame_index;
+
+ /** Position of the ISOC split on full/low speed */
+ uint8_t isoc_split_pos;
+
+ /** Position of the ISOC split in the buffer for the current frame */
+ uint16_t isoc_split_offset;
+
+ /** URB for this transfer */
+ struct dwc_otg_hcd_urb *urb;
+
+ struct dwc_otg_qh *qh;
+
+ /** This list of QTDs */
+ DWC_CIRCLEQ_ENTRY(dwc_otg_qtd) qtd_list_entry;
+
+ /** Indicates if this QTD is currently processed by HW. */
+ uint8_t in_process;
+
+ /** Number of DMA descriptors for this QTD */
+ uint8_t n_desc;
+
+ /**
+ * Last activated frame(packet) index.
+ * Used in Descriptor DMA mode only.
+ */
+ uint16_t isoc_frame_index_last;
+
+} dwc_otg_qtd_t;
+
+DWC_CIRCLEQ_HEAD(dwc_otg_qtd_list, dwc_otg_qtd);
+
+/**
+ * A Queue Head (QH) holds the static characteristics of an endpoint and
+ * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
+ * be entered in either the non-periodic or periodic schedule.
+ */
+typedef struct dwc_otg_qh {
+ /**
+ * Endpoint type.
+ * One of the following values:
+ * - UE_CONTROL
+ * - UE_BULK
+ * - UE_INTERRUPT
+ * - UE_ISOCHRONOUS
+ */
+ uint8_t ep_type;
+ uint8_t ep_is_in;
+
+ /** wMaxPacketSize Field of Endpoint Descriptor. */
+ uint16_t maxp;
+
+ /**
+ * Device speed.
+ * One of the following values:
+ * - DWC_OTG_EP_SPEED_LOW
+ * - DWC_OTG_EP_SPEED_FULL
+ * - DWC_OTG_EP_SPEED_HIGH
+ */
+ uint8_t dev_speed;
+
+ /**
+ * Determines the PID of the next data packet for non-control
+ * transfers. Ignored for control transfers.<br>
+ * One of the following values:
+ * - DWC_OTG_HC_PID_DATA0
+ * - DWC_OTG_HC_PID_DATA1
+ */
+ uint8_t data_toggle;
+
+ /** Ping state if 1. */
+ uint8_t ping_state;
+
+ /**
+ * List of QTDs for this QH.
+ */
+ struct dwc_otg_qtd_list qtd_list;
+
+ /** Host channel currently processing transfers for this QH. */
+ struct dwc_hc *channel;
+
+ /** Full/low speed endpoint on high-speed hub requires split. */
+ uint8_t do_split;
+
+ /** @name Periodic schedule information */
+ /** @{ */
+
+ /** Bandwidth in microseconds per (micro)frame. */
+ uint16_t usecs;
+
+ /** Interval between transfers in (micro)frames. */
+ uint16_t interval;
+
+ /**
+ * (micro)frame to initialize a periodic transfer. The transfer
+ * executes in the following (micro)frame.
+ */
+ uint16_t sched_frame;
+
+ /** (micro)frame at which last start split was initialized. */
+ uint16_t start_split_frame;
+
+ /** @} */
+
+ /**
+ * Used instead of original buffer if
+ * it(physical address) is not dword-aligned.
+ */
+ uint8_t *dw_align_buf;
+ dwc_dma_t dw_align_buf_dma;
+
+ /** Entry for QH in either the periodic or non-periodic schedule. */
+ dwc_list_link_t qh_list_entry;
+
+ /** @name Descriptor DMA support */
+ /** @{ */
+
+ /** Descriptor List. */
+ dwc_otg_host_dma_desc_t *desc_list;
+
+ /** Descriptor List physical address. */
+ dwc_dma_t desc_list_dma;
+
+ /**
+ * Xfer Bytes array.
+ * Each element corresponds to a descriptor and indicates
+ * original XferSize size value for the descriptor.
+ */
+ uint32_t *n_bytes;
+
+ /** Actual number of transfer descriptors in a list. */
+ uint16_t ntd;
+
+ /** First activated isochronous transfer descriptor index. */
+ uint8_t td_first;
+ /** Last activated isochronous transfer descriptor index. */
+ uint8_t td_last;
+
+ /** @} */
+
+} dwc_otg_qh_t;
+
+DWC_CIRCLEQ_HEAD(hc_list, dwc_hc);
+
+/**
+ * This structure holds the state of the HCD, including the non-periodic and
+ * periodic schedules.
+ */
+struct dwc_otg_hcd {
+ /** The DWC otg device pointer */
+ struct dwc_otg_device *otg_dev;
+ /** DWC OTG Core Interface Layer */
+ dwc_otg_core_if_t *core_if;
+
+ /** Function HCD driver callbacks */
+ struct dwc_otg_hcd_function_ops *fops;
+
+ /** Internal DWC HCD Flags */
+ volatile union dwc_otg_hcd_internal_flags {
+ uint32_t d32;
+ struct {
+ unsigned port_connect_status_change:1;
+ unsigned port_connect_status:1;
+ unsigned port_reset_change:1;
+ unsigned port_enable_change:1;
+ unsigned port_suspend_change:1;
+ unsigned port_over_current_change:1;
+ unsigned port_l1_change:1;
+ unsigned reserved:26;
+ } b;
+ } flags;
+
+ /**
+ * Inactive items in the non-periodic schedule. This is a list of
+ * Queue Heads. Transfers associated with these Queue Heads are not
+ * currently assigned to a host channel.
+ */
+ dwc_list_link_t non_periodic_sched_inactive;
+
+ /**
+ * Active items in the non-periodic schedule. This is a list of
+ * Queue Heads. Transfers associated with these Queue Heads are
+ * currently assigned to a host channel.
+ */
+ dwc_list_link_t non_periodic_sched_active;
+
+ /**
+ * Pointer to the next Queue Head to process in the active
+ * non-periodic schedule.
+ */
+ dwc_list_link_t *non_periodic_qh_ptr;
+
+ /**
+ * Inactive items in the periodic schedule. This is a list of QHs for
+ * periodic transfers that are _not_ scheduled for the next frame.
+ * Each QH in the list has an interval counter that determines when it
+ * needs to be scheduled for execution. This scheduling mechanism
+ * allows only a simple calculation for periodic bandwidth used (i.e.
+ * must assume that all periodic transfers may need to execute in the
+ * same frame). However, it greatly simplifies scheduling and should
+ * be sufficient for the vast majority of OTG hosts, which need to
+ * connect to a small number of peripherals at one time.
+ *
+ * Items move from this list to periodic_sched_ready when the QH
+ * interval counter is 0 at SOF.
+ */
+ dwc_list_link_t periodic_sched_inactive;
+
+ /**
+ * List of periodic QHs that are ready for execution in the next
+ * frame, but have not yet been assigned to host channels.
+ *
+ * Items move from this list to periodic_sched_assigned as host
+ * channels become available during the current frame.
+ */
+ dwc_list_link_t periodic_sched_ready;
+
+ /**
+ * List of periodic QHs to be executed in the next frame that are
+ * assigned to host channels.
+ *
+ * Items move from this list to periodic_sched_queued as the
+ * transactions for the QH are queued to the DWC_otg controller.
+ */
+ dwc_list_link_t periodic_sched_assigned;
+
+ /**
+ * List of periodic QHs that have been queued for execution.
+ *
+ * Items move from this list to either periodic_sched_inactive or
+ * periodic_sched_ready when the channel associated with the transfer
+ * is released. If the interval for the QH is 1, the item moves to
+ * periodic_sched_ready because it must be rescheduled for the next
+ * frame. Otherwise, the item moves to periodic_sched_inactive.
+ */
+ dwc_list_link_t periodic_sched_queued;
+
+ /**
+ * Total bandwidth claimed so far for periodic transfers. This value
+ * is in microseconds per (micro)frame. The assumption is that all
+ * periodic transfers may occur in the same (micro)frame.
+ */
+ uint16_t periodic_usecs;
+
+ /**
+ * Frame number read from the core at SOF. The value ranges from 0 to
+ * DWC_HFNUM_MAX_FRNUM.
+ */
+ uint16_t frame_number;
+
+ /**
+ * Count of periodic QHs, if using several eps. For SOF enable/disable.
+ */
+ uint16_t periodic_qh_count;
+
+ /**
+ * Free host channels in the controller. This is a list of
+ * dwc_hc_t items.
+ */
+ struct hc_list free_hc_list;
+ /**
+ * Number of host channels assigned to periodic transfers. Currently
+ * assuming that there is a dedicated host channel for each periodic
+ * transaction and at least one host channel available for
+ * non-periodic transactions.
+ */
+ int periodic_channels;
+
+ /**
+ * Number of host channels assigned to non-periodic transfers.
+ */
+ int non_periodic_channels;
+
+ /**
+ * Array of pointers to the host channel descriptors. Allows accessing
+ * a host channel descriptor given the host channel number. This is
+ * useful in interrupt handlers.
+ */
+ struct dwc_hc *hc_ptr_array[MAX_EPS_CHANNELS];
+
+ /**
+ * Buffer to use for any data received during the status phase of a
+ * control transfer. Normally no data is transferred during the status
+ * phase. This buffer is used as a bit bucket.
+ */
+ uint8_t *status_buf;
+
+ /**
+ * DMA address for status_buf.
+ */
+ dma_addr_t status_buf_dma;
+#define DWC_OTG_HCD_STATUS_BUF_SIZE 64
+
+ /**
+ * Connection timer. An OTG host must display a message if the device
+ * does not connect. Started when the VBus power is turned on via
+ * sysfs attribute "buspower".
+ */
+ dwc_timer_t *conn_timer;
+
+ /* Tasket to do a reset */
+ dwc_tasklet_t *reset_tasklet;
+
+ /* */
+ dwc_spinlock_t *lock;
+
+ /**
+ * Private data that could be used by OS wrapper.
+ */
+ void *priv;
+
+ uint8_t otg_port;
+
+ /** Frame List */
+ uint32_t *frame_list;
+
+ /** Frame List DMA address */
+ dma_addr_t frame_list_dma;
+
+#ifdef DEBUG
+ uint32_t frrem_samples;
+ uint64_t frrem_accum;
+
+ uint32_t hfnum_7_samples_a;
+ uint64_t hfnum_7_frrem_accum_a;
+ uint32_t hfnum_0_samples_a;
+ uint64_t hfnum_0_frrem_accum_a;
+ uint32_t hfnum_other_samples_a;
+ uint64_t hfnum_other_frrem_accum_a;
+
+ uint32_t hfnum_7_samples_b;
+ uint64_t hfnum_7_frrem_accum_b;
+ uint32_t hfnum_0_samples_b;
+ uint64_t hfnum_0_frrem_accum_b;
+ uint32_t hfnum_other_samples_b;
+ uint64_t hfnum_other_frrem_accum_b;
+#endif
+};
+
+/** @name Transaction Execution Functions */
+/** @{ */
+extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t
+ * hcd);
+extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
+ dwc_otg_transaction_type_e tr_type);
+
+/** @} */
+
+/** @name Interrupt Handler Functions */
+/** @{ */
+extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t *
+ dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t *
+ dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t *
+ dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_incomplete_periodic_intr(dwc_otg_hcd_t *
+ dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_conn_id_status_change_intr(dwc_otg_hcd_t *
+ dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_disconnect_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd,
+ uint32_t num);
+extern int32_t dwc_otg_hcd_handle_session_req_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_wakeup_detected_intr(dwc_otg_hcd_t *
+ dwc_otg_hcd);
+/** @} */
+
+/** @name Schedule Queue Functions */
+/** @{ */
+
+/* Implemented in dwc_otg_hcd_queue.c */
+extern dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd,
+ dwc_otg_hcd_urb_t * urb, int atomic_alloc);
+extern void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+ int sched_csplit);
+
+/** Remove and free a QH */
+static inline void dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd_t * hcd,
+ dwc_otg_qh_t * qh)
+{
+ dwc_irqflags_t flags;
+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+ dwc_otg_hcd_qh_free(hcd, qh);
+}
+
+/** Allocates memory for a QH structure.
+ * @return Returns the memory allocate or NULL on error. */
+static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc(int atomic_alloc)
+{
+ if (atomic_alloc)
+ return (dwc_otg_qh_t *) DWC_ALLOC_ATOMIC(sizeof(dwc_otg_qh_t));
+ else
+ return (dwc_otg_qh_t *) DWC_ALLOC(sizeof(dwc_otg_qh_t));
+}
+
+extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb,
+ int atomic_alloc);
+extern void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb);
+extern int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, dwc_otg_hcd_t * dwc_otg_hcd,
+ dwc_otg_qh_t ** qh, int atomic_alloc);
+
+/** Allocates memory for a QTD structure.
+ * @return Returns the memory allocate or NULL on error. */
+static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc(int atomic_alloc)
+{
+ if (atomic_alloc)
+ return (dwc_otg_qtd_t *) DWC_ALLOC_ATOMIC(sizeof(dwc_otg_qtd_t));
+ else
+ return (dwc_otg_qtd_t *) DWC_ALLOC(sizeof(dwc_otg_qtd_t));
+}
+
+/** Frees the memory for a QTD structure. QTD should already be removed from
+ * list.
+ * @param qtd QTD to free.*/
+static inline void dwc_otg_hcd_qtd_free(dwc_otg_qtd_t * qtd)
+{
+ DWC_FREE(qtd);
+}
+
+/** Removes a QTD from list.
+ * @param hcd HCD instance.
+ * @param qtd QTD to remove from list.
+ * @param qh QTD belongs to.
+ */
+static inline void dwc_otg_hcd_qtd_remove(dwc_otg_hcd_t * hcd,
+ dwc_otg_qtd_t * qtd,
+ dwc_otg_qh_t * qh)
+{
+ DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry);
+}
+
+/** Remove and free a QTD
+ * Need to disable IRQ and hold hcd lock while calling this function out of
+ * interrupt servicing chain */
+static inline void dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd_t * hcd,
+ dwc_otg_qtd_t * qtd,
+ dwc_otg_qh_t * qh)
+{
+ dwc_otg_hcd_qtd_remove(hcd, qtd, qh);
+ dwc_otg_hcd_qtd_free(qtd);
+}
+
+/** @} */
+
+/** @name Descriptor DMA Supporting Functions */
+/** @{ */
+
+extern void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_halt_status_e halt_status);
+
+extern int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+
+/** @} */
+
+/** @name Internal Functions */
+/** @{ */
+dwc_otg_qh_t *dwc_urb_to_qh(dwc_otg_hcd_urb_t * urb);
+/** @} */
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+extern int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd,
+ uint8_t devaddr);
+extern void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd);
+#endif
+
+/** Gets the QH that contains the list_head */
+#define dwc_list_to_qh(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qh_t, qh_list_entry)
+
+/** Gets the QTD that contains the list_head */
+#define dwc_list_to_qtd(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qtd_t, qtd_list_entry)
+
+/** Check if QH is non-periodic */
+#define dwc_qh_is_non_per(_qh_ptr_) ((_qh_ptr_->ep_type == UE_BULK) || \
+ (_qh_ptr_->ep_type == UE_CONTROL))
+
+/** High bandwidth multiplier as encoded in highspeed endpoint descriptors */
+#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
+/** Packet size for any kind of endpoint descriptor */
+#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/**
+ * Returns true if _frame1 is less than or equal to _frame2. The comparison is
+ * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the
+ * frame number when the max frame number is reached.
+ */
+static inline int dwc_frame_num_le(uint16_t frame1, uint16_t frame2)
+{
+ return ((frame2 - frame1) & DWC_HFNUM_MAX_FRNUM) <=
+ (DWC_HFNUM_MAX_FRNUM >> 1);
+}
+
+/**
+ * Returns true if _frame1 is greater than _frame2. The comparison is done
+ * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame
+ * number when the max frame number is reached.
+ */
+static inline int dwc_frame_num_gt(uint16_t frame1, uint16_t frame2)
+{
+ return (frame1 != frame2) &&
+ (((frame1 - frame2) & DWC_HFNUM_MAX_FRNUM) <
+ (DWC_HFNUM_MAX_FRNUM >> 1));
+}
+
+/**
+ * Increments _frame by the amount specified by _inc. The addition is done
+ * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value.
+ */
+static inline uint16_t dwc_frame_num_inc(uint16_t frame, uint16_t inc)
+{
+ return (frame + inc) & DWC_HFNUM_MAX_FRNUM;
+}
+
+static inline uint16_t dwc_full_frame_num(uint16_t frame)
+{
+ return (frame & DWC_HFNUM_MAX_FRNUM) >> 3;
+}
+
+static inline uint16_t dwc_micro_frame_num(uint16_t frame)
+{
+ return frame & 0x7;
+}
+
+void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd);
+
+#ifdef DEBUG
+/**
+ * Macro to sample the remaining PHY clocks left in the current frame. This
+ * may be used during debugging to determine the average time it takes to
+ * execute sections of code. There are two possible sample points, "a" and
+ * "b", so the _letter argument must be one of these values.
+ *
+ * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For
+ * example, "cat /sys/devices/lm0/hcd_frrem".
+ */
+#define dwc_sample_frrem(_hcd, _qh, _letter) \
+{ \
+ hfnum_data_t hfnum; \
+ dwc_otg_qtd_t *qtd; \
+ qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); \
+ if (usb_pipeint(qtd->urb->pipe) && _qh->start_split_frame != 0 && !qtd->complete_split) { \
+ hfnum.d32 = DWC_READ_REG32(&_hcd->core_if->host_if->host_global_regs->hfnum); \
+ switch (hfnum.b.frnum & 0x7) { \
+ case 7: \
+ _hcd->hfnum_7_samples_##_letter++; \
+ _hcd->hfnum_7_frrem_accum_##_letter += hfnum.b.frrem; \
+ break; \
+ case 0: \
+ _hcd->hfnum_0_samples_##_letter++; \
+ _hcd->hfnum_0_frrem_accum_##_letter += hfnum.b.frrem; \
+ break; \
+ default: \
+ _hcd->hfnum_other_samples_##_letter++; \
+ _hcd->hfnum_other_frrem_accum_##_letter += hfnum.b.frrem; \
+ break; \
+ } \
+ } \
+}
+#else
+#define dwc_sample_frrem(_hcd, _qh, _letter)
+#endif
+#endif
+#endif /* DWC_DEVICE_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_ddma.c b/drivers/usb/dwc_otg/dwc_otg_hcd_ddma.c
new file mode 100644
index 0000000..6911e04
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd_ddma.c
@@ -0,0 +1,1122 @@
+/*==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_ddma.c $
+ * $Revision: #10 $
+ * $Date: 2011/10/20 $
+ * $Change: 1869464 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+/** @file
+ * This file contains Descriptor DMA support implementation for host mode.
+ */
+
+#include "dwc_otg_hcd.h"
+#include "dwc_otg_regs.h"
+
+static inline uint8_t frame_list_idx(uint16_t frame)
+{
+ return (frame & (MAX_FRLIST_EN_NUM - 1));
+}
+
+static inline uint16_t desclist_idx_inc(uint16_t idx, uint16_t inc, uint8_t speed)
+{
+ return (idx + inc) &
+ (((speed ==
+ DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC :
+ MAX_DMA_DESC_NUM_GENERIC) - 1);
+}
+
+static inline uint16_t desclist_idx_dec(uint16_t idx, uint16_t inc, uint8_t speed)
+{
+ return (idx - inc) &
+ (((speed ==
+ DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC :
+ MAX_DMA_DESC_NUM_GENERIC) - 1);
+}
+
+static inline uint16_t max_desc_num(dwc_otg_qh_t * qh)
+{
+ return (((qh->ep_type == UE_ISOCHRONOUS)
+ && (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH))
+ ? MAX_DMA_DESC_NUM_HS_ISOC : MAX_DMA_DESC_NUM_GENERIC);
+}
+static inline uint16_t frame_incr_val(dwc_otg_qh_t * qh)
+{
+ return ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH)
+ ? ((qh->interval + 8 - 1) / 8)
+ : qh->interval);
+}
+
+static int desc_list_alloc(dwc_otg_qh_t * qh)
+{
+ int retval = 0;
+
+ qh->desc_list = (dwc_otg_host_dma_desc_t *)
+ DWC_DMA_ALLOC(sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh),
+ &qh->desc_list_dma);
+
+ if (!qh->desc_list) {
+ retval = -DWC_E_NO_MEMORY;
+ DWC_ERROR("%s: DMA descriptor list allocation failed\n", __func__);
+
+ }
+
+ dwc_memset(qh->desc_list, 0x00,
+ sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
+
+ qh->n_bytes =
+ (uint32_t *) DWC_ALLOC(sizeof(uint32_t) * max_desc_num(qh));
+
+ if (!qh->n_bytes) {
+ retval = -DWC_E_NO_MEMORY;
+ DWC_ERROR
+ ("%s: Failed to allocate array for descriptors' size actual values\n",
+ __func__);
+
+ }
+ return retval;
+
+}
+
+static void desc_list_free(dwc_otg_qh_t * qh)
+{
+ if (qh->desc_list) {
+ DWC_DMA_FREE(max_desc_num(qh), qh->desc_list,
+ qh->desc_list_dma);
+ qh->desc_list = NULL;
+ }
+
+ if (qh->n_bytes) {
+ DWC_FREE(qh->n_bytes);
+ qh->n_bytes = NULL;
+ }
+}
+
+static int frame_list_alloc(dwc_otg_hcd_t * hcd)
+{
+ int retval = 0;
+ if (hcd->frame_list)
+ return 0;
+
+ hcd->frame_list = DWC_DMA_ALLOC(4 * MAX_FRLIST_EN_NUM,
+ &hcd->frame_list_dma);
+ if (!hcd->frame_list) {
+ retval = -DWC_E_NO_MEMORY;
+ DWC_ERROR("%s: Frame List allocation failed\n", __func__);
+ }
+
+ dwc_memset(hcd->frame_list, 0x00, 4 * MAX_FRLIST_EN_NUM);
+
+ return retval;
+}
+
+static void frame_list_free(dwc_otg_hcd_t * hcd)
+{
+ if (!hcd->frame_list)
+ return;
+
+ DWC_DMA_FREE(4 * MAX_FRLIST_EN_NUM, hcd->frame_list, hcd->frame_list_dma);
+ hcd->frame_list = NULL;
+}
+
+static void per_sched_enable(dwc_otg_hcd_t * hcd, uint16_t fr_list_en)
+{
+
+ hcfg_data_t hcfg;
+
+ hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg);
+
+ if (hcfg.b.perschedena) {
+ /* already enabled */
+ return;
+ }
+
+ DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hflbaddr,
+ hcd->frame_list_dma);
+
+ switch (fr_list_en) {
+ case 64:
+ hcfg.b.frlisten = 3;
+ break;
+ case 32:
+ hcfg.b.frlisten = 2;
+ break;
+ case 16:
+ hcfg.b.frlisten = 1;
+ break;
+ case 8:
+ hcfg.b.frlisten = 0;
+ break;
+ default:
+ break;
+ }
+
+ hcfg.b.perschedena = 1;
+
+ DWC_DEBUGPL(DBG_HCD, "Enabling Periodic schedule\n");
+ DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32);
+
+}
+
+static void per_sched_disable(dwc_otg_hcd_t * hcd)
+{
+ hcfg_data_t hcfg;
+
+ hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg);
+
+ if (!hcfg.b.perschedena) {
+ /* already disabled */
+ return;
+ }
+ hcfg.b.perschedena = 0;
+
+ DWC_DEBUGPL(DBG_HCD, "Disabling Periodic schedule\n");
+ DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32);
+}
+
+/*
+ * Activates/Deactivates FrameList entries for the channel
+ * based on endpoint servicing period.
+ */
+void update_frame_list(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, uint8_t enable)
+{
+ uint16_t i, j, inc;
+ dwc_hc_t *hc = NULL;
+
+ if (!qh->channel) {
+ DWC_ERROR("qh->channel = %p", qh->channel);
+ return;
+ }
+
+ if (!hcd) {
+ DWC_ERROR("------hcd = %p", hcd);
+ return;
+ }
+
+ if (!hcd->frame_list) {
+ DWC_ERROR("-------hcd->frame_list = %p", hcd->frame_list);
+ return;
+ }
+
+ hc = qh->channel;
+ inc = frame_incr_val(qh);
+ if (qh->ep_type == UE_ISOCHRONOUS)
+ i = frame_list_idx(qh->sched_frame);
+ else
+ i = 0;
+
+ j = i;
+ do {
+ if (enable)
+ hcd->frame_list[j] |= (1 << hc->hc_num);
+ else
+ hcd->frame_list[j] &= ~(1 << hc->hc_num);
+ j = (j + inc) & (MAX_FRLIST_EN_NUM - 1);
+ }
+ while (j != i);
+ if (!enable)
+ return;
+ hc->schinfo = 0;
+ if (qh->channel->speed == DWC_OTG_EP_SPEED_HIGH) {
+ j = 1;
+ /* TODO - check this */
+ inc = (8 + qh->interval - 1) / qh->interval;
+ for (i = 0; i < inc; i++) {
+ hc->schinfo |= j;
+ j = j << qh->interval;
+ }
+ } else {
+ hc->schinfo = 0xff;
+ }
+}
+
+#if 1
+void dump_frame_list(dwc_otg_hcd_t * hcd)
+{
+ int i = 0;
+ DWC_PRINTF("--FRAME LIST (hex) --\n");
+ for (i = 0; i < MAX_FRLIST_EN_NUM; i++) {
+ DWC_PRINTF("%x\t", hcd->frame_list[i]);
+ if (!(i % 8) && i)
+ DWC_PRINTF("\n");
+ }
+ DWC_PRINTF("\n----\n");
+
+}
+#endif
+
+static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ dwc_hc_t *hc = qh->channel;
+ if (dwc_qh_is_non_per(qh))
+ hcd->non_periodic_channels--;
+ else
+ update_frame_list(hcd, qh, 0);
+
+ /*
+ * The condition is added to prevent double cleanup try in case of device
+ * disconnect. See channel cleanup in dwc_otg_hcd_disconnect_cb().
+ */
+ if (hc->qh) {
+ dwc_otg_hc_cleanup(hcd->core_if, hc);
+ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
+ hc->qh = NULL;
+ }
+
+ qh->channel = NULL;
+ qh->ntd = 0;
+
+ if (qh->desc_list) {
+ dwc_memset(qh->desc_list, 0x00,
+ sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
+ }
+}
+
+/**
+ * Initializes a QH structure's Descriptor DMA related members.
+ * Allocates memory for descriptor list.
+ * On first periodic QH, allocates memory for FrameList
+ * and enables periodic scheduling.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh The QH to init.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ int retval = 0;
+
+ if (qh->do_split) {
+ DWC_ERROR("SPLIT Transfers are not supported in Descriptor DMA.\n");
+ return -1;
+ }
+
+ retval = desc_list_alloc(qh);
+
+ if ((retval == 0)
+ && (qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)) {
+ if (!hcd->frame_list) {
+ retval = frame_list_alloc(hcd);
+ /* Enable periodic schedule on first periodic QH */
+ if (retval == 0)
+ per_sched_enable(hcd, MAX_FRLIST_EN_NUM);
+ }
+ }
+
+ qh->ntd = 0;
+
+ return retval;
+}
+
+/**
+ * Frees descriptor list memory associated with the QH.
+ * If QH is periodic and the last, frees FrameList memory
+ * and disables periodic scheduling.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh The QH to init.
+ */
+void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ desc_list_free(qh);
+
+ /*
+ * Channel still assigned due to some reasons.
+ * Seen on Isoc URB dequeue. Channel halted but no subsequent
+ * ChHalted interrupt to release the channel. Afterwards
+ * when it comes here from endpoint disable routine
+ * channel remains assigned.
+ */
+ if (qh->channel)
+ release_channel_ddma(hcd, qh);
+
+ if ((qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)
+ && !hcd->periodic_channels && hcd->frame_list) {
+
+ per_sched_disable(hcd);
+ frame_list_free(hcd);
+ }
+}
+
+static uint8_t frame_to_desc_idx(dwc_otg_qh_t * qh, uint16_t frame_idx)
+{
+ if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) {
+ /*
+ * Descriptor set(8 descriptors) index
+ * which is 8-aligned.
+ */
+ return (frame_idx & ((MAX_DMA_DESC_NUM_HS_ISOC / 8) - 1)) * 8;
+ } else {
+ return (frame_idx & (MAX_DMA_DESC_NUM_GENERIC - 1));
+ }
+}
+
+/*
+ * Determine starting frame for Isochronous transfer.
+ * Few frames skipped to prevent race condition with HC.
+ */
+static uint8_t calc_starting_frame(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+ uint8_t * skip_frames)
+{
+ uint16_t frame = 0;
+ hcd->frame_number = dwc_otg_hcd_get_frame_number(hcd);
+
+ /* sched_frame is always frame number(not uFrame) both in FS and HS !! */
+
+ /*
+ * skip_frames is used to limit activated descriptors number
+ * to avoid the situation when HC services the last activated
+ * descriptor firstly.
+ * Example for FS:
+ * Current frame is 1, scheduled frame is 3. Since HC always fetches the descriptor
+ * corresponding to curr_frame+1, the descriptor corresponding to frame 2
+ * will be fetched. If the number of descriptors is max=64 (or greather) the
+ * list will be fully programmed with Active descriptors and it is possible
+ * case(rare) that the latest descriptor(considering rollback) corresponding
+ * to frame 2 will be serviced first. HS case is more probable because, in fact,
+ * up to 11 uframes(16 in the code) may be skipped.
+ */
+ if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) {
+ /*
+ * Consider uframe counter also, to start xfer asap.
+ * If half of the frame elapsed skip 2 frames otherwise
+ * just 1 frame.
+ * Starting descriptor index must be 8-aligned, so
+ * if the current frame is near to complete the next one
+ * is skipped as well.
+ */
+
+ if (dwc_micro_frame_num(hcd->frame_number) >= 5) {
+ *skip_frames = 2 * 8;
+ frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames);
+ } else {
+ *skip_frames = 1 * 8;
+ frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames);
+ }
+
+ frame = dwc_full_frame_num(frame);
+ } else {
+ /*
+ * Two frames are skipped for FS - the current and the next.
+ * But for descriptor programming, 1 frame(descriptor) is enough,
+ * see example above.
+ */
+ *skip_frames = 1;
+ frame = dwc_frame_num_inc(hcd->frame_number, 2);
+ }
+
+ return frame;
+}
+
+/*
+ * Calculate initial descriptor index for isochronous transfer
+ * based on scheduled frame.
+ */
+static uint8_t recalc_initial_desc_idx(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ uint16_t frame = 0, fr_idx, fr_idx_tmp;
+ uint8_t skip_frames = 0;
+ /*
+ * With current ISOC processing algorithm the channel is being
+ * released when no more QTDs in the list(qh->ntd == 0).
+ * Thus this function is called only when qh->ntd == 0 and qh->channel == 0.
+ *
+ * So qh->channel != NULL branch is not used and just not removed from the
+ * source file. It is required for another possible approach which is,
+ * do not disable and release the channel when ISOC session completed,
+ * just move QH to inactive schedule until new QTD arrives.
+ * On new QTD, the QH moved back to 'ready' schedule,
+ * starting frame and therefore starting desc_index are recalculated.
+ * In this case channel is released only on ep_disable.
+ */
+
+ /* Calculate starting descriptor index. For INTERRUPT endpoint it is always 0. */
+ if (qh->channel) {
+ frame = calc_starting_frame(hcd, qh, &skip_frames);
+ /*
+ * Calculate initial descriptor index based on FrameList current bitmap
+ * and servicing period.
+ */
+ fr_idx_tmp = frame_list_idx(frame);
+ fr_idx =
+ (MAX_FRLIST_EN_NUM + frame_list_idx(qh->sched_frame) -
+ fr_idx_tmp)
+ % frame_incr_val(qh);
+ fr_idx = (fr_idx + fr_idx_tmp) % MAX_FRLIST_EN_NUM;
+ } else {
+ qh->sched_frame = calc_starting_frame(hcd, qh, &skip_frames);
+ fr_idx = frame_list_idx(qh->sched_frame);
+ }
+
+ qh->td_first = qh->td_last = frame_to_desc_idx(qh, fr_idx);
+
+ return skip_frames;
+}
+
+#define ISOC_URB_GIVEBACK_ASAP
+
+#define MAX_ISOC_XFER_SIZE_FS 1023
+#define MAX_ISOC_XFER_SIZE_HS 3072
+#define DESCNUM_THRESHOLD 4
+
+static void init_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+ uint8_t skip_frames)
+{
+ struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+ dwc_otg_qtd_t *qtd;
+ dwc_otg_host_dma_desc_t *dma_desc;
+ uint16_t idx, inc, n_desc, ntd_max, max_xfer_size;
+
+ idx = qh->td_last;
+ inc = qh->interval;
+ n_desc = 0;
+
+ ntd_max = (max_desc_num(qh) + qh->interval - 1) / qh->interval;
+ if (skip_frames && !qh->channel)
+ ntd_max = ntd_max - skip_frames / qh->interval;
+
+ max_xfer_size =
+ (qh->dev_speed ==
+ DWC_OTG_EP_SPEED_HIGH) ? MAX_ISOC_XFER_SIZE_HS :
+ MAX_ISOC_XFER_SIZE_FS;
+
+ DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
+ while ((qh->ntd < ntd_max)
+ && (qtd->isoc_frame_index_last <
+ qtd->urb->packet_count)) {
+
+ dma_desc = &qh->desc_list[idx];
+ dwc_memset(dma_desc, 0x00, sizeof(dwc_otg_host_dma_desc_t));
+
+ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last];
+
+ if (frame_desc->length > max_xfer_size)
+ qh->n_bytes[idx] = max_xfer_size;
+ else
+ qh->n_bytes[idx] = frame_desc->length;
+ dma_desc->status.b_isoc.n_bytes = qh->n_bytes[idx];
+ dma_desc->status.b_isoc.a = 1;
+ dma_desc->status.b_isoc.sts = 0;
+
+ dma_desc->buf = qtd->urb->dma + frame_desc->offset;
+
+ qh->ntd++;
+
+ qtd->isoc_frame_index_last++;
+
+#ifdef ISOC_URB_GIVEBACK_ASAP
+ /*
+ * Set IOC for each descriptor corresponding to the
+ * last frame of the URB.
+ */
+ if (qtd->isoc_frame_index_last ==
+ qtd->urb->packet_count)
+ dma_desc->status.b_isoc.ioc = 1;
+
+#endif
+ idx = desclist_idx_inc(idx, inc, qh->dev_speed);
+ n_desc++;
+
+ }
+ qtd->in_process = 1;
+ }
+
+ qh->td_last = idx;
+
+#ifdef ISOC_URB_GIVEBACK_ASAP
+ /* Set IOC for the last descriptor if descriptor list is full */
+ if (qh->ntd == ntd_max) {
+ idx = desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
+ qh->desc_list[idx].status.b_isoc.ioc = 1;
+ }
+#else
+ /*
+ * Set IOC bit only for one descriptor.
+ * Always try to be ahead of HW processing,
+ * i.e. on IOC generation driver activates next descriptors but
+ * core continues to process descriptors followed the one with IOC set.
+ */
+
+ if (n_desc > DESCNUM_THRESHOLD) {
+ /*
+ * Move IOC "up". Required even if there is only one QTD
+ * in the list, cause QTDs migth continue to be queued,
+ * but during the activation it was only one queued.
+ * Actually more than one QTD might be in the list if this function called
+ * from XferCompletion - QTDs was queued during HW processing of the previous
+ * descriptor chunk.
+ */
+ idx = dwc_desclist_idx_dec(idx, inc * ((qh->ntd + 1) / 2), qh->dev_speed);
+ } else {
+ /*
+ * Set the IOC for the latest descriptor
+ * if either number of descriptor is not greather than threshold
+ * or no more new descriptors activated.
+ */
+ idx = dwc_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
+ }
+
+ qh->desc_list[idx].status.b_isoc.ioc = 1;
+#endif
+}
+
+static void init_non_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+
+ dwc_hc_t *hc;
+ dwc_otg_host_dma_desc_t *dma_desc;
+ dwc_otg_qtd_t *qtd;
+ int num_packets, len, n_desc = 0;
+
+ hc = qh->channel;
+
+ /*
+ * Start with hc->xfer_buff initialized in
+ * assign_and_init_hc(), then if SG transfer consists of multiple URBs,
+ * this pointer re-assigned to the buffer of the currently processed QTD.
+ * For non-SG request there is always one QTD active.
+ */
+
+ DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
+
+ if (n_desc) {
+ /* SG request - more than 1 QTDs */
+ hc->xfer_buff = (uint8_t *)qtd->urb->dma + qtd->urb->actual_length;
+ hc->xfer_len = qtd->urb->length - qtd->urb->actual_length;
+ }
+
+ qtd->n_desc = 0;
+
+ do {
+ dma_desc = &qh->desc_list[n_desc];
+ len = hc->xfer_len;
+
+ if (len > MAX_DMA_DESC_SIZE)
+ len = MAX_DMA_DESC_SIZE - hc->max_packet + 1;
+
+ if (hc->ep_is_in) {
+ if (len > 0) {
+ num_packets = (len + hc->max_packet - 1) / hc->max_packet;
+ } else {
+ /* Need 1 packet for transfer length of 0. */
+ num_packets = 1;
+ }
+ /* Always program an integral # of max packets for IN transfers. */
+ len = num_packets * hc->max_packet;
+ }
+
+ dma_desc->status.b.n_bytes = len;
+
+ qh->n_bytes[n_desc] = len;
+
+ if ((qh->ep_type == UE_CONTROL)
+ && (qtd->control_phase == DWC_OTG_CONTROL_SETUP))
+ dma_desc->status.b.sup = 1; /* Setup Packet */
+
+ dma_desc->status.b.a = 1; /* Active descriptor */
+ dma_desc->status.b.sts = 0;
+
+ dma_desc->buf =
+ ((unsigned long)hc->xfer_buff & 0xffffffff);
+
+ /*
+ * Last descriptor(or single) of IN transfer
+ * with actual size less than MaxPacket.
+ */
+ if (len > hc->xfer_len) {
+ hc->xfer_len = 0;
+ } else {
+ hc->xfer_buff += len;
+ hc->xfer_len -= len;
+ }
+
+ qtd->n_desc++;
+ n_desc++;
+ }
+ while ((hc->xfer_len > 0) && (n_desc != MAX_DMA_DESC_NUM_GENERIC));
+
+
+ qtd->in_process = 1;
+
+ if (qh->ep_type == UE_CONTROL)
+ break;
+
+ if (n_desc == MAX_DMA_DESC_NUM_GENERIC)
+ break;
+ }
+
+ if (n_desc) {
+ /* Request Transfer Complete interrupt for the last descriptor */
+ qh->desc_list[n_desc - 1].status.b.ioc = 1;
+ /* End of List indicator */
+ qh->desc_list[n_desc - 1].status.b.eol = 1;
+
+ hc->ntd = n_desc;
+ }
+}
+
+/**
+ * For Control and Bulk endpoints initializes descriptor list
+ * and starts the transfer.
+ *
+ * For Interrupt and Isochronous endpoints initializes descriptor list
+ * then updates FrameList, marking appropriate entries as active.
+ * In case of Isochronous, the starting descriptor index is calculated based
+ * on the scheduled frame, but only on the first transfer descriptor within a session.
+ * Then starts the transfer via enabling the channel.
+ * For Isochronous endpoint the channel is not halted on XferComplete
+ * interrupt so remains assigned to the endpoint(QH) until session is done.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh The QH to init.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ /* Channel is already assigned */
+ dwc_hc_t *hc = qh->channel;
+ uint8_t skip_frames = 0;
+
+ switch (hc->ep_type) {
+ case DWC_OTG_EP_TYPE_CONTROL:
+ case DWC_OTG_EP_TYPE_BULK:
+ init_non_isoc_dma_desc(hcd, qh);
+
+ dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
+ break;
+ case DWC_OTG_EP_TYPE_INTR:
+ init_non_isoc_dma_desc(hcd, qh);
+
+ update_frame_list(hcd, qh, 1);
+
+ dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
+ break;
+ case DWC_OTG_EP_TYPE_ISOC:
+
+ if (!qh->ntd)
+ skip_frames = recalc_initial_desc_idx(hcd, qh);
+
+ init_isoc_dma_desc(hcd, qh, skip_frames);
+
+ if (!hc->xfer_started) {
+
+ update_frame_list(hcd, qh, 1);
+
+ /*
+ * Always set to max, instead of actual size.
+ * Otherwise ntd will be changed with
+ * channel being enabled. Not recommended.
+ *
+ */
+ hc->ntd = max_desc_num(qh);
+ /* Enable channel only once for ISOC */
+ dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
+ }
+
+ break;
+ default:
+
+ break;
+ }
+}
+
+static void complete_isoc_xfer_ddma(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_halt_status_e halt_status)
+{
+ struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+ dwc_otg_qtd_t *qtd, *qtd_tmp;
+ dwc_otg_qh_t *qh;
+ dwc_otg_host_dma_desc_t *dma_desc;
+ uint16_t idx, remain;
+ uint8_t urb_compl;
+
+ qh = hc->qh;
+ idx = qh->td_first;
+
+ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
+ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry)
+ qtd->in_process = 0;
+ return;
+ } else if ((halt_status == DWC_OTG_HC_XFER_AHB_ERR) ||
+ (halt_status == DWC_OTG_HC_XFER_BABBLE_ERR)) {
+ /*
+ * Channel is halted in these error cases.
+ * Considered as serious issues.
+ * Complete all URBs marking all frames as failed,
+ * irrespective whether some of the descriptors(frames) succeeded or no.
+ * Pass error code to completion routine as well, to
+ * update urb->status, some of class drivers might use it to stop
+ * queing transfer requests.
+ */
+ int err = (halt_status == DWC_OTG_HC_XFER_AHB_ERR)
+ ? (-DWC_E_IO)
+ : (-DWC_E_OVERFLOW);
+
+ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
+ for (idx = 0; idx < qtd->urb->packet_count; idx++) {
+ frame_desc = &qtd->urb->iso_descs[idx];
+ frame_desc->status = err;
+ }
+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, err);
+ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+ }
+ return;
+ }
+
+ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
+
+ if (!qtd->in_process)
+ break;
+
+ urb_compl = 0;
+
+ do {
+
+ dma_desc = &qh->desc_list[idx];
+
+ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
+ remain = hc->ep_is_in ? dma_desc->status.b_isoc.n_bytes : 0;
+
+ if (dma_desc->status.b_isoc.sts == DMA_DESC_STS_PKTERR) {
+ /*
+ * XactError or, unable to complete all the transactions
+ * in the scheduled micro-frame/frame,
+ * both indicated by DMA_DESC_STS_PKTERR.
+ */
+ qtd->urb->error_count++;
+ frame_desc->actual_length = qh->n_bytes[idx] - remain;
+ frame_desc->status = -DWC_E_PROTOCOL;
+ } else {
+ /* Success */
+
+ frame_desc->actual_length = qh->n_bytes[idx] - remain;
+ frame_desc->status = 0;
+ }
+
+ if (++qtd->isoc_frame_index == qtd->urb->packet_count) {
+ /*
+ * urb->status is not used for isoc transfers here.
+ * The individual frame_desc status are used instead.
+ */
+
+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+
+ /*
+ * This check is necessary because urb_dequeue can be called
+ * from urb complete callback(sound driver example).
+ * All pending URBs are dequeued there, so no need for
+ * further processing.
+ */
+ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
+ return;
+ }
+
+ urb_compl = 1;
+
+ }
+
+ qh->ntd--;
+
+ /* Stop if IOC requested descriptor reached */
+ if (dma_desc->status.b_isoc.ioc) {
+ idx = desclist_idx_inc(idx, qh->interval, hc->speed);
+ goto stop_scan;
+ }
+
+ idx = desclist_idx_inc(idx, qh->interval, hc->speed);
+
+ if (urb_compl)
+ break;
+ }
+ while (idx != qh->td_first);
+ }
+stop_scan:
+ qh->td_first = idx;
+}
+
+uint8_t update_non_isoc_urb_state_ddma(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_qtd_t * qtd,
+ dwc_otg_host_dma_desc_t * dma_desc,
+ dwc_otg_halt_status_e halt_status,
+ uint32_t n_bytes, uint8_t * xfer_done)
+{
+
+ uint16_t remain = hc->ep_is_in ? dma_desc->status.b.n_bytes : 0;
+ dwc_otg_hcd_urb_t *urb = qtd->urb;
+
+ if (halt_status == DWC_OTG_HC_XFER_AHB_ERR) {
+ urb->status = -DWC_E_IO;
+ return 1;
+ }
+ if (dma_desc->status.b.sts == DMA_DESC_STS_PKTERR) {
+ switch (halt_status) {
+ case DWC_OTG_HC_XFER_STALL:
+ urb->status = -DWC_E_PIPE;
+ break;
+ case DWC_OTG_HC_XFER_BABBLE_ERR:
+ urb->status = -DWC_E_OVERFLOW;
+ break;
+ case DWC_OTG_HC_XFER_XACT_ERR:
+ urb->status = -DWC_E_PROTOCOL;
+ break;
+ default:
+ DWC_ERROR("%s: Unhandled descriptor error status (%d)\n", __func__,
+ halt_status);
+ break;
+ }
+ return 1;
+ }
+
+ if (dma_desc->status.b.a == 1) {
+ DWC_DEBUGPL(DBG_HCDV,
+ "Active descriptor encountered on channel %d\n",
+ hc->hc_num);
+ return 0;
+ }
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL) {
+ if (qtd->control_phase == DWC_OTG_CONTROL_DATA) {
+ urb->actual_length += n_bytes - remain;
+ if (remain || urb->actual_length == urb->length) {
+ /*
+ * For Control Data stage do not set urb->status=0 to prevent
+ * URB callback. Set it when Status phase done. See below.
+ */
+ *xfer_done = 1;
+ }
+
+ } else if (qtd->control_phase == DWC_OTG_CONTROL_STATUS) {
+ urb->status = 0;
+ *xfer_done = 1;
+ }
+ /* No handling for SETUP stage */
+ } else {
+ /* BULK and INTR */
+ urb->actual_length += n_bytes - remain;
+ if (remain || urb->actual_length == urb->length) {
+ urb->status = 0;
+ *xfer_done = 1;
+ }
+ }
+
+ return 0;
+}
+
+static void complete_non_isoc_xfer_ddma(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_halt_status_e halt_status)
+{
+ dwc_otg_hcd_urb_t *urb = NULL;
+ dwc_otg_qtd_t *qtd, *qtd_tmp;
+ dwc_otg_qh_t *qh;
+ dwc_otg_host_dma_desc_t *dma_desc;
+ uint32_t n_bytes, n_desc, i;
+ uint8_t failed = 0, xfer_done;
+
+ n_desc = 0;
+
+ qh = hc->qh;
+
+ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
+ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
+ qtd->in_process = 0;
+ }
+ return;
+ }
+
+ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
+
+ urb = qtd->urb;
+
+ n_bytes = 0;
+ xfer_done = 0;
+
+ for (i = 0; i < qtd->n_desc; i++) {
+ dma_desc = &qh->desc_list[n_desc];
+
+ n_bytes = qh->n_bytes[n_desc];
+
+ failed =
+ update_non_isoc_urb_state_ddma(hcd, hc, qtd,
+ dma_desc,
+ halt_status, n_bytes,
+ &xfer_done);
+
+ if (failed
+ || (xfer_done
+ && (urb->status != -DWC_E_IN_PROGRESS))) {
+
+ hcd->fops->complete(hcd, urb->priv, urb,
+ urb->status);
+ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+
+ if (failed)
+ goto stop_scan;
+ } else if (qh->ep_type == UE_CONTROL) {
+ if (qtd->control_phase == DWC_OTG_CONTROL_SETUP) {
+ if (urb->length > 0) {
+ qtd->control_phase = DWC_OTG_CONTROL_DATA;
+ } else {
+ qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+ }
+ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction done\n");
+ } else if (qtd->control_phase == DWC_OTG_CONTROL_DATA) {
+ if (xfer_done) {
+ qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+ DWC_DEBUGPL(DBG_HCDV, " Control data transfer done\n");
+ } else if (i + 1 == qtd->n_desc) {
+ /*
+ * Last descriptor for Control data stage which is
+ * not completed yet.
+ */
+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+ }
+ }
+ }
+
+ n_desc++;
+ }
+
+ }
+
+stop_scan:
+
+ if (qh->ep_type != UE_CONTROL) {
+ /*
+ * Resetting the data toggle for bulk
+ * and interrupt endpoints in case of stall. See handle_hc_stall_intr()
+ */
+ if (halt_status == DWC_OTG_HC_XFER_STALL)
+ qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+ else
+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+ }
+
+ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
+ hcint_data_t hcint;
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+ if (hcint.b.nyet) {
+ /*
+ * Got a NYET on the last transaction of the transfer. It
+ * means that the endpoint should be in the PING state at the
+ * beginning of the next transfer.
+ */
+ qh->ping_state = 1;
+ clear_hc_int(hc_regs, nyet);
+ }
+
+ }
+
+}
+
+/**
+ * This function is called from interrupt handlers.
+ * Scans the descriptor list, updates URB's status and
+ * calls completion routine for the URB if it's done.
+ * Releases the channel to be used by other transfers.
+ * In case of Isochronous endpoint the channel is not halted until
+ * the end of the session, i.e. QTD list is empty.
+ * If periodic channel released the FrameList is updated accordingly.
+ *
+ * Calls transaction selection routines to activate pending transfers.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param hc Host channel, the transfer is completed on.
+ * @param hc_regs Host channel registers.
+ * @param halt_status Reason the channel is being halted,
+ * or just XferComplete for isochronous transfer
+ */
+void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_halt_status_e halt_status)
+{
+ uint8_t continue_isoc_xfer = 0;
+ dwc_otg_transaction_type_e tr_type;
+ dwc_otg_qh_t *qh = hc->qh;
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+
+ complete_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status);
+
+ /* Release the channel if halted or session completed */
+ if (halt_status != DWC_OTG_HC_XFER_COMPLETE ||
+ DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+
+ /* Halt the channel if session completed */
+ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
+ dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
+ }
+
+ release_channel_ddma(hcd, qh);
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ } else {
+ /* Keep in assigned schedule to continue transfer */
+ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
+ &qh->qh_list_entry);
+ continue_isoc_xfer = 1;
+
+ }
+ /** @todo Consider the case when period exceeds FrameList size.
+ * Frame Rollover interrupt should be used.
+ */
+ } else {
+ /* Scan descriptor list to complete the URB(s), then release the channel */
+ complete_non_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status);
+
+ release_channel_ddma(hcd, qh);
+ dwc_otg_hcd_qh_remove(hcd, qh);
+
+ if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+ /* Add back to inactive non-periodic schedule on normal completion */
+ dwc_otg_hcd_qh_add(hcd, qh);
+ }
+
+ }
+ tr_type = dwc_otg_hcd_select_transactions(hcd);
+ if (tr_type != DWC_OTG_TRANSACTION_NONE || continue_isoc_xfer) {
+ if (continue_isoc_xfer) {
+ if (tr_type == DWC_OTG_TRANSACTION_NONE) {
+ tr_type = DWC_OTG_TRANSACTION_PERIODIC;
+ } else if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC) {
+ tr_type = DWC_OTG_TRANSACTION_ALL;
+ }
+ }
+ dwc_otg_hcd_queue_transactions(hcd, tr_type);
+ }
+}
+
+#endif /* DWC_DEVICE_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_if.h b/drivers/usb/dwc_otg/dwc_otg_hcd_if.h
new file mode 100644
index 0000000..b3dc806
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd_if.h
@@ -0,0 +1,412 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_if.h $
+ * $Revision: #12 $
+ * $Date: 2011/10/26 $
+ * $Change: 1873028 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+#ifndef __DWC_HCD_IF_H__
+#define __DWC_HCD_IF_H__
+
+#include "dwc_otg_core_if.h"
+
+/** @file
+ * This file defines DWC_OTG HCD Core API.
+ */
+
+struct dwc_otg_hcd;
+typedef struct dwc_otg_hcd dwc_otg_hcd_t;
+
+struct dwc_otg_hcd_urb;
+typedef struct dwc_otg_hcd_urb dwc_otg_hcd_urb_t;
+
+/** @name HCD Function Driver Callbacks */
+/** @{ */
+
+/** This function is called whenever core switches to host mode. */
+typedef int (*dwc_otg_hcd_start_cb_t) (dwc_otg_hcd_t * hcd);
+
+/** This function is called when device has been disconnected */
+typedef int (*dwc_otg_hcd_disconnect_cb_t) (dwc_otg_hcd_t * hcd);
+
+/** Wrapper provides this function to HCD to core, so it can get hub information to which device is connected */
+typedef int (*dwc_otg_hcd_hub_info_from_urb_cb_t) (dwc_otg_hcd_t * hcd,
+ void *urb_handle,
+ uint32_t * hub_addr,
+ uint32_t * port_addr);
+/** Via this function HCD core gets device speed */
+typedef int (*dwc_otg_hcd_speed_from_urb_cb_t) (dwc_otg_hcd_t * hcd,
+ void *urb_handle);
+
+/** This function is called when urb is completed */
+typedef int (*dwc_otg_hcd_complete_urb_cb_t) (dwc_otg_hcd_t * hcd,
+ void *urb_handle,
+ dwc_otg_hcd_urb_t * dwc_otg_urb,
+ int32_t status);
+
+/** Via this function HCD core gets b_hnp_enable parameter */
+typedef int (*dwc_otg_hcd_get_b_hnp_enable) (dwc_otg_hcd_t * hcd);
+
+struct dwc_otg_hcd_function_ops {
+ dwc_otg_hcd_start_cb_t start;
+ dwc_otg_hcd_disconnect_cb_t disconnect;
+ dwc_otg_hcd_hub_info_from_urb_cb_t hub_info;
+ dwc_otg_hcd_speed_from_urb_cb_t speed;
+ dwc_otg_hcd_complete_urb_cb_t complete;
+ dwc_otg_hcd_get_b_hnp_enable get_b_hnp_enable;
+};
+/** @} */
+
+/** @name HCD Core API */
+/** @{ */
+/** This function allocates dwc_otg_hcd structure and returns pointer on it. */
+extern dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void);
+
+/** This function should be called to initiate HCD Core.
+ *
+ * @param hcd The HCD
+ * @param core_if The DWC_OTG Core
+ *
+ * Returns -DWC_E_NO_MEMORY if no enough memory.
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if);
+
+/** Frees HCD
+ *
+ * @param hcd The HCD
+ */
+extern void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd);
+
+/** This function should be called on every hardware interrupt.
+ *
+ * @param dwc_otg_hcd The HCD
+ *
+ * Returns non zero if interrupt is handled
+ * Return 0 if interrupt is not handled
+ */
+extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+
+/**
+ * Returns private data set by
+ * dwc_otg_hcd_set_priv_data function.
+ *
+ * @param hcd The HCD
+ */
+extern void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd);
+
+/**
+ * Set private data.
+ *
+ * @param hcd The HCD
+ * @param priv_data pointer to be stored in private data
+ */
+extern void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data);
+
+/**
+ * This function initializes the HCD Core.
+ *
+ * @param hcd The HCD
+ * @param fops The Function Driver Operations data structure containing pointers to all callbacks.
+ *
+ * Returns -DWC_E_NO_DEVICE if Core is currently is in device mode.
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd,
+ struct dwc_otg_hcd_function_ops *fops);
+
+/**
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ *
+ * @param hcd The HCD
+ */
+extern void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd);
+
+/**
+ * Handles hub class-specific requests.
+ *
+ * @param dwc_otg_hcd The HCD
+ * @param typeReq Request Type
+ * @param wValue wValue from control request
+ * @param wIndex wIndex from control request
+ * @param buf data buffer
+ * @param wLength data buffer length
+ *
+ * Returns -DWC_E_INVALID if invalid argument is passed
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd,
+ uint16_t typeReq, uint16_t wValue,
+ uint16_t wIndex, uint8_t * buf,
+ uint16_t wLength);
+
+/**
+ * Returns otg port number.
+ *
+ * @param hcd The HCD
+ */
+extern uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd);
+
+/**
+ * Returns OTG version - either 1.3 or 2.0.
+ *
+ * @param core_if The core_if structure pointer
+ */
+extern uint16_t dwc_otg_get_otg_version(dwc_otg_core_if_t * core_if);
+
+/**
+ * Returns 1 if currently core is acting as B host, and 0 otherwise.
+ *
+ * @param hcd The HCD
+ */
+extern uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd);
+
+/**
+ * Returns current frame number.
+ *
+ * @param hcd The HCD
+ */
+extern int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * hcd);
+
+/**
+ * Dumps hcd state.
+ *
+ * @param hcd The HCD
+ */
+extern void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd);
+
+/**
+ * Dump the average frame remaining at SOF. This can be used to
+ * determine average interrupt latency. Frame remaining is also shown for
+ * start transfer and two additional sample points.
+ * Currently this function is not implemented.
+ *
+ * @param hcd The HCD
+ */
+extern void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd);
+
+/**
+ * Sends LPM transaction to the local device.
+ *
+ * @param hcd The HCD
+ * @param devaddr Device Address
+ * @param hird Host initiated resume duration
+ * @param bRemoteWake Value of bRemoteWake field in LPM transaction
+ *
+ * Returns negative value if sending LPM transaction was not succeeded.
+ * Returns 0 on success.
+ */
+extern int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr,
+ uint8_t hird, uint8_t bRemoteWake);
+
+/* URB interface */
+
+/**
+ * Allocates memory for dwc_otg_hcd_urb structure.
+ * Allocated memory should be freed by call of DWC_FREE.
+ *
+ * @param hcd The HCD
+ * @param iso_desc_count Count of ISOC descriptors
+ * @param atomic_alloc Specefies whether to perform atomic allocation.
+ */
+extern dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd,
+ int iso_desc_count,
+ int atomic_alloc);
+
+/**
+ * Set pipe information in URB.
+ *
+ * @param hcd_urb DWC_OTG URB
+ * @param devaddr Device Address
+ * @param ep_num Endpoint Number
+ * @param ep_type Endpoint Type
+ * @param ep_dir Endpoint Direction
+ * @param mps Max Packet Size
+ */
+extern void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * hcd_urb,
+ uint8_t devaddr, uint8_t ep_num,
+ uint8_t ep_type, uint8_t ep_dir,
+ uint16_t mps);
+
+/* Transfer flags */
+#define URB_GIVEBACK_ASAP 0x1
+#define URB_SEND_ZERO_PACKET 0x2
+
+/**
+ * Sets dwc_otg_hcd_urb parameters.
+ *
+ * @param urb DWC_OTG URB allocated by dwc_otg_hcd_urb_alloc function.
+ * @param urb_handle Unique handle for request, this will be passed back
+ * to function driver in completion callback.
+ * @param buf The buffer for the data
+ * @param dma The DMA buffer for the data
+ * @param buflen Transfer length
+ * @param sp Buffer for setup data
+ * @param sp_dma DMA address of setup data buffer
+ * @param flags Transfer flags
+ * @param interval Polling interval for interrupt or isochronous transfers.
+ */
+extern void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * urb,
+ void *urb_handle, void *buf,
+ dwc_dma_t dma, uint32_t buflen, void *sp,
+ dwc_dma_t sp_dma, uint32_t flags,
+ uint16_t interval);
+
+/** Gets status from dwc_otg_hcd_urb
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ */
+extern uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb);
+
+/** Gets actual length from dwc_otg_hcd_urb
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ */
+extern uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t *
+ dwc_otg_urb);
+
+/** Gets error count from dwc_otg_hcd_urb. Only for ISOC URBs
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ */
+extern uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t *
+ dwc_otg_urb);
+
+/** Set ISOC descriptor offset and length
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ * @param desc_num ISOC descriptor number
+ * @param offset Offset from beginig of buffer.
+ * @param length Transaction length
+ */
+extern void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb,
+ int desc_num, uint32_t offset,
+ uint32_t length);
+
+/** Get status of ISOC descriptor, specified by desc_num
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ * @param desc_num ISOC descriptor number
+ */
+extern uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t *
+ dwc_otg_urb, int desc_num);
+
+/** Get actual length of ISOC descriptor, specified by desc_num
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ * @param desc_num ISOC descriptor number
+ */
+extern uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t *
+ dwc_otg_urb,
+ int desc_num);
+
+/** Queue URB. After transfer is completes, the complete callback will be called with the URB status
+ *
+ * @param dwc_otg_hcd The HCD
+ * @param dwc_otg_urb DWC_OTG URB
+ * @param ep_handle Out parameter for returning endpoint handle
+ * @param atomic_alloc Flag to do atomic allocation if needed
+ *
+ * Returns -DWC_E_NO_DEVICE if no device is connected.
+ * Returns -DWC_E_NO_MEMORY if there is no enough memory.
+ * Returns 0 on success.
+ */
+extern int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * dwc_otg_hcd,
+ dwc_otg_hcd_urb_t * dwc_otg_urb,
+ void **ep_handle, int atomic_alloc);
+
+/** De-queue the specified URB
+ *
+ * @param dwc_otg_hcd The HCD
+ * @param dwc_otg_urb DWC_OTG URB
+ */
+extern int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * dwc_otg_hcd,
+ dwc_otg_hcd_urb_t * dwc_otg_urb);
+
+/** Frees resources in the DWC_otg controller related to a given endpoint.
+ * Any URBs for the endpoint must already be dequeued.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle, returned by dwc_otg_hcd_urb_enqueue function
+ * @param retry Number of retries if there are queued transfers.
+ *
+ * Returns -DWC_E_INVALID if invalid arguments are passed.
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle,
+ int retry);
+
+/* Resets the data toggle in qh structure. This function can be called from
+ * usb_clear_halt routine.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle, returned by dwc_otg_hcd_urb_enqueue function
+ *
+ * Returns -DWC_E_INVALID if invalid arguments are passed.
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_endpoint_reset(dwc_otg_hcd_t * hcd, void *ep_handle);
+
+/** Returns 1 if status of specified port is changed and 0 otherwise.
+ *
+ * @param hcd The HCD
+ * @param port Port number
+ */
+extern int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port);
+
+/** Call this function to check if bandwidth was allocated for specified endpoint.
+ * Only for ISOC and INTERRUPT endpoints.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle
+ */
+extern int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd,
+ void *ep_handle);
+
+/** Call this function to check if bandwidth was freed for specified endpoint.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle
+ */
+extern int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle);
+
+/** Returns bandwidth allocated for specified endpoint in microseconds.
+ * Only for ISOC and INTERRUPT endpoints.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle
+ */
+extern uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd,
+ void *ep_handle);
+
+/** @} */
+
+#endif /* __DWC_HCD_IF_H__ */
+#endif /* DWC_DEVICE_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c
new file mode 100644
index 0000000..9fb3602
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c
@@ -0,0 +1,2101 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_intr.c $
+ * $Revision: #89 $
+ * $Date: 2011/10/20 $
+ * $Change: 1869487 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+#include "dwc_otg_hcd.h"
+#include "dwc_otg_regs.h"
+
+/** @file
+ * This file contains the implementation of the HCD Interrupt handlers.
+ */
+
+/** This function handles interrupts for the HCD. */
+int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+ int retval = 0;
+
+ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+ gintsts_data_t gintsts;
+#ifdef DEBUG
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+#endif
+
+ /* Exit from ISR if core is hibernated */
+ if (core_if->hibernation_suspend == 1) {
+ return retval;
+ }
+ DWC_SPINLOCK(dwc_otg_hcd->lock);
+ /* Check if HOST Mode */
+ if (dwc_otg_is_host_mode(core_if)) {
+ gintsts.d32 = dwc_otg_read_core_intr(core_if);
+ if (!gintsts.d32) {
+ DWC_SPINUNLOCK(dwc_otg_hcd->lock);
+ return 0;
+ }
+#ifdef DEBUG
+ /* Don't print debug message in the interrupt handler on SOF */
+#ifndef DEBUG_SOF
+ if (gintsts.d32 != DWC_SOF_INTR_MASK)
+#endif
+ DWC_DEBUGPL(DBG_HCD, "\n");
+#endif
+
+#ifdef DEBUG
+#ifndef DEBUG_SOF
+ if (gintsts.d32 != DWC_SOF_INTR_MASK)
+#endif
+ DWC_DEBUGPL(DBG_HCD,
+ "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x\n",
+ gintsts.d32);
+#endif
+
+ if (gintsts.b.sofintr) {
+ retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
+ }
+ if (gintsts.b.rxstsqlvl) {
+ retval |=
+ dwc_otg_hcd_handle_rx_status_q_level_intr
+ (dwc_otg_hcd);
+ }
+ if (gintsts.b.nptxfempty) {
+ retval |=
+ dwc_otg_hcd_handle_np_tx_fifo_empty_intr
+ (dwc_otg_hcd);
+ }
+ if (gintsts.b.i2cintr) {
+ /** @todo Implement i2cintr handler. */
+ }
+ if (gintsts.b.portintr) {
+ retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd);
+ }
+ if (gintsts.b.hcintr) {
+ retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd);
+ }
+ if (gintsts.b.ptxfempty) {
+ retval |=
+ dwc_otg_hcd_handle_perio_tx_fifo_empty_intr
+ (dwc_otg_hcd);
+ }
+#ifdef DEBUG
+#ifndef DEBUG_SOF
+ if (gintsts.d32 != DWC_SOF_INTR_MASK)
+#endif
+ {
+ DWC_DEBUGPL(DBG_HCD,
+ "DWC OTG HCD Finished Servicing Interrupts\n");
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintsts=0x%08x\n",
+ DWC_READ_REG32(&global_regs->gintsts));
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintmsk=0x%08x\n",
+ DWC_READ_REG32(&global_regs->gintmsk));
+ }
+#endif
+
+#ifdef DEBUG
+#ifndef DEBUG_SOF
+ if (gintsts.d32 != DWC_SOF_INTR_MASK)
+#endif
+ DWC_DEBUGPL(DBG_HCD, "\n");
+#endif
+
+ }
+ DWC_SPINUNLOCK(dwc_otg_hcd->lock);
+ return retval;
+}
+
+#ifdef DWC_TRACK_MISSED_SOFS
+#warning Compiling code to track missed SOFs
+#define FRAME_NUM_ARRAY_SIZE 1000
+/**
+ * This function is for debug only.
+ */
+static inline void track_missed_sofs(uint16_t curr_frame_number)
+{
+ static uint16_t frame_num_array[FRAME_NUM_ARRAY_SIZE];
+ static uint16_t last_frame_num_array[FRAME_NUM_ARRAY_SIZE];
+ static int frame_num_idx = 0;
+ static uint16_t last_frame_num = DWC_HFNUM_MAX_FRNUM;
+ static int dumped_frame_num_array = 0;
+
+ if (frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
+ if (((last_frame_num + 1) & DWC_HFNUM_MAX_FRNUM) !=
+ curr_frame_number) {
+ frame_num_array[frame_num_idx] = curr_frame_number;
+ last_frame_num_array[frame_num_idx++] = last_frame_num;
+ }
+ } else if (!dumped_frame_num_array) {
+ int i;
+ DWC_PRINTF("Frame Last Frame\n");
+ DWC_PRINTF("----- ----------\n");
+ for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) {
+ DWC_PRINTF("0x%04x 0x%04x\n",
+ frame_num_array[i], last_frame_num_array[i]);
+ }
+ dumped_frame_num_array = 1;
+ }
+ last_frame_num = curr_frame_number;
+}
+#endif
+
+/**
+ * Handles the start-of-frame interrupt in host mode. Non-periodic
+ * transactions may be queued to the DWC_otg controller for the current
+ * (micro)frame. Periodic transactions may be queued to the controller for the
+ * next (micro)frame.
+ */
+int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd)
+{
+ hfnum_data_t hfnum;
+ dwc_list_link_t *qh_entry;
+ dwc_otg_qh_t *qh;
+ dwc_otg_transaction_type_e tr_type;
+ gintsts_data_t gintsts = {.d32 = 0 };
+
+ hfnum.d32 =
+ DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
+
+#ifdef DEBUG_SOF
+ DWC_DEBUGPL(DBG_HCD, "--Start of Frame Interrupt--\n");
+#endif
+ hcd->frame_number = hfnum.b.frnum;
+
+#ifdef DEBUG
+ hcd->frrem_accum += hfnum.b.frrem;
+ hcd->frrem_samples++;
+#endif
+
+#ifdef DWC_TRACK_MISSED_SOFS
+ track_missed_sofs(hcd->frame_number);
+#endif
+ /* Determine whether any periodic QHs should be executed. */
+ qh_entry = DWC_LIST_FIRST(&hcd->periodic_sched_inactive);
+ while (qh_entry != &hcd->periodic_sched_inactive) {
+ qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry);
+ qh_entry = qh_entry->next;
+ if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) {
+ /*
+ * Move QH to the ready list to be executed next
+ * (micro)frame.
+ */
+ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
+ &qh->qh_list_entry);
+ }
+ }
+ tr_type = dwc_otg_hcd_select_transactions(hcd);
+ if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+ dwc_otg_hcd_queue_transactions(hcd, tr_type);
+ }
+
+ /* Clear interrupt */
+ gintsts.b.sofintr = 1;
+ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/** Handles the Rx Status Queue Level Interrupt, which indicates that there is at
+ * least one packet in the Rx FIFO. The packets are moved from the FIFO to
+ * memory if the DWC_otg controller is operating in Slave mode. */
+int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+ host_grxsts_data_t grxsts;
+ dwc_hc_t *hc = NULL;
+
+ DWC_DEBUGPL(DBG_HCD, "--RxStsQ Level Interrupt--\n");
+
+ grxsts.d32 =
+ DWC_READ_REG32(&dwc_otg_hcd->core_if->core_global_regs->grxstsp);
+
+ hc = dwc_otg_hcd->hc_ptr_array[grxsts.b.chnum];
+ if (!hc) {
+ DWC_ERROR("Unable to get corresponding channel\n");
+ return 0;
+ }
+
+ /* Packet Status */
+ DWC_DEBUGPL(DBG_HCDV, " Ch num = %d\n", grxsts.b.chnum);
+ DWC_DEBUGPL(DBG_HCDV, " Count = %d\n", grxsts.b.bcnt);
+ DWC_DEBUGPL(DBG_HCDV, " DPID = %d, hc.dpid = %d\n", grxsts.b.dpid,
+ hc->data_pid_start);
+ DWC_DEBUGPL(DBG_HCDV, " PStatus = %d\n", grxsts.b.pktsts);
+
+ switch (grxsts.b.pktsts) {
+ case DWC_GRXSTS_PKTSTS_IN:
+ /* Read the data into the host buffer. */
+ if (grxsts.b.bcnt > 0) {
+ dwc_otg_read_packet(dwc_otg_hcd->core_if,
+ hc->xfer_buff, grxsts.b.bcnt);
+
+ /* Update the HC fields for the next packet received. */
+ hc->xfer_count += grxsts.b.bcnt;
+ hc->xfer_buff += grxsts.b.bcnt;
+ }
+
+ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
+ case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
+ case DWC_GRXSTS_PKTSTS_CH_HALTED:
+ /* Handled in interrupt, just ignore data */
+ break;
+ default:
+ DWC_ERROR("RX_STS_Q Interrupt: Unknown status %d\n",
+ grxsts.b.pktsts);
+ break;
+ }
+
+ return 1;
+}
+
+/** This interrupt occurs when the non-periodic Tx FIFO is half-empty. More
+ * data packets may be written to the FIFO for OUT transfers. More requests
+ * may be written to the non-periodic request queue for IN transfers. This
+ * interrupt is enabled only in Slave mode. */
+int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Non-Periodic TxFIFO Empty Interrupt--\n");
+ dwc_otg_hcd_queue_transactions(dwc_otg_hcd,
+ DWC_OTG_TRANSACTION_NON_PERIODIC);
+ return 1;
+}
+
+/** This interrupt occurs when the periodic Tx FIFO is half-empty. More data
+ * packets may be written to the FIFO for OUT transfers. More requests may be
+ * written to the periodic request queue for IN transfers. This interrupt is
+ * enabled only in Slave mode. */
+int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Periodic TxFIFO Empty Interrupt--\n");
+ dwc_otg_hcd_queue_transactions(dwc_otg_hcd,
+ DWC_OTG_TRANSACTION_PERIODIC);
+ return 1;
+}
+
+/** There are multiple conditions that can cause a port interrupt. This function
+ * determines which interrupt conditions have occurred and handles them
+ * appropriately. */
+int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+ int retval = 0;
+ hprt0_data_t hprt0;
+ hprt0_data_t hprt0_modify;
+
+ hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
+ hprt0_modify.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
+
+ /* Clear appropriate bits in HPRT0 to clear the interrupt bit in
+ * GINTSTS */
+
+ hprt0_modify.b.prtena = 0;
+ hprt0_modify.b.prtconndet = 0;
+ hprt0_modify.b.prtenchng = 0;
+ hprt0_modify.b.prtovrcurrchng = 0;
+
+ /* Port Connect Detected
+ * Set flag and clear if detected */
+ if (dwc_otg_hcd->core_if->hibernation_suspend == 1) {
+ // Dont modify port status if we are in hibernation state
+ hprt0_modify.b.prtconndet = 1;
+ hprt0_modify.b.prtenchng = 1;
+ DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32);
+ hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
+ return retval;
+ }
+
+ if (hprt0.b.prtconndet) {
+ /** @todo - check if steps performed in 'else' block should be perfromed regardles adp */
+ if (dwc_otg_hcd->core_if->adp_enable &&
+ dwc_otg_hcd->core_if->adp.vbuson_timer_started == 1) {
+ DWC_PRINTF("PORT CONNECT DETECTED ----------------\n");
+ DWC_TIMER_CANCEL(dwc_otg_hcd->core_if->adp.vbuson_timer);
+ dwc_otg_hcd->core_if->adp.vbuson_timer_started = 0;
+ /* TODO - check if this is required, as
+ * host initialization was already performed
+ * after initial ADP probing
+ */
+ /*dwc_otg_hcd->core_if->adp.vbuson_timer_started = 0;
+ dwc_otg_core_init(dwc_otg_hcd->core_if);
+ dwc_otg_enable_global_interrupts(dwc_otg_hcd->core_if);
+ cil_hcd_start(dwc_otg_hcd->core_if);*/
+ } else {
+
+ DWC_DEBUGPL(DBG_HCD, "--Port Interrupt HPRT0=0x%08x "
+ "Port Connect Detected--\n", hprt0.d32);
+ dwc_otg_hcd->flags.b.port_connect_status_change = 1;
+ dwc_otg_hcd->flags.b.port_connect_status = 1;
+ hprt0_modify.b.prtconndet = 1;
+
+ /* B-Device has connected, Delete the connection timer. */
+ DWC_TIMER_CANCEL(dwc_otg_hcd->conn_timer);
+ }
+ /* The Hub driver asserts a reset when it sees port connect
+ * status change flag */
+ retval |= 1;
+ }
+
+ /* Port Enable Changed
+ * Clear if detected - Set internal flag if disabled */
+ if (hprt0.b.prtenchng) {
+ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x "
+ "Port Enable Changed--\n", hprt0.d32);
+ hprt0_modify.b.prtenchng = 1;
+ if (hprt0.b.prtena == 1) {
+ hfir_data_t hfir;
+ int do_reset = 0;
+ dwc_otg_core_params_t *params =
+ dwc_otg_hcd->core_if->core_params;
+ dwc_otg_core_global_regs_t *global_regs =
+ dwc_otg_hcd->core_if->core_global_regs;
+ dwc_otg_host_if_t *host_if =
+ dwc_otg_hcd->core_if->host_if;
+
+ /* Every time when port enables calculate
+ * HFIR.FrInterval
+ */
+ hfir.d32 = DWC_READ_REG32(&host_if->host_global_regs->hfir);
+ hfir.b.frint = calc_frame_interval(dwc_otg_hcd->core_if);
+ DWC_WRITE_REG32(&host_if->host_global_regs->hfir, hfir.d32);
+
+ /* Check if we need to adjust the PHY clock speed for
+ * low power and adjust it */
+ if (params->host_support_fs_ls_low_power) {
+ gusbcfg_data_t usbcfg;
+
+ usbcfg.d32 =
+ DWC_READ_REG32(&global_regs->gusbcfg);
+
+ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED
+ || hprt0.b.prtspd ==
+ DWC_HPRT0_PRTSPD_FULL_SPEED) {
+ /*
+ * Low power
+ */
+ hcfg_data_t hcfg;
+ if (usbcfg.b.phylpwrclksel == 0) {
+ /* Set PHY low power clock select for FS/LS devices */
+ usbcfg.b.phylpwrclksel = 1;
+ DWC_WRITE_REG32
+ (&global_regs->gusbcfg,
+ usbcfg.d32);
+ do_reset = 1;
+ }
+
+ hcfg.d32 =
+ DWC_READ_REG32
+ (&host_if->host_global_regs->hcfg);
+
+ if (hprt0.b.prtspd ==
+ DWC_HPRT0_PRTSPD_LOW_SPEED
+ && params->host_ls_low_power_phy_clk
+ ==
+ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ)
+ {
+ /* 6 MHZ */
+ DWC_DEBUGPL(DBG_CIL,
+ "FS_PHY programming HCFG to 6 MHz (Low Power)\n");
+ if (hcfg.b.fslspclksel !=
+ DWC_HCFG_6_MHZ) {
+ hcfg.b.fslspclksel =
+ DWC_HCFG_6_MHZ;
+ DWC_WRITE_REG32
+ (&host_if->host_global_regs->hcfg,
+ hcfg.d32);
+ do_reset = 1;
+ }
+ } else {
+ /* 48 MHZ */
+ DWC_DEBUGPL(DBG_CIL,
+ "FS_PHY programming HCFG to 48 MHz ()\n");
+ if (hcfg.b.fslspclksel !=
+ DWC_HCFG_48_MHZ) {
+ hcfg.b.fslspclksel =
+ DWC_HCFG_48_MHZ;
+ DWC_WRITE_REG32
+ (&host_if->host_global_regs->hcfg,
+ hcfg.d32);
+ do_reset = 1;
+ }
+ }
+ } else {
+ /*
+ * Not low power
+ */
+ if (usbcfg.b.phylpwrclksel == 1) {
+ usbcfg.b.phylpwrclksel = 0;
+ DWC_WRITE_REG32
+ (&global_regs->gusbcfg,
+ usbcfg.d32);
+ do_reset = 1;
+ }
+ }
+
+ if (do_reset) {
+ DWC_TASK_SCHEDULE(dwc_otg_hcd->reset_tasklet);
+ }
+ }
+
+ if (!do_reset) {
+ /* Port has been enabled set the reset change flag */
+ dwc_otg_hcd->flags.b.port_reset_change = 1;
+ }
+ } else {
+ dwc_otg_hcd->flags.b.port_enable_change = 1;
+ }
+ retval |= 1;
+ }
+
+ /** Overcurrent Change Interrupt */
+ if (hprt0.b.prtovrcurrchng) {
+ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x "
+ "Port Overcurrent Changed--\n", hprt0.d32);
+ dwc_otg_hcd->flags.b.port_over_current_change = 1;
+ hprt0_modify.b.prtovrcurrchng = 1;
+ retval |= 1;
+ }
+
+ /* Clear Port Interrupts */
+ DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32);
+
+ return retval;
+}
+
+/** This interrupt indicates that one or more host channels has a pending
+ * interrupt. There are multiple conditions that can cause each host channel
+ * interrupt. This function determines which conditions have occurred for each
+ * host channel interrupt and handles them appropriately. */
+int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+ int i;
+ int retval = 0;
+ haint_data_t haint;
+
+ /* Clear appropriate bits in HCINTn to clear the interrupt bit in
+ * GINTSTS */
+
+ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if);
+
+ for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) {
+ if (haint.b2.chint & (1 << i)) {
+ retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i);
+ }
+ }
+
+ return retval;
+}
+
+/**
+ * Gets the actual length of a transfer after the transfer halts. _halt_status
+ * holds the reason for the halt.
+ *
+ * For IN transfers where halt_status is DWC_OTG_HC_XFER_COMPLETE,
+ * *short_read is set to 1 upon return if less than the requested
+ * number of bytes were transferred. Otherwise, *short_read is set to 0 upon
+ * return. short_read may also be NULL on entry, in which case it remains
+ * unchanged.
+ */
+static uint32_t get_actual_xfer_length(dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd,
+ dwc_otg_halt_status_e halt_status,
+ int *short_read)
+{
+ hctsiz_data_t hctsiz;
+ uint32_t length;
+
+ if (short_read != NULL) {
+ *short_read = 0;
+ }
+ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+
+ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
+ if (hc->ep_is_in) {
+ length = hc->xfer_len - hctsiz.b.xfersize;
+ if (short_read != NULL) {
+ *short_read = (hctsiz.b.xfersize != 0);
+ }
+ } else if (hc->qh->do_split) {
+ length = qtd->ssplit_out_xfer_count;
+ } else {
+ length = hc->xfer_len;
+ }
+ } else {
+ /*
+ * Must use the hctsiz.pktcnt field to determine how much data
+ * has been transferred. This field reflects the number of
+ * packets that have been transferred via the USB. This is
+ * always an integral number of packets if the transfer was
+ * halted before its normal completion. (Can't use the
+ * hctsiz.xfersize field because that reflects the number of
+ * bytes transferred via the AHB, not the USB).
+ */
+ length =
+ (hc->start_pkt_count - hctsiz.b.pktcnt) * hc->max_packet;
+ }
+
+ return length;
+}
+
+/**
+ * Updates the state of the URB after a Transfer Complete interrupt on the
+ * host channel. Updates the actual_length field of the URB based on the
+ * number of bytes transferred via the host channel. Sets the URB status
+ * if the data transfer is finished.
+ *
+ * @return 1 if the data transfer specified by the URB is completely finished,
+ * 0 otherwise.
+ */
+static int update_urb_state_xfer_comp(dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_hcd_urb_t * urb,
+ dwc_otg_qtd_t * qtd)
+{
+ int xfer_done = 0;
+ int short_read = 0;
+
+ int xfer_length;
+
+ xfer_length = get_actual_xfer_length(hc, hc_regs, qtd,
+ DWC_OTG_HC_XFER_COMPLETE,
+ &short_read);
+
+
+ /* non DWORD-aligned buffer case handling. */
+ if (hc->align_buff && xfer_length && hc->ep_is_in) {
+ dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf,
+ xfer_length);
+ }
+
+ urb->actual_length += xfer_length;
+
+ if (xfer_length && (hc->ep_type == DWC_OTG_EP_TYPE_BULK) &&
+ (urb->flags & URB_SEND_ZERO_PACKET)
+ && (urb->actual_length == urb->length)
+ && !(urb->length % hc->max_packet)) {
+ xfer_done = 0;
+ } else if (short_read || urb->actual_length == urb->length) {
+ xfer_done = 1;
+ urb->status = 0;
+ }
+
+#ifdef DEBUG
+ {
+ hctsiz_data_t hctsiz;
+ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n",
+ __func__, (hc->ep_is_in ? "IN" : "OUT"),
+ hc->hc_num);
+ DWC_DEBUGPL(DBG_HCDV, " hc->xfer_len %d\n", hc->xfer_len);
+ DWC_DEBUGPL(DBG_HCDV, " hctsiz.xfersize %d\n",
+ hctsiz.b.xfersize);
+ DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n",
+ urb->length);
+ DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n",
+ urb->actual_length);
+ DWC_DEBUGPL(DBG_HCDV, " short_read %d, xfer_done %d\n",
+ short_read, xfer_done);
+ }
+#endif
+
+ return xfer_done;
+}
+
+/*
+ * Save the starting data toggle for the next transfer. The data toggle is
+ * saved in the QH for non-control transfers and it's saved in the QTD for
+ * control transfers.
+ */
+void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs, dwc_otg_qtd_t * qtd)
+{
+ hctsiz_data_t hctsiz;
+ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+
+ if (hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) {
+ dwc_otg_qh_t *qh = hc->qh;
+ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {
+ qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+ } else {
+ qh->data_toggle = DWC_OTG_HC_PID_DATA1;
+ }
+ } else {
+ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {
+ qtd->data_toggle = DWC_OTG_HC_PID_DATA0;
+ } else {
+ qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
+ }
+ }
+}
+
+/**
+ * Updates the state of an Isochronous URB when the transfer is stopped for
+ * any reason. The fields of the current entry in the frame descriptor array
+ * are set based on the transfer state and the input _halt_status. Completes
+ * the Isochronous URB if all the URB frames have been completed.
+ *
+ * @return DWC_OTG_HC_XFER_COMPLETE if there are more frames remaining to be
+ * transferred in the URB. Otherwise return DWC_OTG_HC_XFER_URB_COMPLETE.
+ */
+static dwc_otg_halt_status_e
+update_isoc_urb_state(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status)
+{
+ dwc_otg_hcd_urb_t *urb = qtd->urb;
+ dwc_otg_halt_status_e ret_val = halt_status;
+ struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+
+ frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
+ switch (halt_status) {
+ case DWC_OTG_HC_XFER_COMPLETE:
+ frame_desc->status = 0;
+ frame_desc->actual_length =
+ get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL);
+
+ /* non DWORD-aligned buffer case handling. */
+ if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) {
+ dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset,
+ hc->qh->dw_align_buf, frame_desc->actual_length);
+ }
+
+ break;
+ case DWC_OTG_HC_XFER_FRAME_OVERRUN:
+ urb->error_count++;
+ if (hc->ep_is_in) {
+ frame_desc->status = -DWC_E_NO_STREAM_RES;
+ } else {
+ frame_desc->status = -DWC_E_COMMUNICATION;
+ }
+ frame_desc->actual_length = 0;
+ break;
+ case DWC_OTG_HC_XFER_BABBLE_ERR:
+ urb->error_count++;
+ frame_desc->status = -DWC_E_OVERFLOW;
+ /* Don't need to update actual_length in this case. */
+ break;
+ case DWC_OTG_HC_XFER_XACT_ERR:
+ urb->error_count++;
+ frame_desc->status = -DWC_E_PROTOCOL;
+ frame_desc->actual_length =
+ get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL);
+
+ /* non DWORD-aligned buffer case handling. */
+ if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) {
+ dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset,
+ hc->qh->dw_align_buf, frame_desc->actual_length);
+ }
+ /* Skip whole frame */
+ if (hc->qh->do_split && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) &&
+ hc->ep_is_in && hcd->core_if->dma_enable) {
+ qtd->complete_split = 0;
+ qtd->isoc_split_offset = 0;
+ }
+
+ break;
+ default:
+ DWC_ASSERT(1, "Unhandled _halt_status (%d)\n", halt_status);
+ break;
+ }
+ if (++qtd->isoc_frame_index == urb->packet_count) {
+ /*
+ * urb->status is not used for isoc transfers.
+ * The individual frame_desc statuses are used instead.
+ */
+ hcd->fops->complete(hcd, urb->priv, urb, 0);
+ ret_val = DWC_OTG_HC_XFER_URB_COMPLETE;
+ } else {
+ ret_val = DWC_OTG_HC_XFER_COMPLETE;
+ }
+ return ret_val;
+}
+
+/**
+ * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic
+ * QHs, removes the QH from the active non-periodic schedule. If any QTDs are
+ * still linked to the QH, the QH is added to the end of the inactive
+ * non-periodic schedule. For periodic QHs, removes the QH from the periodic
+ * schedule if no more QTDs are linked to the QH.
+ */
+static void deactivate_qh(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, int free_qtd)
+{
+ int continue_split = 0;
+ dwc_otg_qtd_t *qtd;
+
+ DWC_DEBUGPL(DBG_HCDV, " %s(%p,%p,%d)\n", __func__, hcd, qh, free_qtd);
+
+ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+
+ if (qtd->complete_split) {
+ continue_split = 1;
+ } else if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID ||
+ qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END) {
+ continue_split = 1;
+ }
+
+ if (free_qtd) {
+ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+ continue_split = 0;
+ }
+
+ qh->channel = NULL;
+ dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split);
+}
+
+/**
+ * Releases a host channel for use by other transfers. Attempts to select and
+ * queue more transactions since at least one host channel is available.
+ *
+ * @param hcd The HCD state structure.
+ * @param hc The host channel to release.
+ * @param qtd The QTD associated with the host channel. This QTD may be freed
+ * if the transfer is complete or an error has occurred.
+ * @param halt_status Reason the channel is being released. This status
+ * determines the actions taken by this function.
+ */
+static void release_channel(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_qtd_t * qtd,
+ dwc_otg_halt_status_e halt_status)
+{
+ dwc_otg_transaction_type_e tr_type;
+ int free_qtd;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d\n",
+ __func__, hc->hc_num, halt_status);
+
+ switch (halt_status) {
+ case DWC_OTG_HC_XFER_URB_COMPLETE:
+ free_qtd = 1;
+ break;
+ case DWC_OTG_HC_XFER_AHB_ERR:
+ case DWC_OTG_HC_XFER_STALL:
+ case DWC_OTG_HC_XFER_BABBLE_ERR:
+ free_qtd = 1;
+ break;
+ case DWC_OTG_HC_XFER_XACT_ERR:
+ if (qtd->error_count >= 3) {
+ DWC_DEBUGPL(DBG_HCDV,
+ " Complete URB with transaction error\n");
+ free_qtd = 1;
+ qtd->urb->status = -DWC_E_PROTOCOL;
+ hcd->fops->complete(hcd, qtd->urb->priv,
+ qtd->urb, -DWC_E_PROTOCOL);
+ } else {
+ free_qtd = 0;
+ }
+ break;
+ case DWC_OTG_HC_XFER_URB_DEQUEUE:
+ /*
+ * The QTD has already been removed and the QH has been
+ * deactivated. Don't want to do anything except release the
+ * host channel and try to queue more transfers.
+ */
+ goto cleanup;
+ case DWC_OTG_HC_XFER_NO_HALT_STATUS:
+ free_qtd = 0;
+ break;
+ case DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE:
+ DWC_DEBUGPL(DBG_HCDV,
+ " Complete URB with I/O error\n");
+ free_qtd = 1;
+ qtd->urb->status = -DWC_E_IO;
+ hcd->fops->complete(hcd, qtd->urb->priv,
+ qtd->urb, -DWC_E_IO);
+ break;
+ default:
+ free_qtd = 0;
+ break;
+ }
+
+ deactivate_qh(hcd, hc->qh, free_qtd);
+
+cleanup:
+ /*
+ * Release the host channel for use by other transfers. The cleanup
+ * function clears the channel interrupt enables and conditions, so
+ * there's no need to clear the Channel Halted interrupt separately.
+ */
+ dwc_otg_hc_cleanup(hcd->core_if, hc);
+ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
+
+ switch (hc->ep_type) {
+ case DWC_OTG_EP_TYPE_CONTROL:
+ case DWC_OTG_EP_TYPE_BULK:
+ hcd->non_periodic_channels--;
+ break;
+
+ default:
+ /*
+ * Don't release reservations for periodic channels here.
+ * That's done when a periodic transfer is descheduled (i.e.
+ * when the QH is removed from the periodic schedule).
+ */
+ break;
+ }
+
+ /* Try to queue more transfers now that there's a free channel. */
+ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
+ if (!intr_mask.b.sofintr) {
+ tr_type = dwc_otg_hcd_select_transactions(hcd);
+ if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+ dwc_otg_hcd_queue_transactions(hcd, tr_type);
+ }
+ }
+}
+
+/**
+ * Halts a host channel. If the channel cannot be halted immediately because
+ * the request queue is full, this function ensures that the FIFO empty
+ * interrupt for the appropriate queue is enabled so that the halt request can
+ * be queued when there is space in the request queue.
+ *
+ * This function may also be called in DMA mode. In that case, the channel is
+ * simply released since the core always halts the channel automatically in
+ * DMA mode.
+ */
+static void halt_channel(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status)
+{
+ if (hcd->core_if->dma_enable) {
+ release_channel(hcd, hc, qtd, halt_status);
+ return;
+ }
+
+ /* Slave mode processing... */
+ dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
+
+ if (hc->halt_on_queue) {
+ gintmsk_data_t gintmsk = {.d32 = 0 };
+ dwc_otg_core_global_regs_t *global_regs;
+ global_regs = hcd->core_if->core_global_regs;
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+ hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
+ /*
+ * Make sure the Non-periodic Tx FIFO empty interrupt
+ * is enabled so that the non-periodic schedule will
+ * be processed.
+ */
+ gintmsk.b.nptxfempty = 1;
+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
+ } else {
+ /*
+ * Move the QH from the periodic queued schedule to
+ * the periodic assigned schedule. This allows the
+ * halt to be queued when the periodic schedule is
+ * processed.
+ */
+ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
+ &hc->qh->qh_list_entry);
+
+ /*
+ * Make sure the Periodic Tx FIFO Empty interrupt is
+ * enabled so that the periodic schedule will be
+ * processed.
+ */
+ gintmsk.b.ptxfempty = 1;
+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
+ }
+ }
+}
+
+/**
+ * Performs common cleanup for non-periodic transfers after a Transfer
+ * Complete interrupt. This function should be called after any endpoint type
+ * specific handling is finished to release the host channel.
+ */
+static void complete_non_periodic_xfer(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd,
+ dwc_otg_halt_status_e halt_status)
+{
+ hcint_data_t hcint;
+
+ qtd->error_count = 0;
+
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+ if (hcint.b.nyet) {
+ /*
+ * Got a NYET on the last transaction of the transfer. This
+ * means that the endpoint should be in the PING state at the
+ * beginning of the next transfer.
+ */
+ hc->qh->ping_state = 1;
+ clear_hc_int(hc_regs, nyet);
+ }
+
+ /*
+ * Always halt and release the host channel to make it available for
+ * more transfers. There may still be more phases for a control
+ * transfer or more data packets for a bulk transfer at this point,
+ * but the host channel is still halted. A channel will be reassigned
+ * to the transfer when the non-periodic schedule is processed after
+ * the channel is released. This allows transactions to be queued
+ * properly via dwc_otg_hcd_queue_transactions, which also enables the
+ * Tx FIFO Empty interrupt if necessary.
+ */
+ if (hc->ep_is_in) {
+ /*
+ * IN transfers in Slave mode require an explicit disable to
+ * halt the channel. (In DMA mode, this call simply releases
+ * the channel.)
+ */
+ halt_channel(hcd, hc, qtd, halt_status);
+ } else {
+ /*
+ * The channel is automatically disabled by the core for OUT
+ * transfers in Slave mode.
+ */
+ release_channel(hcd, hc, qtd, halt_status);
+ }
+}
+
+/**
+ * Performs common cleanup for periodic transfers after a Transfer Complete
+ * interrupt. This function should be called after any endpoint type specific
+ * handling is finished to release the host channel.
+ */
+static void complete_periodic_xfer(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd,
+ dwc_otg_halt_status_e halt_status)
+{
+ hctsiz_data_t hctsiz;
+ qtd->error_count = 0;
+
+ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+ if (!hc->ep_is_in || hctsiz.b.pktcnt == 0) {
+ /* Core halts channel in these cases. */
+ release_channel(hcd, hc, qtd, halt_status);
+ } else {
+ /* Flush any outstanding requests from the Tx queue. */
+ halt_channel(hcd, hc, qtd, halt_status);
+ }
+}
+
+static int32_t handle_xfercomp_isoc_split_in(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ uint32_t len;
+ struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
+
+ len = get_actual_xfer_length(hc, hc_regs, qtd,
+ DWC_OTG_HC_XFER_COMPLETE, NULL);
+
+ if (!len) {
+ qtd->complete_split = 0;
+ qtd->isoc_split_offset = 0;
+ return 0;
+ }
+ frame_desc->actual_length += len;
+
+ if (hc->align_buff && len)
+ dwc_memcpy(qtd->urb->buf + frame_desc->offset +
+ qtd->isoc_split_offset, hc->qh->dw_align_buf, len);
+ qtd->isoc_split_offset += len;
+
+ if (frame_desc->length == frame_desc->actual_length) {
+ frame_desc->status = 0;
+ qtd->isoc_frame_index++;
+ qtd->complete_split = 0;
+ qtd->isoc_split_offset = 0;
+ }
+
+ if (qtd->isoc_frame_index == qtd->urb->packet_count) {
+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+ } else {
+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+ }
+
+ return 1; /* Indicates that channel released */
+}
+
+/**
+ * Handles a host channel Transfer Complete interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_xfercomp_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ int urb_xfer_done;
+ dwc_otg_halt_status_e halt_status = DWC_OTG_HC_XFER_COMPLETE;
+ dwc_otg_hcd_urb_t *urb = qtd->urb;
+ int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
+
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "Transfer Complete--\n", hc->hc_num);
+
+ if (hcd->core_if->dma_desc_enable) {
+ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, halt_status);
+ if (pipe_type == UE_ISOCHRONOUS) {
+ /* Do not disable the interrupt, just clear it */
+ clear_hc_int(hc_regs, xfercomp);
+ return 1;
+ }
+ goto handle_xfercomp_done;
+ }
+
+ /*
+ * Handle xfer complete on CSPLIT.
+ */
+
+ if (hc->qh->do_split) {
+ if ((hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && hc->ep_is_in
+ && hcd->core_if->dma_enable) {
+ if (qtd->complete_split
+ && handle_xfercomp_isoc_split_in(hcd, hc, hc_regs,
+ qtd))
+ goto handle_xfercomp_done;
+ } else {
+ qtd->complete_split = 0;
+ }
+ }
+
+ /* Update the QTD and URB states. */
+ switch (pipe_type) {
+ case UE_CONTROL:
+ switch (qtd->control_phase) {
+ case DWC_OTG_CONTROL_SETUP:
+ if (urb->length > 0) {
+ qtd->control_phase = DWC_OTG_CONTROL_DATA;
+ } else {
+ qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+ }
+ DWC_DEBUGPL(DBG_HCDV,
+ " Control setup transaction done\n");
+ halt_status = DWC_OTG_HC_XFER_COMPLETE;
+ break;
+ case DWC_OTG_CONTROL_DATA:{
+ urb_xfer_done =
+ update_urb_state_xfer_comp(hc, hc_regs, urb,
+ qtd);
+ if (urb_xfer_done) {
+ qtd->control_phase =
+ DWC_OTG_CONTROL_STATUS;
+ DWC_DEBUGPL(DBG_HCDV,
+ " Control data transfer done\n");
+ } else {
+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+ }
+ halt_status = DWC_OTG_HC_XFER_COMPLETE;
+ break;
+ }
+ case DWC_OTG_CONTROL_STATUS:
+ DWC_DEBUGPL(DBG_HCDV, " Control transfer complete\n");
+ if (urb->status == -DWC_E_IN_PROGRESS) {
+ urb->status = 0;
+ }
+ hcd->fops->complete(hcd, urb->priv, urb, urb->status);
+ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+ break;
+ }
+
+ complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
+ break;
+ case UE_BULK:
+ DWC_DEBUGPL(DBG_HCDV, " Bulk transfer complete\n");
+ urb_xfer_done =
+ update_urb_state_xfer_comp(hc, hc_regs, urb, qtd);
+ if (urb_xfer_done) {
+ hcd->fops->complete(hcd, urb->priv, urb, urb->status);
+ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+ } else {
+ halt_status = DWC_OTG_HC_XFER_COMPLETE;
+ }
+
+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+ complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
+ break;
+ case UE_INTERRUPT:
+ DWC_DEBUGPL(DBG_HCDV, " Interrupt transfer complete\n");
+ urb_xfer_done =
+ update_urb_state_xfer_comp(hc, hc_regs, urb, qtd);
+
+ /*
+ * Interrupt URB is done on the first transfer complete
+ * interrupt.
+ */
+ if (urb_xfer_done) {
+ hcd->fops->complete(hcd, urb->priv, urb, urb->status);
+ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+ } else {
+ halt_status = DWC_OTG_HC_XFER_COMPLETE;
+ }
+
+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+ complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
+ break;
+ case UE_ISOCHRONOUS:
+ DWC_DEBUGPL(DBG_HCDV, " Isochronous transfer complete\n");
+ if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_ALL) {
+ halt_status =
+ update_isoc_urb_state(hcd, hc, hc_regs, qtd,
+ DWC_OTG_HC_XFER_COMPLETE);
+ }
+ complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
+ break;
+ }
+
+handle_xfercomp_done:
+ disable_hc_int(hc_regs, xfercompl);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel STALL interrupt. This handler may be called in
+ * either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_stall_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ dwc_otg_hcd_urb_t *urb = qtd->urb;
+ int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
+
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "STALL Received--\n", hc->hc_num);
+
+ if (hcd->core_if->dma_desc_enable) {
+ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, DWC_OTG_HC_XFER_STALL);
+ goto handle_stall_done;
+ }
+
+ if (pipe_type == UE_CONTROL) {
+ hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE);
+ }
+
+ if (pipe_type == UE_BULK || pipe_type == UE_INTERRUPT) {
+ hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE);
+ /*
+ * USB protocol requires resetting the data toggle for bulk
+ * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT)
+ * setup command is issued to the endpoint. Anticipate the
+ * CLEAR_FEATURE command since a STALL has occurred and reset
+ * the data toggle now.
+ */
+ hc->qh->data_toggle = 0;
+ }
+
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_STALL);
+
+handle_stall_done:
+ disable_hc_int(hc_regs, stall);
+
+ return 1;
+}
+
+/*
+ * Updates the state of the URB when a transfer has been stopped due to an
+ * abnormal condition before the transfer completes. Modifies the
+ * actual_length field of the URB to reflect the number of bytes that have
+ * actually been transferred via the host channel.
+ */
+static void update_urb_state_xfer_intr(dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_hcd_urb_t * urb,
+ dwc_otg_qtd_t * qtd,
+ dwc_otg_halt_status_e halt_status)
+{
+ uint32_t bytes_transferred = get_actual_xfer_length(hc, hc_regs, qtd,
+ halt_status, NULL);
+ /* non DWORD-aligned buffer case handling. */
+ if (hc->align_buff && bytes_transferred && hc->ep_is_in) {
+ dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf,
+ bytes_transferred);
+ }
+
+ urb->actual_length += bytes_transferred;
+
+#ifdef DEBUG
+ {
+ hctsiz_data_t hctsiz;
+ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n",
+ __func__, (hc->ep_is_in ? "IN" : "OUT"),
+ hc->hc_num);
+ DWC_DEBUGPL(DBG_HCDV, " hc->start_pkt_count %d\n",
+ hc->start_pkt_count);
+ DWC_DEBUGPL(DBG_HCDV, " hctsiz.pktcnt %d\n", hctsiz.b.pktcnt);
+ DWC_DEBUGPL(DBG_HCDV, " hc->max_packet %d\n", hc->max_packet);
+ DWC_DEBUGPL(DBG_HCDV, " bytes_transferred %d\n",
+ bytes_transferred);
+ DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n",
+ urb->actual_length);
+ DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n",
+ urb->length);
+ }
+#endif
+}
+
+/**
+ * Handles a host channel NAK interrupt. This handler may be called in either
+ * DMA mode or Slave mode.
+ */
+static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "NAK Received--\n", hc->hc_num);
+
+ /*
+ * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
+ * interrupt. Re-start the SSPLIT transfer.
+ */
+ if (hc->do_split) {
+ if (hc->complete_split) {
+ qtd->error_count = 0;
+ }
+ qtd->complete_split = 0;
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
+ goto handle_nak_done;
+ }
+
+ switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
+ case UE_CONTROL:
+ case UE_BULK:
+ if (hcd->core_if->dma_enable && hc->ep_is_in) {
+ /*
+ * NAK interrupts are enabled on bulk/control IN
+ * transfers in DMA mode for the sole purpose of
+ * resetting the error count after a transaction error
+ * occurs. The core will continue transferring data.
+ */
+ qtd->error_count = 0;
+ goto handle_nak_done;
+ }
+
+ /*
+ * NAK interrupts normally occur during OUT transfers in DMA
+ * or Slave mode. For IN transfers, more requests will be
+ * queued as request queue space is available.
+ */
+ qtd->error_count = 0;
+
+ if (!hc->qh->ping_state) {
+ update_urb_state_xfer_intr(hc, hc_regs,
+ qtd->urb, qtd,
+ DWC_OTG_HC_XFER_NAK);
+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+
+ if (hc->speed == DWC_OTG_EP_SPEED_HIGH)
+ hc->qh->ping_state = 1;
+ }
+
+ /*
+ * Halt the channel so the transfer can be re-started from
+ * the appropriate point or the PING protocol will
+ * start/continue.
+ */
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
+ break;
+ case UE_INTERRUPT:
+ qtd->error_count = 0;
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
+ break;
+ case UE_ISOCHRONOUS:
+ /* Should never get called for isochronous transfers. */
+ DWC_ASSERT(1, "NACK interrupt for ISOC transfer\n");
+ break;
+ }
+
+handle_nak_done:
+ disable_hc_int(hc_regs, nak);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel ACK interrupt. This interrupt is enabled when
+ * performing the PING protocol in Slave mode, when errors occur during
+ * either Slave mode or DMA mode, and during Start Split transactions.
+ */
+static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "ACK Received--\n", hc->hc_num);
+
+ if (hc->do_split) {
+ /*
+ * Handle ACK on SSPLIT.
+ * ACK should not occur in CSPLIT.
+ */
+ if (!hc->ep_is_in && hc->data_pid_start != DWC_OTG_HC_PID_SETUP) {
+ qtd->ssplit_out_xfer_count = hc->xfer_len;
+ }
+ if (!(hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)) {
+ /* Don't need complete for isochronous out transfers. */
+ qtd->complete_split = 1;
+ }
+
+ /* ISOC OUT */
+ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) {
+ switch (hc->xact_pos) {
+ case DWC_HCSPLIT_XACTPOS_ALL:
+ break;
+ case DWC_HCSPLIT_XACTPOS_END:
+ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
+ qtd->isoc_split_offset = 0;
+ break;
+ case DWC_HCSPLIT_XACTPOS_BEGIN:
+ case DWC_HCSPLIT_XACTPOS_MID:
+ /*
+ * For BEGIN or MID, calculate the length for
+ * the next microframe to determine the correct
+ * SSPLIT token, either MID or END.
+ */
+ {
+ struct dwc_otg_hcd_iso_packet_desc
+ *frame_desc;
+
+ frame_desc =
+ &qtd->urb->
+ iso_descs[qtd->isoc_frame_index];
+ qtd->isoc_split_offset += 188;
+
+ if ((frame_desc->length -
+ qtd->isoc_split_offset) <= 188) {
+ qtd->isoc_split_pos =
+ DWC_HCSPLIT_XACTPOS_END;
+ } else {
+ qtd->isoc_split_pos =
+ DWC_HCSPLIT_XACTPOS_MID;
+ }
+
+ }
+ break;
+ }
+ } else {
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK);
+ }
+ } else {
+ qtd->error_count = 0;
+
+ if (hc->qh->ping_state) {
+ hc->qh->ping_state = 0;
+ /*
+ * Halt the channel so the transfer can be re-started
+ * from the appropriate point. This only happens in
+ * Slave mode. In DMA mode, the ping_state is cleared
+ * when the transfer is started because the core
+ * automatically executes the PING, then the transfer.
+ */
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK);
+ }
+ }
+
+ /*
+ * If the ACK occurred when _not_ in the PING state, let the channel
+ * continue transferring data after clearing the error count.
+ */
+
+ disable_hc_int(hc_regs, ack);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel NYET interrupt. This interrupt should only occur on
+ * Bulk and Control OUT endpoints and for complete split transactions. If a
+ * NYET occurs at the same time as a Transfer Complete interrupt, it is
+ * handled in the xfercomp interrupt handler, not here. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "NYET Received--\n", hc->hc_num);
+
+ /*
+ * NYET on CSPLIT
+ * re-do the CSPLIT immediately on non-periodic
+ */
+ if (hc->do_split && hc->complete_split) {
+ if (hc->ep_is_in && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+ && hcd->core_if->dma_enable) {
+ qtd->complete_split = 0;
+ qtd->isoc_split_offset = 0;
+ if (++qtd->isoc_frame_index == qtd->urb->packet_count) {
+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+ }
+ else
+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+ goto handle_nyet_done;
+ }
+
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ int frnum = dwc_otg_hcd_get_frame_number(hcd);
+
+ if (dwc_full_frame_num(frnum) !=
+ dwc_full_frame_num(hc->qh->sched_frame)) {
+ /*
+ * No longer in the same full speed frame.
+ * Treat this as a transaction error.
+ */
+#if 0
+ /** @todo Fix system performance so this can
+ * be treated as an error. Right now complete
+ * splits cannot be scheduled precisely enough
+ * due to other system activity, so this error
+ * occurs regularly in Slave mode.
+ */
+ qtd->error_count++;
+#endif
+ qtd->complete_split = 0;
+ halt_channel(hcd, hc, qtd,
+ DWC_OTG_HC_XFER_XACT_ERR);
+ /** @todo add support for isoc release */
+ goto handle_nyet_done;
+ }
+ }
+
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET);
+ goto handle_nyet_done;
+ }
+
+ hc->qh->ping_state = 1;
+ qtd->error_count = 0;
+
+ update_urb_state_xfer_intr(hc, hc_regs, qtd->urb, qtd,
+ DWC_OTG_HC_XFER_NYET);
+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+
+ /*
+ * Halt the channel and re-start the transfer so the PING
+ * protocol will start.
+ */
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET);
+
+handle_nyet_done:
+ disable_hc_int(hc_regs, nyet);
+ return 1;
+}
+
+/**
+ * Handles a host channel babble interrupt. This handler may be called in
+ * either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_babble_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "Babble Error--\n", hc->hc_num);
+
+ if (hcd->core_if->dma_desc_enable) {
+ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
+ DWC_OTG_HC_XFER_BABBLE_ERR);
+ goto handle_babble_done;
+ }
+
+ if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+ hcd->fops->complete(hcd, qtd->urb->priv,
+ qtd->urb, -DWC_E_OVERFLOW);
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_BABBLE_ERR);
+ } else {
+ dwc_otg_halt_status_e halt_status;
+ halt_status = update_isoc_urb_state(hcd, hc, hc_regs, qtd,
+ DWC_OTG_HC_XFER_BABBLE_ERR);
+ halt_channel(hcd, hc, qtd, halt_status);
+ }
+
+handle_babble_done:
+ disable_hc_int(hc_regs, bblerr);
+ return 1;
+}
+
+/**
+ * Handles a host channel AHB error interrupt. This handler is only called in
+ * DMA mode.
+ */
+static int32_t handle_hc_ahberr_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ hcchar_data_t hcchar;
+ hcsplt_data_t hcsplt;
+ hctsiz_data_t hctsiz;
+ uint32_t hcdma;
+ char *pipetype, *speed;
+
+ dwc_otg_hcd_urb_t *urb = qtd->urb;
+
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "AHB Error--\n", hc->hc_num);
+
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
+ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+ hcdma = DWC_READ_REG32(&hc_regs->hcdma);
+
+ DWC_ERROR("AHB ERROR, Channel %d\n", hc->hc_num);
+ DWC_ERROR(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32);
+ DWC_ERROR(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma);
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Enqueue\n");
+ DWC_ERROR(" Device address: %d\n",
+ dwc_otg_hcd_get_dev_addr(&urb->pipe_info));
+ DWC_ERROR(" Endpoint: %d, %s\n",
+ dwc_otg_hcd_get_ep_num(&urb->pipe_info),
+ (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"));
+
+ switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) {
+ case UE_CONTROL:
+ pipetype = "CONTROL";
+ break;
+ case UE_BULK:
+ pipetype = "BULK";
+ break;
+ case UE_INTERRUPT:
+ pipetype = "INTERRUPT";
+ break;
+ case UE_ISOCHRONOUS:
+ pipetype = "ISOCHRONOUS";
+ break;
+ default:
+ pipetype = "UNKNOWN";
+ break;
+ }
+
+ DWC_ERROR(" Endpoint type: %s\n", pipetype);
+
+ switch (hc->speed) {
+ case DWC_OTG_EP_SPEED_HIGH:
+ speed = "HIGH";
+ break;
+ case DWC_OTG_EP_SPEED_FULL:
+ speed = "FULL";
+ break;
+ case DWC_OTG_EP_SPEED_LOW:
+ speed = "LOW";
+ break;
+ default:
+ speed = "UNKNOWN";
+ break;
+ };
+
+ DWC_ERROR(" Speed: %s\n", speed);
+
+ DWC_ERROR(" Max packet size: %d\n",
+ dwc_otg_hcd_get_mps(&urb->pipe_info));
+ DWC_ERROR(" Data buffer length: %d\n", urb->length);
+ DWC_ERROR(" Transfer buffer: %p, Transfer DMA: %p\n",
+ urb->buf, (void *)urb->dma);
+ DWC_ERROR(" Setup buffer: %p, Setup DMA: %p\n",
+ urb->setup_packet, (void *)urb->setup_dma);
+ DWC_ERROR(" Interval: %d\n", urb->interval);
+
+ /* Core haltes the channel for Descriptor DMA mode */
+ if (hcd->core_if->dma_desc_enable) {
+ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
+ DWC_OTG_HC_XFER_AHB_ERR);
+ goto handle_ahberr_done;
+ }
+
+ hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_IO);
+
+ /*
+ * Force a channel halt. Don't call halt_channel because that won't
+ * write to the HCCHARn register in DMA mode to force the halt.
+ */
+ dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR);
+handle_ahberr_done:
+ disable_hc_int(hc_regs, ahberr);
+ return 1;
+}
+
+/**
+ * Handles a host channel transaction error interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_xacterr_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "Transaction Error--\n", hc->hc_num);
+
+ if (hcd->core_if->dma_desc_enable) {
+ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
+ DWC_OTG_HC_XFER_XACT_ERR);
+ goto handle_xacterr_done;
+ }
+
+ if (qtd == NULL)
+ goto handle_xacterr_done;
+
+ if(qtd->urb == NULL)
+ goto handle_xacterr_done;
+
+ if (&qtd->urb->pipe_info == NULL)
+ goto handle_xacterr_done;
+
+ switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
+ case UE_CONTROL:
+ case UE_BULK:
+ qtd->error_count++;
+ if (!hc->qh->ping_state) {
+
+ update_urb_state_xfer_intr(hc, hc_regs,
+ qtd->urb, qtd,
+ DWC_OTG_HC_XFER_XACT_ERR);
+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+ if (!hc->ep_is_in && hc->speed == DWC_OTG_EP_SPEED_HIGH) {
+ hc->qh->ping_state = 1;
+ }
+ }
+
+ /*
+ * Halt the channel so the transfer can be re-started from
+ * the appropriate point or the PING protocol will start.
+ */
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+ break;
+ case UE_INTERRUPT:
+ qtd->error_count++;
+ if (hc->do_split && hc->complete_split) {
+ qtd->complete_split = 0;
+ }
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+ break;
+ case UE_ISOCHRONOUS:
+ {
+ dwc_otg_halt_status_e halt_status;
+ halt_status =
+ update_isoc_urb_state(hcd, hc, hc_regs, qtd,
+ DWC_OTG_HC_XFER_XACT_ERR);
+
+ halt_channel(hcd, hc, qtd, halt_status);
+ }
+ break;
+ }
+handle_xacterr_done:
+ disable_hc_int(hc_regs, xacterr);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel frame overrun interrupt. This handler may be called
+ * in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_frmovrun_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "Frame Overrun--\n", hc->hc_num);
+
+ switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
+ case UE_CONTROL:
+ case UE_BULK:
+ break;
+ case UE_INTERRUPT:
+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_FRAME_OVERRUN);
+ break;
+ case UE_ISOCHRONOUS:
+ {
+ dwc_otg_halt_status_e halt_status;
+ halt_status =
+ update_isoc_urb_state(hcd, hc, hc_regs, qtd,
+ DWC_OTG_HC_XFER_FRAME_OVERRUN);
+
+ halt_channel(hcd, hc, qtd, halt_status);
+ }
+ break;
+ }
+
+ disable_hc_int(hc_regs, frmovrun);
+
+ return 1;
+}
+
+/**
+ * Handles a host channel data toggle error interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "Data Toggle Error--\n", hc->hc_num);
+
+ if (hc->ep_is_in) {
+ qtd->error_count = 0;
+ } else {
+ DWC_ERROR("Data Toggle Error on OUT transfer,"
+ "channel %d\n", hc->hc_num);
+ }
+
+ disable_hc_int(hc_regs, datatglerr);
+
+ return 1;
+}
+
+#ifdef DEBUG
+/**
+ * This function is for debug only. It checks that a valid halt status is set
+ * and that HCCHARn.chdis is clear. If there's a problem, corrective action is
+ * taken and a warning is issued.
+ * @return 1 if halt status is ok, 0 otherwise.
+ */
+static inline int halt_status_ok(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ hcchar_data_t hcchar;
+ hctsiz_data_t hctsiz;
+ hcint_data_t hcint;
+ hcintmsk_data_t hcintmsk;
+ hcsplt_data_t hcsplt;
+
+ if (hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS) {
+ /*
+ * This code is here only as a check. This condition should
+ * never happen. Ignore the halt if it does occur.
+ */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
+ hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
+ DWC_WARN
+ ("%s: hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS, "
+ "channel %d, hcchar 0x%08x, hctsiz 0x%08x, "
+ "hcint 0x%08x, hcintmsk 0x%08x, "
+ "hcsplt 0x%08x, qtd->complete_split %d\n", __func__,
+ hc->hc_num, hcchar.d32, hctsiz.d32, hcint.d32,
+ hcintmsk.d32, hcsplt.d32, qtd->complete_split);
+
+ DWC_WARN("%s: no halt status, channel %d, ignoring interrupt\n",
+ __func__, hc->hc_num);
+ DWC_WARN("\n");
+ clear_hc_int(hc_regs, chhltd);
+ return 0;
+ }
+
+ /*
+ * This code is here only as a check. hcchar.chdis should
+ * never be set when the halt interrupt occurs. Halt the
+ * channel again if it does occur.
+ */
+ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+ if (hcchar.b.chdis) {
+ DWC_WARN("%s: hcchar.chdis set unexpectedly, "
+ "hcchar 0x%08x, trying to halt again\n",
+ __func__, hcchar.d32);
+ clear_hc_int(hc_regs, chhltd);
+ hc->halt_pending = 0;
+ halt_channel(hcd, hc, qtd, hc->halt_status);
+ return 0;
+ }
+
+ return 1;
+}
+#endif
+
+/**
+ * Handles a host Channel Halted interrupt in DMA mode. This handler
+ * determines the reason the channel halted and proceeds accordingly.
+ */
+static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ hcint_data_t hcint;
+ hcintmsk_data_t hcintmsk;
+ int out_nak_enh = 0;
+
+ /* For core with OUT NAK enhancement, the flow for high-
+ * speed CONTROL/BULK OUT is handled a little differently.
+ */
+ if (hcd->core_if->snpsid >= OTG_CORE_REV_2_71a) {
+ if (hc->speed == DWC_OTG_EP_SPEED_HIGH && !hc->ep_is_in &&
+ (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+ hc->ep_type == DWC_OTG_EP_TYPE_BULK)) {
+ out_nak_enh = 1;
+ }
+ }
+
+ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
+ (hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR
+ && !hcd->core_if->dma_desc_enable)) {
+ /*
+ * Just release the channel. A dequeue can happen on a
+ * transfer timeout. In the case of an AHB Error, the channel
+ * was forced to halt because there's no way to gracefully
+ * recover.
+ */
+ if (hcd->core_if->dma_desc_enable)
+ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
+ hc->halt_status);
+ else
+ release_channel(hcd, hc, qtd, hc->halt_status);
+ return;
+ }
+
+ /* Read the HCINTn register to determine the cause for the halt. */
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
+
+ if (hcint.b.xfercomp) {
+ /** @todo This is here because of a possible hardware bug. Spec
+ * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT
+ * interrupt w/ACK bit set should occur, but I only see the
+ * XFERCOMP bit, even with it masked out. This is a workaround
+ * for that behavior. Should fix this when hardware is fixed.
+ */
+ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) {
+ handle_hc_ack_intr(hcd, hc, hc_regs, qtd);
+ }
+ handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd);
+ } else if (hcint.b.stall) {
+ handle_hc_stall_intr(hcd, hc, hc_regs, qtd);
+ } else if (hcint.b.xacterr && !hcd->core_if->dma_desc_enable) {
+ if (out_nak_enh) {
+ if (hcint.b.nyet || hcint.b.nak || hcint.b.ack) {
+ DWC_DEBUG("XactErr with NYET/NAK/ACK\n");
+ qtd->error_count = 0;
+ } else {
+ DWC_DEBUG("XactErr without NYET/NAK/ACK\n");
+ }
+ }
+
+ /*
+ * Must handle xacterr before nak or ack. Could get a xacterr
+ * at the same time as either of these on a BULK/CONTROL OUT
+ * that started with a PING. The xacterr takes precedence.
+ */
+ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
+ } else if (hcint.b.xcs_xact && hcd->core_if->dma_desc_enable) {
+ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
+ } else if (hcint.b.ahberr && hcd->core_if->dma_desc_enable) {
+ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
+ } else if (hcint.b.bblerr) {
+ handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
+ } else if (hcint.b.frmovrun) {
+ handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd);
+ } else if (!out_nak_enh) {
+ if (hcint.b.nyet) {
+ /*
+ * Must handle nyet before nak or ack. Could get a nyet at the
+ * same time as either of those on a BULK/CONTROL OUT that
+ * started with a PING. The nyet takes precedence.
+ */
+ handle_hc_nyet_intr(hcd, hc, hc_regs, qtd);
+ } else if (hcint.b.nak && !hcintmsk.b.nak) {
+ /*
+ * If nak is not masked, it's because a non-split IN transfer
+ * is in an error state. In that case, the nak is handled by
+ * the nak interrupt handler, not here. Handle nak here for
+ * BULK/CONTROL OUT transfers, which halt on a NAK to allow
+ * rewinding the buffer pointer.
+ */
+ handle_hc_nak_intr(hcd, hc, hc_regs, qtd);
+ } else if (hcint.b.ack && !hcintmsk.b.ack) {
+ /*
+ * If ack is not masked, it's because a non-split IN transfer
+ * is in an error state. In that case, the ack is handled by
+ * the ack interrupt handler, not here. Handle ack here for
+ * split transfers. Start splits halt on ACK.
+ */
+ handle_hc_ack_intr(hcd, hc, hc_regs, qtd);
+ } else {
+ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+ /*
+ * A periodic transfer halted with no other channel
+ * interrupts set. Assume it was halted by the core
+ * because it could not be completed in its scheduled
+ * (micro)frame.
+ */
+#ifdef DEBUG
+ DWC_PRINTF
+ ("%s: Halt channel %d (assume incomplete periodic transfer)\n",
+ __func__, hc->hc_num);
+#endif
+ halt_channel(hcd, hc, qtd,
+ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE);
+ } else {
+ DWC_ERROR
+ ("%s: Channel %d, DMA Mode -- ChHltd set, but reason "
+ "for halting is unknown, hcint 0x%08x, intsts 0x%08x\n",
+ __func__, hc->hc_num, hcint.d32,
+ DWC_READ_REG32(&hcd->
+ core_if->core_global_regs->
+ gintsts));
+ halt_channel(hcd, hc, qtd,
+ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE);
+ }
+
+ }
+ } else {
+ DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n",
+ hcint.d32);
+ }
+}
+
+/**
+ * Handles a host channel Channel Halted interrupt.
+ *
+ * In slave mode, this handler is called only when the driver specifically
+ * requests a halt. This occurs during handling other host channel interrupts
+ * (e.g. nak, xacterr, stall, nyet, etc.).
+ *
+ * In DMA mode, this is the interrupt that occurs when the core has finished
+ * processing a transfer on a channel. Other host channel interrupts (except
+ * ahberr) are disabled in DMA mode.
+ */
+static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd,
+ dwc_hc_t * hc,
+ dwc_otg_hc_regs_t * hc_regs,
+ dwc_otg_qtd_t * qtd)
+{
+ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+ "Channel Halted--\n", hc->hc_num);
+
+ if (hcd->core_if->dma_enable) {
+ handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd);
+ } else {
+#ifdef DEBUG
+ if (!halt_status_ok(hcd, hc, hc_regs, qtd)) {
+ return 1;
+ }
+#endif
+ release_channel(hcd, hc, qtd, hc->halt_status);
+ }
+
+ return 1;
+}
+
+/** Handles interrupt for a specific Host Channel */
+int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
+{
+ int retval = 0;
+ hcint_data_t hcint;
+ hcintmsk_data_t hcintmsk;
+ dwc_hc_t *hc;
+ dwc_otg_hc_regs_t *hc_regs;
+ dwc_otg_qtd_t *qtd;
+
+ DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", num);
+
+ hc = dwc_otg_hcd->hc_ptr_array[num];
+ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num];
+ qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
+
+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
+ DWC_DEBUGPL(DBG_HCDV,
+ " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n",
+ hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32));
+ hcint.d32 = hcint.d32 & hcintmsk.d32;
+
+ if (!dwc_otg_hcd->core_if->dma_enable) {
+ if (hcint.b.chhltd && hcint.d32 != 0x2) {
+ hcint.b.chhltd = 0;
+ }
+ }
+
+ if (hcint.b.xfercomp) {
+ retval |=
+ handle_hc_xfercomp_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ /*
+ * If NYET occurred at same time as Xfer Complete, the NYET is
+ * handled by the Xfer Complete interrupt handler. Don't want
+ * to call the NYET interrupt handler in this case.
+ */
+ hcint.b.nyet = 0;
+ }
+ if (hcint.b.chhltd) {
+ retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.ahberr) {
+ retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.stall) {
+ retval |= handle_hc_stall_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.nak) {
+ retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.ack) {
+ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.nyet) {
+ retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.xacterr) {
+ retval |= handle_hc_xacterr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.bblerr) {
+ retval |= handle_hc_babble_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.frmovrun) {
+ retval |=
+ handle_hc_frmovrun_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+ if (hcint.b.datatglerr) {
+ retval |=
+ handle_hc_datatglerr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+ }
+
+ return retval;
+}
+
+#endif /* DWC_DEVICE_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/dwc_otg/dwc_otg_hcd_linux.c
new file mode 100644
index 0000000..5c09aff
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd_linux.c
@@ -0,0 +1,888 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $
+ * $Revision: #20 $
+ * $Date: 2011/10/26 $
+ * $Change: 1872981 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+/**
+ * @file
+ *
+ * This file contains the implementation of the HCD. In Linux, the HCD
+ * implements the hc_driver API.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <linux/usb.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+#include <../drivers/usb/core/hcd.h>
+#else
+#include <linux/usb/hcd.h>
+#endif
+
+#include "dwc_otg_hcd_if.h"
+#include "dwc_otg_dbg.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_hcd.h"
+/**
+ * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
+ * qualified with its direction (possible 32 endpoints per device).
+ */
+#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \
+ ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4)
+
+static const char dwc_otg_hcd_name[] = "dwc_otg";
+
+/** @name Linux HC Driver API Functions */
+/** @{ */
+static int urb_enqueue(struct usb_hcd *hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ struct usb_host_endpoint *ep,
+#endif
+ struct urb *urb, gfp_t mem_flags);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
+#else
+static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
+#endif
+
+static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
+#endif
+static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd);
+extern int hcd_start(struct usb_hcd *hcd);
+extern void hcd_stop(struct usb_hcd *hcd);
+static int get_frame_number(struct usb_hcd *hcd);
+extern int hub_status_data(struct usb_hcd *hcd, char *buf);
+extern int hub_control(struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue, u16 wIndex, char *buf, u16 wLength);
+
+struct wrapper_priv_data {
+ dwc_otg_hcd_t *dwc_otg_hcd;
+};
+
+/** @} */
+
+int comcerto_dwc_dummy_bus_suspend(struct usb_hcd *hcd)
+{
+ printk("\n comcerto_dwc_dummy_bus_suspend...");
+ return 0;
+}
+
+int comcerto_dwc_dummy_bus_resume(struct usb_hcd *hcd)
+{
+ printk("\n comcerto_dwc_dummy_bus_resume...");
+ return 0;
+}
+
+
+static struct hc_driver dwc_otg_hc_driver = {
+
+ .description = dwc_otg_hcd_name,
+ .product_desc = "DWC OTG Controller",
+ .hcd_priv_size = sizeof(struct wrapper_priv_data),
+
+ .irq = dwc_otg_hcd_irq,
+
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ //.reset =
+ .start = hcd_start,
+ //.suspend =
+ //.resume =
+ .stop = hcd_stop,
+
+ .urb_enqueue = urb_enqueue,
+ .urb_dequeue = urb_dequeue,
+ .endpoint_disable = endpoint_disable,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+ .endpoint_reset = endpoint_reset,
+#endif
+ .get_frame_number = get_frame_number,
+
+ .hub_status_data = hub_status_data,
+ .hub_control = hub_control,
+ .bus_suspend = comcerto_dwc_dummy_bus_suspend,
+ .bus_resume = comcerto_dwc_dummy_bus_resume,
+};
+
+/** Gets the dwc_otg_hcd from a struct usb_hcd */
+static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd)
+{
+ struct wrapper_priv_data *p;
+ p = (struct wrapper_priv_data *)(hcd->hcd_priv);
+ return p->dwc_otg_hcd;
+}
+
+/** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */
+static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+ return dwc_otg_hcd_get_priv_data(dwc_otg_hcd);
+}
+
+/** Gets the usb_host_endpoint associated with an URB. */
+inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *urb)
+{
+ struct usb_device *dev = urb->dev;
+ int ep_num = usb_pipeendpoint(urb->pipe);
+
+ if (usb_pipein(urb->pipe))
+ return dev->ep_in[ep_num];
+ else
+ return dev->ep_out[ep_num];
+}
+
+static int _disconnect(dwc_otg_hcd_t * hcd)
+{
+ struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd);
+
+ usb_hcd->self.is_b_host = 0;
+ return 0;
+}
+
+static int _start(dwc_otg_hcd_t * hcd)
+{
+ struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd);
+
+ usb_hcd->self.is_b_host = dwc_otg_hcd_is_b_host(hcd);
+ hcd_start(usb_hcd);
+
+ return 0;
+}
+
+static int _hub_info(dwc_otg_hcd_t * hcd, void *urb_handle, uint32_t * hub_addr,
+ uint32_t * port_addr)
+{
+ struct urb *urb = (struct urb *)urb_handle;
+ if (urb->dev->tt) {
+ *hub_addr = urb->dev->tt->hub->devnum;
+ } else {
+ *hub_addr = 0;
+ }
+ *port_addr = urb->dev->ttport;
+ return 0;
+}
+
+static int _speed(dwc_otg_hcd_t * hcd, void *urb_handle)
+{
+ struct urb *urb = (struct urb *)urb_handle;
+ return urb->dev->speed;
+}
+
+static int _get_b_hnp_enable(dwc_otg_hcd_t * hcd)
+{
+ struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd);
+ return usb_hcd->self.b_hnp_enable;
+}
+
+static void allocate_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw,
+ struct urb *urb)
+{
+ hcd_to_bus(hcd)->bandwidth_allocated += bw / urb->interval;
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ hcd_to_bus(hcd)->bandwidth_isoc_reqs++;
+ } else {
+ hcd_to_bus(hcd)->bandwidth_int_reqs++;
+ }
+}
+
+static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw,
+ struct urb *urb)
+{
+ hcd_to_bus(hcd)->bandwidth_allocated -= bw / urb->interval;
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ hcd_to_bus(hcd)->bandwidth_isoc_reqs--;
+ } else {
+ hcd_to_bus(hcd)->bandwidth_int_reqs--;
+ }
+}
+
+/**
+ * Sets the final status of an URB and returns it to the device driver. Any
+ * required cleanup of the URB is performed.
+ */
+static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
+ dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
+{
+ struct urb *urb = (struct urb *)urb_handle;
+#ifdef DEBUG
+ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+ DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
+ __func__, urb, usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "IN" : "OUT", status);
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ int i;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ DWC_PRINTF(" ISO Desc %d status: %d\n",
+ i, urb->iso_frame_desc[i].status);
+ }
+ }
+ }
+#endif
+
+ urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
+ /* Convert status value. */
+ switch (status) {
+ case -DWC_E_PROTOCOL:
+ status = -EPROTO;
+ break;
+ case -DWC_E_IN_PROGRESS:
+ status = -EINPROGRESS;
+ break;
+ case -DWC_E_PIPE:
+ status = -EPIPE;
+ break;
+ case -DWC_E_IO:
+ status = -EIO;
+ break;
+ case -DWC_E_TIMEOUT:
+ status = -ETIMEDOUT;
+ break;
+ case -DWC_E_OVERFLOW:
+ status = -EOVERFLOW;
+ break;
+ default:
+ if (status) {
+ DWC_PRINTF("Uknown urb status %d\n", status);
+
+ }
+ }
+
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ int i;
+
+ urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb);
+ for (i = 0; i < urb->number_of_packets; ++i) {
+ urb->iso_frame_desc[i].actual_length =
+ dwc_otg_hcd_urb_get_iso_desc_actual_length
+ (dwc_otg_urb, i);
+ urb->iso_frame_desc[i].status =
+ dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_urb, i);
+ }
+ }
+
+ urb->status = status;
+ urb->hcpriv = NULL;
+ if (!status) {
+ if ((urb->transfer_flags & URB_SHORT_NOT_OK) &&
+ (urb->actual_length < urb->transfer_buffer_length)) {
+ urb->status = -EREMOTEIO;
+ }
+ }
+
+ if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) ||
+ (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+ struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb);
+ if (ep) {
+ free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd),
+ dwc_otg_hcd_get_ep_bandwidth(hcd,
+ ep->hcpriv),
+ urb);
+ }
+ }
+
+ DWC_FREE(dwc_otg_urb);
+
+ DWC_SPINUNLOCK(hcd->lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
+#else
+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
+#endif
+ DWC_SPINLOCK(hcd->lock);
+
+ return 0;
+}
+
+static struct dwc_otg_hcd_function_ops hcd_fops = {
+ .start = _start,
+ .disconnect = _disconnect,
+ .hub_info = _hub_info,
+ .speed = _speed,
+ .complete = _complete,
+ .get_b_hnp_enable = _get_b_hnp_enable,
+};
+
+
+/*
+ * Controller Port Suspend Routiene.
+ */
+void dwc_otg_host_port_suspend(struct usb_hcd *hcd)
+{
+ dwc_otg_host_if_t host_if;
+ hprt0_data_t hprt;
+
+ host_if.hprt0 = hcd->regs + DWC_OTG_HOST_PORT_REGS_OFFSET;
+
+ hprt.d32= DWC_READ_REG32(host_if.hprt0);
+ hprt.b.prtsusp = 1;
+ DWC_WRITE_REG32(host_if.hprt0, hprt.d32);
+}
+
+
+/*
+ * Controller Port Resume Routiene.
+ */
+void dwc_otg_host_port_resume(struct usb_hcd *hcd)
+{
+ dwc_otg_host_if_t host_if;
+ hprt0_data_t hprt;
+
+ host_if.hprt0 = hcd->regs + DWC_OTG_HOST_PORT_REGS_OFFSET;
+
+ hprt.d32= DWC_READ_REG32(host_if.hprt0);
+ hprt.b.prtsusp = 0;
+ DWC_WRITE_REG32(host_if.hprt0, hprt.d32);
+}
+
+
+/**
+ * Initializes the HCD. This function allocates memory for and initializes the
+ * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the
+ * USB bus with the core and calls the hc_driver->start() function. It returns
+ * a negative error on failure.
+ */
+int hcd_init(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#else
+ struct platform_device *_dev
+#endif
+ )
+{
+ struct usb_hcd *hcd = NULL;
+ dwc_otg_hcd_t *dwc_otg_hcd = NULL;
+#ifdef LM_INTERFACE
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
+#endif
+
+ int retval = 0;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT\n");
+
+ /* Set device flags indicating whether the HCD supports DMA. */
+ if (dwc_otg_is_dma_enable(otg_dev->core_if)) {
+#ifdef LM_INTERFACE
+ _dev->dev.dma_mask = (void *)~0;
+ _dev->dev.coherent_dma_mask = ~0;
+#elif defined(PCI_INTERFACE)
+ pci_set_dma_mask(_dev, DMA_32BIT_MASK);
+ pci_set_consistent_dma_mask(_dev, DMA_32BIT_MASK);
+#endif
+
+ } else {
+#ifdef LM_INTERFACE
+ _dev->dev.dma_mask = (void *)0;
+ _dev->dev.coherent_dma_mask = 0;
+#elif defined(PCI_INTERFACE)
+ pci_set_dma_mask(_dev, 0);
+ pci_set_consistent_dma_mask(_dev, 0);
+#endif
+ }
+
+ /*
+ * Allocate memory for the base HCD plus the DWC OTG HCD.
+ * Initialize the base HCD.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+ hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, _dev->dev.bus_id);
+#else
+ hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, dev_name(&_dev->dev));
+ hcd->has_tt = 1;
+// hcd->uses_new_polling = 1;
+// hcd->poll_rh = 0;
+#endif
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto error1;
+ }
+
+ hcd->regs = otg_dev->os_dep.base;
+
+ /* Initialize the DWC OTG HCD. */
+ dwc_otg_hcd = dwc_otg_hcd_alloc_hcd();
+ if (!dwc_otg_hcd) {
+ goto error2;
+ }
+ ((struct wrapper_priv_data *)(hcd->hcd_priv))->dwc_otg_hcd =
+ dwc_otg_hcd;
+ otg_dev->hcd = dwc_otg_hcd;
+
+ if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if)) {
+ goto error2;
+ }
+
+ otg_dev->hcd->otg_dev = otg_dev;
+ hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel)
+ /* Don't support SG list at this point */
+ hcd->self.sg_tablesize = 0;
+#endif
+ /*
+ * Finish generic HCD initialization and start the HCD. This function
+ * allocates the DMA buffer pool, registers the USB bus, requests the
+ * IRQ line, and calls hcd_start method.
+ */
+ retval = usb_add_hcd(hcd, otg_dev->irq, IRQF_SHARED | IRQF_DISABLED);
+ if (retval < 0) {
+ goto error2;
+ }
+
+ dwc_otg_hcd_set_priv_data(dwc_otg_hcd, hcd);
+ return 0;
+
+error2:
+ usb_put_hcd(hcd);
+error1:
+ return retval;
+}
+
+/**
+ * Removes the HCD.
+ * Frees memory and resources associated with the HCD and deregisters the bus.
+ */
+void hcd_remove(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#else
+ struct platform_device *_dev
+#endif
+ )
+{
+#ifdef LM_INTERFACE
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev);
+#else
+ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
+#endif
+
+ dwc_otg_hcd_t *dwc_otg_hcd;
+ struct usb_hcd *hcd;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE\n");
+
+ if (!otg_dev) {
+ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__);
+ return;
+ }
+
+ dwc_otg_hcd = otg_dev->hcd;
+
+ if (!dwc_otg_hcd) {
+ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__);
+ return;
+ }
+
+ hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd);
+
+ if (!hcd) {
+ DWC_DEBUGPL(DBG_ANY,
+ "%s: dwc_otg_hcd_to_hcd(dwc_otg_hcd) NULL!\n",
+ __func__);
+ return;
+ }
+ usb_remove_hcd(hcd);
+ dwc_otg_hcd_set_priv_data(dwc_otg_hcd, NULL);
+ dwc_otg_hcd_remove(dwc_otg_hcd);
+ usb_put_hcd(hcd);
+}
+
+/* =========================================================================
+ * Linux HC Driver Functions
+ * ========================================================================= */
+
+/** Initializes the DWC_otg controller and its root hub and prepares it for host
+ * mode operation. Activates the root port. Returns 0 on success and a negative
+ * error code on failure. */
+int hcd_start(struct usb_hcd *hcd)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+ struct usb_bus *bus;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n");
+ bus = hcd_to_bus(hcd);
+
+ hcd->state = HC_STATE_RUNNING;
+ if (dwc_otg_hcd_start(dwc_otg_hcd, &hcd_fops)) {
+ return 0;
+ }
+
+ /* Initialize and connect root hub if one is not already attached */
+ if (bus->root_hub) {
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n");
+ /* Inform the HUB driver to resume. */
+ usb_hcd_resume_root_hub(hcd);
+ }
+
+ return 0;
+}
+
+/**
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ */
+void hcd_stop(struct usb_hcd *hcd)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+ dwc_otg_hcd_stop(dwc_otg_hcd);
+}
+
+/** Returns the current frame number. */
+static int get_frame_number(struct usb_hcd *hcd)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd);
+}
+
+#ifdef DEBUG
+static void dump_urb_info(struct urb *urb, char *fn_name)
+{
+ DWC_PRINTF("%s, urb %p\n", fn_name, urb);
+ DWC_PRINTF(" Device address: %d\n", usb_pipedevice(urb->pipe));
+ DWC_PRINTF(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe),
+ (usb_pipein(urb->pipe) ? "IN" : "OUT"));
+ DWC_PRINTF(" Endpoint type: %s\n", ( {
+ char *pipetype;
+ switch (usb_pipetype(urb->pipe)) {
+case PIPE_CONTROL:
+pipetype = "CONTROL"; break; case PIPE_BULK:
+pipetype = "BULK"; break; case PIPE_INTERRUPT:
+pipetype = "INTERRUPT"; break; case PIPE_ISOCHRONOUS:
+pipetype = "ISOCHRONOUS"; break; default:
+ pipetype = "UNKNOWN"; break;};
+ pipetype;}
+ )) ;
+ DWC_PRINTF(" Speed: %s\n", ( {
+ char *speed; switch (urb->dev->speed) {
+case USB_SPEED_HIGH:
+speed = "HIGH"; break; case USB_SPEED_FULL:
+speed = "FULL"; break; case USB_SPEED_LOW:
+speed = "LOW"; break; default:
+ speed = "UNKNOWN"; break;};
+ speed;}
+ )) ;
+ DWC_PRINTF(" Max packet size: %d\n",
+ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)));
+ DWC_PRINTF(" Data buffer length: %d\n", urb->transfer_buffer_length);
+ DWC_PRINTF(" Transfer buffer: %p, Transfer DMA: %p\n",
+ urb->transfer_buffer, (void *)urb->transfer_dma);
+ DWC_PRINTF(" Setup buffer: %p, Setup DMA: %p\n",
+ urb->setup_packet, (void *)urb->setup_dma);
+ DWC_PRINTF(" Interval: %d\n", urb->interval);
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ int i;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ DWC_PRINTF(" ISO Desc %d:\n", i);
+ DWC_PRINTF(" offset: %d, length %d\n",
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].length);
+ }
+ }
+}
+
+#endif
+
+/** Starts processing a USB transfer request specified by a USB Request Block
+ * (URB). mem_flags indicates the type of memory allocation to use while
+ * processing this URB. */
+static int urb_enqueue(struct usb_hcd *hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ struct usb_host_endpoint *ep,
+#endif
+ struct urb *urb, gfp_t mem_flags)
+{
+ int retval = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
+ struct usb_host_endpoint *ep = urb->ep;
+#endif
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+ dwc_otg_hcd_urb_t *dwc_otg_urb;
+ int i;
+ int alloc_bandwidth = 0;
+ uint8_t ep_type = 0;
+ uint32_t flags = 0;
+ void *buf;
+
+#ifdef DEBUG
+ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+ dump_urb_info(urb, "urb_enqueue");
+ }
+#endif
+
+ if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+ || (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+ if (!dwc_otg_hcd_is_bandwidth_allocated
+ (dwc_otg_hcd, &ep->hcpriv)) {
+ alloc_bandwidth = 1;
+ }
+ }
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ ep_type = USB_ENDPOINT_XFER_CONTROL;
+ break;
+ case PIPE_ISOCHRONOUS:
+ ep_type = USB_ENDPOINT_XFER_ISOC;
+ break;
+ case PIPE_BULK:
+ ep_type = USB_ENDPOINT_XFER_BULK;
+ break;
+ case PIPE_INTERRUPT:
+ ep_type = USB_ENDPOINT_XFER_INT;
+ break;
+ default:
+ DWC_WARN("Wrong ep type\n");
+ }
+
+ dwc_otg_urb = dwc_otg_hcd_urb_alloc(dwc_otg_hcd,
+ urb->number_of_packets,
+ mem_flags == GFP_ATOMIC ? 1 : 0);
+
+ dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe), ep_type,
+ usb_pipein(urb->pipe),
+ usb_maxpacket(urb->dev, urb->pipe,
+ !(usb_pipein(urb->pipe))));
+
+ buf = urb->transfer_buffer;
+ if (hcd->self.uses_dma) {
+ /*
+ * Calculate virtual address from physical address,
+ * because some class driver may not fill transfer_buffer.
+ * In Buffer DMA mode virual address is used,
+ * when handling non DWORD aligned buffers.
+ */
+ buf = phys_to_virt(urb->transfer_dma);
+ }
+
+ if (!(urb->transfer_flags & URB_NO_INTERRUPT))
+ flags |= URB_GIVEBACK_ASAP;
+ if (urb->transfer_flags & URB_ZERO_PACKET)
+ flags |= URB_SEND_ZERO_PACKET;
+
+ dwc_otg_hcd_urb_set_params(dwc_otg_urb, urb, buf,
+ urb->transfer_dma,
+ urb->transfer_buffer_length,
+ urb->setup_packet,
+ urb->setup_dma, flags, urb->interval);
+
+ for (i = 0; i < urb->number_of_packets; ++i) {
+ dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_urb, i,
+ urb->
+ iso_frame_desc[i].offset,
+ urb->
+ iso_frame_desc[i].length);
+ }
+
+ urb->hcpriv = dwc_otg_urb;
+ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, &ep->hcpriv,
+ mem_flags == GFP_ATOMIC ? 1 : 0);
+ if (!retval) {
+ if (alloc_bandwidth) {
+ allocate_bus_bandwidth(hcd,
+ dwc_otg_hcd_get_ep_bandwidth
+ (dwc_otg_hcd, ep->hcpriv), urb);
+ }
+ } else {
+ if (retval == -DWC_E_NO_DEVICE) {
+ retval = -ENODEV;
+ }
+ }
+
+ return retval;
+}
+
+/** Aborts/cancels a USB transfer request. Always returns 0 to indicate
+ * success. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+#else
+static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+#endif
+{
+ dwc_irqflags_t flags;
+ dwc_otg_hcd_t *dwc_otg_hcd;
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n");
+
+ dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+#ifdef DEBUG
+ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+ dump_urb_info(urb, "urb_dequeue");
+ }
+#endif
+
+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
+
+ dwc_otg_hcd_urb_dequeue(dwc_otg_hcd, urb->hcpriv);
+
+ DWC_FREE(urb->hcpriv);
+ urb->hcpriv = NULL;
+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
+
+ /* Higher layer software sets URB status. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ usb_hcd_giveback_urb(hcd, urb);
+#else
+ usb_hcd_giveback_urb(hcd, urb, status);
+#endif
+ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+ DWC_PRINTF("Called usb_hcd_giveback_urb()\n");
+ DWC_PRINTF(" urb->status = %d\n", urb->status);
+ }
+
+ return 0;
+}
+
+/* Frees resources in the DWC_otg controller related to a given endpoint. Also
+ * clears state in the HCD related to the endpoint. Any URBs for the endpoint
+ * must already be dequeued. */
+static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+ DWC_DEBUGPL(DBG_HCD,
+ "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, "
+ "endpoint=%d\n", ep->desc.bEndpointAddress,
+ dwc_ep_addr_to_endpoint(ep->desc.bEndpointAddress));
+ dwc_otg_hcd_endpoint_disable(dwc_otg_hcd, ep->hcpriv, 250);
+ ep->hcpriv = NULL;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+/* Resets endpoint specific parameter values, in current version used to reset
+ * the data toggle(as a WA). This function can be called from usb_clear_halt routine */
+static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ dwc_irqflags_t flags;
+ struct usb_device *udev = NULL;
+ int epnum = usb_endpoint_num(&ep->desc);
+ int is_out = usb_endpoint_dir_out(&ep->desc);
+ int is_control = usb_endpoint_xfer_control(&ep->desc);
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+#ifdef LM_INTERFACE
+ struct lm_device *_dev = dwc_otg_hcd->otg_dev->os_dep.lmdev;
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev = dwc_otg_hcd->otg_dev->os_dep.pcidev;
+#else
+ struct device *_dev = dwc_otg_hcd->otg_dev->os_dep.parent;
+#endif
+
+ if (_dev)
+ udev = to_usb_device(_dev);
+ else
+ return;
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP RESET: Endpoint Num=0x%02d\n", epnum);
+
+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
+ usb_settoggle(udev, epnum, is_out, 0);
+ if (is_control)
+ usb_settoggle(udev, epnum, !is_out, 0);
+
+ if (ep->hcpriv) {
+ dwc_otg_hcd_endpoint_reset(dwc_otg_hcd, ep->hcpriv);
+ }
+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
+}
+#endif
+
+/** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if
+ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid
+ * interrupt.
+ *
+ * This function is called by the USB core when an interrupt occurs */
+static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+ int32_t retval = dwc_otg_hcd_handle_intr(dwc_otg_hcd);
+ if (retval != 0) {
+ S3C2410X_CLEAR_EINTPEND();
+ }
+ return IRQ_RETVAL(retval);
+}
+
+/** Creates Status Change bitmap for the root hub and root port. The bitmap is
+ * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1
+ * is the status change indicator for the single root port. Returns 1 if either
+ * change indicator is 1, otherwise returns 0. */
+int hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+ buf[0] = 0;
+ buf[0] |= (dwc_otg_hcd_is_status_changed(dwc_otg_hcd, 1)) << 1;
+
+ return (buf[0] != 0);
+}
+
+/** Handles hub class-specific requests. */
+int hub_control(struct usb_hcd *hcd,
+ u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength)
+{
+ int retval;
+
+ retval = dwc_otg_hcd_hub_control(hcd_to_dwc_otg_hcd(hcd),
+ typeReq, wValue, wIndex, buf, wLength);
+
+ switch (retval) {
+ case -DWC_E_INVALID:
+ retval = -EINVAL;
+ break;
+ }
+
+ return retval;
+}
+
+#endif /* DWC_DEVICE_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c
new file mode 100644
index 0000000..903a902
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c
@@ -0,0 +1,726 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_queue.c $
+ * $Revision: #44 $
+ * $Date: 2011/10/26 $
+ * $Change: 1873028 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+/**
+ * @file
+ *
+ * This file contains the functions to manage Queue Heads and Queue
+ * Transfer Descriptors.
+ */
+
+#include "dwc_otg_hcd.h"
+#include "dwc_otg_regs.h"
+
+/**
+ * Free each QTD in the QH's QTD-list then free the QH. QH should already be
+ * removed from a list. QTD list should already be empty if called from URB
+ * Dequeue.
+ *
+ * @param hcd HCD instance.
+ * @param qh The QH to free.
+ */
+void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ dwc_otg_qtd_t *qtd, *qtd_tmp;
+
+ /* Free each QTD in the QTD list */
+ DWC_SPINLOCK(hcd->lock);
+ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
+ DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry);
+ dwc_otg_hcd_qtd_free(qtd);
+ }
+
+ if (hcd->core_if->dma_desc_enable) {
+ dwc_otg_hcd_qh_free_ddma(hcd, qh);
+ } else if (qh->dw_align_buf) {
+ uint32_t buf_size;
+ if (qh->ep_type == UE_ISOCHRONOUS) {
+ buf_size = 4096;
+ } else {
+ buf_size = hcd->core_if->core_params->max_transfer_size;
+ }
+ DWC_DMA_FREE(buf_size, qh->dw_align_buf, qh->dw_align_buf_dma);
+ }
+
+ DWC_FREE(qh);
+ DWC_SPINUNLOCK(hcd->lock);
+ return;
+}
+
+#define BitStuffTime(bytecount) ((8 * 7* bytecount) / 6)
+#define HS_HOST_DELAY 5 /* nanoseconds */
+#define FS_LS_HOST_DELAY 1000 /* nanoseconds */
+#define HUB_LS_SETUP 333 /* nanoseconds */
+#define NS_TO_US(ns) ((ns + 500) / 1000)
+ /* convert & round nanoseconds to microseconds */
+
+static uint32_t calc_bus_time(int speed, int is_in, int is_isoc, int bytecount)
+{
+ unsigned long retval;
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ if (is_isoc) {
+ retval =
+ ((38 * 8 * 2083) +
+ (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
+ HS_HOST_DELAY;
+ } else {
+ retval =
+ ((55 * 8 * 2083) +
+ (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
+ HS_HOST_DELAY;
+ }
+ break;
+ case USB_SPEED_FULL:
+ if (is_isoc) {
+ retval =
+ (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
+ if (is_in) {
+ retval = 7268 + FS_LS_HOST_DELAY + retval;
+ } else {
+ retval = 6265 + FS_LS_HOST_DELAY + retval;
+ }
+ } else {
+ retval =
+ (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
+ retval = 9107 + FS_LS_HOST_DELAY + retval;
+ }
+ break;
+ case USB_SPEED_LOW:
+ if (is_in) {
+ retval =
+ (67667 * (31 + 10 * BitStuffTime(bytecount))) /
+ 1000;
+ retval =
+ 64060 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
+ retval;
+ } else {
+ retval =
+ (66700 * (31 + 10 * BitStuffTime(bytecount))) /
+ 1000;
+ retval =
+ 64107 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
+ retval;
+ }
+ break;
+ default:
+ DWC_WARN("Unknown device speed\n");
+ retval = -1;
+ }
+
+ return NS_TO_US(retval);
+}
+
+/**
+ * Initializes a QH structure.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh The QH to init.
+ * @param urb Holds the information about the device/endpoint that we need
+ * to initialize the QH.
+ */
+#define SCHEDULE_SLOP 10
+void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
+{
+ char *speed, *type;
+ int dev_speed;
+ uint32_t hub_addr, hub_port;
+
+ dwc_memset(qh, 0, sizeof(dwc_otg_qh_t));
+
+ /* Initialize QH */
+ qh->ep_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
+ qh->ep_is_in = dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
+
+ qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+ qh->maxp = dwc_otg_hcd_get_mps(&urb->pipe_info);
+ DWC_CIRCLEQ_INIT(&qh->qtd_list);
+ DWC_LIST_INIT(&qh->qh_list_entry);
+ qh->channel = NULL;
+
+ /* FS/LS Enpoint on HS Hub
+ * NOT virtual root hub */
+ dev_speed = hcd->fops->speed(hcd, urb->priv);
+
+ hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &hub_port);
+ qh->do_split = 0;
+
+ if (((dev_speed == USB_SPEED_LOW) ||
+ (dev_speed == USB_SPEED_FULL)) &&
+ (hub_addr != 0 && hub_addr != 1)) {
+ DWC_DEBUGPL(DBG_HCD,
+ "QH init: EP %d: TT found at hub addr %d, for port %d\n",
+ dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr,
+ hub_port);
+ qh->do_split = 1;
+ }
+
+ if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) {
+ /* Compute scheduling parameters once and save them. */
+ hprt0_data_t hprt;
+
+ /** @todo Account for split transfers in the bus time. */
+ int bytecount =
+ dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp);
+
+ qh->usecs =
+ calc_bus_time((qh->do_split ? USB_SPEED_HIGH : dev_speed),
+ qh->ep_is_in, (qh->ep_type == UE_ISOCHRONOUS),
+ bytecount);
+ /* Start in a slightly future (micro)frame. */
+ qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
+ SCHEDULE_SLOP);
+ qh->interval = urb->interval;
+
+#if 0
+ /* Increase interrupt polling rate for debugging. */
+ if (qh->ep_type == UE_INTERRUPT) {
+ qh->interval = 8;
+ }
+#endif
+ hprt.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0);
+ if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) &&
+ ((dev_speed == USB_SPEED_LOW) ||
+ (dev_speed == USB_SPEED_FULL))) {
+ qh->interval *= 8;
+ qh->sched_frame |= 0x7;
+ qh->start_split_frame = qh->sched_frame;
+ }
+
+ }
+
+ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n");
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", qh);
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n",
+ dwc_otg_hcd_get_dev_addr(&urb->pipe_info));
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n",
+ dwc_otg_hcd_get_ep_num(&urb->pipe_info),
+ dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
+ switch (dev_speed) {
+ case USB_SPEED_LOW:
+ qh->dev_speed = DWC_OTG_EP_SPEED_LOW;
+ speed = "low";
+ break;
+ case USB_SPEED_FULL:
+ qh->dev_speed = DWC_OTG_EP_SPEED_FULL;
+ speed = "full";
+ break;
+ case USB_SPEED_HIGH:
+ qh->dev_speed = DWC_OTG_EP_SPEED_HIGH;
+ speed = "high";
+ break;
+ default:
+ speed = "?";
+ break;
+ }
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", speed);
+
+ switch (qh->ep_type) {
+ case UE_ISOCHRONOUS:
+ type = "isochronous";
+ break;
+ case UE_INTERRUPT:
+ type = "interrupt";
+ break;
+ case UE_CONTROL:
+ type = "control";
+ break;
+ case UE_BULK:
+ type = "bulk";
+ break;
+ default:
+ type = "?";
+ break;
+ }
+
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n", type);
+
+#ifdef DEBUG
+ if (qh->ep_type == UE_INTERRUPT) {
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n",
+ qh->usecs);
+ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n",
+ qh->interval);
+ }
+#endif
+
+}
+
+/**
+ * This function allocates and initializes a QH.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param urb Holds the information about the device/endpoint that we need
+ * to initialize the QH.
+ * @param atomic_alloc Flag to do atomic allocation if needed
+ *
+ * @return Returns pointer to the newly allocated QH, or NULL on error. */
+dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd,
+ dwc_otg_hcd_urb_t * urb, int atomic_alloc)
+{
+ dwc_otg_qh_t *qh;
+
+ /* Allocate memory */
+ /** @todo add memflags argument */
+ qh = dwc_otg_hcd_qh_alloc(atomic_alloc);
+ if (qh == NULL) {
+ DWC_ERROR("qh allocation failed");
+ return NULL;
+ }
+
+ qh_init(hcd, qh, urb);
+
+ if (hcd->core_if->dma_desc_enable
+ && (dwc_otg_hcd_qh_init_ddma(hcd, qh) < 0)) {
+ dwc_otg_hcd_qh_free(hcd, qh);
+ return NULL;
+ }
+
+ return qh;
+}
+
+/**
+ * Checks that a channel is available for a periodic transfer.
+ *
+ * @return 0 if successful, negative error code otherise.
+ */
+static int periodic_channel_available(dwc_otg_hcd_t * hcd)
+{
+ /*
+ * Currently assuming that there is a dedicated host channnel for each
+ * periodic transaction plus at least one host channel for
+ * non-periodic transactions.
+ */
+ int status;
+ int num_channels;
+
+ num_channels = hcd->core_if->core_params->host_channels;
+ if ((hcd->periodic_channels + hcd->non_periodic_channels < num_channels)
+ && (hcd->periodic_channels < num_channels - 1)) {
+ status = 0;
+ } else {
+ DWC_INFO("%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n",
+ __func__, num_channels, hcd->periodic_channels, hcd->non_periodic_channels); //NOTICE
+ status = -DWC_E_NO_SPACE;
+ }
+
+ return status;
+}
+
+/**
+ * Checks that there is sufficient bandwidth for the specified QH in the
+ * periodic schedule. For simplicity, this calculation assumes that all the
+ * transfers in the periodic schedule may occur in the same (micro)frame.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh QH containing periodic bandwidth required.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+static int check_periodic_bandwidth(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ int status;
+ int16_t max_claimed_usecs;
+
+ status = 0;
+
+ if ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) || qh->do_split) {
+ /*
+ * High speed mode.
+ * Max periodic usecs is 80% x 125 usec = 100 usec.
+ */
+
+ max_claimed_usecs = 100 - qh->usecs;
+ } else {
+ /*
+ * Full speed mode.
+ * Max periodic usecs is 90% x 1000 usec = 900 usec.
+ */
+ max_claimed_usecs = 900 - qh->usecs;
+ }
+
+ if (hcd->periodic_usecs > max_claimed_usecs) {
+ DWC_INFO("%s: already claimed usecs %d, required usecs %d\n", __func__, hcd->periodic_usecs, qh->usecs); //NOTICE
+ status = -DWC_E_NO_SPACE;
+ }
+
+ return status;
+}
+
+/**
+ * Checks that the max transfer size allowed in a host channel is large enough
+ * to handle the maximum data transfer in a single (micro)frame for a periodic
+ * transfer.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh QH for a periodic endpoint.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ int status;
+ uint32_t max_xfer_size;
+ uint32_t max_channel_xfer_size;
+
+ status = 0;
+
+ max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp);
+ max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size;
+
+ if (max_xfer_size > max_channel_xfer_size) {
+ DWC_INFO("%s: Periodic xfer length %d > " "max xfer length for channel %d\n",
+ __func__, max_xfer_size, max_channel_xfer_size); //NOTICE
+ status = -DWC_E_NO_SPACE;
+ }
+
+ return status;
+}
+
+/**
+ * Schedules an interrupt or isochronous transfer in the periodic schedule.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh QH for the periodic transfer. The QH should already contain the
+ * scheduling information.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ int status = 0;
+
+ status = periodic_channel_available(hcd);
+ if (status) {
+ DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__); //NOTICE
+ return status;
+ }
+
+ status = check_periodic_bandwidth(hcd, qh);
+ if (status) {
+ DWC_INFO("%s: Insufficient periodic bandwidth for " "periodic transfer.\n", __func__); //NOTICE
+ return status;
+ }
+
+ status = check_max_xfer_size(hcd, qh);
+ if (status) {
+ DWC_INFO("%s: Channel max transfer size too small " "for periodic transfer.\n", __func__); //NOTICE
+ return status;
+ }
+
+ if (hcd->core_if->dma_desc_enable) {
+ /* Don't rely on SOF and start in ready schedule */
+ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry);
+ }
+ else {
+ /* Always start in the inactive schedule. */
+ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
+ }
+
+ /* Reserve the periodic channel. */
+ hcd->periodic_channels++;
+
+ /* Update claimed usecs per (micro)frame. */
+ hcd->periodic_usecs += qh->usecs;
+
+ return status;
+}
+
+/**
+ * This function adds a QH to either the non periodic or periodic schedule if
+ * it is not already in the schedule. If the QH is already in the schedule, no
+ * action is taken.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ int status = 0;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) {
+ /* QH already in a schedule. */
+ return status;
+ }
+
+ /* Add the new QH to the appropriate schedule */
+ if (dwc_qh_is_non_per(qh)) {
+ /* Always start in the inactive schedule. */
+ DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive,
+ &qh->qh_list_entry);
+ } else {
+ status = schedule_periodic(hcd, qh);
+ if ( !hcd->periodic_qh_count ) {
+ intr_mask.b.sofintr = 1;
+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk,
+ intr_mask.d32, intr_mask.d32);
+ }
+ hcd->periodic_qh_count++;
+ }
+
+ return status;
+}
+
+/**
+ * Removes an interrupt or isochronous transfer from the periodic schedule.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh QH for the periodic transfer.
+ */
+static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
+
+ /* Release the periodic channel reservation. */
+ hcd->periodic_channels--;
+
+ /* Update claimed usecs per (micro)frame. */
+ hcd->periodic_usecs -= qh->usecs;
+}
+
+/**
+ * Removes a QH from either the non-periodic or periodic schedule. Memory is
+ * not freed.
+ *
+ * @param hcd The HCD state structure.
+ * @param qh QH to remove from schedule. */
+void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ if (DWC_LIST_EMPTY(&qh->qh_list_entry)) {
+ /* QH is not in a schedule. */
+ return;
+ }
+
+ if (dwc_qh_is_non_per(qh)) {
+ if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry) {
+ hcd->non_periodic_qh_ptr =
+ hcd->non_periodic_qh_ptr->next;
+ }
+ DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
+ } else {
+ deschedule_periodic(hcd, qh);
+ hcd->periodic_qh_count--;
+ if( !hcd->periodic_qh_count ) {
+ intr_mask.b.sofintr = 1;
+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+ }
+ }
+}
+
+/**
+ * Deactivates a QH. For non-periodic QHs, removes the QH from the active
+ * non-periodic schedule. The QH is added to the inactive non-periodic
+ * schedule if any QTDs are still attached to the QH.
+ *
+ * For periodic QHs, the QH is removed from the periodic queued schedule. If
+ * there are any QTDs still attached to the QH, the QH is added to either the
+ * periodic inactive schedule or the periodic ready schedule and its next
+ * scheduled frame is calculated. The QH is placed in the ready schedule if
+ * the scheduled frame has been reached already. Otherwise it's placed in the
+ * inactive schedule. If there are no QTDs attached to the QH, the QH is
+ * completely removed from the periodic schedule.
+ */
+void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+ int sched_next_periodic_split)
+{
+ if (dwc_qh_is_non_per(qh)) {
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+ /* Add back to inactive non-periodic schedule. */
+ dwc_otg_hcd_qh_add(hcd, qh);
+ }
+ } else {
+ uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd);
+
+ if (qh->do_split) {
+ /* Schedule the next continuing periodic split transfer */
+ if (sched_next_periodic_split) {
+
+ qh->sched_frame = frame_number;
+ if (dwc_frame_num_le(frame_number,
+ dwc_frame_num_inc
+ (qh->start_split_frame,
+ 1))) {
+ /*
+ * Allow one frame to elapse after start
+ * split microframe before scheduling
+ * complete split, but DONT if we are
+ * doing the next start split in the
+ * same frame for an ISOC out.
+ */
+ if ((qh->ep_type != UE_ISOCHRONOUS) ||
+ (qh->ep_is_in != 0)) {
+ qh->sched_frame =
+ dwc_frame_num_inc(qh->sched_frame, 1);
+ }
+ }
+ } else {
+ qh->sched_frame =
+ dwc_frame_num_inc(qh->start_split_frame,
+ qh->interval);
+ if (dwc_frame_num_le
+ (qh->sched_frame, frame_number)) {
+ qh->sched_frame = frame_number;
+ }
+ qh->sched_frame |= 0x7;
+ qh->start_split_frame = qh->sched_frame;
+ }
+ } else {
+ qh->sched_frame =
+ dwc_frame_num_inc(qh->sched_frame, qh->interval);
+ if (dwc_frame_num_le(qh->sched_frame, frame_number)) {
+ qh->sched_frame = frame_number;
+ }
+ }
+
+ if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+ dwc_otg_hcd_qh_remove(hcd, qh);
+ } else {
+ /*
+ * Remove from periodic_sched_queued and move to
+ * appropriate queue.
+ */
+ if (qh->sched_frame == frame_number) {
+ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
+ &qh->qh_list_entry);
+ } else {
+ DWC_LIST_MOVE_HEAD
+ (&hcd->periodic_sched_inactive,
+ &qh->qh_list_entry);
+ }
+ }
+ }
+}
+
+/**
+ * This function allocates and initializes a QTD.
+ *
+ * @param urb The URB to create a QTD from. Each URB-QTD pair will end up
+ * pointing to each other so each pair should have a unique correlation.
+ * @param atomic_alloc Flag to do atomic alloc if needed
+ *
+ * @return Returns pointer to the newly allocated QTD, or NULL on error. */
+dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb, int atomic_alloc)
+{
+ dwc_otg_qtd_t *qtd;
+
+ qtd = dwc_otg_hcd_qtd_alloc(atomic_alloc);
+ if (qtd == NULL) {
+ return NULL;
+ }
+
+ dwc_otg_hcd_qtd_init(qtd, urb);
+ return qtd;
+}
+
+/**
+ * Initializes a QTD structure.
+ *
+ * @param qtd The QTD to initialize.
+ * @param urb The URB to use for initialization. */
+void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb)
+{
+ dwc_memset(qtd, 0, sizeof(dwc_otg_qtd_t));
+ qtd->urb = urb;
+ if (dwc_otg_hcd_get_pipe_type(&urb->pipe_info) == UE_CONTROL) {
+ /*
+ * The only time the QTD data toggle is used is on the data
+ * phase of control transfers. This phase always starts with
+ * DATA1.
+ */
+ qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
+ qtd->control_phase = DWC_OTG_CONTROL_SETUP;
+ }
+
+ /* start split */
+ qtd->complete_split = 0;
+ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
+ qtd->isoc_split_offset = 0;
+ qtd->in_process = 0;
+
+ /* Store the qtd ptr in the urb to reference what QTD. */
+ urb->qtd = qtd;
+ return;
+}
+
+/**
+ * This function adds a QTD to the QTD-list of a QH. It will find the correct
+ * QH to place the QTD into. If it does not find a QH, then it will create a
+ * new QH. If the QH to which the QTD is added is not currently scheduled, it
+ * is placed into the proper schedule based on its EP type.
+ *
+ * @param[in] qtd The QTD to add
+ * @param[in] hcd The DWC HCD structure
+ * @param[out] qh out parameter to return queue head
+ * @param atomic_alloc Flag to do atomic alloc if needed
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd,
+ dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc)
+{
+ int retval = 0;
+ dwc_irqflags_t flags;
+
+ dwc_otg_hcd_urb_t *urb = qtd->urb;
+
+ /*
+ * Get the QH which holds the QTD-list to insert to. Create QH if it
+ * doesn't exist.
+ */
+ if (*qh == NULL) {
+ *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc);
+ if (*qh == NULL) {
+ retval = -1;
+ goto done;
+ }
+ }
+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+ retval = dwc_otg_hcd_qh_add(hcd, *qh);
+ if (retval == 0) {
+ DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd,
+ qtd_list_entry);
+ }
+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+
+done:
+
+ return retval;
+}
+
+#endif /* DWC_DEVICE_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_os_dep.h b/drivers/usb/dwc_otg/dwc_otg_os_dep.h
new file mode 100644
index 0000000..1f8db73
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_os_dep.h
@@ -0,0 +1,111 @@
+#ifndef _DWC_OS_DEP_H_
+#define _DWC_OS_DEP_H_
+
+/**
+ * @file
+ *
+ * This file contains OS dependent structures.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+# include <linux/irq.h>
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
+# include <linux/usb/ch9.h>
+#else
+# include <linux/usb_ch9.h>
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+# include <linux/usb/gadget.h>
+#else
+# include <linux/usb_gadget.h>
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+# include <asm/irq.h>
+#endif
+
+#ifdef PCI_INTERFACE
+# include <asm/io.h>
+#endif
+
+#ifdef LM_INTERFACE
+# include <asm/unaligned.h>
+# include <asm/sizes.h>
+# include <asm/param.h>
+# include <asm/io.h>
+# include <asm/arch/lm.h>
+# include <asm/arch/irqs.h>
+# include <asm/arch/regs-irq.h>
+#else
+# include <asm/unaligned.h>
+# include <asm/sizes.h>
+# include <asm/param.h>
+# include <asm/io.h>
+# include <mach/irqs.h>
+#endif
+
+/** The OS page size */
+#define DWC_OS_PAGE_SIZE PAGE_SIZE
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+typedef int gfp_t;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+# define IRQF_SHARED SA_SHIRQ
+#endif
+
+typedef struct os_dependent {
+ /** Base address returned from ioremap() */
+ void *base;
+
+ /** Register offset for Diagnostic API */
+ uint32_t reg_offset;
+
+#ifdef LM_INTERFACE
+ struct lm_device *lmdev;
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *pcidev;
+
+ /** Start address of a PCI region */
+ resource_size_t rsrc_start;
+
+ /** Length address of a PCI region */
+ resource_size_t rsrc_len;
+#else
+ struct device *parent;
+#endif
+} os_dependent_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_OS_DEP_H_ */
diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd.c b/drivers/usb/dwc_otg/dwc_otg_pcd.c
new file mode 100644
index 0000000..e6792e9
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_pcd.c
@@ -0,0 +1,2634 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.c $
+ * $Revision: #99 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871160 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+
+/** @file
+ * This file implements PCD Core. All code in this file is portable and doesn't
+ * use any OS specific functions.
+ * PCD Core provides Interface, defined in <code><dwc_otg_pcd_if.h></code>
+ * header file, which can be used to implement OS specific PCD interface.
+ *
+ * An important function of the PCD is managing interrupts generated
+ * by the DWC_otg controller. The implementation of the DWC_otg device
+ * mode interrupt service routines is in dwc_otg_pcd_intr.c.
+ *
+ * @todo Add Device Mode test modes (Test J mode, Test K mode, etc).
+ * @todo Does it work when the request size is greater than DEPTSIZ
+ * transfer size
+ *
+ */
+
+#include "dwc_otg_pcd.h"
+
+#ifdef DWC_UTE_CFI
+#include "dwc_otg_cfi.h"
+
+extern int init_cfi(cfiobject_t * cfiobj);
+#endif
+
+/**
+ * Choose endpoint from ep arrays using usb_ep structure.
+ */
+static dwc_otg_pcd_ep_t *get_ep_from_handle(dwc_otg_pcd_t * pcd, void *handle)
+{
+ int i;
+ if (pcd->ep0.priv == handle) {
+ return &pcd->ep0;
+ }
+ for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) {
+ if (pcd->in_ep[i].priv == handle)
+ return &pcd->in_ep[i];
+ if (pcd->out_ep[i].priv == handle)
+ return &pcd->out_ep[i];
+ }
+
+ return NULL;
+}
+
+/**
+ * This function completes a request. It call's the request call back.
+ */
+void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req,
+ int32_t status)
+{
+ unsigned stopped = ep->stopped;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(ep %p req %p)\n", __func__, ep, req);
+ DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry);
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+ /* spin_unlock/spin_lock now done in fops->complete() */
+ ep->pcd->fops->complete(ep->pcd, ep->priv, req->priv, status,
+ req->actual);
+
+ if (ep->pcd->request_pending > 0) {
+ --ep->pcd->request_pending;
+ }
+
+ ep->stopped = stopped;
+ DWC_FREE(req);
+}
+
+/**
+ * This function terminates all the requsts in the EP request queue.
+ */
+void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep)
+{
+ dwc_otg_pcd_request_t *req;
+
+ ep->stopped = 1;
+
+ /* called with irqs blocked?? */
+ while (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+ req = DWC_CIRCLEQ_FIRST(&ep->queue);
+ dwc_otg_request_done(ep, req, -DWC_E_SHUTDOWN);
+ }
+}
+
+void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd,
+ const struct dwc_otg_pcd_function_ops *fops)
+{
+ pcd->fops = fops;
+}
+
+/**
+ * PCD Callback function for initializing the PCD when switching to
+ * device mode.
+ *
+ * @param p void pointer to the <code>dwc_otg_pcd_t</code>
+ */
+static int32_t dwc_otg_pcd_start_cb(void *p)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+ /*
+ * Initialized the Core for Device mode.
+ */
+ if (dwc_otg_is_device_mode(core_if)) {
+ dwc_otg_core_dev_init(core_if);
+ /* Set core_if's lock pointer to the pcd->lock */
+ core_if->lock = pcd->lock;
+ }
+ return 1;
+}
+
+/** CFI-specific buffer allocation function for EP */
+#ifdef DWC_UTE_CFI
+uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr,
+ size_t buflen, int flags)
+{
+ dwc_otg_pcd_ep_t *ep;
+ ep = get_ep_from_handle(pcd, pep);
+ if (!ep) {
+ DWC_WARN("bad ep\n");
+ return -DWC_E_INVALID;
+ }
+
+ return pcd->cfi->ops.ep_alloc_buf(pcd->cfi, pcd, ep, addr, buflen,
+ flags);
+}
+#else
+uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr,
+ size_t buflen, int flags);
+#endif
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ *
+ * @param p void pointer to the <code>dwc_otg_pcd_t</code>
+ */
+static int32_t dwc_otg_pcd_resume_cb(void *p)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
+
+ if (pcd->fops->resume) {
+ pcd->fops->resume(pcd);
+ }
+
+ /* Stop the SRP timeout timer. */
+ if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS)
+ || (!GET_CORE_IF(pcd)->core_params->i2c_enable)) {
+ if (GET_CORE_IF(pcd)->srp_timer_started) {
+ GET_CORE_IF(pcd)->srp_timer_started = 0;
+ DWC_TIMER_CANCEL(GET_CORE_IF(pcd)->srp_timer);
+ }
+ }
+ return 1;
+}
+
+/**
+ * PCD Callback function for notifying the PCD device is suspended.
+ *
+ * @param p void pointer to the <code>dwc_otg_pcd_t</code>
+ */
+static int32_t dwc_otg_pcd_suspend_cb(void *p)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
+
+ if (pcd->fops->suspend) {
+ DWC_SPINUNLOCK(pcd->lock);
+ pcd->fops->suspend(pcd);
+ DWC_SPINLOCK(pcd->lock);
+ }
+
+ return 1;
+}
+
+/**
+ * PCD Callback function for stopping the PCD when switching to Host
+ * mode.
+ *
+ * @param p void pointer to the <code>dwc_otg_pcd_t</code>
+ */
+static int32_t dwc_otg_pcd_stop_cb(void *p)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
+ extern void dwc_otg_pcd_stop(dwc_otg_pcd_t * _pcd);
+
+ dwc_otg_pcd_stop(pcd);
+ return 1;
+}
+
+/**
+ * PCD Callback structure for handling mode switching.
+ */
+static dwc_otg_cil_callbacks_t pcd_callbacks = {
+ .start = dwc_otg_pcd_start_cb,
+ .stop = dwc_otg_pcd_stop_cb,
+ .suspend = dwc_otg_pcd_suspend_cb,
+ .resume_wakeup = dwc_otg_pcd_resume_cb,
+ .p = 0, /* Set at registration */
+};
+
+/**
+ * This function allocates a DMA Descriptor chain for the Endpoint
+ * buffer to be used for a transfer to/from the specified endpoint.
+ */
+dwc_otg_dev_dma_desc_t *dwc_otg_ep_alloc_desc_chain(dwc_dma_t * dma_desc_addr,
+ uint32_t count)
+{
+ return DWC_DMA_ALLOC_ATOMIC(count * sizeof(dwc_otg_dev_dma_desc_t),
+ dma_desc_addr);
+}
+
+/**
+ * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc.
+ */
+void dwc_otg_ep_free_desc_chain(dwc_otg_dev_dma_desc_t * desc_addr,
+ uint32_t dma_desc_addr, uint32_t count)
+{
+ DWC_DMA_FREE(count * sizeof(dwc_otg_dev_dma_desc_t), desc_addr,
+ dma_desc_addr);
+}
+
+#ifdef DWC_EN_ISOC
+
+/**
+ * This function initializes a descriptor chain for Isochronous transfer
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dwc_ep The EP to start the transfer on.
+ *
+ */
+void dwc_otg_iso_ep_start_ddma_transfer(dwc_otg_core_if_t * core_if,
+ dwc_ep_t * dwc_ep)
+{
+
+ dsts_data_t dsts = {.d32 = 0 };
+ depctl_data_t depctl = {.d32 = 0 };
+ volatile uint32_t *addr;
+ int i, j;
+ uint32_t len;
+
+ if (dwc_ep->is_in)
+ dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl / dwc_ep->bInterval;
+ else
+ dwc_ep->desc_cnt =
+ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm /
+ dwc_ep->bInterval;
+
+ /** Allocate descriptors for double buffering */
+ dwc_ep->iso_desc_addr =
+ dwc_otg_ep_alloc_desc_chain(&dwc_ep->iso_dma_desc_addr,
+ dwc_ep->desc_cnt * 2);
+ if (dwc_ep->desc_addr) {
+ DWC_WARN("%s, can't allocate DMA descriptor chain\n", __func__);
+ return;
+ }
+
+ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+ /** ISO OUT EP */
+ if (dwc_ep->is_in == 0) {
+ dev_dma_desc_sts_t sts = {.d32 = 0 };
+ dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr;
+ dma_addr_t dma_ad;
+ uint32_t data_per_desc;
+ dwc_otg_dev_out_ep_regs_t *out_regs =
+ core_if->dev_if->out_ep_regs[dwc_ep->num];
+ int offset;
+
+ addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl;
+ dma_ad = (dma_addr_t) DWC_READ_REG32(&(out_regs->doepdma));
+
+ /** Buffer 0 descriptors setup */
+ dma_ad = dwc_ep->dma_addr0;
+
+ sts.b_iso_out.bs = BS_HOST_READY;
+ sts.b_iso_out.rxsts = 0;
+ sts.b_iso_out.l = 0;
+ sts.b_iso_out.sp = 0;
+ sts.b_iso_out.ioc = 0;
+ sts.b_iso_out.pid = 0;
+ sts.b_iso_out.framenum = 0;
+
+ offset = 0;
+ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+ i += dwc_ep->pkt_per_frm) {
+
+ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
+ uint32_t len = (j + 1) * dwc_ep->maxpacket;
+ if (len > dwc_ep->data_per_frame)
+ data_per_desc =
+ dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket;
+ else
+ data_per_desc = dwc_ep->maxpacket;
+ len = data_per_desc % 4;
+ if (len)
+ data_per_desc += 4 - len;
+
+ sts.b_iso_out.rxbytes = data_per_desc;
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ offset += data_per_desc;
+ dma_desc++;
+ dma_ad += data_per_desc;
+ }
+ }
+
+ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
+ uint32_t len = (j + 1) * dwc_ep->maxpacket;
+ if (len > dwc_ep->data_per_frame)
+ data_per_desc =
+ dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket;
+ else
+ data_per_desc = dwc_ep->maxpacket;
+ len = data_per_desc % 4;
+ if (len)
+ data_per_desc += 4 - len;
+ sts.b_iso_out.rxbytes = data_per_desc;
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ offset += data_per_desc;
+ dma_desc++;
+ dma_ad += data_per_desc;
+ }
+
+ sts.b_iso_out.ioc = 1;
+ len = (j + 1) * dwc_ep->maxpacket;
+ if (len > dwc_ep->data_per_frame)
+ data_per_desc =
+ dwc_ep->data_per_frame - j * dwc_ep->maxpacket;
+ else
+ data_per_desc = dwc_ep->maxpacket;
+ len = data_per_desc % 4;
+ if (len)
+ data_per_desc += 4 - len;
+ sts.b_iso_out.rxbytes = data_per_desc;
+
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+ dma_desc++;
+
+ /** Buffer 1 descriptors setup */
+ sts.b_iso_out.ioc = 0;
+ dma_ad = dwc_ep->dma_addr1;
+
+ offset = 0;
+ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+ i += dwc_ep->pkt_per_frm) {
+ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
+ uint32_t len = (j + 1) * dwc_ep->maxpacket;
+ if (len > dwc_ep->data_per_frame)
+ data_per_desc =
+ dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket;
+ else
+ data_per_desc = dwc_ep->maxpacket;
+ len = data_per_desc % 4;
+ if (len)
+ data_per_desc += 4 - len;
+
+ data_per_desc =
+ sts.b_iso_out.rxbytes = data_per_desc;
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ offset += data_per_desc;
+ dma_desc++;
+ dma_ad += data_per_desc;
+ }
+ }
+ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
+ data_per_desc =
+ ((j + 1) * dwc_ep->maxpacket >
+ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+ data_per_desc +=
+ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+ sts.b_iso_out.rxbytes = data_per_desc;
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ offset += data_per_desc;
+ dma_desc++;
+ dma_ad += data_per_desc;
+ }
+
+ sts.b_iso_out.ioc = 1;
+ sts.b_iso_out.l = 1;
+ data_per_desc =
+ ((j + 1) * dwc_ep->maxpacket >
+ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+ data_per_desc +=
+ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+ sts.b_iso_out.rxbytes = data_per_desc;
+
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ dwc_ep->next_frame = 0;
+
+ /** Write dma_ad into DOEPDMA register */
+ DWC_WRITE_REG32(&(out_regs->doepdma),
+ (uint32_t) dwc_ep->iso_dma_desc_addr);
+
+ }
+ /** ISO IN EP */
+ else {
+ dev_dma_desc_sts_t sts = {.d32 = 0 };
+ dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr;
+ dma_addr_t dma_ad;
+ dwc_otg_dev_in_ep_regs_t *in_regs =
+ core_if->dev_if->in_ep_regs[dwc_ep->num];
+ unsigned int frmnumber;
+ fifosize_data_t txfifosize, rxfifosize;
+
+ txfifosize.d32 =
+ DWC_READ_REG32(&core_if->dev_if->
+ in_ep_regs[dwc_ep->num]->dtxfsts);
+ rxfifosize.d32 =
+ DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+
+ addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
+
+ dma_ad = dwc_ep->dma_addr0;
+
+ dsts.d32 =
+ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+ sts.b_iso_in.bs = BS_HOST_READY;
+ sts.b_iso_in.txsts = 0;
+ sts.b_iso_in.sp =
+ (dwc_ep->data_per_frame % dwc_ep->maxpacket) ? 1 : 0;
+ sts.b_iso_in.ioc = 0;
+ sts.b_iso_in.pid = dwc_ep->pkt_per_frm;
+
+ frmnumber = dwc_ep->next_frame;
+
+ sts.b_iso_in.framenum = frmnumber;
+ sts.b_iso_in.txbytes = dwc_ep->data_per_frame;
+ sts.b_iso_in.l = 0;
+
+ /** Buffer 0 descriptors setup */
+ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) {
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+ dma_desc++;
+
+ dma_ad += dwc_ep->data_per_frame;
+ sts.b_iso_in.framenum += dwc_ep->bInterval;
+ }
+
+ sts.b_iso_in.ioc = 1;
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+ ++dma_desc;
+
+ /** Buffer 1 descriptors setup */
+ sts.b_iso_in.ioc = 0;
+ dma_ad = dwc_ep->dma_addr1;
+
+ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+ i += dwc_ep->pkt_per_frm) {
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+ dma_desc++;
+
+ dma_ad += dwc_ep->data_per_frame;
+ sts.b_iso_in.framenum += dwc_ep->bInterval;
+
+ sts.b_iso_in.ioc = 0;
+ }
+ sts.b_iso_in.ioc = 1;
+ sts.b_iso_in.l = 1;
+
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ dwc_ep->next_frame = sts.b_iso_in.framenum + dwc_ep->bInterval;
+
+ /** Write dma_ad into diepdma register */
+ DWC_WRITE_REG32(&(in_regs->diepdma),
+ (uint32_t) dwc_ep->iso_dma_desc_addr);
+ }
+ /** Enable endpoint, clear nak */
+ depctl.d32 = 0;
+ depctl.b.epena = 1;
+ depctl.b.usbactep = 1;
+ depctl.b.cnak = 1;
+
+ DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32);
+ depctl.d32 = DWC_READ_REG32(addr);
+}
+
+/**
+ * This function initializes a descriptor chain for Isochronous transfer
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if,
+ dwc_ep_t * ep)
+{
+ depctl_data_t depctl = {.d32 = 0 };
+ volatile uint32_t *addr;
+
+ if (ep->is_in) {
+ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
+ } else {
+ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
+ }
+
+ if (core_if->dma_enable == 0 || core_if->dma_desc_enable != 0) {
+ return;
+ } else {
+ deptsiz_data_t deptsiz = {.d32 = 0 };
+
+ ep->xfer_len =
+ ep->data_per_frame * ep->buf_proc_intrvl / ep->bInterval;
+ ep->pkt_cnt =
+ (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
+ ep->xfer_count = 0;
+ ep->xfer_buff =
+ (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0;
+ ep->dma_addr =
+ (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0;
+
+ if (ep->is_in) {
+ /* Program the transfer size and packet count
+ * as follows: xfersize = N * maxpacket +
+ * short_packet pktcnt = N + (short_packet
+ * exist ? 1 : 0)
+ */
+ deptsiz.b.mc = ep->pkt_per_frm;
+ deptsiz.b.xfersize = ep->xfer_len;
+ deptsiz.b.pktcnt =
+ (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
+ DWC_WRITE_REG32(&core_if->dev_if->
+ in_ep_regs[ep->num]->dieptsiz,
+ deptsiz.d32);
+
+ /* Write the DMA register */
+ DWC_WRITE_REG32(&
+ (core_if->dev_if->
+ in_ep_regs[ep->num]->diepdma),
+ (uint32_t) ep->dma_addr);
+
+ } else {
+ deptsiz.b.pktcnt =
+ (ep->xfer_len + (ep->maxpacket - 1)) /
+ ep->maxpacket;
+ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
+
+ DWC_WRITE_REG32(&core_if->dev_if->
+ out_ep_regs[ep->num]->doeptsiz,
+ deptsiz.d32);
+
+ /* Write the DMA register */
+ DWC_WRITE_REG32(&
+ (core_if->dev_if->
+ out_ep_regs[ep->num]->doepdma),
+ (uint32_t) ep->dma_addr);
+
+ }
+ /** Enable endpoint, clear nak */
+ depctl.d32 = 0;
+ depctl.b.epena = 1;
+ depctl.b.cnak = 1;
+
+ DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32);
+ }
+}
+
+/**
+ * This function does the setup for a data transfer for an EP and
+ * starts the transfer. For an IN transfer, the packets will be
+ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers,
+ * the packets are unloaded from the Rx FIFO in the ISR.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ */
+
+static void dwc_otg_iso_ep_start_transfer(dwc_otg_core_if_t * core_if,
+ dwc_ep_t * ep)
+{
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable) {
+ if (ep->is_in) {
+ ep->desc_cnt = ep->pkt_cnt / ep->pkt_per_frm;
+ } else {
+ ep->desc_cnt = ep->pkt_cnt;
+ }
+ dwc_otg_iso_ep_start_ddma_transfer(core_if, ep);
+ } else {
+ if (core_if->pti_enh_enable) {
+ dwc_otg_iso_ep_start_buf_transfer(core_if, ep);
+ } else {
+ ep->cur_pkt_addr =
+ (ep->proc_buf_num) ? ep->
+ xfer_buff1 : ep->xfer_buff0;
+ ep->cur_pkt_dma_addr =
+ (ep->proc_buf_num) ? ep->
+ dma_addr1 : ep->dma_addr0;
+ dwc_otg_iso_ep_start_frm_transfer(core_if, ep);
+ }
+ }
+ } else {
+ ep->cur_pkt_addr =
+ (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0;
+ ep->cur_pkt_dma_addr =
+ (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0;
+ dwc_otg_iso_ep_start_frm_transfer(core_if, ep);
+ }
+}
+
+/**
+ * This function stops transfer for an EP and
+ * resets the ep's variables.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ */
+
+void dwc_otg_iso_ep_stop_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ depctl_data_t depctl = {.d32 = 0 };
+ volatile uint32_t *addr;
+
+ if (ep->is_in == 1) {
+ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
+ } else {
+ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
+ }
+
+ /* disable the ep */
+ depctl.d32 = DWC_READ_REG32(addr);
+
+ depctl.b.epdis = 1;
+ depctl.b.snak = 1;
+
+ DWC_WRITE_REG32(addr, depctl.d32);
+
+ if (core_if->dma_desc_enable &&
+ ep->iso_desc_addr && ep->iso_dma_desc_addr) {
+ dwc_otg_ep_free_desc_chain(ep->iso_desc_addr,
+ ep->iso_dma_desc_addr,
+ ep->desc_cnt * 2);
+ }
+
+ /* reset varibales */
+ ep->dma_addr0 = 0;
+ ep->dma_addr1 = 0;
+ ep->xfer_buff0 = 0;
+ ep->xfer_buff1 = 0;
+ ep->data_per_frame = 0;
+ ep->data_pattern_frame = 0;
+ ep->sync_frame = 0;
+ ep->buf_proc_intrvl = 0;
+ ep->bInterval = 0;
+ ep->proc_buf_num = 0;
+ ep->pkt_per_frm = 0;
+ ep->pkt_per_frm = 0;
+ ep->desc_cnt = 0;
+ ep->iso_desc_addr = 0;
+ ep->iso_dma_desc_addr = 0;
+}
+
+int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle,
+ uint8_t * buf0, uint8_t * buf1, dwc_dma_t dma0,
+ dwc_dma_t dma1, int sync_frame, int dp_frame,
+ int data_per_frame, int start_frame,
+ int buf_proc_intrvl, void *req_handle,
+ int atomic_alloc)
+{
+ dwc_otg_pcd_ep_t *ep;
+ dwc_irqflags_t flags = 0;
+ dwc_ep_t *dwc_ep;
+ int32_t frm_data;
+ dsts_data_t dsts;
+ dwc_otg_core_if_t *core_if;
+
+ ep = get_ep_from_handle(pcd, ep_handle);
+
+ if (!ep || !ep->desc || ep->dwc_ep.num == 0) {
+ DWC_WARN("bad ep\n");
+ return -DWC_E_INVALID;
+ }
+
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+ core_if = GET_CORE_IF(pcd);
+ dwc_ep = &ep->dwc_ep;
+
+ if (ep->iso_req_handle) {
+ DWC_WARN("ISO request in progress\n");
+ }
+
+ dwc_ep->dma_addr0 = dma0;
+ dwc_ep->dma_addr1 = dma1;
+
+ dwc_ep->xfer_buff0 = buf0;
+ dwc_ep->xfer_buff1 = buf1;
+
+ dwc_ep->data_per_frame = data_per_frame;
+
+ /** @todo - pattern data support is to be implemented in the future */
+ dwc_ep->data_pattern_frame = dp_frame;
+ dwc_ep->sync_frame = sync_frame;
+
+ dwc_ep->buf_proc_intrvl = buf_proc_intrvl;
+
+ dwc_ep->bInterval = 1 << (ep->desc->bInterval - 1);
+
+ dwc_ep->proc_buf_num = 0;
+
+ dwc_ep->pkt_per_frm = 0;
+ frm_data = ep->dwc_ep.data_per_frame;
+ while (frm_data > 0) {
+ dwc_ep->pkt_per_frm++;
+ frm_data -= ep->dwc_ep.maxpacket;
+ }
+
+ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+ if (start_frame == -1) {
+ dwc_ep->next_frame = dsts.b.soffn + 1;
+ if (dwc_ep->bInterval != 1) {
+ dwc_ep->next_frame =
+ dwc_ep->next_frame + (dwc_ep->bInterval - 1 -
+ dwc_ep->next_frame %
+ dwc_ep->bInterval);
+ }
+ } else {
+ dwc_ep->next_frame = start_frame;
+ }
+
+ if (!core_if->pti_enh_enable) {
+ dwc_ep->pkt_cnt =
+ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm /
+ dwc_ep->bInterval;
+ } else {
+ dwc_ep->pkt_cnt =
+ (dwc_ep->data_per_frame *
+ (dwc_ep->buf_proc_intrvl / dwc_ep->bInterval)
+ - 1 + dwc_ep->maxpacket) / dwc_ep->maxpacket;
+ }
+
+ if (core_if->dma_desc_enable) {
+ dwc_ep->desc_cnt =
+ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm /
+ dwc_ep->bInterval;
+ }
+
+ if (atomic_alloc) {
+ dwc_ep->pkt_info =
+ DWC_ALLOC_ATOMIC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
+ } else {
+ dwc_ep->pkt_info =
+ DWC_ALLOC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
+ }
+ if (!dwc_ep->pkt_info) {
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ return -DWC_E_NO_MEMORY;
+ }
+ if (core_if->pti_enh_enable) {
+ dwc_memset(dwc_ep->pkt_info, 0,
+ sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
+ }
+
+ dwc_ep->cur_pkt = 0;
+ ep->iso_req_handle = req_handle;
+
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ dwc_otg_iso_ep_start_transfer(core_if, dwc_ep);
+ return 0;
+}
+
+int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle)
+{
+ dwc_irqflags_t flags = 0;
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+
+ ep = get_ep_from_handle(pcd, ep_handle);
+ if (!ep || !ep->desc || ep->dwc_ep.num == 0) {
+ DWC_WARN("bad ep\n");
+ return -DWC_E_INVALID;
+ }
+ dwc_ep = &ep->dwc_ep;
+
+ dwc_otg_iso_ep_stop_transfer(GET_CORE_IF(pcd), dwc_ep);
+
+ DWC_FREE(dwc_ep->pkt_info);
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+ if (ep->iso_req_handle != req_handle) {
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ return -DWC_E_INVALID;
+ }
+
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+ ep->iso_req_handle = 0;
+ return 0;
+}
+
+/**
+ * This function is used for perodical data exchnage between PCD and gadget drivers.
+ * for Isochronous EPs
+ *
+ * - Every time a sync period completes this function is called to
+ * perform data exchange between PCD and gadget
+ */
+void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep,
+ void *req_handle)
+{
+ int i;
+ dwc_ep_t *dwc_ep;
+
+ dwc_ep = &ep->dwc_ep;
+
+ DWC_SPINUNLOCK(ep->pcd->lock);
+ pcd->fops->isoc_complete(pcd, ep->priv, ep->iso_req_handle,
+ dwc_ep->proc_buf_num ^ 0x1);
+ DWC_SPINLOCK(ep->pcd->lock);
+
+ for (i = 0; i < dwc_ep->pkt_cnt; ++i) {
+ dwc_ep->pkt_info[i].status = 0;
+ dwc_ep->pkt_info[i].offset = 0;
+ dwc_ep->pkt_info[i].length = 0;
+ }
+}
+
+int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *iso_req_handle)
+{
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+
+ ep = get_ep_from_handle(pcd, ep_handle);
+ if (!ep->desc || ep->dwc_ep.num == 0) {
+ DWC_WARN("bad ep\n");
+ return -DWC_E_INVALID;
+ }
+ dwc_ep = &ep->dwc_ep;
+
+ return dwc_ep->pkt_cnt;
+}
+
+void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *iso_req_handle, int packet,
+ int *status, int *actual, int *offset)
+{
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+
+ ep = get_ep_from_handle(pcd, ep_handle);
+ if (!ep)
+ DWC_WARN("bad ep\n");
+
+ dwc_ep = &ep->dwc_ep;
+
+ *status = dwc_ep->pkt_info[packet].status;
+ *actual = dwc_ep->pkt_info[packet].length;
+ *offset = dwc_ep->pkt_info[packet].offset;
+}
+
+#endif /* DWC_EN_ISOC */
+
+static void dwc_otg_pcd_init_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * pcd_ep,
+ uint32_t is_in, uint32_t ep_num)
+{
+ /* Init EP structure */
+ pcd_ep->desc = 0;
+ pcd_ep->pcd = pcd;
+ pcd_ep->stopped = 1;
+ pcd_ep->queue_sof = 0;
+
+ /* Init DWC ep structure */
+ pcd_ep->dwc_ep.is_in = is_in;
+ pcd_ep->dwc_ep.num = ep_num;
+ pcd_ep->dwc_ep.active = 0;
+ pcd_ep->dwc_ep.tx_fifo_num = 0;
+ /* Control until ep is actvated */
+ pcd_ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+ pcd_ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+ pcd_ep->dwc_ep.dma_addr = 0;
+ pcd_ep->dwc_ep.start_xfer_buff = 0;
+ pcd_ep->dwc_ep.xfer_buff = 0;
+ pcd_ep->dwc_ep.xfer_len = 0;
+ pcd_ep->dwc_ep.xfer_count = 0;
+ pcd_ep->dwc_ep.sent_zlp = 0;
+ pcd_ep->dwc_ep.total_len = 0;
+ pcd_ep->dwc_ep.desc_addr = 0;
+ pcd_ep->dwc_ep.dma_desc_addr = 0;
+ DWC_CIRCLEQ_INIT(&pcd_ep->queue);
+}
+
+/**
+ * Initialize ep's
+ */
+static void dwc_otg_pcd_reinit(dwc_otg_pcd_t * pcd)
+{
+ int i;
+ uint32_t hwcfg1;
+ dwc_otg_pcd_ep_t *ep;
+ int in_ep_cntr, out_ep_cntr;
+ uint32_t num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps;
+ uint32_t num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps;
+
+ /**
+ * Initialize the EP0 structure.
+ */
+ ep = &pcd->ep0;
+ dwc_otg_pcd_init_ep(pcd, ep, 0, 0);
+
+ in_ep_cntr = 0;
+ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3;
+ for (i = 1; in_ep_cntr < num_in_eps; i++) {
+ if ((hwcfg1 & 0x1) == 0) {
+ dwc_otg_pcd_ep_t *ep = &pcd->in_ep[in_ep_cntr];
+ in_ep_cntr++;
+ /**
+ * @todo NGS: Add direction to EP, based on contents
+ * of HWCFG1. Need a copy of HWCFG1 in pcd structure?
+ * sprintf(";r
+ */
+ dwc_otg_pcd_init_ep(pcd, ep, 1 /* IN */ , i);
+
+ DWC_CIRCLEQ_INIT(&ep->queue);
+ }
+ hwcfg1 >>= 2;
+ }
+
+ out_ep_cntr = 0;
+ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2;
+ for (i = 1; out_ep_cntr < num_out_eps; i++) {
+ if ((hwcfg1 & 0x1) == 0) {
+ dwc_otg_pcd_ep_t *ep = &pcd->out_ep[out_ep_cntr];
+ out_ep_cntr++;
+ /**
+ * @todo NGS: Add direction to EP, based on contents
+ * of HWCFG1. Need a copy of HWCFG1 in pcd structure?
+ * sprintf(";r
+ */
+ dwc_otg_pcd_init_ep(pcd, ep, 0 /* OUT */ , i);
+ DWC_CIRCLEQ_INIT(&ep->queue);
+ }
+ hwcfg1 >>= 2;
+ }
+
+ pcd->ep0state = EP0_DISCONNECT;
+ pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE;
+ pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+}
+
+/**
+ * This function is called when the SRP timer expires. The SRP should
+ * complete within 6 seconds.
+ */
+static void srp_timeout(void *ptr)
+{
+ gotgctl_data_t gotgctl;
+ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
+ volatile uint32_t *addr = &core_if->core_global_regs->gotgctl;
+
+ gotgctl.d32 = DWC_READ_REG32(addr);
+
+ core_if->srp_timer_started = 0;
+
+ if (core_if->adp_enable) {
+ if (gotgctl.b.bsesvld == 0) {
+ gpwrdn_data_t gpwrdn = {.d32 = 0 };
+ DWC_PRINTF("SRP Timeout BSESSVLD = 0\n");
+ /* Power off the core */
+ if (core_if->power_down == 2) {
+ gpwrdn.b.pwrdnswtch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, gpwrdn.d32, 0);
+ }
+
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuintsel = 1;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+ dwc_otg_adp_probe_start(core_if);
+ } else {
+ DWC_PRINTF("SRP Timeout BSESSVLD = 1\n");
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+ }
+ }
+
+ if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) &&
+ (core_if->core_params->i2c_enable)) {
+ DWC_PRINTF("SRP Timeout\n");
+
+ if ((core_if->srp_success) && (gotgctl.b.bsesvld)) {
+ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+ }
+
+ /* Clear Session Request */
+ gotgctl.d32 = 0;
+ gotgctl.b.sesreq = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl,
+ gotgctl.d32, 0);
+
+ core_if->srp_success = 0;
+ } else {
+ __DWC_ERROR("Device not connected/responding\n");
+ gotgctl.b.sesreq = 0;
+ DWC_WRITE_REG32(addr, gotgctl.d32);
+ }
+ } else if (gotgctl.b.sesreq) {
+ DWC_PRINTF("SRP Timeout\n");
+
+ __DWC_ERROR("Device not connected/responding\n");
+ gotgctl.b.sesreq = 0;
+ DWC_WRITE_REG32(addr, gotgctl.d32);
+ } else {
+ DWC_PRINTF(" SRP GOTGCTL=%0x\n", gotgctl.d32);
+ }
+}
+
+/**
+ * Tasklet
+ *
+ */
+extern void start_next_request(dwc_otg_pcd_ep_t * ep);
+
+static void start_xfer_tasklet_func(void *data)
+{
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data;
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+ int i;
+ depctl_data_t diepctl;
+
+ DWC_DEBUGPL(DBG_PCDV, "Start xfer tasklet\n");
+
+ diepctl.d32 = DWC_READ_REG32(&core_if->dev_if->in_ep_regs[0]->diepctl);
+
+ if (pcd->ep0.queue_sof) {
+ pcd->ep0.queue_sof = 0;
+ start_next_request(&pcd->ep0);
+ // break;
+ }
+
+ for (i = 0; i < core_if->dev_if->num_in_eps; i++) {
+ depctl_data_t diepctl;
+ diepctl.d32 =
+ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl);
+
+ if (pcd->in_ep[i].queue_sof) {
+ pcd->in_ep[i].queue_sof = 0;
+ start_next_request(&pcd->in_ep[i]);
+ // break;
+ }
+ }
+
+ return;
+}
+
+/**
+ * This function initialized the PCD portion of the driver.
+ *
+ */
+dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_pcd_t *pcd = NULL;
+ dwc_otg_dev_if_t *dev_if;
+ int i;
+
+ /*
+ * Allocate PCD structure
+ */
+ pcd = DWC_ALLOC(sizeof(dwc_otg_pcd_t));
+
+ if (pcd == NULL) {
+ return NULL;
+ }
+
+ pcd->lock = DWC_SPINLOCK_ALLOC();
+ if (!pcd->lock) {
+ DWC_ERROR("Could not allocate lock for pcd");
+ DWC_FREE(pcd);
+ return NULL;
+ }
+ /* Set core_if's lock pointer to hcd->lock */
+ core_if->lock = pcd->lock;
+ pcd->core_if = core_if;
+
+ dev_if = core_if->dev_if;
+ dev_if->isoc_ep = NULL;
+
+ if (core_if->hwcfg4.b.ded_fifo_en) {
+ DWC_PRINTF("Dedicated Tx FIFOs mode\n");
+ } else {
+ DWC_PRINTF("Shared Tx FIFO mode\n");
+ }
+
+ /*
+ * Initialized the Core for Device mode here if there is nod ADP support.
+ * Otherwise it will be done later in dwc_otg_adp_start routine.
+ */
+ if (dwc_otg_is_device_mode(core_if) /*&& !core_if->adp_enable*/) {
+ dwc_otg_core_dev_init(core_if);
+ }
+
+ /*
+ * Register the PCD Callbacks.
+ */
+ dwc_otg_cil_register_pcd_callbacks(core_if, &pcd_callbacks, pcd);
+
+ /*
+ * Initialize the DMA buffer for SETUP packets
+ */
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ pcd->setup_pkt =
+ DWC_DMA_ALLOC(sizeof(*pcd->setup_pkt) * 5,
+ &pcd->setup_pkt_dma_handle);
+ if (pcd->setup_pkt == NULL) {
+ DWC_FREE(pcd);
+ return NULL;
+ }
+
+ pcd->status_buf =
+ DWC_DMA_ALLOC(sizeof(uint16_t),
+ &pcd->status_buf_dma_handle);
+ if (pcd->status_buf == NULL) {
+ DWC_DMA_FREE(sizeof(*pcd->setup_pkt) * 5,
+ pcd->setup_pkt, pcd->setup_pkt_dma_handle);
+ DWC_FREE(pcd);
+ return NULL;
+ }
+
+ if (GET_CORE_IF(pcd)->dma_desc_enable) {
+ dev_if->setup_desc_addr[0] =
+ dwc_otg_ep_alloc_desc_chain(&dev_if->
+ dma_setup_desc_addr[0],
+ 1);
+ dev_if->setup_desc_addr[1] =
+ dwc_otg_ep_alloc_desc_chain(&dev_if->
+ dma_setup_desc_addr[1],
+ 1);
+ dev_if->in_desc_addr =
+ dwc_otg_ep_alloc_desc_chain(&dev_if->
+ dma_in_desc_addr, 1);
+ dev_if->out_desc_addr =
+ dwc_otg_ep_alloc_desc_chain(&dev_if->
+ dma_out_desc_addr, 1);
+
+ if (dev_if->setup_desc_addr[0] == 0
+ || dev_if->setup_desc_addr[1] == 0
+ || dev_if->in_desc_addr == 0
+ || dev_if->out_desc_addr == 0) {
+
+ if (dev_if->out_desc_addr)
+ dwc_otg_ep_free_desc_chain(dev_if->
+ out_desc_addr,
+ dev_if->
+ dma_out_desc_addr,
+ 1);
+ if (dev_if->in_desc_addr)
+ dwc_otg_ep_free_desc_chain(dev_if->
+ in_desc_addr,
+ dev_if->
+ dma_in_desc_addr,
+ 1);
+ if (dev_if->setup_desc_addr[1])
+ dwc_otg_ep_free_desc_chain(dev_if->
+ setup_desc_addr
+ [1],
+ dev_if->
+ dma_setup_desc_addr
+ [1], 1);
+ if (dev_if->setup_desc_addr[0])
+ dwc_otg_ep_free_desc_chain(dev_if->
+ setup_desc_addr
+ [0],
+ dev_if->
+ dma_setup_desc_addr
+ [0], 1);
+
+ DWC_DMA_FREE(sizeof(*pcd->setup_pkt) * 5,
+ pcd->setup_pkt,
+ pcd->setup_pkt_dma_handle);
+ DWC_DMA_FREE(sizeof(*pcd->status_buf),
+ pcd->status_buf,
+ pcd->status_buf_dma_handle);
+
+ DWC_FREE(pcd);
+
+ return NULL;
+ }
+ }
+ } else {
+ pcd->setup_pkt = DWC_ALLOC(sizeof(*pcd->setup_pkt) * 5);
+ if (pcd->setup_pkt == NULL) {
+ DWC_FREE(pcd);
+ return NULL;
+ }
+
+ pcd->status_buf = DWC_ALLOC(sizeof(uint16_t));
+ if (pcd->status_buf == NULL) {
+ DWC_FREE(pcd->setup_pkt);
+ DWC_FREE(pcd);
+ return NULL;
+ }
+ }
+
+ dwc_otg_pcd_reinit(pcd);
+
+ /* Allocate the cfi object for the PCD */
+#ifdef DWC_UTE_CFI
+ pcd->cfi = DWC_ALLOC(sizeof(cfiobject_t));
+ if (NULL == pcd->cfi)
+ goto fail;
+ if (init_cfi(pcd->cfi)) {
+ CFI_INFO("%s: Failed to init the CFI object\n", __func__);
+ goto fail;
+ }
+#endif
+
+ /* Initialize tasklets */
+ pcd->start_xfer_tasklet = DWC_TASK_ALLOC("xfer_tasklet",
+ start_xfer_tasklet_func, pcd);
+ pcd->test_mode_tasklet = DWC_TASK_ALLOC("test_mode_tasklet",
+ do_test_mode, pcd);
+
+ /* Initialize SRP timer */
+ core_if->srp_timer = DWC_TIMER_ALLOC("SRP TIMER", srp_timeout, core_if);
+
+ if (core_if->core_params->dev_out_nak) {
+ /**
+ * Initialize xfer timeout timer. Implemented for
+ * 2.93a feature "Device DDMA OUT NAK Enhancement"
+ */
+ for(i = 0; i < MAX_EPS_CHANNELS; i++) {
+ pcd->core_if->ep_xfer_timer[i] =
+ DWC_TIMER_ALLOC("ep timer", ep_xfer_timeout,
+ &pcd->core_if->ep_xfer_info[i]);
+ }
+ }
+
+ return pcd;
+#ifdef DWC_UTE_CFI
+fail:
+#endif
+ if (pcd->setup_pkt)
+ DWC_FREE(pcd->setup_pkt);
+ if (pcd->status_buf)
+ DWC_FREE(pcd->status_buf);
+#ifdef DWC_UTE_CFI
+ if (pcd->cfi)
+ DWC_FREE(pcd->cfi);
+#endif
+ if (pcd)
+ DWC_FREE(pcd);
+ return NULL;
+
+}
+
+/**
+ * Remove PCD specific data
+ */
+void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
+ int i;
+ if (pcd->core_if->core_params->dev_out_nak) {
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[i]);
+ pcd->core_if->ep_xfer_info[i].state = 0;
+ }
+ }
+
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ DWC_DMA_FREE(sizeof(*pcd->setup_pkt) * 5, pcd->setup_pkt,
+ pcd->setup_pkt_dma_handle);
+ DWC_DMA_FREE(sizeof(uint16_t), pcd->status_buf,
+ pcd->status_buf_dma_handle);
+ if (GET_CORE_IF(pcd)->dma_desc_enable) {
+ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[0],
+ dev_if->dma_setup_desc_addr
+ [0], 1);
+ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[1],
+ dev_if->dma_setup_desc_addr
+ [1], 1);
+ dwc_otg_ep_free_desc_chain(dev_if->in_desc_addr,
+ dev_if->dma_in_desc_addr, 1);
+ dwc_otg_ep_free_desc_chain(dev_if->out_desc_addr,
+ dev_if->dma_out_desc_addr,
+ 1);
+ }
+ } else {
+ DWC_FREE(pcd->setup_pkt);
+ DWC_FREE(pcd->status_buf);
+ }
+ DWC_SPINLOCK_FREE(pcd->lock);
+ /* Set core_if's lock pointer to NULL */
+ pcd->core_if->lock = NULL;
+
+ DWC_TASK_FREE(pcd->start_xfer_tasklet);
+ DWC_TASK_FREE(pcd->test_mode_tasklet);
+ if (pcd->core_if->core_params->dev_out_nak) {
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ if (pcd->core_if->ep_xfer_timer[i]) {
+ DWC_TIMER_FREE(pcd->core_if->ep_xfer_timer[i]);
+ }
+ }
+ }
+
+/* Release the CFI object's dynamic memory */
+#ifdef DWC_UTE_CFI
+ if (pcd->cfi->ops.release) {
+ pcd->cfi->ops.release(pcd->cfi);
+ }
+#endif
+
+ DWC_FREE(pcd);
+}
+
+/**
+ * Returns whether registered pcd is dual speed or not
+ */
+uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+ if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) ||
+ ((core_if->hwcfg2.b.hs_phy_type == 2) &&
+ (core_if->hwcfg2.b.fs_phy_type == 1) &&
+ (core_if->core_params->ulpi_fs_ls))) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Returns whether registered pcd is OTG capable or not
+ */
+uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ gusbcfg_data_t usbcfg = {.d32 = 0 };
+
+ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+ if (!usbcfg.b.srpcap || !usbcfg.b.hnpcap) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP
+ * in shared Tx FIFO mode
+ */
+static uint32_t assign_tx_fifo(dwc_otg_core_if_t * core_if)
+{
+ uint32_t TxMsk = 1;
+ int i;
+
+ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i) {
+ if ((TxMsk & core_if->tx_msk) == 0) {
+ core_if->tx_msk |= TxMsk;
+ return i + 1;
+ }
+ TxMsk <<= 1;
+ }
+ return 0;
+}
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP
+ * in shared Tx FIFO mode
+ */
+static uint32_t assign_perio_tx_fifo(dwc_otg_core_if_t * core_if)
+{
+ uint32_t PerTxMsk = 1;
+ int i;
+ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i) {
+ if ((PerTxMsk & core_if->p_tx_msk) == 0) {
+ core_if->p_tx_msk |= PerTxMsk;
+ return i + 1;
+ }
+ PerTxMsk <<= 1;
+ }
+ return 0;
+}
+
+/**
+ * This function releases periodic Tx FIFO
+ * in shared Tx FIFO mode
+ */
+static void release_perio_tx_fifo(dwc_otg_core_if_t * core_if,
+ uint32_t fifo_num)
+{
+ core_if->p_tx_msk =
+ (core_if->p_tx_msk & (1 << (fifo_num - 1))) ^ core_if->p_tx_msk;
+}
+
+/**
+ * This function releases periodic Tx FIFO
+ * in shared Tx FIFO mode
+ */
+static void release_tx_fifo(dwc_otg_core_if_t * core_if, uint32_t fifo_num)
+{
+ core_if->tx_msk =
+ (core_if->tx_msk & (1 << (fifo_num - 1))) ^ core_if->tx_msk;
+}
+
+/**
+ * This function is being called from gadget
+ * to enable PCD endpoint.
+ */
+int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd,
+ const uint8_t * ep_desc, void *usb_ep)
+{
+ int num, dir;
+ dwc_otg_pcd_ep_t *ep = NULL;
+ const usb_endpoint_descriptor_t *desc;
+ dwc_irqflags_t flags;
+ fifosize_data_t dptxfsiz = {.d32 = 0 };
+ gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
+ gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 };
+ int retval = 0;
+ int i, epcount;
+
+ desc = (const usb_endpoint_descriptor_t *)ep_desc;
+
+ if (!desc) {
+ pcd->ep0.priv = usb_ep;
+ ep = &pcd->ep0;
+ retval = -DWC_E_INVALID;
+ goto out;
+ }
+
+ num = UE_GET_ADDR(desc->bEndpointAddress);
+ dir = UE_GET_DIR(desc->bEndpointAddress);
+
+ if (!desc->wMaxPacketSize) {
+ DWC_WARN("bad maxpacketsize\n");
+ retval = -DWC_E_INVALID;
+ goto out;
+ }
+
+ if (dir == UE_DIR_IN) {
+ epcount = pcd->core_if->dev_if->num_in_eps;
+ for (i = 0; i < epcount; i++) {
+ if (num == pcd->in_ep[i].dwc_ep.num) {
+ ep = &pcd->in_ep[i];
+ break;
+ }
+ }
+ } else {
+ epcount = pcd->core_if->dev_if->num_out_eps;
+ for (i = 0; i < epcount; i++) {
+ if (num == pcd->out_ep[i].dwc_ep.num) {
+ ep = &pcd->out_ep[i];
+ break;
+ }
+ }
+ }
+
+ if (!ep) {
+ DWC_WARN("bad address\n");
+ retval = -DWC_E_INVALID;
+ goto out;
+ }
+
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+ ep->desc = desc;
+ ep->priv = usb_ep;
+
+ /*
+ * Activate the EP
+ */
+ ep->stopped = 0;
+
+ ep->dwc_ep.is_in = (dir == UE_DIR_IN);
+ ep->dwc_ep.maxpacket = UGETW(desc->wMaxPacketSize);
+
+ ep->dwc_ep.type = desc->bmAttributes & UE_XFERTYPE;
+
+ if (ep->dwc_ep.is_in) {
+ if (!GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
+ ep->dwc_ep.tx_fifo_num = 0;
+
+ if (ep->dwc_ep.type == UE_ISOCHRONOUS) {
+ /*
+ * if ISOC EP then assign a Periodic Tx FIFO.
+ */
+ ep->dwc_ep.tx_fifo_num =
+ assign_perio_tx_fifo(GET_CORE_IF(pcd));
+ }
+ } else {
+ /*
+ * if Dedicated FIFOs mode is on then assign a Tx FIFO.
+ */
+ ep->dwc_ep.tx_fifo_num =
+ assign_tx_fifo(GET_CORE_IF(pcd));
+ }
+
+ /* Calculating EP info controller base address */
+ if (ep->dwc_ep.tx_fifo_num && GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
+ gdfifocfg.d32 =
+ DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs->
+ gdfifocfg);
+ gdfifocfgbase.d32 = gdfifocfg.d32 >> 16;
+ dptxfsiz.d32 =
+ (DWC_READ_REG32
+ (&GET_CORE_IF(pcd)->
+ core_global_regs->dtxfsiz[ep->dwc_ep.
+ tx_fifo_num-1]) >> 16);
+ gdfifocfg.b.epinfobase =
+ gdfifocfgbase.d32 + dptxfsiz.d32;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->
+ gdfifocfg, gdfifocfg.d32);
+ }
+ }
+ /* Set initial data PID. */
+ if (ep->dwc_ep.type == UE_BULK) {
+ ep->dwc_ep.data_pid_start = 0;
+ }
+
+ /* Alloc DMA Descriptors */
+ if (GET_CORE_IF(pcd)->dma_desc_enable) {
+#ifndef DWC_UTE_PER_IO
+ if (ep->dwc_ep.type != UE_ISOCHRONOUS) {
+#endif
+ ep->dwc_ep.desc_addr =
+ dwc_otg_ep_alloc_desc_chain(&ep->
+ dwc_ep.dma_desc_addr,
+ MAX_DMA_DESC_CNT);
+ if (!ep->dwc_ep.desc_addr) {
+ DWC_WARN("%s, can't allocate DMA descriptor\n",
+ __func__);
+ retval = -DWC_E_SHUTDOWN;
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ goto out;
+ }
+#ifndef DWC_UTE_PER_IO
+ }
+#endif
+ }
+
+ DWC_DEBUGPL(DBG_PCD, "Activate %s: type=%d, mps=%d desc=%p\n",
+ (ep->dwc_ep.is_in ? "IN" : "OUT"),
+ ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc);
+#ifdef DWC_UTE_PER_IO
+ ep->dwc_ep.xiso_bInterval = 1 << (ep->desc->bInterval - 1);
+#endif
+ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+ ep->dwc_ep.bInterval = 1 << (ep->desc->bInterval - 1);
+ ep->dwc_ep.frame_num = 0xFFFFFFFF;
+ }
+
+ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
+
+#ifdef DWC_UTE_CFI
+ if (pcd->cfi->ops.ep_enable) {
+ pcd->cfi->ops.ep_enable(pcd->cfi, pcd, ep);
+ }
+#endif
+
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+out:
+ return retval;
+}
+
+/**
+ * This function is being called from gadget
+ * to disable PCD endpoint.
+ */
+int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle)
+{
+ dwc_otg_pcd_ep_t *ep;
+ dwc_irqflags_t flags;
+ dwc_otg_dev_dma_desc_t *desc_addr;
+ dwc_dma_t dma_desc_addr;
+ gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 };
+ gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
+ fifosize_data_t dptxfsiz = {.d32 = 0 };
+
+ ep = get_ep_from_handle(pcd, ep_handle);
+
+ if (!ep || !ep->desc) {
+ DWC_DEBUGPL(DBG_PCD, "bad ep address\n");
+ return -DWC_E_INVALID;
+ }
+
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+ dwc_otg_request_nuke(ep);
+
+ dwc_otg_ep_deactivate(GET_CORE_IF(pcd), &ep->dwc_ep);
+ if (pcd->core_if->core_params->dev_out_nak)
+ {
+ DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[ep->dwc_ep.num]);
+ pcd->core_if->ep_xfer_info[ep->dwc_ep.num].state = 0;
+ }
+ ep->desc = NULL;
+ ep->stopped = 1;
+
+ gdfifocfg.d32 =
+ DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg);
+ gdfifocfgbase.d32 = gdfifocfg.d32 >> 16;
+
+ if (ep->dwc_ep.is_in) {
+ if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
+ /* Flush the Tx FIFO */
+ dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num);
+ }
+ release_perio_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num);
+ release_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num);
+ if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
+ /* Decreasing EPinfo Base Addr */
+ dptxfsiz.d32 =
+ (DWC_READ_REG32
+ (&GET_CORE_IF(pcd)->
+ core_global_regs->dtxfsiz[ep->dwc_ep.tx_fifo_num-1]) >> 16);
+ gdfifocfg.b.epinfobase = gdfifocfgbase.d32 - dptxfsiz.d32;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg,
+ gdfifocfg.d32);
+ }
+ }
+
+ /* Free DMA Descriptors */
+ if (GET_CORE_IF(pcd)->dma_desc_enable) {
+ if (ep->dwc_ep.type != UE_ISOCHRONOUS) {
+ desc_addr = ep->dwc_ep.desc_addr;
+ dma_desc_addr = ep->dwc_ep.dma_desc_addr;
+
+ /* Cannot call dma_free_coherent() with IRQs disabled */
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ dwc_otg_ep_free_desc_chain(desc_addr, dma_desc_addr,
+ MAX_DMA_DESC_CNT);
+
+ goto out_unlocked;
+ }
+ }
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+out_unlocked:
+ DWC_DEBUGPL(DBG_PCD, "%d %s disabled\n", ep->dwc_ep.num,
+ ep->dwc_ep.is_in ? "IN" : "OUT");
+ return 0;
+
+}
+
+/******************************************************************************/
+#ifdef DWC_UTE_PER_IO
+
+/**
+ * Free the request and its extended parts
+ *
+ */
+void dwc_pcd_xiso_ereq_free(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req)
+{
+ DWC_FREE(req->ext_req.per_io_frame_descs);
+ DWC_FREE(req);
+}
+
+/**
+ * Start the next request in the endpoint's queue.
+ *
+ */
+int dwc_otg_pcd_xiso_start_next_request(dwc_otg_pcd_t * pcd,
+ dwc_otg_pcd_ep_t * ep)
+{
+ int i;
+ dwc_otg_pcd_request_t *req = NULL;
+ dwc_ep_t *dwcep = NULL;
+ struct dwc_iso_xreq_port *ereq = NULL;
+ struct dwc_iso_pkt_desc_port *ddesc_iso;
+ uint16_t nat;
+ depctl_data_t diepctl;
+
+ dwcep = &ep->dwc_ep;
+
+ if (dwcep->xiso_active_xfers > 0) {
+#if 0 //Disable this to decrease s/w overhead that is crucial for Isoc transfers
+ DWC_WARN("There are currently active transfers for EP%d \
+ (active=%d; queued=%d)", dwcep->num, dwcep->xiso_active_xfers,
+ dwcep->xiso_queued_xfers);
+#endif
+ return 0;
+ }
+
+ nat = UGETW(ep->desc->wMaxPacketSize);
+ nat = (nat >> 11) & 0x03;
+
+ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+ req = DWC_CIRCLEQ_FIRST(&ep->queue);
+ ereq = &req->ext_req;
+ ep->stopped = 0;
+
+ /* Get the frame number */
+ dwcep->xiso_frame_num =
+ dwc_otg_get_frame_number(GET_CORE_IF(pcd));
+ DWC_DEBUG("FRM_NUM=%d", dwcep->xiso_frame_num);
+
+ ddesc_iso = ereq->per_io_frame_descs;
+
+ if (dwcep->is_in) {
+ /* Setup DMA Descriptor chain for IN Isoc request */
+ for (i = 0; i < ereq->pio_pkt_count; i++) {
+ //if ((i % (nat + 1)) == 0)
+ if ( i > 0 )
+ dwcep->xiso_frame_num = (dwcep->xiso_bInterval +
+ dwcep->xiso_frame_num) & 0x3FFF;
+ dwcep->desc_addr[i].buf =
+ req->dma + ddesc_iso[i].offset;
+ dwcep->desc_addr[i].status.b_iso_in.txbytes =
+ ddesc_iso[i].length;
+ dwcep->desc_addr[i].status.b_iso_in.framenum =
+ dwcep->xiso_frame_num;
+ dwcep->desc_addr[i].status.b_iso_in.bs =
+ BS_HOST_READY;
+ dwcep->desc_addr[i].status.b_iso_in.txsts = 0;
+ dwcep->desc_addr[i].status.b_iso_in.sp =
+ (ddesc_iso[i].length %
+ dwcep->maxpacket) ? 1 : 0;
+ dwcep->desc_addr[i].status.b_iso_in.ioc = 0;
+ dwcep->desc_addr[i].status.b_iso_in.pid = nat + 1;
+ dwcep->desc_addr[i].status.b_iso_in.l = 0;
+
+ /* Process the last descriptor */
+ if (i == ereq->pio_pkt_count - 1) {
+ dwcep->desc_addr[i].status.b_iso_in.ioc = 1;
+ dwcep->desc_addr[i].status.b_iso_in.l = 1;
+ }
+ }
+
+ /* Setup and start the transfer for this endpoint */
+ dwcep->xiso_active_xfers++;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->dev_if->
+ in_ep_regs[dwcep->num]->diepdma,
+ dwcep->dma_desc_addr);
+ diepctl.d32 = 0;
+ diepctl.b.epena = 1;
+ diepctl.b.cnak = 1;
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if->
+ in_ep_regs[dwcep->num]->diepctl, 0,
+ diepctl.d32);
+ } else {
+ /* Setup DMA Descriptor chain for OUT Isoc request */
+ for (i = 0; i < ereq->pio_pkt_count; i++) {
+ //if ((i % (nat + 1)) == 0)
+ dwcep->xiso_frame_num = (dwcep->xiso_bInterval +
+ dwcep->xiso_frame_num) & 0x3FFF;
+ dwcep->desc_addr[i].buf =
+ req->dma + ddesc_iso[i].offset;
+ dwcep->desc_addr[i].status.b_iso_out.rxbytes =
+ ddesc_iso[i].length;
+ dwcep->desc_addr[i].status.b_iso_out.framenum =
+ dwcep->xiso_frame_num;
+ dwcep->desc_addr[i].status.b_iso_out.bs =
+ BS_HOST_READY;
+ dwcep->desc_addr[i].status.b_iso_out.rxsts = 0;
+ dwcep->desc_addr[i].status.b_iso_out.sp =
+ (ddesc_iso[i].length %
+ dwcep->maxpacket) ? 1 : 0;
+ dwcep->desc_addr[i].status.b_iso_out.ioc = 0;
+ dwcep->desc_addr[i].status.b_iso_out.pid = nat + 1;
+ dwcep->desc_addr[i].status.b_iso_out.l = 0;
+
+ /* Process the last descriptor */
+ if (i == ereq->pio_pkt_count - 1) {
+ dwcep->desc_addr[i].status.b_iso_out.ioc = 1;
+ dwcep->desc_addr[i].status.b_iso_out.l = 1;
+ }
+ }
+
+ /* Setup and start the transfer for this endpoint */
+ dwcep->xiso_active_xfers++;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->dev_if->
+ out_ep_regs[dwcep->num]->doepdma,
+ dwcep->dma_desc_addr);
+ diepctl.d32 = 0;
+ diepctl.b.epena = 1;
+ diepctl.b.cnak = 1;
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if->
+ out_ep_regs[dwcep->num]->doepctl, 0,
+ diepctl.d32);
+ }
+
+ } else {
+ ep->stopped = 1;
+ }
+
+ return 0;
+}
+
+/**
+ * - Remove the request from the queue
+ */
+void complete_xiso_ep(dwc_otg_pcd_ep_t * ep)
+{
+ dwc_otg_pcd_request_t *req = NULL;
+ struct dwc_iso_xreq_port *ereq = NULL;
+ struct dwc_iso_pkt_desc_port *ddesc_iso = NULL;
+ dwc_ep_t *dwcep = NULL;
+ int i;
+
+ //DWC_DEBUG();
+ dwcep = &ep->dwc_ep;
+
+ /* Get the first pending request from the queue */
+ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+ req = DWC_CIRCLEQ_FIRST(&ep->queue);
+ if (!req) {
+ DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep);
+ return;
+ }
+ dwcep->xiso_active_xfers--;
+ dwcep->xiso_queued_xfers--;
+ /* Remove this request from the queue */
+ DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry);
+ } else {
+ DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep);
+ return;
+ }
+
+ ep->stopped = 1;
+ ereq = &req->ext_req;
+ ddesc_iso = ereq->per_io_frame_descs;
+
+ if (dwcep->xiso_active_xfers < 0) {
+ DWC_WARN("EP#%d (xiso_active_xfers=%d)", dwcep->num,
+ dwcep->xiso_active_xfers);
+ }
+
+ /* Fill the Isoc descs of portable extended req from dma descriptors */
+ for (i = 0; i < ereq->pio_pkt_count; i++) {
+ if (dwcep->is_in) { /* IN endpoints */
+ ddesc_iso[i].actual_length = ddesc_iso[i].length -
+ dwcep->desc_addr[i].status.b_iso_in.txbytes;
+ ddesc_iso[i].status =
+ dwcep->desc_addr[i].status.b_iso_in.txsts;
+ } else { /* OUT endpoints */
+ ddesc_iso[i].actual_length = ddesc_iso[i].length -
+ dwcep->desc_addr[i].status.b_iso_out.rxbytes;
+ ddesc_iso[i].status =
+ dwcep->desc_addr[i].status.b_iso_out.rxsts;
+ }
+ }
+
+ DWC_SPINUNLOCK(ep->pcd->lock);
+
+ /* Call the completion function in the non-portable logic */
+ ep->pcd->fops->xisoc_complete(ep->pcd, ep->priv, req->priv, 0,
+ &req->ext_req);
+
+ DWC_SPINLOCK(ep->pcd->lock);
+
+ /* Free the request - specific freeing needed for extended request object */
+ dwc_pcd_xiso_ereq_free(ep, req);
+
+ /* Start the next request */
+ dwc_otg_pcd_xiso_start_next_request(ep->pcd, ep);
+
+ return;
+}
+
+/**
+ * Create and initialize the Isoc pkt descriptors of the extended request.
+ *
+ */
+static int dwc_otg_pcd_xiso_create_pkt_descs(dwc_otg_pcd_request_t * req,
+ void *ereq_nonport,
+ int atomic_alloc)
+{
+ struct dwc_iso_xreq_port *ereq = NULL;
+ struct dwc_iso_xreq_port *req_mapped = NULL;
+ struct dwc_iso_pkt_desc_port *ipds = NULL; /* To be created in this function */
+ uint32_t pkt_count;
+ int i;
+
+ ereq = &req->ext_req;
+ req_mapped = (struct dwc_iso_xreq_port *)ereq_nonport;
+ pkt_count = req_mapped->pio_pkt_count;
+
+ /* Create the isoc descs */
+ if (atomic_alloc) {
+ ipds = DWC_ALLOC_ATOMIC(sizeof(*ipds) * pkt_count);
+ } else {
+ ipds = DWC_ALLOC(sizeof(*ipds) * pkt_count);
+ }
+
+ if (!ipds) {
+ DWC_ERROR("Failed to allocate isoc descriptors");
+ return -DWC_E_NO_MEMORY;
+ }
+
+ /* Initialize the extended request fields */
+ ereq->per_io_frame_descs = ipds;
+ ereq->error_count = 0;
+ ereq->pio_alloc_pkt_count = pkt_count;
+ ereq->pio_pkt_count = pkt_count;
+ ereq->tr_sub_flags = req_mapped->tr_sub_flags;
+
+ /* Init the Isoc descriptors */
+ for (i = 0; i < pkt_count; i++) {
+ ipds[i].length = req_mapped->per_io_frame_descs[i].length;
+ ipds[i].offset = req_mapped->per_io_frame_descs[i].offset;
+ ipds[i].status = req_mapped->per_io_frame_descs[i].status; /* 0 */
+ ipds[i].actual_length =
+ req_mapped->per_io_frame_descs[i].actual_length;
+ }
+
+ return 0;
+}
+
+static void prn_ext_request(struct dwc_iso_xreq_port *ereq)
+{
+ struct dwc_iso_pkt_desc_port *xfd = NULL;
+ int i;
+
+ DWC_DEBUG("per_io_frame_descs=%p", ereq->per_io_frame_descs);
+ DWC_DEBUG("tr_sub_flags=%d", ereq->tr_sub_flags);
+ DWC_DEBUG("error_count=%d", ereq->error_count);
+ DWC_DEBUG("pio_alloc_pkt_count=%d", ereq->pio_alloc_pkt_count);
+ DWC_DEBUG("pio_pkt_count=%d", ereq->pio_pkt_count);
+ DWC_DEBUG("res=%d", ereq->res);
+
+ for (i = 0; i < ereq->pio_pkt_count; i++) {
+ xfd = &ereq->per_io_frame_descs[0];
+ DWC_DEBUG("FD #%d", i);
+
+ DWC_DEBUG("xfd->actual_length=%d", xfd->actual_length);
+ DWC_DEBUG("xfd->length=%d", xfd->length);
+ DWC_DEBUG("xfd->offset=%d", xfd->offset);
+ DWC_DEBUG("xfd->status=%d", xfd->status);
+ }
+}
+
+/**
+ *
+ */
+int dwc_otg_pcd_xiso_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
+ uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen,
+ int zero, void *req_handle, int atomic_alloc,
+ void *ereq_nonport)
+{
+ dwc_otg_pcd_request_t *req = NULL;
+ dwc_otg_pcd_ep_t *ep;
+ dwc_irqflags_t flags;
+ int res;
+
+ ep = get_ep_from_handle(pcd, ep_handle);
+ if (!ep) {
+ DWC_WARN("bad ep\n");
+ return -DWC_E_INVALID;
+ }
+
+ /* We support this extension only for DDMA mode */
+ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
+ if (!GET_CORE_IF(pcd)->dma_desc_enable)
+ return -DWC_E_INVALID;
+
+ /* Create a dwc_otg_pcd_request_t object */
+ if (atomic_alloc) {
+ req = DWC_ALLOC_ATOMIC(sizeof(*req));
+ } else {
+ req = DWC_ALLOC(sizeof(*req));
+ }
+
+ if (!req) {
+ return -DWC_E_NO_MEMORY;
+ }
+
+ /* Create the Isoc descs for this request which shall be the exact match
+ * of the structure sent to us from the non-portable logic */
+ res =
+ dwc_otg_pcd_xiso_create_pkt_descs(req, ereq_nonport, atomic_alloc);
+ if (res) {
+ DWC_WARN("Failed to init the Isoc descriptors");
+ DWC_FREE(req);
+ return res;
+ }
+
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+ DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry);
+ req->buf = buf;
+ req->dma = dma_buf;
+ req->length = buflen;
+ req->sent_zlp = zero;
+ req->priv = req_handle;
+
+ //DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+ ep->dwc_ep.dma_addr = dma_buf;
+ ep->dwc_ep.start_xfer_buff = buf;
+ ep->dwc_ep.xfer_buff = buf;
+ ep->dwc_ep.xfer_len = 0;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = buflen;
+
+ /* Add this request to the tail */
+ DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry);
+ ep->dwc_ep.xiso_queued_xfers++;
+
+//DWC_DEBUG("CP_0");
+//DWC_DEBUG("req->ext_req.tr_sub_flags=%d", req->ext_req.tr_sub_flags);
+//prn_ext_request((struct dwc_iso_xreq_port *) ereq_nonport);
+//prn_ext_request(&req->ext_req);
+
+ //DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+ /* If the req->status == ASAP then check if there is any active transfer
+ * for this endpoint. If no active transfers, then get the first entry
+ * from the queue and start that transfer
+ */
+ if (req->ext_req.tr_sub_flags == DWC_EREQ_TF_ASAP) {
+ res = dwc_otg_pcd_xiso_start_next_request(pcd, ep);
+ if (res) {
+ DWC_WARN("Failed to start the next Isoc transfer");
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ DWC_FREE(req);
+ return res;
+ }
+ }
+
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ return 0;
+}
+
+#endif
+/* END ifdef DWC_UTE_PER_IO ***************************************************/
+int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
+ uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen,
+ int zero, void *req_handle, int atomic_alloc)
+{
+ dwc_irqflags_t flags;
+ dwc_otg_pcd_request_t *req;
+ dwc_otg_pcd_ep_t *ep;
+ uint32_t max_transfer;
+
+ ep = get_ep_from_handle(pcd, ep_handle);
+ if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) {
+ DWC_WARN("bad ep\n");
+ return -DWC_E_INVALID;
+ }
+
+ if (atomic_alloc) {
+ req = DWC_ALLOC_ATOMIC(sizeof(*req));
+ } else {
+ req = DWC_ALLOC(sizeof(*req));
+ }
+
+ if (!req) {
+ return -DWC_E_NO_MEMORY;
+ }
+ DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry);
+ if (!GET_CORE_IF(pcd)->core_params->opt) {
+ if (ep->dwc_ep.num != 0) {
+ DWC_ERROR("queue req %p, len %d buf %p\n",
+ req_handle, buflen, buf);
+ }
+ }
+
+ req->buf = buf;
+ req->dma = dma_buf;
+ req->length = buflen;
+ req->sent_zlp = zero;
+ req->priv = req_handle;
+ req->dw_align_buf = NULL;
+ if ((dma_buf & 0x3) && GET_CORE_IF(pcd)->dma_enable
+ && !GET_CORE_IF(pcd)->dma_desc_enable)
+ req->dw_align_buf = DWC_DMA_ALLOC(buflen,
+ &req->dw_align_buf_dma);
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+ /*
+ * After adding request to the queue for IN ISOC wait for In Token Received
+ * when TX FIFO is empty interrupt and for OUT ISOC wait for OUT Token
+ * Received when EP is disabled interrupt to obtain starting microframe
+ * (odd/even) start transfer
+ */
+ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
+ {
+ if (req != 0) {
+ depctl_data_t depctl = {.d32 = DWC_READ_REG32(&pcd->core_if->dev_if->in_ep_regs[ep->dwc_ep.num]->diepctl)};
+ ++pcd->request_pending;
+
+ DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry);
+ if (ep->dwc_ep.is_in)
+ {
+ depctl.b.cnak = 1;
+ DWC_WRITE_REG32(&pcd->core_if->dev_if->in_ep_regs[ep->dwc_ep.num]->diepctl, depctl.d32);
+ }
+
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ }
+ return 0;
+ }
+
+ /*
+ * For EP0 IN without premature status, zlp is required?
+ */
+ if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) {
+ DWC_DEBUGPL(DBG_PCDV, "%d-OUT ZLP\n", ep->dwc_ep.num);
+ //_req->zero = 1;
+ }
+
+ /* Start the transfer */
+ if (DWC_CIRCLEQ_EMPTY(&ep->queue) && !ep->stopped) {
+ /* EP0 Transfer? */
+ if (ep->dwc_ep.num == 0) {
+ switch (pcd->ep0state) {
+ case EP0_IN_DATA_PHASE:
+ DWC_DEBUGPL(DBG_PCD,
+ "%s ep0: EP0_IN_DATA_PHASE\n",
+ __func__);
+ break;
+
+ case EP0_OUT_DATA_PHASE:
+ DWC_DEBUGPL(DBG_PCD,
+ "%s ep0: EP0_OUT_DATA_PHASE\n",
+ __func__);
+ if (pcd->request_config) {
+ /* Complete STATUS PHASE */
+ ep->dwc_ep.is_in = 1;
+ pcd->ep0state = EP0_IN_STATUS_PHASE;
+ }
+ break;
+
+ case EP0_IN_STATUS_PHASE:
+ DWC_DEBUGPL(DBG_PCD,
+ "%s ep0: EP0_IN_STATUS_PHASE\n",
+ __func__);
+ break;
+
+ default:
+ DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n",
+ pcd->ep0state);
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ return -DWC_E_SHUTDOWN;
+ }
+
+ ep->dwc_ep.dma_addr = dma_buf;
+ ep->dwc_ep.start_xfer_buff = buf;
+ ep->dwc_ep.xfer_buff = buf;
+ ep->dwc_ep.xfer_len = buflen;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+ if (zero) {
+ if ((ep->dwc_ep.xfer_len %
+ ep->dwc_ep.maxpacket == 0)
+ && (ep->dwc_ep.xfer_len != 0)) {
+ ep->dwc_ep.sent_zlp = 1;
+ }
+
+ }
+
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd),
+ &ep->dwc_ep);
+ } // non-ep0 endpoints
+ else {
+#ifdef DWC_UTE_CFI
+ if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+ /* store the request length */
+ ep->dwc_ep.cfi_req_len = buflen;
+ pcd->cfi->ops.build_descriptors(pcd->cfi, pcd,
+ ep, req);
+ } else {
+#endif
+ max_transfer =
+ GET_CORE_IF(ep->pcd)->
+ core_params->max_transfer_size;
+
+ /* Setup and start the Transfer */
+ if (req->dw_align_buf){
+ if (ep->dwc_ep.is_in)
+ dwc_memcpy(req->dw_align_buf, buf, buflen);
+ ep->dwc_ep.dma_addr = req->dw_align_buf_dma;
+ ep->dwc_ep.start_xfer_buff = req->dw_align_buf;
+ ep->dwc_ep.xfer_buff = req->dw_align_buf;
+ } else {
+ ep->dwc_ep.dma_addr = dma_buf;
+ ep->dwc_ep.start_xfer_buff = buf;
+ ep->dwc_ep.xfer_buff = buf;
+ }
+ ep->dwc_ep.xfer_len = 0;
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = buflen;
+
+ ep->dwc_ep.maxxfer = max_transfer;
+ if (GET_CORE_IF(pcd)->dma_desc_enable) {
+ uint32_t out_max_xfer =
+ DDMA_MAX_TRANSFER_SIZE -
+ (DDMA_MAX_TRANSFER_SIZE % 4);
+ if (ep->dwc_ep.is_in) {
+ if (ep->dwc_ep.maxxfer >
+ DDMA_MAX_TRANSFER_SIZE) {
+ ep->dwc_ep.maxxfer =
+ DDMA_MAX_TRANSFER_SIZE;
+ }
+ } else {
+ if (ep->dwc_ep.maxxfer >
+ out_max_xfer) {
+ ep->dwc_ep.maxxfer =
+ out_max_xfer;
+ }
+ }
+ }
+ if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) {
+ ep->dwc_ep.maxxfer -=
+ (ep->dwc_ep.maxxfer %
+ ep->dwc_ep.maxpacket);
+ }
+
+ if (zero) {
+ if ((ep->dwc_ep.total_len %
+ ep->dwc_ep.maxpacket == 0)
+ && (ep->dwc_ep.total_len != 0)) {
+ ep->dwc_ep.sent_zlp = 1;
+ }
+ }
+#ifdef DWC_UTE_CFI
+ }
+#endif
+ dwc_otg_ep_start_transfer(GET_CORE_IF(pcd),
+ &ep->dwc_ep);
+ }
+ }
+
+ if (req != 0) {
+ ++pcd->request_pending;
+ DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry);
+ if (ep->dwc_ep.is_in && ep->stopped
+ && !(GET_CORE_IF(pcd)->dma_enable)) {
+ /** @todo NGS Create a function for this. */
+ diepmsk_data_t diepmsk = {.d32 = 0 };
+ diepmsk.b.intktxfemp = 1;
+ if (GET_CORE_IF(pcd)->multiproc_int_enable) {
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if->
+ dev_global_regs->
+ diepeachintmsk[ep->dwc_ep.num],
+ 0, diepmsk.d32);
+ } else {
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if->
+ dev_global_regs->diepmsk, 0,
+ diepmsk.d32);
+ }
+
+ }
+ }
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+ return 0;
+}
+
+int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle)
+{
+ dwc_irqflags_t flags;
+ dwc_otg_pcd_request_t *req;
+ dwc_otg_pcd_ep_t *ep;
+
+ ep = get_ep_from_handle(pcd, ep_handle);
+ if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) {
+ DWC_WARN("bad argument\n");
+ return -DWC_E_INVALID;
+ }
+
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+ /* make sure it's actually queued on this endpoint */
+ DWC_CIRCLEQ_FOREACH(req, &ep->queue, queue_entry) {
+ if (req->priv == (void *)req_handle) {
+ break;
+ }
+ }
+
+ if (req->priv != (void *)req_handle) {
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ return -DWC_E_INVALID;
+ }
+
+ if (!DWC_CIRCLEQ_EMPTY_ENTRY(req, queue_entry)) {
+ dwc_otg_request_done(ep, req, -DWC_E_RESTART);
+ } else {
+ req = NULL;
+ }
+
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+ return req ? 0 : -DWC_E_SHUTDOWN;
+
+}
+
+int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value)
+{
+ dwc_otg_pcd_ep_t *ep;
+ dwc_irqflags_t flags;
+ int retval = 0;
+
+ ep = get_ep_from_handle(pcd, ep_handle);
+
+ if (!ep || (!ep->desc && ep != &pcd->ep0) ||
+ (ep->desc && (ep->desc->bmAttributes == UE_ISOCHRONOUS))) {
+ DWC_WARN("%s, bad ep\n", __func__);
+ return -DWC_E_INVALID;
+ }
+
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+ DWC_WARN("%d %s XFer In process\n", ep->dwc_ep.num,
+ ep->dwc_ep.is_in ? "IN" : "OUT");
+ retval = -DWC_E_AGAIN;
+ } else if (value == 0) {
+ dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
+ } else if (value == 1) {
+ if (ep->dwc_ep.is_in == 1 && GET_CORE_IF(pcd)->dma_desc_enable) {
+ dtxfsts_data_t txstatus;
+ fifosize_data_t txfifosize;
+
+ txfifosize.d32 =
+ DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs->
+ dtxfsiz[ep->dwc_ep.tx_fifo_num]);
+ txstatus.d32 =
+ DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if->
+ in_ep_regs[ep->dwc_ep.num]->dtxfsts);
+
+ if (txstatus.b.txfspcavail < txfifosize.b.depth) {
+ DWC_WARN("%s() Data In Tx Fifo\n", __func__);
+ retval = -DWC_E_AGAIN;
+ } else {
+ if (ep->dwc_ep.num == 0) {
+ pcd->ep0state = EP0_STALL;
+ }
+
+ ep->stopped = 1;
+ dwc_otg_ep_set_stall(GET_CORE_IF(pcd),
+ &ep->dwc_ep);
+ }
+ } else {
+ if (ep->dwc_ep.num == 0) {
+ pcd->ep0state = EP0_STALL;
+ }
+
+ ep->stopped = 1;
+ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
+ }
+ } else if (value == 2) {
+ ep->dwc_ep.stall_clear_flag = 0;
+ } else if (value == 3) {
+ ep->dwc_ep.stall_clear_flag = 1;
+ }
+
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+ return retval;
+}
+
+/**
+ * This function initiates remote wakeup of the host from suspend state.
+ */
+void dwc_otg_pcd_rem_wkup_from_suspend(dwc_otg_pcd_t * pcd, int set)
+{
+ dctl_data_t dctl = { 0 };
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dsts_data_t dsts;
+
+ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+ if (!dsts.b.suspsts) {
+ DWC_WARN("Remote wakeup while is not in suspend state\n");
+ }
+ /* Check if DEVICE_REMOTE_WAKEUP feature enabled */
+ if (pcd->remote_wakeup_enable) {
+ if (set) {
+
+ if (core_if->adp_enable) {
+ gpwrdn_data_t gpwrdn;
+
+ dwc_otg_adp_probe_stop(core_if);
+
+ /* Mask SRP detected interrupt from Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.srp_det_msk = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, gpwrdn.d32, 0);
+
+ /* Disable Power Down Logic */
+ gpwrdn.d32 = 0;
+ gpwrdn.b.pmuactv = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->
+ gpwrdn, gpwrdn.d32, 0);
+
+ /*
+ * Initialize the Core for Device mode.
+ */
+ core_if->op_state = B_PERIPHERAL;
+ dwc_otg_core_init(core_if);
+ dwc_otg_enable_global_interrupts(core_if);
+ cil_pcd_start(core_if);
+
+ dwc_otg_initiate_srp(core_if);
+ }
+
+ dctl.b.rmtwkupsig = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->
+ dev_global_regs->dctl, 0, dctl.d32);
+ DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n");
+
+ dwc_mdelay(2);
+ DWC_MODIFY_REG32(&core_if->dev_if->
+ dev_global_regs->dctl, dctl.d32, 0);
+ DWC_DEBUGPL(DBG_PCD, "Clear Remote Wakeup\n");
+ }
+ } else {
+ DWC_DEBUGPL(DBG_PCD, "Remote Wakeup is disabled\n");
+ }
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/**
+ * This function initiates remote wakeup of the host from L1 sleep state.
+ */
+void dwc_otg_pcd_rem_wkup_from_sleep(dwc_otg_pcd_t * pcd, int set)
+{
+ glpmcfg_data_t lpmcfg;
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+
+ /* Check if we are in L1 state */
+ if (!lpmcfg.b.prt_sleep_sts) {
+ DWC_DEBUGPL(DBG_PCD, "Device is not in sleep state\n");
+ return;
+ }
+
+ /* Check if host allows remote wakeup */
+ if (!lpmcfg.b.rem_wkup_en) {
+ DWC_DEBUGPL(DBG_PCD, "Host does not allow remote wakeup\n");
+ return;
+ }
+
+ /* Check if Resume OK */
+ if (!lpmcfg.b.sleep_state_resumeok) {
+ DWC_DEBUGPL(DBG_PCD, "Sleep state resume is not OK\n");
+ return;
+ }
+
+ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+ lpmcfg.b.en_utmi_sleep = 0;
+ lpmcfg.b.hird_thres &= (~(1 << 4));
+ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+
+ if (set) {
+ dctl_data_t dctl = {.d32 = 0 };
+ dctl.b.rmtwkupsig = 1;
+ /* Set RmtWkUpSig bit to start remote wakup signaling.
+ * Hardware will automatically clear this bit.
+ */
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl,
+ 0, dctl.d32);
+ DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n");
+ }
+
+}
+#endif
+
+/**
+ * Performs remote wakeup.
+ */
+void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_irqflags_t flags;
+ if (dwc_otg_is_device_mode(core_if)) {
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ if (core_if->lx_state == DWC_OTG_L1) {
+ dwc_otg_pcd_rem_wkup_from_sleep(pcd, set);
+ } else {
+#endif
+ dwc_otg_pcd_rem_wkup_from_suspend(pcd, set);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ }
+#endif
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+ }
+ return;
+}
+
+void dwc_otg_pcd_disconnect_us(dwc_otg_pcd_t * pcd, int no_of_usecs)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dctl_data_t dctl = { 0 };
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ dctl.b.sftdiscon = 1;
+ DWC_PRINTF("Soft disconnect for %d useconds\n",no_of_usecs);
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+ dwc_udelay(no_of_usecs);
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32,0);
+
+ } else{
+ DWC_PRINTF("NOT SUPPORTED IN HOST MODE\n");
+ }
+ return;
+
+}
+
+int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd)
+{
+ dsts_data_t dsts;
+ gotgctl_data_t gotgctl;
+
+ /*
+ * This function starts the Protocol if no session is in progress. If
+ * a session is already in progress, but the device is suspended,
+ * remote wakeup signaling is started.
+ */
+
+ /* Check if valid session */
+ gotgctl.d32 =
+ DWC_READ_REG32(&(GET_CORE_IF(pcd)->core_global_regs->gotgctl));
+ if (gotgctl.b.bsesvld) {
+ /* Check if suspend state */
+ dsts.d32 =
+ DWC_READ_REG32(&
+ (GET_CORE_IF(pcd)->dev_if->
+ dev_global_regs->dsts));
+ if (dsts.b.suspsts) {
+ dwc_otg_pcd_remote_wakeup(pcd, 1);
+ }
+ } else {
+ dwc_otg_pcd_initiate_srp(pcd);
+ }
+
+ return 0;
+
+}
+
+/**
+ * Start the SRP timer to detect when the SRP does not complete within
+ * 6 seconds.
+ *
+ * @param pcd the pcd structure.
+ */
+void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd)
+{
+ dwc_irqflags_t flags;
+ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+ dwc_otg_initiate_srp(GET_CORE_IF(pcd));
+ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+}
+
+int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd)
+{
+ return dwc_otg_get_frame_number(GET_CORE_IF(pcd));
+}
+
+int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd)
+{
+ return GET_CORE_IF(pcd)->core_params->lpm_enable;
+}
+
+uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd)
+{
+ return pcd->b_hnp_enable;
+}
+
+uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd)
+{
+ return pcd->a_hnp_support;
+}
+
+uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd)
+{
+ return pcd->a_alt_hnp_support;
+}
+
+int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd)
+{
+ return pcd->remote_wakeup_enable;
+}
+
+#endif /* DWC_HOST_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd.h b/drivers/usb/dwc_otg/dwc_otg_pcd.h
new file mode 100644
index 0000000..4b2dde9
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_pcd.h
@@ -0,0 +1,262 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.h $
+ * $Revision: #46 $
+ * $Date: 2011/10/20 $
+ * $Change: 1870124 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+#if !defined(__DWC_PCD_H__)
+#define __DWC_PCD_H__
+
+#include "dwc_otg_os_dep.h"
+#include "usb.h"
+#include "dwc_otg_cil.h"
+#include "dwc_otg_pcd_if.h"
+struct cfiobject;
+
+/**
+ * @file
+ *
+ * This file contains the structures, constants, and interfaces for
+ * the Perpherial Contoller Driver (PCD).
+ *
+ * The Peripheral Controller Driver (PCD) for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used. For
+ * the Mass Storage Function driver the File-backed USB Storage Gadget
+ * (FBS) driver will be used. The FBS driver supports the
+ * Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only
+ * transports.
+ *
+ */
+
+/** Invalid DMA Address */
+#define DWC_DMA_ADDR_INVALID (~(dwc_dma_t)0)
+
+/** Max Transfer size for any EP */
+#define DDMA_MAX_TRANSFER_SIZE 65535
+
+/**
+ * Get the pointer to the core_if from the pcd pointer.
+ */
+#define GET_CORE_IF( _pcd ) (_pcd->core_if)
+
+/**
+ * States of EP0.
+ */
+typedef enum ep0_state {
+ EP0_DISCONNECT, /* no host */
+ EP0_IDLE,
+ EP0_IN_DATA_PHASE,
+ EP0_OUT_DATA_PHASE,
+ EP0_IN_STATUS_PHASE,
+ EP0_OUT_STATUS_PHASE,
+ EP0_STALL,
+} ep0state_e;
+
+/** Fordward declaration.*/
+struct dwc_otg_pcd;
+
+/** DWC_otg iso request structure.
+ *
+ */
+typedef struct usb_iso_request dwc_otg_pcd_iso_request_t;
+
+#ifdef DWC_UTE_PER_IO
+
+/**
+ * This shall be the exact analogy of the same type structure defined in the
+ * usb_gadget.h. Each descriptor contains
+ */
+struct dwc_iso_pkt_desc_port {
+ uint32_t offset;
+ uint32_t length; /* expected length */
+ uint32_t actual_length;
+ uint32_t status;
+};
+
+struct dwc_iso_xreq_port {
+ /** transfer/submission flag */
+ uint32_t tr_sub_flags;
+ /** Start the request ASAP */
+#define DWC_EREQ_TF_ASAP 0x00000002
+ /** Just enqueue the request w/o initiating a transfer */
+#define DWC_EREQ_TF_ENQUEUE 0x00000004
+
+ /**
+ * count of ISO packets attached to this request - shall
+ * not exceed the pio_alloc_pkt_count
+ */
+ uint32_t pio_pkt_count;
+ /** count of ISO packets allocated for this request */
+ uint32_t pio_alloc_pkt_count;
+ /** number of ISO packet errors */
+ uint32_t error_count;
+ /** reserved for future extension */
+ uint32_t res;
+ /** Will be allocated and freed in the UTE gadget and based on the CFC value */
+ struct dwc_iso_pkt_desc_port *per_io_frame_descs;
+};
+#endif
+/** DWC_otg request structure.
+ * This structure is a list of requests.
+ */
+typedef struct dwc_otg_pcd_request {
+ void *priv;
+ void *buf;
+ dwc_dma_t dma;
+ uint32_t length;
+ uint32_t actual;
+ unsigned sent_zlp:1;
+ /**
+ * Used instead of original buffer if
+ * it(physical address) is not dword-aligned.
+ **/
+ uint8_t *dw_align_buf;
+ dwc_dma_t dw_align_buf_dma;
+
+ DWC_CIRCLEQ_ENTRY(dwc_otg_pcd_request) queue_entry;
+#ifdef DWC_UTE_PER_IO
+ struct dwc_iso_xreq_port ext_req;
+ //void *priv_ereq_nport; /* */
+#endif
+} dwc_otg_pcd_request_t;
+
+DWC_CIRCLEQ_HEAD(req_list, dwc_otg_pcd_request);
+
+/** PCD EP structure.
+ * This structure describes an EP, there is an array of EPs in the PCD
+ * structure.
+ */
+typedef struct dwc_otg_pcd_ep {
+ /** USB EP Descriptor */
+ const usb_endpoint_descriptor_t *desc;
+
+ /** queue of dwc_otg_pcd_requests. */
+ struct req_list queue;
+ unsigned stopped:1;
+ unsigned disabling:1;
+ unsigned dma:1;
+ unsigned queue_sof:1;
+
+#ifdef DWC_EN_ISOC
+ /** ISOC req handle passed */
+ void *iso_req_handle;
+#endif //_EN_ISOC_
+
+ /** DWC_otg ep data. */
+ dwc_ep_t dwc_ep;
+
+ /** Pointer to PCD */
+ struct dwc_otg_pcd *pcd;
+
+ void *priv;
+} dwc_otg_pcd_ep_t;
+
+/** DWC_otg PCD Structure.
+ * This structure encapsulates the data for the dwc_otg PCD.
+ */
+struct dwc_otg_pcd {
+ const struct dwc_otg_pcd_function_ops *fops;
+ /** The DWC otg device pointer */
+ struct dwc_otg_device *otg_dev;
+ /** Core Interface */
+ dwc_otg_core_if_t *core_if;
+ /** State of EP0 */
+ ep0state_e ep0state;
+ /** EP0 Request is pending */
+ unsigned ep0_pending:1;
+ /** Indicates when SET CONFIGURATION Request is in process */
+ unsigned request_config:1;
+ /** The state of the Remote Wakeup Enable. */
+ unsigned remote_wakeup_enable:1;
+ /** The state of the B-Device HNP Enable. */
+ unsigned b_hnp_enable:1;
+ /** The state of A-Device HNP Support. */
+ unsigned a_hnp_support:1;
+ /** The state of the A-Device Alt HNP support. */
+ unsigned a_alt_hnp_support:1;
+ /** Count of pending Requests */
+ unsigned request_pending;
+
+ /** SETUP packet for EP0
+ * This structure is allocated as a DMA buffer on PCD initialization
+ * with enough space for up to 3 setup packets.
+ */
+ union {
+ usb_device_request_t req;
+ uint32_t d32[2];
+ } *setup_pkt;
+
+ dwc_dma_t setup_pkt_dma_handle;
+
+ /** 2-byte dma buffer used to return status from GET_STATUS */
+ uint16_t *status_buf;
+ dwc_dma_t status_buf_dma_handle;
+
+ /** EP0 */
+ dwc_otg_pcd_ep_t ep0;
+
+ /** Array of IN EPs. */
+ dwc_otg_pcd_ep_t in_ep[MAX_EPS_CHANNELS - 1];
+ /** Array of OUT EPs. */
+ dwc_otg_pcd_ep_t out_ep[MAX_EPS_CHANNELS - 1];
+ /** number of valid EPs in the above array. */
+// unsigned num_eps : 4;
+ dwc_spinlock_t *lock;
+
+ /** Tasklet to defer starting of TEST mode transmissions until
+ * Status Phase has been completed.
+ */
+ dwc_tasklet_t *test_mode_tasklet;
+
+ /** Tasklet to delay starting of xfer in DMA mode */
+ dwc_tasklet_t *start_xfer_tasklet;
+
+ /** The test mode to enter when the tasklet is executed. */
+ unsigned test_mode;
+ /** The cfi_api structure that implements most of the CFI API
+ * and OTG specific core configuration functionality
+ */
+#ifdef DWC_UTE_CFI
+ struct cfiobject *cfi;
+#endif
+
+};
+
+//FIXME this functions should be static, and this prototypes should be removed
+extern void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep);
+extern void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep,
+ dwc_otg_pcd_request_t * req, int32_t status);
+
+void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep,
+ void *req_handle);
+
+extern void do_test_mode(void *data);
+#endif
+#endif /* DWC_HOST_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd_if.h b/drivers/usb/dwc_otg/dwc_otg_pcd_if.h
new file mode 100644
index 0000000..6283867
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_pcd_if.h
@@ -0,0 +1,357 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_if.h $
+ * $Revision: #11 $
+ * $Date: 2011/10/26 $
+ * $Change: 1873028 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+
+#if !defined(__DWC_PCD_IF_H__)
+#define __DWC_PCD_IF_H__
+
+//#include "dwc_os.h"
+#include "dwc_otg_core_if.h"
+
+/** @file
+ * This file defines DWC_OTG PCD Core API.
+ */
+
+struct dwc_otg_pcd;
+typedef struct dwc_otg_pcd dwc_otg_pcd_t;
+
+/** Maxpacket size for EP0 */
+#define MAX_EP0_SIZE 64
+/** Maxpacket size for any EP */
+#define MAX_PACKET_SIZE 1024
+
+/** @name Function Driver Callbacks */
+/** @{ */
+
+/** This function will be called whenever a previously queued request has
+ * completed. The status value will be set to -DWC_E_SHUTDOWN to indicated a
+ * failed or aborted transfer, or -DWC_E_RESTART to indicate the device was reset,
+ * or -DWC_E_TIMEOUT to indicate it timed out, or -DWC_E_INVALID to indicate invalid
+ * parameters. */
+typedef int (*dwc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle, int32_t status,
+ uint32_t actual);
+/**
+ * This function will be called whenever a previousle queued ISOC request has
+ * completed. Count of ISOC packets could be read using dwc_otg_pcd_get_iso_packet_count
+ * function.
+ * The status of each ISOC packet could be read using dwc_otg_pcd_get_iso_packet_*
+ * functions.
+ */
+typedef int (*dwc_isoc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle, int proc_buf_num);
+/** This function should handle any SETUP request that cannot be handled by the
+ * PCD Core. This includes most GET_DESCRIPTORs, SET_CONFIGS, Any
+ * class-specific requests, etc. The function must non-blocking.
+ *
+ * Returns 0 on success.
+ * Returns -DWC_E_NOT_SUPPORTED if the request is not supported.
+ * Returns -DWC_E_INVALID if the setup request had invalid parameters or bytes.
+ * Returns -DWC_E_SHUTDOWN on any other error. */
+typedef int (*dwc_setup_cb_t) (dwc_otg_pcd_t * pcd, uint8_t * bytes);
+/** This is called whenever the device has been disconnected. The function
+ * driver should take appropriate action to clean up all pending requests in the
+ * PCD Core, remove all endpoints (except ep0), and initialize back to reset
+ * state. */
+typedef int (*dwc_disconnect_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called when device has been connected. */
+typedef int (*dwc_connect_cb_t) (dwc_otg_pcd_t * pcd, int speed);
+/** This function is called when device has been suspended */
+typedef int (*dwc_suspend_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called when device has received LPM tokens, i.e.
+ * device has been sent to sleep state. */
+typedef int (*dwc_sleep_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called when device has been resumed
+ * from suspend(L2) or L1 sleep state. */
+typedef int (*dwc_resume_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called whenever hnp params has been changed.
+ * User can call get_b_hnp_enable, get_a_hnp_support, get_a_alt_hnp_support functions
+ * to get hnp parameters. */
+typedef int (*dwc_hnp_params_changed_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called whenever USB RESET is detected. */
+typedef int (*dwc_reset_cb_t) (dwc_otg_pcd_t * pcd);
+
+typedef int (*cfi_setup_cb_t) (dwc_otg_pcd_t * pcd, void *ctrl_req_bytes);
+
+/**
+ *
+ * @param ep_handle Void pointer to the usb_ep structure
+ * @param ereq_port Pointer to the extended request structure created in the
+ * portable part.
+ */
+typedef int (*xiso_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle, int32_t status,
+ void *ereq_port);
+/** Function Driver Ops Data Structure */
+struct dwc_otg_pcd_function_ops {
+ dwc_connect_cb_t connect;
+ dwc_disconnect_cb_t disconnect;
+ dwc_setup_cb_t setup;
+ dwc_completion_cb_t complete;
+ dwc_isoc_completion_cb_t isoc_complete;
+ dwc_suspend_cb_t suspend;
+ dwc_sleep_cb_t sleep;
+ dwc_resume_cb_t resume;
+ dwc_reset_cb_t reset;
+ dwc_hnp_params_changed_cb_t hnp_changed;
+ cfi_setup_cb_t cfi_setup;
+#ifdef DWC_UTE_PER_IO
+ xiso_completion_cb_t xisoc_complete;
+#endif
+};
+/** @} */
+
+/** @name Function Driver Functions */
+/** @{ */
+
+/** Call this function to get pointer on dwc_otg_pcd_t,
+ * this pointer will be used for all PCD API functions.
+ *
+ * @param core_if The DWC_OTG Core
+ */
+extern dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_core_if_t * core_if);
+
+/** Frees PCD allocated by dwc_otg_pcd_init
+ *
+ * @param pcd The PCD
+ */
+extern void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd);
+
+/** Call this to bind the function driver to the PCD Core.
+ *
+ * @param pcd Pointer on dwc_otg_pcd_t returned by dwc_otg_pcd_init function.
+ * @param fops The Function Driver Ops data structure containing pointers to all callbacks.
+ */
+extern void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd,
+ const struct dwc_otg_pcd_function_ops *fops);
+
+/** Enables an endpoint for use. This function enables an endpoint in
+ * the PCD. The endpoint is described by the ep_desc which has the
+ * same format as a USB ep descriptor. The ep_handle parameter is used to refer
+ * to the endpoint from other API functions and in callbacks. Normally this
+ * should be called after a SET_CONFIGURATION/SET_INTERFACE to configure the
+ * core for that interface.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error ocurred.
+ * Returns 0 on success.
+ *
+ * @param pcd The PCD
+ * @param ep_desc Endpoint descriptor
+ * @param usb_ep Handle on endpoint, that will be used to identify endpoint.
+ */
+extern int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd,
+ const uint8_t * ep_desc, void *usb_ep);
+
+/** Disable the endpoint referenced by ep_handle.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error occurred.
+ * Returns 0 on success. */
+extern int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle);
+
+/** Queue a data transfer request on the endpoint referenced by ep_handle.
+ * After the transfer is completes, the complete callback will be called with
+ * the request status.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param buf The buffer for the data
+ * @param dma_buf The DMA buffer for the data
+ * @param buflen The length of the data transfer
+ * @param zero Specifies whether to send zero length last packet.
+ * @param req_handle Set this handle to any value to use to reference this
+ * request in the ep_dequeue function or from the complete callback
+ * @param atomic_alloc If driver need to perform atomic allocations
+ * for internal data structures.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error ocurred.
+ * Returns 0 on success. */
+extern int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
+ uint8_t * buf, dwc_dma_t dma_buf,
+ uint32_t buflen, int zero, void *req_handle,
+ int atomic_alloc);
+#ifdef DWC_UTE_PER_IO
+/**
+ *
+ * @param ereq_nonport Pointer to the extended request part of the
+ * usb_request structure defined in usb_gadget.h file.
+ */
+extern int dwc_otg_pcd_xiso_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
+ uint8_t * buf, dwc_dma_t dma_buf,
+ uint32_t buflen, int zero,
+ void *req_handle, int atomic_alloc,
+ void *ereq_nonport);
+
+#endif
+
+/** De-queue the specified data transfer that has not yet completed.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error ocurred.
+ * Returns 0 on success. */
+extern int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle);
+
+/** Halt (STALL) an endpoint or clear it.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error ocurred.
+ * Returns -DWC_E_AGAIN if the STALL cannot be sent and must be tried again later
+ * Returns 0 on success. */
+extern int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value);
+
+/** This function should be called on every hardware interrupt */
+extern int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd);
+
+/** This function returns current frame number */
+extern int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd);
+
+/**
+ * Start isochronous transfers on the endpoint referenced by ep_handle.
+ * For isochronous transfers duble buffering is used.
+ * After processing each of buffers comlete callback will be called with
+ * status for each transaction.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param buf0 The virtual address of first data buffer
+ * @param buf1 The virtual address of second data buffer
+ * @param dma0 The DMA address of first data buffer
+ * @param dma1 The DMA address of second data buffer
+ * @param sync_frame Data pattern frame number
+ * @param dp_frame Data size for pattern frame
+ * @param data_per_frame Data size for regular frame
+ * @param start_frame Frame number to start transfers, if -1 then start transfers ASAP.
+ * @param buf_proc_intrvl Interval of ISOC Buffer processing
+ * @param req_handle Handle of ISOC request
+ * @param atomic_alloc Specefies whether to perform atomic allocation for
+ * internal data structures.
+ *
+ * Returns -DWC_E_NO_MEMORY if there is no enough memory.
+ * Returns -DWC_E_INVALID if incorrect arguments are passed to the function.
+ * Returns -DW_E_SHUTDOWN for any other error.
+ * Returns 0 on success
+ */
+extern int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle,
+ uint8_t * buf0, uint8_t * buf1,
+ dwc_dma_t dma0, dwc_dma_t dma1,
+ int sync_frame, int dp_frame,
+ int data_per_frame, int start_frame,
+ int buf_proc_intrvl, void *req_handle,
+ int atomic_alloc);
+
+/** Stop ISOC transfers on endpoint referenced by ep_handle.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param req_handle Handle of ISOC request
+ *
+ * Returns -DWC_E_INVALID if incorrect arguments are passed to the function
+ * Returns 0 on success
+ */
+int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle);
+
+/** Get ISOC packet status.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param iso_req_handle Isochronoush request handle
+ * @param packet Number of packet
+ * @param status Out parameter for returning status
+ * @param actual Out parameter for returning actual length
+ * @param offset Out parameter for returning offset
+ *
+ */
+extern void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd,
+ void *ep_handle,
+ void *iso_req_handle, int packet,
+ int *status, int *actual,
+ int *offset);
+
+/** Get ISOC packet count.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param iso_req_handle
+ */
+extern int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd,
+ void *ep_handle,
+ void *iso_req_handle);
+
+/** This function starts the SRP Protocol if no session is in progress. If
+ * a session is already in progress, but the device is suspended,
+ * remote wakeup signaling is started.
+ */
+extern int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd);
+
+/** This function returns 1 if LPM support is enabled, and 0 otherwise. */
+extern int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd);
+
+/** This function returns 1 if remote wakeup is allowed and 0, otherwise. */
+extern int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd);
+
+/** Initiate SRP */
+extern void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd);
+
+/** Starts remote wakeup signaling. */
+extern void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set);
+
+/** Starts micorsecond soft disconnect. */
+extern void dwc_otg_pcd_disconnect_us(dwc_otg_pcd_t * pcd, int no_of_usecs);
+/** This function returns whether device is dualspeed.*/
+extern uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd);
+
+/** This function returns whether device is otg. */
+extern uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd);
+
+/** These functions allow to get hnp parameters */
+extern uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd);
+extern uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd);
+extern uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd);
+
+/** CFI specific Interface functions */
+/** Allocate a cfi buffer */
+extern uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep,
+ dwc_dma_t * addr, size_t buflen,
+ int flags);
+
+/******************************************************************************/
+
+/** @} */
+
+#endif /* __DWC_PCD_IF_H__ */
+
+#endif /* DWC_HOST_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c b/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c
new file mode 100644
index 0000000..32286fd
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c
@@ -0,0 +1,4815 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_intr.c $
+ * $Revision: #113 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871160 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+
+#include "dwc_otg_pcd.h"
+
+#ifdef DWC_UTE_CFI
+#include "dwc_otg_cfi.h"
+#endif
+
+#ifdef DWC_UTE_PER_IO
+extern void complete_xiso_ep(dwc_otg_pcd_ep_t * ep);
+#endif
+//#define PRINT_CFI_DMA_DESCS
+
+#define DEBUG_EP0
+
+/**
+ * This function updates OTG.
+ */
+static void dwc_otg_pcd_update_otg(dwc_otg_pcd_t * pcd, const unsigned reset)
+{
+
+ if (reset) {
+ pcd->b_hnp_enable = 0;
+ pcd->a_hnp_support = 0;
+ pcd->a_alt_hnp_support = 0;
+ }
+
+ if (pcd->fops->hnp_changed) {
+ pcd->fops->hnp_changed(pcd);
+ }
+}
+
+/** @file
+ * This file contains the implementation of the PCD Interrupt handlers.
+ *
+ * The PCD handles the device interrupts. Many conditions can cause a
+ * device interrupt. When an interrupt occurs, the device interrupt
+ * service routine determines the cause of the interrupt and
+ * dispatches handling to the appropriate function. These interrupt
+ * handling functions are described below.
+ * All interrupt registers are processed from LSB to MSB.
+ */
+
+/**
+ * This function prints the ep0 state for debug purposes.
+ */
+static inline void print_ep0_state(dwc_otg_pcd_t * pcd)
+{
+#ifdef DEBUG
+ char str[40];
+
+ switch (pcd->ep0state) {
+ case EP0_DISCONNECT:
+ dwc_strcpy(str, "EP0_DISCONNECT");
+ break;
+ case EP0_IDLE:
+ dwc_strcpy(str, "EP0_IDLE");
+ break;
+ case EP0_IN_DATA_PHASE:
+ dwc_strcpy(str, "EP0_IN_DATA_PHASE");
+ break;
+ case EP0_OUT_DATA_PHASE:
+ dwc_strcpy(str, "EP0_OUT_DATA_PHASE");
+ break;
+ case EP0_IN_STATUS_PHASE:
+ dwc_strcpy(str, "EP0_IN_STATUS_PHASE");
+ break;
+ case EP0_OUT_STATUS_PHASE:
+ dwc_strcpy(str, "EP0_OUT_STATUS_PHASE");
+ break;
+ case EP0_STALL:
+ dwc_strcpy(str, "EP0_STALL");
+ break;
+ default:
+ dwc_strcpy(str, "EP0_INVALID");
+ }
+
+ DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, pcd->ep0state);
+#endif
+}
+
+/**
+ * This function calculate the size of the payload in the memory
+ * for out endpoints and prints size for debug purposes(used in
+ * 2.93a DevOutNak feature).
+ */
+static inline void print_memory_payload(dwc_otg_pcd_t * pcd, dwc_ep_t * ep)
+{
+#ifdef DEBUG
+ deptsiz_data_t deptsiz_init = {.d32 = 0 };
+ deptsiz_data_t deptsiz_updt = {.d32 = 0 };
+ int pack_num;
+ unsigned payload;
+
+ deptsiz_init.d32 = pcd->core_if->start_doeptsiz_val[ep->num];
+ deptsiz_updt.d32 =
+ DWC_READ_REG32(&pcd->core_if->dev_if->
+ out_ep_regs[ep->num]->doeptsiz);
+ /* Payload will be */
+ payload = deptsiz_init.b.xfersize - deptsiz_updt.b.xfersize;
+ /* Packet count is decremented every time a packet
+ * is written to the RxFIFO not in to the external memory
+ * So, if payload == 0, then it means no packet was sent to ext memory*/
+ pack_num = (!payload) ? 0 : (deptsiz_init.b.pktcnt - deptsiz_updt.b.pktcnt);
+ DWC_DEBUGPL(DBG_PCDV,
+ "Payload for EP%d-%s\n",
+ ep->num, (ep->is_in ? "IN" : "OUT"));
+ DWC_DEBUGPL(DBG_PCDV,
+ "Number of transfered bytes = 0x%08x\n", payload);
+ DWC_DEBUGPL(DBG_PCDV,
+ "Number of transfered packets = %d\n", pack_num);
+#endif
+}
+
+
+#ifdef DWC_UTE_CFI
+static inline void print_desc(struct dwc_otg_dma_desc *ddesc,
+ const uint8_t * epname, int descnum)
+{
+ CFI_INFO
+ ("%s DMA_DESC(%d) buf=0x%08x bytes=0x%04x; sp=0x%x; l=0x%x; sts=0x%02x; bs=0x%02x\n",
+ epname, descnum, ddesc->buf, ddesc->status.b.bytes,
+ ddesc->status.b.sp, ddesc->status.b.l, ddesc->status.b.sts,
+ ddesc->status.b.bs);
+}
+#endif
+
+/**
+ * This function returns pointer to in ep struct with number ep_num
+ */
+static inline dwc_otg_pcd_ep_t *get_in_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num)
+{
+ int i;
+ int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+ if (ep_num == 0) {
+ return &pcd->ep0;
+ } else {
+ for (i = 0; i < num_in_eps; ++i) {
+ if (pcd->in_ep[i].dwc_ep.num == ep_num)
+ return &pcd->in_ep[i];
+ }
+ return 0;
+ }
+}
+
+/**
+ * This function returns pointer to out ep struct with number ep_num
+ */
+static inline dwc_otg_pcd_ep_t *get_out_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num)
+{
+ int i;
+ int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+ if (ep_num == 0) {
+ return &pcd->ep0;
+ } else {
+ for (i = 0; i < num_out_eps; ++i) {
+ if (pcd->out_ep[i].dwc_ep.num == ep_num)
+ return &pcd->out_ep[i];
+ }
+ return 0;
+ }
+}
+
+/**
+ * This functions gets a pointer to an EP from the wIndex address
+ * value of the control request.
+ */
+dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex)
+{
+ dwc_otg_pcd_ep_t *ep;
+ uint32_t ep_num = UE_GET_ADDR(wIndex);
+
+ if (ep_num == 0) {
+ ep = &pcd->ep0;
+ } else if (UE_GET_DIR(wIndex) == UE_DIR_IN) { /* in ep */
+ ep = &pcd->in_ep[ep_num - 1];
+ } else {
+ ep = &pcd->out_ep[ep_num - 1];
+ }
+
+ return ep;
+}
+
+/**
+ * This function checks the EP request queue, if the queue is not
+ * empty the next request is started.
+ */
+void start_next_request(dwc_otg_pcd_ep_t * ep)
+{
+ dwc_otg_pcd_request_t *req = 0;
+ uint32_t max_transfer =
+ GET_CORE_IF(ep->pcd)->core_params->max_transfer_size;
+
+#ifdef DWC_UTE_CFI
+ struct dwc_otg_pcd *pcd;
+ pcd = ep->pcd;
+#endif
+
+ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+ req = DWC_CIRCLEQ_FIRST(&ep->queue);
+
+#ifdef DWC_UTE_CFI
+ if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+ ep->dwc_ep.cfi_req_len = req->length;
+ pcd->cfi->ops.build_descriptors(pcd->cfi, pcd, ep, req);
+ } else {
+#endif
+ /* Setup and start the Transfer */
+ if (req->dw_align_buf) {
+ ep->dwc_ep.dma_addr = req->dw_align_buf_dma;
+ ep->dwc_ep.start_xfer_buff = req->dw_align_buf;
+ ep->dwc_ep.xfer_buff = req->dw_align_buf;
+ } else {
+ ep->dwc_ep.dma_addr = req->dma;
+ ep->dwc_ep.start_xfer_buff = req->buf;
+ ep->dwc_ep.xfer_buff = req->buf;
+ }
+ ep->dwc_ep.sent_zlp = 0;
+ ep->dwc_ep.total_len = req->length;
+ ep->dwc_ep.xfer_len = 0;
+ ep->dwc_ep.xfer_count = 0;
+
+ ep->dwc_ep.maxxfer = max_transfer;
+ if (GET_CORE_IF(ep->pcd)->dma_desc_enable) {
+ uint32_t out_max_xfer = DDMA_MAX_TRANSFER_SIZE
+ - (DDMA_MAX_TRANSFER_SIZE % 4);
+ if (ep->dwc_ep.is_in) {
+ if (ep->dwc_ep.maxxfer >
+ DDMA_MAX_TRANSFER_SIZE) {
+ ep->dwc_ep.maxxfer =
+ DDMA_MAX_TRANSFER_SIZE;
+ }
+ } else {
+ if (ep->dwc_ep.maxxfer > out_max_xfer) {
+ ep->dwc_ep.maxxfer =
+ out_max_xfer;
+ }
+ }
+ }
+ if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) {
+ ep->dwc_ep.maxxfer -=
+ (ep->dwc_ep.maxxfer % ep->dwc_ep.maxpacket);
+ }
+ if (req->sent_zlp) {
+ if ((ep->dwc_ep.total_len %
+ ep->dwc_ep.maxpacket == 0)
+ && (ep->dwc_ep.total_len != 0)) {
+ ep->dwc_ep.sent_zlp = 1;
+ }
+
+ }
+#ifdef DWC_UTE_CFI
+ }
+#endif
+ dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep);
+ } else if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+ DWC_PRINTF("There are no more ISOC requests \n");
+ ep->dwc_ep.frame_num = 0xFFFFFFFF;
+ }
+}
+
+/**
+ * This function handles the SOF Interrupts. At this time the SOF
+ * Interrupt is disabled.
+ */
+int32_t dwc_otg_pcd_handle_sof_intr(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+ gintsts_data_t gintsts;
+
+ DWC_DEBUGPL(DBG_PCD, "SOF\n");
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.sofintr = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This function handles the Rx Status Queue Level Interrupt, which
+ * indicates that there is a least one packet in the Rx FIFO. The
+ * packets are moved from the FIFO to memory, where they will be
+ * processed when the Endpoint Interrupt Register indicates Transfer
+ * Complete or SETUP Phase Done.
+ *
+ * Repeat the following until the Rx Status Queue is empty:
+ * -# Read the Receive Status Pop Register (GRXSTSP) to get Packet
+ * info
+ * -# If Receive FIFO is empty then skip to step Clear the interrupt
+ * and exit
+ * -# If SETUP Packet call dwc_otg_read_setup_packet to copy the
+ * SETUP data to the buffer
+ * -# If OUT Data Packet call dwc_otg_read_packet to copy the data
+ * to the destination buffer
+ */
+int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ gintmsk_data_t gintmask = {.d32 = 0 };
+ device_grxsts_data_t status;
+ dwc_otg_pcd_ep_t *ep;
+ gintsts_data_t gintsts;
+#ifdef DEBUG
+ static char *dpid_str[] = { "D0", "D2", "D1", "MDATA" };
+#endif
+
+ //DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _pcd);
+ /* Disable the Rx Status Queue Level interrupt */
+ gintmask.b.rxstsqlvl = 1;
+ DWC_MODIFY_REG32(&global_regs->gintmsk, gintmask.d32, 0);
+
+ /* Get the Status from the top of the FIFO */
+ status.d32 = DWC_READ_REG32(&global_regs->grxstsp);
+
+ DWC_DEBUGPL(DBG_PCD, "EP:%d BCnt:%d DPID:%s "
+ "pktsts:%x Frame:%d(0x%0x)\n",
+ status.b.epnum, status.b.bcnt,
+ dpid_str[status.b.dpid],
+ status.b.pktsts, status.b.fn, status.b.fn);
+ /* Get pointer to EP structure */
+ ep = get_out_ep(pcd, status.b.epnum);
+
+ switch (status.b.pktsts) {
+ case DWC_DSTS_GOUT_NAK:
+ DWC_DEBUGPL(DBG_PCDV, "Global OUT NAK\n");
+ break;
+ case DWC_STS_DATA_UPDT:
+ DWC_DEBUGPL(DBG_PCDV, "OUT Data Packet\n");
+ if (status.b.bcnt && ep->dwc_ep.xfer_buff) {
+ /** @todo NGS Check for buffer overflow? */
+ dwc_otg_read_packet(core_if,
+ ep->dwc_ep.xfer_buff,
+ status.b.bcnt);
+ ep->dwc_ep.xfer_count += status.b.bcnt;
+ ep->dwc_ep.xfer_buff += status.b.bcnt;
+ }
+ break;
+ case DWC_STS_XFER_COMP:
+ DWC_DEBUGPL(DBG_PCDV, "OUT Complete\n");
+ break;
+ case DWC_DSTS_SETUP_COMP:
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCDV, "Setup Complete\n");
+#endif
+ break;
+ case DWC_DSTS_SETUP_UPDT:
+ dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32);
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD,
+ "SETUP PKT: %02x.%02x v%04x i%04x l%04x\n",
+ pcd->setup_pkt->req.bmRequestType,
+ pcd->setup_pkt->req.bRequest,
+ UGETW(pcd->setup_pkt->req.wValue),
+ UGETW(pcd->setup_pkt->req.wIndex),
+ UGETW(pcd->setup_pkt->req.wLength));
+#endif
+ ep->dwc_ep.xfer_count += status.b.bcnt;
+ break;
+ default:
+ DWC_DEBUGPL(DBG_PCDV, "Invalid Packet Status (0x%0x)\n",
+ status.b.pktsts);
+ break;
+ }
+
+ /* Enable the Rx Status Queue Level interrupt */
+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmask.d32);
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.rxstsqlvl = 1;
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ //DWC_DEBUGPL(DBG_PCDV, "EXIT: %s\n", __func__);
+ return 1;
+}
+
+/**
+ * This function examines the Device IN Token Learning Queue to
+ * determine the EP number of the last IN token received. This
+ * implementation is for the Mass Storage device where there are only
+ * 2 IN EPs (Control-IN and BULK-IN).
+ *
+ * The EP numbers for the first six IN Tokens are in DTKNQR1 and there
+ * are 8 EP Numbers in each of the other possible DTKNQ Registers.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ *
+ */
+static inline int get_ep_of_last_in_token(dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_device_global_regs_t *dev_global_regs =
+ core_if->dev_if->dev_global_regs;
+ const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth;
+ /* Number of Token Queue Registers */
+ const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
+ dtknq1_data_t dtknqr1;
+ uint32_t in_tkn_epnums[4];
+ int ndx = 0;
+ int i = 0;
+ volatile uint32_t *addr = &dev_global_regs->dtknqr1;
+ int epnum = 0;
+
+ //DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH);
+
+ /* Read the DTKNQ Registers */
+ for (i = 0; i < DTKNQ_REG_CNT; i++) {
+ in_tkn_epnums[i] = DWC_READ_REG32(addr);
+ DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i + 1,
+ in_tkn_epnums[i]);
+ if (addr == &dev_global_regs->dvbusdis) {
+ addr = &dev_global_regs->dtknqr3_dthrctl;
+ } else {
+ ++addr;
+ }
+
+ }
+
+ /* Copy the DTKNQR1 data to the bit field. */
+ dtknqr1.d32 = in_tkn_epnums[0];
+ /* Get the EP numbers */
+ in_tkn_epnums[0] = dtknqr1.b.epnums0_5;
+ ndx = dtknqr1.b.intknwptr - 1;
+
+ //DWC_DEBUGPL(DBG_PCDV,"ndx=%d\n",ndx);
+ if (ndx == -1) {
+ /** @todo Find a simpler way to calculate the max
+ * queue position.*/
+ int cnt = TOKEN_Q_DEPTH;
+ if (TOKEN_Q_DEPTH <= 6) {
+ cnt = TOKEN_Q_DEPTH - 1;
+ } else if (TOKEN_Q_DEPTH <= 14) {
+ cnt = TOKEN_Q_DEPTH - 7;
+ } else if (TOKEN_Q_DEPTH <= 22) {
+ cnt = TOKEN_Q_DEPTH - 15;
+ } else {
+ cnt = TOKEN_Q_DEPTH - 23;
+ }
+ epnum = (in_tkn_epnums[DTKNQ_REG_CNT - 1] >> (cnt * 4)) & 0xF;
+ } else {
+ if (ndx <= 5) {
+ epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF;
+ } else if (ndx <= 13) {
+ ndx -= 6;
+ epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF;
+ } else if (ndx <= 21) {
+ ndx -= 14;
+ epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF;
+ } else if (ndx <= 29) {
+ ndx -= 22;
+ epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF;
+ }
+ }
+ //DWC_DEBUGPL(DBG_PCD,"epnum=%d\n",epnum);
+ return epnum;
+}
+
+/**
+ * This interrupt occurs when the non-periodic Tx FIFO is half-empty.
+ * The active request is checked for the next packet to be loaded into
+ * the non-periodic Tx FIFO.
+ */
+int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ dwc_otg_dev_in_ep_regs_t *ep_regs;
+ gnptxsts_data_t txstatus = {.d32 = 0 };
+ gintsts_data_t gintsts;
+
+ int epnum = 0;
+ dwc_otg_pcd_ep_t *ep = 0;
+ uint32_t len = 0;
+ int dwords;
+
+ /* Get the epnum from the IN Token Learning Queue. */
+ epnum = get_ep_of_last_in_token(core_if);
+ ep = get_in_ep(pcd, epnum);
+
+ DWC_DEBUGPL(DBG_PCD, "NP TxFifo Empty: %d \n", epnum);
+
+ ep_regs = core_if->dev_if->in_ep_regs[epnum];
+
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ if (len > ep->dwc_ep.maxpacket) {
+ len = ep->dwc_ep.maxpacket;
+ }
+ dwords = (len + 3) / 4;
+
+ /* While there is space in the queue and space in the FIFO and
+ * More data to tranfer, Write packets to the Tx FIFO */
+ txstatus.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+ DWC_DEBUGPL(DBG_PCDV, "b4 GNPTXSTS=0x%08x\n", txstatus.d32);
+
+ while (txstatus.b.nptxqspcavail > 0 &&
+ txstatus.b.nptxfspcavail > dwords &&
+ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) {
+ /* Write the FIFO */
+ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+
+ if (len > ep->dwc_ep.maxpacket) {
+ len = ep->dwc_ep.maxpacket;
+ }
+
+ dwords = (len + 3) / 4;
+ txstatus.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+ DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n", txstatus.d32);
+ }
+
+ DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n",
+ DWC_READ_REG32(&global_regs->gnptxsts));
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.nptxfempty = 1;
+ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This function is called when dedicated Tx FIFO Empty interrupt occurs.
+ * The active request is checked for the next packet to be loaded into
+ * apropriate Tx FIFO.
+ */
+static int32_t write_empty_tx_fifo(dwc_otg_pcd_t * pcd, uint32_t epnum)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ dwc_otg_dev_in_ep_regs_t *ep_regs;
+ dtxfsts_data_t txstatus = {.d32 = 0 };
+ dwc_otg_pcd_ep_t *ep = 0;
+ uint32_t len = 0;
+ int dwords;
+
+ ep = get_in_ep(pcd, epnum);
+
+ DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", epnum);
+
+ ep_regs = core_if->dev_if->in_ep_regs[epnum];
+
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+
+ if (len > ep->dwc_ep.maxpacket) {
+ len = ep->dwc_ep.maxpacket;
+ }
+
+ dwords = (len + 3) / 4;
+
+ /* While there is space in the queue and space in the FIFO and
+ * More data to tranfer, Write packets to the Tx FIFO */
+ txstatus.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
+ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32);
+
+ while (txstatus.b.txfspcavail > dwords &&
+ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len &&
+ ep->dwc_ep.xfer_len != 0) {
+ /* Write the FIFO */
+ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+
+ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+ if (len > ep->dwc_ep.maxpacket) {
+ len = ep->dwc_ep.maxpacket;
+ }
+
+ dwords = (len + 3) / 4;
+ txstatus.d32 =
+ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
+ DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum,
+ txstatus.d32);
+ }
+
+ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum,
+ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts));
+
+ return 1;
+}
+
+/**
+ * This function is called when the Device is disconnected. It stops
+ * any active requests and informs the Gadget driver of the
+ * disconnect.
+ */
+void dwc_otg_pcd_stop(dwc_otg_pcd_t * pcd)
+{
+ int i, num_in_eps, num_out_eps;
+ dwc_otg_pcd_ep_t *ep;
+
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_SPINLOCK(pcd->lock);
+
+ num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+ num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s() \n", __func__);
+ /* don't disconnect drivers more than once */
+ if (pcd->ep0state == EP0_DISCONNECT) {
+ DWC_DEBUGPL(DBG_ANY, "%s() Already Disconnected\n", __func__);
+ DWC_SPINUNLOCK(pcd->lock);
+ return;
+ }
+ pcd->ep0state = EP0_DISCONNECT;
+
+ /* Reset the OTG state. */
+ dwc_otg_pcd_update_otg(pcd, 1);
+
+ /* Disable the NP Tx Fifo Empty Interrupt. */
+ intr_mask.b.nptxfempty = 1;
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Flush the FIFOs */
+ /**@todo NGS Flush Periodic FIFOs */
+ dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0x10);
+ dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd));
+
+ /* prevent new request submissions, kill any outstanding requests */
+ ep = &pcd->ep0;
+ dwc_otg_request_nuke(ep);
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < num_in_eps; i++) {
+ dwc_otg_pcd_ep_t *ep = &pcd->in_ep[i];
+ dwc_otg_request_nuke(ep);
+ }
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < num_out_eps; i++) {
+ dwc_otg_pcd_ep_t *ep = &pcd->out_ep[i];
+ dwc_otg_request_nuke(ep);
+ }
+
+ /* report disconnect; the driver is already quiesced */
+ if (pcd->fops->disconnect) {
+ DWC_SPINUNLOCK(pcd->lock);
+ pcd->fops->disconnect(pcd);
+ DWC_SPINLOCK(pcd->lock);
+ }
+ DWC_SPINUNLOCK(pcd->lock);
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+int32_t dwc_otg_pcd_handle_i2c_intr(dwc_otg_pcd_t * pcd)
+{
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ gintsts_data_t gintsts;
+
+ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "i2cintr");
+ intr_mask.b.i2cintr = 1;
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.i2cintr = 1;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+ return 1;
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+int32_t dwc_otg_pcd_handle_early_suspend_intr(dwc_otg_pcd_t * pcd)
+{
+ gintsts_data_t gintsts;
+#if defined(VERBOSE)
+ DWC_PRINTF("Early Suspend Detected\n");
+#endif
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.erlysuspend = 1;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+ return 1;
+}
+
+/**
+ * This function configures EPO to receive SETUP packets.
+ *
+ * @todo NGS: Update the comments from the HW FS.
+ *
+ * -# Program the following fields in the endpoint specific registers
+ * for Control OUT EP 0, in order to receive a setup packet
+ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
+ * setup packets)
+ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
+ * to back setup packets)
+ * - In DMA mode, DOEPDMA0 Register with a memory address to
+ * store any setup packets received
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param pcd Programming view of the PCD.
+ */
+static inline void ep0_out_start(dwc_otg_core_if_t * core_if,
+ dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ deptsiz0_data_t doeptsize0 = {.d32 = 0 };
+ dwc_otg_dev_dma_desc_t *dma_desc;
+ depctl_data_t doepctl = {.d32 = 0 };
+
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV, "%s() doepctl0=%0x\n", __func__,
+ DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl));
+#endif
+
+ doeptsize0.b.supcnt = 3;
+ doeptsize0.b.pktcnt = 1;
+ doeptsize0.b.xfersize = 8 * 3;
+
+ if (core_if->dma_enable) {
+ if (!core_if->dma_desc_enable) {
+ /** put here as for Hermes mode deptisz register should not be written */
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz,
+ doeptsize0.d32);
+
+ /** @todo dma needs to handle multiple setup packets (up to 3) */
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma,
+ pcd->setup_pkt_dma_handle);
+ } else {
+ dev_if->setup_desc_index =
+ (dev_if->setup_desc_index + 1) & 1;
+ dma_desc =
+ dev_if->setup_desc_addr[dev_if->setup_desc_index];
+
+ /** DMA Descriptor Setup */
+ dma_desc->status.b.bs = BS_HOST_BUSY;
+ dma_desc->status.b.l = 1;
+ dma_desc->status.b.ioc = 1;
+ dma_desc->status.b.bytes = pcd->ep0.dwc_ep.maxpacket;
+ dma_desc->buf = pcd->setup_pkt_dma_handle;
+ dma_desc->status.b.sts = 0;
+ dma_desc->status.b.bs = BS_HOST_READY;
+
+ /** DOEPDMA0 Register write */
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma,
+ dev_if->
+ dma_setup_desc_addr
+ [dev_if->setup_desc_index]);
+ }
+
+ } else {
+ /** put here as for Hermes mode deptisz register should not be written */
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz,
+ doeptsize0.d32);
+ }
+
+ /** DOEPCTL0 Register write */
+ doepctl.b.epena = 1;
+ doepctl.b.cnak = 1;
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);
+
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n",
+ DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl));
+ DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n",
+ DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl));
+#endif
+}
+
+/**
+ * This interrupt occurs when a USB Reset is detected. When the USB
+ * Reset Interrupt occurs the device state is set to DEFAULT and the
+ * EP0 state is set to IDLE.
+ * -# Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1)
+ * -# Unmask the following interrupt bits
+ * - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint)
+ * - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint)
+ * - DOEPMSK.SETUP = 1
+ * - DOEPMSK.XferCompl = 1
+ * - DIEPMSK.XferCompl = 1
+ * - DIEPMSK.TimeOut = 1
+ * -# Program the following fields in the endpoint specific registers
+ * for Control OUT EP 0, in order to receive a setup packet
+ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
+ * setup packets)
+ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
+ * to back setup packets)
+ * - In DMA mode, DOEPDMA0 Register with a memory address to
+ * store any setup packets received
+ * At this point, all the required initialization, except for enabling
+ * the control 0 OUT endpoint is done, for receiving SETUP packets.
+ */
+int32_t dwc_otg_pcd_handle_usb_reset_intr(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ depctl_data_t doepctl = {.d32 = 0 };
+ depctl_data_t diepctl = {.d32 = 0 };
+ daint_data_t daintmsk = {.d32 = 0 };
+ doepmsk_data_t doepmsk = {.d32 = 0 };
+ diepmsk_data_t diepmsk = {.d32 = 0 };
+ dcfg_data_t dcfg = {.d32 = 0 };
+ grstctl_t resetctl = {.d32 = 0 };
+ dctl_data_t dctl = {.d32 = 0 };
+ int i = 0;
+ gintsts_data_t gintsts;
+ pcgcctl_data_t power = {.d32 = 0 };
+
+ power.d32 = DWC_READ_REG32(core_if->pcgcctl);
+ if (power.b.stoppclk) {
+ power.d32 = 0;
+ power.b.stoppclk = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0);
+
+ power.b.pwrclmp = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0);
+
+ power.b.rstpdwnmodule = 1;
+ DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0);
+ }
+
+ core_if->lx_state = DWC_OTG_L0;
+
+ DWC_PRINTF("USB RESET\n");
+#ifdef DWC_EN_ISOC
+ for (i = 1; i < 16; ++i) {
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+ ep = get_in_ep(pcd, i);
+ if (ep != 0) {
+ dwc_ep = &ep->dwc_ep;
+ dwc_ep->next_frame = 0xffffffff;
+ }
+ }
+#endif /* DWC_EN_ISOC */
+
+ /* reset the HNP settings */
+ dwc_otg_pcd_update_otg(pcd, 1);
+
+ /* Clear the Remote Wakeup Signalling */
+ dctl.b.rmtwkupsig = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0);
+
+ /* Set NAK for all OUT EPs */
+ doepctl.b.snak = 1;
+ for (i = 0; i <= dev_if->num_out_eps; i++) {
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, doepctl.d32);
+ }
+
+ /* Flush the NP Tx FIFO */
+ dwc_otg_flush_tx_fifo(core_if, 0x10);
+ /* Flush the Learning Queue */
+ resetctl.b.intknqflsh = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32);
+
+ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) {
+ core_if->start_predict = 0;
+ for (i = 0; i<= core_if->dev_if->num_in_eps; ++i) {
+ core_if->nextep_seq[i] = 0xff; // 0xff - EP not active
+ }
+ core_if->nextep_seq[0] = 0;
+ core_if->first_in_nextep_seq = 0;
+ diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl);
+ diepctl.b.nextep = 0;
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
+
+ /* Update IN Endpoint Mismatch Count by active IN NP EP count + 1 */
+ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+ dcfg.b.epmscnt = 2;
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+ DWC_DEBUGPL(DBG_PCDV,"%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+ __func__, core_if->first_in_nextep_seq);
+ for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+ DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]);
+ }
+ }
+
+ if (core_if->multiproc_int_enable) {
+ daintmsk.b.inep0 = 1;
+ daintmsk.b.outep0 = 1;
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->deachintmsk,
+ daintmsk.d32);
+
+ doepmsk.b.setup = 1;
+ doepmsk.b.xfercompl = 1;
+ doepmsk.b.ahberr = 1;
+ doepmsk.b.epdisabled = 1;
+
+ if (core_if->dma_desc_enable) {
+ doepmsk.b.stsphsercvd = 1;
+ doepmsk.b.bna = 1;
+ }
+/*
+ doepmsk.b.babble = 1;
+ doepmsk.b.nyet = 1;
+
+ if (core_if->dma_enable) {
+ doepmsk.b.nak = 1;
+ }
+*/
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->doepeachintmsk[0],
+ doepmsk.d32);
+
+ diepmsk.b.xfercompl = 1;
+ diepmsk.b.timeout = 1;
+ diepmsk.b.epdisabled = 1;
+ diepmsk.b.ahberr = 1;
+ diepmsk.b.intknepmis = 1;
+ if (!core_if->en_multiple_tx_fifo && core_if->dma_enable)
+ diepmsk.b.intknepmis = 0;
+
+/* if (core_if->dma_desc_enable) {
+ diepmsk.b.bna = 1;
+ }
+*/
+/*
+ if (core_if->dma_enable) {
+ diepmsk.b.nak = 1;
+ }
+*/
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->diepeachintmsk[0],
+ diepmsk.d32);
+ } else {
+ daintmsk.b.inep0 = 1;
+ daintmsk.b.outep0 = 1;
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->daintmsk,
+ daintmsk.d32);
+
+ doepmsk.b.setup = 1;
+ doepmsk.b.xfercompl = 1;
+ doepmsk.b.ahberr = 1;
+ doepmsk.b.epdisabled = 1;
+
+ if (core_if->dma_desc_enable) {
+ doepmsk.b.stsphsercvd = 1;
+ doepmsk.b.bna = 1;
+ }
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->doepmsk, doepmsk.d32);
+
+ diepmsk.b.xfercompl = 1;
+ diepmsk.b.timeout = 1;
+ diepmsk.b.epdisabled = 1;
+ diepmsk.b.ahberr = 1;
+ if (!core_if->en_multiple_tx_fifo && core_if->dma_enable)
+ diepmsk.b.intknepmis = 0;
+/*
+ if (core_if->dma_desc_enable) {
+ diepmsk.b.bna = 1;
+ }
+*/
+
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32);
+ }
+
+ /* Reset Device Address */
+ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+ dcfg.b.devaddr = 0;
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+ /* setup EP0 to receive SETUP packets */
+ ep0_out_start(core_if, pcd);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.usbreset = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * Get the device speed from the device status register and convert it
+ * to USB speed constant.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static int get_device_speed(dwc_otg_core_if_t * core_if)
+{
+ dsts_data_t dsts;
+ int speed = 0;
+ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+ switch (dsts.b.enumspd) {
+ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+ speed = USB_SPEED_HIGH;
+ break;
+ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+ speed = USB_SPEED_FULL;
+ break;
+
+ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+ speed = USB_SPEED_LOW;
+ break;
+ }
+
+ return speed;
+}
+
+/**
+ * Read the device status register and set the device speed in the
+ * data structure.
+ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
+ */
+int32_t dwc_otg_pcd_handle_enum_done_intr(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+ gintsts_data_t gintsts;
+ gusbcfg_data_t gusbcfg;
+ dwc_otg_core_global_regs_t *global_regs =
+ GET_CORE_IF(pcd)->core_global_regs;
+ uint8_t utmi16b, utmi8b;
+ int speed;
+ DWC_DEBUGPL(DBG_PCD, "SPEED ENUM\n");
+
+ if (GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_2_60a) {
+ utmi16b = 6; //vahrama old value was 6;
+ utmi8b = 9;
+ } else {
+ utmi16b = 4;
+ utmi8b = 8;
+ }
+ dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+#ifdef DEBUG_EP0
+ print_ep0_state(pcd);
+#endif
+
+ if (pcd->ep0state == EP0_DISCONNECT) {
+ pcd->ep0state = EP0_IDLE;
+ } else if (pcd->ep0state == EP0_STALL) {
+ pcd->ep0state = EP0_IDLE;
+ }
+
+ pcd->ep0state = EP0_IDLE;
+
+ ep0->stopped = 0;
+
+ speed = get_device_speed(GET_CORE_IF(pcd));
+ pcd->fops->connect(pcd, speed);
+
+ /* Set USB turnaround time based on device speed and PHY interface. */
+ gusbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+ if (speed == USB_SPEED_HIGH) {
+ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type ==
+ DWC_HWCFG2_HS_PHY_TYPE_ULPI) {
+ /* ULPI interface */
+ gusbcfg.b.usbtrdtim = 9;
+ }
+ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type ==
+ DWC_HWCFG2_HS_PHY_TYPE_UTMI) {
+ /* UTMI+ interface */
+ if (GET_CORE_IF(pcd)->hwcfg4.b.utmi_phy_data_width == 0) {
+ gusbcfg.b.usbtrdtim = utmi8b;
+ } else if (GET_CORE_IF(pcd)->hwcfg4.
+ b.utmi_phy_data_width == 1) {
+ gusbcfg.b.usbtrdtim = utmi16b;
+ } else if (GET_CORE_IF(pcd)->
+ core_params->phy_utmi_width == 8) {
+ gusbcfg.b.usbtrdtim = utmi8b;
+ } else {
+ gusbcfg.b.usbtrdtim = utmi16b;
+ }
+ }
+ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type ==
+ DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI) {
+ /* UTMI+ OR ULPI interface */
+ if (gusbcfg.b.ulpi_utmi_sel == 1) {
+ /* ULPI interface */
+ gusbcfg.b.usbtrdtim = 9;
+ } else {
+ /* UTMI+ interface */
+ if (GET_CORE_IF(pcd)->
+ core_params->phy_utmi_width == 16) {
+ gusbcfg.b.usbtrdtim = utmi16b;
+ } else {
+ gusbcfg.b.usbtrdtim = utmi8b;
+ }
+ }
+ }
+ } else {
+ /* Full or low speed */
+ gusbcfg.b.usbtrdtim = 9;
+ }
+ DWC_WRITE_REG32(&global_regs->gusbcfg, gusbcfg.d32);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.enumdone = 1;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+ return 1;
+}
+
+/**
+ * This interrupt indicates that the ISO OUT Packet was dropped due to
+ * Rx FIFO full or Rx Status Queue Full. If this interrupt occurs
+ * read all the data from the Rx FIFO.
+ */
+int32_t dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(dwc_otg_pcd_t * pcd)
+{
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ gintsts_data_t gintsts;
+
+ DWC_WARN("INTERRUPT Handler not implemented for %s\n",
+ "ISOC Out Dropped");
+
+ intr_mask.b.isooutdrop = 1;
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.isooutdrop = 1;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates the end of the portion of the micro-frame
+ * for periodic transactions. If there is a periodic transaction for
+ * the next frame, load the packets into the EP periodic Tx FIFO.
+ */
+int32_t dwc_otg_pcd_handle_end_periodic_frame_intr(dwc_otg_pcd_t * pcd)
+{
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ gintsts_data_t gintsts;
+ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "EOP");
+
+ intr_mask.b.eopframe = 1;
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.eopframe = 1;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that EP of the packet on the top of the
+ * non-periodic Tx FIFO does not match EP of the IN Token received.
+ *
+ * The "Device IN Token Queue" Registers are read to determine the
+ * order the IN Tokens have been received. The non-periodic Tx FIFO
+ * is flushed, so it can be reloaded in the order seen in the IN Token
+ * Queue.
+ */
+int32_t dwc_otg_pcd_handle_ep_mismatch_intr(dwc_otg_pcd_t * pcd)
+{
+ gintsts_data_t gintsts;
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dctl_data_t dctl;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) {
+ core_if->start_predict = 1;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if);
+
+ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+ if (!gintsts.b.ginnakeff) {
+ /* Disable EP Mismatch interrupt */
+ intr_mask.d32 = 0;
+ intr_mask.b.epmismatch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
+ /* Enable the Global IN NAK Effective Interrupt */
+ intr_mask.d32 = 0;
+ intr_mask.b.ginnakeff = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32);
+ /* Set the global non-periodic IN NAK handshake */
+ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+ dctl.b.sgnpinnak = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
+ } else {
+ DWC_PRINTF("gintsts.b.ginnakeff = 1! dctl.b.sgnpinnak not set\n");
+ }
+ /* Disabling of all EP's will be done in dwc_otg_pcd_handle_in_nak_effective()
+ * handler after Global IN NAK Effective interrupt will be asserted */
+ }
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.epmismatch = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This interrupt is valid only in DMA mode. This interrupt indicates that the
+ * core has stopped fetching data for IN endpoints due to the unavailability of
+ * TxFIFO space or Request Queue space. This interrupt is used by the
+ * application for an endpoint mismatch algorithm.
+ *
+ * @param pcd The PCD
+ */
+int32_t dwc_otg_pcd_handle_ep_fetsusp_intr(dwc_otg_pcd_t * pcd)
+{
+ gintsts_data_t gintsts;
+ gintmsk_data_t gintmsk_data;
+ dctl_data_t dctl;
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if);
+
+ /* Clear the global non-periodic IN NAK handshake */
+ dctl.d32 = 0;
+ dctl.b.cgnpinnak = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+
+ /* Mask GINTSTS.FETSUSP interrupt */
+ gintmsk_data.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+ gintmsk_data.b.fetsusp = 0;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_data.d32);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.fetsusp = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+ return 1;
+}
+/**
+ * This funcion stalls EP0.
+ */
+static inline void ep0_do_stall(dwc_otg_pcd_t * pcd, const int err_val)
+{
+ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+ usb_device_request_t *ctrl = &pcd->setup_pkt->req;
+ DWC_WARN("req %02x.%02x protocol STALL; err %d\n",
+ ctrl->bmRequestType, ctrl->bRequest, err_val);
+
+ ep0->dwc_ep.is_in = 1;
+ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep0->dwc_ep);
+ pcd->ep0.stopped = 1;
+ pcd->ep0state = EP0_IDLE;
+ ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This functions delegates the setup command to the gadget driver.
+ */
+static inline void do_gadget_setup(dwc_otg_pcd_t * pcd,
+ usb_device_request_t * ctrl)
+{
+ int ret = 0;
+ DWC_SPINUNLOCK(pcd->lock);
+ ret = pcd->fops->setup(pcd, (uint8_t *) ctrl);
+ DWC_SPINLOCK(pcd->lock);
+ if (ret < 0) {
+ ep0_do_stall(pcd, ret);
+ }
+
+ /** @todo This is a g_file_storage gadget driver specific
+ * workaround: a DELAYED_STATUS result from the fsg_setup
+ * routine will result in the gadget queueing a EP0 IN status
+ * phase for a two-stage control transfer. Exactly the same as
+ * a SET_CONFIGURATION/SET_INTERFACE except that this is a class
+ * specific request. Need a generic way to know when the gadget
+ * driver will queue the status phase. Can we assume when we
+ * call the gadget driver setup() function that it will always
+ * queue and require the following flag? Need to look into
+ * this.
+ */
+
+ if (ret == 256 + 999) {
+ pcd->request_config = 1;
+ }
+}
+
+#ifdef DWC_UTE_CFI
+/**
+ * This functions delegates the CFI setup commands to the gadget driver.
+ * This function will return a negative value to indicate a failure.
+ */
+static inline int cfi_gadget_setup(dwc_otg_pcd_t * pcd,
+ struct cfi_usb_ctrlrequest *ctrl_req)
+{
+ int ret = 0;
+
+ if (pcd->fops && pcd->fops->cfi_setup) {
+ DWC_SPINUNLOCK(pcd->lock);
+ ret = pcd->fops->cfi_setup(pcd, ctrl_req);
+ DWC_SPINLOCK(pcd->lock);
+ if (ret < 0) {
+ ep0_do_stall(pcd, ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+/**
+ * This function starts the Zero-Length Packet for the IN status phase
+ * of a 2 stage control transfer.
+ */
+static inline void do_setup_in_status_phase(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+ if (pcd->ep0state == EP0_STALL) {
+ return;
+ }
+
+ pcd->ep0state = EP0_IN_STATUS_PHASE;
+
+ /* Prepare for more SETUP Packets */
+ DWC_DEBUGPL(DBG_PCD, "EP0 IN ZLP\n");
+ ep0->dwc_ep.xfer_len = 0;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.is_in = 1;
+ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+ /* Prepare for more SETUP Packets */
+ //ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This function starts the Zero-Length Packet for the OUT status phase
+ * of a 2 stage control transfer.
+ */
+static inline void do_setup_out_status_phase(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+ if (pcd->ep0state == EP0_STALL) {
+ DWC_DEBUGPL(DBG_PCD, "EP0 STALLED\n");
+ return;
+ }
+ pcd->ep0state = EP0_OUT_STATUS_PHASE;
+
+ DWC_DEBUGPL(DBG_PCD, "EP0 OUT ZLP\n");
+ ep0->dwc_ep.xfer_len = 0;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.is_in = 0;
+ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+ /* Prepare for more SETUP Packets */
+ if (GET_CORE_IF(pcd)->dma_enable == 0) {
+ ep0_out_start(GET_CORE_IF(pcd), pcd);
+ }
+}
+
+/**
+ * Clear the EP halt (STALL) and if pending requests start the
+ * transfer.
+ */
+static inline void pcd_clear_halt(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep)
+{
+ if (ep->dwc_ep.stall_clear_flag == 0)
+ dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
+
+ /* Reactive the EP */
+ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
+ if (ep->stopped) {
+ ep->stopped = 0;
+ /* If there is a request in the EP queue start it */
+
+ /** @todo FIXME: this causes an EP mismatch in DMA mode.
+ * epmismatch not yet implemented. */
+
+ /*
+ * Above fixme is solved by implmenting a tasklet to call the
+ * start_next_request(), outside of interrupt context at some
+ * time after the current time, after a clear-halt setup packet.
+ * Still need to implement ep mismatch in the future if a gadget
+ * ever uses more than one endpoint at once
+ */
+ ep->queue_sof = 1;
+ DWC_TASK_SCHEDULE(pcd->start_xfer_tasklet);
+ }
+ /* Start Control Status Phase */
+ do_setup_in_status_phase(pcd);
+}
+
+/**
+ * This function is called when the SET_FEATURE TEST_MODE Setup packet
+ * is sent from the host. The Device Control register is written with
+ * the Test Mode bits set to the specified Test Mode. This is done as
+ * a tasklet so that the "Status" phase of the control transfer
+ * completes before transmitting the TEST packets.
+ *
+ * @todo This has not been tested since the tasklet struct was put
+ * into the PCD struct!
+ *
+ */
+void do_test_mode(void *data)
+{
+ dctl_data_t dctl;
+ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data;
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ int test_mode = pcd->test_mode;
+
+// DWC_WARN("%s() has not been tested since being rewritten!\n", __func__);
+
+ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+ switch (test_mode) {
+ case 1: // TEST_J
+ dctl.b.tstctl = 1;
+ break;
+
+ case 2: // TEST_K
+ dctl.b.tstctl = 2;
+ break;
+
+ case 3: // TEST_SE0_NAK
+ dctl.b.tstctl = 3;
+ break;
+
+ case 4: // TEST_PACKET
+ dctl.b.tstctl = 4;
+ break;
+
+ case 5: // TEST_FORCE_ENABLE
+ dctl.b.tstctl = 5;
+ break;
+ }
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
+}
+
+/**
+ * This function process the GET_STATUS Setup Commands.
+ */
+static inline void do_get_status(dwc_otg_pcd_t * pcd)
+{
+ usb_device_request_t ctrl = pcd->setup_pkt->req;
+ dwc_otg_pcd_ep_t *ep;
+ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+ uint16_t *status = pcd->status_buf;
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD,
+ "GET_STATUS %02x.%02x v%04x i%04x l%04x\n",
+ ctrl.bmRequestType, ctrl.bRequest,
+ UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
+ UGETW(ctrl.wLength));
+#endif
+
+ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
+ case UT_DEVICE:
+ if(UGETW(ctrl.wIndex) == 0xF000) { /* OTG Status selector */
+ DWC_PRINTF("wIndex - %d\n", UGETW(ctrl.wIndex));
+ DWC_PRINTF("OTG VERSION - %d\n", core_if->otg_ver);
+ DWC_PRINTF("OTG CAP - %d, %d\n", core_if->core_params->otg_cap,
+ DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE);
+ if(core_if->otg_ver == 1 &&
+ core_if->core_params->otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ uint8_t *otgsts = (uint8_t*)pcd->status_buf;
+ *otgsts = (core_if->otg_sts & 0x1);
+ pcd->ep0_pending = 1;
+ ep0->dwc_ep.start_xfer_buff = (uint8_t *) otgsts;
+ ep0->dwc_ep.xfer_buff = (uint8_t *) otgsts;
+ ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle;
+ ep0->dwc_ep.xfer_len = 1;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+ return;
+ } else {
+ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+ return;
+ }
+ break;
+ } else {
+ *status = 0x1; /* Self powered */
+ *status |= pcd->remote_wakeup_enable << 1;
+ break;
+ }
+ case UT_INTERFACE:
+ *status = 0;
+ break;
+
+ case UT_ENDPOINT:
+ ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex));
+ if (ep == 0 || UGETW(ctrl.wLength) > 2) {
+ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+ return;
+ }
+ /** @todo check for EP stall */
+ *status = ep->stopped;
+ break;
+ }
+ pcd->ep0_pending = 1;
+ ep0->dwc_ep.start_xfer_buff = (uint8_t *) status;
+ ep0->dwc_ep.xfer_buff = (uint8_t *) status;
+ ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle;
+ ep0->dwc_ep.xfer_len = 2;
+ ep0->dwc_ep.xfer_count = 0;
+ ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
+ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+}
+
+/**
+ * This function process the SET_FEATURE Setup Commands.
+ */
+static inline void do_set_feature(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ usb_device_request_t ctrl = pcd->setup_pkt->req;
+ dwc_otg_pcd_ep_t *ep = 0;
+ int32_t otg_cap_param = core_if->core_params->otg_cap;
+ gotgctl_data_t gotgctl = {.d32 = 0 };
+
+ DWC_DEBUGPL(DBG_PCD, "SET_FEATURE:%02x.%02x v%04x i%04x l%04x\n",
+ ctrl.bmRequestType, ctrl.bRequest,
+ UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
+ UGETW(ctrl.wLength));
+ DWC_DEBUGPL(DBG_PCD, "otg_cap=%d\n", otg_cap_param);
+
+ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
+ case UT_DEVICE:
+ switch (UGETW(ctrl.wValue)) {
+ case UF_DEVICE_REMOTE_WAKEUP:
+ pcd->remote_wakeup_enable = 1;
+ break;
+
+ case UF_TEST_MODE:
+ /* Setup the Test Mode tasklet to do the Test
+ * Packet generation after the SETUP Status
+ * phase has completed. */
+
+ /** @todo This has not been tested since the
+ * tasklet struct was put into the PCD
+ * struct! */
+ pcd->test_mode = UGETW(ctrl.wIndex) >> 8;
+ DWC_TASK_SCHEDULE(pcd->test_mode_tasklet);
+ break;
+
+ case UF_DEVICE_B_HNP_ENABLE:
+ DWC_DEBUGPL(DBG_PCDV,
+ "SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
+
+ /* dev may initiate HNP */
+ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ pcd->b_hnp_enable = 1;
+ dwc_otg_pcd_update_otg(pcd, 0);
+ DWC_DEBUGPL(DBG_PCD, "Request B HNP\n");
+ /**@todo Is the gotgctl.devhnpen cleared
+ * by a USB Reset? */
+ gotgctl.b.devhnpen = 1;
+ gotgctl.b.hnpreq = 1;
+ DWC_WRITE_REG32(&global_regs->gotgctl,
+ gotgctl.d32);
+ } else {
+ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+ return;
+ }
+ break;
+
+ case UF_DEVICE_A_HNP_SUPPORT:
+ /* RH port supports HNP */
+ DWC_DEBUGPL(DBG_PCDV,
+ "SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n");
+ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ pcd->a_hnp_support = 1;
+ dwc_otg_pcd_update_otg(pcd, 0);
+ } else {
+ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+ return;
+ }
+ break;
+
+ case UF_DEVICE_A_ALT_HNP_SUPPORT:
+ /* other RH port does */
+ DWC_DEBUGPL(DBG_PCDV,
+ "SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n");
+ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+ pcd->a_alt_hnp_support = 1;
+ dwc_otg_pcd_update_otg(pcd, 0);
+ } else {
+ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+ return;
+ }
+ break;
+
+ default:
+ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+ return;
+
+ }
+ do_setup_in_status_phase(pcd);
+ break;
+
+ case UT_INTERFACE:
+ do_gadget_setup(pcd, &ctrl);
+ break;
+
+ case UT_ENDPOINT:
+ if (UGETW(ctrl.wValue) == UF_ENDPOINT_HALT) {
+ ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex));
+ if (ep == 0) {
+ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+ return;
+ }
+ ep->stopped = 1;
+ dwc_otg_ep_set_stall(core_if, &ep->dwc_ep);
+ }
+ do_setup_in_status_phase(pcd);
+ break;
+ }
+}
+
+/**
+ * This function process the CLEAR_FEATURE Setup Commands.
+ */
+static inline void do_clear_feature(dwc_otg_pcd_t * pcd)
+{
+ usb_device_request_t ctrl = pcd->setup_pkt->req;
+ dwc_otg_pcd_ep_t *ep = 0;
+
+ DWC_DEBUGPL(DBG_PCD,
+ "CLEAR_FEATURE:%02x.%02x v%04x i%04x l%04x\n",
+ ctrl.bmRequestType, ctrl.bRequest,
+ UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
+ UGETW(ctrl.wLength));
+
+ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
+ case UT_DEVICE:
+ switch (UGETW(ctrl.wValue)) {
+ case UF_DEVICE_REMOTE_WAKEUP:
+ pcd->remote_wakeup_enable = 0;
+ break;
+
+ case UF_TEST_MODE:
+ /** @todo Add CLEAR_FEATURE for TEST modes. */
+ break;
+
+ default:
+ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+ return;
+ }
+ do_setup_in_status_phase(pcd);
+ break;
+
+ case UT_ENDPOINT:
+ ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex));
+ if (ep == 0) {
+ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+ return;
+ }
+
+ pcd_clear_halt(pcd, ep);
+
+ break;
+ }
+}
+
+/**
+ * This function process the SET_ADDRESS Setup Commands.
+ */
+static inline void do_set_address(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
+ usb_device_request_t ctrl = pcd->setup_pkt->req;
+
+ if (ctrl.bmRequestType == UT_DEVICE) {
+ dcfg_data_t dcfg = {.d32 = 0 };
+
+#ifdef DEBUG_EP0
+// DWC_DEBUGPL(DBG_PCDV, "SET_ADDRESS:%d\n", ctrl.wValue);
+#endif
+ dcfg.b.devaddr = UGETW(ctrl.wValue);
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dcfg, 0, dcfg.d32);
+ do_setup_in_status_phase(pcd);
+ }
+}
+
+/**
+ * This function processes SETUP commands. In Linux, the USB Command
+ * processing is done in two places - the first being the PCD and the
+ * second in the Gadget Driver (for example, the File-Backed Storage
+ * Gadget Driver).
+ *
+ * <table>
+ * <tr><td>Command </td><td>Driver </td><td>Description</td></tr>
+ *
+ * <tr><td>GET_STATUS </td><td>PCD </td><td>Command is processed as
+ * defined in chapter 9 of the USB 2.0 Specification chapter 9
+ * </td></tr>
+ *
+ * <tr><td>CLEAR_FEATURE </td><td>PCD </td><td>The Device and Endpoint
+ * requests are the ENDPOINT_HALT feature is procesed, all others the
+ * interface requests are ignored.</td></tr>
+ *
+ * <tr><td>SET_FEATURE </td><td>PCD </td><td>The Device and Endpoint
+ * requests are processed by the PCD. Interface requests are passed
+ * to the Gadget Driver.</td></tr>
+ *
+ * <tr><td>SET_ADDRESS </td><td>PCD </td><td>Program the DCFG reg,
+ * with device address received </td></tr>
+ *
+ * <tr><td>GET_DESCRIPTOR </td><td>Gadget Driver </td><td>Return the
+ * requested descriptor</td></tr>
+ *
+ * <tr><td>SET_DESCRIPTOR </td><td>Gadget Driver </td><td>Optional -
+ * not implemented by any of the existing Gadget Drivers.</td></tr>
+ *
+ * <tr><td>SET_CONFIGURATION </td><td>Gadget Driver </td><td>Disable
+ * all EPs and enable EPs for new configuration.</td></tr>
+ *
+ * <tr><td>GET_CONFIGURATION </td><td>Gadget Driver </td><td>Return
+ * the current configuration</td></tr>
+ *
+ * <tr><td>SET_INTERFACE </td><td>Gadget Driver </td><td>Disable all
+ * EPs and enable EPs for new configuration.</td></tr>
+ *
+ * <tr><td>GET_INTERFACE </td><td>Gadget Driver </td><td>Return the
+ * current interface.</td></tr>
+ *
+ * <tr><td>SYNC_FRAME </td><td>PCD </td><td>Display debug
+ * message.</td></tr>
+ * </table>
+ *
+ * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are
+ * processed by pcd_setup. Calling the Function Driver's setup function from
+ * pcd_setup processes the gadget SETUP commands.
+ */
+static inline void pcd_setup(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ usb_device_request_t ctrl = pcd->setup_pkt->req;
+ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+
+ deptsiz0_data_t doeptsize0 = {.d32 = 0 };
+
+#ifdef DWC_UTE_CFI
+ int retval = 0;
+ struct cfi_usb_ctrlrequest cfi_req;
+#endif
+
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+ ctrl.bmRequestType, ctrl.bRequest,
+ UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
+ UGETW(ctrl.wLength));
+#endif
+
+ doeptsize0.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doeptsiz);
+
+ /** @todo handle > 1 setup packet , assert error for now */
+
+ if (core_if->dma_enable && core_if->dma_desc_enable == 0
+ && (doeptsize0.b.supcnt < 2)) {
+ DWC_ERROR
+ ("\n\n----------- CANNOT handle > 1 setup packet in DMA mode\n\n");
+ }
+
+ /* Clean up the request queue */
+ dwc_otg_request_nuke(ep0);
+ ep0->stopped = 0;
+
+ if (ctrl.bmRequestType & UE_DIR_IN) {
+ ep0->dwc_ep.is_in = 1;
+ pcd->ep0state = EP0_IN_DATA_PHASE;
+ } else {
+ ep0->dwc_ep.is_in = 0;
+ pcd->ep0state = EP0_OUT_DATA_PHASE;
+ }
+
+ if (UGETW(ctrl.wLength) == 0) {
+ ep0->dwc_ep.is_in = 1;
+ pcd->ep0state = EP0_IN_STATUS_PHASE;
+ }
+
+ if (UT_GET_TYPE(ctrl.bmRequestType) != UT_STANDARD) {
+
+#ifdef DWC_UTE_CFI
+ DWC_MEMCPY(&cfi_req, &ctrl, sizeof(usb_device_request_t));
+
+ //printk(KERN_ALERT "CFI: req_type=0x%02x; req=0x%02x\n",
+ ctrl.bRequestType, ctrl.bRequest);
+ if (UT_GET_TYPE(cfi_req.bRequestType) == UT_VENDOR) {
+ if (cfi_req.bRequest > 0xB0 && cfi_req.bRequest < 0xBF) {
+ retval = cfi_setup(pcd, &cfi_req);
+ if (retval < 0) {
+ ep0_do_stall(pcd, retval);
+ pcd->ep0_pending = 0;
+ return;
+ }
+
+ /* if need gadget setup then call it and check the retval */
+ if (pcd->cfi->need_gadget_att) {
+ retval =
+ cfi_gadget_setup(pcd,
+ &pcd->
+ cfi->ctrl_req);
+ if (retval < 0) {
+ pcd->ep0_pending = 0;
+ return;
+ }
+ }
+
+ if (pcd->cfi->need_status_in_complete) {
+ do_setup_in_status_phase(pcd);
+ }
+ return;
+ }
+ }
+#endif
+
+ /* handle non-standard (class/vendor) requests in the gadget driver */
+ do_gadget_setup(pcd, &ctrl);
+ return;
+ }
+
+ /** @todo NGS: Handle bad setup packet? */
+
+///////////////////////////////////////////
+//// --- Standard Request handling --- ////
+
+ switch (ctrl.bRequest) {
+ case UR_GET_STATUS:
+ do_get_status(pcd);
+ break;
+
+ case UR_CLEAR_FEATURE:
+ do_clear_feature(pcd);
+ break;
+
+ case UR_SET_FEATURE:
+ do_set_feature(pcd);
+ break;
+
+ case UR_SET_ADDRESS:
+ do_set_address(pcd);
+ break;
+
+ case UR_SET_INTERFACE:
+ case UR_SET_CONFIG:
+// _pcd->request_config = 1; /* Configuration changed */
+ do_gadget_setup(pcd, &ctrl);
+ break;
+
+ case UR_SYNCH_FRAME:
+ do_gadget_setup(pcd, &ctrl);
+ break;
+
+ default:
+ /* Call the Gadget Driver's setup functions */
+ do_gadget_setup(pcd, &ctrl);
+ break;
+ }
+}
+
+/**
+ * This function completes the ep0 control transfer.
+ */
+static int32_t ep0_complete_request(dwc_otg_pcd_ep_t * ep)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ dwc_otg_dev_in_ep_regs_t *in_ep_regs =
+ dev_if->in_ep_regs[ep->dwc_ep.num];
+#ifdef DEBUG_EP0
+ dwc_otg_dev_out_ep_regs_t *out_ep_regs =
+ dev_if->out_ep_regs[ep->dwc_ep.num];
+#endif
+ deptsiz0_data_t deptsiz;
+ dev_dma_desc_sts_t desc_sts;
+ dwc_otg_pcd_request_t *req;
+ int is_last = 0;
+ dwc_otg_pcd_t *pcd = ep->pcd;
+
+#ifdef DWC_UTE_CFI
+ struct cfi_usb_ctrlrequest *ctrlreq;
+ int retval = -DWC_E_NOT_SUPPORTED;
+#endif
+
+ if (pcd->ep0_pending && DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+ if (ep->dwc_ep.is_in) {
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCDV, "Do setup OUT status phase\n");
+#endif
+ do_setup_out_status_phase(pcd);
+ } else {
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCDV, "Do setup IN status phase\n");
+#endif
+
+#ifdef DWC_UTE_CFI
+ ctrlreq = &pcd->cfi->ctrl_req;
+
+ if (UT_GET_TYPE(ctrlreq->bRequestType) == UT_VENDOR) {
+ if (ctrlreq->bRequest > 0xB0
+ && ctrlreq->bRequest < 0xBF) {
+
+ /* Return if the PCD failed to handle the request */
+ if ((retval =
+ pcd->cfi->ops.
+ ctrl_write_complete(pcd->cfi,
+ pcd)) < 0) {
+ CFI_INFO
+ ("ERROR setting a new value in the PCD(%d)\n",
+ retval);
+ ep0_do_stall(pcd, retval);
+ pcd->ep0_pending = 0;
+ return 0;
+ }
+
+ /* If the gadget needs to be notified on the request */
+ if (pcd->cfi->need_gadget_att == 1) {
+ //retval = do_gadget_setup(pcd, &pcd->cfi->ctrl_req);
+ retval =
+ cfi_gadget_setup(pcd,
+ &pcd->cfi->
+ ctrl_req);
+
+ /* Return from the function if the gadget failed to process
+ * the request properly - this should never happen !!!
+ */
+ if (retval < 0) {
+ CFI_INFO
+ ("ERROR setting a new value in the gadget(%d)\n",
+ retval);
+ pcd->ep0_pending = 0;
+ return 0;
+ }
+ }
+
+ CFI_INFO("%s: RETVAL=%d\n", __func__,
+ retval);
+ /* If we hit here then the PCD and the gadget has properly
+ * handled the request - so send the ZLP IN to the host.
+ */
+ /* @todo: MAS - decide whether we need to start the setup
+ * stage based on the need_setup value of the cfi object
+ */
+ do_setup_in_status_phase(pcd);
+ pcd->ep0_pending = 0;
+ return 1;
+ }
+ }
+#endif
+
+ do_setup_in_status_phase(pcd);
+ }
+ pcd->ep0_pending = 0;
+ return 1;
+ }
+
+ if (DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+ return 0;
+ }
+ req = DWC_CIRCLEQ_FIRST(&ep->queue);
+
+ if (pcd->ep0state == EP0_OUT_STATUS_PHASE
+ || pcd->ep0state == EP0_IN_STATUS_PHASE) {
+ is_last = 1;
+ } else if (ep->dwc_ep.is_in) {
+ deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz);
+ if (core_if->dma_desc_enable != 0)
+ desc_sts = dev_if->in_desc_addr->status;
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCDV, "%d len=%d xfersize=%d pktcnt=%d\n",
+ ep->dwc_ep.num, ep->dwc_ep.xfer_len,
+ deptsiz.b.xfersize, deptsiz.b.pktcnt);
+#endif
+
+ if (((core_if->dma_desc_enable == 0)
+ && (deptsiz.b.xfersize == 0))
+ || ((core_if->dma_desc_enable != 0)
+ && (desc_sts.b.bytes == 0))) {
+ req->actual = ep->dwc_ep.xfer_count;
+ /* Is a Zero Len Packet needed? */
+ if (req->sent_zlp) {
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD, "Setup Rx ZLP\n");
+#endif
+ req->sent_zlp = 0;
+ }
+ do_setup_out_status_phase(pcd);
+ }
+ } else {
+ /* ep0-OUT */
+#ifdef DEBUG_EP0
+ deptsiz.d32 = DWC_READ_REG32(&out_ep_regs->doeptsiz);
+ DWC_DEBUGPL(DBG_PCDV, "%d len=%d xsize=%d pktcnt=%d\n",
+ ep->dwc_ep.num, ep->dwc_ep.xfer_len,
+ deptsiz.b.xfersize, deptsiz.b.pktcnt);
+#endif
+ req->actual = ep->dwc_ep.xfer_count;
+
+ /* Is a Zero Len Packet needed? */
+ if (req->sent_zlp) {
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCDV, "Setup Tx ZLP\n");
+#endif
+ req->sent_zlp = 0;
+ }
+ if (core_if->dma_desc_enable == 0)
+ do_setup_in_status_phase(pcd);
+ }
+
+ /* Complete the request */
+ if (is_last) {
+ dwc_otg_request_done(ep, req, 0);
+ ep->dwc_ep.start_xfer_buff = 0;
+ ep->dwc_ep.xfer_buff = 0;
+ ep->dwc_ep.xfer_len = 0;
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef DWC_UTE_CFI
+/**
+ * This function calculates traverses all the CFI DMA descriptors and
+ * and accumulates the bytes that are left to be transfered.
+ *
+ * @return The total bytes left to transfered, or a negative value as failure
+ */
+static inline int cfi_calc_desc_residue(dwc_otg_pcd_ep_t * ep)
+{
+ int32_t ret = 0;
+ int i;
+ struct dwc_otg_dma_desc *ddesc = NULL;
+ struct cfi_ep *cfiep;
+
+ /* See if the pcd_ep has its respective cfi_ep mapped */
+ cfiep = get_cfi_ep_by_pcd_ep(ep->pcd->cfi, ep);
+ if (!cfiep) {
+ CFI_INFO("%s: Failed to find ep\n", __func__);
+ return -1;
+ }
+
+ ddesc = ep->dwc_ep.descs;
+
+ for (i = 0; (i < cfiep->desc_count) && (i < MAX_DMA_DESCS_PER_EP); i++) {
+
+#if defined(PRINT_CFI_DMA_DESCS)
+ print_desc(ddesc, ep->ep.name, i);
+#endif
+ ret += ddesc->status.b.bytes;
+ ddesc++;
+ }
+
+ if (ret)
+ CFI_INFO("!!!!!!!!!! WARNING (%s) - residue=%d\n", __func__,
+ ret);
+
+ return ret;
+}
+#endif
+
+/**
+ * This function completes the request for the EP. If there are
+ * additional requests for the EP in the queue they will be started.
+ */
+static void complete_ep(dwc_otg_pcd_ep_t * ep)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ dwc_otg_dev_in_ep_regs_t *in_ep_regs =
+ dev_if->in_ep_regs[ep->dwc_ep.num];
+ deptsiz_data_t deptsiz;
+ dev_dma_desc_sts_t desc_sts;
+ dwc_otg_pcd_request_t *req = 0;
+ dwc_otg_dev_dma_desc_t *dma_desc;
+ uint32_t byte_count = 0;
+ int is_last = 0;
+ int i;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s() %d-%s\n", __func__, ep->dwc_ep.num,
+ (ep->dwc_ep.is_in ? "IN" : "OUT"));
+
+ /* Get any pending requests */
+ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+ req = DWC_CIRCLEQ_FIRST(&ep->queue);
+ if (!req) {
+ DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep);
+ return;
+ }
+ } else {
+ DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep);
+ return;
+ }
+
+ DWC_DEBUGPL(DBG_PCD, "Requests %d\n", ep->pcd->request_pending);
+
+ if (ep->dwc_ep.is_in) {
+ deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz);
+
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable == 0) {
+ if (deptsiz.b.xfersize == 0
+ && deptsiz.b.pktcnt == 0) {
+ byte_count =
+ ep->dwc_ep.xfer_len -
+ ep->dwc_ep.xfer_count;
+
+ ep->dwc_ep.xfer_buff += byte_count;
+ ep->dwc_ep.dma_addr += byte_count;
+ ep->dwc_ep.xfer_count += byte_count;
+
+ DWC_DEBUGPL(DBG_PCDV,
+ "%d-%s len=%d xfersize=%d pktcnt=%d\n",
+ ep->dwc_ep.num,
+ (ep->dwc_ep.
+ is_in ? "IN" : "OUT"),
+ ep->dwc_ep.xfer_len,
+ deptsiz.b.xfersize,
+ deptsiz.b.pktcnt);
+
+ if (ep->dwc_ep.xfer_len <
+ ep->dwc_ep.total_len) {
+ dwc_otg_ep_start_transfer
+ (core_if, &ep->dwc_ep);
+ } else if (ep->dwc_ep.sent_zlp) {
+ /*
+ * This fragment of code should initiate 0
+ * length transfer in case if it is queued
+ * a transfer with size divisible to EPs max
+ * packet size and with usb_request zero field
+ * is set, which means that after data is transfered,
+ * it is also should be transfered
+ * a 0 length packet at the end. For Slave and
+ * Buffer DMA modes in this case SW has
+ * to initiate 2 transfers one with transfer size,
+ * and the second with 0 size. For Descriptor
+ * DMA mode SW is able to initiate a transfer,
+ * which will handle all the packets including
+ * the last 0 length.
+ */
+ ep->dwc_ep.sent_zlp = 0;
+ dwc_otg_ep_start_zl_transfer
+ (core_if, &ep->dwc_ep);
+ } else {
+ is_last = 1;
+ }
+ } else {
+ if(ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
+ {
+ req->actual = 0;
+ dwc_otg_request_done(ep, req, 0);
+
+ ep->dwc_ep.start_xfer_buff = 0;
+ ep->dwc_ep.xfer_buff = 0;
+ ep->dwc_ep.xfer_len = 0;
+
+ /* If there is a request in the queue start it. */
+ start_next_request(ep);
+ } else
+ DWC_WARN
+ ("Incomplete transfer (%d - %s [siz=%d pkt=%d])\n",
+ ep->dwc_ep.num,
+ (ep->dwc_ep.is_in ? "IN" : "OUT"),
+ deptsiz.b.xfersize,
+ deptsiz.b.pktcnt);
+ }
+ } else {
+ dma_desc = ep->dwc_ep.desc_addr;
+ byte_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+
+#ifdef DWC_UTE_CFI
+ CFI_INFO("%s: BUFFER_MODE=%d\n", __func__,
+ ep->dwc_ep.buff_mode);
+ if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+ int residue;
+
+ residue = cfi_calc_desc_residue(ep);
+ if (residue < 0)
+ return;
+
+ byte_count = residue;
+ } else {
+#endif
+ for (i = 0; i < ep->dwc_ep.desc_cnt;
+ ++i) {
+ desc_sts = dma_desc->status;
+ byte_count += desc_sts.b.bytes;
+ dma_desc++;
+ }
+#ifdef DWC_UTE_CFI
+ }
+#endif
+ if (byte_count == 0) {
+ ep->dwc_ep.xfer_count =
+ ep->dwc_ep.total_len;
+ is_last = 1;
+ } else {
+ DWC_WARN("Incomplete transfer\n");
+ }
+ }
+ } else {
+ if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0) {
+ DWC_DEBUGPL(DBG_PCDV,
+ "%d-%s len=%d xfersize=%d pktcnt=%d\n",
+ ep->dwc_ep.num,
+ ep->dwc_ep.is_in ? "IN" : "OUT",
+ ep->dwc_ep.xfer_len,
+ deptsiz.b.xfersize,
+ deptsiz.b.pktcnt);
+
+ /* Check if the whole transfer was completed,
+ * if no, setup transfer for next portion of data
+ */
+ if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) {
+ dwc_otg_ep_start_transfer(core_if,
+ &ep->dwc_ep);
+ } else if (ep->dwc_ep.sent_zlp) {
+ /*
+ * This fragment of code should initiate 0
+ * length trasfer in case if it is queued
+ * a trasfer with size divisible to EPs max
+ * packet size and with usb_request zero field
+ * is set, which means that after data is transfered,
+ * it is also should be transfered
+ * a 0 length packet at the end. For Slave and
+ * Buffer DMA modes in this case SW has
+ * to initiate 2 transfers one with transfer size,
+ * and the second with 0 size. For Desriptor
+ * DMA mode SW is able to initiate a transfer,
+ * which will handle all the packets including
+ * the last 0 legth.
+ */
+ ep->dwc_ep.sent_zlp = 0;
+ dwc_otg_ep_start_zl_transfer(core_if,
+ &ep->dwc_ep);
+ } else {
+ is_last = 1;
+ }
+ } else {
+ DWC_WARN
+ ("Incomplete transfer (%d-%s [siz=%d pkt=%d])\n",
+ ep->dwc_ep.num,
+ (ep->dwc_ep.is_in ? "IN" : "OUT"),
+ deptsiz.b.xfersize, deptsiz.b.pktcnt);
+ }
+ }
+ } else {
+ dwc_otg_dev_out_ep_regs_t *out_ep_regs =
+ dev_if->out_ep_regs[ep->dwc_ep.num];
+ desc_sts.d32 = 0;
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable) {
+ dma_desc = ep->dwc_ep.desc_addr;
+ byte_count = 0;
+ ep->dwc_ep.sent_zlp = 0;
+
+#ifdef DWC_UTE_CFI
+ CFI_INFO("%s: BUFFER_MODE=%d\n", __func__,
+ ep->dwc_ep.buff_mode);
+ if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+ int residue;
+ residue = cfi_calc_desc_residue(ep);
+ if (residue < 0)
+ return;
+ byte_count = residue;
+ } else {
+#endif
+
+ for (i = 0; i < ep->dwc_ep.desc_cnt;
+ ++i) {
+ desc_sts = dma_desc->status;
+ byte_count += desc_sts.b.bytes;
+ dma_desc++;
+ }
+
+#ifdef DWC_UTE_CFI
+ }
+#endif
+ /* Checking for interrupt Out transfers with not
+ * dword aligned mps sizes
+ */
+ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_INTR &&
+ (ep->dwc_ep.maxpacket%4)) {
+ ep->dwc_ep.xfer_count = ep->dwc_ep.total_len - byte_count;
+ if ((ep->dwc_ep.xfer_len % ep->dwc_ep.maxpacket) &&
+ (ep->dwc_ep.xfer_len/ep->dwc_ep.maxpacket < MAX_DMA_DESC_CNT))
+ ep->dwc_ep.xfer_len -=
+ (ep->dwc_ep.desc_cnt - 1) * ep->dwc_ep.maxpacket +
+ ep->dwc_ep.xfer_len % ep->dwc_ep.maxpacket;
+ else
+ ep->dwc_ep.xfer_len -=
+ ep->dwc_ep.desc_cnt * ep->dwc_ep.maxpacket;
+ if (ep->dwc_ep.xfer_len > 0) {
+ dwc_otg_ep_start_transfer(core_if,
+ &ep->dwc_ep);
+ } else {
+ is_last = 1;
+ }
+ } else {
+ ep->dwc_ep.xfer_count = ep->dwc_ep.total_len
+ - byte_count +
+ ((4 - (ep->dwc_ep.total_len & 0x3)) & 0x3);
+ is_last = 1;
+ }
+ } else {
+ deptsiz.d32 = 0;
+ deptsiz.d32 =
+ DWC_READ_REG32(&out_ep_regs->doeptsiz);
+
+ byte_count = (ep->dwc_ep.xfer_len -
+ ep->dwc_ep.xfer_count -
+ deptsiz.b.xfersize);
+ ep->dwc_ep.xfer_buff += byte_count;
+ ep->dwc_ep.dma_addr += byte_count;
+ ep->dwc_ep.xfer_count += byte_count;
+
+ /* Check if the whole transfer was completed,
+ * if no, setup transfer for next portion of data
+ */
+ if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) {
+ dwc_otg_ep_start_transfer(core_if,
+ &ep->dwc_ep);
+ } else if (ep->dwc_ep.sent_zlp) {
+ /*
+ * This fragment of code should initiate 0
+ * length trasfer in case if it is queued
+ * a trasfer with size divisible to EPs max
+ * packet size and with usb_request zero field
+ * is set, which means that after data is transfered,
+ * it is also should be transfered
+ * a 0 length packet at the end. For Slave and
+ * Buffer DMA modes in this case SW has
+ * to initiate 2 transfers one with transfer size,
+ * and the second with 0 size. For Desriptor
+ * DMA mode SW is able to initiate a transfer,
+ * which will handle all the packets including
+ * the last 0 legth.
+ */
+ ep->dwc_ep.sent_zlp = 0;
+ dwc_otg_ep_start_zl_transfer(core_if,
+ &ep->dwc_ep);
+ } else {
+ is_last = 1;
+ }
+ }
+ } else {
+ /* Check if the whole transfer was completed,
+ * if no, setup transfer for next portion of data
+ */
+ if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) {
+ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+ } else if (ep->dwc_ep.sent_zlp) {
+ /*
+ * This fragment of code should initiate 0
+ * length transfer in case if it is queued
+ * a transfer with size divisible to EPs max
+ * packet size and with usb_request zero field
+ * is set, which means that after data is transfered,
+ * it is also should be transfered
+ * a 0 length packet at the end. For Slave and
+ * Buffer DMA modes in this case SW has
+ * to initiate 2 transfers one with transfer size,
+ * and the second with 0 size. For Descriptor
+ * DMA mode SW is able to initiate a transfer,
+ * which will handle all the packets including
+ * the last 0 length.
+ */
+ ep->dwc_ep.sent_zlp = 0;
+ dwc_otg_ep_start_zl_transfer(core_if,
+ &ep->dwc_ep);
+ } else {
+ is_last = 1;
+ }
+ }
+
+ DWC_DEBUGPL(DBG_PCDV,
+ "addr %p, %d-%s len=%d cnt=%d xsize=%d pktcnt=%d\n",
+ &out_ep_regs->doeptsiz, ep->dwc_ep.num,
+ ep->dwc_ep.is_in ? "IN" : "OUT",
+ ep->dwc_ep.xfer_len, ep->dwc_ep.xfer_count,
+ deptsiz.b.xfersize, deptsiz.b.pktcnt);
+ }
+
+ /* Complete the request */
+ if (is_last) {
+#ifdef DWC_UTE_CFI
+ if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+ req->actual = ep->dwc_ep.cfi_req_len - byte_count;
+ } else {
+#endif
+ req->actual = ep->dwc_ep.xfer_count;
+#ifdef DWC_UTE_CFI
+ }
+#endif
+ if (req->dw_align_buf) {
+ if (!ep->dwc_ep.is_in) {
+ dwc_memcpy(req->buf, req->dw_align_buf, req->length);
+ }
+ DWC_DMA_FREE(req->length, req->dw_align_buf,
+ req->dw_align_buf_dma);
+ }
+
+ dwc_otg_request_done(ep, req, 0);
+
+ ep->dwc_ep.start_xfer_buff = 0;
+ ep->dwc_ep.xfer_buff = 0;
+ ep->dwc_ep.xfer_len = 0;
+
+ /* If there is a request in the queue start it. */
+ start_next_request(ep);
+ }
+}
+
+#ifdef DWC_EN_ISOC
+
+/**
+ * This function BNA interrupt for Isochronous EPs
+ *
+ */
+static void dwc_otg_pcd_handle_iso_bna(dwc_otg_pcd_ep_t * ep)
+{
+ dwc_ep_t *dwc_ep = &ep->dwc_ep;
+ volatile uint32_t *addr;
+ depctl_data_t depctl = {.d32 = 0 };
+ dwc_otg_pcd_t *pcd = ep->pcd;
+ dwc_otg_dev_dma_desc_t *dma_desc;
+ int i;
+
+ dma_desc =
+ dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * (dwc_ep->proc_buf_num);
+
+ if (dwc_ep->is_in) {
+ dev_dma_desc_sts_t sts = {.d32 = 0 };
+ for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) {
+ sts.d32 = dma_desc->status.d32;
+ sts.b_iso_in.bs = BS_HOST_READY;
+ dma_desc->status.d32 = sts.d32;
+ }
+ } else {
+ dev_dma_desc_sts_t sts = {.d32 = 0 };
+ for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) {
+ sts.d32 = dma_desc->status.d32;
+ sts.b_iso_out.bs = BS_HOST_READY;
+ dma_desc->status.d32 = sts.d32;
+ }
+ }
+
+ if (dwc_ep->is_in == 0) {
+ addr =
+ &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->
+ num]->doepctl;
+ } else {
+ addr =
+ &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
+ }
+ depctl.b.epena = 1;
+ DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32);
+}
+
+/**
+ * This function sets latest iso packet information(non-PTI mode)
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void set_current_pkt_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ deptsiz_data_t deptsiz = {.d32 = 0 };
+ dma_addr_t dma_addr;
+ uint32_t offset;
+
+ if (ep->proc_buf_num)
+ dma_addr = ep->dma_addr1;
+ else
+ dma_addr = ep->dma_addr0;
+
+ if (ep->is_in) {
+ deptsiz.d32 =
+ DWC_READ_REG32(&core_if->dev_if->
+ in_ep_regs[ep->num]->dieptsiz);
+ offset = ep->data_per_frame;
+ } else {
+ deptsiz.d32 =
+ DWC_READ_REG32(&core_if->dev_if->
+ out_ep_regs[ep->num]->doeptsiz);
+ offset =
+ ep->data_per_frame +
+ (0x4 & (0x4 - (ep->data_per_frame & 0x3)));
+ }
+
+ if (!deptsiz.b.xfersize) {
+ ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame;
+ ep->pkt_info[ep->cur_pkt].offset =
+ ep->cur_pkt_dma_addr - dma_addr;
+ ep->pkt_info[ep->cur_pkt].status = 0;
+ } else {
+ ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame;
+ ep->pkt_info[ep->cur_pkt].offset =
+ ep->cur_pkt_dma_addr - dma_addr;
+ ep->pkt_info[ep->cur_pkt].status = -DWC_E_NO_DATA;
+ }
+ ep->cur_pkt_addr += offset;
+ ep->cur_pkt_dma_addr += offset;
+ ep->cur_pkt++;
+}
+
+/**
+ * This function sets latest iso packet information(DDMA mode)
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dwc_ep The EP to start the transfer on.
+ *
+ */
+static void set_ddma_iso_pkts_info(dwc_otg_core_if_t * core_if,
+ dwc_ep_t * dwc_ep)
+{
+ dwc_otg_dev_dma_desc_t *dma_desc;
+ dev_dma_desc_sts_t sts = {.d32 = 0 };
+ iso_pkt_info_t *iso_packet;
+ uint32_t data_per_desc;
+ uint32_t offset;
+ int i, j;
+
+ iso_packet = dwc_ep->pkt_info;
+
+ /** Reinit closed DMA Descriptors*/
+ /** ISO OUT EP */
+ if (dwc_ep->is_in == 0) {
+ dma_desc =
+ dwc_ep->iso_desc_addr +
+ dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
+ offset = 0;
+
+ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+ i += dwc_ep->pkt_per_frm) {
+ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
+ data_per_desc =
+ ((j + 1) * dwc_ep->maxpacket >
+ dwc_ep->
+ data_per_frame) ? dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+ data_per_desc +=
+ (data_per_desc % 4) ? (4 -
+ data_per_desc %
+ 4) : 0;
+
+ sts.d32 = dma_desc->status.d32;
+
+ /* Write status in iso_packet_decsriptor */
+ iso_packet->status =
+ sts.b_iso_out.rxsts +
+ (sts.b_iso_out.bs ^ BS_DMA_DONE);
+ if (iso_packet->status) {
+ iso_packet->status = -DWC_E_NO_DATA;
+ }
+
+ /* Received data length */
+ if (!sts.b_iso_out.rxbytes) {
+ iso_packet->length =
+ data_per_desc -
+ sts.b_iso_out.rxbytes;
+ } else {
+ iso_packet->length =
+ data_per_desc -
+ sts.b_iso_out.rxbytes + (4 -
+ dwc_ep->data_per_frame
+ % 4);
+ }
+
+ iso_packet->offset = offset;
+
+ offset += data_per_desc;
+ dma_desc++;
+ iso_packet++;
+ }
+ }
+
+ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
+ data_per_desc =
+ ((j + 1) * dwc_ep->maxpacket >
+ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+ data_per_desc +=
+ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+
+ sts.d32 = dma_desc->status.d32;
+
+ /* Write status in iso_packet_decsriptor */
+ iso_packet->status =
+ sts.b_iso_out.rxsts +
+ (sts.b_iso_out.bs ^ BS_DMA_DONE);
+ if (iso_packet->status) {
+ iso_packet->status = -DWC_E_NO_DATA;
+ }
+
+ /* Received data length */
+ iso_packet->length =
+ dwc_ep->data_per_frame - sts.b_iso_out.rxbytes;
+
+ iso_packet->offset = offset;
+
+ offset += data_per_desc;
+ iso_packet++;
+ dma_desc++;
+ }
+
+ sts.d32 = dma_desc->status.d32;
+
+ /* Write status in iso_packet_decsriptor */
+ iso_packet->status =
+ sts.b_iso_out.rxsts + (sts.b_iso_out.bs ^ BS_DMA_DONE);
+ if (iso_packet->status) {
+ iso_packet->status = -DWC_E_NO_DATA;
+ }
+ /* Received data length */
+ if (!sts.b_iso_out.rxbytes) {
+ iso_packet->length =
+ dwc_ep->data_per_frame - sts.b_iso_out.rxbytes;
+ } else {
+ iso_packet->length =
+ dwc_ep->data_per_frame - sts.b_iso_out.rxbytes +
+ (4 - dwc_ep->data_per_frame % 4);
+ }
+
+ iso_packet->offset = offset;
+ } else {
+/** ISO IN EP */
+
+ dma_desc =
+ dwc_ep->iso_desc_addr +
+ dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
+
+ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) {
+ sts.d32 = dma_desc->status.d32;
+
+ /* Write status in iso packet descriptor */
+ iso_packet->status =
+ sts.b_iso_in.txsts +
+ (sts.b_iso_in.bs ^ BS_DMA_DONE);
+ if (iso_packet->status != 0) {
+ iso_packet->status = -DWC_E_NO_DATA;
+
+ }
+ /* Bytes has been transfered */
+ iso_packet->length =
+ dwc_ep->data_per_frame - sts.b_iso_in.txbytes;
+
+ dma_desc++;
+ iso_packet++;
+ }
+
+ sts.d32 = dma_desc->status.d32;
+ while (sts.b_iso_in.bs == BS_DMA_BUSY) {
+ sts.d32 = dma_desc->status.d32;
+ }
+
+ /* Write status in iso packet descriptor ??? do be done with ERROR codes */
+ iso_packet->status =
+ sts.b_iso_in.txsts + (sts.b_iso_in.bs ^ BS_DMA_DONE);
+ if (iso_packet->status != 0) {
+ iso_packet->status = -DWC_E_NO_DATA;
+ }
+
+ /* Bytes has been transfered */
+ iso_packet->length =
+ dwc_ep->data_per_frame - sts.b_iso_in.txbytes;
+ }
+}
+
+/**
+ * This function reinitialize DMA Descriptors for Isochronous transfer
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dwc_ep The EP to start the transfer on.
+ *
+ */
+static void reinit_ddma_iso_xfer(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep)
+{
+ int i, j;
+ dwc_otg_dev_dma_desc_t *dma_desc;
+ dma_addr_t dma_ad;
+ volatile uint32_t *addr;
+ dev_dma_desc_sts_t sts = {.d32 = 0 };
+ uint32_t data_per_desc;
+
+ if (dwc_ep->is_in == 0) {
+ addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl;
+ } else {
+ addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
+ }
+
+ if (dwc_ep->proc_buf_num == 0) {
+ /** Buffer 0 descriptors setup */
+ dma_ad = dwc_ep->dma_addr0;
+ } else {
+ /** Buffer 1 descriptors setup */
+ dma_ad = dwc_ep->dma_addr1;
+ }
+
+ /** Reinit closed DMA Descriptors*/
+ /** ISO OUT EP */
+ if (dwc_ep->is_in == 0) {
+ dma_desc =
+ dwc_ep->iso_desc_addr +
+ dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
+
+ sts.b_iso_out.bs = BS_HOST_READY;
+ sts.b_iso_out.rxsts = 0;
+ sts.b_iso_out.l = 0;
+ sts.b_iso_out.sp = 0;
+ sts.b_iso_out.ioc = 0;
+ sts.b_iso_out.pid = 0;
+ sts.b_iso_out.framenum = 0;
+
+ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+ i += dwc_ep->pkt_per_frm) {
+ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
+ data_per_desc =
+ ((j + 1) * dwc_ep->maxpacket >
+ dwc_ep->
+ data_per_frame) ? dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+ data_per_desc +=
+ (data_per_desc % 4) ? (4 -
+ data_per_desc %
+ 4) : 0;
+ sts.b_iso_out.rxbytes = data_per_desc;
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ dma_ad += data_per_desc;
+ dma_desc++;
+ }
+ }
+
+ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
+
+ data_per_desc =
+ ((j + 1) * dwc_ep->maxpacket >
+ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+ data_per_desc +=
+ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+ sts.b_iso_out.rxbytes = data_per_desc;
+
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ dma_desc++;
+ dma_ad += data_per_desc;
+ }
+
+ sts.b_iso_out.ioc = 1;
+ sts.b_iso_out.l = dwc_ep->proc_buf_num;
+
+ data_per_desc =
+ ((j + 1) * dwc_ep->maxpacket >
+ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+ j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+ data_per_desc +=
+ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+ sts.b_iso_out.rxbytes = data_per_desc;
+
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+ } else {
+/** ISO IN EP */
+
+ dma_desc =
+ dwc_ep->iso_desc_addr +
+ dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
+
+ sts.b_iso_in.bs = BS_HOST_READY;
+ sts.b_iso_in.txsts = 0;
+ sts.b_iso_in.sp = 0;
+ sts.b_iso_in.ioc = 0;
+ sts.b_iso_in.pid = dwc_ep->pkt_per_frm;
+ sts.b_iso_in.framenum = dwc_ep->next_frame;
+ sts.b_iso_in.txbytes = dwc_ep->data_per_frame;
+ sts.b_iso_in.l = 0;
+
+ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) {
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ sts.b_iso_in.framenum += dwc_ep->bInterval;
+ dma_ad += dwc_ep->data_per_frame;
+ dma_desc++;
+ }
+
+ sts.b_iso_in.ioc = 1;
+ sts.b_iso_in.l = dwc_ep->proc_buf_num;
+
+ dma_desc->buf = dma_ad;
+ dma_desc->status.d32 = sts.d32;
+
+ dwc_ep->next_frame =
+ sts.b_iso_in.framenum + dwc_ep->bInterval * 1;
+ }
+ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1;
+}
+
+/**
+ * This function is to handle Iso EP transfer complete interrupt
+ * in case Iso out packet was dropped
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dwc_ep The EP for wihich transfer complete was asserted
+ *
+ */
+static uint32_t handle_iso_out_pkt_dropped(dwc_otg_core_if_t * core_if,
+ dwc_ep_t * dwc_ep)
+{
+ uint32_t dma_addr;
+ uint32_t drp_pkt;
+ uint32_t drp_pkt_cnt;
+ deptsiz_data_t deptsiz = {.d32 = 0 };
+ depctl_data_t depctl = {.d32 = 0 };
+ int i;
+
+ deptsiz.d32 =
+ DWC_READ_REG32(&core_if->dev_if->
+ out_ep_regs[dwc_ep->num]->doeptsiz);
+
+ drp_pkt = dwc_ep->pkt_cnt - deptsiz.b.pktcnt;
+ drp_pkt_cnt = dwc_ep->pkt_per_frm - (drp_pkt % dwc_ep->pkt_per_frm);
+
+ /* Setting dropped packets status */
+ for (i = 0; i < drp_pkt_cnt; ++i) {
+ dwc_ep->pkt_info[drp_pkt].status = -DWC_E_NO_DATA;
+ drp_pkt++;
+ deptsiz.b.pktcnt--;
+ }
+
+ if (deptsiz.b.pktcnt > 0) {
+ deptsiz.b.xfersize =
+ dwc_ep->xfer_len - (dwc_ep->pkt_cnt -
+ deptsiz.b.pktcnt) * dwc_ep->maxpacket;
+ } else {
+ deptsiz.b.xfersize = 0;
+ deptsiz.b.pktcnt = 0;
+ }
+
+ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz,
+ deptsiz.d32);
+
+ if (deptsiz.b.pktcnt > 0) {
+ if (dwc_ep->proc_buf_num) {
+ dma_addr =
+ dwc_ep->dma_addr1 + dwc_ep->xfer_len -
+ deptsiz.b.xfersize;
+ } else {
+ dma_addr =
+ dwc_ep->dma_addr0 + dwc_ep->xfer_len -
+ deptsiz.b.xfersize;;
+ }
+
+ DWC_WRITE_REG32(&core_if->dev_if->
+ out_ep_regs[dwc_ep->num]->doepdma, dma_addr);
+
+ /** Re-enable endpoint, clear nak */
+ depctl.d32 = 0;
+ depctl.b.epena = 1;
+ depctl.b.cnak = 1;
+
+ DWC_MODIFY_REG32(&core_if->dev_if->
+ out_ep_regs[dwc_ep->num]->doepctl, depctl.d32,
+ depctl.d32);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/**
+ * This function sets iso packets information(PTI mode)
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+static uint32_t set_iso_pkts_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+ int i, j;
+ dma_addr_t dma_ad;
+ iso_pkt_info_t *packet_info = ep->pkt_info;
+ uint32_t offset;
+ uint32_t frame_data;
+ deptsiz_data_t deptsiz;
+
+ if (ep->proc_buf_num == 0) {
+ /** Buffer 0 descriptors setup */
+ dma_ad = ep->dma_addr0;
+ } else {
+ /** Buffer 1 descriptors setup */
+ dma_ad = ep->dma_addr1;
+ }
+
+ if (ep->is_in) {
+ deptsiz.d32 =
+ DWC_READ_REG32(&core_if->dev_if->
+ in_ep_regs[ep->num]->dieptsiz);
+ } else {
+ deptsiz.d32 =
+ DWC_READ_REG32(&core_if->dev_if->
+ out_ep_regs[ep->num]->doeptsiz);
+ }
+
+ if (!deptsiz.b.xfersize) {
+ offset = 0;
+ for (i = 0; i < ep->pkt_cnt; i += ep->pkt_per_frm) {
+ frame_data = ep->data_per_frame;
+ for (j = 0; j < ep->pkt_per_frm; ++j) {
+
+ /* Packet status - is not set as initially
+ * it is set to 0 and if packet was sent
+ successfully, status field will remain 0*/
+
+ /* Bytes has been transfered */
+ packet_info->length =
+ (ep->maxpacket <
+ frame_data) ? ep->maxpacket : frame_data;
+
+ /* Received packet offset */
+ packet_info->offset = offset;
+ offset += packet_info->length;
+ frame_data -= packet_info->length;
+
+ packet_info++;
+ }
+ }
+ return 1;
+ } else {
+ /* This is a workaround for in case of Transfer Complete with
+ * PktDrpSts interrupts merging - in this case Transfer complete
+ * interrupt for Isoc Out Endpoint is asserted without PktDrpSts
+ * set and with DOEPTSIZ register non zero. Investigations showed,
+ * that this happens when Out packet is dropped, but because of
+ * interrupts merging during first interrupt handling PktDrpSts
+ * bit is cleared and for next merged interrupts it is not reset.
+ * In this case SW hadles the interrupt as if PktDrpSts bit is set.
+ */
+ if (ep->is_in) {
+ return 1;
+ } else {
+ return handle_iso_out_pkt_dropped(core_if, ep);
+ }
+ }
+}
+
+/**
+ * This function is to handle Iso EP transfer complete interrupt
+ *
+ * @param pcd The PCD
+ * @param ep The EP for which transfer complete was asserted
+ *
+ */
+static void complete_iso_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd);
+ dwc_ep_t *dwc_ep = &ep->dwc_ep;
+ uint8_t is_last = 0;
+
+ if (ep->dwc_ep.next_frame == 0xffffffff) {
+ DWC_WARN("Next frame is not set!\n");
+ return;
+ }
+
+ if (core_if->dma_enable) {
+ if (core_if->dma_desc_enable) {
+ set_ddma_iso_pkts_info(core_if, dwc_ep);
+ reinit_ddma_iso_xfer(core_if, dwc_ep);
+ is_last = 1;
+ } else {
+ if (core_if->pti_enh_enable) {
+ if (set_iso_pkts_info(core_if, dwc_ep)) {
+ dwc_ep->proc_buf_num =
+ (dwc_ep->proc_buf_num ^ 1) & 0x1;
+ dwc_otg_iso_ep_start_buf_transfer
+ (core_if, dwc_ep);
+ is_last = 1;
+ }
+ } else {
+ set_current_pkt_info(core_if, dwc_ep);
+ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+ is_last = 1;
+ dwc_ep->cur_pkt = 0;
+ dwc_ep->proc_buf_num =
+ (dwc_ep->proc_buf_num ^ 1) & 0x1;
+ if (dwc_ep->proc_buf_num) {
+ dwc_ep->cur_pkt_addr =
+ dwc_ep->xfer_buff1;
+ dwc_ep->cur_pkt_dma_addr =
+ dwc_ep->dma_addr1;
+ } else {
+ dwc_ep->cur_pkt_addr =
+ dwc_ep->xfer_buff0;
+ dwc_ep->cur_pkt_dma_addr =
+ dwc_ep->dma_addr0;
+ }
+
+ }
+ dwc_otg_iso_ep_start_frm_transfer(core_if,
+ dwc_ep);
+ }
+ }
+ } else {
+ set_current_pkt_info(core_if, dwc_ep);
+ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+ is_last = 1;
+ dwc_ep->cur_pkt = 0;
+ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1;
+ if (dwc_ep->proc_buf_num) {
+ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1;
+ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1;
+ } else {
+ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0;
+ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0;
+ }
+
+ }
+ dwc_otg_iso_ep_start_frm_transfer(core_if, dwc_ep);
+ }
+ if (is_last)
+ dwc_otg_iso_buffer_done(pcd, ep, ep->iso_req_handle);
+}
+#endif /* DWC_EN_ISOC */
+
+/**
+ * This function handle BNA interrupt for Non Isochronous EPs
+ *
+ */
+static void dwc_otg_pcd_handle_noniso_bna(dwc_otg_pcd_ep_t * ep)
+{
+ dwc_ep_t *dwc_ep = &ep->dwc_ep;
+ volatile uint32_t *addr;
+ depctl_data_t depctl = {.d32 = 0 };
+ dwc_otg_pcd_t *pcd = ep->pcd;
+ dwc_otg_dev_dma_desc_t *dma_desc;
+ dev_dma_desc_sts_t sts = {.d32 = 0 };
+ dwc_otg_core_if_t *core_if = ep->pcd->core_if;
+ int i, start;
+
+ if (!dwc_ep->desc_cnt)
+ DWC_WARN("Descriptor count = %d\n", dwc_ep->desc_cnt);
+
+ if (core_if->core_params->cont_on_bna && !dwc_ep->is_in
+ && dwc_ep->type != DWC_OTG_EP_TYPE_CONTROL) {
+ uint32_t doepdma;
+ dwc_otg_dev_out_ep_regs_t *out_regs =
+ core_if->dev_if->out_ep_regs[dwc_ep->num];
+ doepdma = DWC_READ_REG32(&(out_regs->doepdma));
+ start = (doepdma - dwc_ep->dma_desc_addr)/sizeof(dwc_otg_dev_dma_desc_t);
+ dma_desc = &(dwc_ep->desc_addr[start]);
+ } else {
+ start = 0;
+ dma_desc = dwc_ep->desc_addr;
+ }
+
+
+ for (i = start; i < dwc_ep->desc_cnt; ++i, ++dma_desc) {
+ sts.d32 = dma_desc->status.d32;
+ sts.b.bs = BS_HOST_READY;
+ dma_desc->status.d32 = sts.d32;
+ }
+
+ if (dwc_ep->is_in == 0) {
+ addr =
+ &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->
+ num]->doepctl;
+ } else {
+ addr =
+ &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
+ }
+ depctl.b.epena = 1;
+ depctl.b.cnak = 1;
+ DWC_MODIFY_REG32(addr, 0, depctl.d32);
+}
+
+/**
+ * This function handles EP0 Control transfers.
+ *
+ * The state of the control tranfers are tracked in
+ * <code>ep0state</code>.
+ */
+static void handle_ep0(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+ dev_dma_desc_sts_t desc_sts;
+ deptsiz0_data_t deptsiz;
+ uint32_t byte_count;
+
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__);
+ print_ep0_state(pcd);
+#endif
+
+// DWC_PRINTF("HANDLE EP0\n");
+
+ switch (pcd->ep0state) {
+ case EP0_DISCONNECT:
+ break;
+
+ case EP0_IDLE:
+ pcd->request_config = 0;
+
+ pcd_setup(pcd);
+ break;
+
+ case EP0_IN_DATA_PHASE:
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD, "DATA_IN EP%d-%s: type=%d, mps=%d\n",
+ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"),
+ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket);
+#endif
+
+ if (core_if->dma_enable != 0) {
+ /*
+ * For EP0 we can only program 1 packet at a time so we
+ * need to do the make calculations after each complete.
+ * Call write_packet to make the calculations, as in
+ * slave mode, and use those values to determine if we
+ * can complete.
+ */
+ if (core_if->dma_desc_enable == 0) {
+ deptsiz.d32 =
+ DWC_READ_REG32(&core_if->
+ dev_if->in_ep_regs[0]->
+ dieptsiz);
+ byte_count =
+ ep0->dwc_ep.xfer_len - deptsiz.b.xfersize;
+ } else {
+ desc_sts =
+ core_if->dev_if->in_desc_addr->status;
+ byte_count =
+ ep0->dwc_ep.xfer_len - desc_sts.b.bytes;
+ }
+ ep0->dwc_ep.xfer_count += byte_count;
+ ep0->dwc_ep.xfer_buff += byte_count;
+ ep0->dwc_ep.dma_addr += byte_count;
+ }
+ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) {
+ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
+ &ep0->dwc_ep);
+ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+ } else if (ep0->dwc_ep.sent_zlp) {
+ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
+ &ep0->dwc_ep);
+ ep0->dwc_ep.sent_zlp = 0;
+ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+ } else {
+ ep0_complete_request(ep0);
+ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n");
+ }
+ break;
+ case EP0_OUT_DATA_PHASE:
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD, "DATA_OUT EP%d-%s: type=%d, mps=%d\n",
+ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"),
+ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket);
+#endif
+ if (core_if->dma_enable != 0) {
+ if (core_if->dma_desc_enable == 0) {
+ deptsiz.d32 =
+ DWC_READ_REG32(&core_if->
+ dev_if->out_ep_regs[0]->
+ doeptsiz);
+ byte_count =
+ ep0->dwc_ep.maxpacket - deptsiz.b.xfersize;
+ } else {
+ desc_sts =
+ core_if->dev_if->out_desc_addr->status;
+ byte_count =
+ ep0->dwc_ep.maxpacket - desc_sts.b.bytes;
+ }
+ ep0->dwc_ep.xfer_count += byte_count;
+ ep0->dwc_ep.xfer_buff += byte_count;
+ ep0->dwc_ep.dma_addr += byte_count;
+ }
+ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) {
+ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
+ &ep0->dwc_ep);
+ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+ } else if (ep0->dwc_ep.sent_zlp) {
+ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
+ &ep0->dwc_ep);
+ ep0->dwc_ep.sent_zlp = 0;
+ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+ } else {
+ ep0_complete_request(ep0);
+ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n");
+ }
+ break;
+
+ case EP0_IN_STATUS_PHASE:
+ case EP0_OUT_STATUS_PHASE:
+ DWC_DEBUGPL(DBG_PCD, "CASE: EP0_STATUS\n");
+ ep0_complete_request(ep0);
+ pcd->ep0state = EP0_IDLE;
+ ep0->stopped = 1;
+ ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */
+
+ /* Prepare for more SETUP Packets */
+ if (core_if->dma_enable) {
+ ep0_out_start(core_if, pcd);
+ }
+ break;
+
+ case EP0_STALL:
+ DWC_ERROR("EP0 STALLed, should not get here pcd_setup()\n");
+ break;
+ }
+#ifdef DEBUG_EP0
+ print_ep0_state(pcd);
+#endif
+}
+
+/**
+ * Restart transfer
+ */
+static void restart_transfer(dwc_otg_pcd_t * pcd, const uint32_t epnum)
+{
+ dwc_otg_core_if_t *core_if;
+ dwc_otg_dev_if_t *dev_if;
+ deptsiz_data_t dieptsiz = {.d32 = 0 };
+ dwc_otg_pcd_ep_t *ep;
+
+ ep = get_in_ep(pcd, epnum);
+
+#ifdef DWC_EN_ISOC
+ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+ return;
+ }
+#endif /* DWC_EN_ISOC */
+
+ core_if = GET_CORE_IF(pcd);
+ dev_if = core_if->dev_if;
+
+ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dieptsiz);
+
+ DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x xfer_len=%0x"
+ " stopped=%d\n", ep->dwc_ep.xfer_buff,
+ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len, ep->stopped);
+ /*
+ * If xfersize is 0 and pktcnt in not 0, resend the last packet.
+ */
+ if (dieptsiz.b.pktcnt && dieptsiz.b.xfersize == 0 &&
+ ep->dwc_ep.start_xfer_buff != 0) {
+ if (ep->dwc_ep.total_len <= ep->dwc_ep.maxpacket) {
+ ep->dwc_ep.xfer_count = 0;
+ ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff;
+ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count;
+ } else {
+ ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket;
+ /* convert packet size to dwords. */
+ ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket;
+ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count;
+ }
+ ep->stopped = 0;
+ DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x "
+ "xfer_len=%0x stopped=%d\n",
+ ep->dwc_ep.xfer_buff,
+ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len,
+ ep->stopped);
+ if (epnum == 0) {
+ dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+ } else {
+ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+ }
+ }
+}
+
+/*
+ * This function create new nextep sequnce based on Learn Queue.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ */
+void predict_nextep_seq( dwc_otg_core_if_t * core_if)
+{
+ dwc_otg_device_global_regs_t *dev_global_regs =
+ core_if->dev_if->dev_global_regs;
+ const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth;
+ /* Number of Token Queue Registers */
+ const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
+ dtknq1_data_t dtknqr1;
+ uint32_t in_tkn_epnums[4];
+ uint8_t seqnum[MAX_EPS_CHANNELS];
+ uint8_t intkn_seq[TOKEN_Q_DEPTH];
+ grstctl_t resetctl = {.d32 = 0 };
+ uint8_t temp;
+ int ndx = 0;
+ int start = 0;
+ int end = 0;
+ int sort_done = 0;
+ int i = 0;
+ volatile uint32_t *addr = &dev_global_regs->dtknqr1;
+
+
+ DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH);
+
+ /* Read the DTKNQ Registers */
+ for (i = 0; i < DTKNQ_REG_CNT; i++) {
+ in_tkn_epnums[i] = DWC_READ_REG32(addr);
+ DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i + 1,
+ in_tkn_epnums[i]);
+ if (addr == &dev_global_regs->dvbusdis) {
+ addr = &dev_global_regs->dtknqr3_dthrctl;
+ } else {
+ ++addr;
+ }
+
+ }
+
+ /* Copy the DTKNQR1 data to the bit field. */
+ dtknqr1.d32 = in_tkn_epnums[0];
+ if (dtknqr1.b.wrap_bit) {
+ ndx = dtknqr1.b.intknwptr;
+ end = ndx -1;
+ if (end < 0)
+ end = TOKEN_Q_DEPTH -1;
+ } else {
+ ndx = 0;
+ end = dtknqr1.b.intknwptr -1;
+ if (end < 0)
+ end = 0;
+ }
+ start = ndx;
+
+ /* Fill seqnum[] by initial values: EP number + 31 */
+ for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+ seqnum[i] = i +31;
+ }
+
+ /* Fill intkn_seq[] from in_tkn_epnums[0] */
+ for (i=0; i < 6; i++)
+ intkn_seq[i] = (in_tkn_epnums[0] >> ((7-i) * 4)) & 0xf;
+
+ if (TOKEN_Q_DEPTH > 6) {
+ /* Fill intkn_seq[] from in_tkn_epnums[1] */
+ for (i=6; i < 14; i++)
+ intkn_seq[i] = (in_tkn_epnums[1] >> ((7-(i-6)) * 4)) & 0xf;
+ }
+
+ if (TOKEN_Q_DEPTH > 14) {
+ /* Fill intkn_seq[] from in_tkn_epnums[1] */
+ for (i=14; i < 22; i++)
+ intkn_seq[i] = (in_tkn_epnums[2] >> ((7-(i-14)) * 4)) & 0xf;
+ }
+
+ if (TOKEN_Q_DEPTH > 22) {
+ /* Fill intkn_seq[] from in_tkn_epnums[1] */
+ for (i=22; i < 30; i++)
+ intkn_seq[i] = (in_tkn_epnums[3] >> ((7-(i-22)) * 4)) & 0xf;
+ }
+
+ DWC_DEBUGPL(DBG_PCDV,"%s start=%d end=%d intkn_seq[]:\n", __func__, start, end);
+ for (i=0; i<TOKEN_Q_DEPTH; i++)
+ DWC_DEBUGPL(DBG_PCDV,"%d\n", intkn_seq[i]);
+
+ /* Update seqnum based on intkn_seq[] */
+ i = 0;
+ do {
+ seqnum[intkn_seq[ndx]] = i;
+ ndx++;
+ i++;
+ if (ndx == TOKEN_Q_DEPTH)
+ ndx = 0;
+ } while ( i < TOKEN_Q_DEPTH );
+
+ /* Mark non active EP's in seqnum[] by 0xff */
+ for (i=0; i<=core_if->dev_if->num_in_eps; i++) {
+ if (core_if->nextep_seq[i] == 0xff )
+ seqnum[i] = 0xff;
+ }
+
+ /* Sort seqnum[] */
+ sort_done = 0;
+ while (!sort_done) {
+ sort_done = 1;
+ for (i=0; i<core_if->dev_if->num_in_eps; i++) {
+ if (seqnum[i] > seqnum[i+1]) {
+ temp = seqnum[i];
+ seqnum[i] = seqnum[i+1];
+ seqnum[i+1] = temp;
+ sort_done = 0;
+ }
+ }
+ }
+
+ ndx = start + seqnum[0];
+ if (ndx >= TOKEN_Q_DEPTH)
+ ndx = ndx % TOKEN_Q_DEPTH;
+ core_if->first_in_nextep_seq = intkn_seq[ndx];
+
+ /* Update seqnum[] by EP numbers */
+ for (i=0; i<=core_if->dev_if->num_in_eps; i++) {
+ ndx = start + i;
+ if (seqnum[i] < 31) {
+ ndx = start + seqnum[i];
+ if (ndx >= TOKEN_Q_DEPTH)
+ ndx = ndx % TOKEN_Q_DEPTH;
+ seqnum[i] = intkn_seq[ndx];
+ } else {
+ if (seqnum[i] < 0xff) {
+ seqnum[i] = seqnum[i] - 31;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /* Update nextep_seq[] based on seqnum[] */
+ for (i=0; i<core_if->dev_if->num_in_eps; i++) {
+ if (seqnum[i] != 0xff) {
+ if (seqnum[i+1] != 0xff) {
+ core_if->nextep_seq[seqnum[i]] = seqnum[i+1];
+ } else {
+ core_if->nextep_seq[seqnum[i]] = core_if->first_in_nextep_seq;
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ DWC_DEBUGPL(DBG_PCDV, "%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+ __func__, core_if->first_in_nextep_seq);
+ for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+ DWC_DEBUGPL(DBG_PCDV,"%2d\n", core_if->nextep_seq[i]);
+ }
+
+ /* Flush the Learning Queue */
+ resetctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->grstctl);
+ resetctl.b.intknqflsh = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32);
+
+
+}
+
+/**
+ * handle the IN EP disable interrupt.
+ */
+static inline void handle_in_ep_disable_intr(dwc_otg_pcd_t * pcd,
+ const uint32_t epnum)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ deptsiz_data_t dieptsiz = {.d32 = 0 };
+ dctl_data_t dctl = {.d32 = 0 };
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+ gintmsk_data_t gintmsk_data;
+ depctl_data_t depctl;
+ uint32_t diepdma;
+ uint32_t remain_to_transfer = 0;
+ uint8_t i;
+ uint32_t xfer_size;
+
+ ep = get_in_ep(pcd, epnum);
+ dwc_ep = &ep->dwc_ep;
+
+ if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+ complete_ep(ep);
+ return;
+ }
+
+ DWC_DEBUGPL(DBG_PCD, "diepctl%d=%0x\n", epnum,
+ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl));
+ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dieptsiz);
+ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl);
+
+ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n",
+ dieptsiz.b.pktcnt, dieptsiz.b.xfersize);
+
+ if ((core_if->start_predict == 0) || (depctl.b.eptype & 1)) {
+ if (ep->stopped) {
+ if (core_if->en_multiple_tx_fifo)
+ /* Flush the Tx FIFO */
+ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+ /* Clear the Global IN NP NAK */
+ dctl.d32 = 0;
+ dctl.b.cgnpinnak = 1;
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+ /* Restart the transaction */
+ if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) {
+ restart_transfer(pcd, epnum);
+ }
+ } else {
+ /* Restart the transaction */
+ if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) {
+ restart_transfer(pcd, epnum);
+ }
+ DWC_DEBUGPL(DBG_ANY, "STOPPED!!!\n");
+ }
+ return;
+ }
+
+ if (core_if->start_predict > 2) { // NP IN EP
+ core_if->start_predict--;
+ return;
+ }
+
+ core_if->start_predict--;
+
+ if (core_if->start_predict == 1) { // All NP IN Ep's disabled now
+
+ predict_nextep_seq(core_if);
+
+ /* Update all active IN EP's NextEP field based of nextep_seq[] */
+ for ( i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+ if (core_if->nextep_seq[i] != 0xff) { // Active NP IN EP
+ depctl.b.nextep = core_if->nextep_seq[i];
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32);
+ }
+ }
+ /* Flush Shared NP TxFIFO */
+ dwc_otg_flush_tx_fifo(core_if, 0);
+ /* Rewind buffers */
+ if (!core_if->dma_desc_enable) {
+ i = core_if->first_in_nextep_seq;
+ do {
+ ep = get_in_ep(pcd, i);
+ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz);
+ xfer_size = ep->dwc_ep.total_len - ep->dwc_ep.xfer_count;
+ if (xfer_size > ep->dwc_ep.maxxfer)
+ xfer_size = ep->dwc_ep.maxxfer;
+ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+ if (dieptsiz.b.pktcnt != 0) {
+ if (xfer_size == 0) {
+ remain_to_transfer = 0;
+ } else {
+ if ((xfer_size % ep->dwc_ep.maxpacket) == 0) {
+ remain_to_transfer =
+ dieptsiz.b.pktcnt * ep->dwc_ep.maxpacket;
+ } else {
+ remain_to_transfer = ((dieptsiz.b.pktcnt -1) * ep->dwc_ep.maxpacket)
+ + (xfer_size % ep->dwc_ep.maxpacket);
+ }
+ }
+ diepdma = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepdma);
+ dieptsiz.b.xfersize = remain_to_transfer;
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->dieptsiz, dieptsiz.d32);
+ diepdma = ep->dwc_ep.dma_addr + (xfer_size - remain_to_transfer);
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepdma, diepdma);
+ }
+ i = core_if->nextep_seq[i];
+ } while (i != core_if->first_in_nextep_seq);
+ } else { // dma_desc_enable
+ DWC_PRINTF("%s Learning Queue not supported in DDMA\n", __func__);
+ }
+
+ /* Restart transfers in predicted sequences */
+ i = core_if->first_in_nextep_seq;
+ do {
+ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz);
+ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+ if (dieptsiz.b.pktcnt != 0) {
+ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+ depctl.b.epena = 1;
+ depctl.b.cnak = 1;
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32);
+ }
+ i = core_if->nextep_seq[i];
+ } while (i != core_if->first_in_nextep_seq);
+
+ /* Clear the global non-periodic IN NAK handshake */
+ dctl.d32 = 0;
+ dctl.b.cgnpinnak = 1;
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+
+ /* Unmask EP Mismatch interrupt */
+ gintmsk_data.d32 = 0;
+ gintmsk_data.b.epmismatch = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk_data.d32);
+
+ core_if->start_predict = 0;
+
+ }
+}
+
+/**
+ * Handler for the IN EP timeout handshake interrupt.
+ */
+static inline void handle_in_ep_timeout_intr(dwc_otg_pcd_t * pcd,
+ const uint32_t epnum)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+
+#ifdef DEBUG
+ deptsiz_data_t dieptsiz = {.d32 = 0 };
+ uint32_t num = 0;
+#endif
+ dctl_data_t dctl = {.d32 = 0 };
+ dwc_otg_pcd_ep_t *ep;
+
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ ep = get_in_ep(pcd, epnum);
+
+ /* Disable the NP Tx Fifo Empty Interrrupt */
+ if (!core_if->dma_enable) {
+ intr_mask.b.nptxfempty = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+ }
+ /** @todo NGS Check EP type.
+ * Implement for Periodic EPs */
+ /*
+ * Non-periodic EP
+ */
+ /* Enable the Global IN NAK Effective Interrupt */
+ intr_mask.b.ginnakeff = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32);
+
+ /* Set Global IN NAK */
+ dctl.b.sgnpinnak = 1;
+ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+
+ ep->stopped = 1;
+
+#ifdef DEBUG
+ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[num]->dieptsiz);
+ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n",
+ dieptsiz.b.pktcnt, dieptsiz.b.xfersize);
+#endif
+
+#ifdef DISABLE_PERIODIC_EP
+ /*
+ * Set the NAK bit for this EP to
+ * start the disable process.
+ */
+ diepctl.d32 = 0;
+ diepctl.b.snak = 1;
+ DWC_MODIFY_REG32(&dev_if->in_ep_regs[num]->diepctl, diepctl.d32,
+ diepctl.d32);
+ ep->disabling = 1;
+ ep->stopped = 1;
+#endif
+}
+
+/**
+ * Handler for the IN EP NAK interrupt.
+ */
+static inline int32_t handle_in_ep_nak_intr(dwc_otg_pcd_t * pcd,
+ const uint32_t epnum)
+{
+ /** @todo implement ISR */
+ dwc_otg_core_if_t *core_if;
+ diepmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "IN EP NAK");
+ core_if = GET_CORE_IF(pcd);
+ intr_mask.b.nak = 1;
+
+ if (core_if->multiproc_int_enable) {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+ diepeachintmsk[epnum], intr_mask.d32, 0);
+ } else {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->diepmsk,
+ intr_mask.d32, 0);
+ }
+
+ return 1;
+}
+
+/**
+ * Handler for the OUT EP Babble interrupt.
+ */
+static inline int32_t handle_out_ep_babble_intr(dwc_otg_pcd_t * pcd,
+ const uint32_t epnum)
+{
+ /** @todo implement ISR */
+ dwc_otg_core_if_t *core_if;
+ doepmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n",
+ "OUT EP Babble");
+ core_if = GET_CORE_IF(pcd);
+ intr_mask.b.babble = 1;
+
+ if (core_if->multiproc_int_enable) {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+ doepeachintmsk[epnum], intr_mask.d32, 0);
+ } else {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
+ intr_mask.d32, 0);
+ }
+
+ return 1;
+}
+
+/**
+ * Handler for the OUT EP NAK interrupt.
+ */
+static inline int32_t handle_out_ep_nak_intr(dwc_otg_pcd_t * pcd,
+ const uint32_t epnum)
+{
+ /** @todo implement ISR */
+ dwc_otg_core_if_t *core_if;
+ doepmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "OUT EP NAK");
+ core_if = GET_CORE_IF(pcd);
+ intr_mask.b.nak = 1;
+
+ if (core_if->multiproc_int_enable) {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+ doepeachintmsk[epnum], intr_mask.d32, 0);
+ } else {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
+ intr_mask.d32, 0);
+ }
+
+ return 1;
+}
+
+/**
+ * Handler for the OUT EP NYET interrupt.
+ */
+static inline int32_t handle_out_ep_nyet_intr(dwc_otg_pcd_t * pcd,
+ const uint32_t epnum)
+{
+ /** @todo implement ISR */
+ dwc_otg_core_if_t *core_if;
+ doepmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "OUT EP NYET");
+ core_if = GET_CORE_IF(pcd);
+ intr_mask.b.nyet = 1;
+
+ if (core_if->multiproc_int_enable) {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+ doepeachintmsk[epnum], intr_mask.d32, 0);
+ } else {
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
+ intr_mask.d32, 0);
+ }
+
+ return 1;
+}
+
+/**
+ * This interrupt indicates that an IN EP has a pending Interrupt.
+ * The sequence for handling the IN EP interrupt is shown below:
+ * -# Read the Device All Endpoint Interrupt register
+ * -# Repeat the following for each IN EP interrupt bit set (from
+ * LSB to MSB).
+ * -# Read the Device Endpoint Interrupt (DIEPINTn) register
+ * -# If "Transfer Complete" call the request complete function
+ * -# If "Endpoint Disabled" complete the EP disable procedure.
+ * -# If "AHB Error Interrupt" log error
+ * -# If "Time-out Handshake" log error
+ * -# If "IN Token Received when TxFIFO Empty" write packet to Tx
+ * FIFO.
+ * -# If "IN Token EP Mismatch" (disable, this is handled by EP
+ * Mismatch Interrupt)
+ */
+static int32_t dwc_otg_pcd_handle_in_ep_intr(dwc_otg_pcd_t * pcd)
+{
+#define CLEAR_IN_EP_INTR(__core_if,__epnum,__intr) \
+do { \
+ diepint_data_t diepint = {.d32=0}; \
+ diepint.b.__intr = 1; \
+ DWC_WRITE_REG32(&__core_if->dev_if->in_ep_regs[__epnum]->diepint, \
+ diepint.d32); \
+} while (0)
+
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+ diepint_data_t diepint = {.d32 = 0 };
+ depctl_data_t depctl = {.d32 = 0 };
+ uint32_t ep_intr;
+ uint32_t epnum = 0;
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pcd);
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if);
+
+ /* Service the Device IN interrupts for each endpoint */
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ uint32_t empty_msk;
+ /* Get EP pointer */
+ ep = get_in_ep(pcd, epnum);
+ dwc_ep = &ep->dwc_ep;
+
+ depctl.d32 =
+ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl);
+ empty_msk =
+ DWC_READ_REG32(&dev_if->
+ dev_global_regs->dtknqr4_fifoemptymsk);
+
+ DWC_DEBUGPL(DBG_PCDV,
+ "IN EP INTERRUPT - %d\nepmty_msk - %8x diepctl - %8x\n",
+ epnum, empty_msk, depctl.d32);
+
+ DWC_DEBUGPL(DBG_PCD,
+ "EP%d-%s: type=%d, mps=%d\n",
+ dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"),
+ dwc_ep->type, dwc_ep->maxpacket);
+
+ diepint.d32 =
+ dwc_otg_read_dev_in_ep_intr(core_if, dwc_ep);
+
+ DWC_DEBUGPL(DBG_PCDV,
+ "EP %d Interrupt Register - 0x%x\n", epnum,
+ diepint.d32);
+ /* Transfer complete */
+ if (diepint.b.xfercompl) {
+ /* Disable the NP Tx FIFO Empty
+ * Interrrupt */
+ if (core_if->en_multiple_tx_fifo == 0) {
+ intr_mask.b.nptxfempty = 1;
+ DWC_MODIFY_REG32
+ (&core_if->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+ } else {
+ /* Disable the Tx FIFO Empty Interrupt for this EP */
+ uint32_t fifoemptymsk =
+ 0x1 << dwc_ep->num;
+ DWC_MODIFY_REG32(&core_if->
+ dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+ fifoemptymsk, 0);
+ }
+ /* Clear the bit in DIEPINTn for this interrupt */
+ CLEAR_IN_EP_INTR(core_if, epnum, xfercompl);
+
+ /* Complete the transfer */
+ if (epnum == 0) {
+ handle_ep0(pcd);
+ }
+#ifdef DWC_EN_ISOC
+ else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ if (!ep->stopped)
+ complete_iso_ep(pcd, ep);
+ }
+#endif /* DWC_EN_ISOC */
+#ifdef DWC_UTE_PER_IO
+ else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ if (!ep->stopped)
+ complete_xiso_ep(ep);
+ }
+#endif /* DWC_UTE_PER_IO */
+ else {
+ if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC &&
+ dwc_ep->bInterval > 1) {
+ dwc_ep->frame_num += dwc_ep->bInterval;
+ if (dwc_ep->frame_num > 0x3FFF)
+ {
+ dwc_ep->frm_overrun = 1;
+ dwc_ep->frame_num &= 0x3FFF;
+ } else
+ dwc_ep->frm_overrun = 0;
+ }
+ complete_ep(ep);
+ if(diepint.b.nak)
+ CLEAR_IN_EP_INTR(core_if, epnum, nak);
+ }
+ }
+ /* Endpoint disable */
+ if (diepint.b.epdisabled) {
+ DWC_DEBUGPL(DBG_ANY, "EP%d IN disabled\n",
+ epnum);
+ handle_in_ep_disable_intr(pcd, epnum);
+
+ /* Clear the bit in DIEPINTn for this interrupt */
+ CLEAR_IN_EP_INTR(core_if, epnum, epdisabled);
+ }
+ /* AHB Error */
+ if (diepint.b.ahberr) {
+ DWC_ERROR("EP%d IN AHB Error\n", epnum);
+ /* Clear the bit in DIEPINTn for this interrupt */
+ CLEAR_IN_EP_INTR(core_if, epnum, ahberr);
+ }
+ /* TimeOUT Handshake (non-ISOC IN EPs) */
+ if (diepint.b.timeout) {
+ DWC_ERROR("EP%d IN Time-out\n", epnum);
+ handle_in_ep_timeout_intr(pcd, epnum);
+
+ CLEAR_IN_EP_INTR(core_if, epnum, timeout);
+ }
+ /** IN Token received with TxF Empty */
+ if (diepint.b.intktxfemp) {
+ DWC_DEBUGPL(DBG_ANY,
+ "EP%d IN TKN TxFifo Empty\n",
+ epnum);
+ if (!ep->stopped && epnum != 0) {
+
+ diepmsk_data_t diepmsk = {.d32 = 0 };
+ diepmsk.b.intktxfemp = 1;
+
+ if (core_if->multiproc_int_enable) {
+ DWC_MODIFY_REG32
+ (&dev_if->dev_global_regs->diepeachintmsk
+ [epnum], diepmsk.d32, 0);
+ } else {
+ DWC_MODIFY_REG32
+ (&dev_if->dev_global_regs->diepmsk,
+ diepmsk.d32, 0);
+ }
+ } else if (core_if->dma_desc_enable
+ && epnum == 0
+ && pcd->ep0state ==
+ EP0_OUT_STATUS_PHASE) {
+ // EP0 IN set STALL
+ depctl.d32 =
+ DWC_READ_REG32(&dev_if->in_ep_regs
+ [epnum]->diepctl);
+
+ /* set the disable and stall bits */
+ if (depctl.b.epena) {
+ depctl.b.epdis = 1;
+ }
+ depctl.b.stall = 1;
+ DWC_WRITE_REG32(&dev_if->in_ep_regs
+ [epnum]->diepctl,
+ depctl.d32);
+ }
+ CLEAR_IN_EP_INTR(core_if, epnum, intktxfemp);
+ }
+ /** IN Token Received with EP mismatch */
+ if (diepint.b.intknepmis) {
+ DWC_DEBUGPL(DBG_ANY,
+ "EP%d IN TKN EP Mismatch\n", epnum);
+ CLEAR_IN_EP_INTR(core_if, epnum, intknepmis);
+ }
+ /** IN Endpoint NAK Effective */
+ if (diepint.b.inepnakeff) {
+ DWC_DEBUGPL(DBG_ANY,
+ "EP%d IN EP NAK Effective\n",
+ epnum);
+ /* Periodic EP */
+ if (ep->disabling) {
+ depctl.d32 = 0;
+ depctl.b.snak = 1;
+ depctl.b.epdis = 1;
+ DWC_MODIFY_REG32(&dev_if->in_ep_regs
+ [epnum]->diepctl,
+ depctl.d32,
+ depctl.d32);
+ }
+ CLEAR_IN_EP_INTR(core_if, epnum, inepnakeff);
+
+ }
+
+ /** IN EP Tx FIFO Empty Intr */
+ if (diepint.b.emptyintr) {
+ DWC_DEBUGPL(DBG_ANY,
+ "EP%d Tx FIFO Empty Intr \n",
+ epnum);
+ write_empty_tx_fifo(pcd, epnum);
+
+ CLEAR_IN_EP_INTR(core_if, epnum, emptyintr);
+
+ }
+
+ /** IN EP BNA Intr */
+ if (diepint.b.bna) {
+ CLEAR_IN_EP_INTR(core_if, epnum, bna);
+ if (core_if->dma_desc_enable) {
+#ifdef DWC_EN_ISOC
+ if (dwc_ep->type ==
+ DWC_OTG_EP_TYPE_ISOC) {
+ /*
+ * This checking is performed to prevent first "false" BNA
+ * handling occuring right after reconnect
+ */
+ if (dwc_ep->next_frame !=
+ 0xffffffff)
+ dwc_otg_pcd_handle_iso_bna(ep);
+ } else
+#endif /* DWC_EN_ISOC */
+ {
+ dwc_otg_pcd_handle_noniso_bna(ep);
+ }
+ }
+ }
+ /* NAK Interrutp */
+ if (diepint.b.nak) {
+ DWC_DEBUGPL(DBG_ANY, "EP%d IN NAK Interrupt\n",
+ epnum);
+ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
+ {
+ depctl_data_t depctl;
+ if (ep->dwc_ep.frame_num == 0xFFFFFFFF)
+ {
+ ep->dwc_ep.frame_num = core_if->frame_num;
+ if (ep->dwc_ep.bInterval > 1)
+ {
+ depctl.d32 = 0;
+ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl);
+ if (ep->dwc_ep.frame_num & 0x1) {
+ depctl.b.setd1pid = 1;
+ depctl.b.setd0pid = 0;
+ } else {
+ depctl.b.setd0pid = 1;
+ depctl.b.setd1pid = 0;
+ }
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32);
+ }
+ start_next_request(ep);
+ }
+ ep->dwc_ep.frame_num += ep->dwc_ep.bInterval;
+ if (dwc_ep->frame_num > 0x3FFF)
+ {
+ dwc_ep->frm_overrun = 1;
+ dwc_ep->frame_num &= 0x3FFF;
+ } else
+ dwc_ep->frm_overrun = 0;
+ }
+
+ CLEAR_IN_EP_INTR(core_if, epnum, nak);
+ }
+ }
+ epnum++;
+ ep_intr >>= 1;
+ }
+
+ return 1;
+#undef CLEAR_IN_EP_INTR
+}
+
+/**
+ * This interrupt indicates that an OUT EP has a pending Interrupt.
+ * The sequence for handling the OUT EP interrupt is shown below:
+ * -# Read the Device All Endpoint Interrupt register
+ * -# Repeat the following for each OUT EP interrupt bit set (from
+ * LSB to MSB).
+ * -# Read the Device Endpoint Interrupt (DOEPINTn) register
+ * -# If "Transfer Complete" call the request complete function
+ * -# If "Endpoint Disabled" complete the EP disable procedure.
+ * -# If "AHB Error Interrupt" log error
+ * -# If "Setup Phase Done" process Setup Packet (See Standard USB
+ * Command Processing)
+ */
+static int32_t dwc_otg_pcd_handle_out_ep_intr(dwc_otg_pcd_t * pcd)
+{
+#define CLEAR_OUT_EP_INTR(__core_if,__epnum,__intr) \
+do { \
+ doepint_data_t doepint = {.d32=0}; \
+ doepint.b.__intr = 1; \
+ DWC_WRITE_REG32(&__core_if->dev_if->out_ep_regs[__epnum]->doepint, \
+ doepint.d32); \
+} while (0)
+
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ uint32_t ep_intr;
+ doepint_data_t doepint = {.d32 = 0 };
+ uint32_t epnum = 0;
+ dwc_otg_pcd_ep_t *ep;
+ dwc_ep_t *dwc_ep;
+ dctl_data_t dctl = {.d32 = 0 };
+ gintmsk_data_t gintmsk = {.d32 = 0 };
+
+
+ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__);
+
+ /* Read in the device interrupt bits */
+ ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if);
+
+ while (ep_intr) {
+ if (ep_intr & 0x1) {
+ /* Get EP pointer */
+ ep = get_out_ep(pcd, epnum);
+ dwc_ep = &ep->dwc_ep;
+
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV,
+ "EP%d-%s: type=%d, mps=%d\n",
+ dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"),
+ dwc_ep->type, dwc_ep->maxpacket);
+#endif
+ doepint.d32 =
+ dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep);
+
+ /* Transfer complete */
+ if (doepint.b.xfercompl) {
+
+ if (epnum == 0) {
+ /* Clear the bit in DOEPINTn for this interrupt */
+ CLEAR_OUT_EP_INTR(core_if, epnum,
+ xfercompl);
+ if (core_if->dma_desc_enable == 0
+ || pcd->ep0state != EP0_IDLE)
+ handle_ep0(pcd);
+#ifdef DWC_EN_ISOC
+ } else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ if (doepint.b.pktdrpsts == 0) {
+ /* Clear the bit in DOEPINTn for this interrupt */
+ CLEAR_OUT_EP_INTR(core_if,
+ epnum,
+ xfercompl);
+ complete_iso_ep(pcd, ep);
+ } else {
+
+ doepint_data_t doepint = {.d32 = 0 };
+ doepint.b.xfercompl = 1;
+ doepint.b.pktdrpsts = 1;
+ DWC_WRITE_REG32
+ (&core_if->dev_if->out_ep_regs
+ [epnum]->doepint,
+ doepint.d32);
+ if (handle_iso_out_pkt_dropped
+ (core_if, dwc_ep)) {
+ complete_iso_ep(pcd,
+ ep);
+ }
+ }
+#endif /* DWC_EN_ISOC */
+#ifdef DWC_UTE_PER_IO
+ } else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ CLEAR_OUT_EP_INTR(core_if, epnum, xfercompl);
+ if (!ep->stopped)
+ complete_xiso_ep(ep);
+#endif /* DWC_UTE_PER_IO */
+ } else {
+ /* Clear the bit in DOEPINTn for this interrupt */
+ CLEAR_OUT_EP_INTR(core_if, epnum,
+ xfercompl);
+
+ if (core_if->core_params->dev_out_nak) {
+ DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[epnum]);
+ pcd->core_if->ep_xfer_info[epnum].state = 0;
+#ifdef DEBUG
+ print_memory_payload(pcd, dwc_ep);
+#endif
+ }
+ complete_ep(ep);
+ }
+
+ }
+
+ /* Endpoint disable */
+ if (doepint.b.epdisabled) {
+
+ /* Clear the bit in DOEPINTn for this interrupt */
+ CLEAR_OUT_EP_INTR(core_if, epnum, epdisabled);
+ if (core_if->core_params->dev_out_nak) {
+#ifdef DEBUG
+ print_memory_payload(pcd, dwc_ep);
+#endif
+ /* In case of timeout condition */
+ if (core_if->ep_xfer_info[epnum].state == 2) {
+ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->
+ dev_global_regs->dctl);
+ dctl.b.cgoutnak = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl,
+ dctl.d32);
+ /* Unmask goutnakeff interrupt which was masked
+ * during handle nak out interrupt */
+ gintmsk.b.goutnakeff = 1;
+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
+ 0, gintmsk.d32);
+
+ complete_ep(ep);
+ }
+ }
+ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
+ {
+ dctl_data_t dctl;
+ gintmsk_data_t intr_mask = {.d32 = 0};
+ dwc_otg_pcd_request_t *req = 0;
+
+ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->
+ dev_global_regs->dctl);
+ dctl.b.cgoutnak = 1;
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl,
+ dctl.d32);
+
+ intr_mask.d32 = 0;
+ intr_mask.b.incomplisoout = 1;
+
+ /* Get any pending requests */
+ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+ req = DWC_CIRCLEQ_FIRST(&ep->queue);
+ if (!req) {
+ DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep);
+ } else {
+ dwc_otg_request_done(ep, req, 0);
+ start_next_request(ep);
+ }
+ } else {
+ DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep);
+ }
+ }
+ }
+ /* AHB Error */
+ if (doepint.b.ahberr) {
+ DWC_ERROR("EP%d OUT AHB Error\n", epnum);
+ DWC_ERROR("EP%d DEPDMA=0x%08x \n",
+ epnum, core_if->dev_if->out_ep_regs[epnum]->doepdma);
+ CLEAR_OUT_EP_INTR(core_if, epnum, ahberr);
+ }
+ /* Setup Phase Done (contorl EPs) */
+ if (doepint.b.setup) {
+#ifdef DEBUG_EP0
+ DWC_DEBUGPL(DBG_PCD, "EP%d SETUP Done\n",
+ epnum);
+#endif
+ CLEAR_OUT_EP_INTR(core_if, epnum, setup);
+
+ handle_ep0(pcd);
+ }
+
+ /** OUT EP BNA Intr */
+ if (doepint.b.bna) {
+ CLEAR_OUT_EP_INTR(core_if, epnum, bna);
+ if (core_if->dma_desc_enable) {
+#ifdef DWC_EN_ISOC
+ if (dwc_ep->type ==
+ DWC_OTG_EP_TYPE_ISOC) {
+ /*
+ * This checking is performed to prevent first "false" BNA
+ * handling occuring right after reconnect
+ */
+ if (dwc_ep->next_frame !=
+ 0xffffffff)
+ dwc_otg_pcd_handle_iso_bna(ep);
+ } else
+#endif /* DWC_EN_ISOC */
+ {
+ dwc_otg_pcd_handle_noniso_bna(ep);
+ }
+ }
+ }
+ if (doepint.b.stsphsercvd) {
+ CLEAR_OUT_EP_INTR(core_if, epnum, stsphsercvd);
+ if (core_if->dma_desc_enable) {
+ do_setup_in_status_phase(pcd);
+ }
+ }
+ /* Babble Interrutp */
+ if (doepint.b.babble) {
+ DWC_DEBUGPL(DBG_ANY, "EP%d OUT Babble\n",
+ epnum);
+ handle_out_ep_babble_intr(pcd, epnum);
+
+ CLEAR_OUT_EP_INTR(core_if, epnum, babble);
+ }
+ if (doepint.b.outtknepdis)
+ {
+ DWC_DEBUGPL(DBG_ANY, "EP%d OUT Token received when EP is \
+ disabled\n",epnum);
+ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
+ {
+ doepmsk_data_t doepmsk = {.d32 = 0};
+ ep->dwc_ep.frame_num = core_if->frame_num;
+ if (ep->dwc_ep.bInterval > 1)
+ {
+ depctl_data_t depctl;
+ depctl.d32 = DWC_READ_REG32(&core_if->dev_if->
+ out_ep_regs[epnum]->doepctl);
+ if (ep->dwc_ep.frame_num & 0x1) {
+ depctl.b.setd1pid = 1;
+ depctl.b.setd0pid = 0;
+ } else {
+ depctl.b.setd0pid = 1;
+ depctl.b.setd1pid = 0;
+ }
+ DWC_WRITE_REG32(&core_if->dev_if->
+ out_ep_regs[epnum]->doepctl, depctl.d32);
+ }
+ start_next_request(ep);
+ doepmsk.b.outtknepdis = 1;
+ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
+ doepmsk.d32, 0);
+ }
+ CLEAR_OUT_EP_INTR(core_if, epnum, outtknepdis);
+ }
+
+ /* NAK Interrutp */
+ if (doepint.b.nak) {
+ DWC_DEBUGPL(DBG_ANY, "EP%d OUT NAK\n", epnum);
+ handle_out_ep_nak_intr(pcd, epnum);
+
+ CLEAR_OUT_EP_INTR(core_if, epnum, nak);
+ }
+ /* NYET Interrutp */
+ if (doepint.b.nyet) {
+ DWC_DEBUGPL(DBG_ANY, "EP%d OUT NYET\n", epnum);
+ handle_out_ep_nyet_intr(pcd, epnum);
+
+ CLEAR_OUT_EP_INTR(core_if, epnum, nyet);
+ }
+ }
+
+ epnum++;
+ ep_intr >>= 1;
+ }
+
+ return 1;
+
+#undef CLEAR_OUT_EP_INTR
+}
+static int drop_transfer(uint32_t trgt_fr, uint32_t curr_fr, uint8_t frm_overrun)
+{
+ int retval = 0;
+ if(!frm_overrun && curr_fr >= trgt_fr)
+ retval = 1;
+ else if (frm_overrun && (curr_fr >= trgt_fr && ((curr_fr - trgt_fr) < 0x3FFF/2)))
+ retval = 1;
+ return retval;
+}
+/**
+ * Incomplete ISO IN Transfer Interrupt.
+ * This interrupt indicates one of the following conditions occurred
+ * while transmitting an ISOC transaction.
+ * - Corrupted IN Token for ISOC EP.
+ * - Packet not complete in FIFO.
+ * The follow actions will be taken:
+ * -# Determine the EP
+ * -# Set incomplete flag in dwc_ep structure
+ * -# Disable EP; when "Endpoint Disabled" interrupt is received
+ * Flush FIFO
+ */
+int32_t dwc_otg_pcd_handle_incomplete_isoc_in_intr(dwc_otg_pcd_t * pcd)
+{
+ gintsts_data_t gintsts;
+
+#ifdef DWC_EN_ISOC
+ dwc_otg_dev_if_t *dev_if;
+ deptsiz_data_t deptsiz = {.d32 = 0 };
+ depctl_data_t depctl = {.d32 = 0 };
+ dsts_data_t dsts = {.d32 = 0 };
+ dwc_ep_t *dwc_ep;
+ int i;
+
+ dev_if = GET_CORE_IF(pcd)->dev_if;
+
+ for (i = 1; i <= dev_if->num_in_eps; ++i) {
+ dwc_ep = &pcd->in_ep[i].dwc_ep;
+ if (dwc_ep->active && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ deptsiz.d32 =
+ DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz);
+ depctl.d32 =
+ DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+
+ if (depctl.b.epdis && deptsiz.d32) {
+ set_current_pkt_info(GET_CORE_IF(pcd), dwc_ep);
+ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+ dwc_ep->cur_pkt = 0;
+ dwc_ep->proc_buf_num =
+ (dwc_ep->proc_buf_num ^ 1) & 0x1;
+
+ if (dwc_ep->proc_buf_num) {
+ dwc_ep->cur_pkt_addr =
+ dwc_ep->xfer_buff1;
+ dwc_ep->cur_pkt_dma_addr =
+ dwc_ep->dma_addr1;
+ } else {
+ dwc_ep->cur_pkt_addr =
+ dwc_ep->xfer_buff0;
+ dwc_ep->cur_pkt_dma_addr =
+ dwc_ep->dma_addr0;
+ }
+
+ }
+
+ dsts.d32 =
+ DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if->
+ dev_global_regs->dsts);
+ dwc_ep->next_frame = dsts.b.soffn;
+
+ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF
+ (pcd),
+ dwc_ep);
+ }
+ }
+ }
+
+#else
+ depctl_data_t depctl = {.d32 = 0 };
+ dwc_ep_t *dwc_ep;
+ dwc_otg_dev_if_t *dev_if;
+ int i;
+ dev_if = GET_CORE_IF(pcd)->dev_if;
+
+ DWC_DEBUGPL(DBG_PCD,"Incomplete ISO IN \n");
+
+ for (i = 1; i <= dev_if->num_in_eps; ++i) {
+ dwc_ep = &pcd->in_ep[i-1].dwc_ep;
+ depctl.d32 =
+ DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+ if (depctl.b.epena && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+ if (drop_transfer(dwc_ep->frame_num, GET_CORE_IF(pcd)->frame_num,
+ dwc_ep->frm_overrun))
+ {
+ depctl.d32 =
+ DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+ depctl.b.snak = 1;
+ depctl.b.epdis = 1;
+ DWC_MODIFY_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32, depctl.d32);
+ }
+ }
+ }
+
+ /*intr_mask.b.incomplisoin = 1;
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0); */
+#endif //DWC_EN_ISOC
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.incomplisoin = 1;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * Incomplete ISO OUT Transfer Interrupt.
+ *
+ * This interrupt indicates that the core has dropped an ISO OUT
+ * packet. The following conditions can be the cause:
+ * - FIFO Full, the entire packet would not fit in the FIFO.
+ * - CRC Error
+ * - Corrupted Token
+ * The follow actions will be taken:
+ * -# Determine the EP
+ * -# Set incomplete flag in dwc_ep structure
+ * -# Read any data from the FIFO
+ * -# Disable EP. When "Endpoint Disabled" interrupt is received
+ * re-enable EP.
+ */
+int32_t dwc_otg_pcd_handle_incomplete_isoc_out_intr(dwc_otg_pcd_t * pcd)
+{
+
+ gintsts_data_t gintsts;
+
+#ifdef DWC_EN_ISOC
+ dwc_otg_dev_if_t *dev_if;
+ deptsiz_data_t deptsiz = {.d32 = 0 };
+ depctl_data_t depctl = {.d32 = 0 };
+ dsts_data_t dsts = {.d32 = 0 };
+ dwc_ep_t *dwc_ep;
+ int i;
+
+ dev_if = GET_CORE_IF(pcd)->dev_if;
+
+ for (i = 1; i <= dev_if->num_out_eps; ++i) {
+ dwc_ep = &pcd->in_ep[i].dwc_ep;
+ if (pcd->out_ep[i].dwc_ep.active &&
+ pcd->out_ep[i].dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+ deptsiz.d32 =
+ DWC_READ_REG32(&dev_if->out_ep_regs[i]->doeptsiz);
+ depctl.d32 =
+ DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl);
+
+ if (depctl.b.epdis && deptsiz.d32) {
+ set_current_pkt_info(GET_CORE_IF(pcd),
+ &pcd->out_ep[i].dwc_ep);
+ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+ dwc_ep->cur_pkt = 0;
+ dwc_ep->proc_buf_num =
+ (dwc_ep->proc_buf_num ^ 1) & 0x1;
+
+ if (dwc_ep->proc_buf_num) {
+ dwc_ep->cur_pkt_addr =
+ dwc_ep->xfer_buff1;
+ dwc_ep->cur_pkt_dma_addr =
+ dwc_ep->dma_addr1;
+ } else {
+ dwc_ep->cur_pkt_addr =
+ dwc_ep->xfer_buff0;
+ dwc_ep->cur_pkt_dma_addr =
+ dwc_ep->dma_addr0;
+ }
+
+ }
+
+ dsts.d32 =
+ DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if->
+ dev_global_regs->dsts);
+ dwc_ep->next_frame = dsts.b.soffn;
+
+ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF
+ (pcd),
+ dwc_ep);
+ }
+ }
+ }
+#else
+ /** @todo implement ISR */
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ dwc_otg_core_if_t *core_if;
+ deptsiz_data_t deptsiz = {.d32 = 0 };
+ depctl_data_t depctl = {.d32 = 0 };
+ dctl_data_t dctl = {.d32 = 0 };
+ dwc_ep_t *dwc_ep = NULL;
+ int i;
+ core_if = GET_CORE_IF(pcd);
+
+ for (i = 0; i < core_if->dev_if->num_out_eps; ++i) {
+ dwc_ep = &pcd->out_ep[i].dwc_ep;
+ depctl.d32 =
+ DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl);
+ if (depctl.b.epena && depctl.b.dpid == (core_if->frame_num & 0x1)) {
+ core_if->dev_if->isoc_ep = dwc_ep;
+ deptsiz.d32 =
+ DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz);
+ break;
+ }
+ }
+ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+ intr_mask.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+
+ if (!intr_mask.b.goutnakeff) {
+ /* Unmask it */
+ intr_mask.b.goutnakeff = 1;
+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, intr_mask.d32);
+ }
+ if (!gintsts.b.goutnakeff) {
+ dctl.b.sgoutnak = 1;
+ }
+ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
+
+ depctl.d32 = DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl);
+ if (depctl.b.epena) {
+ depctl.b.epdis = 1;
+ depctl.b.snak = 1;
+ }
+ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl, depctl.d32);
+
+ intr_mask.d32 = 0;
+ intr_mask.b.incomplisoout = 1;
+
+#endif /* DWC_EN_ISOC */
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.incomplisoout = 1;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * This function handles the Global IN NAK Effective interrupt.
+ *
+ */
+int32_t dwc_otg_pcd_handle_in_nak_effective(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
+ depctl_data_t diepctl = {.d32 = 0 };
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ gintsts_data_t gintsts;
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+ int i;
+
+ DWC_DEBUGPL(DBG_PCD, "Global IN NAK Effective\n");
+
+ /* Disable all active IN EPs */
+ for (i = 0; i <= dev_if->num_in_eps; i++) {
+ diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+ if (!(diepctl.b.eptype & 1) && diepctl.b.epena) {
+ if (core_if->start_predict > 0)
+ core_if->start_predict++;
+ diepctl.b.epdis = 1;
+ diepctl.b.snak = 1;
+ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, diepctl.d32);
+ }
+ }
+
+
+ /* Disable the Global IN NAK Effective Interrupt */
+ intr_mask.b.ginnakeff = 1;
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.ginnakeff = 1;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * OUT NAK Effective.
+ *
+ */
+int32_t dwc_otg_pcd_handle_out_nak_effective(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
+ gintmsk_data_t intr_mask = {.d32 = 0 };
+ gintsts_data_t gintsts;
+ depctl_data_t doepctl;
+ int i;
+
+ /* Disable the Global OUT NAK Effective Interrupt */
+ intr_mask.b.goutnakeff = 1;
+ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+ intr_mask.d32, 0);
+
+ /* If DEV OUT NAK enabled*/
+ if (pcd->core_if->core_params->dev_out_nak) {
+ /* Run over all out endpoints to determine the ep number on
+ * which the timeout has happened
+ */
+ for (i = 0; i <= dev_if->num_out_eps; i++) {
+ if ( pcd->core_if->ep_xfer_info[i].state == 2 )
+ break;
+ }
+ if (i > dev_if->num_out_eps) {
+ dctl_data_t dctl;
+ dctl.d32 = DWC_READ_REG32(&dev_if->
+ dev_global_regs->dctl);
+ dctl.b.cgoutnak = 1;
+ DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl,
+ dctl.d32);
+ goto out;
+ }
+
+ /* Disable the endpoint */
+ doepctl.d32 = DWC_READ_REG32(&dev_if->
+ out_ep_regs[i]->doepctl);
+ if (doepctl.b.epena) {
+ doepctl.b.epdis = 1;
+ doepctl.b.snak = 1;
+ }
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, doepctl.d32);
+ return 1;
+ }
+ /* We come here from Incomplete ISO OUT handler */
+ if(dev_if->isoc_ep)
+ {
+ dwc_ep_t *dwc_ep = (dwc_ep_t *)dev_if->isoc_ep;
+ uint32_t epnum = dwc_ep->num;
+ doepint_data_t doepint;
+ doepint.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[dwc_ep->num]->doepint);
+ dev_if->isoc_ep = NULL;
+ doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[epnum]->doepctl);
+ DWC_PRINTF("Before disable DOEPCTL = %08x\n", doepctl.d32);
+ if (doepctl.b.epena) {
+ doepctl.b.epdis = 1;
+ doepctl.b.snak = 1;
+ }
+ DWC_WRITE_REG32(&dev_if->out_ep_regs[epnum]->doepctl, doepctl.d32);
+ return 1;
+ } else
+ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n",
+ "Global OUT NAK Effective\n");
+
+out:
+ /* Clear interrupt */
+ gintsts.d32 = 0;
+ gintsts.b.goutnakeff = 1;
+ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+ gintsts.d32);
+
+ return 1;
+}
+
+/**
+ * PCD interrupt handler.
+ *
+ * The PCD handles the device interrupts. Many conditions can cause a
+ * device interrupt. When an interrupt occurs, the device interrupt
+ * service routine determines the cause of the interrupt and
+ * dispatches handling to the appropriate function. These interrupt
+ * handling functions are described below.
+ *
+ * All interrupt registers are processed from LSB to MSB.
+ *
+ */
+int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd)
+{
+ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+#ifdef VERBOSE
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+#endif
+ gintsts_data_t gintr_status;
+ int32_t retval = 0;
+
+ /* Exit from ISR if core is hibernated */
+ if (core_if->hibernation_suspend == 1) {
+ return retval;
+ }
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_ANY, "%s() gintsts=%08x gintmsk=%08x\n",
+ __func__,
+ DWC_READ_REG32(&global_regs->gintsts),
+ DWC_READ_REG32(&global_regs->gintmsk));
+#endif
+
+ if (dwc_otg_is_device_mode(core_if)) {
+ DWC_SPINLOCK(pcd->lock);
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%08x gintmsk=%08x\n",
+ __func__,
+ DWC_READ_REG32(&global_regs->gintsts),
+ DWC_READ_REG32(&global_regs->gintmsk));
+#endif
+
+ gintr_status.d32 = dwc_otg_read_core_intr(core_if);
+
+ DWC_DEBUGPL(DBG_PCDV, "%s: gintsts&gintmsk=%08x\n",
+ __func__, gintr_status.d32);
+
+ if (gintr_status.b.sofintr) {
+ retval |= dwc_otg_pcd_handle_sof_intr(pcd);
+ }
+ if (gintr_status.b.rxstsqlvl) {
+ retval |=
+ dwc_otg_pcd_handle_rx_status_q_level_intr(pcd);
+ }
+ if (gintr_status.b.nptxfempty) {
+ retval |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd);
+ }
+ if (gintr_status.b.goutnakeff) {
+ retval |= dwc_otg_pcd_handle_out_nak_effective(pcd);
+ }
+ if (gintr_status.b.i2cintr) {
+ retval |= dwc_otg_pcd_handle_i2c_intr(pcd);
+ }
+ if (gintr_status.b.erlysuspend) {
+ retval |= dwc_otg_pcd_handle_early_suspend_intr(pcd);
+ }
+ if (gintr_status.b.usbreset) {
+ retval |= dwc_otg_pcd_handle_usb_reset_intr(pcd);
+ }
+ if (gintr_status.b.enumdone) {
+ retval |= dwc_otg_pcd_handle_enum_done_intr(pcd);
+ }
+ if (gintr_status.b.isooutdrop) {
+ retval |=
+ dwc_otg_pcd_handle_isoc_out_packet_dropped_intr
+ (pcd);
+ }
+ if (gintr_status.b.eopframe) {
+ retval |=
+ dwc_otg_pcd_handle_end_periodic_frame_intr(pcd);
+ }
+ if (gintr_status.b.inepint) {
+ if (!core_if->multiproc_int_enable) {
+ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+ }
+ }
+ if (gintr_status.b.outepintr) {
+ if (!core_if->multiproc_int_enable) {
+ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+ }
+ }
+ if (gintr_status.b.epmismatch) {
+ retval |= dwc_otg_pcd_handle_ep_mismatch_intr(pcd);
+ }
+ if (gintr_status.b.fetsusp) {
+ retval |= dwc_otg_pcd_handle_ep_fetsusp_intr(pcd);
+ }
+ if (gintr_status.b.ginnakeff) {
+ retval |= dwc_otg_pcd_handle_in_nak_effective(pcd);
+ }
+ if (gintr_status.b.incomplisoin) {
+ retval |=
+ dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd);
+ }
+ if (gintr_status.b.incomplisoout) {
+ retval |=
+ dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd);
+ }
+
+ /* In MPI mode Device Endpoints interrupts are asserted
+ * without setting outepintr and inepint bits set, so these
+ * Interrupt handlers are called without checking these bit-fields
+ */
+ if (core_if->multiproc_int_enable) {
+ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+ }
+#ifdef VERBOSE
+ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%0x\n", __func__,
+ DWC_READ_REG32(&global_regs->gintsts));
+#endif
+ DWC_SPINUNLOCK(pcd->lock);
+ }
+ return retval;
+}
+
+#endif /* DWC_HOST_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd_linux.c b/drivers/usb/dwc_otg/dwc_otg_pcd_linux.c
new file mode 100644
index 0000000..387d725
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_pcd_linux.c
@@ -0,0 +1,1320 @@
+ /* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_linux.c $
+ * $Revision: #19 $
+ * $Date: 2011/10/26 $
+ * $Change: 1873028 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+
+/** @file
+ * This file implements the Peripheral Controller Driver.
+ *
+ * The Peripheral Controller Driver (PCD) is responsible for
+ * translating requests from the Function Driver into the appropriate
+ * actions on the DWC_otg controller. It isolates the Function Driver
+ * from the specifics of the controller by providing an API to the
+ * Function Driver.
+ *
+ * The Peripheral Controller Driver for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used.
+ * (Gadget Driver is the Linux terminology for a Function Driver.)
+ *
+ * The Linux Gadget API is defined in the header file
+ * <code><linux/usb_gadget.h></code>. The USB EP operations API is
+ * defined in the structure <code>usb_ep_ops</code> and the USB
+ * Controller API is defined in the structure
+ * <code>usb_gadget_ops</code>.
+ *
+ */
+
+#include "dwc_otg_os_dep.h"
+#include "dwc_otg_pcd_if.h"
+#include "dwc_otg_pcd.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_dbg.h"
+
+static struct gadget_wrapper {
+ dwc_otg_pcd_t *pcd;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+ struct usb_ep ep0;
+ struct usb_ep in_ep[16];
+ struct usb_ep out_ep[16];
+
+} *gadget_wrapper;
+
+/* Display the contents of the buffer */
+extern void dump_msg(const u8 * buf, unsigned int length);
+/**
+ * Get the dwc_otg_pcd_ep_t* from usb_ep* pointer - NULL in case
+ * if the endpoint is not found
+ */
+static struct dwc_otg_pcd_ep *ep_from_handle(dwc_otg_pcd_t * pcd, void *handle)
+{
+ int i;
+ if (pcd->ep0.priv == handle) {
+ return &pcd->ep0;
+ }
+
+ for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) {
+ if (pcd->in_ep[i].priv == handle)
+ return &pcd->in_ep[i];
+ if (pcd->out_ep[i].priv == handle)
+ return &pcd->out_ep[i];
+ }
+
+ return NULL;
+}
+
+/* USB Endpoint Operations */
+/*
+ * The following sections briefly describe the behavior of the Gadget
+ * API endpoint operations implemented in the DWC_otg driver
+ * software. Detailed descriptions of the generic behavior of each of
+ * these functions can be found in the Linux header file
+ * include/linux/usb_gadget.h.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper
+ * function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper
+ * functions. Within each section, the corresponding DWC_otg PCD
+ * function name is specified.
+ *
+ */
+
+/**
+ * This function is called by the Gadget Driver for each EP to be
+ * configured for the current configuration (SET_CONFIGURATION).
+ *
+ * This function initializes the dwc_otg_ep_t data structure, and then
+ * calls dwc_otg_ep_activate.
+ */
+static int ep_enable(struct usb_ep *usb_ep,
+ const struct usb_endpoint_descriptor *ep_desc)
+{
+ int retval;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, ep_desc);
+
+ if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) {
+ DWC_WARN("%s, bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+ if (usb_ep == &gadget_wrapper->ep0) {
+ DWC_WARN("%s, bad ep(0)\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Check FIFO size? */
+ if (!ep_desc->wMaxPacketSize) {
+ DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name);
+ return -ERANGE;
+ }
+
+ if (!gadget_wrapper->driver ||
+ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
+ DWC_WARN("%s, bogus device state\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ /* Delete after check - MAS */
+#if 0
+ nat = (uint32_t) ep_desc->wMaxPacketSize;
+ printk(KERN_ALERT "%s: nat (before) =%d\n", __func__, nat);
+ nat = (nat >> 11) & 0x03;
+ printk(KERN_ALERT "%s: nat (after) =%d\n", __func__, nat);
+#endif
+ retval = dwc_otg_pcd_ep_enable(gadget_wrapper->pcd,
+ (const uint8_t *)ep_desc,
+ (void *)usb_ep);
+ if (retval) {
+ DWC_WARN("dwc_otg_pcd_ep_enable failed\n");
+ return -EINVAL;
+ }
+
+ usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize);
+
+ return 0;
+}
+
+/**
+ * This function is called when an EP is disabled due to disconnect or
+ * change in configuration. Any pending requests will terminate with a
+ * status of -ESHUTDOWN.
+ *
+ * This function modifies the dwc_otg_ep_t data structure for this EP,
+ * and then calls dwc_otg_ep_deactivate.
+ */
+static int ep_disable(struct usb_ep *usb_ep)
+{
+ int retval;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, usb_ep);
+ if (!usb_ep) {
+ DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__,
+ usb_ep ? usb_ep->name : NULL);
+ return -EINVAL;
+ }
+
+ retval = dwc_otg_pcd_ep_disable(gadget_wrapper->pcd, usb_ep);
+ if (retval) {
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+/**
+ * This function allocates a request object to use with the specified
+ * endpoint.
+ *
+ * @param ep The endpoint to be used with with the request
+ * @param gfp_flags the GFP_* flags to use.
+ */
+static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct usb_request *usb_req;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, ep, gfp_flags);
+ if (0 == ep) {
+ DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n");
+ return 0;
+ }
+ usb_req = kmalloc(sizeof(*usb_req), gfp_flags);
+ if (0 == usb_req) {
+ DWC_WARN("%s() %s\n", __func__, "request allocation failed!\n");
+ return 0;
+ }
+ memset(usb_req, 0, sizeof(*usb_req));
+ usb_req->dma = DWC_DMA_ADDR_INVALID;
+
+ return usb_req;
+}
+
+/**
+ * This function frees a request object.
+ *
+ * @param ep The endpoint associated with the request
+ * @param req The request being freed
+ */
+static void dwc_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, ep, req);
+
+ if (0 == ep || 0 == req) {
+ DWC_WARN("%s() %s\n", __func__,
+ "Invalid ep or req argument!\n");
+ return;
+ }
+
+ kfree(req);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+/**
+ * This function allocates an I/O buffer to be used for a transfer
+ * to/from the specified endpoint.
+ *
+ * @param usb_ep The endpoint to be used with with the request
+ * @param bytes The desired number of bytes for the buffer
+ * @param dma Pointer to the buffer's DMA address; must be valid
+ * @param gfp_flags the GFP_* flags to use.
+ * @return address of a new buffer or null is buffer could not be allocated.
+ */
+static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *usb_ep, unsigned bytes,
+ dma_addr_t * dma, gfp_t gfp_flags)
+{
+ void *buf;
+ dwc_otg_pcd_t *pcd = 0;
+
+ pcd = gadget_wrapper->pcd;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d,%p,%0x)\n", __func__, usb_ep, bytes,
+ dma, gfp_flags);
+
+ /* Check dword alignment */
+ if ((bytes & 0x3UL) != 0) {
+ DWC_WARN("%s() Buffer size is not a multiple of"
+ "DWORD size (%d)", __func__, bytes);
+ }
+
+ buf = dma_alloc_coherent(NULL, bytes, dma, gfp_flags);
+
+ /* Check dword alignment */
+ if (((int)buf & 0x3UL) != 0) {
+ DWC_WARN("%s() Buffer is not DWORD aligned (%p)",
+ __func__, buf);
+ }
+
+ return buf;
+}
+
+/**
+ * This function frees an I/O buffer that was allocated by alloc_buffer.
+ *
+ * @param usb_ep the endpoint associated with the buffer
+ * @param buf address of the buffer
+ * @param dma The buffer's DMA address
+ * @param bytes The number of bytes of the buffer
+ */
+static void dwc_otg_pcd_free_buffer(struct usb_ep *usb_ep, void *buf,
+ dma_addr_t dma, unsigned bytes)
+{
+ dwc_otg_pcd_t *pcd = 0;
+
+ pcd = gadget_wrapper->pcd;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%0x,%d)\n", __func__, buf, dma, bytes);
+
+ dma_free_coherent(NULL, bytes, buf, dma);
+}
+#endif
+
+/**
+ * This function is used to submit an I/O Request to an EP.
+ *
+ * - When the request completes the request's completion callback
+ * is called to return the request to the driver.
+ * - An EP, except control EPs, may have multiple requests
+ * pending.
+ * - Once submitted the request cannot be examined or modified.
+ * - Each request is turned into one or more packets.
+ * - A BULK EP can queue any amount of data; the transfer is
+ * packetized.
+ * - Zero length Packets are specified with the request 'zero'
+ * flag.
+ */
+static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req,
+ gfp_t gfp_flags)
+{
+ dwc_otg_pcd_t *pcd;
+ struct dwc_otg_pcd_ep *ep = NULL;
+ int retval = 0, is_isoc_ep = 0;
+ dma_addr_t dma_addr = DWC_DMA_ADDR_INVALID;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p,%d)\n",
+ __func__, usb_ep, usb_req, gfp_flags);
+
+ if (!usb_req || !usb_req->complete || !usb_req->buf) {
+ DWC_WARN("bad params\n");
+ return -EINVAL;
+ }
+
+ if (!usb_ep) {
+ DWC_WARN("bad ep\n");
+ return -EINVAL;
+ }
+
+ pcd = gadget_wrapper->pcd;
+ if (!gadget_wrapper->driver ||
+ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
+ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
+ gadget_wrapper->gadget.speed);
+ DWC_WARN("bogus device state\n");
+ return -ESHUTDOWN;
+ }
+
+ DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n",
+ usb_ep->name, usb_req, usb_req->length, usb_req->buf);
+
+ usb_req->status = -EINPROGRESS;
+ usb_req->actual = 0;
+
+ ep = ep_from_handle(pcd, usb_ep);
+ if (ep == NULL)
+ is_isoc_ep = 0;
+ else
+ is_isoc_ep = (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) ? 1 : 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ dma_addr = usb_req->dma;
+#else
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ struct pci_dev *dev = gadget_wrapper->pcd->otg_dev->os_dep.pcidev;
+ if (usb_req->length != 0 && usb_req->dma == DWC_DMA_ADDR_INVALID) {
+ dma_addr = pci_map_single(dev, usb_req->buf, usb_req->length,
+ ep->dwc_ep.is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+ }
+ }
+#endif
+
+#ifdef DWC_UTE_PER_IO
+ if (is_isoc_ep == 1) {
+ retval = dwc_otg_pcd_xiso_ep_queue(pcd, usb_ep, usb_req->buf, dma_addr,
+ usb_req->length, usb_req->zero, usb_req,
+ gfp_flags == GFP_ATOMIC ? 1 : 0, &usb_req->ext_req);
+ if (retval)
+ return -EINVAL;
+
+ return 0;
+ }
+#endif
+ retval = dwc_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, dma_addr,
+ usb_req->length, usb_req->zero, usb_req,
+ gfp_flags == GFP_ATOMIC ? 1 : 0);
+ if (retval) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * This function cancels an I/O request from an EP.
+ */
+static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req)
+{
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, usb_req);
+
+ if (!usb_ep || !usb_req) {
+ DWC_WARN("bad argument\n");
+ return -EINVAL;
+ }
+ if (!gadget_wrapper->driver ||
+ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
+ DWC_WARN("bogus device state\n");
+ return -ESHUTDOWN;
+ }
+ if (dwc_otg_pcd_ep_dequeue(gadget_wrapper->pcd, usb_ep, usb_req)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * usb_ep_set_halt stalls an endpoint.
+ *
+ * usb_ep_clear_halt clears an endpoint halt and resets its data
+ * toggle.
+ *
+ * Both of these functions are implemented with the same underlying
+ * function. The behavior depends on the value argument.
+ *
+ * @param[in] usb_ep the Endpoint to halt or clear halt.
+ * @param[in] value
+ * - 0 means clear_halt.
+ * - 1 means set_halt,
+ * - 2 means clear stall lock flag.
+ * - 3 means set stall lock flag.
+ */
+static int ep_halt(struct usb_ep *usb_ep, int value)
+{
+ int retval = 0;
+
+ DWC_DEBUGPL(DBG_PCD, "HALT %s %d\n", usb_ep->name, value);
+
+ if (!usb_ep) {
+ DWC_WARN("bad ep\n");
+ return -EINVAL;
+ }
+
+ retval = dwc_otg_pcd_ep_halt(gadget_wrapper->pcd, usb_ep, value);
+ if (retval == -DWC_E_AGAIN) {
+ return -EAGAIN;
+ } else if (retval) {
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+#ifdef DWC_EN_ISOC
+/**
+ * This function is used to submit an ISOC Transfer Request to an EP.
+ *
+ * - Every time a sync period completes the request's completion callback
+ * is called to provide data to the gadget driver.
+ * - Once submitted the request cannot be modified.
+ * - Each request is turned into periodic data packets untill ISO
+ * Transfer is stopped..
+ */
+static int iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req,
+ gfp_t gfp_flags)
+{
+ int retval = 0;
+
+ if (!req || !req->process_buffer || !req->buf0 || !req->buf1) {
+ DWC_WARN("bad params\n");
+ return -EINVAL;
+ }
+
+ if (!usb_ep) {
+ DWC_PRINTF("bad params\n");
+ return -EINVAL;
+ }
+
+ req->status = -EINPROGRESS;
+
+ retval =
+ dwc_otg_pcd_iso_ep_start(gadget_wrapper->pcd, usb_ep, req->buf0,
+ req->buf1, req->dma0, req->dma1,
+ req->sync_frame, req->data_pattern_frame,
+ req->data_per_frame,
+ req->flags & USB_REQ_ISO_ASAP ? -1 : req->
+ start_frame, req->buf_proc_intrvl, req,
+ gfp_flags == GFP_ATOMIC ? 1 : 0);
+
+ if (retval) {
+ return -EINVAL;
+ }
+
+ return retval;
+}
+
+/**
+ * This function stops ISO EP Periodic Data Transfer.
+ */
+static int iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req)
+{
+ int retval = 0;
+ if (!usb_ep) {
+ DWC_WARN("bad ep\n");
+ }
+
+ if (!gadget_wrapper->driver ||
+ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
+ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
+ gadget_wrapper->gadget.speed);
+ DWC_WARN("bogus device state\n");
+ }
+
+ dwc_otg_pcd_iso_ep_stop(gadget_wrapper->pcd, usb_ep, req);
+ if (retval) {
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+static struct usb_iso_request *alloc_iso_request(struct usb_ep *ep,
+ int packets, gfp_t gfp_flags)
+{
+ struct usb_iso_request *pReq = NULL;
+ uint32_t req_size;
+
+ req_size = sizeof(struct usb_iso_request);
+ req_size +=
+ (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor)));
+
+ pReq = kmalloc(req_size, gfp_flags);
+ if (!pReq) {
+ DWC_WARN("Can't allocate Iso Request\n");
+ return 0;
+ }
+ pReq->iso_packet_desc0 = (void *)(pReq + 1);
+
+ pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets;
+
+ return pReq;
+}
+
+static void free_iso_request(struct usb_ep *ep, struct usb_iso_request *req)
+{
+ kfree(req);
+}
+
+static struct usb_isoc_ep_ops dwc_otg_pcd_ep_ops = {
+ .ep_ops = {
+ .enable = ep_enable,
+ .disable = ep_disable,
+
+ .alloc_request = dwc_otg_pcd_alloc_request,
+ .free_request = dwc_otg_pcd_free_request,
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ .alloc_buffer = dwc_otg_pcd_alloc_buffer,
+ .free_buffer = dwc_otg_pcd_free_buffer,
+#endif
+
+ .queue = ep_queue,
+ .dequeue = ep_dequeue,
+
+ .set_halt = ep_halt,
+ .fifo_status = 0,
+ .fifo_flush = 0,
+ },
+ .iso_ep_start = iso_ep_start,
+ .iso_ep_stop = iso_ep_stop,
+ .alloc_iso_request = alloc_iso_request,
+ .free_iso_request = free_iso_request,
+};
+
+#else
+
+static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
+ .enable = ep_enable,
+ .disable = ep_disable,
+
+ .alloc_request = dwc_otg_pcd_alloc_request,
+ .free_request = dwc_otg_pcd_free_request,
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ .alloc_buffer = dwc_otg_pcd_alloc_buffer,
+ .free_buffer = dwc_otg_pcd_free_buffer,
+#endif
+
+ .queue = ep_queue,
+ .dequeue = ep_dequeue,
+
+ .set_halt = ep_halt,
+ .fifo_status = 0,
+ .fifo_flush = 0,
+
+};
+
+#endif /* _EN_ISOC_ */
+/* Gadget Operations */
+/**
+ * The following gadget operations will be implemented in the DWC_otg
+ * PCD. Functions in the API that are not described below are not
+ * implemented.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_gadget_ops. The Gadget Driver calls the
+ * wrapper function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper functions
+ * (except for ioctl, which doesn't have a wrapper function). Within
+ * each section, the corresponding DWC_otg PCD function name is
+ * specified.
+ *
+ */
+
+/**
+ *Gets the USB Frame number of the last SOF.
+ */
+static int get_frame_number(struct usb_gadget *gadget)
+{
+ struct gadget_wrapper *d;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget);
+
+ if (gadget == 0) {
+ return -ENODEV;
+ }
+
+ d = container_of(gadget, struct gadget_wrapper, gadget);
+ return dwc_otg_pcd_get_frame_number(d->pcd);
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+static int test_lpm_enabled(struct usb_gadget *gadget)
+{
+ struct gadget_wrapper *d;
+
+ d = container_of(gadget, struct gadget_wrapper, gadget);
+
+ return dwc_otg_pcd_is_lpm_enabled(d->pcd);
+}
+#endif
+
+/**
+ * Initiates Session Request Protocol (SRP) to wakeup the host if no
+ * session is in progress. If a session is already in progress, but
+ * the device is suspended, remote wakeup signaling is started.
+ *
+ */
+static int wakeup(struct usb_gadget *gadget)
+{
+ struct gadget_wrapper *d;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget);
+
+ if (gadget == 0) {
+ return -ENODEV;
+ } else {
+ d = container_of(gadget, struct gadget_wrapper, gadget);
+ }
+ dwc_otg_pcd_wakeup(d->pcd);
+ return 0;
+}
+
+static const struct usb_gadget_ops dwc_otg_pcd_ops = {
+ .get_frame = get_frame_number,
+ .wakeup = wakeup,
+#ifdef CONFIG_USB_DWC_OTG_LPM
+ .lpm_support = test_lpm_enabled,
+#endif
+ // current versions must always be self-powered
+};
+
+static int _setup(dwc_otg_pcd_t * pcd, uint8_t * bytes)
+{
+ int retval = -DWC_E_NOT_SUPPORTED;
+ if (gadget_wrapper->driver && gadget_wrapper->driver->setup) {
+ retval = gadget_wrapper->driver->setup(&gadget_wrapper->gadget,
+ (struct usb_ctrlrequest
+ *)bytes);
+ }
+
+ if (retval == -ENOTSUPP) {
+ retval = -DWC_E_NOT_SUPPORTED;
+ } else if (retval < 0) {
+ retval = -DWC_E_INVALID;
+ }
+
+ return retval;
+}
+
+#ifdef DWC_EN_ISOC
+static int _isoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle, int proc_buf_num)
+{
+ int i, packet_count;
+ struct usb_gadget_iso_packet_descriptor *iso_packet = 0;
+ struct usb_iso_request *iso_req = req_handle;
+
+ if (proc_buf_num) {
+ iso_packet = iso_req->iso_packet_desc1;
+ } else {
+ iso_packet = iso_req->iso_packet_desc0;
+ }
+ packet_count =
+ dwc_otg_pcd_get_iso_packet_count(pcd, ep_handle, req_handle);
+ for (i = 0; i < packet_count; ++i) {
+ int status;
+ int actual;
+ int offset;
+ dwc_otg_pcd_get_iso_packet_params(pcd, ep_handle, req_handle,
+ i, &status, &actual, &offset);
+ switch (status) {
+ case -DWC_E_NO_DATA:
+ status = -ENODATA;
+ break;
+ default:
+ if (status) {
+ DWC_PRINTF("unknown status in isoc packet\n");
+ }
+
+ }
+ iso_packet[i].status = status;
+ iso_packet[i].offset = offset;
+ iso_packet[i].actual_length = actual;
+ }
+
+ iso_req->status = 0;
+ iso_req->process_buffer(ep_handle, iso_req);
+
+ return 0;
+}
+#endif /* DWC_EN_ISOC */
+
+#ifdef DWC_UTE_PER_IO
+/**
+ * Copy the contents of the extended request to the Linux usb_request's
+ * extended part and call the gadget's completion.
+ *
+ * @param pcd Pointer to the pcd structure
+ * @param ep_handle Void pointer to the usb_ep structure
+ * @param req_handle Void pointer to the usb_request structure
+ * @param status Request status returned from the portable logic
+ * @param ereq_port Void pointer to the extended request structure
+ * created in the the portable part that contains the
+ * results of the processed iso packets.
+ */
+static int _xisoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle, int32_t status, void *ereq_port)
+{
+ struct dwc_ute_iso_req_ext *ereqorg = NULL;
+ struct dwc_iso_xreq_port *ereqport = NULL;
+ struct dwc_ute_iso_packet_descriptor *desc_org = NULL;
+ int i;
+ struct usb_request *req;
+ //struct dwc_ute_iso_packet_descriptor *
+ //int status = 0;
+
+ req = (struct usb_request *)req_handle;
+ ereqorg = &req->ext_req;
+ ereqport = (struct dwc_iso_xreq_port *)ereq_port;
+ desc_org = ereqorg->per_io_frame_descs;
+
+ if (req && req->complete) {
+ /* Copy the request data from the portable logic to our request */
+ for (i = 0; i < ereqport->pio_pkt_count; i++) {
+ desc_org[i].actual_length =
+ ereqport->per_io_frame_descs[i].actual_length;
+ desc_org[i].status =
+ ereqport->per_io_frame_descs[i].status;
+ }
+
+ switch (status) {
+ case -DWC_E_SHUTDOWN:
+ req->status = -ESHUTDOWN;
+ break;
+ case -DWC_E_RESTART:
+ req->status = -ECONNRESET;
+ break;
+ case -DWC_E_INVALID:
+ req->status = -EINVAL;
+ break;
+ case -DWC_E_TIMEOUT:
+ req->status = -ETIMEDOUT;
+ break;
+ default:
+ req->status = status;
+ }
+
+ /* And call the gadget's completion */
+ req->complete(ep_handle, req);
+ }
+
+ return 0;
+}
+#endif /* DWC_UTE_PER_IO */
+static int _complete(dwc_otg_pcd_t * pcd, void *ep_handle,
+ void *req_handle, int32_t status, uint32_t actual)
+{
+ struct usb_request *req = (struct usb_request *)req_handle;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)
+ struct dwc_otg_pcd_ep *ep = NULL;
+#endif
+#ifdef PCI_INTERFACE
+ struct pci_dev *dev = NULL;
+#endif
+
+ if (req && req->complete) {
+ switch (status) {
+ case -DWC_E_SHUTDOWN:
+ req->status = -ESHUTDOWN;
+ break;
+ case -DWC_E_RESTART:
+ req->status = -ECONNRESET;
+ break;
+ case -DWC_E_INVALID:
+ req->status = -EINVAL;
+ break;
+ case -DWC_E_TIMEOUT:
+ req->status = -ETIMEDOUT;
+ break;
+ default:
+ req->status = status;
+
+ }
+
+ req->actual = actual;
+ DWC_SPINUNLOCK(pcd->lock);
+ req->complete(ep_handle, req);
+ DWC_SPINLOCK(pcd->lock);
+ }
+#ifdef PCI_INTERFACE
+ dev = gadget_wrapper->pcd->otg_dev->os_dep.pcidev;
+ ep = ep_from_handle(pcd, ep_handle);
+ if (GET_CORE_IF(pcd)->dma_enable) {
+ if (req->length != 0)
+ pci_unmap_single(dev, req->dma, req->length,
+ ep->dwc_ep.
+ is_in ? PCI_DMA_TODEVICE :
+ PCI_DMA_FROMDEVICE);
+ }
+#endif
+
+ return 0;
+}
+
+static int _connect(dwc_otg_pcd_t * pcd, int speed)
+{
+ gadget_wrapper->gadget.speed = speed;
+ return 0;
+}
+
+static int _disconnect(dwc_otg_pcd_t * pcd)
+{
+ if (gadget_wrapper->driver && gadget_wrapper->driver->disconnect) {
+ gadget_wrapper->driver->disconnect(&gadget_wrapper->gadget);
+ }
+ return 0;
+}
+
+static int _resume(dwc_otg_pcd_t * pcd)
+{
+ if (gadget_wrapper->driver && gadget_wrapper->driver->resume) {
+ gadget_wrapper->driver->resume(&gadget_wrapper->gadget);
+ }
+
+ return 0;
+}
+
+static int _suspend(dwc_otg_pcd_t * pcd)
+{
+ if (gadget_wrapper->driver && gadget_wrapper->driver->suspend) {
+ gadget_wrapper->driver->suspend(&gadget_wrapper->gadget);
+ }
+ return 0;
+}
+
+/**
+ * This function updates the otg values in the gadget structure.
+ */
+static int _hnp_changed(dwc_otg_pcd_t * pcd)
+{
+
+ if (!gadget_wrapper->gadget.is_otg)
+ return 0;
+
+ gadget_wrapper->gadget.b_hnp_enable = get_b_hnp_enable(pcd);
+ gadget_wrapper->gadget.a_hnp_support = get_a_hnp_support(pcd);
+ gadget_wrapper->gadget.a_alt_hnp_support = get_a_alt_hnp_support(pcd);
+ return 0;
+}
+
+static int _reset(dwc_otg_pcd_t * pcd)
+{
+ return 0;
+}
+
+#ifdef DWC_UTE_CFI
+static int _cfi_setup(dwc_otg_pcd_t * pcd, void *cfi_req)
+{
+ int retval = -DWC_E_INVALID;
+ if (gadget_wrapper->driver->cfi_feature_setup) {
+ retval =
+ gadget_wrapper->driver->
+ cfi_feature_setup(&gadget_wrapper->gadget,
+ (struct cfi_usb_ctrlrequest *)cfi_req);
+ }
+
+ return retval;
+}
+#endif
+
+static const struct dwc_otg_pcd_function_ops fops = {
+ .complete = _complete,
+#ifdef DWC_EN_ISOC
+ .isoc_complete = _isoc_complete,
+#endif
+ .setup = _setup,
+ .disconnect = _disconnect,
+ .connect = _connect,
+ .resume = _resume,
+ .suspend = _suspend,
+ .hnp_changed = _hnp_changed,
+ .reset = _reset,
+#ifdef DWC_UTE_CFI
+ .cfi_setup = _cfi_setup,
+#endif
+#ifdef DWC_UTE_PER_IO
+ .xisoc_complete = _xisoc_complete,
+#endif
+};
+
+/**
+ * This function is the top level PCD interrupt handler.
+ */
+static irqreturn_t dwc_otg_pcd_irq(int irq, void *dev)
+{
+ dwc_otg_pcd_t *pcd = dev;
+ int32_t retval = IRQ_NONE;
+
+ retval = dwc_otg_pcd_handle_intr(pcd);
+ if (retval != 0) {
+ S3C2410X_CLEAR_EINTPEND();
+ }
+ return IRQ_RETVAL(retval);
+}
+
+/**
+ * This function initialized the usb_ep structures to there default
+ * state.
+ *
+ * @param d Pointer on gadget_wrapper.
+ */
+void gadget_add_eps(struct gadget_wrapper *d)
+{
+ static const char *names[] = {
+
+ "ep0",
+ "ep1in",
+ "ep2in",
+ "ep3in",
+ "ep4in",
+ "ep5in",
+ "ep6in",
+ "ep7in",
+ "ep8in",
+ "ep9in",
+ "ep10in",
+ "ep11in",
+ "ep12in",
+ "ep13in",
+ "ep14in",
+ "ep15in",
+ "ep1out",
+ "ep2out",
+ "ep3out",
+ "ep4out",
+ "ep5out",
+ "ep6out",
+ "ep7out",
+ "ep8out",
+ "ep9out",
+ "ep10out",
+ "ep11out",
+ "ep12out",
+ "ep13out",
+ "ep14out",
+ "ep15out"
+ };
+
+ int i;
+ struct usb_ep *ep;
+ int8_t dev_endpoints;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s\n", __func__);
+
+ INIT_LIST_HEAD(&d->gadget.ep_list);
+ d->gadget.ep0 = &d->ep0;
+ d->gadget.speed = USB_SPEED_UNKNOWN;
+
+ INIT_LIST_HEAD(&d->gadget.ep0->ep_list);
+
+ /**
+ * Initialize the EP0 structure.
+ */
+ ep = &d->ep0;
+
+ /* Init the usb_ep structure. */
+ ep->name = names[0];
+ ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
+
+ /**
+ * @todo NGS: What should the max packet size be set to
+ * here? Before EP type is set?
+ */
+ ep->maxpacket = MAX_PACKET_SIZE;
+ dwc_otg_pcd_ep_enable(d->pcd, NULL, ep);
+
+ list_add_tail(&ep->ep_list, &d->gadget.ep_list);
+
+ /**
+ * Initialize the EP structures.
+ */
+ dev_endpoints = d->pcd->core_if->dev_if->num_in_eps;
+
+ for (i = 0; i < dev_endpoints; i++) {
+ ep = &d->in_ep[i];
+
+ /* Init the usb_ep structure. */
+ ep->name = names[d->pcd->in_ep[i].dwc_ep.num];
+ ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
+
+ /**
+ * @todo NGS: What should the max packet size be set to
+ * here? Before EP type is set?
+ */
+ ep->maxpacket = MAX_PACKET_SIZE;
+ list_add_tail(&ep->ep_list, &d->gadget.ep_list);
+ }
+
+ dev_endpoints = d->pcd->core_if->dev_if->num_out_eps;
+
+ for (i = 0; i < dev_endpoints; i++) {
+ ep = &d->out_ep[i];
+
+ /* Init the usb_ep structure. */
+ ep->name = names[15 + d->pcd->out_ep[i].dwc_ep.num];
+ ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
+
+ /**
+ * @todo NGS: What should the max packet size be set to
+ * here? Before EP type is set?
+ */
+ ep->maxpacket = MAX_PACKET_SIZE;
+
+ list_add_tail(&ep->ep_list, &d->gadget.ep_list);
+ }
+
+ /* remove ep0 from the list. There is a ep0 pointer. */
+ list_del_init(&d->ep0.ep_list);
+
+ d->ep0.maxpacket = MAX_EP0_SIZE;
+}
+
+/**
+ * This function releases the Gadget device.
+ * required by device_unregister().
+ *
+ * @todo Should this do something? Should it free the PCD?
+ */
+static void dwc_otg_pcd_gadget_release(struct device *dev)
+{
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, dev);
+}
+
+static struct gadget_wrapper *alloc_wrapper(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#endif
+ )
+{
+ static char pcd_name[] = "dwc_otg_pcd";
+#ifdef LM_INTERFACE
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev);
+#endif
+
+ struct gadget_wrapper *d;
+ int retval;
+
+ d = DWC_ALLOC(sizeof(*d));
+ if (d == NULL) {
+ return NULL;
+ }
+
+ memset(d, 0, sizeof(*d));
+
+ d->gadget.name = pcd_name;
+ d->pcd = otg_dev->pcd;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+ strcpy(d->gadget.dev.bus_id, "gadget");
+#else
+ dev_set_name(&d->gadget.dev, "%s", "gadget");
+#endif
+
+ d->gadget.dev.parent = &_dev->dev;
+ d->gadget.dev.release = dwc_otg_pcd_gadget_release;
+ d->gadget.ops = &dwc_otg_pcd_ops;
+ d->gadget.is_dualspeed = dwc_otg_pcd_is_dualspeed(otg_dev->pcd);
+ d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd);
+
+ d->driver = 0;
+ /* Register the gadget device */
+ retval = device_register(&d->gadget.dev);
+ if (retval != 0) {
+ DWC_ERROR("device_register failed\n");
+ DWC_FREE(d);
+ return NULL;
+ }
+
+ return d;
+}
+
+static void free_wrapper(struct gadget_wrapper *d)
+{
+ if (d->driver) {
+ /* should have been done already by driver model core */
+ DWC_WARN("driver '%s' is still registered\n",
+ d->driver->driver.name);
+ usb_gadget_unregister_driver(d->driver);
+ }
+
+ device_unregister(&d->gadget.dev);
+ DWC_FREE(d);
+}
+
+/**
+ * This function initialized the PCD portion of the driver.
+ *
+ */
+int pcd_init(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#else
+ struct platform_device *_dev
+#endif
+ )
+{
+#ifdef LM_INTERFACE
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev);
+#endif
+
+ int retval = 0;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev);
+
+ otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if);
+
+ if (!otg_dev->pcd) {
+ DWC_ERROR("dwc_otg_pcd_init failed\n");
+ return -ENOMEM;
+ }
+
+ otg_dev->pcd->otg_dev = otg_dev;
+ gadget_wrapper = alloc_wrapper(_dev);
+
+ /*
+ * Initialize EP structures
+ */
+ gadget_add_eps(gadget_wrapper);
+ /*
+ * Setup interupt handler
+ */
+ DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", _dev->irq);
+ retval = request_irq(_dev->irq, dwc_otg_pcd_irq,
+ IRQF_SHARED | IRQF_DISABLED,
+ gadget_wrapper->gadget.name, otg_dev->pcd);
+ if (retval != 0) {
+ DWC_ERROR("request of irq%d failed\n", _dev->irq);
+ free_wrapper(gadget_wrapper);
+ return -EBUSY;
+ }
+
+ dwc_otg_pcd_start(gadget_wrapper->pcd, &fops);
+
+ return retval;
+}
+
+/**
+ * Cleanup the PCD.
+ */
+void pcd_remove(
+#ifdef LM_INTERFACE
+ struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+ struct pci_dev *_dev
+#endif
+ )
+{
+#ifdef LM_INTERFACE
+ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+#elif defined(PCI_INTERFACE)
+ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev);
+#endif
+ dwc_otg_pcd_t *pcd = otg_dev->pcd;
+
+ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev);
+
+ /*
+ * Free the IRQ
+ */
+ free_irq(_dev->irq, pcd);
+ dwc_otg_pcd_remove(otg_dev->pcd);
+ free_wrapper(gadget_wrapper);
+ otg_dev->pcd = 0;
+}
+
+/**
+ * This function registers a gadget driver with the PCD.
+ *
+ * When a driver is successfully registered, it will receive control
+ * requests including set_configuration(), which enables non-control
+ * requests. then usb traffic follows until a disconnect is reported.
+ * then a host may connect again, or the driver might get unbound.
+ *
+ * @param driver The driver being registered
+ * @param bind The bind function of gadget driver
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+#else
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+#endif
+{
+ int retval;
+
+ DWC_DEBUGPL(DBG_PCD, "registering gadget driver '%s'\n",
+ driver->driver.name);
+
+ if (!driver || driver->speed == USB_SPEED_UNKNOWN ||
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+ !driver->bind ||
+#else
+ !bind ||
+#endif
+ !driver->unbind || !driver->disconnect || !driver->setup) {
+ DWC_DEBUGPL(DBG_PCDV, "EINVAL\n");
+ return -EINVAL;
+ }
+ if (gadget_wrapper == 0) {
+ DWC_DEBUGPL(DBG_PCDV, "ENODEV\n");
+ return -ENODEV;
+ }
+ if (gadget_wrapper->driver != 0) {
+ DWC_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver);
+ return -EBUSY;
+ }
+
+ /* hook up the driver */
+ gadget_wrapper->driver = driver;
+ gadget_wrapper->gadget.dev.driver = &driver->driver;
+
+ DWC_DEBUGPL(DBG_PCD, "bind to driver %s\n", driver->driver.name);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+ retval = driver->bind(&gadget_wrapper->gadget);
+#else
+ retval = bind(&gadget_wrapper->gadget);
+#endif
+ if (retval) {
+ DWC_ERROR("bind to driver %s --> error %d\n",
+ driver->driver.name, retval);
+ gadget_wrapper->driver = 0;
+ gadget_wrapper->gadget.dev.driver = 0;
+ return retval;
+ }
+ DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n",
+ driver->driver.name);
+ return 0;
+}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+EXPORT_SYMBOL(usb_gadget_register_driver);
+#else
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+#endif
+
+/**
+ * This function unregisters a gadget driver
+ *
+ * @param driver The driver being unregistered
+ */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ //DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, _driver);
+
+ if (gadget_wrapper == 0) {
+ DWC_DEBUGPL(DBG_ANY, "%s Return(%d): s_pcd==0\n", __func__,
+ -ENODEV);
+ return -ENODEV;
+ }
+ if (driver == 0 || driver != gadget_wrapper->driver) {
+ DWC_DEBUGPL(DBG_ANY, "%s Return(%d): driver?\n", __func__,
+ -EINVAL);
+ return -EINVAL;
+ }
+
+ driver->unbind(&gadget_wrapper->gadget);
+ gadget_wrapper->driver = 0;
+
+ DWC_DEBUGPL(DBG_ANY, "unregistered driver '%s'\n", driver->driver.name);
+ return 0;
+}
+
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+#endif /* DWC_HOST_ONLY */
diff --git a/drivers/usb/dwc_otg/dwc_otg_regs.h b/drivers/usb/dwc_otg/dwc_otg_regs.h
new file mode 100644
index 0000000..8dc648b
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_regs.h
@@ -0,0 +1,2545 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_regs.h $
+ * $Revision: #97 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871160 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#ifndef __DWC_OTG_REGS_H__
+#define __DWC_OTG_REGS_H__
+
+#include "dwc_otg_core_if.h"
+
+/**
+ * @file
+ *
+ * This file contains the data structures for accessing the DWC_otg core registers.
+ *
+ * The application interfaces with the HS OTG core by reading from and
+ * writing to the Control and Status Register (CSR) space through the
+ * AHB Slave interface. These registers are 32 bits wide, and the
+ * addresses are 32-bit-block aligned.
+ * CSRs are classified as follows:
+ * - Core Global Registers
+ * - Device Mode Registers
+ * - Device Global Registers
+ * - Device Endpoint Specific Registers
+ * - Host Mode Registers
+ * - Host Global Registers
+ * - Host Port CSRs
+ * - Host Channel Specific Registers
+ *
+ * Only the Core Global registers can be accessed in both Device and
+ * Host modes. When the HS OTG core is operating in one mode, either
+ * Device or Host, the application must not access registers from the
+ * other mode. When the core switches from one mode to another, the
+ * registers in the new mode of operation must be reprogrammed as they
+ * would be after a power-on reset.
+ */
+
+/****************************************************************************/
+/** DWC_otg Core registers .
+ * The dwc_otg_core_global_regs structure defines the size
+ * and relative field offsets for the Core Global registers.
+ */
+typedef struct dwc_otg_core_global_regs {
+ /** OTG Control and Status Register. <i>Offset: 000h</i> */
+ volatile uint32_t gotgctl;
+ /** OTG Interrupt Register. <i>Offset: 004h</i> */
+ volatile uint32_t gotgint;
+ /**Core AHB Configuration Register. <i>Offset: 008h</i> */
+ volatile uint32_t gahbcfg;
+
+#define DWC_GLBINTRMASK 0x0001
+#define DWC_DMAENABLE 0x0020
+#define DWC_NPTXEMPTYLVL_EMPTY 0x0080
+#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000
+#define DWC_PTXEMPTYLVL_EMPTY 0x0100
+#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000
+
+ /**Core USB Configuration Register. <i>Offset: 00Ch</i> */
+ volatile uint32_t gusbcfg;
+ /**Core Reset Register. <i>Offset: 010h</i> */
+ volatile uint32_t grstctl;
+ /**Core Interrupt Register. <i>Offset: 014h</i> */
+ volatile uint32_t gintsts;
+ /**Core Interrupt Mask Register. <i>Offset: 018h</i> */
+ volatile uint32_t gintmsk;
+ /**Receive Status Queue Read Register (Read Only). <i>Offset: 01Ch</i> */
+ volatile uint32_t grxstsr;
+ /**Receive Status Queue Read & POP Register (Read Only). <i>Offset: 020h</i>*/
+ volatile uint32_t grxstsp;
+ /**Receive FIFO Size Register. <i>Offset: 024h</i> */
+ volatile uint32_t grxfsiz;
+ /**Non Periodic Transmit FIFO Size Register. <i>Offset: 028h</i> */
+ volatile uint32_t gnptxfsiz;
+ /**Non Periodic Transmit FIFO/Queue Status Register (Read
+ * Only). <i>Offset: 02Ch</i> */
+ volatile uint32_t gnptxsts;
+ /**I2C Access Register. <i>Offset: 030h</i> */
+ volatile uint32_t gi2cctl;
+ /**PHY Vendor Control Register. <i>Offset: 034h</i> */
+ volatile uint32_t gpvndctl;
+ /**General Purpose Input/Output Register. <i>Offset: 038h</i> */
+ volatile uint32_t ggpio;
+ /**User ID Register. <i>Offset: 03Ch</i> */
+ volatile uint32_t guid;
+ /**Synopsys ID Register (Read Only). <i>Offset: 040h</i> */
+ volatile uint32_t gsnpsid;
+ /**User HW Config1 Register (Read Only). <i>Offset: 044h</i> */
+ volatile uint32_t ghwcfg1;
+ /**User HW Config2 Register (Read Only). <i>Offset: 048h</i> */
+ volatile uint32_t ghwcfg2;
+#define DWC_SLAVE_ONLY_ARCH 0
+#define DWC_EXT_DMA_ARCH 1
+#define DWC_INT_DMA_ARCH 2
+
+#define DWC_MODE_HNP_SRP_CAPABLE 0
+#define DWC_MODE_SRP_ONLY_CAPABLE 1
+#define DWC_MODE_NO_HNP_SRP_CAPABLE 2
+#define DWC_MODE_SRP_CAPABLE_DEVICE 3
+#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4
+#define DWC_MODE_SRP_CAPABLE_HOST 5
+#define DWC_MODE_NO_SRP_CAPABLE_HOST 6
+
+ /**User HW Config3 Register (Read Only). <i>Offset: 04Ch</i> */
+ volatile uint32_t ghwcfg3;
+ /**User HW Config4 Register (Read Only). <i>Offset: 050h</i>*/
+ volatile uint32_t ghwcfg4;
+ /** Core LPM Configuration register <i>Offset: 054h</i>*/
+ volatile uint32_t glpmcfg;
+ /** Global PowerDn Register <i>Offset: 058h</i> */
+ volatile uint32_t gpwrdn;
+ /** Global DFIFO SW Config Register <i>Offset: 05Ch</i> */
+ volatile uint32_t gdfifocfg;
+ /** ADP Control Register <i>Offset: 060h</i> */
+ volatile uint32_t adpctl;
+ /** Reserved <i>Offset: 064h-0FFh</i> */
+ volatile uint32_t reserved39[39];
+ /** Host Periodic Transmit FIFO Size Register. <i>Offset: 100h</i> */
+ volatile uint32_t hptxfsiz;
+ /** Device Periodic Transmit FIFO#n Register if dedicated fifos are disabled,
+ otherwise Device Transmit FIFO#n Register.
+ * <i>Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15).</i> */
+ volatile uint32_t dtxfsiz[15];
+} dwc_otg_core_global_regs_t;
+
+/**
+ * This union represents the bit fields of the Core OTG Control
+ * and Status Register (GOTGCTL). Set the bits using the bit
+ * fields then write the <i>d32</i> value to the register.
+ */
+typedef union gotgctl_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned sesreqscs:1;
+ unsigned sesreq:1;
+ unsigned vbvalidoven:1;
+ unsigned vbvalidovval:1;
+ unsigned avalidoven:1;
+ unsigned avalidovval:1;
+ unsigned bvalidoven:1;
+ unsigned bvalidovval:1;
+ unsigned hstnegscs:1;
+ unsigned hnpreq:1;
+ unsigned hstsethnpen:1;
+ unsigned devhnpen:1;
+ unsigned reserved12_15:4;
+ unsigned conidsts:1;
+ unsigned dbnctime:1;
+ unsigned asesvld:1;
+ unsigned bsesvld:1;
+ unsigned otgver:1;
+ unsigned reserved1:1;
+ unsigned multvalidbc:5;
+ unsigned chirpen:1;
+ unsigned reserved28_31:4;
+ } b;
+} gotgctl_data_t;
+
+/**
+ * This union represents the bit fields of the Core OTG Interrupt Register
+ * (GOTGINT). Set/clear the bits using the bit fields then write the <i>d32</i>
+ * value to the register.
+ */
+typedef union gotgint_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Current Mode */
+ unsigned reserved0_1:2;
+
+ /** Session End Detected */
+ unsigned sesenddet:1;
+
+ unsigned reserved3_7:5;
+
+ /** Session Request Success Status Change */
+ unsigned sesreqsucstschng:1;
+ /** Host Negotiation Success Status Change */
+ unsigned hstnegsucstschng:1;
+
+ unsigned reserved10_16:7;
+
+ /** Host Negotiation Detected */
+ unsigned hstnegdet:1;
+ /** A-Device Timeout Change */
+ unsigned adevtoutchng:1;
+ /** Debounce Done */
+ unsigned debdone:1;
+ /** Multi-Valued input changed */
+ unsigned mvic:1;
+
+ unsigned reserved31_21:11;
+
+ } b;
+} gotgint_data_t;
+
+/**
+ * This union represents the bit fields of the Core AHB Configuration
+ * Register (GAHBCFG). Set/clear the bits using the bit fields then
+ * write the <i>d32</i> value to the register.
+ */
+typedef union gahbcfg_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned glblintrmsk:1;
+#define DWC_GAHBCFG_GLBINT_ENABLE 1
+
+ unsigned hburstlen:4;
+#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7
+
+ unsigned dmaenable:1;
+#define DWC_GAHBCFG_DMAENABLE 1
+ unsigned reserved:1;
+ unsigned nptxfemplvl_txfemplvl:1;
+ unsigned ptxfemplvl:1;
+#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1
+#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0
+ unsigned reserved9_20:12;
+ unsigned remmemsupp:1;
+ unsigned notialldmawrit:1;
+ unsigned ahbsingle:1;
+ unsigned reserved24_31:8;
+ } b;
+} gahbcfg_data_t;
+
+/**
+ * This union represents the bit fields of the Core USB Configuration
+ * Register (GUSBCFG). Set the bits using the bit fields then write
+ * the <i>d32</i> value to the register.
+ */
+typedef union gusbcfg_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned toutcal:3;
+ unsigned phyif:1;
+ unsigned ulpi_utmi_sel:1;
+ unsigned fsintf:1;
+ unsigned physel:1;
+ unsigned ddrsel:1;
+ unsigned srpcap:1;
+ unsigned hnpcap:1;
+ unsigned usbtrdtim:4;
+ unsigned reserved1:1;
+ unsigned phylpwrclksel:1;
+ unsigned otgutmifssel:1;
+ unsigned ulpi_fsls:1;
+ unsigned ulpi_auto_res:1;
+ unsigned ulpi_clk_sus_m:1;
+ unsigned ulpi_ext_vbus_drv:1;
+ unsigned ulpi_int_vbus_indicator:1;
+ unsigned term_sel_dl_pulse:1;
+ unsigned indicator_complement:1;
+ unsigned indicator_pass_through:1;
+ unsigned ulpi_int_prot_dis:1;
+ unsigned ic_usb_cap:1;
+ unsigned ic_traffic_pull_remove:1;
+ unsigned tx_end_delay:1;
+ unsigned force_host_mode:1;
+ unsigned force_dev_mode:1;
+ unsigned reserved31:1;
+ } b;
+} gusbcfg_data_t;
+
+/**
+ * This union represents the bit fields of the Core Reset Register
+ * (GRSTCTL). Set/clear the bits using the bit fields then write the
+ * <i>d32</i> value to the register.
+ */
+typedef union grstctl_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Core Soft Reset (CSftRst) (Device and Host)
+ *
+ * The application can flush the control logic in the
+ * entire core using this bit. This bit resets the
+ * pipelines in the AHB Clock domain as well as the
+ * PHY Clock domain.
+ *
+ * The state machines are reset to an IDLE state, the
+ * control bits in the CSRs are cleared, all the
+ * transmit FIFOs and the receive FIFO are flushed.
+ *
+ * The status mask bits that control the generation of
+ * the interrupt, are cleared, to clear the
+ * interrupt. The interrupt status bits are not
+ * cleared, so the application can get the status of
+ * any events that occurred in the core after it has
+ * set this bit.
+ *
+ * Any transactions on the AHB are terminated as soon
+ * as possible following the protocol. Any
+ * transactions on the USB are terminated immediately.
+ *
+ * The configuration settings in the CSRs are
+ * unchanged, so the software doesn't have to
+ * reprogram these registers (Device
+ * Configuration/Host Configuration/Core System
+ * Configuration/Core PHY Configuration).
+ *
+ * The application can write to this bit, any time it
+ * wants to reset the core. This is a self clearing
+ * bit and the core clears this bit after all the
+ * necessary logic is reset in the core, which may
+ * take several clocks, depending on the current state
+ * of the core.
+ */
+ unsigned csftrst:1;
+ /** Hclk Soft Reset
+ *
+ * The application uses this bit to reset the control logic in
+ * the AHB clock domain. Only AHB clock domain pipelines are
+ * reset.
+ */
+ unsigned hsftrst:1;
+ /** Host Frame Counter Reset (Host Only)<br>
+ *
+ * The application can reset the (micro)frame number
+ * counter inside the core, using this bit. When the
+ * (micro)frame counter is reset, the subsequent SOF
+ * sent out by the core, will have a (micro)frame
+ * number of 0.
+ */
+ unsigned hstfrm:1;
+ /** In Token Sequence Learning Queue Flush
+ * (INTknQFlsh) (Device Only)
+ */
+ unsigned intknqflsh:1;
+ /** RxFIFO Flush (RxFFlsh) (Device and Host)
+ *
+ * The application can flush the entire Receive FIFO
+ * using this bit. The application must first
+ * ensure that the core is not in the middle of a
+ * transaction. The application should write into
+ * this bit, only after making sure that neither the
+ * DMA engine is reading from the RxFIFO nor the MAC
+ * is writing the data in to the FIFO. The
+ * application should wait until the bit is cleared
+ * before performing any other operations. This bit
+ * will takes 8 clocks (slowest of PHY or AHB clock)
+ * to clear.
+ */
+ unsigned rxfflsh:1;
+ /** TxFIFO Flush (TxFFlsh) (Device and Host).
+ *
+ * This bit is used to selectively flush a single or
+ * all transmit FIFOs. The application must first
+ * ensure that the core is not in the middle of a
+ * transaction. The application should write into
+ * this bit, only after making sure that neither the
+ * DMA engine is writing into the TxFIFO nor the MAC
+ * is reading the data out of the FIFO. The
+ * application should wait until the core clears this
+ * bit, before performing any operations. This bit
+ * will takes 8 clocks (slowest of PHY or AHB clock)
+ * to clear.
+ */
+ unsigned txfflsh:1;
+
+ /** TxFIFO Number (TxFNum) (Device and Host).
+ *
+ * This is the FIFO number which needs to be flushed,
+ * using the TxFIFO Flush bit. This field should not
+ * be changed until the TxFIFO Flush bit is cleared by
+ * the core.
+ * - 0x0 : Non Periodic TxFIFO Flush
+ * - 0x1 : Periodic TxFIFO #1 Flush in device mode
+ * or Periodic TxFIFO in host mode
+ * - 0x2 : Periodic TxFIFO #2 Flush in device mode.
+ * - ...
+ * - 0xF : Periodic TxFIFO #15 Flush in device mode
+ * - 0x10: Flush all the Transmit NonPeriodic and
+ * Transmit Periodic FIFOs in the core
+ */
+ unsigned txfnum:5;
+ /** Reserved */
+ unsigned reserved11_29:19;
+ /** DMA Request Signal. Indicated DMA request is in
+ * probress. Used for debug purpose. */
+ unsigned dmareq:1;
+ /** AHB Master Idle. Indicates the AHB Master State
+ * Machine is in IDLE condition. */
+ unsigned ahbidle:1;
+ } b;
+} grstctl_t;
+
+/**
+ * This union represents the bit fields of the Core Interrupt Mask
+ * Register (GINTMSK). Set/clear the bits using the bit fields then
+ * write the <i>d32</i> value to the register.
+ */
+typedef union gintmsk_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned reserved0:1;
+ unsigned modemismatch:1;
+ unsigned otgintr:1;
+ unsigned sofintr:1;
+ unsigned rxstsqlvl:1;
+ unsigned nptxfempty:1;
+ unsigned ginnakeff:1;
+ unsigned goutnakeff:1;
+ unsigned ulpickint:1;
+ unsigned i2cintr:1;
+ unsigned erlysuspend:1;
+ unsigned usbsuspend:1;
+ unsigned usbreset:1;
+ unsigned enumdone:1;
+ unsigned isooutdrop:1;
+ unsigned eopframe:1;
+ unsigned restoredone:1;
+ unsigned epmismatch:1;
+ unsigned inepintr:1;
+ unsigned outepintr:1;
+ unsigned incomplisoin:1;
+ unsigned incomplisoout:1;
+ unsigned fetsusp:1;
+ unsigned resetdet:1;
+ unsigned portintr:1;
+ unsigned hcintr:1;
+ unsigned ptxfempty:1;
+ unsigned lpmtranrcvd:1;
+ unsigned conidstschng:1;
+ unsigned disconnect:1;
+ unsigned sessreqintr:1;
+ unsigned wkupintr:1;
+ } b;
+} gintmsk_data_t;
+/**
+ * This union represents the bit fields of the Core Interrupt Register
+ * (GINTSTS). Set/clear the bits using the bit fields then write the
+ * <i>d32</i> value to the register.
+ */
+typedef union gintsts_data {
+ /** raw register data */
+ uint32_t d32;
+#define DWC_SOF_INTR_MASK 0x0008
+ /** register bits */
+ struct {
+#define DWC_HOST_MODE 1
+ unsigned curmode:1;
+ unsigned modemismatch:1;
+ unsigned otgintr:1;
+ unsigned sofintr:1;
+ unsigned rxstsqlvl:1;
+ unsigned nptxfempty:1;
+ unsigned ginnakeff:1;
+ unsigned goutnakeff:1;
+ unsigned ulpickint:1;
+ unsigned i2cintr:1;
+ unsigned erlysuspend:1;
+ unsigned usbsuspend:1;
+ unsigned usbreset:1;
+ unsigned enumdone:1;
+ unsigned isooutdrop:1;
+ unsigned eopframe:1;
+ unsigned restoredone:1;
+ unsigned epmismatch:1;
+ unsigned inepint:1;
+ unsigned outepintr:1;
+ unsigned incomplisoin:1;
+ unsigned incomplisoout:1;
+ unsigned fetsusp:1;
+ unsigned resetdet:1;
+ unsigned portintr:1;
+ unsigned hcintr:1;
+ unsigned ptxfempty:1;
+ unsigned lpmtranrcvd:1;
+ unsigned conidstschng:1;
+ unsigned disconnect:1;
+ unsigned sessreqintr:1;
+ unsigned wkupintr:1;
+ } b;
+} gintsts_data_t;
+
+/**
+ * This union represents the bit fields in the Device Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i>
+ * element then read out the bits using the <i>b</i>it elements.
+ */
+typedef union device_grxsts_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned epnum:4;
+ unsigned bcnt:11;
+ unsigned dpid:2;
+
+#define DWC_STS_DATA_UPDT 0x2 // OUT Data Packet
+#define DWC_STS_XFER_COMP 0x3 // OUT Data Transfer Complete
+
+#define DWC_DSTS_GOUT_NAK 0x1 // Global OUT NAK
+#define DWC_DSTS_SETUP_COMP 0x4 // Setup Phase Complete
+#define DWC_DSTS_SETUP_UPDT 0x6 // SETUP Packet
+ unsigned pktsts:4;
+ unsigned fn:4;
+ unsigned reserved25_31:7;
+ } b;
+} device_grxsts_data_t;
+
+/**
+ * This union represents the bit fields in the Host Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i>
+ * element then read out the bits using the <i>b</i>it elements.
+ */
+typedef union host_grxsts_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned chnum:4;
+ unsigned bcnt:11;
+ unsigned dpid:2;
+
+ unsigned pktsts:4;
+#define DWC_GRXSTS_PKTSTS_IN 0x2
+#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3
+#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5
+#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7
+
+ unsigned reserved21_31:11;
+ } b;
+} host_grxsts_data_t;
+
+/**
+ * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ,
+ * GNPTXFSIZ, DPTXFSIZn, DIEPTXFn). Read the register into the <i>d32</i> element
+ * then read out the bits using the <i>b</i>it elements.
+ */
+typedef union fifosize_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned startaddr:16;
+ unsigned depth:16;
+ } b;
+} fifosize_data_t;
+
+/**
+ * This union represents the bit fields in the Non-Periodic Transmit
+ * FIFO/Queue Status Register (GNPTXSTS). Read the register into the
+ * <i>d32</i> element then read out the bits using the <i>b</i>it
+ * elements.
+ */
+typedef union gnptxsts_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned nptxfspcavail:16;
+ unsigned nptxqspcavail:8;
+ /** Top of the Non-Periodic Transmit Request Queue
+ * - bit 24 - Terminate (Last entry for the selected
+ * channel/EP)
+ * - bits 26:25 - Token Type
+ * - 2'b00 - IN/OUT
+ * - 2'b01 - Zero Length OUT
+ * - 2'b10 - PING/Complete Split
+ * - 2'b11 - Channel Halt
+ * - bits 30:27 - Channel/EP Number
+ */
+ unsigned nptxqtop_terminate:1;
+ unsigned nptxqtop_token:2;
+ unsigned nptxqtop_chnep:4;
+ unsigned reserved:1;
+ } b;
+} gnptxsts_data_t;
+
+/**
+ * This union represents the bit fields in the Transmit
+ * FIFO Status Register (DTXFSTS). Read the register into the
+ * <i>d32</i> element then read out the bits using the <i>b</i>it
+ * elements.
+ */
+typedef union dtxfsts_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned txfspcavail:16;
+ unsigned reserved:16;
+ } b;
+} dtxfsts_data_t;
+
+/**
+ * This union represents the bit fields in the I2C Control Register
+ * (I2CCTL). Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union gi2cctl_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned rwdata:8;
+ unsigned regaddr:8;
+ unsigned addr:7;
+ unsigned i2cen:1;
+ unsigned ack:1;
+ unsigned i2csuspctl:1;
+ unsigned i2cdevaddr:2;
+ unsigned i2cdatse0:1;
+ unsigned reserved:1;
+ unsigned rw:1;
+ unsigned bsydne:1;
+ } b;
+} gi2cctl_data_t;
+
+/**
+ * This union represents the bit fields in the PHY Vendor Control Register
+ * (GPVNDCTL). Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union gpvndctl_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned regdata:8;
+ unsigned vctrl:8;
+ unsigned regaddr16_21:6;
+ unsigned regwr:1;
+ unsigned reserved23_24:2;
+ unsigned newregreq:1;
+ unsigned vstsbsy:1;
+ unsigned vstsdone:1;
+ unsigned reserved28_30:3;
+ unsigned disulpidrvr:1;
+ } b;
+} gpvndctl_data_t;
+
+/**
+ * This union represents the bit fields in the General Purpose
+ * Input/Output Register (GGPIO).
+ * Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union ggpio_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned gpi:16;
+ unsigned gpo:16;
+ } b;
+} ggpio_data_t;
+
+/**
+ * This union represents the bit fields in the User ID Register
+ * (GUID). Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union guid_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned rwdata:32;
+ } b;
+} guid_data_t;
+
+/**
+ * This union represents the bit fields in the Synopsys ID Register
+ * (GSNPSID). Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union gsnpsid_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned rwdata:32;
+ } b;
+} gsnpsid_data_t;
+
+/**
+ * This union represents the bit fields in the User HW Config1
+ * Register. Read the register into the <i>d32</i> element then read
+ * out the bits using the <i>b</i>it elements.
+ */
+typedef union hwcfg1_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned ep_dir0:2;
+ unsigned ep_dir1:2;
+ unsigned ep_dir2:2;
+ unsigned ep_dir3:2;
+ unsigned ep_dir4:2;
+ unsigned ep_dir5:2;
+ unsigned ep_dir6:2;
+ unsigned ep_dir7:2;
+ unsigned ep_dir8:2;
+ unsigned ep_dir9:2;
+ unsigned ep_dir10:2;
+ unsigned ep_dir11:2;
+ unsigned ep_dir12:2;
+ unsigned ep_dir13:2;
+ unsigned ep_dir14:2;
+ unsigned ep_dir15:2;
+ } b;
+} hwcfg1_data_t;
+
+/**
+ * This union represents the bit fields in the User HW Config2
+ * Register. Read the register into the <i>d32</i> element then read
+ * out the bits using the <i>b</i>it elements.
+ */
+typedef union hwcfg2_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /* GHWCFG2 */
+ unsigned op_mode:3;
+#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0
+#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1
+#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6
+
+ unsigned architecture:2;
+ unsigned point2point:1;
+ unsigned hs_phy_type:2;
+#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1
+#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3
+
+ unsigned fs_phy_type:2;
+ unsigned num_dev_ep:4;
+ unsigned num_host_chan:4;
+ unsigned perio_ep_supported:1;
+ unsigned dynamic_fifo:1;
+ unsigned multi_proc_int:1;
+ unsigned reserved21:1;
+ unsigned nonperio_tx_q_depth:2;
+ unsigned host_perio_tx_q_depth:2;
+ unsigned dev_token_q_depth:5;
+ unsigned otg_enable_ic_usb:1;
+ } b;
+} hwcfg2_data_t;
+
+/**
+ * This union represents the bit fields in the User HW Config3
+ * Register. Read the register into the <i>d32</i> element then read
+ * out the bits using the <i>b</i>it elements.
+ */
+typedef union hwcfg3_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /* GHWCFG3 */
+ unsigned xfer_size_cntr_width:4;
+ unsigned packet_size_cntr_width:3;
+ unsigned otg_func:1;
+ unsigned i2c:1;
+ unsigned vendor_ctrl_if:1;
+ unsigned optional_features:1;
+ unsigned synch_reset_type:1;
+ unsigned adp_supp:1;
+ unsigned otg_enable_hsic:1;
+ unsigned bc_support:1;
+ unsigned otg_lpm_en:1;
+ unsigned dfifo_depth:16;
+ } b;
+} hwcfg3_data_t;
+
+/**
+ * This union represents the bit fields in the User HW Config4
+ * Register. Read the register into the <i>d32</i> element then read
+ * out the bits using the <i>b</i>it elements.
+ */
+typedef union hwcfg4_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned num_dev_perio_in_ep:4;
+ unsigned power_optimiz:1;
+ unsigned min_ahb_freq:1;
+ unsigned part_power_down:1;
+ unsigned reserved:7;
+ unsigned utmi_phy_data_width:2;
+ unsigned num_dev_mode_ctrl_ep:4;
+ unsigned iddig_filt_en:1;
+ unsigned vbus_valid_filt_en:1;
+ unsigned a_valid_filt_en:1;
+ unsigned b_valid_filt_en:1;
+ unsigned session_end_filt_en:1;
+ unsigned ded_fifo_en:1;
+ unsigned num_in_eps:4;
+ unsigned desc_dma:1;
+ unsigned desc_dma_dyn:1;
+ } b;
+} hwcfg4_data_t;
+
+/**
+ * This union represents the bit fields of the Core LPM Configuration
+ * Register (GLPMCFG). Set the bits using bit fields then write
+ * the <i>d32</i> value to the register.
+ */
+typedef union glpmctl_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** LPM-Capable (LPMCap) (Device and Host)
+ * The application uses this bit to control
+ * the DWC_otg core LPM capabilities.
+ */
+ unsigned lpm_cap_en:1;
+ /** LPM response programmed by application (AppL1Res) (Device)
+ * Handshake response to LPM token pre-programmed
+ * by device application software.
+ */
+ unsigned appl_resp:1;
+ /** Host Initiated Resume Duration (HIRD) (Device and Host)
+ * In Host mode this field indicates the value of HIRD
+ * to be sent in an LPM transaction.
+ * In Device mode this field is updated with the
+ * Received LPM Token HIRD bmAttribute
+ * when an ACK/NYET/STALL response is sent
+ * to an LPM transaction.
+ */
+ unsigned hird:4;
+ /** RemoteWakeEnable (bRemoteWake) (Device and Host)
+ * In Host mode this bit indicates the value of remote
+ * wake up to be sent in wIndex field of LPM transaction.
+ * In Device mode this field is updated with the
+ * Received LPM Token bRemoteWake bmAttribute
+ * when an ACK/NYET/STALL response is sent
+ * to an LPM transaction.
+ */
+ unsigned rem_wkup_en:1;
+ /** Enable utmi_sleep_n (EnblSlpM) (Device and Host)
+ * The application uses this bit to control
+ * the utmi_sleep_n assertion to the PHY when in L1 state.
+ */
+ unsigned en_utmi_sleep:1;
+ /** HIRD Threshold (HIRD_Thres) (Device and Host)
+ */
+ unsigned hird_thres:5;
+ /** LPM Response (CoreL1Res) (Device and Host)
+ * In Host mode this bit contains handsake response to
+ * LPM transaction.
+ * In Device mode the response of the core to
+ * LPM transaction received is reflected in these two bits.
+ - 0x0 : ERROR (No handshake response)
+ - 0x1 : STALL
+ - 0x2 : NYET
+ - 0x3 : ACK
+ */
+ unsigned lpm_resp:2;
+ /** Port Sleep Status (SlpSts) (Device and Host)
+ * This bit is set as long as a Sleep condition
+ * is present on the USB bus.
+ */
+ unsigned prt_sleep_sts:1;
+ /** Sleep State Resume OK (L1ResumeOK) (Device and Host)
+ * Indicates that the application or host
+ * can start resume from Sleep state.
+ */
+ unsigned sleep_state_resumeok:1;
+ /** LPM channel Index (LPM_Chnl_Indx) (Host)
+ * The channel number on which the LPM transaction
+ * has to be applied while sending
+ * an LPM transaction to the local device.
+ */
+ unsigned lpm_chan_index:4;
+ /** LPM Retry Count (LPM_Retry_Cnt) (Host)
+ * Number host retries that would be performed
+ * if the device response was not valid response.
+ */
+ unsigned retry_count:3;
+ /** Send LPM Transaction (SndLPM) (Host)
+ * When set by application software,
+ * an LPM transaction containing two tokens
+ * is sent.
+ */
+ unsigned send_lpm:1;
+ /** LPM Retry status (LPM_RetryCnt_Sts) (Host)
+ * Number of LPM Host Retries still remaining
+ * to be transmitted for the current LPM sequence
+ */
+ unsigned retry_count_sts:3;
+ unsigned reserved28_29:2;
+ /** In host mode once this bit is set, the host
+ * configures to drive the HSIC Idle state on the bus.
+ * It then waits for the device to initiate the Connect sequence.
+ * In device mode once this bit is set, the device waits for
+ * the HSIC Idle line state on the bus. Upon receving the Idle
+ * line state, it initiates the HSIC Connect sequence.
+ */
+ unsigned hsic_connect:1;
+ /** This bit overrides and functionally inverts
+ * the if_select_hsic input port signal.
+ */
+ unsigned inv_sel_hsic:1;
+ } b;
+} glpmcfg_data_t;
+
+/**
+ * This union represents the bit fields of the Core ADP Timer, Control and
+ * Status Register (ADPTIMCTLSTS). Set the bits using bit fields then write
+ * the <i>d32</i> value to the register.
+ */
+typedef union adpctl_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Probe Discharge (PRB_DSCHG)
+ * These bits set the times for TADP_DSCHG.
+ * These bits are defined as follows:
+ * 2'b00 - 4 msec
+ * 2'b01 - 8 msec
+ * 2'b10 - 16 msec
+ * 2'b11 - 32 msec
+ */
+ unsigned prb_dschg:2;
+ /** Probe Delta (PRB_DELTA)
+ * These bits set the resolution for RTIM value.
+ * The bits are defined in units of 32 kHz clock cycles as follows:
+ * 2'b00 - 1 cycles
+ * 2'b01 - 2 cycles
+ * 2'b10 - 3 cycles
+ * 2'b11 - 4 cycles
+ * For example if this value is chosen to 2'b01, it means that RTIM
+ * increments for every 3(three) 32Khz clock cycles.
+ */
+ unsigned prb_delta:2;
+ /** Probe Period (PRB_PER)
+ * These bits sets the TADP_PRD as shown in Figure 4 as follows:
+ * 2'b00 - 0.625 to 0.925 sec (typical 0.775 sec)
+ * 2'b01 - 1.25 to 1.85 sec (typical 1.55 sec)
+ * 2'b10 - 1.9 to 2.6 sec (typical 2.275 sec)
+ * 2'b11 - Reserved
+ */
+ unsigned prb_per:2;
+ /** These bits capture the latest time it took for VBUS to ramp from
+ * VADP_SINK to VADP_PRB.
+ * 0x000 - 1 cycles
+ * 0x001 - 2 cycles
+ * 0x002 - 3 cycles
+ * etc
+ * 0x7FF - 2048 cycles
+ * A time of 1024 cycles at 32 kHz corresponds to a time of 32 msec.
+ */
+ unsigned rtim:11;
+ /** Enable Probe (EnaPrb)
+ * When programmed to 1'b1, the core performs a probe operation.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned enaprb:1;
+ /** Enable Sense (EnaSns)
+ * When programmed to 1'b1, the core performs a Sense operation.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned enasns:1;
+ /** ADP Reset (ADPRes)
+ * When set, ADP controller is reset.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned adpres:1;
+ /** ADP Enable (ADPEn)
+ * When set, the core performs either ADP probing or sensing
+ * based on EnaPrb or EnaSns.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned adpen:1;
+ /** ADP Probe Interrupt (ADP_PRB_INT)
+ * When this bit is set, it means that the VBUS
+ * voltage is greater than VADP_PRB or VADP_PRB is reached.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned adp_prb_int:1;
+ /**
+ * ADP Sense Interrupt (ADP_SNS_INT)
+ * When this bit is set, it means that the VBUS voltage is greater than
+ * VADP_SNS value or VADP_SNS is reached.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned adp_sns_int:1;
+ /** ADP Tomeout Interrupt (ADP_TMOUT_INT)
+ * This bit is relevant only for an ADP probe.
+ * When this bit is set, it means that the ramp time has
+ * completed ie ADPCTL.RTIM has reached its terminal value
+ * of 0x7FF. This is a debug feature that allows software
+ * to read the ramp time after each cycle.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned adp_tmout_int:1;
+ /** ADP Probe Interrupt Mask (ADP_PRB_INT_MSK)
+ * When this bit is set, it unmasks the interrupt due to ADP_PRB_INT.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned adp_prb_int_msk:1;
+ /** ADP Sense Interrupt Mask (ADP_SNS_INT_MSK)
+ * When this bit is set, it unmasks the interrupt due to ADP_SNS_INT.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned adp_sns_int_msk:1;
+ /** ADP Timoeout Interrupt Mask (ADP_TMOUT_MSK)
+ * When this bit is set, it unmasks the interrupt due to ADP_TMOUT_INT.
+ * This bit is valid only if OTG_Ver = 1'b1.
+ */
+ unsigned adp_tmout_int_msk:1;
+ /** Access Request
+ * 2'b00 - Read/Write Valid (updated by the core)
+ * 2'b01 - Read
+ * 2'b00 - Write
+ * 2'b00 - Reserved
+ */
+ unsigned ar:2;
+ /** Reserved */
+ unsigned reserved29_31:3;
+ } b;
+} adpctl_data_t;
+
+////////////////////////////////////////////
+// Device Registers
+/**
+ * Device Global Registers. <i>Offsets 800h-BFFh</i>
+ *
+ * The following structures define the size and relative field offsets
+ * for the Device Mode Registers.
+ *
+ * <i>These registers are visible only in Device mode and must not be
+ * accessed in Host mode, as the results are unknown.</i>
+ */
+typedef struct dwc_otg_dev_global_regs {
+ /** Device Configuration Register. <i>Offset 800h</i> */
+ volatile uint32_t dcfg;
+ /** Device Control Register. <i>Offset: 804h</i> */
+ volatile uint32_t dctl;
+ /** Device Status Register (Read Only). <i>Offset: 808h</i> */
+ volatile uint32_t dsts;
+ /** Reserved. <i>Offset: 80Ch</i> */
+ uint32_t unused;
+ /** Device IN Endpoint Common Interrupt Mask
+ * Register. <i>Offset: 810h</i> */
+ volatile uint32_t diepmsk;
+ /** Device OUT Endpoint Common Interrupt Mask
+ * Register. <i>Offset: 814h</i> */
+ volatile uint32_t doepmsk;
+ /** Device All Endpoints Interrupt Register. <i>Offset: 818h</i> */
+ volatile uint32_t daint;
+ /** Device All Endpoints Interrupt Mask Register. <i>Offset:
+ * 81Ch</i> */
+ volatile uint32_t daintmsk;
+ /** Device IN Token Queue Read Register-1 (Read Only).
+ * <i>Offset: 820h</i> */
+ volatile uint32_t dtknqr1;
+ /** Device IN Token Queue Read Register-2 (Read Only).
+ * <i>Offset: 824h</i> */
+ volatile uint32_t dtknqr2;
+ /** Device VBUS discharge Register. <i>Offset: 828h</i> */
+ volatile uint32_t dvbusdis;
+ /** Device VBUS Pulse Register. <i>Offset: 82Ch</i> */
+ volatile uint32_t dvbuspulse;
+ /** Device IN Token Queue Read Register-3 (Read Only). /
+ * Device Thresholding control register (Read/Write)
+ * <i>Offset: 830h</i> */
+ volatile uint32_t dtknqr3_dthrctl;
+ /** Device IN Token Queue Read Register-4 (Read Only). /
+ * Device IN EPs empty Inr. Mask Register (Read/Write)
+ * <i>Offset: 834h</i> */
+ volatile uint32_t dtknqr4_fifoemptymsk;
+ /** Device Each Endpoint Interrupt Register (Read Only). /
+ * <i>Offset: 838h</i> */
+ volatile uint32_t deachint;
+ /** Device Each Endpoint Interrupt mask Register (Read/Write). /
+ * <i>Offset: 83Ch</i> */
+ volatile uint32_t deachintmsk;
+ /** Device Each In Endpoint Interrupt mask Register (Read/Write). /
+ * <i>Offset: 840h</i> */
+ volatile uint32_t diepeachintmsk[MAX_EPS_CHANNELS];
+ /** Device Each Out Endpoint Interrupt mask Register (Read/Write). /
+ * <i>Offset: 880h</i> */
+ volatile uint32_t doepeachintmsk[MAX_EPS_CHANNELS];
+} dwc_otg_device_global_regs_t;
+
+/**
+ * This union represents the bit fields in the Device Configuration
+ * Register. Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements. Write the
+ * <i>d32</i> member to the dcfg register.
+ */
+typedef union dcfg_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Device Speed */
+ unsigned devspd:2;
+ /** Non Zero Length Status OUT Handshake */
+ unsigned nzstsouthshk:1;
+#define DWC_DCFG_SEND_STALL 1
+
+ unsigned ena32khzs:1;
+ /** Device Addresses */
+ unsigned devaddr:7;
+ /** Periodic Frame Interval */
+ unsigned perfrint:2;
+#define DWC_DCFG_FRAME_INTERVAL_80 0
+#define DWC_DCFG_FRAME_INTERVAL_85 1
+#define DWC_DCFG_FRAME_INTERVAL_90 2
+#define DWC_DCFG_FRAME_INTERVAL_95 3
+
+ /** Enable Device OUT NAK for bulk in DDMA mode */
+ unsigned endevoutnak:1;
+
+ unsigned reserved14_17:4;
+ /** In Endpoint Mis-match count */
+ unsigned epmscnt:5;
+ /** Enable Descriptor DMA in Device mode */
+ unsigned descdma:1;
+ unsigned perschintvl:2;
+ unsigned resvalid:6;
+ } b;
+} dcfg_data_t;
+
+/**
+ * This union represents the bit fields in the Device Control
+ * Register. Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union dctl_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Remote Wakeup */
+ unsigned rmtwkupsig:1;
+ /** Soft Disconnect */
+ unsigned sftdiscon:1;
+ /** Global Non-Periodic IN NAK Status */
+ unsigned gnpinnaksts:1;
+ /** Global OUT NAK Status */
+ unsigned goutnaksts:1;
+ /** Test Control */
+ unsigned tstctl:3;
+ /** Set Global Non-Periodic IN NAK */
+ unsigned sgnpinnak:1;
+ /** Clear Global Non-Periodic IN NAK */
+ unsigned cgnpinnak:1;
+ /** Set Global OUT NAK */
+ unsigned sgoutnak:1;
+ /** Clear Global OUT NAK */
+ unsigned cgoutnak:1;
+ /** Power-On Programming Done */
+ unsigned pwronprgdone:1;
+ /** Reserved */
+ unsigned reserved:1;
+ /** Global Multi Count */
+ unsigned gmc:2;
+ /** Ignore Frame Number for ISOC EPs */
+ unsigned ifrmnum:1;
+ /** NAK on Babble */
+ unsigned nakonbble:1;
+ /** Enable Continue on BNA */
+ unsigned encontonbna:1;
+
+ unsigned reserved18_31:14;
+ } b;
+} dctl_data_t;
+
+/**
+ * This union represents the bit fields in the Device Status
+ * Register. Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union dsts_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Suspend Status */
+ unsigned suspsts:1;
+ /** Enumerated Speed */
+ unsigned enumspd:2;
+#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0
+#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1
+#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2
+#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3
+ /** Erratic Error */
+ unsigned errticerr:1;
+ unsigned reserved4_7:4;
+ /** Frame or Microframe Number of the received SOF */
+ unsigned soffn:14;
+ unsigned reserved22_31:10;
+ } b;
+} dsts_data_t;
+
+/**
+ * This union represents the bit fields in the Device IN EP Interrupt
+ * Register and the Device IN EP Common Mask Register.
+ *
+ * - Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union diepint_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Transfer complete mask */
+ unsigned xfercompl:1;
+ /** Endpoint disable mask */
+ unsigned epdisabled:1;
+ /** AHB Error mask */
+ unsigned ahberr:1;
+ /** TimeOUT Handshake mask (non-ISOC EPs) */
+ unsigned timeout:1;
+ /** IN Token received with TxF Empty mask */
+ unsigned intktxfemp:1;
+ /** IN Token Received with EP mismatch mask */
+ unsigned intknepmis:1;
+ /** IN Endpoint NAK Effective mask */
+ unsigned inepnakeff:1;
+ /** Reserved */
+ unsigned emptyintr:1;
+
+ unsigned txfifoundrn:1;
+
+ /** BNA Interrupt mask */
+ unsigned bna:1;
+
+ unsigned reserved10_12:3;
+ /** BNA Interrupt mask */
+ unsigned nak:1;
+
+ unsigned reserved14_31:18;
+ } b;
+} diepint_data_t;
+
+/**
+ * This union represents the bit fields in the Device IN EP
+ * Common/Dedicated Interrupt Mask Register.
+ */
+typedef union diepint_data diepmsk_data_t;
+
+/**
+ * This union represents the bit fields in the Device OUT EP Interrupt
+ * Registerand Device OUT EP Common Interrupt Mask Register.
+ *
+ * - Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union doepint_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Transfer complete */
+ unsigned xfercompl:1;
+ /** Endpoint disable */
+ unsigned epdisabled:1;
+ /** AHB Error */
+ unsigned ahberr:1;
+ /** Setup Phase Done (contorl EPs) */
+ unsigned setup:1;
+ /** OUT Token Received when Endpoint Disabled */
+ unsigned outtknepdis:1;
+
+ unsigned stsphsercvd:1;
+ /** Back-to-Back SETUP Packets Received */
+ unsigned back2backsetup:1;
+
+ unsigned reserved7:1;
+ /** OUT packet Error */
+ unsigned outpkterr:1;
+ /** BNA Interrupt */
+ unsigned bna:1;
+
+ unsigned reserved10:1;
+ /** Packet Drop Status */
+ unsigned pktdrpsts:1;
+ /** Babble Interrupt */
+ unsigned babble:1;
+ /** NAK Interrupt */
+ unsigned nak:1;
+ /** NYET Interrupt */
+ unsigned nyet:1;
+
+ unsigned reserved15_31:17;
+ } b;
+} doepint_data_t;
+
+/**
+ * This union represents the bit fields in the Device OUT EP
+ * Common/Dedicated Interrupt Mask Register.
+ */
+typedef union doepint_data doepmsk_data_t;
+
+/**
+ * This union represents the bit fields in the Device All EP Interrupt
+ * and Mask Registers.
+ * - Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union daint_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** IN Endpoint bits */
+ unsigned in:16;
+ /** OUT Endpoint bits */
+ unsigned out:16;
+ } ep;
+ struct {
+ /** IN Endpoint bits */
+ unsigned inep0:1;
+ unsigned inep1:1;
+ unsigned inep2:1;
+ unsigned inep3:1;
+ unsigned inep4:1;
+ unsigned inep5:1;
+ unsigned inep6:1;
+ unsigned inep7:1;
+ unsigned inep8:1;
+ unsigned inep9:1;
+ unsigned inep10:1;
+ unsigned inep11:1;
+ unsigned inep12:1;
+ unsigned inep13:1;
+ unsigned inep14:1;
+ unsigned inep15:1;
+ /** OUT Endpoint bits */
+ unsigned outep0:1;
+ unsigned outep1:1;
+ unsigned outep2:1;
+ unsigned outep3:1;
+ unsigned outep4:1;
+ unsigned outep5:1;
+ unsigned outep6:1;
+ unsigned outep7:1;
+ unsigned outep8:1;
+ unsigned outep9:1;
+ unsigned outep10:1;
+ unsigned outep11:1;
+ unsigned outep12:1;
+ unsigned outep13:1;
+ unsigned outep14:1;
+ unsigned outep15:1;
+ } b;
+} daint_data_t;
+
+/**
+ * This union represents the bit fields in the Device IN Token Queue
+ * Read Registers.
+ * - Read the register into the <i>d32</i> member.
+ * - READ-ONLY Register
+ */
+typedef union dtknq1_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** In Token Queue Write Pointer */
+ unsigned intknwptr:5;
+ /** Reserved */
+ unsigned reserved05_06:2;
+ /** write pointer has wrapped. */
+ unsigned wrap_bit:1;
+ /** EP Numbers of IN Tokens 0 ... 4 */
+ unsigned epnums0_5:24;
+ } b;
+} dtknq1_data_t;
+
+/**
+ * This union represents Threshold control Register
+ * - Read and write the register into the <i>d32</i> member.
+ * - READ-WRITABLE Register
+ */
+typedef union dthrctl_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** non ISO Tx Thr. Enable */
+ unsigned non_iso_thr_en:1;
+ /** ISO Tx Thr. Enable */
+ unsigned iso_thr_en:1;
+ /** Tx Thr. Length */
+ unsigned tx_thr_len:9;
+ /** AHB Threshold ratio */
+ unsigned ahb_thr_ratio:2;
+ /** Reserved */
+ unsigned reserved13_15:3;
+ /** Rx Thr. Enable */
+ unsigned rx_thr_en:1;
+ /** Rx Thr. Length */
+ unsigned rx_thr_len:9;
+ unsigned reserved26:1;
+ /** Arbiter Parking Enable*/
+ unsigned arbprken:1;
+ /** Reserved */
+ unsigned reserved28_31:4;
+ } b;
+} dthrctl_data_t;
+
+/**
+ * Device Logical IN Endpoint-Specific Registers. <i>Offsets
+ * 900h-AFCh</i>
+ *
+ * There will be one set of endpoint registers per logical endpoint
+ * implemented.
+ *
+ * <i>These registers are visible only in Device mode and must not be
+ * accessed in Host mode, as the results are unknown.</i>
+ */
+typedef struct dwc_otg_dev_in_ep_regs {
+ /** Device IN Endpoint Control Register. <i>Offset:900h +
+ * (ep_num * 20h) + 00h</i> */
+ volatile uint32_t diepctl;
+ /** Reserved. <i>Offset:900h + (ep_num * 20h) + 04h</i> */
+ uint32_t reserved04;
+ /** Device IN Endpoint Interrupt Register. <i>Offset:900h +
+ * (ep_num * 20h) + 08h</i> */
+ volatile uint32_t diepint;
+ /** Reserved. <i>Offset:900h + (ep_num * 20h) + 0Ch</i> */
+ uint32_t reserved0C;
+ /** Device IN Endpoint Transfer Size
+ * Register. <i>Offset:900h + (ep_num * 20h) + 10h</i> */
+ volatile uint32_t dieptsiz;
+ /** Device IN Endpoint DMA Address Register. <i>Offset:900h +
+ * (ep_num * 20h) + 14h</i> */
+ volatile uint32_t diepdma;
+ /** Device IN Endpoint Transmit FIFO Status Register. <i>Offset:900h +
+ * (ep_num * 20h) + 18h</i> */
+ volatile uint32_t dtxfsts;
+ /** Device IN Endpoint DMA Buffer Register. <i>Offset:900h +
+ * (ep_num * 20h) + 1Ch</i> */
+ volatile uint32_t diepdmab;
+} dwc_otg_dev_in_ep_regs_t;
+
+/**
+ * Device Logical OUT Endpoint-Specific Registers. <i>Offsets:
+ * B00h-CFCh</i>
+ *
+ * There will be one set of endpoint registers per logical endpoint
+ * implemented.
+ *
+ * <i>These registers are visible only in Device mode and must not be
+ * accessed in Host mode, as the results are unknown.</i>
+ */
+typedef struct dwc_otg_dev_out_ep_regs {
+ /** Device OUT Endpoint Control Register. <i>Offset:B00h +
+ * (ep_num * 20h) + 00h</i> */
+ volatile uint32_t doepctl;
+ /** Reserved. <i>Offset:B00h + (ep_num * 20h) + 04h</i> */
+ uint32_t reserved04;
+ /** Device OUT Endpoint Interrupt Register. <i>Offset:B00h +
+ * (ep_num * 20h) + 08h</i> */
+ volatile uint32_t doepint;
+ /** Reserved. <i>Offset:B00h + (ep_num * 20h) + 0Ch</i> */
+ uint32_t reserved0C;
+ /** Device OUT Endpoint Transfer Size Register. <i>Offset:
+ * B00h + (ep_num * 20h) + 10h</i> */
+ volatile uint32_t doeptsiz;
+ /** Device OUT Endpoint DMA Address Register. <i>Offset:B00h
+ * + (ep_num * 20h) + 14h</i> */
+ volatile uint32_t doepdma;
+ /** Reserved. <i>Offset:B00h + * (ep_num * 20h) + 18h</i> */
+ uint32_t unused;
+ /** Device OUT Endpoint DMA Buffer Register. <i>Offset:B00h
+ * + (ep_num * 20h) + 1Ch</i> */
+ uint32_t doepdmab;
+} dwc_otg_dev_out_ep_regs_t;
+
+/**
+ * This union represents the bit fields in the Device EP Control
+ * Register. Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union depctl_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Maximum Packet Size
+ * IN/OUT EPn
+ * IN/OUT EP0 - 2 bits
+ * 2'b00: 64 Bytes
+ * 2'b01: 32
+ * 2'b10: 16
+ * 2'b11: 8 */
+ unsigned mps:11;
+#define DWC_DEP0CTL_MPS_64 0
+#define DWC_DEP0CTL_MPS_32 1
+#define DWC_DEP0CTL_MPS_16 2
+#define DWC_DEP0CTL_MPS_8 3
+
+ /** Next Endpoint
+ * IN EPn/IN EP0
+ * OUT EPn/OUT EP0 - reserved */
+ unsigned nextep:4;
+
+ /** USB Active Endpoint */
+ unsigned usbactep:1;
+
+ /** Endpoint DPID (INTR/Bulk IN and OUT endpoints)
+ * This field contains the PID of the packet going to
+ * be received or transmitted on this endpoint. The
+ * application should program the PID of the first
+ * packet going to be received or transmitted on this
+ * endpoint , after the endpoint is
+ * activated. Application use the SetD1PID and
+ * SetD0PID fields of this register to program either
+ * D0 or D1 PID.
+ *
+ * The encoding for this field is
+ * - 0: D0
+ * - 1: D1
+ */
+ unsigned dpid:1;
+
+ /** NAK Status */
+ unsigned naksts:1;
+
+ /** Endpoint Type
+ * 2'b00: Control
+ * 2'b01: Isochronous
+ * 2'b10: Bulk
+ * 2'b11: Interrupt */
+ unsigned eptype:2;
+
+ /** Snoop Mode
+ * OUT EPn/OUT EP0
+ * IN EPn/IN EP0 - reserved */
+ unsigned snp:1;
+
+ /** Stall Handshake */
+ unsigned stall:1;
+
+ /** Tx Fifo Number
+ * IN EPn/IN EP0
+ * OUT EPn/OUT EP0 - reserved */
+ unsigned txfnum:4;
+
+ /** Clear NAK */
+ unsigned cnak:1;
+ /** Set NAK */
+ unsigned snak:1;
+ /** Set DATA0 PID (INTR/Bulk IN and OUT endpoints)
+ * Writing to this field sets the Endpoint DPID (DPID)
+ * field in this register to DATA0. Set Even
+ * (micro)frame (SetEvenFr) (ISO IN and OUT Endpoints)
+ * Writing to this field sets the Even/Odd
+ * (micro)frame (EO_FrNum) field to even (micro)
+ * frame.
+ */
+ unsigned setd0pid:1;
+ /** Set DATA1 PID (INTR/Bulk IN and OUT endpoints)
+ * Writing to this field sets the Endpoint DPID (DPID)
+ * field in this register to DATA1 Set Odd
+ * (micro)frame (SetOddFr) (ISO IN and OUT Endpoints)
+ * Writing to this field sets the Even/Odd
+ * (micro)frame (EO_FrNum) field to odd (micro) frame.
+ */
+ unsigned setd1pid:1;
+
+ /** Endpoint Disable */
+ unsigned epdis:1;
+ /** Endpoint Enable */
+ unsigned epena:1;
+ } b;
+} depctl_data_t;
+
+/**
+ * This union represents the bit fields in the Device EP Transfer
+ * Size Register. Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union deptsiz_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Transfer size */
+ unsigned xfersize:19;
+/** Max packet count for EP (pow(2,10)-1) */
+#define MAX_PKT_CNT 1023
+ /** Packet Count */
+ unsigned pktcnt:10;
+ /** Multi Count - Periodic IN endpoints */
+ unsigned mc:2;
+ unsigned reserved:1;
+ } b;
+} deptsiz_data_t;
+
+/**
+ * This union represents the bit fields in the Device EP 0 Transfer
+ * Size Register. Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union deptsiz0_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Transfer size */
+ unsigned xfersize:7;
+ /** Reserved */
+ unsigned reserved7_18:12;
+ /** Packet Count */
+ unsigned pktcnt:2;
+ /** Reserved */
+ unsigned reserved21_28:8;
+ /**Setup Packet Count (DOEPTSIZ0 Only) */
+ unsigned supcnt:2;
+ unsigned reserved31;
+ } b;
+} deptsiz0_data_t;
+
+/////////////////////////////////////////////////
+// DMA Descriptor Specific Structures
+//
+
+/** Buffer status definitions */
+
+#define BS_HOST_READY 0x0
+#define BS_DMA_BUSY 0x1
+#define BS_DMA_DONE 0x2
+#define BS_HOST_BUSY 0x3
+
+/** Receive/Transmit status definitions */
+
+#define RTS_SUCCESS 0x0
+#define RTS_BUFFLUSH 0x1
+#define RTS_RESERVED 0x2
+#define RTS_BUFERR 0x3
+
+/**
+ * This union represents the bit fields in the DMA Descriptor
+ * status quadlet. Read the quadlet into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it, <i>b_iso_out</i> and
+ * <i>b_iso_in</i> elements.
+ */
+typedef union dev_dma_desc_sts {
+ /** raw register data */
+ uint32_t d32;
+ /** quadlet bits */
+ struct {
+ /** Received number of bytes */
+ unsigned bytes:16;
+ /** NAK bit - only for OUT EPs */
+ unsigned nak:1;
+ unsigned reserved17_22:6;
+ /** Multiple Transfer - only for OUT EPs */
+ unsigned mtrf:1;
+ /** Setup Packet received - only for OUT EPs */
+ unsigned sr:1;
+ /** Interrupt On Complete */
+ unsigned ioc:1;
+ /** Short Packet */
+ unsigned sp:1;
+ /** Last */
+ unsigned l:1;
+ /** Receive Status */
+ unsigned sts:2;
+ /** Buffer Status */
+ unsigned bs:2;
+ } b;
+
+//#ifdef DWC_EN_ISOC
+ /** iso out quadlet bits */
+ struct {
+ /** Received number of bytes */
+ unsigned rxbytes:11;
+
+ unsigned reserved11:1;
+ /** Frame Number */
+ unsigned framenum:11;
+ /** Received ISO Data PID */
+ unsigned pid:2;
+ /** Interrupt On Complete */
+ unsigned ioc:1;
+ /** Short Packet */
+ unsigned sp:1;
+ /** Last */
+ unsigned l:1;
+ /** Receive Status */
+ unsigned rxsts:2;
+ /** Buffer Status */
+ unsigned bs:2;
+ } b_iso_out;
+
+ /** iso in quadlet bits */
+ struct {
+ /** Transmited number of bytes */
+ unsigned txbytes:12;
+ /** Frame Number */
+ unsigned framenum:11;
+ /** Transmited ISO Data PID */
+ unsigned pid:2;
+ /** Interrupt On Complete */
+ unsigned ioc:1;
+ /** Short Packet */
+ unsigned sp:1;
+ /** Last */
+ unsigned l:1;
+ /** Transmit Status */
+ unsigned txsts:2;
+ /** Buffer Status */
+ unsigned bs:2;
+ } b_iso_in;
+//#endif /* DWC_EN_ISOC */
+} dev_dma_desc_sts_t;
+
+/**
+ * DMA Descriptor structure
+ *
+ * DMA Descriptor structure contains two quadlets:
+ * Status quadlet and Data buffer pointer.
+ */
+typedef struct dwc_otg_dev_dma_desc {
+ /** DMA Descriptor status quadlet */
+ dev_dma_desc_sts_t status;
+ /** DMA Descriptor data buffer pointer */
+ uint32_t buf;
+} dwc_otg_dev_dma_desc_t;
+
+/**
+ * The dwc_otg_dev_if structure contains information needed to manage
+ * the DWC_otg controller acting in device mode. It represents the
+ * programming view of the device-specific aspects of the controller.
+ */
+typedef struct dwc_otg_dev_if {
+ /** Pointer to device Global registers.
+ * Device Global Registers starting at offset 800h
+ */
+ dwc_otg_device_global_regs_t *dev_global_regs;
+#define DWC_DEV_GLOBAL_REG_OFFSET 0x800
+
+ /**
+ * Device Logical IN Endpoint-Specific Registers 900h-AFCh
+ */
+ dwc_otg_dev_in_ep_regs_t *in_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_IN_EP_REG_OFFSET 0x900
+#define DWC_EP_REG_OFFSET 0x20
+
+ /** Device Logical OUT Endpoint-Specific Registers B00h-CFCh */
+ dwc_otg_dev_out_ep_regs_t *out_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00
+
+ /* Device configuration information */
+ uint8_t speed; /**< Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */
+ uint8_t num_in_eps; /**< Number # of Tx EP range: 0-15 exept ep0 */
+ uint8_t num_out_eps; /**< Number # of Rx EP range: 0-15 exept ep 0*/
+
+ /** Size of periodic FIFOs (Bytes) */
+ uint16_t perio_tx_fifo_size[MAX_PERIO_FIFOS];
+
+ /** Size of Tx FIFOs (Bytes) */
+ uint16_t tx_fifo_size[MAX_TX_FIFOS];
+
+ /** Thresholding enable flags and length varaiables **/
+ uint16_t rx_thr_en;
+ uint16_t iso_tx_thr_en;
+ uint16_t non_iso_tx_thr_en;
+
+ uint16_t rx_thr_length;
+ uint16_t tx_thr_length;
+
+ /**
+ * Pointers to the DMA Descriptors for EP0 Control
+ * transfers (virtual and physical)
+ */
+
+ /** 2 descriptors for SETUP packets */
+ dwc_dma_t dma_setup_desc_addr[2];
+ dwc_otg_dev_dma_desc_t *setup_desc_addr[2];
+
+ /** Pointer to Descriptor with latest SETUP packet */
+ dwc_otg_dev_dma_desc_t *psetup;
+
+ /** Index of current SETUP handler descriptor */
+ uint32_t setup_desc_index;
+
+ /** Descriptor for Data In or Status In phases */
+ dwc_dma_t dma_in_desc_addr;
+ dwc_otg_dev_dma_desc_t *in_desc_addr;
+
+ /** Descriptor for Data Out or Status Out phases */
+ dwc_dma_t dma_out_desc_addr;
+ dwc_otg_dev_dma_desc_t *out_desc_addr;
+
+ /** Setup Packet Detected - if set clear NAK when queueing */
+ uint32_t spd;
+ /** Isoc ep pointer on which incomplete happens */
+ void *isoc_ep;
+
+} dwc_otg_dev_if_t;
+
+/////////////////////////////////////////////////
+// Host Mode Register Structures
+//
+/**
+ * The Host Global Registers structure defines the size and relative
+ * field offsets for the Host Mode Global Registers. Host Global
+ * Registers offsets 400h-7FFh.
+*/
+typedef struct dwc_otg_host_global_regs {
+ /** Host Configuration Register. <i>Offset: 400h</i> */
+ volatile uint32_t hcfg;
+ /** Host Frame Interval Register. <i>Offset: 404h</i> */
+ volatile uint32_t hfir;
+ /** Host Frame Number / Frame Remaining Register. <i>Offset: 408h</i> */
+ volatile uint32_t hfnum;
+ /** Reserved. <i>Offset: 40Ch</i> */
+ uint32_t reserved40C;
+ /** Host Periodic Transmit FIFO/ Queue Status Register. <i>Offset: 410h</i> */
+ volatile uint32_t hptxsts;
+ /** Host All Channels Interrupt Register. <i>Offset: 414h</i> */
+ volatile uint32_t haint;
+ /** Host All Channels Interrupt Mask Register. <i>Offset: 418h</i> */
+ volatile uint32_t haintmsk;
+ /** Host Frame List Base Address Register . <i>Offset: 41Ch</i> */
+ volatile uint32_t hflbaddr;
+} dwc_otg_host_global_regs_t;
+
+/**
+ * This union represents the bit fields in the Host Configuration Register.
+ * Read the register into the <i>d32</i> member then set/clear the bits using
+ * the <i>b</i>it elements. Write the <i>d32</i> member to the hcfg register.
+ */
+typedef union hcfg_data {
+ /** raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ /** FS/LS Phy Clock Select */
+ unsigned fslspclksel:2;
+#define DWC_HCFG_30_60_MHZ 0
+#define DWC_HCFG_48_MHZ 1
+#define DWC_HCFG_6_MHZ 2
+
+ /** FS/LS Only Support */
+ unsigned fslssupp:1;
+ unsigned reserved3_6:4;
+ /** Enable 32-KHz Suspend Mode */
+ unsigned ena32khzs:1;
+ /** Resume Validation Periiod */
+ unsigned resvalid:8;
+ unsigned reserved16_22:7;
+ /** Enable Scatter/gather DMA in Host mode */
+ unsigned descdma:1;
+ /** Frame List Entries */
+ unsigned frlisten:2;
+ /** Enable Periodic Scheduling */
+ unsigned perschedena:1;
+ unsigned reserved27_30:4;
+ unsigned modechtimen:1;
+ } b;
+} hcfg_data_t;
+
+/**
+ * This union represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+typedef union hfir_data {
+ /** raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ unsigned frint:16;
+ unsigned hfirrldctrl:1;
+ unsigned reserved:15;
+ } b;
+} hfir_data_t;
+
+/**
+ * This union represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+typedef union hfnum_data {
+ /** raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ unsigned frnum:16;
+#define DWC_HFNUM_MAX_FRNUM 0x3FFF
+ unsigned frrem:16;
+ } b;
+} hfnum_data_t;
+
+typedef union hptxsts_data {
+ /** raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ unsigned ptxfspcavail:16;
+ unsigned ptxqspcavail:8;
+ /** Top of the Periodic Transmit Request Queue
+ * - bit 24 - Terminate (last entry for the selected channel)
+ * - bits 26:25 - Token Type
+ * - 2'b00 - Zero length
+ * - 2'b01 - Ping
+ * - 2'b10 - Disable
+ * - bits 30:27 - Channel Number
+ * - bit 31 - Odd/even microframe
+ */
+ unsigned ptxqtop_terminate:1;
+ unsigned ptxqtop_token:2;
+ unsigned ptxqtop_chnum:4;
+ unsigned ptxqtop_odd:1;
+ } b;
+} hptxsts_data_t;
+
+/**
+ * This union represents the bit fields in the Host Port Control and Status
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
+ * hprt0 register.
+ */
+typedef union hprt0_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned prtconnsts:1;
+ unsigned prtconndet:1;
+ unsigned prtena:1;
+ unsigned prtenchng:1;
+ unsigned prtovrcurract:1;
+ unsigned prtovrcurrchng:1;
+ unsigned prtres:1;
+ unsigned prtsusp:1;
+ unsigned prtrst:1;
+ unsigned reserved9:1;
+ unsigned prtlnsts:2;
+ unsigned prtpwr:1;
+ unsigned prttstctl:4;
+ unsigned prtspd:2;
+#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0
+#define DWC_HPRT0_PRTSPD_FULL_SPEED 1
+#define DWC_HPRT0_PRTSPD_LOW_SPEED 2
+ unsigned reserved19_31:13;
+ } b;
+} hprt0_data_t;
+
+/**
+ * This union represents the bit fields in the Host All Interrupt
+ * Register.
+ */
+typedef union haint_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned ch0:1;
+ unsigned ch1:1;
+ unsigned ch2:1;
+ unsigned ch3:1;
+ unsigned ch4:1;
+ unsigned ch5:1;
+ unsigned ch6:1;
+ unsigned ch7:1;
+ unsigned ch8:1;
+ unsigned ch9:1;
+ unsigned ch10:1;
+ unsigned ch11:1;
+ unsigned ch12:1;
+ unsigned ch13:1;
+ unsigned ch14:1;
+ unsigned ch15:1;
+ unsigned reserved:16;
+ } b;
+
+ struct {
+ unsigned chint:16;
+ unsigned reserved:16;
+ } b2;
+} haint_data_t;
+
+/**
+ * This union represents the bit fields in the Host All Interrupt
+ * Register.
+ */
+typedef union haintmsk_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned ch0:1;
+ unsigned ch1:1;
+ unsigned ch2:1;
+ unsigned ch3:1;
+ unsigned ch4:1;
+ unsigned ch5:1;
+ unsigned ch6:1;
+ unsigned ch7:1;
+ unsigned ch8:1;
+ unsigned ch9:1;
+ unsigned ch10:1;
+ unsigned ch11:1;
+ unsigned ch12:1;
+ unsigned ch13:1;
+ unsigned ch14:1;
+ unsigned ch15:1;
+ unsigned reserved:16;
+ } b;
+
+ struct {
+ unsigned chint:16;
+ unsigned reserved:16;
+ } b2;
+} haintmsk_data_t;
+
+/**
+ * Host Channel Specific Registers. <i>500h-5FCh</i>
+ */
+typedef struct dwc_otg_hc_regs {
+ /** Host Channel 0 Characteristic Register. <i>Offset: 500h + (chan_num * 20h) + 00h</i> */
+ volatile uint32_t hcchar;
+ /** Host Channel 0 Split Control Register. <i>Offset: 500h + (chan_num * 20h) + 04h</i> */
+ volatile uint32_t hcsplt;
+ /** Host Channel 0 Interrupt Register. <i>Offset: 500h + (chan_num * 20h) + 08h</i> */
+ volatile uint32_t hcint;
+ /** Host Channel 0 Interrupt Mask Register. <i>Offset: 500h + (chan_num * 20h) + 0Ch</i> */
+ volatile uint32_t hcintmsk;
+ /** Host Channel 0 Transfer Size Register. <i>Offset: 500h + (chan_num * 20h) + 10h</i> */
+ volatile uint32_t hctsiz;
+ /** Host Channel 0 DMA Address Register. <i>Offset: 500h + (chan_num * 20h) + 14h</i> */
+ volatile uint32_t hcdma;
+ volatile uint32_t reserved;
+ /** Host Channel 0 DMA Buffer Address Register. <i>Offset: 500h + (chan_num * 20h) + 1Ch</i> */
+ volatile uint32_t hcdmab;
+} dwc_otg_hc_regs_t;
+
+/**
+ * This union represents the bit fields in the Host Channel Characteristics
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
+ * hcchar register.
+ */
+typedef union hcchar_data {
+ /** raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ /** Maximum packet size in bytes */
+ unsigned mps:11;
+
+ /** Endpoint number */
+ unsigned epnum:4;
+
+ /** 0: OUT, 1: IN */
+ unsigned epdir:1;
+
+ unsigned reserved:1;
+
+ /** 0: Full/high speed device, 1: Low speed device */
+ unsigned lspddev:1;
+
+ /** 0: Control, 1: Isoc, 2: Bulk, 3: Intr */
+ unsigned eptype:2;
+
+ /** Packets per frame for periodic transfers. 0 is reserved. */
+ unsigned multicnt:2;
+
+ /** Device address */
+ unsigned devaddr:7;
+
+ /**
+ * Frame to transmit periodic transaction.
+ * 0: even, 1: odd
+ */
+ unsigned oddfrm:1;
+
+ /** Channel disable */
+ unsigned chdis:1;
+
+ /** Channel enable */
+ unsigned chen:1;
+ } b;
+} hcchar_data_t;
+
+typedef union hcsplt_data {
+ /** raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ /** Port Address */
+ unsigned prtaddr:7;
+
+ /** Hub Address */
+ unsigned hubaddr:7;
+
+ /** Transaction Position */
+ unsigned xactpos:2;
+#define DWC_HCSPLIT_XACTPOS_MID 0
+#define DWC_HCSPLIT_XACTPOS_END 1
+#define DWC_HCSPLIT_XACTPOS_BEGIN 2
+#define DWC_HCSPLIT_XACTPOS_ALL 3
+
+ /** Do Complete Split */
+ unsigned compsplt:1;
+
+ /** Reserved */
+ unsigned reserved:14;
+
+ /** Split Enble */
+ unsigned spltena:1;
+ } b;
+} hcsplt_data_t;
+
+/**
+ * This union represents the bit fields in the Host All Interrupt
+ * Register.
+ */
+typedef union hcint_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** Transfer Complete */
+ unsigned xfercomp:1;
+ /** Channel Halted */
+ unsigned chhltd:1;
+ /** AHB Error */
+ unsigned ahberr:1;
+ /** STALL Response Received */
+ unsigned stall:1;
+ /** NAK Response Received */
+ unsigned nak:1;
+ /** ACK Response Received */
+ unsigned ack:1;
+ /** NYET Response Received */
+ unsigned nyet:1;
+ /** Transaction Err */
+ unsigned xacterr:1;
+ /** Babble Error */
+ unsigned bblerr:1;
+ /** Frame Overrun */
+ unsigned frmovrun:1;
+ /** Data Toggle Error */
+ unsigned datatglerr:1;
+ /** Buffer Not Available (only for DDMA mode) */
+ unsigned bna:1;
+ /** Exessive transaction error (only for DDMA mode) */
+ unsigned xcs_xact:1;
+ /** Frame List Rollover interrupt */
+ unsigned frm_list_roll:1;
+ /** Reserved */
+ unsigned reserved14_31:18;
+ } b;
+} hcint_data_t;
+
+/**
+ * This union represents the bit fields in the Host Channel Interrupt Mask
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
+ * hcintmsk register.
+ */
+typedef union hcintmsk_data {
+ /** raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ unsigned xfercompl:1;
+ unsigned chhltd:1;
+ unsigned ahberr:1;
+ unsigned stall:1;
+ unsigned nak:1;
+ unsigned ack:1;
+ unsigned nyet:1;
+ unsigned xacterr:1;
+ unsigned bblerr:1;
+ unsigned frmovrun:1;
+ unsigned datatglerr:1;
+ unsigned bna:1;
+ unsigned xcs_xact:1;
+ unsigned frm_list_roll:1;
+ unsigned reserved14_31:18;
+ } b;
+} hcintmsk_data_t;
+
+/**
+ * This union represents the bit fields in the Host Channel Transfer Size
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
+ * hcchar register.
+ */
+
+typedef union hctsiz_data {
+ /** raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ /** Total transfer size in bytes */
+ unsigned xfersize:19;
+
+ /** Data packets to transfer */
+ unsigned pktcnt:10;
+
+ /**
+ * Packet ID for next data packet
+ * 0: DATA0
+ * 1: DATA2
+ * 2: DATA1
+ * 3: MDATA (non-Control), SETUP (Control)
+ */
+ unsigned pid:2;
+#define DWC_HCTSIZ_DATA0 0
+#define DWC_HCTSIZ_DATA1 2
+#define DWC_HCTSIZ_DATA2 1
+#define DWC_HCTSIZ_MDATA 3
+#define DWC_HCTSIZ_SETUP 3
+
+ /** Do PING protocol when 1 */
+ unsigned dopng:1;
+ } b;
+
+ /** register bits */
+ struct {
+ /** Scheduling information */
+ unsigned schinfo:8;
+
+ /** Number of transfer descriptors.
+ * Max value:
+ * 64 in general,
+ * 256 only for HS isochronous endpoint.
+ */
+ unsigned ntd:8;
+
+ /** Data packets to transfer */
+ unsigned reserved16_28:13;
+
+ /**
+ * Packet ID for next data packet
+ * 0: DATA0
+ * 1: DATA2
+ * 2: DATA1
+ * 3: MDATA (non-Control)
+ */
+ unsigned pid:2;
+
+ /** Do PING protocol when 1 */
+ unsigned dopng:1;
+ } b_ddma;
+} hctsiz_data_t;
+
+/**
+ * This union represents the bit fields in the Host DMA Address
+ * Register used in Descriptor DMA mode.
+ */
+typedef union hcdma_data {
+ /** raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ unsigned reserved0_2:3;
+ /** Current Transfer Descriptor. Not used for ISOC */
+ unsigned ctd:8;
+ /** Start Address of Descriptor List */
+ unsigned dma_addr:21;
+ } b;
+} hcdma_data_t;
+
+/**
+ * This union represents the bit fields in the DMA Descriptor
+ * status quadlet for host mode. Read the quadlet into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union host_dma_desc_sts {
+ /** raw register data */
+ uint32_t d32;
+ /** quadlet bits */
+
+ /* for non-isochronous */
+ struct {
+ /** Number of bytes */
+ unsigned n_bytes:17;
+ /** QTD offset to jump when Short Packet received - only for IN EPs */
+ unsigned qtd_offset:6;
+ /**
+ * Set to request the core to jump to alternate QTD if
+ * Short Packet received - only for IN EPs
+ */
+ unsigned a_qtd:1;
+ /**
+ * Setup Packet bit. When set indicates that buffer contains
+ * setup packet.
+ */
+ unsigned sup:1;
+ /** Interrupt On Complete */
+ unsigned ioc:1;
+ /** End of List */
+ unsigned eol:1;
+ unsigned reserved27:1;
+ /** Rx/Tx Status */
+ unsigned sts:2;
+#define DMA_DESC_STS_PKTERR 1
+ unsigned reserved30:1;
+ /** Active Bit */
+ unsigned a:1;
+ } b;
+ /* for isochronous */
+ struct {
+ /** Number of bytes */
+ unsigned n_bytes:12;
+ unsigned reserved12_24:13;
+ /** Interrupt On Complete */
+ unsigned ioc:1;
+ unsigned reserved26_27:2;
+ /** Rx/Tx Status */
+ unsigned sts:2;
+ unsigned reserved30:1;
+ /** Active Bit */
+ unsigned a:1;
+ } b_isoc;
+} host_dma_desc_sts_t;
+
+#define MAX_DMA_DESC_SIZE 131071
+#define MAX_DMA_DESC_NUM_GENERIC 64
+#define MAX_DMA_DESC_NUM_HS_ISOC 256
+#define MAX_FRLIST_EN_NUM 64
+/**
+ * Host-mode DMA Descriptor structure
+ *
+ * DMA Descriptor structure contains two quadlets:
+ * Status quadlet and Data buffer pointer.
+ */
+typedef struct dwc_otg_host_dma_desc {
+ /** DMA Descriptor status quadlet */
+ host_dma_desc_sts_t status;
+ /** DMA Descriptor data buffer pointer */
+ uint32_t buf;
+} dwc_otg_host_dma_desc_t;
+
+/** OTG Host Interface Structure.
+ *
+ * The OTG Host Interface Structure structure contains information
+ * needed to manage the DWC_otg controller acting in host mode. It
+ * represents the programming view of the host-specific aspects of the
+ * controller.
+ */
+typedef struct dwc_otg_host_if {
+ /** Host Global Registers starting at offset 400h.*/
+ dwc_otg_host_global_regs_t *host_global_regs;
+#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400
+
+ /** Host Port 0 Control and Status Register */
+ volatile uint32_t *hprt0;
+#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440
+
+ /** Host Channel Specific Registers at offsets 500h-5FCh. */
+ dwc_otg_hc_regs_t *hc_regs[MAX_EPS_CHANNELS];
+#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500
+#define DWC_OTG_CHAN_REGS_OFFSET 0x20
+
+ /* Host configuration information */
+ /** Number of Host Channels (range: 1-16) */
+ uint8_t num_host_channels;
+ /** Periodic EPs supported (0: no, 1: yes) */
+ uint8_t perio_eps_supported;
+ /** Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */
+ uint16_t perio_tx_fifo_size;
+
+} dwc_otg_host_if_t;
+
+/**
+ * This union represents the bit fields in the Power and Clock Gating Control
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union pcgcctl_data {
+ /** raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ /** Stop Pclk */
+ unsigned stoppclk:1;
+ /** Gate Hclk */
+ unsigned gatehclk:1;
+ /** Power Clamp */
+ unsigned pwrclmp:1;
+ /** Reset Power Down Modules */
+ unsigned rstpdwnmodule:1;
+ /** Reserved */
+ unsigned reserved:1;
+ /** Enable Sleep Clock Gating (Enbl_L1Gating) */
+ unsigned enbl_sleep_gating:1;
+ /** PHY In Sleep (PhySleep) */
+ unsigned phy_in_sleep:1;
+ /** Deep Sleep*/
+ unsigned deep_sleep:1;
+ unsigned resetaftsusp:1;
+ unsigned restoremode:1;
+ unsigned reserved10_12:3;
+ unsigned ess_reg_restored:1;
+ unsigned prt_clk_sel:2;
+ unsigned port_power:1;
+ unsigned max_xcvrselect:2;
+ unsigned max_termsel:1;
+ unsigned mac_dev_addr:7;
+ unsigned p2hd_dev_enum_spd:2;
+ unsigned p2hd_prt_spd:2;
+ unsigned if_dev_mode:1;
+ } b;
+} pcgcctl_data_t;
+
+/**
+ * This union represents the bit fields in the Global Data FIFO Software
+ * Configuration Register. Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union gdfifocfg_data {
+ /* raw register data */
+ uint32_t d32;
+ /** register bits */
+ struct {
+ /** OTG Data FIFO depth */
+ unsigned gdfifocfg:16;
+ /** Start address of EP info controller */
+ unsigned epinfobase:16;
+ } b;
+} gdfifocfg_data_t;
+
+/**
+ * This union represents the bit fields in the Global Power Down Register
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union gpwrdn_data {
+ /* raw register data */
+ uint32_t d32;
+
+ /** register bits */
+ struct {
+ /** PMU Interrupt Select */
+ unsigned pmuintsel:1;
+ /** PMU Active */
+ unsigned pmuactv:1;
+ /** Restore */
+ unsigned restore:1;
+ /** Power Down Clamp */
+ unsigned pwrdnclmp:1;
+ /** Power Down Reset */
+ unsigned pwrdnrstn:1;
+ /** Power Down Switch */
+ unsigned pwrdnswtch:1;
+ /** Disable VBUS */
+ unsigned dis_vbus:1;
+ /** Line State Change */
+ unsigned lnstschng:1;
+ /** Line state change mask */
+ unsigned lnstchng_msk:1;
+ /** Reset Detected */
+ unsigned rst_det:1;
+ /** Reset Detect mask */
+ unsigned rst_det_msk:1;
+ /** Disconnect Detected */
+ unsigned disconn_det:1;
+ /** Disconnect Detect mask */
+ unsigned disconn_det_msk:1;
+ /** Connect Detected*/
+ unsigned connect_det:1;
+ /** Connect Detected Mask*/
+ unsigned connect_det_msk:1;
+ /** SRP Detected */
+ unsigned srp_det:1;
+ /** SRP Detect mask */
+ unsigned srp_det_msk:1;
+ /** Status Change Interrupt */
+ unsigned sts_chngint:1;
+ /** Status Change Interrupt Mask */
+ unsigned sts_chngint_msk:1;
+ /** Line State */
+ unsigned linestate:2;
+ /** Indicates current mode(status of IDDIG signal) */
+ unsigned idsts:1;
+ /** B Session Valid signal status*/
+ unsigned bsessvld:1;
+ /** ADP Event Detected */
+ unsigned adp_int:1;
+ /** Multi Valued ID pin */
+ unsigned mult_val_id_bc:5;
+ /** Reserved 24_31 */
+ unsigned reserved29_31:3;
+ } b;
+} gpwrdn_data_t;
+
+#endif
diff --git a/drivers/usb/dwc_otg/linux/dwc_otg_plat.h b/drivers/usb/dwc_otg/linux/dwc_otg_plat.h
new file mode 100644
index 0000000..36e1846
--- /dev/null
+++ b/drivers/usb/dwc_otg/linux/dwc_otg_plat.h
@@ -0,0 +1,268 @@
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/platform/dwc_otg_plat.h $
+ * $Revision: 1.2 $
+ * $Date: 2008-11-21 05:39:16 $
+ * $Change: 1064915 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_OTG_PLAT_H__)
+#define __DWC_OTG_PLAT_H__
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+
+/**
+ * @file
+ *
+ * This file contains the Platform Specific constants, interfaces
+ * (functions and macros) for Linux.
+ *
+ */
+//#if !defined(__LINUX_ARM_ARCH__)
+//#error "The contents of this file is Linux specific!!!"
+//#endif
+
+/**
+ * Reads the content of a register.
+ *
+ * @param reg address of register to read.
+ * @return contents of the register.
+ *
+
+ * Usage:<br>
+ * <code>uint32_t dev_ctl = dwc_read_reg32(&dev_regs->dctl);</code>
+ */
+static __inline__ uint32_t dwc_read_reg32( volatile uint32_t *reg)
+{
+ return readl(reg);
+};
+
+/**
+ * Writes a register with a 32 bit value.
+ *
+ * @param reg address of register to read.
+ * @param value to write to _reg.
+ *
+ * Usage:<br>
+ * <code>dwc_write_reg32(&dev_regs->dctl, 0); </code>
+ */
+static __inline__ void dwc_write_reg32( volatile uint32_t *reg, const uint32_t value)
+{
+ writel( value, reg );
+};
+
+/**
+ * This function modifies bit values in a register. Using the
+ * algorithm: (reg_contents & ~clear_mask) | set_mask.
+ *
+ * @param reg address of register to read.
+ * @param clear_mask bit mask to be cleared.
+ * @param set_mask bit mask to be set.
+ *
+ * Usage:<br>
+ * <code> // Clear the SOF Interrupt Mask bit and <br>
+ * // set the OTG Interrupt mask bit, leaving all others as they were.
+ * dwc_modify_reg32(&dev_regs->gintmsk, DWC_SOF_INT, DWC_OTG_INT);</code>
+ */
+static __inline__
+ void dwc_modify_reg32( volatile uint32_t *reg, const uint32_t clear_mask, const uint32_t set_mask)
+{
+ writel( (readl(reg) & ~clear_mask) | set_mask, reg );
+};
+
+
+/**
+ * Wrapper for the OS micro-second delay function.
+ * @param[in] usecs Microseconds of delay
+ */
+static __inline__ void UDELAY( const uint32_t usecs )
+{
+ udelay( usecs );
+}
+
+/**
+ * Wrapper for the OS milli-second delay function.
+ * @param[in] msecs milliseconds of delay
+ */
+static __inline__ void MDELAY( const uint32_t msecs )
+{
+ mdelay( msecs );
+}
+
+/**
+ * Wrapper for the Linux spin_lock. On the ARM (Integrator)
+ * spin_lock() is a nop.
+ *
+ * @param lock Pointer to the spinlock.
+ */
+static __inline__ void SPIN_LOCK( spinlock_t *lock )
+{
+ spin_lock(lock);
+}
+
+/**
+ * Wrapper for the Linux spin_unlock. On the ARM (Integrator)
+ * spin_lock() is a nop.
+ *
+ * @param lock Pointer to the spinlock.
+ */
+static __inline__ void SPIN_UNLOCK( spinlock_t *lock )
+{
+ spin_unlock(lock);
+}
+
+/**
+ * Wrapper (macro) for the Linux spin_lock_irqsave. On the ARM
+ * (Integrator) spin_lock() is a nop.
+ *
+ * @param l Pointer to the spinlock.
+ * @param f unsigned long for irq flags storage.
+ */
+#define SPIN_LOCK_IRQSAVE( l, f ) spin_lock_irqsave(l,f);
+//#define SPIN_LOCK_IRQSAVE( l, f ) local_irq_save (f);
+
+/**
+ * Wrapper (macro) for the Linux spin_unlock_irqrestore. On the ARM
+ * (Integrator) spin_lock() is a nop.
+ *
+ * @param l Pointer to the spinlock.
+ * @param f unsigned long for irq flags storage.
+ */
+#define SPIN_UNLOCK_IRQRESTORE( l,f ) spin_unlock_irqrestore(l,f);
+//#define SPIN_UNLOCK_IRQRESTORE( l,f ) local_irq_restore (f);
+
+/*
+ * Debugging support vanishes in non-debug builds.
+ */
+
+
+/**
+ * The Debug Level bit-mask variable.
+ */
+extern uint32_t g_dbg_lvl;
+/**
+ * Set the Debug Level variable.
+ */
+static inline uint32_t SET_DEBUG_LEVEL( const uint32_t new )
+{
+ uint32_t old = g_dbg_lvl;
+ g_dbg_lvl = new;
+ return old;
+}
+
+/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */
+#define DBG_CIL (0x2)
+/** When debug level has the DBG_CILV bit set, display CIL Verbose debug
+ * messages */
+#define DBG_CILV (0x20)
+/** When debug level has the DBG_PCD bit set, display PCD (Device) debug
+ * messages */
+#define DBG_PCD (0x4)
+/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug
+ * messages */
+#define DBG_PCDV (0x40)
+/** When debug level has the DBG_HCD bit set, display Host debug messages */
+#define DBG_HCD (0x8)
+/** When debug level has the DBG_HCDV bit set, display Verbose Host debug
+ * messages */
+#define DBG_HCDV (0x80)
+/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host
+ * mode. */
+#define DBG_HCD_URB (0x800)
+
+/** When debug level has any bit set, display debug messages */
+#define DBG_ANY (0xFF)
+
+/** All debug messages off */
+#define DBG_OFF 0
+
+/** Prefix string for DWC_DEBUG print macros. */
+#define USB_DWC "dwc_otg: "
+
+/**
+ * Print a debug message when the Global debug level variable contains
+ * the bit defined in <code>lvl</code>.
+ *
+ * @param[in] lvl - Debug level, use one of the DBG_ constants above.
+ * @param[in] x - like printf
+ *
+ * Example:<p>
+ * <code>
+ * DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr);
+ * </code>
+ * <br>
+ * results in:<br>
+ * <code>
+ * usb-DWC_otg: dwc_otg_cil_init(ca867000)
+ * </code>
+ */
+#ifdef DEBUG
+
+# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)printk( KERN_DEBUG USB_DWC x ); }while(0)
+//# define DWC_DEBUGPL(lvl, x...) printk(x)
+
+# define DWC_DEBUGP(x...) DWC_DEBUGPL(DBG_ANY, x )
+
+# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl)
+//# define CHK_DEBUG_LEVEL(level) 1
+
+#else
+
+# define DWC_DEBUGPL(lvl, x...) do{}while(0)
+# define DWC_DEBUGP(x...)
+
+# define CHK_DEBUG_LEVEL(level) (0)
+
+#endif /*DEBUG*/
+
+/**
+ * Print an Error message.
+ */
+#define DWC_ERROR(x...) printk( KERN_ERR USB_DWC x )
+//#define DWC_ERROR(x...) printk( x )
+
+/**
+ * Print a Warning message.
+ */
+#define DWC_WARN(x...) printk( KERN_WARNING USB_DWC x )
+/**
+ * Print a notice (normal but significant message).
+ */
+#define DWC_NOTICE(x...) printk( KERN_NOTICE USB_DWC x )
+/**
+ * Basic message printing.
+ */
+#define DWC_PRINT(x...) printk( KERN_INFO USB_DWC x )
+
+#endif
+
diff --git a/drivers/usb/dwc_otg/usb.h b/drivers/usb/dwc_otg/usb.h
new file mode 100644
index 0000000..27bda82
--- /dev/null
+++ b/drivers/usb/dwc_otg/usb.h
@@ -0,0 +1,946 @@
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Modified by Synopsys, Inc, 12/12/2007 */
+
+
+#ifndef _USB_H_
+#define _USB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The USB records contain some unaligned little-endian word
+ * components. The U[SG]ETW macros take care of both the alignment
+ * and endian problem and should always be used to access non-byte
+ * values.
+ */
+typedef u_int8_t uByte;
+typedef u_int8_t uWord[2];
+typedef u_int8_t uDWord[4];
+
+#define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h))
+#define UCONSTW(x) { (x) & 0xff, ((x) >> 8) & 0xff }
+#define UCONSTDW(x) { (x) & 0xff, ((x) >> 8) & 0xff, \
+ ((x) >> 16) & 0xff, ((x) >> 24) & 0xff }
+
+#if 1
+#define UGETW(w) ((w)[0] | ((w)[1] << 8))
+#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8))
+#define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24))
+#define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \
+ (w)[1] = (u_int8_t)((v) >> 8), \
+ (w)[2] = (u_int8_t)((v) >> 16), \
+ (w)[3] = (u_int8_t)((v) >> 24))
+#else
+/*
+ * On little-endian machines that can handle unanliged accesses
+ * (e.g. i386) these macros can be replaced by the following.
+ */
+#define UGETW(w) (*(u_int16_t *)(w))
+#define USETW(w,v) (*(u_int16_t *)(w) = (v))
+#define UGETDW(w) (*(u_int32_t *)(w))
+#define USETDW(w,v) (*(u_int32_t *)(w) = (v))
+#endif
+
+/*
+ * Macros for accessing UAS IU fields, which are big-endian
+ */
+#define IUSETW2(w,h,l) ((w)[0] = (u_int8_t)(h), (w)[1] = (u_int8_t)(l))
+#define IUCONSTW(x) { ((x) >> 8) & 0xff, (x) & 0xff }
+#define IUCONSTDW(x) { ((x) >> 24) & 0xff, ((x) >> 16) & 0xff, \
+ ((x) >> 8) & 0xff, (x) & 0xff }
+#define IUGETW(w) (((w)[0] << 8) | (w)[1])
+#define IUSETW(w,v) ((w)[0] = (u_int8_t)((v) >> 8), (w)[1] = (u_int8_t)(v))
+#define IUGETDW(w) (((w)[0] << 24) | ((w)[1] << 16) | ((w)[2] << 8) | (w)[3])
+#define IUSETDW(w,v) ((w)[0] = (u_int8_t)((v) >> 24), \
+ (w)[1] = (u_int8_t)((v) >> 16), \
+ (w)[2] = (u_int8_t)((v) >> 8), \
+ (w)[3] = (u_int8_t)(v))
+
+#define UPACKED __attribute__((__packed__))
+
+typedef struct {
+ uByte bmRequestType;
+ uByte bRequest;
+ uWord wValue;
+ uWord wIndex;
+ uWord wLength;
+} UPACKED usb_device_request_t;
+
+#define UT_GET_DIR(a) ((a) & 0x80)
+#define UT_WRITE 0x00
+#define UT_READ 0x80
+
+#define UT_GET_TYPE(a) ((a) & 0x60)
+#define UT_STANDARD 0x00
+#define UT_CLASS 0x20
+#define UT_VENDOR 0x40
+
+#define UT_GET_RECIPIENT(a) ((a) & 0x1f)
+#define UT_DEVICE 0x00
+#define UT_INTERFACE 0x01
+#define UT_ENDPOINT 0x02
+#define UT_OTHER 0x03
+
+#define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE)
+#define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE)
+#define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT)
+#define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE)
+#define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE)
+#define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT)
+#define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE)
+#define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE)
+#define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER)
+#define UT_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT)
+#define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE)
+#define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE)
+#define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER)
+#define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT)
+#define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE)
+#define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE)
+#define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER)
+#define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT)
+#define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE)
+#define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE)
+#define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER)
+#define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT)
+
+/* Requests */
+#define UR_GET_STATUS 0x00
+#define USTAT_STANDARD_STATUS 0x00
+#define WUSTAT_WUSB_FEATURE 0x01
+#define WUSTAT_CHANNEL_INFO 0x02
+#define WUSTAT_RECEIVED_DATA 0x03
+#define WUSTAT_MAS_AVAILABILITY 0x04
+#define WUSTAT_CURRENT_TRANSMIT_POWER 0x05
+#define UR_CLEAR_FEATURE 0x01
+#define UR_SET_FEATURE 0x03
+#define UR_SET_AND_TEST_FEATURE 0x0c
+#define UR_SET_ADDRESS 0x05
+#define UR_GET_DESCRIPTOR 0x06
+#define UDESC_DEVICE 0x01
+#define UDESC_CONFIG 0x02
+#define UDESC_STRING 0x03
+#define UDESC_INTERFACE 0x04
+#define UDESC_ENDPOINT 0x05
+#define UDESC_SS_USB_COMPANION 0x30
+#define UDESC_DEVICE_QUALIFIER 0x06
+#define UDESC_OTHER_SPEED_CONFIGURATION 0x07
+#define UDESC_INTERFACE_POWER 0x08
+#define UDESC_OTG 0x09
+#define WUDESC_SECURITY 0x0c
+#define WUDESC_KEY 0x0d
+#define WUD_GET_KEY_INDEX(_wValue_) ((_wValue_) & 0xf)
+#define WUD_GET_KEY_TYPE(_wValue_) (((_wValue_) & 0x30) >> 4)
+#define WUD_KEY_TYPE_ASSOC 0x01
+#define WUD_KEY_TYPE_GTK 0x02
+#define WUD_GET_KEY_ORIGIN(_wValue_) (((_wValue_) & 0x40) >> 6)
+#define WUD_KEY_ORIGIN_HOST 0x00
+#define WUD_KEY_ORIGIN_DEVICE 0x01
+#define WUDESC_ENCRYPTION_TYPE 0x0e
+#define WUDESC_BOS 0x0f
+#define WUDESC_DEVICE_CAPABILITY 0x10
+#define WUDESC_WIRELESS_ENDPOINT_COMPANION 0x11
+#define UDESC_BOS 0x0f
+#define UDESC_DEVICE_CAPABILITY 0x10
+#define UDESC_CS_DEVICE 0x21 /* class specific */
+#define UDESC_CS_CONFIG 0x22
+#define UDESC_CS_STRING 0x23
+#define UDESC_CS_INTERFACE 0x24
+#define UDESC_CS_ENDPOINT 0x25
+#define UDESC_HUB 0x29
+#define UR_SET_DESCRIPTOR 0x07
+#define UR_GET_CONFIG 0x08
+#define UR_SET_CONFIG 0x09
+#define UR_GET_INTERFACE 0x0a
+#define UR_SET_INTERFACE 0x0b
+#define UR_SYNCH_FRAME 0x0c
+#define WUR_SET_ENCRYPTION 0x0d
+#define WUR_GET_ENCRYPTION 0x0e
+#define WUR_SET_HANDSHAKE 0x0f
+#define WUR_GET_HANDSHAKE 0x10
+#define WUR_SET_CONNECTION 0x11
+#define WUR_SET_SECURITY_DATA 0x12
+#define WUR_GET_SECURITY_DATA 0x13
+#define WUR_SET_WUSB_DATA 0x14
+#define WUDATA_DRPIE_INFO 0x01
+#define WUDATA_TRANSMIT_DATA 0x02
+#define WUDATA_TRANSMIT_PARAMS 0x03
+#define WUDATA_RECEIVE_PARAMS 0x04
+#define WUDATA_TRANSMIT_POWER 0x05
+#define WUR_LOOPBACK_DATA_WRITE 0x15
+#define WUR_LOOPBACK_DATA_READ 0x16
+#define WUR_SET_INTERFACE_DS 0x17
+
+/* Feature numbers */
+#define UF_ENDPOINT_HALT 0
+#define UF_DEVICE_REMOTE_WAKEUP 1
+#define UF_TEST_MODE 2
+#define UF_DEVICE_B_HNP_ENABLE 3
+#define UF_DEVICE_A_HNP_SUPPORT 4
+#define UF_DEVICE_A_ALT_HNP_SUPPORT 5
+#define WUF_WUSB 3
+#define WUF_TX_DRPIE 0x0
+#define WUF_DEV_XMIT_PACKET 0x1
+#define WUF_COUNT_PACKETS 0x2
+#define WUF_CAPTURE_PACKETS 0x3
+#define UF_FUNCTION_SUSPEND 0
+#define UF_U1_ENABLE 48
+#define UF_U2_ENABLE 49
+#define UF_LTM_ENABLE 50
+
+/* Class requests from the USB 2.0 hub spec, table 11-15 */
+#define UCR_CLEAR_HUB_FEATURE (0x2000 | UR_CLEAR_FEATURE)
+#define UCR_CLEAR_PORT_FEATURE (0x2300 | UR_CLEAR_FEATURE)
+#define UCR_GET_HUB_DESCRIPTOR (0xa000 | UR_GET_DESCRIPTOR)
+#define UCR_GET_HUB_STATUS (0xa000 | UR_GET_STATUS)
+#define UCR_GET_PORT_STATUS (0xa300 | UR_GET_STATUS)
+#define UCR_SET_HUB_FEATURE (0x2000 | UR_SET_FEATURE)
+#define UCR_SET_PORT_FEATURE (0x2300 | UR_SET_FEATURE)
+#define UCR_SET_AND_TEST_PORT_FEATURE (0xa300 | UR_SET_AND_TEST_FEATURE)
+
+#ifdef _MSC_VER
+#include <pshpack1.h>
+#endif
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+} UPACKED usb_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+} UPACKED usb_descriptor_header_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+#define UD_USB_2_0 0x0200
+#define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0)
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize;
+ /* The fields below are not part of the initial descriptor. */
+ uWord idVendor;
+ uWord idProduct;
+ uWord bcdDevice;
+ uByte iManufacturer;
+ uByte iProduct;
+ uByte iSerialNumber;
+ uByte bNumConfigurations;
+} UPACKED usb_device_descriptor_t;
+#define USB_DEVICE_DESCRIPTOR_SIZE 18
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord wTotalLength;
+ uByte bNumInterface;
+ uByte bConfigurationValue;
+ uByte iConfiguration;
+#define UC_ATT_ONE (1 << 7) /* must be set */
+#define UC_ATT_SELFPOWER (1 << 6) /* self powered */
+#define UC_ATT_WAKEUP (1 << 5) /* can wakeup */
+#define UC_ATT_BATTERY (1 << 4) /* battery powered */
+ uByte bmAttributes;
+#define UC_BUS_POWERED 0x80
+#define UC_SELF_POWERED 0x40
+#define UC_REMOTE_WAKEUP 0x20
+ uByte bMaxPower; /* max current in 2 mA units */
+#define UC_POWER_FACTOR 2
+} UPACKED usb_config_descriptor_t;
+#define USB_CONFIG_DESCRIPTOR_SIZE 9
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bInterfaceNumber;
+ uByte bAlternateSetting;
+ uByte bNumEndpoints;
+ uByte bInterfaceClass;
+ uByte bInterfaceSubClass;
+ uByte bInterfaceProtocol;
+ uByte iInterface;
+} UPACKED usb_interface_descriptor_t;
+#define USB_INTERFACE_DESCRIPTOR_SIZE 9
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bEndpointAddress;
+#define UE_GET_DIR(a) ((a) & 0x80)
+#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7))
+#define UE_DIR_IN 0x80
+#define UE_DIR_OUT 0x00
+#define UE_ADDR 0x0f
+#define UE_GET_ADDR(a) ((a) & UE_ADDR)
+ uByte bmAttributes;
+#define UE_XFERTYPE 0x03
+#define UE_CONTROL 0x00
+#define UE_ISOCHRONOUS 0x01
+#define UE_BULK 0x02
+#define UE_INTERRUPT 0x03
+#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE)
+#define UE_ISO_TYPE 0x0c
+#define UE_ISO_ASYNC 0x04
+#define UE_ISO_ADAPT 0x08
+#define UE_ISO_SYNC 0x0c
+#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE)
+ uWord wMaxPacketSize;
+ uByte bInterval;
+} UPACKED usb_endpoint_descriptor_t;
+#define USB_ENDPOINT_DESCRIPTOR_SIZE 7
+
+typedef struct ss_endpoint_companion_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bMaxBurst;
+#define USSE_GET_MAX_STREAMS(a) ((a) & 0x1f)
+#define USSE_SET_MAX_STREAMS(a, b) ((a) | ((b) & 0x1f))
+#define USSE_GET_MAX_PACKET_NUM(a) ((a) & 0x03)
+#define USSE_SET_MAX_PACKET_NUM(a, b) ((a) | ((b) & 0x03))
+ uByte bmAttributes;
+ uWord wBytesPerInterval;
+} UPACKED ss_endpoint_companion_descriptor_t;
+#define USB_SS_ENDPOINT_COMPANION_DESCRIPTOR_SIZE 6
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bString[127];
+} UPACKED usb_string_descriptor_t;
+#define USB_MAX_STRING_LEN 128
+#define USB_LANGUAGE_TABLE 0 /* # of the string language id table */
+
+/* Hub specific request */
+#define UR_GET_BUS_STATE 0x02
+#define UR_CLEAR_TT_BUFFER 0x08
+#define UR_RESET_TT 0x09
+#define UR_GET_TT_STATE 0x0a
+#define UR_STOP_TT 0x0b
+
+/* Hub features */
+#define UHF_C_HUB_LOCAL_POWER 0
+#define UHF_C_HUB_OVER_CURRENT 1
+#define UHF_PORT_CONNECTION 0
+#define UHF_PORT_ENABLE 1
+#define UHF_PORT_SUSPEND 2
+#define UHF_PORT_OVER_CURRENT 3
+#define UHF_PORT_RESET 4
+#define UHF_PORT_L1 5
+#define UHF_PORT_POWER 8
+#define UHF_PORT_LOW_SPEED 9
+#define UHF_PORT_HIGH_SPEED 10
+#define UHF_C_PORT_CONNECTION 16
+#define UHF_C_PORT_ENABLE 17
+#define UHF_C_PORT_SUSPEND 18
+#define UHF_C_PORT_OVER_CURRENT 19
+#define UHF_C_PORT_RESET 20
+#define UHF_C_PORT_L1 23
+#define UHF_PORT_TEST 21
+#define UHF_PORT_INDICATOR 22
+
+typedef struct {
+ uByte bDescLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts;
+ uWord wHubCharacteristics;
+#define UHD_PWR 0x0003
+#define UHD_PWR_GANGED 0x0000
+#define UHD_PWR_INDIVIDUAL 0x0001
+#define UHD_PWR_NO_SWITCH 0x0002
+#define UHD_COMPOUND 0x0004
+#define UHD_OC 0x0018
+#define UHD_OC_GLOBAL 0x0000
+#define UHD_OC_INDIVIDUAL 0x0008
+#define UHD_OC_NONE 0x0010
+#define UHD_TT_THINK 0x0060
+#define UHD_TT_THINK_8 0x0000
+#define UHD_TT_THINK_16 0x0020
+#define UHD_TT_THINK_24 0x0040
+#define UHD_TT_THINK_32 0x0060
+#define UHD_PORT_IND 0x0080
+ uByte bPwrOn2PwrGood; /* delay in 2 ms units */
+#define UHD_PWRON_FACTOR 2
+ uByte bHubContrCurrent;
+ uByte DeviceRemovable[32]; /* max 255 ports */
+#define UHD_NOT_REMOV(desc, i) \
+ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1)
+ /* deprecated */ uByte PortPowerCtrlMask[1];
+} UPACKED usb_hub_descriptor_t;
+#define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize0;
+ uByte bNumConfigurations;
+ uByte bReserved;
+} UPACKED usb_device_qualifier_t;
+#define USB_DEVICE_QUALIFIER_SIZE 10
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bmAttributes;
+#define UOTG_SRP 0x01
+#define UOTG_HNP 0x02
+} UPACKED usb_otg_descriptor_t;
+
+/* OTG feature selectors */
+#define UOTG_B_HNP_ENABLE 3
+#define UOTG_A_HNP_SUPPORT 4
+#define UOTG_A_ALT_HNP_SUPPORT 5
+
+typedef struct {
+ uWord wStatus;
+/* Device status flags */
+#define UDS_SELF_POWERED 0x0001
+#define UDS_REMOTE_WAKEUP 0x0002
+/* Endpoint status flags */
+#define UES_HALT 0x0001
+} UPACKED usb_status_t;
+
+typedef struct {
+ uWord wHubStatus;
+#define UHS_LOCAL_POWER 0x0001
+#define UHS_OVER_CURRENT 0x0002
+ uWord wHubChange;
+} UPACKED usb_hub_status_t;
+
+typedef struct {
+ uWord wPortStatus;
+#define UPS_CURRENT_CONNECT_STATUS 0x0001
+#define UPS_PORT_ENABLED 0x0002
+#define UPS_SUSPEND 0x0004
+#define UPS_OVERCURRENT_INDICATOR 0x0008
+#define UPS_RESET 0x0010
+#define UPS_PORT_POWER 0x0100
+#define UPS_LOW_SPEED 0x0200
+#define UPS_HIGH_SPEED 0x0400
+#define UPS_PORT_TEST 0x0800
+#define UPS_PORT_INDICATOR 0x1000
+ uWord wPortChange;
+#define UPS_C_CONNECT_STATUS 0x0001
+#define UPS_C_PORT_ENABLED 0x0002
+#define UPS_C_SUSPEND 0x0004
+#define UPS_C_OVERCURRENT_INDICATOR 0x0008
+#define UPS_C_PORT_RESET 0x0010
+} UPACKED usb_port_status_t;
+
+#ifdef _MSC_VER
+#include <poppack.h>
+#endif
+
+/* Device class codes */
+#define UDCLASS_IN_INTERFACE 0x00
+#define UDCLASS_COMM 0x02
+#define UDCLASS_HUB 0x09
+#define UDSUBCLASS_HUB 0x00
+#define UDPROTO_FSHUB 0x00
+#define UDPROTO_HSHUBSTT 0x01
+#define UDPROTO_HSHUBMTT 0x02
+#define UDCLASS_DIAGNOSTIC 0xdc
+#define UDCLASS_WIRELESS 0xe0
+#define UDSUBCLASS_RF 0x01
+#define UDPROTO_BLUETOOTH 0x01
+#define UDCLASS_VENDOR 0xff
+
+/* Interface class codes */
+#define UICLASS_UNSPEC 0x00
+
+#define UICLASS_AUDIO 0x01
+#define UISUBCLASS_AUDIOCONTROL 1
+#define UISUBCLASS_AUDIOSTREAM 2
+#define UISUBCLASS_MIDISTREAM 3
+
+#define UICLASS_CDC 0x02 /* communication */
+#define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1
+#define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2
+#define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3
+#define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4
+#define UISUBCLASS_CAPI_CONTROLMODEL 5
+#define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6
+#define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7
+#define UIPROTO_CDC_AT 1
+
+#define UICLASS_HID 0x03
+#define UISUBCLASS_BOOT 1
+#define UIPROTO_BOOT_KEYBOARD 1
+
+#define UICLASS_PHYSICAL 0x05
+
+#define UICLASS_IMAGE 0x06
+
+#define UICLASS_PRINTER 0x07
+#define UISUBCLASS_PRINTER 1
+#define UIPROTO_PRINTER_UNI 1
+#define UIPROTO_PRINTER_BI 2
+#define UIPROTO_PRINTER_1284 3
+
+#define UICLASS_MASS 0x08
+#define UISUBCLASS_RBC 1
+#define UISUBCLASS_SFF8020I 2
+#define UISUBCLASS_QIC157 3
+#define UISUBCLASS_UFI 4
+#define UISUBCLASS_SFF8070I 5
+#define UISUBCLASS_SCSI 6
+#define UIPROTO_MASS_CBI_I 0
+#define UIPROTO_MASS_CBI 1
+#define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */
+#define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */
+
+#define UICLASS_HUB 0x09
+#define UISUBCLASS_HUB 0
+#define UIPROTO_FSHUB 0
+#define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */
+#define UIPROTO_HSHUBMTT 1
+
+#define UICLASS_CDC_DATA 0x0a
+#define UISUBCLASS_DATA 0
+#define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */
+#define UIPROTO_DATA_HDLC 0x31 /* HDLC */
+#define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */
+#define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */
+#define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */
+#define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */
+#define UIPROTO_DATA_V42BIS 0x90 /* Data compression */
+#define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */
+#define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */
+#define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */
+#define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */
+#define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc.*/
+#define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */
+
+#define UICLASS_SMARTCARD 0x0b
+
+/*#define UICLASS_FIRM_UPD 0x0c*/
+
+#define UICLASS_SECURITY 0x0d
+
+#define UICLASS_DIAGNOSTIC 0xdc
+
+#define UICLASS_WIRELESS 0xe0
+#define UISUBCLASS_RF 0x01
+#define UIPROTO_BLUETOOTH 0x01
+
+#define UICLASS_APPL_SPEC 0xfe
+#define UISUBCLASS_FIRMWARE_DOWNLOAD 1
+#define UISUBCLASS_IRDA 2
+#define UIPROTO_IRDA 0
+
+#define UICLASS_VENDOR 0xff
+
+#define USB_HUB_MAX_DEPTH 5
+
+/*
+ * Minimum time a device needs to be powered down to go through
+ * a power cycle. XXX Are these time in the spec?
+ */
+#define USB_POWER_DOWN_TIME 200 /* ms */
+#define USB_PORT_POWER_DOWN_TIME 100 /* ms */
+
+#if 0
+/* These are the values from the spec. */
+#define USB_PORT_RESET_DELAY 10 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY 50 /* ms */
+#define USB_PORT_RESET_RECOVERY 10 /* ms */
+#define USB_PORT_POWERUP_DELAY 100 /* ms */
+#define USB_SET_ADDRESS_SETTLE 2 /* ms */
+#define USB_RESUME_DELAY (20*5) /* ms */
+#define USB_RESUME_WAIT 10 /* ms */
+#define USB_RESUME_RECOVERY 10 /* ms */
+#define USB_EXTRA_POWER_UP_TIME 0 /* ms */
+#else
+/* Allow for marginal (i.e. non-conforming) devices. */
+#define USB_PORT_RESET_DELAY 50 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */
+#define USB_PORT_RESET_RECOVERY 250 /* ms */
+#define USB_PORT_POWERUP_DELAY 300 /* ms */
+#define USB_SET_ADDRESS_SETTLE 10 /* ms */
+#define USB_RESUME_DELAY (50*5) /* ms */
+#define USB_RESUME_WAIT 50 /* ms */
+#define USB_RESUME_RECOVERY 50 /* ms */
+#define USB_EXTRA_POWER_UP_TIME 20 /* ms */
+#endif
+
+#define USB_MIN_POWER 100 /* mA */
+#define USB_MAX_POWER 500 /* mA */
+
+#define USB_BUS_RESET_DELAY 100 /* ms XXX?*/
+
+#define USB_UNCONFIG_NO 0
+#define USB_UNCONFIG_INDEX (-1)
+
+/*** ioctl() related stuff ***/
+
+struct usb_ctl_request {
+ int ucr_addr;
+ usb_device_request_t ucr_request;
+ void *ucr_data;
+ int ucr_flags;
+#define USBD_SHORT_XFER_OK 0x04 /* allow short reads */
+ int ucr_actlen; /* actual length transferred */
+};
+
+struct usb_alt_interface {
+ int uai_config_index;
+ int uai_interface_index;
+ int uai_alt_no;
+};
+
+#define USB_CURRENT_CONFIG_INDEX (-1)
+#define USB_CURRENT_ALT_INDEX (-1)
+
+struct usb_config_desc {
+ int ucd_config_index;
+ usb_config_descriptor_t ucd_desc;
+};
+
+struct usb_interface_desc {
+ int uid_config_index;
+ int uid_interface_index;
+ int uid_alt_index;
+ usb_interface_descriptor_t uid_desc;
+};
+
+struct usb_endpoint_desc {
+ int ued_config_index;
+ int ued_interface_index;
+ int ued_alt_index;
+ int ued_endpoint_index;
+ usb_endpoint_descriptor_t ued_desc;
+};
+
+struct usb_full_desc {
+ int ufd_config_index;
+ u_int ufd_size;
+ u_char *ufd_data;
+};
+
+struct usb_string_desc {
+ int usd_string_index;
+ int usd_language_id;
+ usb_string_descriptor_t usd_desc;
+};
+
+struct usb_ctl_report_desc {
+ int ucrd_size;
+ u_char ucrd_data[1024]; /* filled data size will vary */
+};
+
+typedef struct { u_int32_t cookie; } usb_event_cookie_t;
+
+#define USB_MAX_DEVNAMES 4
+#define USB_MAX_DEVNAMELEN 16
+struct usb_device_info {
+ u_int8_t udi_bus;
+ u_int8_t udi_addr; /* device address */
+ usb_event_cookie_t udi_cookie;
+ char udi_product[USB_MAX_STRING_LEN];
+ char udi_vendor[USB_MAX_STRING_LEN];
+ char udi_release[8];
+ u_int16_t udi_productNo;
+ u_int16_t udi_vendorNo;
+ u_int16_t udi_releaseNo;
+ u_int8_t udi_class;
+ u_int8_t udi_subclass;
+ u_int8_t udi_protocol;
+ u_int8_t udi_config;
+ u_int8_t udi_speed;
+#define USB_SPEED_UNKNOWN 0
+#define USB_SPEED_LOW 1
+#define USB_SPEED_FULL 2
+#define USB_SPEED_HIGH 3
+#define USB_SPEED_VARIABLE 4
+#define USB_SPEED_SUPER 5
+ int udi_power; /* power consumption in mA, 0 if selfpowered */
+ int udi_nports;
+ char udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN];
+ u_int8_t udi_ports[16];/* hub only: addresses of devices on ports */
+#define USB_PORT_ENABLED 0xff
+#define USB_PORT_SUSPENDED 0xfe
+#define USB_PORT_POWERED 0xfd
+#define USB_PORT_DISABLED 0xfc
+};
+
+struct usb_ctl_report {
+ int ucr_report;
+ u_char ucr_data[1024]; /* filled data size will vary */
+};
+
+struct usb_device_stats {
+ u_long uds_requests[4]; /* indexed by transfer type UE_* */
+};
+
+#define WUSB_MIN_IE 0x80
+#define WUSB_WCTA_IE 0x80
+#define WUSB_WCONNECTACK_IE 0x81
+#define WUSB_WHOSTINFO_IE 0x82
+#define WUHI_GET_CA(_bmAttributes_) ((_bmAttributes_) & 0x3)
+#define WUHI_CA_RECONN 0x00
+#define WUHI_CA_LIMITED 0x01
+#define WUHI_CA_ALL 0x03
+#define WUHI_GET_MLSI(_bmAttributes_) (((_bmAttributes_) & 0x38) >> 3)
+#define WUSB_WCHCHANGEANNOUNCE_IE 0x83
+#define WUSB_WDEV_DISCONNECT_IE 0x84
+#define WUSB_WHOST_DISCONNECT_IE 0x85
+#define WUSB_WRELEASE_CHANNEL_IE 0x86
+#define WUSB_WWORK_IE 0x87
+#define WUSB_WCHANNEL_STOP_IE 0x88
+#define WUSB_WDEV_KEEPALIVE_IE 0x89
+#define WUSB_WISOCH_DISCARD_IE 0x8A
+#define WUSB_WRESETDEVICE_IE 0x8B
+#define WUSB_WXMIT_PACKET_ADJUST_IE 0x8C
+#define WUSB_MAX_IE 0x8C
+
+/* Device Notification Types */
+
+#define WUSB_DN_MIN 0x01
+#define WUSB_DN_CONNECT 0x01
+# define WUSB_DA_OLDCONN 0x00
+# define WUSB_DA_NEWCONN 0x01
+# define WUSB_DA_SELF_BEACON 0x02
+# define WUSB_DA_DIR_BEACON 0x04
+# define WUSB_DA_NO_BEACON 0x06
+#define WUSB_DN_DISCONNECT 0x02
+#define WUSB_DN_EPRDY 0x03
+#define WUSB_DN_MASAVAILCHANGED 0x04
+#define WUSB_DN_REMOTEWAKEUP 0x05
+#define WUSB_DN_SLEEP 0x06
+#define WUSB_DN_ALIVE 0x07
+#define WUSB_DN_MAX 0x07
+
+#ifdef _MSC_VER
+#include <pshpack1.h>
+#endif
+
+/* WUSB Handshake Data. Used during the SET/GET HANDSHAKE requests */
+typedef struct wusb_hndshk_data {
+ uByte bMessageNumber;
+ uByte bStatus;
+ uByte tTKID[3];
+ uByte bReserved;
+ uByte CDID[16];
+ uByte Nonce[16];
+ uByte MIC[8];
+} UPACKED wusb_hndshk_data_t;
+#define WUSB_HANDSHAKE_LEN_FOR_MIC 38
+
+/* WUSB Connection Context */
+typedef struct wusb_conn_context {
+ uByte CHID [16];
+ uByte CDID [16];
+ uByte CK [16];
+} UPACKED wusb_conn_context_t;
+
+/* WUSB Security Descriptor */
+typedef struct wusb_security_desc {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord wTotalLength;
+ uByte bNumEncryptionTypes;
+} UPACKED wusb_security_desc_t;
+
+/* WUSB Encryption Type Descriptor */
+typedef struct wusb_encrypt_type_desc {
+ uByte bLength;
+ uByte bDescriptorType;
+
+ uByte bEncryptionType;
+#define WUETD_UNSECURE 0
+#define WUETD_WIRED 1
+#define WUETD_CCM_1 2
+#define WUETD_RSA_1 3
+
+ uByte bEncryptionValue;
+ uByte bAuthKeyIndex;
+} UPACKED wusb_encrypt_type_desc_t;
+
+/* WUSB Key Descriptor */
+typedef struct wusb_key_desc {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte tTKID[3];
+ uByte bReserved;
+ uByte KeyData[1]; /* variable length */
+} UPACKED wusb_key_desc_t;
+
+/* WUSB BOS Descriptor (Binary device Object Store) */
+typedef struct wusb_bos_desc {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord wTotalLength;
+ uByte bNumDeviceCaps;
+} UPACKED wusb_bos_desc_t;
+
+#define USB_DEVICE_CAPABILITY_20_EXTENSION 0x02
+typedef struct usb_dev_cap_20_ext_desc {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+#define USB_20_EXT_LPM 0x02
+ uDWord bmAttributes;
+} UPACKED usb_dev_cap_20_ext_desc_t;
+
+#define USB_DEVICE_CAPABILITY_SS_USB 0x03
+typedef struct usb_dev_cap_ss_usb {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+#define USB_DC_SS_USB_LTM_CAPABLE 0x02
+ uByte bmAttributes;
+#define USB_DC_SS_USB_SPEED_SUPPORT_LOW 0x01
+#define USB_DC_SS_USB_SPEED_SUPPORT_FULL 0x02
+#define USB_DC_SS_USB_SPEED_SUPPORT_HIGH 0x04
+#define USB_DC_SS_USB_SPEED_SUPPORT_SS 0x08
+ uWord wSpeedsSupported;
+ uByte bFunctionalitySupport;
+ uByte bU1DevExitLat;
+ uWord wU2DevExitLat;
+} UPACKED usb_dev_cap_ss_usb_t;
+
+#define USB_DEVICE_CAPABILITY_CONTAINER_ID 0x04
+typedef struct usb_dev_cap_container_id {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+ uByte bReserved;
+ uByte containerID[16];
+} UPACKED usb_dev_cap_container_id_t;
+
+/* Device Capability Type Codes */
+#define WUSB_DEVICE_CAPABILITY_WIRELESS_USB 0x01
+
+/* Device Capability Descriptor */
+typedef struct wusb_dev_cap_desc {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+ uByte caps[1]; /* Variable length */
+} UPACKED wusb_dev_cap_desc_t;
+
+/* Device Capability Descriptor */
+typedef struct wusb_dev_cap_uwb_desc {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+ uByte bmAttributes;
+ uWord wPHYRates; /* Bitmap */
+ uByte bmTFITXPowerInfo;
+ uByte bmFFITXPowerInfo;
+ uWord bmBandGroup;
+ uByte bReserved;
+} UPACKED wusb_dev_cap_uwb_desc_t;
+
+/* Wireless USB Endpoint Companion Descriptor */
+typedef struct wusb_endpoint_companion_desc {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bMaxBurst;
+ uByte bMaxSequence;
+ uWord wMaxStreamDelay;
+ uWord wOverTheAirPacketSize;
+ uByte bOverTheAirInterval;
+ uByte bmCompAttributes;
+} UPACKED wusb_endpoint_companion_desc_t;
+
+/* Wireless USB Numeric Association M1 Data Structure */
+typedef struct wusb_m1_data {
+ uByte version;
+ uWord langId;
+ uByte deviceFriendlyNameLength;
+ uByte sha_256_m3[32];
+ uByte deviceFriendlyName[256];
+} UPACKED wusb_m1_data_t;
+
+typedef struct wusb_m2_data {
+ uByte version;
+ uWord langId;
+ uByte hostFriendlyNameLength;
+ uByte pkh[384];
+ uByte hostFriendlyName[256];
+} UPACKED wusb_m2_data_t;
+
+typedef struct wusb_m3_data {
+ uByte pkd[384];
+ uByte nd;
+} UPACKED wusb_m3_data_t;
+
+typedef struct wusb_m4_data {
+ uDWord _attributeTypeIdAndLength_1;
+ uWord associationTypeId;
+
+ uDWord _attributeTypeIdAndLength_2;
+ uWord associationSubTypeId;
+
+ uDWord _attributeTypeIdAndLength_3;
+ uDWord length;
+
+ uDWord _attributeTypeIdAndLength_4;
+ uDWord associationStatus;
+
+ uDWord _attributeTypeIdAndLength_5;
+ uByte chid[16];
+
+ uDWord _attributeTypeIdAndLength_6;
+ uByte cdid[16];
+
+ uDWord _attributeTypeIdAndLength_7;
+ uByte bandGroups[2];
+} UPACKED wusb_m4_data_t;
+
+#ifdef _MSC_VER
+#include <poppack.h>
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _USB_H_ */
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 060e0e2..5d2d92d 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -27,6 +27,11 @@
To compile this driver as a module, choose M here: the
module will be called xhci-hcd.
+config USB_XHCI_PLATFORM
+ tristate
+ depends on USB_XHCI_HCD
+ default y
+
config USB_XHCI_HCD_DEBUGGING
bool "Debugging for the xHCI host controller"
depends on USB_XHCI_HCD
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 7ca290f..383fc37 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -15,6 +15,10 @@
xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
xhci-hcd-$(CONFIG_PCI) += xhci-pci.o
+ifneq ($(CONFIG_USB_XHCI_PLATFORM), )
+ xhci-hcd-y += xhci-plat.o xhci-comcerto2000.o
+endif
+
obj-$(CONFIG_USB_WHCI_HCD) += whci/
obj-$(CONFIG_PCI) += pci-quirks.o
diff --git a/drivers/usb/host/xhci-comcerto2000.c b/drivers/usb/host/xhci-comcerto2000.c
new file mode 100644
index 0000000..de92ccc
--- /dev/null
+++ b/drivers/usb/host/xhci-comcerto2000.c
@@ -0,0 +1,203 @@
+/*
+ * xhci-comcerto.c - Comcerto-2000 Platform specific routienes.
+ *
+ * Author: Makarand Pawagi
+ */
+
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "xhci.h"
+
+#include <linux/clk.h>
+#include <mach/reset.h>
+#include <mach/hardware.h>
+
+extern int usb3_clk_internal;
+/* USB 3.0 clock */
+static struct clk *usb3_clk;
+
+#ifdef CONFIG_PM
+
+u32 usb3_suspended = 0;
+
+int comcerto_xhci_bus_suspend(struct usb_hcd *hcd)
+{
+ int error_status = 0;
+ int val;
+
+ if (usb3_suspended) {
+ pr_err("comcerto_xhci_bus_suspend: USB 3.0 Already Suspended \n");
+ return error_status;
+ }
+
+ error_status = xhci_bus_suspend(hcd);
+
+ if (!error_status) {
+
+ /* APPLYING THE RESET TO USB3 UTMI */
+ c2000_block_reset(COMPONENT_UTMI_USB1, 1);
+
+ /* APPLYING THE RESET TO USB3 PHY */
+ c2000_block_reset(COMPONENT_USB1_PHY, 1);
+
+ /* Disable the Clock */
+ clk_disable(usb3_clk);
+
+ usb3_suspended = 1;
+ }
+
+ return error_status;
+}
+
+int comcerto_xhci_bus_resume(struct usb_hcd *hcd)
+{
+ int val;
+ int error_status = 0;
+
+ if (!usb3_suspended) {
+ pr_err("comcerto_xhci_bus_resume: USB 3.0 Already in Resume state \n");
+ return error_status;
+ }
+
+ /* Enable the Clock */
+ if (clk_enable(usb3_clk)){
+ pr_err("comcerto_start_xhc:Unable to enable the usb1 clock \n");
+ }
+
+ /* Bring usb3 PHY out of reset */
+ c2000_block_reset(COMPONENT_USB1_PHY, 0);
+
+ for (val = 0 ; val < 50 ; val++)
+ udelay(1000);
+
+ /* Bring usb3 UTMI out of reset */
+ c2000_block_reset(COMPONENT_UTMI_USB1, 0);
+
+ for (val = 0 ; val < 50 ; val++)
+ udelay(1000);
+
+ error_status = xhci_bus_resume(hcd);
+
+ if (error_status) {
+ /* xhci_bus_resume is not successfull keep USB3 in suspend mode */
+
+ /* Put USB3 PHY, UTMI and Controller in Reset */
+
+ /* APPLYING THE RESET TO USB3 UTMI */
+ c2000_block_reset(COMPONENT_UTMI_USB1, 1);
+
+ /* APPLYING THE RESET TO USB3 PHY */
+ c2000_block_reset(COMPONENT_USB1_PHY, 1);
+
+ /* Disable the Clock */
+ clk_disable(usb3_clk);
+
+ return error_status;
+ }
+
+ usb3_suspended = 0;
+
+ return error_status;
+}
+#endif
+
+static void comcerto_usb3_phy_init(void)
+{
+ u32 val;
+
+ writel(0x00E00080, USB3_PHY_BASE + 0x10);
+
+ //Configuration for internal clock
+ if(usb3_clk_internal)
+ {
+ printk(KERN_INFO "USB3.0 clock selected: internal\n", __func__);
+
+ if(HAL_get_ref_clk() == REF_CLK_24MHZ)
+ val = 0x420E82A8;
+ else
+ val = 0x420E82A9;
+ }
+ else
+ {
+ val = 0x4209927A;
+ printk(KERN_INFO "USB3.0 clock selected: external\n", __func__);
+ }
+
+ writel(val, USB3_PHY_BASE + 0x20);
+ writel(0x69C34F53, USB3_PHY_BASE + 0x24);
+ writel(0x0005D815, USB3_PHY_BASE + 0x28);
+ writel(0x00000801, USB3_PHY_BASE + 0x2C);
+}
+
+
+void comcerto_start_xhci(void)
+{
+ u32 val;
+
+ printk(KERN_INFO "### %s\n", __func__);
+
+#if defined(CONFIG_C2K_MFCN_EVM)
+ printk("%s: Reseting usb3...\n", __func__);
+ GPIO_reset_external_device(COMPONENT_USB_HUB, 0);
+#endif
+ /* Enable the USB 3.0 controller clock */
+ /* Get the usb3 clock structure */
+ usb3_clk = clk_get(NULL,"usb1");
+
+ /* Enable the Clock */
+ if (clk_enable(usb3_clk)){
+ pr_err("comcerto_start_xhci:Unable to enable the usb1 clock \n");
+ }
+
+ /* APPLYING THE RESET TO USB3 UTMI */
+ c2000_block_reset(COMPONENT_UTMI_USB1, 1);
+
+ /* APPLYING THE RESET TO USB3 PHY */
+ c2000_block_reset(COMPONENT_USB1_PHY, 1);
+
+ /* APLLYING RESET TO USB3 AXI RESET */
+ c2000_block_reset(COMPONENT_AXI_USB1, 1);
+
+ comcerto_usb3_phy_init();
+
+
+ /* Bring usb3 PHY out of reset */
+ c2000_block_reset(COMPONENT_USB1_PHY, 0);
+
+ udelay(1000);
+
+ /* Bring usb3 UTMI out of reset */
+ c2000_block_reset(COMPONENT_UTMI_USB1, 0);
+
+ udelay(1000);
+
+ /* Bring usb3 Controller out of reset */
+ c2000_block_reset(COMPONENT_AXI_USB1, 0);
+
+ udelay(1000);
+}
+
+void comcerto_stop_xhci(void)
+{
+ u32 val;
+
+ printk(KERN_INFO "### %s\n", __func__);
+
+ /* APPLYING THE RESET TO USB3 UTMI */
+ c2000_block_reset(COMPONENT_UTMI_USB1, 1);
+
+ /* APPLYING THE RESET TO USB3 PHY */
+ c2000_block_reset(COMPONENT_USB1_PHY, 1);
+
+ /* APLLYING RESET TO USB3 AXI RESET */
+ c2000_block_reset(COMPONENT_AXI_USB1, 1);
+
+ /* Disable the Clock */
+ clk_disable(usb3_clk);
+
+ /* Release the clock */
+ clk_put(usb3_clk);
+}
+
diff --git a/drivers/usb/host/xhci-comcerto2000.h b/drivers/usb/host/xhci-comcerto2000.h
new file mode 100644
index 0000000..02b36e0
--- /dev/null
+++ b/drivers/usb/host/xhci-comcerto2000.h
@@ -0,0 +1,24 @@
+/*
+ * xhci-comcerto.h - Comcerto-2000 Platform specific routienes.
+ *
+ * Author: Makarand Pawagi
+ */
+
+
+#ifndef __XHCI_COMCERTO_2000_H__
+#define __XHCI_COMCERTO_2000_H__
+
+
+
+/* Exported Functions */
+
+
+extern void comcerto_start_xhci(void);
+extern void comcerto_stop_xhci(void);
+
+extern int comcerto_xhci_bus_resume(struct usb_hcd *hcd);
+extern int comcerto_xhci_bus_suspend(struct usb_hcd *hcd);
+
+
+
+#endif /* __XHCI_COMCERTO_2000_H__ */
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index e9b0f04..4b436f5 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -119,7 +119,7 @@
xhci_dbg(xhci, " Event Interrupts %s\n",
(temp & CMD_EIE) ? "enabled " : "disabled");
xhci_dbg(xhci, " Host System Error Interrupts %s\n",
- (temp & CMD_EIE) ? "enabled " : "disabled");
+ (temp & CMD_HSEIE) ? "enabled " : "disabled");
xhci_dbg(xhci, " HC has %sfinished light reset\n",
(temp & CMD_LRESET) ? "not " : "");
}
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index fd8a2c2..78e98a6 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -57,10 +57,8 @@
desc->bHubContrCurrent = 0;
desc->bNbrPorts = ports;
- /* Ugh, these should be #defines, FIXME */
- /* Using table 11-13 in USB 2.0 spec. */
temp = 0;
- /* Bits 1:0 - support port power switching, or power always on */
+ /* Bits 1:0 - support per-port power switching, or power always on */
if (HCC_PPC(xhci->hcc_params))
temp |= 0x0001;
else
@@ -86,9 +84,9 @@
ports = xhci->num_usb2_ports;
xhci_common_hub_descriptor(xhci, desc, ports);
- desc->bDescriptorType = 0x29;
+ desc->bDescriptorType = USB_DT_HUB;
temp = 1 + (ports / 8);
- desc->bDescLength = 7 + 2 * temp;
+ desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * temp;
/* The Device Removable bits are reported on a byte granularity.
* If the port doesn't exist within that byte, the bit is set to 0.
@@ -137,8 +135,8 @@
ports = xhci->num_usb3_ports;
xhci_common_hub_descriptor(xhci, desc, ports);
- desc->bDescriptorType = 0x2a;
- desc->bDescLength = 12;
+ desc->bDescriptorType = USB_DT_SS_HUB;
+ desc->bDescLength = USB_DT_SS_HUB_SIZE;
/* header decode latency should be zero for roothubs,
* see section 4.23.5.2.
@@ -424,6 +422,34 @@
xhci_writel(xhci, temp, port_array[port_id]);
}
+#if 0
+void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
+ __le32 __iomem **port_array, int port_id, u16 wake_mask)
+{
+ u32 temp;
+
+ temp = xhci_readl(xhci, port_array[port_id]);
+ temp = xhci_port_state_to_neutral(temp);
+
+ if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT)
+ temp |= PORT_WKCONN_E;
+ else
+ temp &= ~PORT_WKCONN_E;
+
+ if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT)
+ temp |= PORT_WKDISC_E;
+ else
+ temp &= ~PORT_WKDISC_E;
+
+ if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT)
+ temp |= PORT_WKOC_E;
+ else
+ temp &= ~PORT_WKOC_E;
+
+ xhci_writel(xhci, temp, port_array[port_id]);
+}
+#endif
+
/* Test and clear port RWC bit */
void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
int port_id, u32 port_bit)
@@ -486,6 +512,7 @@
int slot_id;
struct xhci_bus_state *bus_state;
u16 link_state = 0;
+ u16 wake_mask = 0;
max_ports = xhci_get_ports(hcd, &port_array);
bus_state = &xhci->bus_state[hcd_index(hcd)];
@@ -627,6 +654,8 @@
case SetPortFeature:
if (wValue == USB_PORT_FEAT_LINK_STATE)
link_state = (wIndex & 0xff00) >> 3;
+ if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
+ wake_mask = wIndex & 0xff00;
wIndex &= 0xff;
if (!wIndex || wIndex > max_ports)
goto error;
@@ -737,6 +766,16 @@
temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
break;
+#if 0
+ case USB_PORT_FEAT_REMOTE_WAKE_MASK:
+ xhci_set_remote_wake_mask(xhci, port_array,
+ wIndex, wake_mask);
+ temp = xhci_readl(xhci, port_array[wIndex]);
+ xhci_dbg(xhci, "set port remote wake mask, "
+ "actual port %d status = 0x%x\n",
+ wIndex, temp);
+ break;
+#endif
case USB_PORT_FEAT_BH_PORT_RESET:
temp |= PORT_WR;
xhci_writel(xhci, temp, port_array[wIndex]);
@@ -917,6 +956,10 @@
t2 |= PORT_LINK_STROBE | XDEV_U3;
set_bit(port_index, &bus_state->bus_suspended);
}
+ /* USB core sets remote wake mask for USB 3.0 hubs,
+ * including the USB 3.0 roothub, but only if CONFIG_USB_SUSPEND
+ * is enabled, so also enable remote wake here.
+ */
if (hcd->self.root_hub->do_remote_wakeup) {
if (t1 & PORT_CONNECT) {
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index a40ab98..98d4c07 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -34,25 +34,29 @@
* Section 4.11.1.1:
* "All components of all Command and Transfer TRBs shall be initialized to '0'"
*/
-static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flags)
+static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
+ unsigned int cycle_state, gfp_t flags)
{
struct xhci_segment *seg;
dma_addr_t dma;
+ int i;
seg = kzalloc(sizeof *seg, flags);
if (!seg)
return NULL;
- xhci_dbg(xhci, "Allocating priv segment structure at %p\n", seg);
seg->trbs = dma_pool_alloc(xhci->segment_pool, flags, &dma);
if (!seg->trbs) {
kfree(seg);
return NULL;
}
- xhci_dbg(xhci, "// Allocating segment at %p (virtual) 0x%llx (DMA)\n",
- seg->trbs, (unsigned long long)dma);
memset(seg->trbs, 0, SEGMENT_SIZE);
+ /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */
+ if (cycle_state == 0) {
+ for (i = 0; i < TRBS_PER_SEGMENT; i++)
+ seg->trbs[i].link.control |= TRB_CYCLE;
+ }
seg->dma = dma;
seg->next = NULL;
@@ -62,15 +66,26 @@
static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
{
if (seg->trbs) {
- xhci_dbg(xhci, "Freeing DMA segment at %p (virtual) 0x%llx (DMA)\n",
- seg->trbs, (unsigned long long)seg->dma);
dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
seg->trbs = NULL;
}
- xhci_dbg(xhci, "Freeing priv segment structure at %p\n", seg);
kfree(seg);
}
+static void xhci_free_segments_for_ring(struct xhci_hcd *xhci,
+ struct xhci_segment *first)
+{
+ struct xhci_segment *seg;
+
+ seg = first->next;
+ while (seg != first) {
+ struct xhci_segment *next = seg->next;
+ xhci_segment_free(xhci, seg);
+ seg = next;
+ }
+ xhci_segment_free(xhci, first);
+}
+
/*
* Make the prev segment point to the next segment.
*
@@ -79,14 +94,14 @@
* related flags, such as End TRB, Toggle Cycle, and no snoop.
*/
static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev,
- struct xhci_segment *next, bool link_trbs, bool isoc)
+ struct xhci_segment *next, enum xhci_ring_type type)
{
u32 val;
if (!prev || !next)
return;
prev->next = next;
- if (link_trbs) {
+ if (type != TYPE_EVENT) {
prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr =
cpu_to_le64(next->dma);
@@ -97,39 +112,55 @@
/* Always set the chain bit with 0.95 hardware */
/* Set chain bit for isoc rings on AMD 0.96 host */
if (xhci_link_trb_quirk(xhci) ||
- (isoc && (xhci->quirks & XHCI_AMD_0x96_HOST)))
+ (type == TYPE_ISOC &&
+ (xhci->quirks & XHCI_AMD_0x96_HOST)))
val |= TRB_CHAIN;
prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
}
- xhci_dbg(xhci, "Linking segment 0x%llx to segment 0x%llx (DMA)\n",
- (unsigned long long)prev->dma,
- (unsigned long long)next->dma);
+}
+
+/*
+ * Link the ring to the new segments.
+ * Set Toggle Cycle for the new ring if needed.
+ */
+static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ struct xhci_segment *first, struct xhci_segment *last,
+ unsigned int num_segs)
+{
+ struct xhci_segment *next;
+
+ if (!ring || !first || !last)
+ return;
+
+ next = ring->enq_seg->next;
+ xhci_link_segments(xhci, ring->enq_seg, first, ring->type);
+ xhci_link_segments(xhci, last, next, ring->type);
+ ring->num_segs += num_segs;
+ ring->num_trbs_free += (TRBS_PER_SEGMENT - 1) * num_segs;
+
+ if (ring->type != TYPE_EVENT && ring->enq_seg == ring->last_seg) {
+ ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control
+ &= ~cpu_to_le32(LINK_TOGGLE);
+ last->trbs[TRBS_PER_SEGMENT-1].link.control
+ |= cpu_to_le32(LINK_TOGGLE);
+ ring->last_seg = last;
+ }
}
/* XXX: Do we need the hcd structure in all these functions? */
void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
- struct xhci_segment *seg;
- struct xhci_segment *first_seg;
-
if (!ring)
return;
- if (ring->first_seg) {
- first_seg = ring->first_seg;
- seg = first_seg->next;
- xhci_dbg(xhci, "Freeing ring at %p\n", ring);
- while (seg != first_seg) {
- struct xhci_segment *next = seg->next;
- xhci_segment_free(xhci, seg);
- seg = next;
- }
- xhci_segment_free(xhci, first_seg);
- ring->first_seg = NULL;
- }
+
+ if (ring->first_seg)
+ xhci_free_segments_for_ring(xhci, ring->first_seg);
+
kfree(ring);
}
-static void xhci_initialize_ring_info(struct xhci_ring *ring)
+static void xhci_initialize_ring_info(struct xhci_ring *ring,
+ unsigned int cycle_state)
{
/* The ring is empty, so the enqueue pointer == dequeue pointer */
ring->enqueue = ring->first_seg->trbs;
@@ -139,11 +170,53 @@
/* The ring is initialized to 0. The producer must write 1 to the cycle
* bit to handover ownership of the TRB, so PCS = 1. The consumer must
* compare CCS to the cycle bit to check ownership, so CCS = 1.
+ *
+ * New rings are initialized with cycle state equal to 1; if we are
+ * handling ring expansion, set the cycle state equal to the old ring.
*/
- ring->cycle_state = 1;
+ ring->cycle_state = cycle_state;
/* Not necessary for new rings, but needed for re-initialized rings */
ring->enq_updates = 0;
ring->deq_updates = 0;
+
+ /*
+ * Each segment has a link TRB, and leave an extra TRB for SW
+ * accounting purpose
+ */
+ ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
+}
+
+/* Allocate segments and link them for a ring */
+static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
+ struct xhci_segment **first, struct xhci_segment **last,
+ unsigned int num_segs, unsigned int cycle_state,
+ enum xhci_ring_type type, gfp_t flags)
+{
+ struct xhci_segment *prev;
+
+ prev = xhci_segment_alloc(xhci, cycle_state, flags);
+ if (!prev)
+ return -ENOMEM;
+ num_segs--;
+
+ *first = prev;
+ while (num_segs > 0) {
+ struct xhci_segment *next;
+
+ next = xhci_segment_alloc(xhci, cycle_state, flags);
+ if (!next) {
+ xhci_free_segments_for_ring(xhci, *first);
+ return -ENOMEM;
+ }
+ xhci_link_segments(xhci, prev, next, type);
+
+ prev = next;
+ num_segs--;
+ }
+ xhci_link_segments(xhci, prev, *first, type);
+ *last = prev;
+
+ return 0;
}
/**
@@ -154,48 +227,34 @@
* See section 4.9.1 and figures 15 and 16.
*/
static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
- unsigned int num_segs, bool link_trbs, bool isoc, gfp_t flags)
+ unsigned int num_segs, unsigned int cycle_state,
+ enum xhci_ring_type type, gfp_t flags)
{
struct xhci_ring *ring;
- struct xhci_segment *prev;
+ int ret;
ring = kzalloc(sizeof *(ring), flags);
- xhci_dbg(xhci, "Allocating ring at %p\n", ring);
if (!ring)
return NULL;
+ ring->num_segs = num_segs;
INIT_LIST_HEAD(&ring->td_list);
+ ring->type = type;
if (num_segs == 0)
return ring;
- ring->first_seg = xhci_segment_alloc(xhci, flags);
- if (!ring->first_seg)
+ ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg,
+ &ring->last_seg, num_segs, cycle_state, type, flags);
+ if (ret)
goto fail;
- num_segs--;
- prev = ring->first_seg;
- while (num_segs > 0) {
- struct xhci_segment *next;
-
- next = xhci_segment_alloc(xhci, flags);
- if (!next)
- goto fail;
- xhci_link_segments(xhci, prev, next, link_trbs, isoc);
-
- prev = next;
- num_segs--;
- }
- xhci_link_segments(xhci, prev, ring->first_seg, link_trbs, isoc);
-
- if (link_trbs) {
+ /* Only event ring does not use link TRB */
+ if (type != TYPE_EVENT) {
/* See section 4.9.2.1 and 6.4.4.1 */
- prev->trbs[TRBS_PER_SEGMENT-1].link.control |=
+ ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |=
cpu_to_le32(LINK_TOGGLE);
- xhci_dbg(xhci, "Wrote link toggle flag to"
- " segment %p (virtual), 0x%llx (DMA)\n",
- prev, (unsigned long long)prev->dma);
}
- xhci_initialize_ring_info(ring);
+ xhci_initialize_ring_info(ring, cycle_state);
return ring;
fail:
@@ -231,23 +290,64 @@
* pointers to the beginning of the ring.
*/
static void xhci_reinit_cached_ring(struct xhci_hcd *xhci,
- struct xhci_ring *ring, bool isoc)
+ struct xhci_ring *ring, unsigned int cycle_state,
+ enum xhci_ring_type type)
{
struct xhci_segment *seg = ring->first_seg;
+ int i;
+
do {
memset(seg->trbs, 0,
sizeof(union xhci_trb)*TRBS_PER_SEGMENT);
+ if (cycle_state == 0) {
+ for (i = 0; i < TRBS_PER_SEGMENT; i++)
+ seg->trbs[i].link.control |= TRB_CYCLE;
+ }
/* All endpoint rings have link TRBs */
- xhci_link_segments(xhci, seg, seg->next, 1, isoc);
+ xhci_link_segments(xhci, seg, seg->next, type);
seg = seg->next;
} while (seg != ring->first_seg);
- xhci_initialize_ring_info(ring);
+ ring->type = type;
+ xhci_initialize_ring_info(ring, cycle_state);
/* td list should be empty since all URBs have been cancelled,
* but just in case...
*/
INIT_LIST_HEAD(&ring->td_list);
}
+/*
+ * Expand an existing ring.
+ * Look for a cached ring or allocate a new ring which has same segment numbers
+ * and link the two rings.
+ */
+int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ unsigned int num_trbs, gfp_t flags)
+{
+ struct xhci_segment *first;
+ struct xhci_segment *last;
+ unsigned int num_segs;
+ unsigned int num_segs_needed;
+ int ret;
+
+ num_segs_needed = (num_trbs + (TRBS_PER_SEGMENT - 1) - 1) /
+ (TRBS_PER_SEGMENT - 1);
+
+ /* Allocate number of segments we needed, or double the ring size */
+ num_segs = ring->num_segs > num_segs_needed ?
+ ring->num_segs : num_segs_needed;
+
+ ret = xhci_alloc_segments_for_ring(xhci, &first, &last,
+ num_segs, ring->cycle_state, ring->type, flags);
+ if (ret)
+ return -ENOMEM;
+
+ xhci_link_rings(xhci, ring, first, last, num_segs);
+ xhci_dbg(xhci, "ring expansion succeed, now has %d segments\n",
+ ring->num_segs);
+
+ return 0;
+}
+
#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
@@ -542,7 +642,7 @@
*/
for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
stream_info->stream_rings[cur_stream] =
- xhci_ring_alloc(xhci, 1, true, false, mem_flags);
+ xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, mem_flags);
cur_ring = stream_info->stream_rings[cur_stream];
if (!cur_ring)
goto cleanup_rings;
@@ -857,7 +957,7 @@
}
/* Allocate endpoint 0 ring */
- dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, false, flags);
+ dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, flags);
if (!dev->eps[0].ring)
goto fail;
@@ -1295,24 +1395,16 @@
struct xhci_ring *ep_ring;
unsigned int max_packet;
unsigned int max_burst;
+ enum xhci_ring_type type;
u32 max_esit_payload;
ep_index = xhci_get_endpoint_index(&ep->desc);
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
+ type = usb_endpoint_type(&ep->desc);
/* Set up the endpoint ring */
- /*
- * Isochronous endpoint ring needs bigger size because one isoc URB
- * carries multiple packets and it will insert multiple tds to the
- * ring.
- * This should be replaced with dynamic ring resizing in the future.
- */
- if (usb_endpoint_xfer_isoc(&ep->desc))
- virt_dev->eps[ep_index].new_ring =
- xhci_ring_alloc(xhci, 8, true, true, mem_flags);
- else
- virt_dev->eps[ep_index].new_ring =
- xhci_ring_alloc(xhci, 1, true, false, mem_flags);
+ virt_dev->eps[ep_index].new_ring =
+ xhci_ring_alloc(xhci, 2, 1, type, mem_flags);
if (!virt_dev->eps[ep_index].new_ring) {
/* Attempt to use the ring cache */
if (virt_dev->num_rings_cached == 0)
@@ -1322,7 +1414,7 @@
virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL;
virt_dev->num_rings_cached--;
xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring,
- usb_endpoint_xfer_isoc(&ep->desc) ? true : false);
+ 1, type);
}
virt_dev->eps[ep_index].skip = false;
ep_ring = virt_dev->eps[ep_index].new_ring;
@@ -1697,6 +1789,7 @@
xhci_dbg(xhci, "Freed event ring\n");
xhci->cmd_ring_reserved_trbs = 0;
+
if (xhci->cmd_ring)
xhci_ring_free(xhci, xhci->cmd_ring);
xhci->cmd_ring = NULL;
@@ -2165,7 +2258,7 @@
unsigned int val, val2;
u64 val_64;
struct xhci_segment *seg;
- u32 page_size;
+ u32 page_size, temp;
int i;
page_size = xhci_readl(xhci, &xhci->op_regs->page_size);
@@ -2243,7 +2336,7 @@
goto fail;
/* Set up the command ring to have one segments for now. */
- xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, false, flags);
+ xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, flags);
if (!xhci->cmd_ring)
goto fail;
xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring);
@@ -2274,7 +2367,7 @@
* the event ring segment table (ERST). Section 4.9.3.
*/
xhci_dbg(xhci, "// Allocating event ring\n");
- xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, false,
+ xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
flags);
if (!xhci->event_ring)
goto fail;
@@ -2348,6 +2441,15 @@
INIT_LIST_HEAD(&xhci->lpm_failed_devs);
+ /* Enable USB 3.0 device notifications for function remote wake, which
+ * is necessary for allowing USB 3.0 devices to do remote wakeup from
+ * U3 (device suspend).
+ */
+ temp = xhci_readl(xhci, &xhci->op_regs->dev_notification);
+ temp &= ~DEV_NOTE_MASK;
+ temp |= DEV_NOTE_FWAKE;
+ xhci_writel(xhci, temp, &xhci->op_regs->dev_notification);
+
return 0;
fail:
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index daf5754..19e8921 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -329,7 +329,7 @@
return pci_register_driver(&xhci_pci_driver);
}
-void __exit xhci_unregister_pci(void)
+void xhci_unregister_pci(void)
{
pci_unregister_driver(&xhci_pci_driver);
}
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
new file mode 100644
index 0000000..3288465
--- /dev/null
+++ b/drivers/usb/host/xhci-plat.c
@@ -0,0 +1,224 @@
+/*
+ * xhci-plat.c - xHCI host controller driver platform Bus Glue.
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * A lot of code borrowed from the Linux xHCI driver.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "xhci.h"
+#include "xhci-comcerto2000.h"
+
+
+
+static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+ /*
+ * As of now platform drivers don't provide MSI support so we ensure
+ * here that the generic code does not try to make a pci_dev from our
+ * dev struct in order to setup MSI
+ */
+ xhci->quirks |= XHCI_BROKEN_MSI;
+}
+
+/* called during probe() after chip reset completes */
+static int xhci_plat_setup(struct usb_hcd *hcd)
+{
+ return xhci_gen_setup(hcd, xhci_plat_quirks);
+}
+
+static const struct hc_driver xhci_plat_xhci_driver = {
+ .description = "xhci-hcd",
+ .product_desc = "xHCI Host Controller",
+ .hcd_priv_size = sizeof(struct xhci_hcd *),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = xhci_irq,
+ .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = xhci_plat_setup,
+ .start = xhci_run,
+ .stop = xhci_stop,
+ .shutdown = xhci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = xhci_urb_enqueue,
+ .urb_dequeue = xhci_urb_dequeue,
+ .alloc_dev = xhci_alloc_dev,
+ .free_dev = xhci_free_dev,
+ .alloc_streams = xhci_alloc_streams,
+ .free_streams = xhci_free_streams,
+ .add_endpoint = xhci_add_endpoint,
+ .drop_endpoint = xhci_drop_endpoint,
+ .endpoint_reset = xhci_endpoint_reset,
+ .check_bandwidth = xhci_check_bandwidth,
+ .reset_bandwidth = xhci_reset_bandwidth,
+ .address_device = xhci_address_device,
+ .update_hub_device = xhci_update_hub_device,
+ .reset_device = xhci_discover_or_reset_device,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = xhci_get_frame,
+
+ /* Root hub support */
+ .hub_control = xhci_hub_control,
+ .hub_status_data = xhci_hub_status_data,
+#ifdef CONFIG_PM
+ .bus_suspend = comcerto_xhci_bus_suspend,
+ .bus_resume = comcerto_xhci_bus_resume,
+#endif
+};
+
+static int xhci_plat_probe(struct platform_device *pdev)
+{
+ const struct hc_driver *driver;
+ struct xhci_hcd *xhci;
+ struct resource *res;
+ struct usb_hcd *hcd;
+ int ret;
+ int irq;
+
+ printk(KERN_INFO "## %s\n",__func__);
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ /* Do the Platform specific initializations */
+ comcerto_start_xhci();
+
+ driver = &xhci_plat_xhci_driver;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ ret = -EBUSY;
+ goto put_hcd;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ ret = -EFAULT;
+ goto release_mem_region;
+ }
+#if 1
+ writel(readl(hcd->regs + 0xc200) & 0x7FFFFFFF, hcd->regs + 0xc200);
+ writel(readl(hcd->regs + 0xc2c0) & 0x7FFFFFFF, hcd->regs + 0xc2c0);
+ writel(0x5Dc11000, hcd->regs + 0xc110);
+#endif
+
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto unmap_registers;
+
+ /* USB 2.0 roothub is stored in the platform_device now. */
+ hcd = dev_get_drvdata(&pdev->dev);
+ xhci = hcd_to_xhci(hcd);
+ xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
+ dev_name(&pdev->dev), hcd);
+ if (!xhci->shared_hcd) {
+ ret = -ENOMEM;
+ goto dealloc_usb2_hcd;
+ }
+
+ /*
+ * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
+ * is called by usb_add_hcd().
+ */
+ *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
+
+ ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto put_usb3_hcd;
+
+ return 0;
+
+put_usb3_hcd:
+ usb_put_hcd(xhci->shared_hcd);
+
+dealloc_usb2_hcd:
+ usb_remove_hcd(hcd);
+
+unmap_registers:
+ iounmap(hcd->regs);
+
+release_mem_region:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+put_hcd:
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+static int xhci_plat_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ usb_remove_hcd(xhci->shared_hcd);
+ usb_put_hcd(xhci->shared_hcd);
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ usb_put_hcd(hcd);
+
+ /* Do the Platform specific shutdown */
+ comcerto_stop_xhci();
+
+ kfree(xhci);
+
+ return 0;
+}
+
+static struct platform_driver usb_xhci_driver = {
+ .probe = xhci_plat_probe,
+ .remove = xhci_plat_remove,
+ .driver = {
+ .name = "xhci-hcd",
+ },
+};
+MODULE_ALIAS("platform:xhci-hcd");
+
+int xhci_register_plat(void)
+{
+ return platform_driver_register(&usb_xhci_driver);
+}
+
+void xhci_unregister_plat(void)
+{
+ platform_driver_unregister(&usb_xhci_driver);
+}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index fb0981e..1e4b126 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -143,22 +143,26 @@
* See Cycle bit rules. SW is the consumer for the event ring only.
* Don't make a ring full of link TRBs. That would be dumb and this would loop.
*/
-static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer)
+static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
- union xhci_trb *next = ++(ring->dequeue);
+ union xhci_trb *next;
unsigned long long addr;
ring->deq_updates++;
+
+ /* If this is not event ring, there is one more usable TRB */
+ if (ring->type != TYPE_EVENT &&
+ !last_trb(xhci, ring, ring->deq_seg, ring->dequeue))
+ ring->num_trbs_free++;
+ next = ++(ring->dequeue);
+
/* Update the dequeue pointer further if that was a link TRB or we're at
* the end of an event ring segment (which doesn't have link TRBS)
*/
while (last_trb(xhci, ring, ring->deq_seg, next)) {
- if (consumer && last_trb_on_last_seg(xhci, ring, ring->deq_seg, next)) {
+ if (ring->type == TYPE_EVENT && last_trb_on_last_seg(xhci,
+ ring, ring->deq_seg, next)) {
ring->cycle_state = (ring->cycle_state ? 0 : 1);
- if (!in_interrupt())
- xhci_dbg(xhci, "Toggle cycle state for ring %p = %i\n",
- ring,
- (unsigned int) ring->cycle_state);
}
ring->deq_seg = ring->deq_seg->next;
ring->dequeue = ring->deq_seg->trbs;
@@ -185,13 +189,17 @@
* prepare_transfer()?
*/
static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
- bool consumer, bool more_trbs_coming, bool isoc)
+ bool more_trbs_coming)
{
u32 chain;
union xhci_trb *next;
unsigned long long addr;
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
+ /* If this is not event ring, there is one less usable TRB */
+ if (ring->type != TYPE_EVENT &&
+ !last_trb(xhci, ring, ring->enq_seg, ring->enqueue))
+ ring->num_trbs_free--;
next = ++(ring->enqueue);
ring->enq_updates++;
@@ -199,42 +207,38 @@
* the end of an event ring segment (which doesn't have link TRBS)
*/
while (last_trb(xhci, ring, ring->enq_seg, next)) {
- if (!consumer) {
- if (ring != xhci->event_ring) {
- /*
- * If the caller doesn't plan on enqueueing more
- * TDs before ringing the doorbell, then we
- * don't want to give the link TRB to the
- * hardware just yet. We'll give the link TRB
- * back in prepare_ring() just before we enqueue
- * the TD at the top of the ring.
- */
- if (!chain && !more_trbs_coming)
- break;
+ if (ring->type != TYPE_EVENT) {
+ /*
+ * If the caller doesn't plan on enqueueing more
+ * TDs before ringing the doorbell, then we
+ * don't want to give the link TRB to the
+ * hardware just yet. We'll give the link TRB
+ * back in prepare_ring() just before we enqueue
+ * the TD at the top of the ring.
+ */
+ if (!chain && !more_trbs_coming)
+ break;
- /* If we're not dealing with 0.95 hardware or
- * isoc rings on AMD 0.96 host,
- * carry over the chain bit of the previous TRB
- * (which may mean the chain bit is cleared).
- */
- if (!(isoc && (xhci->quirks & XHCI_AMD_0x96_HOST))
+ /* If we're not dealing with 0.95 hardware or
+ * isoc rings on AMD 0.96 host,
+ * carry over the chain bit of the previous TRB
+ * (which may mean the chain bit is cleared).
+ */
+ if (!(ring->type == TYPE_ISOC &&
+ (xhci->quirks & XHCI_AMD_0x96_HOST))
&& !xhci_link_trb_quirk(xhci)) {
- next->link.control &=
- cpu_to_le32(~TRB_CHAIN);
- next->link.control |=
- cpu_to_le32(chain);
- }
- /* Give this link TRB to the hardware */
- wmb();
- next->link.control ^= cpu_to_le32(TRB_CYCLE);
+ next->link.control &=
+ cpu_to_le32(~TRB_CHAIN);
+ next->link.control |=
+ cpu_to_le32(chain);
}
+ /* Give this link TRB to the hardware */
+ wmb();
+ next->link.control ^= cpu_to_le32(TRB_CYCLE);
+
/* Toggle the cycle bit after the last ring segment. */
if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
ring->cycle_state = (ring->cycle_state ? 0 : 1);
- if (!in_interrupt())
- xhci_dbg(xhci, "Toggle cycle state for ring %p = %i\n",
- ring,
- (unsigned int) ring->cycle_state);
}
}
ring->enq_seg = ring->enq_seg->next;
@@ -245,55 +249,23 @@
}
/*
- * Check to see if there's room to enqueue num_trbs on the ring. See rules
- * above.
- * FIXME: this would be simpler and faster if we just kept track of the number
- * of free TRBs in a ring.
+ * Check to see if there's room to enqueue num_trbs on the ring and make sure
+ * enqueue pointer will not advance into dequeue segment. See rules above.
*/
-static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
+static inline int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
unsigned int num_trbs)
{
- int i;
- union xhci_trb *enq = ring->enqueue;
- struct xhci_segment *enq_seg = ring->enq_seg;
- struct xhci_segment *cur_seg;
- unsigned int left_on_ring;
+ int num_trbs_in_deq_seg;
- /* If we are currently pointing to a link TRB, advance the
- * enqueue pointer before checking for space */
- while (last_trb(xhci, ring, enq_seg, enq)) {
- enq_seg = enq_seg->next;
- enq = enq_seg->trbs;
- }
+ if (ring->num_trbs_free < num_trbs)
+ return 0;
- /* Check if ring is empty */
- if (enq == ring->dequeue) {
- /* Can't use link trbs */
- left_on_ring = TRBS_PER_SEGMENT - 1;
- for (cur_seg = enq_seg->next; cur_seg != enq_seg;
- cur_seg = cur_seg->next)
- left_on_ring += TRBS_PER_SEGMENT - 1;
-
- /* Always need one TRB free in the ring. */
- left_on_ring -= 1;
- if (num_trbs > left_on_ring) {
- xhci_warn(xhci, "Not enough room on ring; "
- "need %u TRBs, %u TRBs left\n",
- num_trbs, left_on_ring);
+ if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) {
+ num_trbs_in_deq_seg = ring->dequeue - ring->deq_seg->trbs;
+ if (ring->num_trbs_free < num_trbs + num_trbs_in_deq_seg)
return 0;
- }
- return 1;
}
- /* Make sure there's an extra empty TRB available */
- for (i = 0; i <= num_trbs; ++i) {
- if (enq == ring->dequeue)
- return 0;
- enq++;
- while (last_trb(xhci, ring, enq_seg, enq)) {
- enq_seg = enq_seg->next;
- enq = enq_seg->trbs;
- }
- }
+
return 1;
}
@@ -560,12 +532,9 @@
cpu_to_le32(TRB_CYCLE);
cur_trb->generic.field[3] |= cpu_to_le32(
TRB_TYPE(TRB_TR_NOOP));
- xhci_dbg(xhci, "Cancel TRB %p (0x%llx dma) "
- "in seg %p (0x%llx dma)\n",
- cur_trb,
- (unsigned long long)xhci_trb_virt_to_dma(cur_seg, cur_trb),
- cur_seg,
- (unsigned long long)cur_seg->dma);
+ xhci_dbg(xhci, "TRB to noop at offset 0x%llx\n",
+ (unsigned long long)
+ xhci_trb_virt_to_dma(cur_seg, cur_trb));
}
if (cur_trb == cur_td->last_trb)
break;
@@ -705,9 +674,9 @@
*/
list_for_each(entry, &ep->cancelled_td_list) {
cur_td = list_entry(entry, struct xhci_td, cancelled_td_list);
- xhci_dbg(xhci, "Cancelling TD starting at %p, 0x%llx (dma).\n",
- cur_td->first_trb,
- (unsigned long long)xhci_trb_virt_to_dma(cur_td->start_seg, cur_td->first_trb));
+ xhci_dbg(xhci, "Removing canceled TD starting at 0x%llx (dma).\n",
+ (unsigned long long)xhci_trb_virt_to_dma(
+ cur_td->start_seg, cur_td->first_trb));
ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb);
if (!ep_ring) {
/* This shouldn't happen unless a driver is mucking
@@ -903,6 +872,43 @@
xhci_dbg(xhci, "xHCI host controller is dead.\n");
}
+
+static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
+ struct xhci_virt_device *dev,
+ struct xhci_ring *ep_ring,
+ unsigned int ep_index)
+{
+ union xhci_trb *dequeue_temp;
+ int num_trbs_free_temp;
+ bool revert = false;
+
+ num_trbs_free_temp = ep_ring->num_trbs_free;
+ dequeue_temp = ep_ring->dequeue;
+
+ while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
+ /* We have more usable TRBs */
+ ep_ring->num_trbs_free++;
+ ep_ring->dequeue++;
+ if (last_trb(xhci, ep_ring, ep_ring->deq_seg,
+ ep_ring->dequeue)) {
+ if (ep_ring->dequeue ==
+ dev->eps[ep_index].queued_deq_ptr)
+ break;
+ ep_ring->deq_seg = ep_ring->deq_seg->next;
+ ep_ring->dequeue = ep_ring->deq_seg->trbs;
+ }
+ if (ep_ring->dequeue == dequeue_temp) {
+ revert = true;
+ break;
+ }
+ }
+
+ if (revert) {
+ xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
+ ep_ring->num_trbs_free = num_trbs_free_temp;
+ }
+}
+
/*
* When we get a completion for a Set Transfer Ring Dequeue Pointer command,
* we need to clear the set deq pending flag in the endpoint ring state, so that
@@ -984,8 +990,8 @@
/* Update the ring's dequeue segment and dequeue pointer
* to reflect the new position.
*/
- ep_ring->deq_seg = dev->eps[ep_index].queued_deq_seg;
- ep_ring->dequeue = dev->eps[ep_index].queued_deq_ptr;
+ update_ring_for_set_deq_completion(xhci, dev,
+ ep_ring, ep_index);
} else {
xhci_warn(xhci, "Mismatch between completed Set TR Deq "
"Ptr command & xHCI internal state.\n");
@@ -1196,7 +1202,7 @@
xhci->error_bitmask |= 1 << 6;
break;
}
- inc_deq(xhci, xhci->cmd_ring, false);
+ inc_deq(xhci, xhci->cmd_ring);
}
static void handle_vendor_event(struct xhci_hcd *xhci,
@@ -1248,6 +1254,28 @@
return num_similar_speed_ports;
}
+#if 0
+static void handle_device_notification(struct xhci_hcd *xhci,
+ union xhci_trb *event)
+{
+ u32 slot_id;
+ struct usb_device *udev;
+
+ slot_id = TRB_TO_SLOT_ID(event->generic.field[3]);
+ if (!xhci->devs[slot_id]) {
+ xhci_warn(xhci, "Device Notification event for "
+ "unused slot %u\n", slot_id);
+ return;
+ }
+
+ xhci_dbg(xhci, "Device Wake Notification event for slot ID %u\n",
+ slot_id);
+ udev = xhci->devs[slot_id]->udev;
+ if (udev && udev->parent)
+ usb_wakeup_notification(udev->parent, udev->portnum);
+}
+#endif
+
static void handle_port_status(struct xhci_hcd *xhci,
union xhci_trb *event)
{
@@ -1332,7 +1360,14 @@
}
if (DEV_SUPERSPEED(temp)) {
- xhci_dbg(xhci, "resume SS port %d\n", port_id);
+ xhci_dbg(xhci, "remote wake SS port %d\n", port_id);
+ /* Set a flag to say the port signaled remote wakeup,
+ * so we can tell the difference between the end of
+ * device and host initiated resume.
+ */
+ bus_state->port_remote_wakeup |= 1 << faked_port_index;
+ xhci_test_and_clear_bit(xhci, port_array,
+ faked_port_index, PORT_PLC);
xhci_set_link_state(xhci, port_array, faked_port_index,
XDEV_U0);
slot_id = xhci_find_slot_id_by_port(hcd, xhci,
@@ -1356,13 +1391,40 @@
}
}
+#if 0
+ if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 &&
+ DEV_SUPERSPEED(temp)) {
+ xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
+ /* We've just brought the device into U0 through either the
+ * Resume state after a device remote wakeup, or through the
+ * U3Exit state after a host-initiated resume. If it's a device
+ * initiated remote wake, don't pass up the link state change,
+ * so the roothub behavior is consistent with external
+ * USB 3.0 hub behavior.
+ */
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ faked_port_index + 1);
+ if (slot_id && xhci->devs[slot_id])
+ xhci_ring_device(xhci, slot_id);
+ if (bus_state->port_remote_wakeup && (1 << faked_port_index)) {
+ bus_state->port_remote_wakeup &=
+ ~(1 << faked_port_index);
+ xhci_test_and_clear_bit(xhci, port_array,
+ faked_port_index, PORT_PLC);
+ usb_wakeup_notification(hcd->self.root_hub,
+ faked_port_index + 1);
+ bogus_port_status = true;
+ goto cleanup;
+ }
+ }
+#endif
if (hcd->speed != HCD_USB3)
xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
PORT_PLC);
cleanup:
/* Update event ring dequeue pointer before dropping the lock */
- inc_deq(xhci, xhci->event_ring, true);
+ inc_deq(xhci, xhci->event_ring);
/* Don't make the USB core poll the roothub if we got a bad port status
* change event. Besides, at that point we can't tell which roothub
@@ -1557,8 +1619,8 @@
} else {
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
- inc_deq(xhci, ep_ring, false);
- inc_deq(xhci, ep_ring, false);
+ inc_deq(xhci, ep_ring);
+ inc_deq(xhci, ep_ring);
}
td_cleanup:
@@ -1628,7 +1690,6 @@
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
- xhci_debug_trb(xhci, xhci->event_ring->dequeue);
switch (trb_comp_code) {
case COMP_SUCCESS:
if (event_trb == ep_ring->dequeue) {
@@ -1644,7 +1705,6 @@
}
break;
case COMP_SHORT_TX:
- xhci_warn(xhci, "WARN: short transfer on control ep\n");
if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
*status = -EREMOTEIO;
else
@@ -1813,8 +1873,8 @@
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
- inc_deq(xhci, ep_ring, false);
- inc_deq(xhci, ep_ring, false);
+ inc_deq(xhci, ep_ring);
+ inc_deq(xhci, ep_ring);
return finish_td(xhci, td, NULL, event, ep, status, true);
}
@@ -1955,6 +2015,16 @@
xdev = xhci->devs[slot_id];
if (!xdev) {
xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n");
+ xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n",
+ (unsigned long long) xhci_trb_virt_to_dma(
+ xhci->event_ring->deq_seg,
+ xhci->event_ring->dequeue),
+ lower_32_bits(le64_to_cpu(event->buffer)),
+ upper_32_bits(le64_to_cpu(event->buffer)),
+ le32_to_cpu(event->transfer_len),
+ le32_to_cpu(event->flags));
+ xhci_dbg(xhci, "Event ring:\n");
+ xhci_debug_segment(xhci, xhci->event_ring->deq_seg);
return -ENODEV;
}
@@ -1968,6 +2038,16 @@
EP_STATE_DISABLED) {
xhci_err(xhci, "ERROR Transfer event for disabled endpoint "
"or incorrect stream ring\n");
+ xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n",
+ (unsigned long long) xhci_trb_virt_to_dma(
+ xhci->event_ring->deq_seg,
+ xhci->event_ring->dequeue),
+ lower_32_bits(le64_to_cpu(event->buffer)),
+ upper_32_bits(le64_to_cpu(event->buffer)),
+ le32_to_cpu(event->transfer_len),
+ le32_to_cpu(event->flags));
+ xhci_dbg(xhci, "Event ring:\n");
+ xhci_debug_segment(xhci, xhci->event_ring->deq_seg);
return -ENODEV;
}
@@ -2001,7 +2081,7 @@
xhci_dbg(xhci, "Stopped on No-op or Link TRB\n");
break;
case COMP_STALL:
- xhci_warn(xhci, "WARN: Stalled endpoint\n");
+ xhci_dbg(xhci, "Stalled endpoint\n");
ep->ep_state |= EP_HALTED;
status = -EPIPE;
break;
@@ -2011,11 +2091,11 @@
break;
case COMP_SPLIT_ERR:
case COMP_TX_ERR:
- xhci_warn(xhci, "WARN: transfer error on endpoint\n");
+ xhci_dbg(xhci, "Transfer error on endpoint\n");
status = -EPROTO;
break;
case COMP_BABBLE:
- xhci_warn(xhci, "WARN: babble error on endpoint\n");
+ xhci_dbg(xhci, "Babble error on endpoint\n");
status = -EOVERFLOW;
break;
case COMP_DB_ERR:
@@ -2191,7 +2271,7 @@
* Will roll back to continue process missed tds.
*/
if (trb_comp_code == COMP_MISSED_INT || !ep->skip) {
- inc_deq(xhci, xhci->event_ring, true);
+ inc_deq(xhci, xhci->event_ring);
}
if (ret) {
@@ -2285,6 +2365,11 @@
else
update_ptrs = 0;
break;
+#if 0
+ case TRB_TYPE(TRB_DEV_NOTE):
+ handle_device_notification(xhci, event);
+ break;
+#endif
default:
if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
TRB_TYPE(48))
@@ -2303,7 +2388,7 @@
if (update_ptrs)
/* Update SW event ring dequeue pointer */
- inc_deq(xhci, xhci->event_ring, true);
+ inc_deq(xhci, xhci->event_ring);
/* Are there more items on the event ring? Caller will call us again to
* check.
@@ -2354,7 +2439,7 @@
/* FIXME when MSI-X is supported and there are multiple vectors */
/* Clear the MSI-X event interrupt status */
- if (hcd->irq != -1) {
+ if (hcd->irq) {
u32 irq_pending;
/* Acknowledge the PCI interrupt */
irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
@@ -2406,17 +2491,7 @@
irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd)
{
- irqreturn_t ret;
- struct xhci_hcd *xhci;
-
- xhci = hcd_to_xhci(hcd);
- set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
- if (xhci->shared_hcd)
- set_bit(HCD_FLAG_SAW_IRQ, &xhci->shared_hcd->flags);
-
- ret = xhci_irq(hcd);
-
- return ret;
+ return xhci_irq(hcd);
}
/**** Endpoint Ring Operations ****/
@@ -2429,7 +2504,7 @@
* prepare_transfer()?
*/
static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
- bool consumer, bool more_trbs_coming, bool isoc,
+ bool more_trbs_coming,
u32 field1, u32 field2, u32 field3, u32 field4)
{
struct xhci_generic_trb *trb;
@@ -2439,7 +2514,7 @@
trb->field[1] = cpu_to_le32(field2);
trb->field[2] = cpu_to_le32(field3);
trb->field[3] = cpu_to_le32(field4);
- inc_enq(xhci, ring, consumer, more_trbs_coming, isoc);
+ inc_enq(xhci, ring, more_trbs_coming);
}
/*
@@ -2447,8 +2522,10 @@
* FIXME allocate segments if the ring is full.
*/
static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
- u32 ep_state, unsigned int num_trbs, bool isoc, gfp_t mem_flags)
+ u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
{
+ unsigned int num_trbs_needed;
+
/* Make sure the endpoint has been added to xHC schedule */
switch (ep_state) {
case EP_STATE_DISABLED:
@@ -2476,11 +2553,25 @@
*/
return -EINVAL;
}
- if (!room_on_ring(xhci, ep_ring, num_trbs)) {
- /* FIXME allocate more room */
- xhci_err(xhci, "ERROR no room on ep ring\n");
- return -ENOMEM;
- }
+
+ while (1) {
+ if (room_on_ring(xhci, ep_ring, num_trbs))
+ break;
+
+ if (ep_ring == xhci->cmd_ring) {
+ xhci_err(xhci, "Do not support expand command ring\n");
+ return -ENOMEM;
+ }
+
+ xhci_dbg(xhci, "ERROR no room on ep ring, "
+ "try ring expansion\n");
+ num_trbs_needed = num_trbs - ep_ring->num_trbs_free;
+ if (xhci_ring_expansion(xhci, ep_ring, num_trbs_needed,
+ mem_flags)) {
+ xhci_err(xhci, "Ring expansion failed\n");
+ return -ENOMEM;
+ }
+ };
if (enqueue_is_link_trb(ep_ring)) {
struct xhci_ring *ring = ep_ring;
@@ -2492,8 +2583,9 @@
/* If we're not dealing with 0.95 hardware or isoc rings
* on AMD 0.96 host, clear the chain bit.
*/
- if (!xhci_link_trb_quirk(xhci) && !(isoc &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)))
+ if (!xhci_link_trb_quirk(xhci) &&
+ !(ring->type == TYPE_ISOC &&
+ (xhci->quirks & XHCI_AMD_0x96_HOST)))
next->link.control &= cpu_to_le32(~TRB_CHAIN);
else
next->link.control |= cpu_to_le32(TRB_CHAIN);
@@ -2504,11 +2596,6 @@
/* Toggle the cycle bit after the last ring segment. */
if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
ring->cycle_state = (ring->cycle_state ? 0 : 1);
- if (!in_interrupt()) {
- xhci_dbg(xhci, "queue_trb: Toggle cycle "
- "state for ring %p = %i\n",
- ring, (unsigned int)ring->cycle_state);
- }
}
ring->enq_seg = ring->enq_seg->next;
ring->enqueue = ring->enq_seg->trbs;
@@ -2526,7 +2613,6 @@
unsigned int num_trbs,
struct urb *urb,
unsigned int td_index,
- bool isoc,
gfp_t mem_flags)
{
int ret;
@@ -2544,7 +2630,7 @@
ret = prepare_ring(xhci, ep_ring,
le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
- num_trbs, isoc, mem_flags);
+ num_trbs, mem_flags);
if (ret)
return ret;
@@ -2580,10 +2666,8 @@
num_sgs = urb->num_mapped_sgs;
temp = urb->transfer_buffer_length;
- xhci_dbg(xhci, "count sg list trbs: \n");
num_trbs = 0;
for_each_sg(urb->sg, sg, num_sgs, i) {
- unsigned int previous_total_trbs = num_trbs;
unsigned int len = sg_dma_len(sg);
/* Scatter gather list entries may cross 64KB boundaries */
@@ -2598,22 +2682,11 @@
num_trbs++;
running_total += TRB_MAX_BUFF_SIZE;
}
- xhci_dbg(xhci, " sg #%d: dma = %#llx, len = %#x (%d), num_trbs = %d\n",
- i, (unsigned long long)sg_dma_address(sg),
- len, len, num_trbs - previous_total_trbs);
-
len = min_t(int, len, temp);
temp -= len;
if (temp == 0)
break;
}
- xhci_dbg(xhci, "\n");
- if (!in_interrupt())
- xhci_dbg(xhci, "ep %#x - urb len = %d, sglist used, "
- "num_trbs = %d\n",
- urb->ep->desc.bEndpointAddress,
- urb->transfer_buffer_length,
- num_trbs);
return num_trbs;
}
@@ -2687,7 +2760,7 @@
urb->dev->speed == USB_SPEED_FULL)
urb->interval /= 8;
}
- return xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index);
+ return xhci_queue_bulk_tx(xhci, mem_flags, urb, slot_id, ep_index);
}
/*
@@ -2767,7 +2840,7 @@
trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, 0, false, mem_flags);
+ num_trbs, urb, 0, mem_flags);
if (trb_buff_len < 0)
return trb_buff_len;
@@ -2799,8 +2872,6 @@
trb_buff_len = min_t(int, trb_buff_len, this_sg_len);
if (trb_buff_len > urb->transfer_buffer_length)
trb_buff_len = urb->transfer_buffer_length;
- xhci_dbg(xhci, "First length to xfer from 1st sglist entry = %u\n",
- trb_buff_len);
first_trb = true;
/* Queue the first TRB, even if it's zero-length */
@@ -2832,11 +2903,6 @@
if (usb_urb_dir_in(urb))
field |= TRB_ISP;
- xhci_dbg(xhci, " sg entry: dma = %#x, len = %#x (%d), "
- "64KB boundary at %#x, end dma = %#x\n",
- (unsigned int) addr, trb_buff_len, trb_buff_len,
- (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1),
- (unsigned int) addr + trb_buff_len);
if (TRB_MAX_BUFF_SIZE -
(addr & (TRB_MAX_BUFF_SIZE - 1)) < trb_buff_len) {
xhci_warn(xhci, "WARN: sg dma xfer crosses 64KB boundaries!\n");
@@ -2862,7 +2928,7 @@
more_trbs_coming = true;
else
more_trbs_coming = false;
- queue_trb(xhci, ep_ring, false, more_trbs_coming, false,
+ queue_trb(xhci, ep_ring, more_trbs_coming,
lower_32_bits(addr),
upper_32_bits(addr),
length_field,
@@ -2942,18 +3008,9 @@
}
/* FIXME: this doesn't deal with URB_ZERO_PACKET - need one more */
- if (!in_interrupt())
- xhci_dbg(xhci, "ep %#x - urb len = %#x (%d), "
- "addr = %#llx, num_trbs = %d\n",
- urb->ep->desc.bEndpointAddress,
- urb->transfer_buffer_length,
- urb->transfer_buffer_length,
- (unsigned long long)urb->transfer_dma,
- num_trbs);
-
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, 0, false, mem_flags);
+ num_trbs, urb, 0, mem_flags);
if (ret < 0)
return ret;
@@ -3025,7 +3082,7 @@
more_trbs_coming = true;
else
more_trbs_coming = false;
- queue_trb(xhci, ep_ring, false, more_trbs_coming, false,
+ queue_trb(xhci, ep_ring, more_trbs_coming,
lower_32_bits(addr),
upper_32_bits(addr),
length_field,
@@ -3071,9 +3128,6 @@
if (!urb->setup_packet)
return -EINVAL;
- if (!in_interrupt())
- xhci_dbg(xhci, "Queueing ctrl tx for slot id %d, ep %d\n",
- slot_id, ep_index);
/* 1 TRB for setup, 1 for status */
num_trbs = 2;
/*
@@ -3085,7 +3139,7 @@
num_trbs++;
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, 0, false, mem_flags);
+ num_trbs, urb, 0, mem_flags);
if (ret < 0)
return ret;
@@ -3118,7 +3172,7 @@
}
}
- queue_trb(xhci, ep_ring, false, true, false,
+ queue_trb(xhci, ep_ring, true,
setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16,
le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16,
TRB_LEN(8) | TRB_INTR_TARGET(0),
@@ -3138,7 +3192,7 @@
if (urb->transfer_buffer_length > 0) {
if (setup->bRequestType & USB_DIR_IN)
field |= TRB_DIR_IN;
- queue_trb(xhci, ep_ring, false, true, false,
+ queue_trb(xhci, ep_ring, true,
lower_32_bits(urb->transfer_dma),
upper_32_bits(urb->transfer_dma),
length_field,
@@ -3154,7 +3208,7 @@
field = 0;
else
field = TRB_DIR_IN;
- queue_trb(xhci, ep_ring, false, false, false,
+ queue_trb(xhci, ep_ring, false,
0,
0,
TRB_INTR_TARGET(0),
@@ -3265,15 +3319,6 @@
return -EINVAL;
}
- if (!in_interrupt())
- xhci_dbg(xhci, "ep %#x - urb len = %#x (%d),"
- " addr = %#llx, num_tds = %d\n",
- urb->ep->desc.bEndpointAddress,
- urb->transfer_buffer_length,
- urb->transfer_buffer_length,
- (unsigned long long)urb->transfer_dma,
- num_tds);
-
start_addr = (u64) urb->transfer_dma;
start_trb = &ep_ring->enqueue->generic;
start_cycle = ep_ring->cycle_state;
@@ -3303,8 +3348,7 @@
trbs_per_td = count_isoc_trbs_needed(xhci, urb, i);
ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
- urb->stream_id, trbs_per_td, urb, i, true,
- mem_flags);
+ urb->stream_id, trbs_per_td, urb, i, mem_flags);
if (ret < 0) {
if (i == 0)
return ret;
@@ -3374,7 +3418,7 @@
remainder |
TRB_INTR_TARGET(0);
- queue_trb(xhci, ep_ring, false, more_trbs_coming, true,
+ queue_trb(xhci, ep_ring, more_trbs_coming,
lower_32_bits(addr),
upper_32_bits(addr),
length_field,
@@ -3421,6 +3465,7 @@
ep_ring->enqueue = urb_priv->td[0]->first_trb;
ep_ring->enq_seg = urb_priv->td[0]->start_seg;
ep_ring->cycle_state = start_cycle;
+ ep_ring->num_trbs_free = ep_ring->num_trbs_free_temp;
usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
return ret;
}
@@ -3457,7 +3502,7 @@
* Do not insert any td of the urb to the ring if the check failed.
*/
ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
- num_trbs, true, mem_flags);
+ num_trbs, mem_flags);
if (ret)
return ret;
@@ -3493,7 +3538,9 @@
urb->dev->speed == USB_SPEED_FULL)
urb->interval /= 8;
}
- return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index);
+ ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free;
+
+ return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
}
/**** Command Ring Operations ****/
@@ -3516,7 +3563,7 @@
reserved_trbs++;
ret = prepare_ring(xhci, xhci->cmd_ring, EP_STATE_RUNNING,
- reserved_trbs, false, GFP_ATOMIC);
+ reserved_trbs, GFP_ATOMIC);
if (ret < 0) {
xhci_err(xhci, "ERR: No room for command on command ring\n");
if (command_must_succeed)
@@ -3524,8 +3571,8 @@
"unfailable commands failed.\n");
return ret;
}
- queue_trb(xhci, xhci->cmd_ring, false, false, false, field1, field2,
- field3, field4 | xhci->cmd_ring->cycle_state);
+ queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
+ field4 | xhci->cmd_ring->cycle_state);
return 0;
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 05f82e9..4acfe22 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -106,6 +106,9 @@
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
if (!ret)
xhci->xhc_state |= XHCI_STATE_HALTED;
+ else
+ xhci_warn(xhci, "Host not halted after %u microseconds.\n",
+ XHCI_MAX_HALT_USEC);
return ret;
}
@@ -200,14 +203,14 @@
ret = pci_enable_msi(pdev);
if (ret) {
- xhci_err(xhci, "failed to allocate MSI entry\n");
+ xhci_dbg(xhci, "failed to allocate MSI entry\n");
return ret;
}
ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq,
0, "xhci_hcd", xhci_to_hcd(xhci));
if (ret) {
- xhci_err(xhci, "disable MSI interrupt\n");
+ xhci_dbg(xhci, "disable MSI interrupt\n");
pci_disable_msi(pdev);
}
@@ -224,13 +227,13 @@
int ret;
/* return if using legacy interrupt */
- if (xhci_to_hcd(xhci)->irq >= 0)
+ if (xhci_to_hcd(xhci)->irq > 0)
return;
ret = xhci_free_msi(xhci);
if (!ret)
return;
- if (pdev->irq >= 0)
+ if (pdev->irq > 0)
free_irq(pdev->irq, xhci_to_hcd(xhci));
return;
@@ -270,7 +273,7 @@
ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count);
if (ret) {
- xhci_err(xhci, "Failed to enable MSI-X\n");
+ xhci_dbg(xhci, "Failed to enable MSI-X\n");
goto free_entries;
}
@@ -286,7 +289,7 @@
return ret;
disable_msix:
- xhci_err(xhci, "disable MSI-X interrupt\n");
+ xhci_dbg(xhci, "disable MSI-X interrupt\n");
xhci_free_irq(xhci);
pci_disable_msix(pdev);
free_entries:
@@ -341,7 +344,7 @@
/* unregister the legacy interrupt */
if (hcd->irq)
free_irq(hcd->irq, hcd);
- hcd->irq = -1;
+ hcd->irq = 0;
ret = xhci_setup_msix(xhci);
if (ret)
@@ -349,7 +352,7 @@
ret = xhci_setup_msi(xhci);
if (!ret)
- /* hcd->irq is -1, we have MSI */
+ /* hcd->irq is 0, we have MSI */
return 0;
if (!pdev->irq) {
@@ -730,6 +733,7 @@
ring->enq_seg = ring->deq_seg;
ring->enqueue = ring->dequeue;
+ ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
/*
* Ring is now zeroed, so the HW should look for change of ownership
* when the cycle bit is set to 1.
@@ -1339,9 +1343,6 @@
goto done;
}
- xhci_dbg(xhci, "Cancel URB %p\n", urb);
- xhci_dbg(xhci, "Event ring:\n");
- xhci_debug_ring(xhci, xhci->event_ring);
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index];
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
@@ -1350,12 +1351,18 @@
goto done;
}
- xhci_dbg(xhci, "Endpoint ring:\n");
- xhci_debug_ring(xhci, ep_ring);
-
urb_priv = urb->hcpriv;
+ i = urb_priv->td_cnt;
+ if (i < urb_priv->length)
+ xhci_dbg(xhci, "Cancel URB %p, dev %s, ep 0x%x, "
+ "starting at offset 0x%llx\n",
+ urb, urb->dev->devpath,
+ urb->ep->desc.bEndpointAddress,
+ (unsigned long long) xhci_trb_virt_to_dma(
+ urb_priv->td[i]->start_seg,
+ urb_priv->td[i]->first_trb));
- for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
+ for (; i < urb_priv->length; i++) {
td = urb_priv->td[i];
list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
}
@@ -3612,26 +3619,38 @@
3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000};
/* Calculate HIRD/BESL for USB2 PORTPMSC*/
-static int xhci_calculate_hird_besl(int u2del, bool use_besl)
+static int xhci_calculate_hird_besl(struct xhci_hcd *xhci,
+ struct usb_device *udev)
{
- int hird;
+ int u2del, besl, besl_host;
+ int besl_device = 0;
+ u32 field;
- if (use_besl) {
- for (hird = 0; hird < 16; hird++) {
- if (xhci_besl_encoding[hird] >= u2del)
+ u2del = HCS_U2_LATENCY(xhci->hcs_params3);
+ field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
+
+ if (field & USB_BESL_SUPPORT) {
+ for (besl_host = 0; besl_host < 16; besl_host++) {
+ if (xhci_besl_encoding[besl_host] >= u2del)
break;
}
+ /* Use baseline BESL value as default */
+ if (field & USB_BESL_BASELINE_VALID)
+ besl_device = USB_GET_BESL_BASELINE(field);
+ else if (field & USB_BESL_DEEP_VALID)
+ besl_device = USB_GET_BESL_DEEP(field);
} else {
if (u2del <= 50)
- hird = 0;
+ besl_host = 0;
else
- hird = (u2del - 51) / 75 + 1;
-
- if (hird > 15)
- hird = 15;
+ besl_host = (u2del - 51) / 75 + 1;
}
- return hird;
+ besl = besl_host + besl_device;
+ if (besl > 15)
+ besl = 15;
+
+ return besl;
}
static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
@@ -3644,7 +3663,7 @@
u32 temp, dev_id;
unsigned int port_num;
unsigned long flags;
- int u2del, hird;
+ int hird;
int ret;
if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
@@ -3690,12 +3709,7 @@
* HIRD or BESL shoule be used. See USB2.0 LPM errata.
*/
pm_addr = port_array[port_num] + 1;
- u2del = HCS_U2_LATENCY(xhci->hcs_params3);
- if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2))
- hird = xhci_calculate_hird_besl(u2del, 1);
- else
- hird = xhci_calculate_hird_besl(u2del, 0);
-
+ hird = xhci_calculate_hird_besl(xhci, udev);
temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird);
xhci_writel(xhci, temp, pm_addr);
@@ -3774,7 +3788,7 @@
u32 temp;
unsigned int port_num;
unsigned long flags;
- int u2del, hird;
+ int hird;
if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support ||
!udev->lpm_capable)
@@ -3797,11 +3811,7 @@
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
enable ? "enable" : "disable", port_num);
- u2del = HCS_U2_LATENCY(xhci->hcs_params3);
- if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2))
- hird = xhci_calculate_hird_besl(u2del, 1);
- else
- hird = xhci_calculate_hird_besl(u2del, 0);
+ hird = xhci_calculate_hird_besl(xhci, udev);
if (enable) {
temp &= ~PORT_HIRD_MASK;
@@ -3962,7 +3972,8 @@
int retval;
u32 temp;
- hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2;
+ /* Accept arbitrarily long scatter-gather lists */
+ hcd->self.sg_tablesize = ~0;
if (usb_hcd_is_primary_hcd(hcd)) {
xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
@@ -4057,6 +4068,11 @@
printk(KERN_DEBUG "Problem registering PCI driver.");
return retval;
}
+ retval = xhci_register_plat();
+ if (retval < 0) {
+ printk(KERN_DEBUG "Problem registering platform driver.");
+ goto unreg_pci;
+ }
/*
* Check the compiler generated sizes of structures that must be laid
* out in specific ways for hardware access.
@@ -4076,11 +4092,15 @@
BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
BUILD_BUG_ON(sizeof(struct xhci_doorbell_array) != 256*32/8);
return 0;
+unreg_pci:
+ xhci_unregister_pci();
+ return retval;
}
module_init(xhci_hcd_init);
static void __exit xhci_hcd_cleanup(void)
{
xhci_unregister_pci();
+ xhci_unregister_plat();
}
module_exit(xhci_hcd_cleanup);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 7a56805..ec29483 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1231,10 +1231,7 @@
/* Allow two commands + a link TRB, along with any reserved command TRBs */
#define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3)
#define SEGMENT_SIZE (TRBS_PER_SEGMENT*16)
-/* SEGMENT_SHIFT should be log2(SEGMENT_SIZE).
- * Change this if you change TRBS_PER_SEGMENT!
- */
-#define SEGMENT_SHIFT 10
+#define SEGMENT_SHIFT (__ffs(SEGMENT_SIZE))
/* TRB buffer pointers can't cross 64KB boundaries */
#define TRB_MAX_BUFF_SHIFT 16
#define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT)
@@ -1261,8 +1258,19 @@
int new_cycle_state;
};
+enum xhci_ring_type {
+ TYPE_CTRL = 0,
+ TYPE_ISOC,
+ TYPE_BULK,
+ TYPE_INTR,
+ TYPE_STREAM,
+ TYPE_COMMAND,
+ TYPE_EVENT,
+};
+
struct xhci_ring {
struct xhci_segment *first_seg;
+ struct xhci_segment *last_seg;
union xhci_trb *enqueue;
struct xhci_segment *enq_seg;
unsigned int enq_updates;
@@ -1277,6 +1285,10 @@
*/
u32 cycle_state;
unsigned int stream_id;
+ unsigned int num_segs;
+ unsigned int num_trbs_free;
+ unsigned int num_trbs_free_temp;
+ enum xhci_ring_type type;
bool last_td_was_short;
};
@@ -1352,6 +1364,7 @@
/* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */
u32 port_c_suspend;
u32 suspended_ports;
+ u32 port_remote_wakeup;
unsigned long resume_done[USB_MAXCHILDREN];
};
@@ -1363,7 +1376,7 @@
return 1;
}
-/* There is one ehci_hci structure per controller */
+/* There is one xhci_hcd structure per controller */
struct xhci_hcd {
struct usb_hcd *main_hcd;
struct usb_hcd *shared_hcd;
@@ -1618,6 +1631,8 @@
struct usb_device *udev, struct usb_host_endpoint *ep,
gfp_t mem_flags);
void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
+int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ unsigned int num_trbs, gfp_t flags);
void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
unsigned int ep_index);
@@ -1657,6 +1672,17 @@
static inline void xhci_unregister_pci(void) {}
#endif
+#if defined(CONFIG_USB_XHCI_PLATFORM) \
+ || defined(CONFIG_USB_XHCI_PLATFORM_MODULE)
+int xhci_register_plat(void);
+void xhci_unregister_plat(void);
+#else
+static inline int xhci_register_plat(void)
+{ return 0; }
+static inline void xhci_unregister_plat(void)
+{ }
+#endif
+
/* xHCI host controller glue */
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
void xhci_quiesce(struct xhci_hcd *xhci);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 79fd606..ad7818a 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -343,6 +343,17 @@
To compile this driver as a module, choose M here: the
module will be called imx2_wdt.
+config COMCERTO_WATCHDOG
+ tristate "Comcerto Watchdog"
+ depends on ARCH_COMCERTO
+ help
+ Say Y here if to include support for the watchdog timer in
+ Comcerto device.
+
+ This driver can be built as a module by choosing M.
+ The module will be called comcerto_wdt
+
+
# AVR32 Architecture
config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index fe893e9..0ffbb99 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -53,6 +53,7 @@
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
+obj-$(CONFIG_COMCERTO_WATCHDOG) += comcerto_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/comcerto_wdt.c b/drivers/watchdog/comcerto_wdt.c
new file mode 100644
index 0000000..d89aeb2
--- /dev/null
+++ b/drivers/watchdog/comcerto_wdt.c
@@ -0,0 +1,406 @@
+/*
+ * drivers/char/watchdog/comcerto_wdt.c
+ *
+ * Copyright (C) 2004,2005,2013 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/clk.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <mach/wdt.h>
+#include <mach/reset.h>
+
+#define WDT_NAME "comcerto_wdt"
+#define WDT_DEFAULT_TIMEOUT 5
+#define WDT_MAX_TIMEOUT (0xffffffff / COMCERTO_AHBCLK)
+
+static unsigned long COMCERTO_AHBCLK;
+static int wd_heartbeat = WDT_DEFAULT_TIMEOUT;
+static int nowayout = WATCHDOG_NOWAYOUT;
+static struct clk *clk_axi;
+
+module_param(wd_heartbeat, int, 0);
+MODULE_PARM_DESC(wd_heartbeat, "Watchdog heartbeat in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+#endif
+
+static unsigned long comcerto_wdt_busy;
+static char expect_close;
+static spinlock_t wdt_lock;
+
+/*
+ * Inform whether the boot was caused by AXI watchdog or not.
+ * If the boot was caused by AXI WDT, the WD status is cleared from
+ * reset control register.
+ */
+static int comcerto_wdt_rst_status(void)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&wdt_lock, flags);
+
+ if(__raw_readl(GNRL_DEVICE_STATUS) & AXI_WD_RST_ACTIVATED) {
+ __raw_writel(WD_STATUS_CLR | __raw_readl(DEVICE_RST_CNTRL), DEVICE_RST_CNTRL);
+ ret = 1;
+ }
+
+ spin_unlock_irqrestore(&wdt_lock, flags);
+
+ return ret;
+}
+
+/*
+ * Set a new heartbeat value for the watchdog device. If the heartbeat value is
+ * incorrect we keep the old value and return -EINVAL. If successfull we return 0.
+ */
+static int comcerto_wdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > WDT_MAX_TIMEOUT)
+ return -EINVAL;
+
+ wd_heartbeat = t;
+ return 0;
+}
+
+/*
+ * Write wd_heartbeat to high bound register.
+ */
+static void comcerto_wdt_set_timeout(void)
+{
+ __raw_writel(wd_heartbeat * COMCERTO_AHBCLK, COMCERTO_TIMER_WDT_HIGH_BOUND);
+}
+
+/*
+ * Disable the watchdog.
+ */
+static void comcerto_wdt_stop(void)
+{
+ unsigned long flags;
+ u32 wdt_control;
+
+ spin_lock_irqsave(&wdt_lock, flags);
+
+ wdt_control = __raw_readl(COMCERTO_TIMER_WDT_CONTROL);
+
+ __raw_writel(wdt_control & ~COMCERTO_TIMER_WDT_CONTROL_TIMER_ENABLE, COMCERTO_TIMER_WDT_CONTROL);
+
+ spin_unlock_irqrestore(&wdt_lock, flags);
+
+ comcerto_wdt_set_timeout();
+}
+
+/*
+ * Enable the watchdog.
+ */
+static void comcerto_wdt_start(void)
+{
+ unsigned long flags;
+ u32 wdt_control;
+
+ spin_lock_irqsave(&wdt_lock, flags);
+
+ wdt_control = __raw_readl(COMCERTO_TIMER_WDT_CONTROL);
+
+ __raw_writel(wdt_control | COMCERTO_TIMER_WDT_CONTROL_TIMER_ENABLE, COMCERTO_TIMER_WDT_CONTROL);
+
+ comcerto_rst_cntrl_set(AXI_WD_RST_EN);
+
+ spin_unlock_irqrestore(&wdt_lock, flags);
+}
+
+/*
+ * Disable WDT and:
+ * - set max. possible timeout to avoid reset, it can occur
+ * since current counter value could be bigger then
+ * high bound one at the moment
+ * Function is called once at start (while configuration),
+ * and it's safe not to disable/enable IRQs.
+ */
+static void comcerto_wdt_config(void)
+{
+ comcerto_wdt_stop();
+
+ __raw_writel(~0, COMCERTO_TIMER_WDT_HIGH_BOUND); /* write max timout */
+}
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int comcerto_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &comcerto_wdt_busy))
+ return -EBUSY;
+
+ comcerto_wdt_set_timeout();
+ comcerto_wdt_start();
+
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * Release the watchdog device.
+ * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined and expect_close == 42
+ * i.e. magic char 'V' has been passed while write() then the watchdog
+ * is also disabled.
+ */
+static int comcerto_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ comcerto_wdt_stop(); /* disable the watchdog when file is closed */
+ clear_bit(0, &comcerto_wdt_busy);
+ } else {
+ printk(KERN_CRIT "%s: closed unexpectedly. WDT will not stop!\n", WDT_NAME);
+ }
+
+ expect_close = 0;
+ return 0;
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long comcerto_wdt_ioctl(struct file *file, uint cmd, ulong arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+ int err;
+ static struct watchdog_info comcerto_wdt_info = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ };
+
+ switch(cmd) {
+ case WDIOC_KEEPALIVE:
+ comcerto_wdt_set_timeout();
+ break;
+
+ case WDIOC_GETSUPPORT:
+ strncpy(comcerto_wdt_info.identity, WDT_NAME, sizeof(comcerto_wdt_info.identity));
+ if (copy_to_user(argp, &comcerto_wdt_info, sizeof(comcerto_wdt_info)) != 0) {
+ err = -EFAULT;
+ goto err;
+ }
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p)) {
+ err = -EFAULT;
+ goto err;
+ }
+
+ if (comcerto_wdt_set_heartbeat(new_value)) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ comcerto_wdt_set_timeout();
+
+ return put_user(wd_heartbeat, p);
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(wd_heartbeat, p);
+ break;
+
+ case WDIOC_GETSTATUS:
+ return put_user(0, p);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(comcerto_wdt_rst_status(), p);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_value, p)) {
+ err = -EFAULT;
+ goto err;
+ }
+
+ if (new_value & WDIOS_DISABLECARD)
+ comcerto_wdt_stop();
+
+ if (new_value & WDIOS_ENABLECARD)
+ comcerto_wdt_start();
+
+ break;
+
+ default:
+ err = -ENOIOCTLCMD;
+ goto err;
+ break;
+ }
+
+ return 0;
+
+err:
+ return err;
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t comcerto_wdt_write(struct file *file, const char *buf, size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+ char c;
+
+ /* in case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ if (get_user(c, buf + i))
+ return -EFAULT;
+
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+
+ comcerto_wdt_set_timeout();
+ }
+
+ return len;
+}
+
+static const struct file_operations comcerto_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = comcerto_wdt_ioctl,
+ .open = comcerto_wdt_open,
+ .release = comcerto_wdt_release,
+ .write = comcerto_wdt_write,
+};
+
+static struct miscdevice comcerto_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = WDT_NAME,
+ .fops = &comcerto_wdt_fops,
+};
+
+static int __init comcerto_wdt_probe(struct platform_device *pdev)
+{
+ int res;
+
+ if (comcerto_wdt_miscdev.parent)
+ return -EBUSY;
+
+ clk_axi = clk_get(NULL, "axi");
+
+ if (IS_ERR(clk_axi)){
+ pr_err("%s: Unable to obtain axi clock: %ld\n",__func__,PTR_ERR(clk_axi));
+ /* System cannot proceed from here */
+ BUG();
+ }
+
+ res = clk_enable(clk_axi);
+ if (res){
+ pr_err("%s: axi clock failed to enable:\n", __func__);
+ goto err_clk;
+ }
+
+ COMCERTO_AHBCLK = clk_get_rate(clk_axi);
+
+ comcerto_wdt_miscdev.parent = &pdev->dev;
+ comcerto_wdt_config();
+
+ res = misc_register(&comcerto_wdt_miscdev);
+ if (res)
+ goto err_misc;
+
+ printk(KERN_INFO "%s: support registered\n", WDT_NAME);
+
+ /* check that the heartbeat value is within range; if not reset to the default */
+ if (comcerto_wdt_set_heartbeat(wd_heartbeat)) {
+ comcerto_wdt_set_heartbeat(WDT_DEFAULT_TIMEOUT);
+
+ printk(KERN_INFO "%s: wd_heartbeat value is out of range: 1..%lu, using %d\n",
+ WDT_NAME, WDT_MAX_TIMEOUT, WDT_DEFAULT_TIMEOUT);
+ }
+
+ return 0;
+
+err_misc:
+ clk_disable(clk_axi);
+err_clk:
+ clk_put(clk_axi);
+
+ return res;
+}
+
+static int __exit comcerto_wdt_remove(struct platform_device *pdev)
+{
+ int res;
+
+
+ clk_disable(clk_axi);
+ clk_put(clk_axi);
+ res = misc_deregister(&comcerto_wdt_miscdev);
+ if (!res)
+ comcerto_wdt_miscdev.parent = NULL;
+
+ return res;
+}
+
+static struct platform_driver comcerto_wdt_driver = {
+ .probe = comcerto_wdt_probe,
+ .remove = __exit_p(comcerto_wdt_remove),
+ .driver = {
+ .name = WDT_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init comcerto_wdt_init(void)
+{
+ spin_lock_init(&wdt_lock);
+
+ return platform_driver_register(&comcerto_wdt_driver);
+}
+
+static void __exit comcerto_wdt_exit(void)
+{
+ platform_driver_unregister(&comcerto_wdt_driver);
+}
+
+module_init(comcerto_wdt_init);
+module_exit(comcerto_wdt_exit);
+
+MODULE_AUTHOR("Mindspeed Technologies, Inc.");
+MODULE_DESCRIPTION("Watchdog driver for Comcerto 2000 devices");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 97fbe93..a6a9129 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -30,6 +30,16 @@
#include <linux/statfs.h>
#include <linux/compat.h>
#include <linux/slab.h>
+
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+#include <linux/socket.h>
+#include <net/sock.h>
+#include <linux/net.h>
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+#include <mach/hardware.h>
+#endif
+#endif
+
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
@@ -1718,6 +1728,173 @@
return ret;
}
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+static ssize_t btrfs_splice_from_socket(struct file *file, struct socket *sock,
+ loff_t __user *ppos, size_t count)
+{
+ struct inode *inode = fdentry(file)->d_inode;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct page **pages = NULL;
+ struct kvec *iov = NULL;
+ struct msghdr msg;
+ long recvtimeo;
+ ssize_t copied = 0;
+ size_t offset, offset_tmp;
+ int num_pages, dirty_pages;
+ int err = 0;
+ loff_t start_pos;
+ loff_t pos = file->f_pos;
+ int i;
+ unsigned count_tmp = count;
+
+#define ERROR_OUT do {mutex_unlock(&inode->i_mutex); goto out;} while(0)
+
+ if (!count)
+ return 0;
+
+ if (ppos && copy_from_user(&pos, ppos, sizeof pos))
+ return -EFAULT;
+ offset = pos & (PAGE_CACHE_SIZE - 1);
+ num_pages = (offset + count + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+ if (num_pages > MDMA_OUTBOUND_BUF_DESC)
+ ERROR_OUT;
+#endif
+ start_pos = round_down(pos, root->sectorsize);
+
+ if (!(pages = kmalloc(num_pages * sizeof(struct page *), GFP_KERNEL)) ||
+ !(iov = kmalloc(num_pages * sizeof(*iov), GFP_KERNEL)))
+ ERROR_OUT;
+
+ vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+ current->backing_dev_info = inode->i_mapping->backing_dev_info;
+
+ mutex_lock(&inode->i_mutex);
+
+ if ((err = generic_write_checks(file, &pos, &count,
+ S_ISBLK(inode->i_mode))))
+ ERROR_OUT;
+
+ if ((err = file_remove_suid(file)))
+ ERROR_OUT;
+
+ if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
+ err = -EROFS;
+ ERROR_OUT;
+ }
+
+ if ((err = btrfs_update_time(file)))
+ ERROR_OUT;
+
+ BTRFS_I(inode)->sequence++;
+ if (start_pos > i_size_read(inode) &&
+ (err = btrfs_cont_expand(inode, i_size_read(inode), start_pos)))
+ ERROR_OUT;
+
+ if ((err = btrfs_delalloc_reserve_space(inode,
+ num_pages << PAGE_CACHE_SHIFT)))
+ goto out_free;
+
+ if ((err = prepare_pages(root, file, pages, num_pages,
+ pos, pos >> PAGE_CACHE_SHIFT,
+ count, false))) {
+ btrfs_delalloc_release_space(inode,
+ num_pages << PAGE_CACHE_SHIFT);
+ goto out_free;
+ }
+
+ for (i = 0, offset_tmp = offset; i < num_pages; i++) {
+ unsigned bytes = PAGE_CACHE_SIZE - offset_tmp;
+
+ if (bytes > count_tmp)
+ bytes = count_tmp;
+ iov[i].iov_base = kmap(pages[i]) + offset_tmp;
+ iov[i].iov_len = bytes;
+ offset_tmp = 0;
+ count_tmp -= bytes;
+ }
+
+ /* IOV is ready, receive the date from socket now */
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = (struct iovec *)&iov[0];
+ msg.msg_iovlen = num_pages;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = MSG_KERNSPACE;
+
+ recvtimeo = sock->sk->sk_rcvtimeo;
+ sock->sk->sk_rcvtimeo = 8 * HZ;
+ copied = kernel_recvmsg(sock, &msg, iov, num_pages, count,
+ MSG_WAITALL | MSG_NOCATCHSIG);
+ sock->sk->sk_rcvtimeo = recvtimeo;
+
+ if (copied < 0) {
+ err = copied;
+ copied = 0;
+ }
+
+ /* FIXME:
+ * The following results in at least one dirty_page even for copied==0
+ * unless offset==0, but otherwise the fir page would be corrupted
+ * for an unknown reason.
+ */
+ dirty_pages = (copied + offset + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT;
+
+ for (i = 0; i < num_pages; i++)
+ kunmap(pages[i]);
+ if (dirty_pages < num_pages) {
+ if (1||dirty_pages) {
+ spin_lock(&BTRFS_I(inode)->lock);
+ BTRFS_I(inode)->outstanding_extents++;
+ spin_unlock(&BTRFS_I(inode)->lock);
+ }
+ btrfs_delalloc_release_space(inode,
+ (num_pages - dirty_pages) <<
+ PAGE_CACHE_SHIFT);
+ }
+
+ if (dirty_pages) {
+ if ((err = btrfs_dirty_pages(root, inode, pages,
+ dirty_pages, pos, copied, NULL))) {
+ btrfs_delalloc_release_space(inode,
+ dirty_pages << PAGE_CACHE_SHIFT);
+ btrfs_drop_pages(pages, num_pages);
+ goto out_free;
+ }
+ }
+
+ btrfs_drop_pages(pages, num_pages);
+ cond_resched();
+
+ balance_dirty_pages_ratelimited_nr(inode->i_mapping, dirty_pages);
+ if (dirty_pages < (root->leafsize >> PAGE_CACHE_SHIFT) + 1)
+ btrfs_btree_balance_dirty(root, 1);
+
+ pos += copied;
+
+out_free:
+ mutex_unlock(&inode->i_mutex);
+
+ if (copied > 0) {
+ file->f_pos = pos;
+ if (ppos && copy_to_user(ppos, &pos, sizeof *ppos))
+ err = -EFAULT;
+ }
+
+ BTRFS_I(inode)->last_trans = root->fs_info->generation + 1;
+ if (copied > 0 || err == -EIOCBQUEUED)
+ err = generic_write_sync(file, pos, copied);
+out:
+ kfree(iov);
+ kfree(pages);
+ current->backing_dev_info = NULL;
+
+ return err ? err : copied;
+}
+#endif
+
static int find_desired_extent(struct inode *inode, loff_t *offset, int origin)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -1879,6 +2056,9 @@
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.splice_read = generic_file_splice_read,
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+ .splice_from_socket = btrfs_splice_from_socket,
+#endif
.aio_write = btrfs_file_aio_write,
.mmap = btrfs_file_mmap,
.open = generic_file_open,
diff --git a/fs/exec.c b/fs/exec.c
index 160cd2f..47982f5 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -624,7 +624,7 @@
* when the old and new regions overlap clear from new_end.
*/
free_pgd_range(&tlb, new_end, old_end, new_end,
- vma->vm_next ? vma->vm_next->vm_start : 0);
+ vma->vm_next ? vma->vm_next->vm_start : mm->task_size);
} else {
/*
* otherwise, clean from old_start; this is done to not touch
@@ -633,7 +633,7 @@
* for the others its just a little faster.
*/
free_pgd_range(&tlb, old_start, old_end, new_end,
- vma->vm_next ? vma->vm_next->vm_start : 0);
+ vma->vm_next ? vma->vm_next->vm_start : mm->task_size);
}
tlb_finish_mmu(&tlb, new_end, old_end);
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index cb70f18..d9ee0fd 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -243,7 +243,11 @@
.release = ext4_release_file,
.fsync = ext4_sync_file,
.splice_read = generic_file_splice_read,
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+ .splice_write = comcerto_file_splice_write,
+#else
.splice_write = generic_file_splice_write,
+#endif
.fallocate = ext4_fallocate,
};
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index a071348..0a6eeff 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3107,6 +3107,10 @@
ext4_group_t i, ngroups = ext4_get_groups_count(sb);
int s, j, count = 0;
+ if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC))
+ return (ext4_bg_has_super(sb, grp) + ext4_bg_num_gdb(sb, grp) +
+ sbi->s_itb_per_group + 2);
+
first_block = le32_to_cpu(sbi->s_es->s_first_data_block) +
(grp * EXT4_BLOCKS_PER_GROUP(sb));
last_block = first_block + EXT4_BLOCKS_PER_GROUP(sb) - 1;
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 22764c7..0ffe61f 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -142,6 +142,7 @@
}
return ret;
}
+EXPORT_SYMBOL(sys_dup);
#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index e3024ed..04109f9 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -22,6 +22,10 @@
#define DEFAULT_EMPTY_SCAN_SIZE 256
+#if defined CONFIG_MTD_NAND_COMCERTO
+#define BIT_FLIP_TOLERENCE 7
+#endif
+
#define noisy_printk(noise, args...) do { \
if (*(noise)) { \
printk(KERN_NOTICE args); \
@@ -64,6 +68,17 @@
return DEFAULT_EMPTY_SCAN_SIZE;
}
+#if defined CONFIG_MTD_NAND_COMCERTO
+static inline uint32_t count_zero_bits( uint32_t value) {
+ uint32_t num_zeros = 0;
+ size_t i;
+ for (i = 0; i < sizeof value; ++i, value >>= 1) {
+ if ((value & 1) == 0)
+ ++num_zeros;
+ }
+}
+#endif
+
static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
int ret;
@@ -589,6 +604,34 @@
else
return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
}
+
+#if defined CONFIG_MTD_NAND_COMCERTO
+ else if (cleanmarkerfound) {
+ ofs = 0;
+ uint32_t num_zeros = 0;
+ while((ofs < max_ofs) && (num_zeros < BIT_FLIP_TOLERENCE)) {
+ if (!(*(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF))
+ num_zeros = count_zero_bits(*(uint32_t *)(&buf[ofs]));
+ ofs += 4;
+ }
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+ if ((num_zeros < BIT_FLIP_TOLERENCE) && jffs2_cleanmarker_oob(c)) {
+ /* scan oob, take care of cleanmarker */
+ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
+ D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret));
+ switch (ret) {
+ case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
+ case 1: return BLK_STATE_ALLDIRTY;
+ default: return ret;
+ }
+ } else {
+ return BLK_STATE_ALLDIRTY;
+ }
+#endif
+
+ }
+#endif
+
if (ofs) {
D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
jeb->offset + ofs));
diff --git a/fs/splice.c b/fs/splice.c
index 014fcb4..3b0d632 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -32,6 +32,11 @@
#include <linux/security.h>
#include <linux/gfp.h>
#include <linux/socket.h>
+#include <linux/time.h>
+
+#include <net/sock.h>
+#include <linux/net.h>
+#include <linux/genalloc.h>
/*
* Attempt to steal a page from a pipe buffer. This should perhaps go into
@@ -335,7 +340,11 @@
/*
* Lookup the (hopefully) full range of pages we need.
*/
+#ifdef CONFIG_COMCERTO_SPLICE_READ_NOCONTIG
+ spd.nr_pages = find_get_pages(mapping, index, nr_pages, spd.pages);
+#else
spd.nr_pages = find_get_pages_contig(mapping, index, nr_pages, spd.pages);
+#endif
index += spd.nr_pages;
/*
@@ -840,6 +849,380 @@
}
EXPORT_SYMBOL(splice_from_pipe_feed);
+
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+#if !defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+#define MSPD_SPLICE_NUM_DMA 100
+#else
+#define MSPD_SPLICE_NUM_DMA MDMA_OUTBOUND_BUF_DESC
+#endif
+
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+unsigned int enable_splice_prof = 0;
+#endif
+
+int comcerto_splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd)
+{
+ struct page **mspd_splice_pages;
+ void **mspd_splice_fsdata;
+ struct pipe_buffer *buf;
+ const struct pipe_buf_operations *ops;
+ int ret, ret2 = 0, remaining;
+ unsigned int curbuf, nrbufs, len, nrbufs_len, done;
+ loff_t pos, offset;
+ struct file *file = sd->u.file;
+ struct address_space *mapping = file->f_mapping;
+ struct page **page;
+ void **fsdata;
+ unsigned int size;
+#if !defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+ unsigned int buf_len, buf_offset;
+ char *src, *dst;
+#else
+ struct comcerto_dma_sg *sg;
+#endif
+
+ size = (sizeof(struct page *) + sizeof(void *)) * MSPD_SPLICE_NUM_DMA;
+
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+ size = ALIGN(size, 8) + sizeof(struct comcerto_dma_sg);
+#endif
+
+ mspd_splice_pages = kmalloc(size, GFP_KERNEL);
+ if (!mspd_splice_pages)
+ return -ENOMEM;
+
+ mspd_splice_fsdata = (void **)(mspd_splice_pages + MSPD_SPLICE_NUM_DMA);
+
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+ sg = (struct comcerto_dma_sg *)(mspd_splice_fsdata + MSPD_SPLICE_NUM_DMA);
+ sg = PTR_ALIGN(sg, 8);
+#endif
+
+start:
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+ comcerto_dma_sg_init(sg);
+#endif
+
+ //Compute length to transfer (in bytes), and make sure data is there
+ nrbufs_len = 0;
+ nrbufs = pipe->nrbufs;
+ curbuf = pipe->curbuf;
+ while (nrbufs) {
+ buf = pipe->bufs + curbuf;
+
+ ret = buf->ops->confirm(pipe, buf);
+ if (unlikely(ret)) {
+ printk(KERN_WARNING "%s: buf->ops->confirm() failed(%d)\n", __func__, ret);
+ if (ret == -ENODATA)
+ ret = 0;
+ goto err;
+ }
+
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+ // Is there a risk of getting the same page more than once (several buffers in a single page)?
+ ret = comcerto_dma_sg_add_input(sg, page_address(buf->page) + buf->offset, buf->len, 0);
+ if (unlikely(ret)) {
+ printk(KERN_WARNING "%s: out of input bdescs\n", __func__);
+ break; //We will transfer what we could up to the previous buffer, based on nrbufs_len
+ }
+#endif
+
+ nrbufs_len += buf->len;
+
+ if (nrbufs_len > sd->total_len) {
+ nrbufs_len = sd->total_len;
+ break;
+ }
+
+ // - 2 because first and last pages could be almost empty depending on alignment
+ if (nrbufs_len > (MSPD_SPLICE_NUM_DMA - 2)*PAGE_CACHE_SIZE) {
+ nrbufs_len = (MSPD_SPLICE_NUM_DMA - 2)*PAGE_CACHE_SIZE;
+ break;
+ }
+ curbuf = (curbuf + 1) & (pipe->buffers - 1);
+ nrbufs--;
+ }
+
+ if (unlikely(nrbufs_len == 0)) {
+ printk(KERN_WARNING "%s: nrbufs_len == 0\n", __func__);
+ ret = 0;
+ goto err;
+ }
+
+// printk("BLA nrbufs_len: %d\n", nrbufs_len);
+
+ /* Allocate as many destinations pages as needed.
+ * First and last pages are likely not to be filled, but the ones in-between will.
+ * If some allocations fail, finish the work on the allocated pages.
+ */
+ page = &mspd_splice_pages[0];
+ fsdata = &mspd_splice_fsdata[0];
+
+ pos = sd->pos;
+ offset = pos & ~PAGE_CACHE_MASK;
+ len = nrbufs_len;
+
+ if (likely(len + offset > PAGE_CACHE_SIZE))
+ len = PAGE_CACHE_SIZE - offset;
+
+ ret = pagecache_write_begin(file, mapping, pos, len,
+ AOP_FLAG_UNINTERRUPTIBLE, page, fsdata);
+ if (unlikely(ret))
+ goto err; // We failed early, so we still have an easy way out
+
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+ comcerto_dma_sg_add_output(sg, page_address(*page) + offset, len, 1); //Don't check result since we should have at least one entry at this point
+#endif
+
+ pos += len;
+ remaining = nrbufs_len - len;
+ page++;
+ fsdata++;
+
+ while (remaining > PAGE_CACHE_SIZE) {
+ ret = pagecache_write_begin(file, mapping, pos, PAGE_CACHE_SIZE,
+ AOP_FLAG_UNINTERRUPTIBLE, page, fsdata);
+
+ if (unlikely(ret))
+ goto write_begin_done;
+
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+ ret = comcerto_dma_sg_add_output(sg, page_address(*page), PAGE_CACHE_SIZE, 1);
+ if (unlikely(ret)) {
+ pagecache_write_end(file, mapping, pos, PAGE_CACHE_SIZE, 0, *page, *fsdata);
+ goto write_begin_done;
+ }
+#endif
+ pos += PAGE_CACHE_SIZE;
+ remaining -= PAGE_CACHE_SIZE;
+ page++;
+ fsdata++;
+ }
+
+ if (remaining) {
+ ret = pagecache_write_begin(file, mapping, pos, remaining,
+ AOP_FLAG_UNINTERRUPTIBLE, page, fsdata);
+
+ if (unlikely(ret))
+ goto write_begin_done;
+
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+ ret = comcerto_dma_sg_add_output(sg, page_address(*page), remaining, 1);
+ if (unlikely(ret)) {
+ pagecache_write_end(file, mapping, pos, remaining, 0, *page, *fsdata);
+ goto write_begin_done;
+ }
+#endif
+ remaining = 0;
+ }
+
+write_begin_done:
+ // Couldn't allocate all pages or bdescs, so update the total length accordingly
+ if (unlikely(remaining))
+ nrbufs_len = nrbufs_len - remaining;
+
+ //Now do the copies
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+
+ comcerto_dma_get();
+
+ comcerto_dma_sg_setup(sg, nrbufs_len);
+
+ comcerto_dma_start();
+ comcerto_dma_wait();
+ comcerto_dma_put();
+
+ comcerto_dma_sg_cleanup(sg, nrbufs_len);
+#else
+ remaining = nrbufs_len;
+ curbuf = pipe->curbuf;
+ buf = pipe->bufs + curbuf;
+ buf_len = buf->len;
+ buf_offset = buf->offset;
+ src = buf->ops->map(pipe, buf, 1);
+ pos = sd->pos;
+ offset = pos & ~PAGE_CACHE_MASK;
+ page = &mspd_splice_pages[0];
+ dst = kmap_atomic(*page, KM_USER1);
+
+ while (remaining) {
+ len = remaining;
+ if (len + offset > PAGE_CACHE_SIZE)
+ len = PAGE_CACHE_SIZE - offset;
+ if (len > buf_len)
+ len = buf_len;
+
+ memcpy(dst + offset, src + buf_offset, len);
+
+ buf_len -= len;
+ buf_offset += len;
+ remaining -= len;
+ pos += len;
+ offset = pos & ~PAGE_CACHE_MASK;
+
+ if (!offset) {
+ /* FIXME if this was the last page we should still flush/unmap, even if it's not a full page */
+ /* ... actually it looks ok, the unmap is done outside the loop */
+ flush_dcache_page(*page);
+ kunmap_atomic(dst, KM_USER1);
+ if (remaining) {
+ page++;
+ dst = kmap_atomic(*page, KM_USER1);
+ }
+ }
+
+ if (!buf_len) {
+ buf->ops->unmap(pipe, buf, src);
+ if (remaining) {
+ curbuf = (curbuf + 1) & (pipe->buffers - 1);
+ buf = pipe->bufs + curbuf;
+ buf_len = buf->len;
+ buf_offset = buf->offset;
+ src = buf->ops->map(pipe, buf, 1);
+ }
+ }
+ }
+
+ if (offset) {
+ flush_dcache_page(*page);
+ kunmap_atomic(dst, KM_USER1);
+ }
+
+ if (buf_len)
+ buf->ops->unmap(pipe, buf, src);
+#endif
+
+
+ //loop on write_end, update sd fields
+ page = &mspd_splice_pages[0];
+ fsdata = &mspd_splice_fsdata[0];
+ offset = sd->pos & ~PAGE_CACHE_MASK;
+ pos = sd->pos;
+ remaining = nrbufs_len;
+ len = nrbufs_len;
+ done = 0;
+
+ if (likely(len + offset > PAGE_CACHE_SIZE))
+ len = PAGE_CACHE_SIZE - offset;
+
+ ret = pagecache_write_end(file, mapping, pos, len, len,
+ *page, *fsdata);
+
+ /* In case of error or short write we need to report error to the caller */
+ /* If there was already a previous error, just continue doing the pagecache_write_end() cleanup */
+ /* Otherwise keep track of how many bytes we have succefully written and that an error happened */
+ if (unlikely(ret != len)) {
+ printk(KERN_ERR "Failed on write_end, continuing with other buffers\n");
+
+ /* Only report error to caller if nothing has been done */
+ ret2 = ret;
+ nrbufs_len = (ret > 0) ? ret: 0;
+ }
+
+ pos += len;
+ done += len;
+ remaining -= len;
+
+ page++;
+ fsdata++;
+
+ while (remaining > PAGE_CACHE_SIZE) {
+ ret = pagecache_write_end(file, mapping, pos, PAGE_CACHE_SIZE, PAGE_CACHE_SIZE,
+ *page, *fsdata);
+
+ if (unlikely((ret != PAGE_CACHE_SIZE) && !ret2)) {
+ printk(KERN_ERR "Failed on write_end, continuing with other buffers\n");
+
+ nrbufs_len = done;
+
+ if (ret >= 0)
+ nrbufs_len += ret;
+
+ ret2 = nrbufs_len;
+ }
+
+ pos += PAGE_CACHE_SIZE;
+ done += PAGE_CACHE_SIZE;
+ remaining -= PAGE_CACHE_SIZE;
+
+ page++;
+ fsdata++;
+ }
+
+ if (remaining) {
+ ret = pagecache_write_end(file, mapping, pos, remaining, remaining,
+ *page, *fsdata);
+
+ if (unlikely((ret != remaining) && !ret2)) {
+ printk(KERN_ERR "Failed on write_end, continuing with other buffers\n");
+
+ nrbufs_len = done;
+
+ if (ret >= 0)
+ nrbufs_len += ret;
+
+ ret2 = nrbufs_len;
+ }
+ }
+
+ sd->num_spliced += nrbufs_len;
+ sd->len -= nrbufs_len;
+ sd->pos += nrbufs_len;
+ sd->total_len -= nrbufs_len;
+
+ //loop on pipe buffers to release them
+ remaining = nrbufs_len;
+ buf = pipe->bufs + pipe->curbuf;
+
+ while (remaining && (remaining >= buf->len)) {
+ ops = buf->ops;
+
+ remaining -= buf->len;
+ buf->len = 0;
+ buf->ops = NULL;
+ ops->release(pipe, buf);
+ pipe->nrbufs--;
+ pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
+ buf = pipe->bufs + pipe->curbuf;
+ }
+
+ // Last buffer, might not be empty
+ if (remaining) {
+ buf->len -= remaining;
+ buf->offset += remaining;
+ }
+
+ if (pipe->inode)
+ sd->need_wakeup = true;
+
+ if (!sd->total_len) {
+ kfree(mspd_splice_pages);
+ return 0;
+ }
+
+ if (ret2) {
+ if (ret2 > 0)
+ ret = 0;
+ else
+ ret = ret2;
+
+ goto err;
+ }
+
+ if (pipe->nrbufs)
+ goto start;
+
+ ret = 1;
+
+err:
+ kfree(mspd_splice_pages);
+
+ return ret;
+}
+EXPORT_SYMBOL(comcerto_splice_from_pipe_feed);
+#endif
+
/**
* splice_from_pipe_next - wait for some data to splice from
* @pipe: pipe to splice from
@@ -1042,6 +1425,113 @@
EXPORT_SYMBOL(generic_file_splice_write);
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+unsigned int splicew_time_counter[256];
+unsigned int splicew_reqtime_counter[256];
+unsigned int splicew_data_counter[256];
+static struct timeval last_splicew;
+unsigned int init_splicew_prof = 0;
+#endif
+
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+ssize_t
+comcerto_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags)
+{
+ struct address_space *mapping = out->f_mapping;
+ struct inode *inode = mapping->host;
+ struct splice_desc sd = {
+ .total_len = len,
+ .flags = flags,
+ .pos = *ppos,
+ .u.file = out,
+ };
+ ssize_t ret;
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+ struct timeval now;
+ int diff_time_ms;
+#endif
+
+ pipe_lock(pipe);
+
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+ if (enable_splice_prof) {
+ do_gettimeofday(&now);
+ if (init_splicew_prof) {
+ diff_time_ms = ((now.tv_sec - last_splicew.tv_sec) * 1000) + ((now.tv_usec - last_splicew.tv_usec) / 1000);
+ if (diff_time_ms < 1000) {
+ splicew_time_counter[diff_time_ms >> 3]++;
+ }
+ else {
+ splicew_time_counter[255]++;
+ }
+ }
+ last_splicew = now;
+ if (len < (1 <<21))
+ splicew_data_counter[(len >> 13) & 0xFF]++;
+ else
+ splicew_data_counter[255]++;
+ }
+#endif
+
+ splice_from_pipe_begin(&sd);
+ do {
+ ret = splice_from_pipe_next(pipe, &sd);
+ if (ret <= 0)
+ break;
+
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
+ ret = file_remove_suid(out);
+ if (!ret) {
+ file_update_time(out);
+ ret = comcerto_splice_from_pipe_feed(pipe, &sd);
+ }
+ mutex_unlock(&inode->i_mutex);
+ } while (ret > 0);
+ splice_from_pipe_end(pipe, &sd);
+
+ pipe_unlock(pipe);
+
+ if (sd.num_spliced)
+ ret = sd.num_spliced;
+
+ if (ret > 0) {
+ unsigned long nr_pages;
+ int err;
+
+ nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+
+ err = generic_write_sync(out, *ppos, ret);
+ if (err)
+ ret = err;
+ else
+ *ppos += ret;
+ balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
+ }
+
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+ if (enable_splice_prof) {
+ do_gettimeofday(&now);
+
+ diff_time_ms = ((now.tv_sec - last_splicew.tv_sec) * 1000) + ((now.tv_usec - last_splicew.tv_usec) / 1000);
+ if (diff_time_ms < 1000) {//Don't record useless data
+ splicew_reqtime_counter[diff_time_ms >> 3]++;
+ }
+ else
+ splicew_reqtime_counter[255]++;
+
+ if(!init_splicew_prof)
+ init_splicew_prof = 1;
+
+ last_splicew = now;
+ }
+#endif
+ return ret;
+}
+
+EXPORT_SYMBOL(comcerto_file_splice_write);
+#endif
+
static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
struct splice_desc *sd)
{
@@ -1692,30 +2182,39 @@
int, fd_out, loff_t __user *, off_out,
size_t, len, unsigned int, flags)
{
- long error;
- struct file *in, *out;
+ int error = -EBADF;
+ struct file *in, *out = NULL;
int fput_in, fput_out;
+ struct socket *sock = NULL;
if (unlikely(!len))
return 0;
- error = -EBADF;
- in = fget_light(fd_in, &fput_in);
- if (in) {
- if (in->f_mode & FMODE_READ) {
- out = fget_light(fd_out, &fput_out);
- if (out) {
- if (out->f_mode & FMODE_WRITE)
- error = do_splice(in, off_in,
- out, off_out,
- len, flags);
- fput_light(out, fput_out);
- }
- }
+ if (!(out = fget_light(fd_out, &fput_out)))
+ return -EBADF;
+ if (!(out->f_mode & FMODE_WRITE))
+ goto out;
+
+ /* Check if fd_in is a socket while out_fd is NOT a pipe. */
+ if (!get_pipe_info(out) &&
+ (sock = sockfd_lookup(fd_in, &error))) {
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+ if (sock->sk && out->f_op->splice_from_socket)
+ error = out->f_op->splice_from_socket(out, sock,
+ off_out, len);
+#endif
+ fput(sock->file);
+ } else
+ {
+ if (!(in = fget_light(fd_in, &fput_in)))
+ goto out;
+ if ((in->f_mode & FMODE_READ))
+ error = do_splice(in, off_in, out, off_out, len, flags);
fput_light(in, fput_in);
}
-
+out:
+ fput_light(out, fput_out);
return error;
}
@@ -1725,7 +2224,7 @@
*/
static int ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
{
- int ret;
+ int ret = 0;
/*
* Check ->nrbufs without the inode lock first. This function
@@ -1734,7 +2233,6 @@
if (pipe->nrbufs)
return 0;
- ret = 0;
pipe_lock(pipe);
while (!pipe->nrbufs) {
@@ -1763,7 +2261,7 @@
*/
static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
{
- int ret;
+ int ret = 0;
/*
* Check ->nrbufs without the inode lock first. This function
@@ -1772,7 +2270,6 @@
if (pipe->nrbufs < pipe->buffers)
return 0;
- ret = 0;
pipe_lock(pipe);
while (pipe->nrbufs >= pipe->buffers) {
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index b1038bd..a165ab8 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -108,6 +108,10 @@
struct dentry *debug_dir;
struct dentry *debug_stats;
#endif
+
+#ifdef CONFIG_ARCH_M86XXX
+ unsigned int cpu0_bind;
+#endif
};
int bdi_init(struct backing_dev_info *bdi);
diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index fc8a3ff..d756d92 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -26,6 +26,23 @@
(bit) < (size); \
(bit) = find_next_bit((addr), (size), (bit) + 1))
+/* same as for_each_set_bit() but use bit as value to start with */
+#define for_each_set_bit_from(bit, addr, size) \
+ for ((bit) = find_next_bit((addr), (size), (bit)); \
+ (bit) < (size); \
+ (bit) = find_next_bit((addr), (size), (bit) + 1))
+
+#define for_each_clear_bit(bit, addr, size) \
+ for ((bit) = find_first_zero_bit((addr), (size)); \
+ (bit) < (size); \
+ (bit) = find_next_zero_bit((addr), (size), (bit) + 1))
+
+/* same as for_each_clear_bit() but use bit as value to start with */
+#define for_each_clear_bit_from(bit, addr, size) \
+ for ((bit) = find_next_zero_bit((addr), (size), (bit)); \
+ (bit) < (size); \
+ (bit) = find_next_zero_bit((addr), (size), (bit) + 1))
+
static __inline__ int get_bitmask_order(unsigned int count)
{
int order;
diff --git a/include/linux/c2k-devfreq.h b/include/linux/c2k-devfreq.h
new file mode 100644
index 0000000..3fae95e
--- /dev/null
+++ b/include/linux/c2k-devfreq.h
@@ -0,0 +1,137 @@
+#ifndef _C2K_DEVFREQ_H
+#define _C2K_DEVFREQ_H
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/opp.h>
+#include <linux/devfreq.h>
+
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/jiffies.h>
+
+#define C2K_DEVFREQ_DEBUG
+#ifdef C2K_DEVFREQ_DEBUG
+ #define c2k_devfreq_debug(fmt, arg...) printk(fmt, ##arg)
+#else
+ #define c2k_devfreq_debug(fmt, arg...) ;
+#endif
+
+#define C2K_DEVFREQ_USE_KTIME
+typedef struct devfreq_perf_counters {
+#ifdef C2K_DEVFREQ_USE_KTIME
+ ktime_t fentry_time; /* ktime at the enrty of a func */
+ ktime_t start_time; /* starting ktime for every busy/total \
+ load cal in the last x seconds*/
+ ktime_t prev_count; /* prev ktime count */
+ s64 busy_time; /* total execution ktime count in the last \
+ x seconds */
+#else
+ unsigned long fentry_time; /* jiffies at the enrty of a func */
+ unsigned long start_time; /* starting jiffies for every busy/total \
+ load cal in the last x seconds*/
+ unsigned long prev_count; /* last jiffies count */
+ unsigned long busy_time; /* total execution jiffies count in the last \
+ x seconds */
+#endif
+}devfreq_counters;
+
+struct c2k_devfreq_opp_table {
+ unsigned int idx;
+ unsigned long freq; /* MHz */
+ unsigned long volt; /* uVolt */
+};
+
+struct c2k_devfreq_data{
+ devfreq_counters *dpc;
+ struct device *dev; /* provided by driver */
+ struct regulator *vdd_int; /* if not provided by driver, use default */
+ struct opp *curr_opp;
+ struct notifier_block *pm_notifier; /* if not provided by driver, use default */
+ struct c2k_devfreq_opp_table *opp_table; /* provided by driver */
+ struct devfreq_dev_profile *devfreq_profile; /* provided by driver */
+ const struct devfreq_governor *gov; /* provided by driver */
+ struct clk *clk;
+ int (*set_freq)(struct c2k_devfreq_data *data, unsigned long *freq);
+
+ int disabled;
+ unsigned long max_freq;
+ unsigned long min_freq;
+ struct mutex lock;
+};
+
+#ifdef C2K_DEVFREQ_USE_KTIME
+/* using ktime */
+ #define devfreq_func_start(dc) do {\
+ (dc)->fentry_time = ktime_get();\
+ }while(0)
+
+ #define devfreq_func_end(dc) do {\
+ (dc)->prev_count = ktime_get();\
+ (dc)->busy_time += ktime_to_ns(ktime_sub((dc)->prev_count, \
+ (dc)->fentry_time));\
+ }while(0)
+
+ #define module_busy_time(dc) ((dc)->busy_time)
+
+ #define module_busy_plus_not_busy_time(dc) \
+ ktime_to_ns(ktime_sub(ktime_get(), (dc)->start_time))
+
+ #define devfreq_reset_counters(dc) do { \
+ (dc)->start_time = ktime_get();\
+ }while(0)
+#else
+ #define devfreq_func_start(dc) \
+ (dc)->fentry_time = jiffies
+
+ #define devfreq_fun_end(dc) do { \
+ (dc)->prev_count = (jiffies)-((dc)->fentry_time);\
+ (dc)->busy_time += (dc)->prev_count;\
+ }while(0)
+
+ #define module_busy_time(dc) \
+ jiffies_to_usecs((dc)->busy_time)
+
+ #define module_busy_plus_not_busy_time(dc) \
+ jiffies_to_usecs(((jiffies) - (dc)->start_time))
+
+ #define devfreq_reset_counters(dc) do { \
+ (dc)->fentry_time = 0UL; \
+ (dc)->prev_count = 0UL; \
+ (dc)->busy_time = 0UL; \
+ (dc)->start_time = jiffies;\
+ }while(0)
+#endif
+
+#if 0
+static int devfreq_counters_init(struct c2k_devfreq_data *data)
+{
+ devfreq_counters **dc = &data->dpc;
+
+ *dc = kzalloc(sizeof (devfreq_counters), GFP_KERNEL);
+ if (dc == NULL) {
+ printk ("%s: Cannot allocate memory for devfreq_counters.\n"\
+ , __func__);
+ return -ENOMEM;
+ }
+
+#ifdef C2K_DEVFREQ_USE_KTIME
+ (*dc)->start_time = ktime_get();
+#else
+ (*dc)->fentry_time = 0UL;
+ (*dc)->prev_count = 0UL;
+ (*dc)->busy_time = 0UL;
+ (*dc)->start_time = jiffies;
+#endif
+ return 0;
+}
+#endif
+
+extern int c2k_driver_devfreq(struct device *dev, struct c2k_devfreq_data *data);
+
+#endif
diff --git a/include/linux/cryptodev.h b/include/linux/cryptodev.h
new file mode 100644
index 0000000..9185082
--- /dev/null
+++ b/include/linux/cryptodev.h
@@ -0,0 +1,561 @@
+/* $FreeBSD: src/sys/opencrypto/cryptodev.h,v 1.25 2007/05/09 19:37:02 gnn Exp $ */
+/* $OpenBSD: cryptodev.h,v 1.31 2002/06/11 11:14:29 beck Exp $ */
+
+/*-
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2006-2010 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
+ *
+ * This code was written by Angelos D. Keromytis in Athens, Greece, in
+ * February 2000. Network Security Technologies Inc. (NSTI) kindly
+ * supported the development of this code.
+ *
+ * Copyright (c) 2000 Angelos D. Keromytis
+ *
+ * Permission to use, copy, and modify this software with or without fee
+ * is hereby granted, provided that this entire notice is included in
+ * all source code copies of any software which is or includes a copy or
+ * modification of this software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
+ * PURPOSE.
+ *
+ * Copyright (c) 2001 Theo de Raadt
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ *
+ */
+
+#ifndef _CRYPTO_CRYPTO_H_
+#define _CRYPTO_CRYPTO_H_
+
+#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
+#include <linux/in.h>
+#endif
+
+
+/* Some initial values */
+#define CRYPTO_DRIVERS_INITIAL 4
+#define CRYPTO_SW_SESSIONS 32
+
+/* Hash values */
+#define NULL_HASH_LEN 0
+#define MD5_HASH_LEN 16
+#define SHA1_HASH_LEN 20
+#define RIPEMD160_HASH_LEN 20
+#define SHA2_256_HASH_LEN 32
+#define SHA2_384_HASH_LEN 48
+#define SHA2_512_HASH_LEN 64
+#define MD5_KPDK_HASH_LEN 16
+#define SHA1_KPDK_HASH_LEN 20
+/* Maximum hash algorithm result length */
+#define HASH_MAX_LEN SHA2_512_HASH_LEN /* Keep this updated */
+
+/* HMAC values */
+#define NULL_HMAC_BLOCK_LEN 1
+#define MD5_HMAC_BLOCK_LEN 64
+#define SHA1_HMAC_BLOCK_LEN 64
+#define RIPEMD160_HMAC_BLOCK_LEN 64
+#define SHA2_256_HMAC_BLOCK_LEN 64
+#define SHA2_384_HMAC_BLOCK_LEN 128
+#define SHA2_512_HMAC_BLOCK_LEN 128
+/* Maximum HMAC block length */
+#define HMAC_MAX_BLOCK_LEN SHA2_512_HMAC_BLOCK_LEN /* Keep this updated */
+#define HMAC_IPAD_VAL 0x36
+#define HMAC_OPAD_VAL 0x5C
+
+/* Encryption algorithm block sizes */
+#define NULL_BLOCK_LEN 1
+#define DES_BLOCK_LEN 8
+#define DES3_BLOCK_LEN 8
+#define BLOWFISH_BLOCK_LEN 8
+#define SKIPJACK_BLOCK_LEN 8
+#define CAST128_BLOCK_LEN 8
+#define RIJNDAEL128_BLOCK_LEN 16
+#define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN
+#define CAMELLIA_BLOCK_LEN 16
+#define ARC4_BLOCK_LEN 1
+#define EALG_MAX_BLOCK_LEN AES_BLOCK_LEN /* Keep this updated */
+
+/* Encryption algorithm min and max key sizes */
+#define NULL_MIN_KEY_LEN 0
+#define NULL_MAX_KEY_LEN 0
+#define DES_MIN_KEY_LEN 8
+#define DES_MAX_KEY_LEN 8
+#define DES3_MIN_KEY_LEN 24
+#define DES3_MAX_KEY_LEN 24
+#define BLOWFISH_MIN_KEY_LEN 4
+#define BLOWFISH_MAX_KEY_LEN 56
+#define SKIPJACK_MIN_KEY_LEN 10
+#define SKIPJACK_MAX_KEY_LEN 10
+#define CAST128_MIN_KEY_LEN 5
+#define CAST128_MAX_KEY_LEN 16
+#define RIJNDAEL128_MIN_KEY_LEN 16
+#define RIJNDAEL128_MAX_KEY_LEN 32
+#define AES_MIN_KEY_LEN RIJNDAEL128_MIN_KEY_LEN
+#define AES_MAX_KEY_LEN RIJNDAEL128_MAX_KEY_LEN
+#define CAMELLIA_MIN_KEY_LEN 16
+#define CAMELLIA_MAX_KEY_LEN 32
+#define ARC4_MIN_KEY_LEN 1
+#define ARC4_MAX_KEY_LEN 256
+
+/* Max size of data that can be processed */
+#define CRYPTO_MAX_DATA_LEN 64*1024 - 1
+
+#define CRYPTO_ALGORITHM_MIN 1
+#define CRYPTO_DES_CBC 1
+#define CRYPTO_3DES_CBC 2
+#define CRYPTO_BLF_CBC 3
+#define CRYPTO_CAST_CBC 4
+#define CRYPTO_SKIPJACK_CBC 5
+#define CRYPTO_MD5_HMAC 6
+#define CRYPTO_SHA1_HMAC 7
+#define CRYPTO_RIPEMD160_HMAC 8
+#define CRYPTO_MD5_KPDK 9
+#define CRYPTO_SHA1_KPDK 10
+#define CRYPTO_RIJNDAEL128_CBC 11 /* 128 bit blocksize */
+#define CRYPTO_AES_CBC 11 /* 128 bit blocksize -- the same as above */
+#define CRYPTO_ARC4 12
+#define CRYPTO_MD5 13
+#define CRYPTO_SHA1 14
+#define CRYPTO_NULL_HMAC 15
+#define CRYPTO_NULL_CBC 16
+#define CRYPTO_DEFLATE_COMP 17 /* Deflate compression algorithm */
+#define CRYPTO_SHA2_256_HMAC 18
+#define CRYPTO_SHA2_384_HMAC 19
+#define CRYPTO_SHA2_512_HMAC 20
+#define CRYPTO_CAMELLIA_CBC 21
+#define CRYPTO_SHA2_256 22
+#define CRYPTO_SHA2_384 23
+#define CRYPTO_SHA2_512 24
+#define CRYPTO_RIPEMD160 25
+#define CRYPTO_LZS_COMP 26
+#define CRYPTO_ESP_RFC2406 27
+//#define CRYPTO_ESP_RFC2406_TRANSPORT 20
+#define CRYPTO_ESP_RFC4303 28
+#define CRYPTO_ESP4_RFC4303 28
+#define CRYPTO_ESP6_RFC4303 29
+#define CRYPTO_AH 30
+#define CRYPTO_AH4 30
+#define CRYPTO_AH6 31
+#define CRYPTO_SHA2_HMAC 32 /*TODO is it a duplicate entry*/
+#define CRYPTO_ALGORITHM_MAX 32 /* Keep updated - see below */
+
+/* Algorithm flags */
+#define CRYPTO_ALG_FLAG_SUPPORTED 0x01 /* Algorithm is supported */
+#define CRYPTO_ALG_FLAG_RNG_ENABLE 0x02 /* Has HW RNG for DH/DSA */
+#define CRYPTO_ALG_FLAG_DSA_SHA 0x04 /* Can do SHA on msg */
+
+/*
+ * Crypto driver/device flags. They can set in the crid
+ * parameter when creating a session or submitting a key
+ * op to affect the device/driver assigned. If neither
+ * of these are specified then the crid is assumed to hold
+ * the driver id of an existing (and suitable) device that
+ * must be used to satisfy the request.
+ */
+#define CRYPTO_FLAG_HARDWARE 0x01000000 /* hardware accelerated */
+#define CRYPTO_FLAG_SOFTWARE 0x02000000 /* software implementation */
+
+/* NB: deprecated */
+struct session_op {
+ u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
+ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
+
+ u_int32_t keylen; /* cipher key */
+ caddr_t key;
+ int mackeylen; /* mac key */
+ caddr_t mackey;
+
+ u_int32_t ses; /* returns: session # */
+};
+
+struct session2_op {
+ u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
+ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
+
+ u_int32_t keylen; /* cipher key */
+ caddr_t key;
+ int mackeylen; /* mac key */
+ caddr_t mackey;
+
+ u_int32_t ses; /* returns: session # */
+ int crid; /* driver id + flags (rw) */
+ int pad[4]; /* for future expansion */
+};
+
+struct crypt_op {
+ u_int32_t ses;
+ u_int16_t op; /* i.e. COP_ENCRYPT */
+#define COP_NONE 0
+#define COP_ENCRYPT 1
+#define COP_DECRYPT 2
+ u_int16_t flags;
+#define COP_F_BATCH 0x0008 /* Batch op if possible */
+ u_int len;
+ caddr_t src, dst; /* become iov[] inside kernel */
+ caddr_t mac; /* must be big enough for chosen MAC */
+ caddr_t iv;
+};
+
+/*
+ * Parameters for looking up a crypto driver/device by
+ * device name or by id. The latter are returned for
+ * created sessions (crid) and completed key operations.
+ */
+struct crypt_find_op {
+ int crid; /* driver id + flags */
+ char name[32]; /* device/driver name */
+};
+
+/* bignum parameter, in packed bytes, ... */
+struct crparam {
+ caddr_t crp_p;
+ u_int crp_nbits;
+};
+
+#define CRK_MAXPARAM 8
+
+struct crypt_kop {
+ u_int crk_op; /* ie. CRK_MOD_EXP or other */
+ u_int crk_status; /* return status */
+ u_short crk_iparams; /* # of input parameters */
+ u_short crk_oparams; /* # of output parameters */
+ u_int crk_crid; /* NB: only used by CIOCKEY2 (rw) */
+ struct crparam crk_param[CRK_MAXPARAM];
+};
+#define CRK_ALGORITM_MIN 0
+#define CRK_MOD_EXP 0
+#define CRK_MOD_EXP_CRT 1
+#define CRK_DSA_SIGN 2
+#define CRK_DSA_VERIFY 3
+#define CRK_DH_COMPUTE_KEY 4
+#define CRK_ALGORITHM_MAX 4 /* Keep updated - see below */
+
+#define CRF_MOD_EXP (1 << CRK_MOD_EXP)
+#define CRF_MOD_EXP_CRT (1 << CRK_MOD_EXP_CRT)
+#define CRF_DSA_SIGN (1 << CRK_DSA_SIGN)
+#define CRF_DSA_VERIFY (1 << CRK_DSA_VERIFY)
+#define CRF_DH_COMPUTE_KEY (1 << CRK_DH_COMPUTE_KEY)
+
+/*
+ * done against open of /dev/crypto, to get a cloned descriptor.
+ * Please use F_SETFD against the cloned descriptor.
+ */
+#define CRIOGET _IOWR('c', 100, u_int32_t)
+#define CRIOASYMFEAT CIOCASYMFEAT
+#define CRIOFINDDEV CIOCFINDDEV
+
+/* the following are done against the cloned descriptor */
+#define CIOCGSESSION _IOWR('c', 101, struct session_op)
+#define CIOCFSESSION _IOW('c', 102, u_int32_t)
+#define CIOCCRYPT _IOWR('c', 103, struct crypt_op)
+#define CIOCKEY _IOWR('c', 104, struct crypt_kop)
+#define CIOCASYMFEAT _IOR('c', 105, u_int32_t)
+#define CIOCGSESSION2 _IOWR('c', 106, struct session2_op)
+#define CIOCKEY2 _IOWR('c', 107, struct crypt_kop)
+#define CIOCFINDDEV _IOWR('c', 108, struct crypt_find_op)
+
+struct cryptotstat {
+ struct timespec acc; /* total accumulated time */
+ struct timespec min; /* min time */
+ struct timespec max; /* max time */
+ u_int32_t count; /* number of observations */
+};
+
+struct cryptostats {
+ u_int32_t cs_ops; /* symmetric crypto ops submitted */
+ u_int32_t cs_errs; /* symmetric crypto ops that failed */
+ u_int32_t cs_kops; /* asymetric/key ops submitted */
+ u_int32_t cs_kerrs; /* asymetric/key ops that failed */
+ u_int32_t cs_intrs; /* crypto swi thread activations */
+ u_int32_t cs_rets; /* crypto return thread activations */
+ u_int32_t cs_blocks; /* symmetric op driver block */
+ u_int32_t cs_kblocks; /* symmetric op driver block */
+ /*
+ * When CRYPTO_TIMING is defined at compile time and the
+ * sysctl debug.crypto is set to 1, the crypto system will
+ * accumulate statistics about how long it takes to process
+ * crypto requests at various points during processing.
+ */
+ struct cryptotstat cs_invoke; /* crypto_dipsatch -> crypto_invoke */
+ struct cryptotstat cs_done; /* crypto_invoke -> crypto_done */
+ struct cryptotstat cs_cb; /* crypto_done -> callback */
+ struct cryptotstat cs_finis; /* callback -> callback return */
+
+ u_int32_t cs_drops; /* crypto ops dropped due to congestion */
+};
+
+#ifdef __KERNEL__
+
+/* Standard initialization structure beginning */
+struct cryptoini {
+ int cri_alg; /* Algorithm to use */
+ int cri_flags;
+ union {
+ struct {
+ int cri_mlen; /* Number of bytes we want from the
+ entire hash. 0 means all. */
+ int cri_klen; /* Key length, in bits */
+ caddr_t cri_key; /* key to use */
+ u_int8_t cri_iv[EALG_MAX_BLOCK_LEN]; /* IV to use */
+ } cri_alg;
+ struct {
+ u_int32_t basealg;
+ struct sockaddr_in tun_source;
+ struct sockaddr_in tun_destination;
+ int tun_df_mode;
+ int tun_ds_mode;
+ int tun_ttl_value;
+ int tun_replay_windowsize;
+ int spivalue ;
+ int replayinit; /* set to 0 to disable replay on receive */
+ u_int64_t time_hard_lifetime;
+ u_int64_t time_soft_lifetime;
+ u_int64_t byte_hard_lifetime;
+ u_int64_t byte_soft_lifetime;
+ } cri_pack;
+ } u;
+ struct cryptoini *cri_next;
+};
+#define cri_mlen u.cri_alg.cri_mlen
+#define cri_klen u.cri_alg.cri_klen
+#define cri_key u.cri_alg.cri_key
+#define cri_iv u.cri_alg.cri_iv
+#define crip_basealg u.cri_pack.basealg
+#define crip_tun_source u.cri_pack.tun_source
+#define crip_tun_destination u.cri_pack.tun_destination
+#define crip_tun_df_mode u.cri_pack.tun_df_mode
+#define crip_tun_ds_mode u.cri_pack.tun_ds_mode
+#define crip_tun_ttl_value u.cri_pack.tun_ttl_value
+#define crip_tun_replay_windowsize u.cri_pack.tun_replay_windowsize
+#define crip_spivalue u.cri_pack.spivalue
+#define crip_replayinit u.cri_pack.replayinit
+#define crip_time_hard_lifetime u.cri_pack.time_hard_lifetime
+#define crip_time_soft_lifetime u.cri_pack.time_soft_lifetime
+#define crip_byte_hard_lifetime u.cri_pack.byte_hard_lifetime
+#define crip_byte_soft_lifetime u.cri_pack.byte_soft_lifetime
+
+/* Describe boundaries of a single crypto operation */
+struct cryptodesc {
+ int crd_skip; /* How many bytes to ignore from start */
+ int crd_len; /* How many bytes to process */
+ int crd_inject; /* Where to inject results, if applicable */
+ int crd_flags;
+
+#define CRD_F_ENCRYPT 0x01 /* Set when doing encryption */
+#define CRD_F_IV_PRESENT 0x02 /* When encrypting, IV is already in
+ place, so don't copy. */
+#define CRD_F_IV_EXPLICIT 0x04 /* IV explicitly provided */
+#define CRD_F_DSA_SHA_NEEDED 0x08 /* Compute SHA-1 of buffer for DSA */
+#define CRD_F_KEY_EXPLICIT 0x10 /* Key explicitly provided */
+#define CRD_F_COMP 0x0f /* Set when doing compression */
+
+ struct cryptoini CRD_INI; /* Initialization/context data */
+#define crd_iv CRD_INI.cri_iv
+#define crd_key CRD_INI.cri_key
+#define crd_alg CRD_INI.cri_alg
+#define crd_klen CRD_INI.cri_klen
+#define crd_mlen CRD_INI.cri_mlen
+
+ struct cryptodesc *crd_next;
+};
+
+/* Structure describing complete operation */
+struct cryptop {
+ struct list_head crp_next;
+ wait_queue_head_t crp_waitq;
+
+ u_int64_t crp_sid; /* Session ID */
+ int crp_ilen; /* Input data total length */
+ int crp_olen; /* Result total length */
+
+ int crp_etype; /*
+ * Error type (zero means no error).
+ * All error codes except EAGAIN
+ * indicate possible data corruption (as in,
+ * the data have been touched). On all
+ * errors, the crp_sid may have changed
+ * (reset to a new one), so the caller
+ * should always check and use the new
+ * value on future requests.
+ */
+ int crp_flags;
+
+#define CRYPTO_F_SKBUF 0x0001 /* Input/output are skbuf chains */
+#define CRYPTO_F_IOV 0x0002 /* Input/output are uio */
+#define CRYPTO_F_REL 0x0004 /* Must return data in same place */
+#define CRYPTO_F_BATCH 0x0008 /* Batch op if possible */
+#define CRYPTO_F_CBIMM 0x0010 /* Do callback immediately */
+#define CRYPTO_F_DONE 0x0020 /* Operation completed */
+#define CRYPTO_F_CBIFSYNC 0x0040 /* Do CBIMM if op is synchronous */
+
+ caddr_t crp_buf; /* Data to be processed */
+ caddr_t crp_out_buf; /* Crypto Result Buffer */
+ caddr_t crp_opaque; /* Opaque pointer, passed along */
+ struct cryptodesc *crp_desc; /* Linked list of processing descriptors */
+
+ int (*crp_callback)(struct cryptop *); /* Callback function */
+};
+enum crypto_packet_return_code {
+ CRYPTO_OK=0,
+ CRYPTO_SOFT_TTL = 2,
+ CRYPTO_HARD_TTL,
+ CRYPTO_SA_INACTIVE,
+ CRYPTO_REPLAY,
+ CRYPTO_ICV_FAIL,
+ CRYPTO_SEQ_ROLL,
+ CRYPTO_MEM_ERROR,
+ CRYPTO_VERS_ERROR,
+ CRYPTO_PROT_ERROR,
+ CRYPTO_PYLD_ERROR,
+ CRYPTO_PAD_ERROR
+};
+
+enum crypto_accel_type {
+ CRYPTO_PACKET =0x2, /* OR together desired bits */
+ CRYPTO_HARDWARE=0x1,
+ CRYPTO_SOFTWARE=0x0
+};
+
+enum crypto_flags {
+ CRYPTO_ENCRYPT=0x1, // same for encap (OCF l2)
+ CRYPTO_DECRYPT=0x2, // same for decap (OCF l2)
+ CRYPTO_MAC_GEN=0x4,
+ CRYPTO_MAC_CHECK=0x08,
+ CRYPTO_COMPRESS_SMALLER=0x10,
+ CRYPTO_COMPRESS_BIGGER=0x20
+};
+
+#define CRYPTO_BUF_CONTIG 0x0
+#define CRYPTO_BUF_IOV 0x1
+#define CRYPTO_BUF_SKBUF 0x2
+
+#define CRYPTO_OP_DECRYPT 0x0
+#define CRYPTO_OP_ENCRYPT 0x1
+
+/*
+ * Hints passed to process methods.
+ */
+#define CRYPTO_HINT_MORE 0x1 /* more ops coming shortly */
+
+struct cryptkop {
+ struct list_head krp_next;
+ wait_queue_head_t krp_waitq;
+
+ int krp_flags;
+#define CRYPTO_KF_DONE 0x0001 /* Operation completed */
+#define CRYPTO_KF_CBIMM 0x0002 /* Do callback immediately */
+
+ u_int krp_op; /* ie. CRK_MOD_EXP or other */
+ u_int krp_status; /* return status */
+ u_short krp_iparams; /* # of input parameters */
+ u_short krp_oparams; /* # of output parameters */
+ u_int krp_crid; /* desired device, etc. */
+ u_int32_t krp_hid;
+ struct crparam krp_param[CRK_MAXPARAM]; /* kvm */
+ int (*krp_callback)(struct cryptkop *);
+};
+
+#include <linux/ocf-compat.h>
+
+/*
+ * Session ids are 64 bits. The lower 32 bits contain a "local id" which
+ * is a driver-private session identifier. The upper 32 bits contain a
+ * "hardware id" used by the core crypto code to identify the driver and
+ * a copy of the driver's capabilities that can be used by client code to
+ * optimize operation.
+ */
+#define CRYPTO_SESID2HID(_sid) (((_sid) >> 32) & 0x00ffffff)
+#define CRYPTO_SESID2CAPS(_sid) (((_sid) >> 32) & 0xff000000)
+#define CRYPTO_SESID2LID(_sid) (((u_int32_t) (_sid)) & 0xffffffff)
+
+extern int crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard);
+extern int crypto_freesession(u_int64_t sid);
+#define CRYPTOCAP_F_HARDWARE CRYPTO_FLAG_HARDWARE
+#define CRYPTOCAP_F_SOFTWARE CRYPTO_FLAG_SOFTWARE
+#define CRYPTOCAP_F_SYNC 0x04000000 /* operates synchronously */
+extern int32_t crypto_get_driverid(device_t dev, int flags);
+extern int crypto_find_driver(const char *);
+extern device_t crypto_find_device_byhid(int hid);
+extern int crypto_getcaps(int hid);
+extern int crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
+ u_int32_t flags);
+extern int crypto_kregister(u_int32_t, int, u_int32_t);
+extern int crypto_unregister(u_int32_t driverid, int alg);
+extern int crypto_unregister_all(u_int32_t driverid);
+extern int crypto_dispatch(struct cryptop *crp);
+extern int crypto_kdispatch(struct cryptkop *);
+#define CRYPTO_SYMQ 0x1
+#define CRYPTO_ASYMQ 0x2
+extern int crypto_unblock(u_int32_t, int);
+extern void crypto_done(struct cryptop *crp);
+extern void crypto_kdone(struct cryptkop *);
+extern int crypto_getfeat(int *);
+
+extern void crypto_freereq(struct cryptop *crp);
+extern struct cryptop *crypto_getreq(int num);
+
+extern int crypto_usercrypto; /* userland may do crypto requests */
+extern int crypto_userasymcrypto; /* userland may do asym crypto reqs */
+extern int crypto_devallowsoft; /* only use hardware crypto */
+
+/*
+ * random number support, crypto_unregister_all will unregister
+ */
+extern int crypto_rregister(u_int32_t driverid,
+ int (*read_random)(void *arg, u_int32_t *buf, int len), void *arg);
+extern int crypto_runregister_all(u_int32_t driverid);
+
+/*
+ * Crypto-related utility routines used mainly by drivers.
+ *
+ * XXX these don't really belong here; but for now they're
+ * kept apart from the rest of the system.
+ */
+struct uio;
+extern void cuio_copydata(struct uio* uio, int off, int len, caddr_t cp);
+extern void cuio_copyback(struct uio* uio, int off, int len, caddr_t cp);
+extern struct iovec *cuio_getptr(struct uio *uio, int loc, int *off);
+
+extern void crypto_copyback(int flags, caddr_t buf, int off, int size,
+ caddr_t in);
+extern void crypto_copydata(int flags, caddr_t buf, int off, int size,
+ caddr_t out);
+extern int crypto_apply(int flags, caddr_t buf, int off, int len,
+ int (*f)(void *, void *, u_int), void *arg);
+
+#endif /* __KERNEL__ */
+#endif /* _CRYPTO_CRYPTO_H_ */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 29b6353..2573b1e 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -410,6 +410,9 @@
struct vm_area_struct;
struct vfsmount;
struct cred;
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+struct socket;
+#endif
extern void __init inode_init(void);
extern void __init inode_init_early(void);
@@ -1615,6 +1618,10 @@
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+ ssize_t (*splice_from_socket)(struct file *file, struct socket *sock,
+ loff_t __user *ppos, size_t count);
+#endif
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
@@ -2434,6 +2441,10 @@
struct pipe_inode_info *, size_t, unsigned int);
extern ssize_t generic_file_splice_write(struct pipe_inode_info *,
struct file *, loff_t *, size_t, unsigned int);
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+extern ssize_t comcerto_file_splice_write(struct pipe_inode_info *,
+ struct file *, loff_t *, size_t, unsigned int);
+#endif
extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe,
struct file *out, loff_t *, size_t len, unsigned int flags);
extern long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 3a76faf..e371b05 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -141,7 +141,12 @@
/* Flag - indicates that the buffer will be suitable for DMA. Ignored on some
platforms, used as appropriate on others */
+#if defined(CONFIG_COMCERTO_ZONE_DMA_NCNB)
+#define GFP_DMA __GFP_DMA32
+#define GFP_DMA_NCNB __GFP_DMA
+#else
#define GFP_DMA __GFP_DMA
+#endif
/* 4GB DMA on some platforms */
#define GFP_DMA32 __GFP_DMA32
diff --git a/include/linux/if.h b/include/linux/if.h
index db20bd4..d8498f6 100644
--- a/include/linux/if.h
+++ b/include/linux/if.h
@@ -52,6 +52,9 @@
#define IFF_DORMANT 0x20000 /* driver signals dormant */
#define IFF_ECHO 0x40000 /* echo sent packets */
+#if defined(CONFIG_ARCH_COMCERTO)
+#define IFF_WIFI_OFLD 0x80000 /* Offload interface */
+#endif
#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
index 6d722f4..5e77aad 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -92,6 +92,8 @@
#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */
#define ARPHRD_CAIF 822 /* CAIF media type */
+#define ARPHRD_IPV6_IPV6_TUNNEL ARPHRD_ETHER
+
#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */
#define ARPHRD_NONE 0xFFFE /* zero header length */
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index dd3f201..7a8bb26 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -106,6 +106,22 @@
typedef int br_should_route_hook_t(struct sk_buff *skb);
extern br_should_route_hook_t __rcu *br_should_route_hook;
+#if defined(CONFIG_ARCH_COMCERTO)
+struct brevent_fdb_update{
+ char * mac_addr;
+ struct net_device * dev;
+};
+
+enum brevent_notif_type {
+ BREVENT_PORT_DOWN = 1, /* arg is struct net_device ptr */
+ BREVENT_FDB_UPDATE /* arg is struct brevent_fdb_update ptr */
+};
+
+int register_brevent_notifier(struct notifier_block *nb);
+int unregister_brevent_notifier(struct notifier_block *nb);
+int call_brevent_notifiers(unsigned long val, void *v);
+#endif
+
#endif
#endif
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index e473003..e85de7c 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -34,6 +34,7 @@
#define ETH_DATA_LEN 1500 /* Max. octets in payload */
#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
#define ETH_FCS_LEN 4 /* Octets in the FCS */
+#define ETH_IPHLEN 2 /* EtherIP header length */
/*
* These are the defined Ethernet Protocol ID's.
diff --git a/include/linux/if_ppp.h b/include/linux/if_ppp.h
index c9ad383..38f0d79 100644
--- a/include/linux/if_ppp.h
+++ b/include/linux/if_ppp.h
@@ -161,6 +161,9 @@
#define PPPIOCATTCHAN _IOW('t', 56, int) /* attach to ppp channel */
#define PPPIOCGCHAN _IOR('t', 55, int) /* get ppp channel number */
#define PPPIOCGL2TPSTATS _IOR('t', 54, struct pppol2tp_ioc_stats)
+#if defined(CONFIG_COMCERTO_FP)
+#define PPPIOCSFPPIDLE _IOW('t', 53, struct ppp_idle) /* Set the FPP stats */
+#endif
#define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0)
#define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */
diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
index 16b92d0..9453ec2 100644
--- a/include/linux/if_tunnel.h
+++ b/include/linux/if_tunnel.h
@@ -21,6 +21,10 @@
#define SIOCADD6RD (SIOCDEVPRIVATE + 9)
#define SIOCDEL6RD (SIOCDEVPRIVATE + 10)
#define SIOCCHG6RD (SIOCDEVPRIVATE + 11)
+#define SIOCGET4RD (SIOCDEVPRIVATE + 12)
+#define SIOCADD4RD (SIOCDEVPRIVATE + 13)
+#define SIOCDEL4RD (SIOCDEVPRIVATE + 14)
+#define SIOCCHG4RD (SIOCDEVPRIVATE + 15)
#define GRE_CSUM __cpu_to_be16(0x8000)
#define GRE_ROUTING __cpu_to_be16(0x4000)
@@ -63,6 +67,20 @@
__u16 relay_prefixlen;
};
+/* ip6 tnl 4rd parm -start */
+struct ip6_tnl_4rd {
+ __be32 prefix;
+ struct in6_addr relay_prefix;
+ struct in6_addr relay_suffix;
+ __u16 prefixlen;
+ __u16 relay_prefixlen;
+ __u16 relay_suffixlen;
+ __u16 psid_offsetlen;
+ __u16 eabit_len;
+ __u16 entry_num;
+};
+/* ip6 tnl 4rd parm -end */
+
enum {
IFLA_GRE_UNSPEC,
IFLA_GRE_LINK,
diff --git a/include/linux/in.h b/include/linux/in.h
index 01129c0..3422759 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -41,6 +41,7 @@
IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
IPPROTO_AH = 51, /* Authentication Header protocol */
IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */
+ IPPROTO_ETHERIP = 97, /* IP option for EtherIP tunnel (rfc 3378) */
IPPROTO_PIM = 103, /* Protocol Independent Multicast */
IPPROTO_COMP = 108, /* Compression Header protocol */
diff --git a/include/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h
index bf22b03..554e24e 100644
--- a/include/linux/ip6_tunnel.h
+++ b/include/linux/ip6_tunnel.h
@@ -31,4 +31,51 @@
struct in6_addr raddr; /* remote tunnel end-point address */
};
+
+struct ip6_4rd_map_msg {
+ __u32 reset;
+ __u32 ifindex;
+ __be32 prefix;
+ __u16 prefixlen;
+ struct in6_addr relay_prefix;
+ struct in6_addr relay_suffix;
+ __u16 relay_prefixlen;
+ __u16 relay_suffixlen;
+ __u16 psid_offsetlen;
+ __u16 eabit_len;
+ __u16 entry_num;
+};
+
+struct ip6_tnl_4rd_map_rule {
+ __be32 prefix;
+ __u16 prefixlen;
+ struct in6_addr relay_prefix;
+ struct in6_addr relay_suffix;
+ __u16 relay_prefixlen;
+ __u16 relay_suffixlen;
+ __u16 psid_offsetlen;
+ __u16 eabit_len;
+ __u16 entry_num;
+ struct list_head mr_list;
+};
+
+#ifdef __KERNEL__
+struct ip6_tnl_4rd_parm {
+ __be32 prefix;
+ struct in6_addr relay_prefix;
+ struct in6_addr relay_suffix;
+ __u16 prefixlen;
+ __u16 relay_prefixlen;
+ __u16 relay_suffixlen;
+ __be32 laddr4;
+ __u16 port_set_id;
+ __u16 port_set_id_len;
+ __u16 psid_offsetlen;
+ __u16 eabit_len;
+
+ struct list_head map_list;
+ rwlock_t map_lock;
+};
+#endif
+
#endif
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index c41d727..24b73c0 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -19,6 +19,7 @@
#define APOLLO_MOUSE_MINOR 7
#define PC110PAD_MINOR 9
/*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */
+#define CRYPTODEV_MINOR 70 /* /dev/crypto */
#define WATCHDOG_MINOR 130 /* Watchdog timer */
#define TEMP_MINOR 131 /* Temperature Sensor */
#define RTC_MINOR 135
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ae451f8..b11401d 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1005,6 +1005,11 @@
/* mask of features inheritable by VLAN devices */
u32 vlan_features;
+#if defined(CONFIG_ARCH_COMCERTO)
+ /* This is pointing to network device that offload WiFi data to PFE */
+ struct net_device *wifi_offload_dev;
+#endif
+
/* Net device feature bits; if you change something,
* also update netdev_features_strings[] in ethtool.c */
@@ -1620,6 +1625,9 @@
extern int dev_close(struct net_device *dev);
extern void dev_disable_lro(struct net_device *dev);
extern int dev_queue_xmit(struct sk_buff *skb);
+#if defined(CONFIG_ARCH_COMCERTO)
+extern int original_dev_queue_xmit(struct sk_buff *skb);
+#endif
extern int register_netdevice(struct net_device *dev);
extern void unregister_netdevice_queue(struct net_device *dev,
struct list_head *head);
@@ -2048,6 +2056,9 @@
extern int netif_rx(struct sk_buff *skb);
extern int netif_rx_ni(struct sk_buff *skb);
extern int netif_receive_skb(struct sk_buff *skb);
+#if defined(CONFIG_ARCH_COMCERTO)
+extern int capture_receive_skb(struct sk_buff *skb);
+#endif
extern gro_result_t dev_gro_receive(struct napi_struct *napi,
struct sk_buff *skb);
extern gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb);
diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild
index a1b410c..e91c119 100644
--- a/include/linux/netfilter/Kbuild
+++ b/include/linux/netfilter/Kbuild
@@ -45,6 +45,7 @@
header-y += xt_helper.h
header-y += xt_iprange.h
header-y += xt_ipvs.h
+header-y += xt_layer7.h
header-y += xt_length.h
header-y += xt_limit.h
header-y += xt_mac.h
diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
index 0d3dd66..3ca1f46 100644
--- a/include/linux/netfilter/nf_conntrack_common.h
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -83,6 +83,16 @@
/* Conntrack is a fake untracked entry */
IPS_UNTRACKED_BIT = 12,
IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT),
+
+ /* Connection cannot expire */
+ IPS_PERMANENT_BIT = 13,
+ IPS_PERMANENT = (1 << IPS_PERMANENT_BIT),
+
+#ifdef CONFIG_COMCERTO_FP
+ /* Connection is assured by DPI application */
+ IPS_DPI_ALLOWED_BIT = 14,
+ IPS_DPI_ALLOWED = (1 << IPS_DPI_ALLOWED_BIT),
+#endif
};
/* Connection tracking event types */
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h
index debf1ae..fb0dd93 100644
--- a/include/linux/netfilter/nfnetlink_conntrack.h
+++ b/include/linux/netfilter/nfnetlink_conntrack.h
@@ -43,6 +43,8 @@
CTA_ZONE,
CTA_SECCTX,
CTA_TIMESTAMP,
+ CTA_COMCERTO_FP_ORIG,
+ CTA_COMCERTO_FP_REPLY,
__CTA_MAX
};
#define CTA_MAX (__CTA_MAX - 1)
@@ -190,4 +192,13 @@
};
#define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1)
+enum ctattr_comcerto_fp {
+ CTA_COMCERTO_FP_UNSPEC,
+ CTA_COMCERTO_FP_MARK,
+ CTA_COMCERTO_FP_IFINDEX,
+ CTA_COMCERTO_FP_IIF,
+ __CTA_COMCERTO_FP_MAX
+};
+#define CTA_COMCERTO_FP_MAX (__CTA_COMCERTO_FP_MAX - 1)
+
#endif /* _IPCONNTRACK_NETLINK_H */
diff --git a/include/linux/netfilter_ipv4/nf_nat.h b/include/linux/netfilter_ipv4/nf_nat.h
index 7a861d0..a7275b3 100644
--- a/include/linux/netfilter_ipv4/nf_nat.h
+++ b/include/linux/netfilter_ipv4/nf_nat.h
@@ -7,6 +7,7 @@
#define IP_NAT_RANGE_PROTO_SPECIFIED 2
#define IP_NAT_RANGE_PROTO_RANDOM 4
#define IP_NAT_RANGE_PERSISTENT 8
+#define IP_NAT_RANGE_4RD_NAPT 16
/* The protocol-specific manipulable parts of the tuple. */
union nf_conntrack_man_proto {
diff --git a/include/linux/netfilter_ipv6/Kbuild b/include/linux/netfilter_ipv6/Kbuild
index bd095bc..b88c005 100644
--- a/include/linux/netfilter_ipv6/Kbuild
+++ b/include/linux/netfilter_ipv6/Kbuild
@@ -1,6 +1,7 @@
header-y += ip6_tables.h
header-y += ip6t_HL.h
header-y += ip6t_LOG.h
+header-y += ip6t_NPT.h
header-y += ip6t_REJECT.h
header-y += ip6t_ah.h
header-y += ip6t_frag.h
diff --git a/include/linux/netfilter_ipv6/ip6t_NPT.h b/include/linux/netfilter_ipv6/ip6t_NPT.h
new file mode 100644
index 0000000..f763355
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_NPT.h
@@ -0,0 +1,16 @@
+#ifndef __NETFILTER_IP6T_NPT
+#define __NETFILTER_IP6T_NPT
+
+#include <linux/types.h>
+#include <linux/netfilter.h>
+
+struct ip6t_npt_tginfo {
+ union nf_inet_addr src_pfx;
+ union nf_inet_addr dst_pfx;
+ __u8 src_pfx_len;
+ __u8 dst_pfx_len;
+ /* Used internally by the kernel */
+ __sum16 adjustment;
+};
+
+#endif /* __NETFILTER_IP6T_NPT */
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 8374d29..6f65e12 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -26,8 +26,23 @@
#define NETLINK_ECRYPTFS 19
#define NETLINK_RDMA 20
#define NETLINK_CRYPTO 21 /* Crypto layer */
+#define NETLINK_FF 30
+#define NETLINK_VOP 31
+#define NETLINK_KEY 32
+#define NETLINK_L2FLOW 33
-#define MAX_LINKS 32
+#define NETLINK_VOIP 34
+#define NETLINK_DTAM 35
+#define NETLINK_MCH 36
+#define NETLINK_CONFIG 37
+#define NETLINK_GENPLAY 38
+#define NETLINK_COMADEBUG 39
+#define NETLINK_EEPROM 40
+#define NETLINK_SS7 41
+#define NETLINK_DSR 42
+#define NETLINK_MMI 43
+
+#define MAX_LINKS 44
struct sockaddr_nl {
__kernel_sa_family_t nl_family; /* AF_NETLINK */
diff --git a/include/linux/ocf-compat.h b/include/linux/ocf-compat.h
new file mode 100644
index 0000000..4ad1223
--- /dev/null
+++ b/include/linux/ocf-compat.h
@@ -0,0 +1,372 @@
+#ifndef _BSD_COMPAT_H_
+#define _BSD_COMPAT_H_ 1
+/****************************************************************************/
+/*
+ * Provide compat routines for older linux kernels and BSD kernels
+ *
+ * Written by David McCullough <david_mccullough@mcafee.com>
+ * Copyright (C) 2010 David McCullough <david_mccullough@mcafee.com>
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this file
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ */
+/****************************************************************************/
+#ifdef __KERNEL__
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+
+/*
+ * fake some BSD driver interface stuff specifically for OCF use
+ */
+
+typedef struct ocf_device *device_t;
+
+typedef struct {
+ int (*cryptodev_newsession)(device_t dev, u_int32_t *sidp, struct cryptoini *cri);
+ int (*cryptodev_freesession)(device_t dev, u_int64_t tid);
+ int (*cryptodev_process)(device_t dev, struct cryptop *crp, int hint);
+ int (*cryptodev_kprocess)(device_t dev, struct cryptkop *krp, int hint);
+} device_method_t;
+#define DEVMETHOD(id, func) id: func
+
+struct ocf_device {
+ char name[32]; /* the driver name */
+ char nameunit[32]; /* the driver name + HW instance */
+ int unit;
+ device_method_t methods;
+ void *softc;
+};
+
+#define CRYPTODEV_NEWSESSION(dev, sid, cri) \
+ ((*(dev)->methods.cryptodev_newsession)(dev,sid,cri))
+#define CRYPTODEV_FREESESSION(dev, sid) \
+ ((*(dev)->methods.cryptodev_freesession)(dev, sid))
+#define CRYPTODEV_PROCESS(dev, crp, hint) \
+ ((*(dev)->methods.cryptodev_process)(dev, crp, hint))
+#define CRYPTODEV_KPROCESS(dev, krp, hint) \
+ ((*(dev)->methods.cryptodev_kprocess)(dev, krp, hint))
+
+#define device_get_name(dev) ((dev)->name)
+#define device_get_nameunit(dev) ((dev)->nameunit)
+#define device_get_unit(dev) ((dev)->unit)
+#define device_get_softc(dev) ((dev)->softc)
+
+#define softc_device_decl \
+ struct ocf_device _device; \
+ device_t
+
+#define softc_device_init(_sc, _name, _unit, _methods) \
+ if (1) {\
+ strncpy((_sc)->_device.name, _name, sizeof((_sc)->_device.name) - 1); \
+ snprintf((_sc)->_device.nameunit, sizeof((_sc)->_device.name), "%s%d", _name, _unit); \
+ (_sc)->_device.unit = _unit; \
+ (_sc)->_device.methods = _methods; \
+ (_sc)->_device.softc = (void *) _sc; \
+ *(device_t *)((softc_get_device(_sc))+1) = &(_sc)->_device; \
+ } else
+
+#define softc_get_device(_sc) (&(_sc)->_device)
+
+/*
+ * iomem support for 2.4 and 2.6 kernels
+ */
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define ocf_iomem_t unsigned long
+
+/*
+ * implement simple workqueue like support for older kernels
+ */
+
+#include <linux/tqueue.h>
+
+#define work_struct tq_struct
+
+#define INIT_WORK(wp, fp, ap) \
+ do { \
+ (wp)->sync = 0; \
+ (wp)->routine = (fp); \
+ (wp)->data = (ap); \
+ } while (0)
+
+#define schedule_work(wp) \
+ do { \
+ queue_task((wp), &tq_immediate); \
+ mark_bh(IMMEDIATE_BH); \
+ } while (0)
+
+#define flush_scheduled_work() run_task_queue(&tq_immediate)
+
+#else
+#define ocf_iomem_t void __iomem *
+
+#include <linux/workqueue.h>
+
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+#include <linux/fdtable.h>
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+#define files_fdtable(files) (files)
+#endif
+
+#ifdef MODULE_PARM
+#undef module_param /* just in case */
+#define module_param(a,b,c) MODULE_PARM(a,"i")
+#endif
+
+#define bzero(s,l) memset(s,0,l)
+#define bcopy(s,d,l) memcpy(d,s,l)
+#define bcmp(x, y, l) memcmp(x,y,l)
+
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+#define device_printf(dev, a...) ({ \
+ printk("%s: ", device_get_nameunit(dev)); printk(a); \
+ })
+
+#undef printf
+#define printf(fmt...) printk(fmt)
+
+#define KASSERT(c,p) if (!(c)) { printk p ; } else
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define ocf_daemonize(str) \
+ daemonize(); \
+ spin_lock_irq(¤t->sigmask_lock); \
+ sigemptyset(¤t->blocked); \
+ recalc_sigpending(current); \
+ spin_unlock_irq(¤t->sigmask_lock); \
+ sprintf(current->comm, str);
+#else
+#define ocf_daemonize(str) daemonize(str);
+#endif
+
+#define TAILQ_INSERT_TAIL(q,d,m) list_add_tail(&(d)->m, (q))
+#define TAILQ_EMPTY(q) list_empty(q)
+#define TAILQ_FOREACH(v, q, m) list_for_each_entry(v, q, m)
+
+#define read_random(p,l) get_random_bytes(p,l)
+
+#define DELAY(x) ((x) > 2000 ? mdelay((x)/1000) : udelay(x))
+#define strtoul simple_strtoul
+
+#define pci_get_vendor(dev) ((dev)->vendor)
+#define pci_get_device(dev) ((dev)->device)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define pci_set_consistent_dma_mask(dev, mask) (0)
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+#define pci_dma_sync_single_for_cpu pci_dma_sync_single
+#endif
+
+#ifndef DMA_32BIT_MASK
+#define DMA_32BIT_MASK 0x00000000ffffffffULL
+#endif
+
+#ifndef htole32
+#define htole32(x) cpu_to_le32(x)
+#endif
+#ifndef htobe32
+#define htobe32(x) cpu_to_be32(x)
+#endif
+#ifndef htole16
+#define htole16(x) cpu_to_le16(x)
+#endif
+#ifndef htobe16
+#define htobe16(x) cpu_to_be16(x)
+#endif
+
+/* older kernels don't have these */
+
+#include <asm/irq.h>
+#if !defined(IRQ_NONE) && !defined(IRQ_RETVAL)
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_WAKE_THREAD
+#define IRQ_RETVAL
+#define irqreturn_t void
+typedef irqreturn_t (*irq_handler_t)(int irq, void *arg, struct pt_regs *regs);
+#endif
+#ifndef IRQF_SHARED
+#define IRQF_SHARED SA_SHIRQ
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+# define strlcpy(dest,src,len) \
+ ({strncpy(dest,src,(len)-1); ((char *)dest)[(len)-1] = '\0'; })
+#endif
+
+#ifndef MAX_ERRNO
+#define MAX_ERRNO 4095
+#endif
+#ifndef IS_ERR_VALUE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,5)
+#include <linux/err.h>
+#endif
+#ifndef IS_ERR_VALUE
+#define IS_ERR_VALUE(x) ((unsigned long)(x) >= (unsigned long)-MAX_ERRNO)
+#endif
+#endif
+
+/*
+ * common debug for all
+ */
+#if 1
+#define dprintk(a...) do { if (debug) printk(a); } while(0)
+#else
+#define dprintk(a...)
+#endif
+
+#ifndef SLAB_ATOMIC
+/* Changed in 2.6.20, must use GFP_ATOMIC now */
+#define SLAB_ATOMIC GFP_ATOMIC
+#endif
+
+/*
+ * need some additional support for older kernels */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,2)
+#define pci_register_driver_compat(driver, rc) \
+ do { \
+ if ((rc) > 0) { \
+ (rc) = 0; \
+ } else if (rc == 0) { \
+ (rc) = -ENODEV; \
+ } else { \
+ pci_unregister_driver(driver); \
+ } \
+ } while (0)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+#define pci_register_driver_compat(driver,rc) ((rc) = (rc) < 0 ? (rc) : 0)
+#else
+#define pci_register_driver_compat(driver,rc)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+
+static inline void sg_set_page(struct scatterlist *sg, struct page *page,
+ unsigned int len, unsigned int offset)
+{
+ sg->page = page;
+ sg->offset = offset;
+ sg->length = len;
+}
+
+static inline void *sg_virt(struct scatterlist *sg)
+{
+ return page_address(sg->page) + sg->offset;
+}
+
+#define sg_init_table(sg, n)
+
+#define sg_mark_end(sg)
+
+#endif
+
+#ifndef late_initcall
+#define late_initcall(init) module_init(init)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) || !defined(CONFIG_SMP)
+#define ocf_for_each_cpu(cpu) for ((cpu) = 0; (cpu) == 0; (cpu)++)
+#else
+#define ocf_for_each_cpu(cpu) for_each_present_cpu(cpu)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#include <linux/sched.h>
+#define kill_proc(p,s,v) send_sig(s,find_task_by_vpid(p),0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4)
+
+struct ocf_thread {
+ struct task_struct *task;
+ int (*func)(void *arg);
+ void *arg;
+};
+
+/* thread startup helper func */
+static inline int ocf_run_thread(void *arg)
+{
+ struct ocf_thread *t = (struct ocf_thread *) arg;
+ if (!t)
+ return -1; /* very bad */
+ t->task = current;
+ daemonize();
+ spin_lock_irq(¤t->sigmask_lock);
+ sigemptyset(¤t->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+ return (*t->func)(t->arg);
+}
+
+#define kthread_create(f,a,fmt...) \
+ ({ \
+ struct ocf_thread t; \
+ pid_t p; \
+ t.task = NULL; \
+ t.func = (f); \
+ t.arg = (a); \
+ p = kernel_thread(ocf_run_thread, &t, CLONE_FS|CLONE_FILES); \
+ while (p != (pid_t) -1 && t.task == NULL) \
+ schedule(); \
+ if (t.task) \
+ snprintf(t.task->comm, sizeof(t.task->comm), fmt); \
+ (t.task); \
+ })
+
+#define kthread_bind(t,cpu) /**/
+
+#define kthread_should_stop() (strcmp(current->comm, "stopping") == 0)
+
+#define kthread_stop(t) \
+ ({ \
+ strcpy((t)->comm, "stopping"); \
+ kill_proc((t)->pid, SIGTERM, 1); \
+ do { \
+ schedule(); \
+ } while (kill_proc((t)->pid, SIGTERM, 1) == 0); \
+ })
+
+#else
+#include <linux/kthread.h>
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
+#define skb_frag_page(x) ((x)->page)
+#endif
+
+#endif /* __KERNEL__ */
+
+/****************************************************************************/
+#endif /* _BSD_COMPAT_H_ */
diff --git a/include/linux/of.h b/include/linux/of.h
index 4948552..7b3ed80 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -336,6 +336,22 @@
#define of_match_node(_matches, _node) NULL
#endif /* CONFIG_OF */
+/**
+* of_property_read_bool - Findfrom a property
+* @np: device node from which the property value is to be read.
+* @propname: name of the property to be searched.
+*
+* Search for a property in a device node.
+* Returns true if the property exist false otherwise.
+*/
+static inline bool of_property_read_bool(const struct device_node *np,
+ const char *propname)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+
+ return prop ? true : false;
+}
+
static inline int of_property_read_u32(const struct device_node *np,
const char *propname,
u32 *out_value)
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index e90a673..35ff525 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -104,6 +104,9 @@
#ifdef CONFIG_MEMORY_FAILURE
PG_hwpoison, /* hardware poisoned page. Don't touch */
#endif
+#ifdef CONFIG_RAID_ZERO_COPY
+ PG_constant, /* const page not modified during raid5 io */
+#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
PG_compound_lock,
#endif
@@ -193,6 +196,16 @@
struct page; /* forward declaration */
+#ifdef CONFIG_RAID_ZERO_COPY
+#define PageConstant(page) test_bit(PG_constant, &(page)->flags)
+#define SetPageConstant(page) set_bit(PG_constant, &(page)->flags)
+#define ClearPageConstant(page) clear_bit(PG_constant, &(page->flags))
+#define TestSetPageConstant(page) test_and_set_bit(PG_constant, &(page)->flags)
+extern void clear_page_constant(struct page *page);
+#endif
+
+
+
TESTPAGEFLAG(Locked, locked)
PAGEFLAG(Error, error) TESTCLEARFLAG(Error, error)
PAGEFLAG(Referenced, referenced) TESTCLEARFLAG(Referenced, referenced)
diff --git a/include/linux/pfkeyv2.h b/include/linux/pfkeyv2.h
index 0b80c80..0456669 100644
--- a/include/linux/pfkeyv2.h
+++ b/include/linux/pfkeyv2.h
@@ -268,6 +268,7 @@
#define SADB_SAFLAGS_NOPMTUDISC 0x20000000
#define SADB_SAFLAGS_DECAP_DSCP 0x40000000
#define SADB_SAFLAGS_NOECN 0x80000000
+#define SADB_SAFLAGS_ESN 0x01000000
/* Security Association states */
#define SADB_SASTATE_LARVAL 0
diff --git a/include/linux/random.h b/include/linux/random.h
index 8f74538..0ff31a9 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -34,6 +34,30 @@
/* Clear the entropy pool and associated counters. (Superuser only.) */
#define RNDCLEARPOOL _IO( 'R', 0x06 )
+#ifdef CONFIG_FIPS_RNG
+
+/* Size of seed value - equal to AES blocksize */
+#define AES_BLOCK_SIZE_BYTES 16
+#define SEED_SIZE_BYTES AES_BLOCK_SIZE_BYTES
+/* Size of AES key */
+#define KEY_SIZE_BYTES 16
+
+/* ioctl() structure used by FIPS 140-2 Tests */
+struct rand_fips_test {
+ unsigned char key[KEY_SIZE_BYTES]; /* Input */
+ unsigned char datetime[SEED_SIZE_BYTES]; /* Input */
+ unsigned char seed[SEED_SIZE_BYTES]; /* Input */
+ unsigned char result[SEED_SIZE_BYTES]; /* Output */
+};
+
+/* FIPS 140-2 RNG Variable Seed Test. (Superuser only.) */
+#define RNDFIPSVST _IOWR('R', 0x10, struct rand_fips_test)
+
+/* FIPS 140-2 RNG Monte Carlo Test. (Superuser only.) */
+#define RNDFIPSMCT _IOWR('R', 0x11, struct rand_fips_test)
+
+#endif /* #ifdef CONFIG_FIPS_RNG */
+
struct rand_pool_info {
int entropy_count;
int buf_size;
@@ -54,6 +78,10 @@
unsigned int value);
extern void add_interrupt_randomness(int irq);
+extern void random_input_words(__u32 *buf, size_t wordcount, int ent_count);
+extern int random_input_wait(void);
+#define HAS_RANDOM_INPUT_WAIT 1
+
extern void get_random_bytes(void *buf, int nbytes);
void generate_random_uuid(unsigned char uuid_out[16]);
diff --git a/include/linux/rtc/rtc-c2k.h b/include/linux/rtc/rtc-c2k.h
new file mode 100644
index 0000000..e4d7af9
--- /dev/null
+++ b/include/linux/rtc/rtc-c2k.h
@@ -0,0 +1,24 @@
+
+/*
+ REGISTER OFFSET_ADDRESS R/W DESCRIPTION RESET VALUE
+*/
+
+#define C2K_RTC_RTCCON 0X00 /* R/W RTC CONTROL REGISTER 0X00 */
+#define C2K_RTC_RTCALM 0X04 /* R/W RTC ALARM CONTROL REGISTER 0X00 */
+#define C2K_RTC_ALMSEC 0X08 /* R/W ALARM SECOND DATA REGISTER 0X00 */
+#define C2K_RTC_ALMMIN 0X0C /* R/W ALARM MINUTE DATA REGISTER 0X00 */
+#define C2K_RTC_ALMHOUR 0X10 /* R/W ALARM HOUR DATA REGISTER 0X00 */
+#define C2K_RTC_ALMDATE 0X14 /* R/W ALARM DATE DATA REGISTER 0X00 */
+#define C2K_RTC_ALMDAY 0X18 /* R/W ALARM DAY OF WEEK DATA REGISTER 0X00 */
+#define C2K_RTC_ALMMON 0X1C /* R/W ALARM MONTH DATA REGISTER 0X00 */
+#define C2K_RTC_ALMYEAR 0X20 /* R/W ALARM YEAR DATA REGISTER 0X00 */
+#define C2K_RTC_BCDSEC 0X24 /* R/W BCD SECOND REGISTER */
+#define C2K_RTC_BCDMIN 0X28 /* R/W BCD MINUTE REGISTER */
+#define C2K_RTC_BCDHOUR 0X2C /* R/W BCD HOUR REGISTER */
+#define C2K_RTC_BCDDATE 0X30 /* R/W BCD DATE REGISTER */
+#define C2K_RTC_BCDDAY 0X34 /* R/W BCD DAY OF WEEK REGISTER */
+#define C2K_RTC_BCDMON 0X38 /* R/W BCD MONTH REGISTER */
+#define C2K_RTC_BCDYEAR 0X3C /* R/W BCD YEAR REGISTER */
+#define C2K_RTC_RTCIM 0X40 /* R/W RTC INTERRUPT MODE REGISTER 0X00 */
+#define C2K_RTC_RTCPEND 0X44 /* R/W RTC INTERRUPT PENDING REGISTER 0X00 */
+
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 8e872ea..6c2fb2f 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -119,7 +119,12 @@
#define RTM_GETDCB RTM_GETDCB
RTM_SETDCB,
#define RTM_SETDCB RTM_SETDCB
-
+ RTM_NEW4RD = 80,
+#define RTM_NEW4RD RTM_NEW4RD
+ RTM_DEL4RD,
+#define RTM_DEL4RD RTM_DEL4RD
+ RTM_GET4RD,
+#define RTM_GET4RD RTM_GET4RD
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
@@ -621,6 +626,9 @@
u32 group, struct nlmsghdr *nlh, gfp_t flags);
extern void rtnl_set_sk_err(struct net *net, u32 group, int error);
extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics);
+#ifdef CONFIG_ARCH_COMCERTO
+extern int rtnetlink_put_metrics_2(struct sk_buff *skb, u32 *metrics, struct dst_entry *dst);
+#endif
extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst,
u32 id, u32 ts, u32 tsage, long expires,
u32 error);
@@ -742,6 +750,7 @@
__rta_reserve(skb, attrtype, attrlen); })
extern void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change);
+extern void __rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, gfp_t flags);
/* RTNL is used as a global lock for all changes to network configuration */
extern void rtnl_lock(void);
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 53dc7e7..4b42eb7 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -414,6 +414,9 @@
kmemcheck_bitfield_end(flags1);
__be16 protocol;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ __u32 ipsec_offload;
+#endif
void (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
@@ -468,6 +471,11 @@
sk_buff_data_t end;
unsigned char *head,
*data;
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+ unsigned char *mspd_data;
+ __u32 mspd_len;
+ __u32 mspd_ofst;
+#endif
unsigned int truesize;
atomic_t users;
};
@@ -550,6 +558,17 @@
return __alloc_skb(size, priority, 1, NUMA_NO_NODE);
}
+#if defined(CONFIG_ARCH_COMCERTO)
+extern struct sk_buff *__alloc_skb_header(unsigned int size, void* data, gfp_t gfp_mask,
+ int fclone, int node);
+static inline struct sk_buff *alloc_skb_header(unsigned int size,
+ void* data,
+ gfp_t priority)
+{
+ return __alloc_skb_header(size, data, priority, 0, NUMA_NO_NODE);
+}
+#endif
+
extern void skb_recycle(struct sk_buff *skb);
extern bool skb_recycle_check(struct sk_buff *skb, int skb_size);
@@ -2085,6 +2104,11 @@
extern int skb_copy_datagram_iovec(const struct sk_buff *from,
int offset, struct iovec *to,
int size);
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+extern int skb_copy_datagram_to_kernel_iovec(const struct sk_buff *from,
+ int offset, struct iovec *to,
+ int size);
+#endif
extern int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
int hlen,
struct iovec *iov);
diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h
index d00e0ba..334f381 100644
--- a/include/linux/slab_def.h
+++ b/include/linux/slab_def.h
@@ -148,7 +148,7 @@
return NULL;
found:
#ifdef CONFIG_ZONE_DMA
- if (flags & GFP_DMA)
+ if (flags & __GFP_DMA)
cachep = malloc_sizes[i].cs_dmacachep;
else
#endif
diff --git a/include/linux/socket.h b/include/linux/socket.h
index ad919e0..ce12aa5 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -195,7 +195,10 @@
#define AF_CAIF 37 /* CAIF sockets */
#define AF_ALG 38 /* Algorithm sockets */
#define AF_NFC 39 /* NFC sockets */
-#define AF_MAX 40 /* For now.. */
+#define AF_COMA 40 /* COMA sockets */
+#define AF_MAX 41 /* For now.. */
+
+
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -238,8 +241,10 @@
#define PF_CAIF AF_CAIF
#define PF_ALG AF_ALG
#define PF_NFC AF_NFC
+#define PF_COMA AF_COMA
#define PF_MAX AF_MAX
+
/* Maximum queue length specifiable by listen. */
#define SOMAXCONN 128
@@ -266,6 +271,10 @@
#define MSG_MORE 0x8000 /* Sender will send more */
#define MSG_WAITFORONE 0x10000 /* recvmmsg(): block until 1+ packets avail */
#define MSG_SENDPAGE_NOTLAST 0x20000 /* sendpage() internal : not the last page */
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+#define MSG_KERNSPACE 0x40000
+#define MSG_NOCATCHSIG 0x80000
+#endif
#define MSG_EOF MSG_FIN
#define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exit for file
@@ -332,6 +341,9 @@
int offset, int len);
extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr);
extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+extern void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len);
+#endif
struct timespec;
diff --git a/include/linux/spi/comcerto_spi.h b/include/linux/spi/comcerto_spi.h
new file mode 100644
index 0000000..f5b5da6
--- /dev/null
+++ b/include/linux/spi/comcerto_spi.h
@@ -0,0 +1,211 @@
+/*
+ * linux/drivers/spi/busses/comcerto_spi.h
+ *
+ * Copyright (C) Mindspeed Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#ifndef _COMCERTO_SPI_H
+#define _COMCERTO_SPI_H
+
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+//#include <asm-arm/types.h>
+#include <linux/types.h>
+
+#define comcerto_SPI_NAME "comcerto_spi"
+
+#define CFG_HZ_CLOCK 165000000 /* 165 MHz */
+#define SPI_BASEADDR COMCERTO_APB_SPI_BASE
+
+#define SPI_BAUDR_MIN 2
+#define SPI_BAUDR_MAX 0xFFFE
+#define SPI_SPEED_MAX (4*1000*1000)
+#define SPI_SPEED_MIN 0 /*need to be calculated using c2k pll bus clock*/
+#define SPI_FRAME_SIZE_MIN 4
+#define SPI_FRAME_SIZE_MAX 16
+#define SPI_CHIP_SELECT_MAX 15
+
+struct comcerto_spi_platform {
+ int *chipselect;
+ int num_chipselect;
+ u8 bits_per_word;
+ u8 num_devices;
+ unsigned long min_sc_rate; /* maximum supported serial clock rate (in MHz) */
+ unsigned long max_sc_rate; /* minimum supported serial clock rate (in MHz) */
+
+ u8 max_fs; /* maximum supported frame size (in bits) */
+ u8 min_fs; /* minimum supported frame size (in bits) */
+
+ u16 max_nframe; /* maximum supported transfer frame number */
+ u16 min_nframe; /* minimum supported transfer frame number */
+
+ u16 cs_msk;
+ unsigned long clock_rate;
+
+ struct spi_board_info *devices;
+};
+
+struct comcerto_transfer
+{
+ u8 *wbuf;
+ unsigned int wlen;
+
+ u8 *rbuf;
+ unsigned int rlen;
+
+ u8 mode;
+
+ u32 ctrlr0;
+
+ u8 bits_per_word;
+ u32 baudr;
+ u32 ser;
+
+ u8 fs; /* transfer frame size (in bits) */
+};
+
+struct spi_client_conf
+{
+ u16 cs_msk; /* chip select mask for this client */
+ u8 sc_polarity; /* serial clock polarity */
+ u8 sc_phase; /* serial clock phase */
+ unsigned long sc_rate; /* serial clock rate (in MHz)*/
+ u8 cs_delay; /* chip select deassert time (in serial clock cycles) */
+};
+
+#define COMCERTO_SPI_CTRLR0 0x00
+#define COMCERTO_SPI_CTRLR1 0x04
+#define COMCERTO_SPI_SSIENR 0x08
+#define COMCERTO_SPI_MWCR 0x0C
+#define COMCERTO_SPI_SER 0x10
+#define COMCERTO_SPI_BAUDR 0x14
+#define COMCERTO_SPI_TXFTLR 0x18
+#define COMCERTO_SPI_RXFTLR 0x1C
+#define COMCERTO_SPI_TXFLR 0x20
+#define COMCERTO_SPI_RXFLR 0x24
+#define COMCERTO_SPI_SR 0x28
+#define COMCERTO_SPI_IMR 0x2C
+#define COMCERTO_SPI_ISR 0x30
+#define COMCERTO_SPI_RISR 0x34
+#define COMCERTO_SPI_TXOICR 0x38
+#define COMCERTO_SPI_RXOICR 0x3C
+#define COMCERTO_SPI_RXUICR 0x40
+#define COMCERTO_SPI_MSTICR 0x44
+#define COMCERTO_SPI_ICR 0x48
+#define COMCERTO_SPI_IDR 0x58
+#define COMCERTO_SPI_DR 0x60
+
+
+/* SR - status register bits */
+#define BUSY (1<<0) /* SSI busy flag, serial transfer in progress */
+#define TFNF (1<<1) /* Transmit FIFO not full */
+#define TFE (1<<2) /* Transmit FIFO empty */
+#define RFNE (1<<3) /* Receive FIFO not empty */
+#define RFF (1<<4) /* Receive FIFO full */
+#define TXE (1<<5) /* Transmission error */
+#define DCOL (1<<6) /* Data collision error */
+
+/* Interrupt status after being masked */
+#define TXEIS (1<<0) /* Transmit FIFO empty interrupt status */
+#define TXOIS (1<<1) /* Transmit FIFO overflow interrupt status */
+#define RXUIS (1<<2) /* Receive FIFO underflow interrupt status */
+#define RXOIS (1<<3) /* Receive FIFO overflow interrupt status */
+#define RXFIS (1<<4) /* Receive FIFO full interrupt status */
+#define MSTIS (1<<5) /* Multi-Master contention interrupt status */
+
+/* Interrupt status before being masked */
+#define TXEIR (1<<0) /* Transmit FIFO empty interrupt status */
+#define TXOIR (1<<1) /* Transmit FIFO overflow interrupt status */
+#define RXUIR (1<<2) /* Receive FIFO underflow interrupt status */
+#define RXOIR (1<<3) /* Receive FIFO overflow interrupt status */
+#define RXFIR (1<<4) /* Receive FIFO full interrupt status */
+#define MSTIR (1<<5) /* Multi-Master contention interrupt status */
+
+
+/* Interrupt mask register */
+#define TXEIM (1<<0) /* Transmit FIFO empty interrupt status */
+#define TXOIM (1<<1) /* Transmit FIFO overflow interrupt status */
+#define RXUIM (1<<2) /* Receive FIFO underflow interrupt status */
+#define RXOIM (1<<3) /* Receive FIFO overflow interrupt status */
+#define RXFIM (1<<4) /* Receive FIFO full interrupt status */
+#define MSTIM (1<<5) /* Multi-Master contention interrupt status */
+
+
+#define SPI_TRANSFER_MODE_WRITE_ONLY 0x01
+#define SPI_TRANSFER_MODE_READ_ONLY 0x02
+#define SPI_TRANSFER_MODE_WRITE_READ 0x03
+
+typedef u8 u4;
+
+struct spi_local_transfer
+{
+ u8 *wbuf;
+ unsigned int wlen;
+
+ u8 *rbuf;
+ unsigned int rlen;
+
+ u8 mode;
+
+ u8 fs; /* transfer frame size (in bits) */
+};
+
+struct spi_adapter_caps
+{
+ unsigned long min_sc_rate; /* maximum supported serial clock rate (in MHz) */
+ unsigned long max_sc_rate; /* minimum supported serial clock rate (in MHz) */
+
+ u8 max_fs; /* maximum supported frame size (in bits) */
+ u8 min_fs; /* minimum supported frame size (in bits) */
+
+ u16 cs_msk; /* mask of supported chip selects */
+
+ u16 max_nframe; /* maximum supported transfer frame number */
+ u16 min_nframe; /* minimum supported transfer frame number */
+};
+
+struct spi_client_config
+{
+ u16 cs_msk; /* chip select mask for this client */
+ u8 sc_polarity; /* serial clock polarity */
+ u8 sc_phase; /* serial clock phase */
+ unsigned long sc_rate; /* serial clock rate (in MHz)*/
+ u8 cs_delay; /* chip select deassert time (in serial clock cycles) */
+};
+
+/* A SPI bus adapter instance */
+struct spi_adapter
+{
+ char *name;
+
+ int (*do_transfer)(struct spi_adapter *adapter, struct spi_local_transfer *transfer, struct spi_client_config *config);
+
+ void *data;
+ unsigned long membase;
+ unsigned long clock_rate;
+
+ struct list_head clients;
+
+ struct spi_adapter_caps caps;
+
+ struct spi_master master;
+};
+
+/* A SPI device instance */
+struct spi_client
+{
+ struct spi_client_config config;
+ struct spi_adapter *adapter;
+ void *data;
+ struct list_head list;
+};
+
+//int spi_writeread_mem(struct spi_client *client, u8 fs, u8 *rbuffer, int rlen, u8 *wbuffer, int wlen);
+int spi_writeread_mem(struct spi_adapter *adapter, u8 fs, u8 *rbuffer, int rlen, u8 *wbuffer, int wlen);
+
+
+#endif /* _COMCERTO_SPI_H */
diff --git a/include/linux/spi/designware.h b/include/linux/spi/designware.h
new file mode 100644
index 0000000..cc0335b
--- /dev/null
+++ b/include/linux/spi/designware.h
@@ -0,0 +1,15 @@
+/*
+ * designware.h - platform glue for the Synopsys DesignWare SPI controller
+ */
+
+#define CLK_NAME 10
+#define TX_FIFO_DEPTH 8
+#define RX_FIFO_DEPTH 8
+
+struct spi_controller_pdata {
+ int use_dma;
+ int num_chipselects;
+ int bus_num;
+ u32 max_freq;
+ char clk_name[CLK_NAME];
+};
diff --git a/include/linux/spi2/spi.h b/include/linux/spi2/spi.h
new file mode 100644
index 0000000..9cba316
--- /dev/null
+++ b/include/linux/spi2/spi.h
@@ -0,0 +1,167 @@
+/*
+ * linux/include/linux/spi.h
+ *
+ * Copyright (C) Mindspeed Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#ifndef _SPI_H
+#define _SPI_H
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#include <linux/device.h>
+#endif
+
+#define SPI_TRANSFER_MODE_WRITE_ONLY 0x01
+#define SPI_TRANSFER_MODE_READ_ONLY 0x02
+#define SPI_TRANSFER_MODE_WRITE_READ 0x03
+
+typedef u8 u4;
+
+struct spi_transfer
+{
+ u8 *wbuf;
+ unsigned int wlen;
+
+ u8 *rbuf;
+ unsigned int rlen;
+
+ u8 mode;
+
+ u8 fs; /* transfer frame size (in bits) */
+};
+
+struct spi_adapter_caps
+{
+ unsigned long min_sc_rate; /* maximum supported serial clock rate (in MHz) */
+ unsigned long max_sc_rate; /* minimum supported serial clock rate (in MHz) */
+
+ u8 max_fs; /* maximum supported frame size (in bits) */
+ u8 min_fs; /* minimum supported frame size (in bits) */
+
+ u16 cs_msk; /* mask of supported chip selects */
+
+ u16 max_nframe; /* maximum supported transfer frame number */
+ u16 min_nframe; /* minimum supported transfer frame number */
+};
+
+struct spi_client_config
+{
+ u16 cs_msk; /* chip select mask for this client */
+ u8 sc_polarity; /* serial clock polarity */
+ u8 sc_phase; /* serial clock phase */
+ unsigned long sc_rate; /* serial clock rate (in MHz)*/
+ u8 cs_delay; /* chip select deassert time (in serial clock cycles) */
+ unsigned long ba_delay; /* bus access delay, in us */
+};
+
+/* A SPI bus adapter instance */
+struct spi_adapter
+{
+ char *name;
+
+ int (*do_transfer)(struct spi_adapter *adapter, struct spi_transfer *transfer, struct spi_client_config *config);
+
+ void *data;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ struct device dev;
+#endif
+ struct list_head list;
+ struct list_head clients;
+
+ struct spi_adapter_caps caps;
+
+ int bus_num;
+
+ spinlock_t lock;
+};
+
+/* A SPI device instance */
+struct spi_client
+{
+ struct spi_client_config config;
+
+ struct spi_driver *driver;
+
+ struct spi_adapter *adapter;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ struct device dev;
+#else
+ void *data;
+#endif
+ struct list_head list;
+};
+
+/* A SPI device driver */
+struct spi_driver {
+ char *name;
+
+ int (*attach_adapter)(struct spi_adapter *adapter);
+ int (*detach_client)(struct spi_client *client);
+#ifdef CONFIG_PM
+ int (*suspend)(struct spi_client *client, pm_message_t state);
+ int (*resume)(struct spi_client *client);
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ struct device_driver driver;
+#endif
+ struct list_head list;
+ short bus_id;
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static inline void *spi_get_clientdata (struct spi_client *dev)
+{
+ return dev_get_drvdata (&dev->dev);
+}
+
+static inline void spi_set_clientdata (struct spi_client *dev, void *data)
+{
+ dev_set_drvdata (&dev->dev, data);
+}
+#else
+static inline void *spi_get_clientdata (struct spi_client *dev)
+{
+ return (void *) dev->data;
+}
+
+static inline void spi_set_clientdata (struct spi_client *dev, void *data)
+{
+ dev->data = data;
+}
+#endif
+
+int spi_add_adapter(struct spi_adapter *adapter);
+int spi_del_adapter(struct spi_adapter *adapter);
+
+int spi_add_driver(struct spi_driver *driver);
+int spi_del_driver(struct spi_driver *driver);
+
+int spi_attach_client(struct spi_client *client);
+int spi_detach_client(struct spi_client *client);
+
+int spi_write_mem(struct spi_client *client, u8 fs, u8 *buffer, int len);
+int spi_write_single(struct spi_client *client, u8 fs, u16 value);
+
+int spi_writen(struct spi_client *client, u4 value);
+int spi_writeb(struct spi_client *client, u8 value);
+int spi_writew(struct spi_client *client, u16 value);
+
+int spi_read_mem(struct spi_client *client, u8 fs, u8 *buffer, int len);
+int spi_read_single(struct spi_client *client, u8 fs, u16 *value);
+int spi_writeread_mem(struct spi_client *client, u8 fs, u8 *rbuffer, int rlen, u8 *wbuffer, int wlen);
+
+int spi_readn(struct spi_client *client, u4 *value);
+int spi_readb(struct spi_client *client, u8 *value);
+int spi_readw(struct spi_client *client, u16 *value);
+
+#endif /* _SPI_H */
diff --git a/include/net/flow.h b/include/net/flow.h
index 2a7eefd..2b4adb6 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -212,9 +212,17 @@
struct net *net, const struct flowi *key, u16 family,
u8 dir, struct flow_cache_object *oldobj, void *ctx);
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+extern struct flow_cache_object *flow_cache_lookup(
+ struct net *net, const struct flowi *key, u16 family,
+ u8 dir, u8 *new_flow, flow_resolve_t resolver, void *ctx);
+extern void flow_cache_remove(
+ const struct flowi *fl, unsigned short family, unsigned short dir);
+#else
extern struct flow_cache_object *flow_cache_lookup(
struct net *net, const struct flowi *key, u16 family,
u8 dir, flow_resolve_t resolver, void *ctx);
+#endif
extern void flow_cache_flush(void);
extern void flow_cache_flush_deferred(void);
diff --git a/include/net/ip.h b/include/net/ip.h
index eca0ef7..6616ba5 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -408,6 +408,7 @@
IP_DEFRAG_VS_FWD,
IP_DEFRAG_AF_PACKET,
IP_DEFRAG_MACVLAN,
+ IP_DEFRAG_IP6_TNL_4RD /* Used to support Post Fragmentation for 4o6 tunnels */
};
int ip_defrag(struct sk_buff *skb, u32 user);
diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index fc73e66..68205dd 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -19,6 +19,10 @@
struct flowi fl; /* flowi template for xmit */
struct dst_entry *dst_cache; /* cached dst */
u32 dst_cookie;
+#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ u32 genid;
+#endif
+ struct ip6_tnl_4rd_parm ip4rd; /* 4rd parameters for the tunnel */
};
/* Tunnel encapsulation limit destination sub-option */
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index 0b6a3e8..2a39687 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -100,6 +100,14 @@
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
+#if defined(CONFIG_COMCERTO_FP)
+struct comcerto_fp_info {
+ int ifindex;
+ int iif;
+ u32 mark;
+};
+#endif
+
struct nf_conn {
/* Usage count in here is 1 for hash table/destruct timer, 1 per skb,
plus 1 for any connection(s) we are `master' for */
@@ -128,6 +136,10 @@
u_int32_t secmark;
#endif
+#if defined(CONFIG_COMCERTO_FP)
+ struct comcerto_fp_info fp_info[IP_CT_DIR_MAX];
+#endif
+
/* Extensions */
struct nf_ct_ext *ext;
#ifdef CONFIG_NET_NS
@@ -331,6 +343,9 @@
struct kernel_param;
+extern int nf_conntrack_set_dpi_allow_report(struct sk_buff *skb);
+extern int nf_conntrack_set_dpi_allow_and_mark(struct sk_buff *skb, int mark);
+
extern int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp);
extern unsigned int nf_conntrack_htable_size;
extern unsigned int nf_conntrack_max;
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index aced085..f8fb25f 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -76,4 +76,8 @@
extern spinlock_t nf_conntrack_lock ;
+#ifdef CONFIG_COMCERTO_FP
+#define COMCERTO_PERMANENT_TIMEOUT 1000
+#endif
+
#endif /* _NF_CONNTRACK_CORE_H */
diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h
index a88fb69..ea6f8a4 100644
--- a/include/net/netfilter/nf_conntrack_ecache.h
+++ b/include/net/netfilter/nf_conntrack_ecache.h
@@ -18,6 +18,7 @@
u16 ctmask; /* bitmask of ct events to be delivered */
u16 expmask; /* bitmask of expect events to be delivered */
u32 pid; /* netlink pid of destroyer */
+ struct timer_list timeout;
};
static inline struct nf_conntrack_ecache *
diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h
index 748f91f..19295e5 100644
--- a/include/net/netns/xfrm.h
+++ b/include/net/netns/xfrm.h
@@ -27,6 +27,9 @@
struct hlist_head *state_bydst;
struct hlist_head *state_bysrc;
struct hlist_head *state_byspi;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ struct hlist_head *state_byh;
+#endif
unsigned int state_hmask;
unsigned int state_num;
struct work_struct state_hash_work;
diff --git a/include/net/udp.h b/include/net/udp.h
index 3b285f4..009c6b0 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -191,6 +191,9 @@
extern int udp_lib_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, unsigned int optlen,
int (*push_pending_frames)(struct sock *));
+extern int udp6_lib_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, unsigned int optlen,
+ int (*push_pending_frames)(struct sock *));
extern struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport,
int dif);
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index b203e14..add91c3 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -137,7 +137,10 @@
};
struct hlist_node bysrc;
struct hlist_node byspi;
-
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ struct hlist_node byh;
+ u16 handle;
+#endif
atomic_t refcnt;
spinlock_t lock;
@@ -229,6 +232,11 @@
/* Private data of this transformer, format is opaque,
* interpreted by xfrm_type methods. */
void *data;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ /* Intended direction of this state, used for offloading */
+ int dir;
+ int offloaded;
+#endif
};
static inline struct net *xs_net(struct xfrm_state *x)
@@ -247,6 +255,13 @@
XFRM_STATE_EXPIRED,
XFRM_STATE_DEAD
};
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+enum {
+ XFRM_STATE_DIR_UNKNOWN,
+ XFRM_STATE_DIR_IN,
+ XFRM_STATE_DIR_OUT,
+};
+#endif
/* callback structure passed from either netlink or pfkey */
struct km_event {
@@ -299,6 +314,9 @@
extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo);
extern int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo);
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+extern struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
+#endif
extern void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c);
extern void km_state_notify(struct xfrm_state *x, const struct km_event *c);
@@ -958,6 +976,35 @@
struct xfrm_state *xvec[XFRM_MAX_DEPTH];
};
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+struct xfrm_input_shared
+{
+ struct sk_buff *skb;
+ int xfrm_nr, first, xfrm_encap;
+ struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
+ __u16 encap_type;
+ int decaps;
+ u32 seq, spi;
+ unsigned int nhoff;
+ int nexthdr;
+ int (*callback)(struct xfrm_input_shared *sh);
+ atomic_t refcnt;
+};
+
+
+static inline void xfrm_shared_get(struct xfrm_input_shared *sh)
+{
+ atomic_inc(&sh->refcnt);
+}
+
+static inline void xfrm_shared_put(struct xfrm_input_shared *sh)
+{
+ if (atomic_dec_and_test(&sh->refcnt)) {
+ kfree(sh);
+ }
+}
+#endif
+
static inline int secpath_exists(struct sk_buff *skb)
{
#ifdef CONFIG_XFRM
@@ -1469,6 +1516,10 @@
extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
extern int xfrm6_extract_header(struct sk_buff *skb);
extern int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb);
+/* NAT-T changes Start */
+extern int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
+ int encap_type);
+/* NAT-T changes End */
extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
extern int xfrm6_transport_finish(struct sk_buff *skb, int async);
extern int xfrm6_rcv(struct sk_buff *skb);
@@ -1487,12 +1538,21 @@
#ifdef CONFIG_XFRM
extern int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
+extern int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
extern int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen);
#else
static inline int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
{
return -ENOPROTOOPT;
}
+/* NAT-T changes Start */
+static inline int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ /* should not happen */
+ kfree_skb(skb);
+ return 0;
+}
+/* NAT-T changes End */
static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
{
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c
index 4e316e1..22b499b 100644
--- a/kernel/ksysfs.c
+++ b/kernel/ksysfs.c
@@ -141,6 +141,484 @@
}
KERNEL_ATTR_RO(fscaps);
+#if defined(CONFIG_COMCERTO_MDMA_PROF)
+extern unsigned int mdma_time_counter[256]; // 16 -> 4000 us
+extern unsigned int mdma_reqtime_counter[256]; // 16 -> 4000 us
+extern unsigned int mdma_data_counter[256];
+extern unsigned int init_mdma_prof;
+extern unsigned int enable_mdma_prof;
+
+static ssize_t comcerto_mdma_prof_enable_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int n;
+ buf[0] = '\0';
+ n = 0;
+ if (enable_mdma_prof)
+ n += sprintf(buf, "MDMA profiling is enabled\n");
+ else
+ n += sprintf(buf, "MDMA profiling is disabled\n");
+
+ return (n + 1);
+}
+
+static ssize_t comcerto_mdma_prof_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int enable;
+
+ if (kstrtouint(buf, 0, &enable))
+ return -EINVAL;
+
+ if (enable > 0)
+ enable_mdma_prof = 1;
+ else
+ enable_mdma_prof = 0;
+
+ return count;
+}
+KERNEL_ATTR_RW(comcerto_mdma_prof_enable);
+
+static ssize_t comcerto_mdma_reqtiming_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of mdma request time\n");
+
+ for (i = 0; i < 255; i++)
+ {
+ if (mdma_reqtime_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] us\n", mdma_reqtime_counter[i], i << 4, (i + 1) << 4);
+ mdma_reqtime_counter[i] = 0;
+ }
+ }
+ if (mdma_reqtime_counter[255]) {
+ n += sprintf(buf + n, "%d >= %d us\n", mdma_reqtime_counter[255], 255 << 4);
+ mdma_reqtime_counter[255] = 0;
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_mdma_reqtiming);
+
+static ssize_t comcerto_mdma_timing_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ init_mdma_prof = 0;
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of inter mdma request time\n");
+
+ for (i = 0; i < 255; i++)
+ {
+ if (mdma_time_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] us\n", mdma_time_counter[i], i << 4, (i + 1) << 4);
+ mdma_time_counter[i] = 0;
+ }
+ }
+ if (mdma_time_counter[255]) {
+ n += sprintf(buf + n, "%d >= %d us\n", mdma_time_counter[255], 255 << 4);
+ mdma_time_counter[255] = 0;
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_mdma_timing);
+
+static ssize_t comcerto_mdma_data_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of mdma data length (up to 1M)\n");
+ for (i = 0; i < 256; i++)
+ {
+ if (mdma_data_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] KB\n", mdma_data_counter[i], i << (13 - 10), (i + 1) << (13 - 10));
+ mdma_data_counter[i] = 0;
+ }
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_mdma_data);
+#endif
+
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+extern unsigned int splicew_time_counter[256]; // 4 ms -> 1S
+extern unsigned int splicew_reqtime_counter[256]; // 4 ms -> 1S
+extern unsigned int splicew_data_counter[256];
+extern unsigned int init_splicew_prof;
+extern unsigned int enable_splice_prof;
+static ssize_t comcerto_splice_prof_enable_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int n;
+ buf[0] = '\0';
+ n = 0;
+ if (enable_splice_prof)
+ n += sprintf(buf, "Splice profiling is enabled\n");
+ else
+ n += sprintf(buf, "Splice profiling is disabled\n");
+
+ return (n + 1);
+}
+static ssize_t comcerto_splice_prof_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int enable;
+
+ if (kstrtouint(buf, 0, &enable))
+ return -EINVAL;
+
+ if (enable > 0)
+ enable_splice_prof = 1;
+ else
+ enable_splice_prof = 0;
+
+ return count;
+}
+KERNEL_ATTR_RW(comcerto_splice_prof_enable);
+static ssize_t comcerto_splicew_reqtiming_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of splice write time (up to 1 sec otherwise date is discarded)\n");
+
+ for (i = 0; i < 255; i++)
+ {
+ if (splicew_reqtime_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] ms\n", splicew_reqtime_counter[i], (i * 8), (i * 8) + 8);
+ splicew_reqtime_counter[i] = 0;
+ }
+ }
+ if (splicew_reqtime_counter[255]) {
+ n += sprintf(buf + n, "%d > 1 second\n", splicew_reqtime_counter[255]);
+ splicew_reqtime_counter[255] = 0;
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_splicew_reqtiming);
+static ssize_t comcerto_splicew_timing_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ init_splicew_prof = 0;
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of inter splice write time (up to 1 sec otherwise date is discarded)\n");
+
+ for (i = 0; i < 255; i++)
+ {
+ if (splicew_time_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] ms\n", splicew_time_counter[i], (i * 8), (i * 8) + 8);
+ splicew_time_counter[i] = 0;
+ }
+ }
+ if (splicew_time_counter[255]) {
+ n += sprintf(buf + n, "%d > 1 second\n", splicew_time_counter[255]);
+ splicew_time_counter[255] = 0;
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_splicew_timing);
+static ssize_t comcerto_splicew_data_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of splice write data length (up to 1M)\n");
+ for (i = 0; i < 256; i++)
+ {
+ if (splicew_data_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] KB\n", splicew_data_counter[i], (i * 8), (i * 8) + 8);
+ splicew_data_counter[i] = 0;
+ }
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_splicew_data);
+
+
+extern unsigned int splicer_time_counter[256]; // 4 ms -> 1S
+extern unsigned int splicer_reqtime_counter[256]; // 4 ms -> 1S
+extern unsigned int splicer_data_counter[256];
+extern unsigned int splicer_tcp_rsock_counter[64];
+extern unsigned int init_splicer_prof;
+static ssize_t comcerto_splicer_reqtiming_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of splice read time\n");
+
+ for (i = 0; i < 255; i++)
+ {
+ if (splicer_reqtime_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] ms\n", splicer_reqtime_counter[i], (i * 8), (i * 8) + 8);
+ splicer_reqtime_counter[i] = 0;
+ }
+ }
+ if (splicer_reqtime_counter[255]) {
+ n += sprintf(buf + n, "%d > 1 second\n", splicer_reqtime_counter[255]);
+ splicer_reqtime_counter[255] = 0;
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_splicer_reqtiming);
+static ssize_t comcerto_splicer_timing_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ init_splicer_prof = 0;
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of inter splice read time\n");
+
+ for (i = 0; i < 255; i++)
+ {
+ if (splicer_time_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] ms\n", splicer_time_counter[i], (i * 8), (i * 8) + 8);
+ splicer_time_counter[i] = 0;
+ }
+ }
+ if (splicer_time_counter[255]) {
+ n += sprintf(buf + n, "%d > 1 second\n", splicer_time_counter[255]);
+ splicer_time_counter[255] = 0;
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_splicer_timing);
+static ssize_t comcerto_splicer_data_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of splice read data length (up to 1M)\n");
+ for (i = 0; i < 256; i++)
+ {
+ if (splicer_data_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] KB\n", splicer_data_counter[i], (i * 8), (i * 8) + 8);
+ splicer_data_counter[i] = 0;
+ }
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_splicer_data);
+static ssize_t comcerto_splicer_tcp_rsock_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int n;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of TCP receive queue size when splice read is performed\n");
+ for (i = 0; i < 63; i++)
+ {
+ if (splicer_tcp_rsock_counter[i]) {
+ n += sprintf(buf + n, "%d in [%d-%d] KB\n", splicer_tcp_rsock_counter[i], (i * 64), (i * 64) + 64);
+ splicer_tcp_rsock_counter[i] = 0;
+ }
+ }
+ if (splicer_tcp_rsock_counter[i]) {
+ n += sprintf(buf + n, "%d >= %d KB\n", splicer_tcp_rsock_counter[i], (i * 64));
+ splicer_tcp_rsock_counter[i] = 0;
+ }
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_splicer_tcp_rsock);
+#endif
+
+#if defined(CONFIG_COMCERTO_AHCI_PROF)
+
+#include "../drivers/ata/ahci.h"
+
+static ssize_t comcerto_ahci_prof_enable_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int n;
+ buf[0] = '\0';
+ n = 0;
+ if (enable_ahci_prof)
+ n += sprintf(buf, "AHCI profiling is enabled\n");
+ else
+ n += sprintf(buf, "AHCI profiling is disabled\n");
+
+ return (n + 1);
+}
+static ssize_t comcerto_ahci_prof_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int enable;
+
+ if (kstrtouint(buf, 0, &enable))
+ return -EINVAL;
+
+ if (enable > 0)
+ enable_ahci_prof = 1;
+ else
+ enable_ahci_prof = 0;
+
+ return count;
+}
+KERNEL_ATTR_RW(comcerto_ahci_prof_enable);
+static ssize_t comcerto_ahci_timing_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i, n, p;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of ahci inter request time (us)\n");
+
+ for (p = 0; p < MAX_AHCI_PORTS; p++) {
+ struct ahci_port_stats *stats = &ahci_port_stats[p];
+
+ n += sprintf(buf + n, "AHCI Port %d\n", p);
+
+ stats->init_prof = 0;
+
+ for (i = 0; i < MAX_BINS - 1; i++)
+ {
+ if (stats->time_counter[i]) {
+ n += sprintf(buf + n, "%8d in [%5d-%5d]\n", stats->time_counter[i], i << US_SHIFT, (i + 1) << US_SHIFT);
+ stats->time_counter[i] = 0;
+ }
+ }
+
+ if (stats->time_counter[MAX_BINS - 1]) {
+ n += sprintf(buf + n, "%d > %d us\n", stats->time_counter[MAX_BINS - 1], (MAX_BINS - 1) << US_SHIFT);
+ stats->time_counter[MAX_BINS - 1] = 0;
+ }
+ }
+
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_ahci_timing);
+static ssize_t comcerto_ahci_data_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i, n, p;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of ahci requests data length (KiB)\n");
+
+ for (p = 0; p < MAX_AHCI_PORTS; p++) {
+ struct ahci_port_stats *stats = &ahci_port_stats[p];
+
+ n += sprintf(buf + n, "AHCI Port %d\n", p);
+
+ for (i = 0; i < MAX_BINS; i++)
+ {
+ if (stats->data_counter[i]) {
+ n += sprintf(buf + n, "%8d in [%3d-%3d]\n", stats->data_counter[i], (i << BYTE_SHIFT) / 1024, ((i + 1) << BYTE_SHIFT) / 1024);
+ stats->data_counter[i] = 0;
+ }
+ }
+ }
+
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_ahci_data);
+
+
+extern struct ahci_port_stats ahci_port_stats[MAX_AHCI_PORTS];
+static ssize_t comcerto_ahci_qc_rate_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i, n, p;
+ unsigned int mean_rate, total_kb;
+
+ buf[0] = '\0';
+ n = 0;
+ n += sprintf(buf, "Histogram of AHCI requests rate completion (MiB MiB/s):\n");
+
+ for (p = 0; p < MAX_AHCI_PORTS; p++) {
+ struct ahci_port_stats *stats = &ahci_port_stats[p];
+
+ n += sprintf(buf + n, "AHCI Port %d\n", p);
+ total_kb = 0;
+ mean_rate = 0;
+
+ for (i = 0; i < MAX_BINS; i++)
+ {
+ if (stats->rate_counter[i]) {
+ n += sprintf(buf + n, "%8d in [%3d-%3d]\n", stats->rate_counter[i] / 1024, i << RATE_SHIFT, (i + 1) << RATE_SHIFT);
+ mean_rate += stats->rate_counter[i] * (((2 * i + 1) << RATE_SHIFT) / 2);
+ total_kb += stats->rate_counter[i];
+ stats->rate_counter[i] = 0;
+ }
+ }
+ n += sprintf(buf + n, "\n");
+
+ for (i = 0; i < MAX_AHCI_SLOTS; i++) {
+ if (stats->pending_counter[i]) {
+ n += sprintf(buf + n, "%8d in [%2d]\n", stats->pending_counter[i], i);
+ stats->pending_counter[i] = 0;
+ }
+ }
+
+ if (total_kb) {
+ n += sprintf(buf + n, "Mean: %d MiB/s for %d MiB\n", mean_rate / total_kb, total_kb / 1024);
+ n += sprintf(buf + n, "Max issues in a row : %d \n\n", stats->nb_pending_max);
+ }
+
+ stats->nb_pending_max = 0;
+ }
+
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_ahci_qc_rate);
+
+
+extern unsigned int ahci_qc_no_free_slot;
+static ssize_t comcerto_ahci_qc_no_free_slot_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int n, p;
+
+ buf[0] = '\0';
+ n = 0;
+
+ for (p = 0; p < MAX_AHCI_PORTS; p++) {
+ struct ahci_port_stats *stats = &ahci_port_stats[p];
+
+ n += sprintf(buf + n, "AHCI Port %d no_free_slot count: %d\n", p, stats->no_free_slot);
+
+ stats->no_free_slot = 0;
+ }
+
+ return (n + 1);
+}
+KERNEL_ATTR_RO(comcerto_ahci_qc_no_free_slot);
+#endif
+
/*
* Make /sys/kernel/notes give the raw contents of our kernel .notes section.
*/
@@ -182,6 +660,29 @@
&kexec_crash_size_attr.attr,
&vmcoreinfo_attr.attr,
#endif
+#if defined(CONFIG_COMCERTO_MDMA_PROF)
+ &comcerto_mdma_prof_enable_attr.attr,
+ &comcerto_mdma_timing_attr.attr,
+ &comcerto_mdma_reqtiming_attr.attr,
+ &comcerto_mdma_data_attr.attr,
+#endif
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+ &comcerto_splice_prof_enable_attr.attr,
+ &comcerto_splicew_timing_attr.attr,
+ &comcerto_splicew_reqtiming_attr.attr,
+ &comcerto_splicew_data_attr.attr,
+ &comcerto_splicer_timing_attr.attr,
+ &comcerto_splicer_reqtiming_attr.attr,
+ &comcerto_splicer_data_attr.attr,
+ &comcerto_splicer_tcp_rsock_attr.attr,
+#endif
+#if defined(CONFIG_COMCERTO_AHCI_PROF)
+ &comcerto_ahci_prof_enable_attr.attr,
+ &comcerto_ahci_timing_attr.attr,
+ &comcerto_ahci_data_attr.attr,
+ &comcerto_ahci_qc_rate_attr.attr,
+ &comcerto_ahci_qc_no_free_slot_attr.attr,
+#endif
NULL
};
diff --git a/kernel/pid.c b/kernel/pid.c
index fa5f722..2bf49fd 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -428,6 +428,7 @@
{
return find_task_by_pid_ns(vnr, current->nsproxy->pid_ns);
}
+EXPORT_SYMBOL(find_task_by_vpid);
struct pid *get_task_pid(struct task_struct *task, enum pid_type type)
{
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index deb5461..d4c5692 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -148,6 +148,15 @@
You probably want to have your system's RTC driver statically
linked, ensuring that it's available when this test runs.
+config PM_SYSFS_MANUAL
+ bool "Driver model /sys/devices/.../power/state files"
+ depends on PM
+ default n
+ ---help---
+ The driver model started out with a sysfs file intended to provide
+ a userspace hook for device power management. By this hook Non CPU
+ devices can be put in power off state.
+
config CAN_PM_TRACE
def_bool y
depends on PM_DEBUG && PM_SLEEP
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 36e0f09..bb998cc 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -15,6 +15,7 @@
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <mach/comcerto-2000/pm.h>
#include "power.h"
@@ -306,6 +307,28 @@
power_attr(state);
+#ifdef CONFIG_ARCH_M86XXX
+static ssize_t bitmask_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ unsigned int value=c2k_pm_bitmask_show();
+ return sprintf(buf,"%02x\n",value);
+
+}
+
+static ssize_t bitmask_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long value;
+ value=simple_strtoul(buf,NULL,16);
+ /* Store the Bitmask value in the Global Variable */
+ c2k_pm_bitmask_store(value);
+ return n;
+
+}
+power_attr(bitmask);
+#endif
+
#ifdef CONFIG_PM_SLEEP
/*
* The 'wakeup_count' attribute, along with the functions defined in
@@ -404,6 +427,9 @@
static struct attribute * g[] = {
&state_attr.attr,
+#ifdef CONFIG_ARCH_M86XXX
+ &bitmask_attr.attr,
+#endif
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
&pm_trace_dev_match_attr.attr,
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index af48faa..f31d659 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -171,15 +171,16 @@
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
-
- error = syscore_suspend();
+ /* FIXME syscore suspend/resume is not working for HGW build */
+ //error = syscore_suspend();
+ error = 0;
if (!error) {
*wakeup = pm_wakeup_pending();
if (!(suspend_test(TEST_CORE) || *wakeup)) {
error = suspend_ops->enter(state);
events_check_enabled = false;
}
- syscore_resume();
+ //syscore_resume();
}
arch_suspend_enable_irqs();
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 2c71d91..e34cc37 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -202,7 +202,11 @@
* we want to handle softirqs as soon as possible, but they
* should not be able to lock up the box.
*/
+#if defined(CONFIG_ARCH_COMCERTO)
+#define MAX_SOFTIRQ_RESTART 2
+#else
#define MAX_SOFTIRQ_RESTART 10
+#endif
asmlinkage void __do_softirq(void)
{
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 2b49dd2..0462891 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -225,12 +225,40 @@
}
BDI_SHOW(max_ratio, bdi->max_ratio)
+#ifdef CONFIG_ARCH_M86XXX
+static ssize_t cpu0_bind_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct backing_dev_info *bdi = dev_get_drvdata(dev);
+ unsigned int flag;
+
+ flag = simple_strtoul(buf, NULL, 10);
+ if (flag)
+ bdi->cpu0_bind = 1;
+ else
+ bdi->cpu0_bind = 0;
+
+ return count;
+}
+
+static ssize_t cpu0_bind_show(struct device *dev,
+ struct device_attribute *attr, char *page)
+{
+ struct backing_dev_info *bdi = dev_get_drvdata(dev);
+
+ return snprintf(page, PAGE_SIZE-1, "%d\n", bdi->cpu0_bind);
+}
+#endif /* CONFIG_ARCH_M86XXX */
+
#define __ATTR_RW(attr) __ATTR(attr, 0644, attr##_show, attr##_store)
static struct device_attribute bdi_dev_attrs[] = {
__ATTR_RW(read_ahead_kb),
__ATTR_RW(min_ratio),
__ATTR_RW(max_ratio),
+#ifdef CONFIG_ARCH_M86XXX
+ __ATTR_RW(cpu0_bind),
+#endif
__ATTR_NULL,
};
@@ -478,6 +506,10 @@
writeback_inodes_wb(&bdi->wb, 1024,
WB_REASON_FORKER_THREAD);
} else {
+#ifdef CONFIG_ARCH_M86XXX
+ if (bdi->cpu0_bind)
+ kthread_bind(task, 0);
+#endif
/*
* The spinlock makes sure we do not lose
* wake-ups when racing with 'bdi_queue_work()'.
diff --git a/mm/filemap.c b/mm/filemap.c
index 556858c..e310263 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -28,6 +28,9 @@
#include <linux/backing-dev.h>
#include <linux/pagevec.h>
#include <linux/blkdev.h>
+#ifdef CONFIG_RAID_ZERO_COPY
+#include <linux/rmap.h>
+#endif
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/cpuset.h>
@@ -609,10 +612,27 @@
BUG();
smp_mb__after_clear_bit();
+
+#ifdef CONFIG_RAID_ZERO_COPY
+ clear_page_constant(page);
+#endif
+
wake_up_page(page, PG_writeback);
}
EXPORT_SYMBOL(end_page_writeback);
+#ifdef CONFIG_RAID_ZERO_COPY
+void clear_page_constant(struct page *page)
+{
+ if (PageConstant(page)) {
+ ClearPageConstant(page);
+ SetPageUptodate(page);
+ }
+}
+EXPORT_SYMBOL(clear_page_constant);
+#endif
+
+
/**
* __lock_page - get a lock on the page, assuming we need to sleep to get it
* @page: the page to lock
diff --git a/mm/mmap.c b/mm/mmap.c
index eae90af..6e12c5f 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1886,7 +1886,7 @@
unmap_vmas(&tlb, vma, start, end, &nr_accounted, NULL);
vm_unacct_memory(nr_accounted);
free_pgtables(&tlb, vma, prev ? prev->vm_end : FIRST_USER_ADDRESS,
- next ? next->vm_start : 0);
+ next ? next->vm_start : mm->task_size);
tlb_finish_mmu(&tlb, start, end);
}
@@ -2261,7 +2261,7 @@
end = unmap_vmas(&tlb, vma, 0, -1, &nr_accounted, NULL);
vm_unacct_memory(nr_accounted);
- free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, 0);
+ free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, mm->task_size);
tlb_finish_mmu(&tlb, 0, end);
/*
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 065dbe8..b7b898d 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1668,6 +1668,11 @@
!cpuset_zone_allowed_softwall(zone, gfp_mask))
continue;
+#ifdef CONFIG_COMCERTO_ZONE_DMA_NCNB
+ if (!(gfp_mask & __GFP_DMA) && (zone_idx(zone) == ZONE_DMA))
+ continue;
+#endif
+
BUILD_BUG_ON(ALLOC_NO_WATERMARKS < NR_WMARK);
if (!(alloc_flags & ALLOC_NO_WATERMARKS)) {
unsigned long mark;
diff --git a/mm/slab.c b/mm/slab.c
index cd3ab93..8bffa97 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -749,7 +749,7 @@
* for large kmalloc calls required.
*/
#ifdef CONFIG_ZONE_DMA
- if (unlikely(gfpflags & GFP_DMA))
+ if (unlikely(gfpflags & __GFP_DMA))
return csizep->cs_dmacachep;
#endif
return csizep->cs_cachep;
@@ -2445,7 +2445,8 @@
cachep->flags = flags;
cachep->gfpflags = 0;
if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA))
- cachep->gfpflags |= GFP_DMA;
+ cachep->gfpflags |= __GFP_DMA;
+
cachep->buffer_size = size;
cachep->reciprocal_buffer_size = reciprocal_value(size);
@@ -2791,10 +2792,10 @@
static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags)
{
if (CONFIG_ZONE_DMA_FLAG) {
- if (flags & GFP_DMA)
- BUG_ON(!(cachep->gfpflags & GFP_DMA));
+ if (flags & __GFP_DMA)
+ BUG_ON(!(cachep->gfpflags & __GFP_DMA));
else
- BUG_ON(cachep->gfpflags & GFP_DMA);
+ BUG_ON(cachep->gfpflags & __GFP_DMA);
}
}
diff --git a/net/bridge/br.c b/net/bridge/br.c
index f20c4fd..e92aa84 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -100,6 +100,60 @@
br_fdb_fini();
}
+#if defined(CONFIG_ARCH_COMCERTO)
+static ATOMIC_NOTIFIER_HEAD(brevent_notif_chain);
+
+/**
+ * register_brevent_notifier - register a netevent notifier block
+ * @nb: notifier
+ *
+ * Register a notifier to be called when a bridge event occurs.
+ * The notifier passed is linked into the kernel structures and must
+ * not be reused until it has been unregistered. A negative errno code
+ * is returned on a failure.
+ */
+int register_brevent_notifier(struct notifier_block *nb)
+{
+ int err;
+
+ err = atomic_notifier_chain_register(&brevent_notif_chain, nb);
+ return err;
+}
+
+/**
+ * unregister_brevent_notifier - unregister a netevent notifier block
+ * @nb: notifier
+ *
+ * Unregister a notifier previously registered by
+ * register_neigh_notifier(). The notifier is unlinked into the
+ * kernel structures and may then be reused. A negative errno code
+ * is returned on a failure.
+ */
+
+int unregister_brevent_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&brevent_notif_chain, nb);
+}
+
+/**
+ * call_brevent_notifiers - call all netevent notifier blocks
+ * @val: value passed unmodified to notifier function
+ * @v: pointer passed unmodified to notifier function
+ *
+ * Call all neighbour notifier blocks. Parameters and return value
+ * are as for notifier_call_chain().
+ */
+
+int call_brevent_notifiers(unsigned long val, void *v)
+{
+ return atomic_notifier_call_chain(&brevent_notif_chain, val, v);
+}
+
+EXPORT_SYMBOL_GPL(register_brevent_notifier);
+EXPORT_SYMBOL_GPL(unregister_brevent_notifier);
+EXPORT_SYMBOL_GPL(call_brevent_notifiers);
+#endif
+
module_init(br_init)
module_exit(br_deinit)
MODULE_LICENSE("GPL");
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index c8e7861..3ae122f 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -18,6 +18,10 @@
#include <linux/times.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#if defined(CONFIG_ARCH_COMCERTO)
+#include <linux/rtnetlink.h>
+#include <linux/module.h>
+#endif
#include <linux/jhash.h>
#include <linux/random.h>
#include <linux/slab.h>
@@ -32,6 +36,11 @@
static u32 fdb_salt __read_mostly;
+#if defined(CONFIG_ARCH_COMCERTO)
+ int(*br_fdb_can_expire)(unsigned char *mac_addr, struct net_device *dev) = NULL;
+ DEFINE_SPINLOCK(br_fdb_cb_lock);
+#endif
+
int __init br_fdb_init(void)
{
br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
@@ -42,6 +51,9 @@
return -ENOMEM;
get_random_bytes(&fdb_salt, sizeof(fdb_salt));
+#if defined(CONFIG_ARCH_COMCERTO)
+ spin_lock_init(&br_fdb_cb_lock);
+#endif
return 0;
}
@@ -142,6 +154,15 @@
unsigned long this_timer;
if (f->is_static)
continue;
+#if defined(CONFIG_ARCH_COMCERTO)
+ spin_lock(&br_fdb_cb_lock);
+ if(br_fdb_can_expire && !(*br_fdb_can_expire)(f->addr.addr, f->dst->dev)){
+ f->updated = jiffies;
+ spin_unlock(&br_fdb_cb_lock);
+ continue;
+ }
+ spin_unlock(&br_fdb_cb_lock);
+#endif
this_timer = f->updated + delay;
if (time_before_eq(this_timer, jiffies))
fdb_delete(f);
@@ -418,7 +439,19 @@
"own address as source address\n",
source->dev->name);
} else {
- /* fastpath: update of existing entry */
+ /* fastpath: update of existing entry */
+#if defined(CONFIG_ARCH_COMCERTO)
+ if (fdb->dst != source) {
+ struct brevent_fdb_update fdb_update;
+
+ fdb_update.dev = source->dev;
+ fdb_update.mac_addr = fdb->addr.addr;
+ //FIXME
+ //__rtmsg_ifinfo(RTM_NEWLINK, br->dev, 0, GFP_ATOMIC);
+ //FIXME
+ call_brevent_notifiers(BREVENT_FDB_UPDATE, &fdb_update);
+ }
+#endif
fdb->dst = source;
fdb->updated = jiffies;
}
@@ -434,6 +467,24 @@
}
}
+#if defined(CONFIG_ARCH_COMCERTO)
+void br_fdb_register_can_expire_cb(int(*cb)(unsigned char *mac_addr, struct net_device *dev))
+{
+ spin_lock_bh(&br_fdb_cb_lock);
+ br_fdb_can_expire = cb;
+ spin_unlock_bh(&br_fdb_cb_lock);
+}
+EXPORT_SYMBOL(br_fdb_register_can_expire_cb);
+
+void br_fdb_deregister_can_expire_cb()
+{
+ spin_lock_bh(&br_fdb_cb_lock);
+ br_fdb_can_expire = NULL;
+ spin_unlock_bh(&br_fdb_cb_lock);
+}
+EXPORT_SYMBOL(br_fdb_deregister_can_expire_cb);
+#endif
+
static int fdb_to_nud(const struct net_bridge_fdb_entry *fdb)
{
if (fdb->is_local)
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 69e0c8f..eb602b9 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -43,7 +43,11 @@
{
/* ip_fragment doesn't copy the MAC header */
if (nf_bridge_maybe_copy_header(skb) ||
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb) && (!skb->ipsec_offload))) {
+#else
(packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))) {
+#endif
kfree_skb(skb);
} else {
skb_push(skb, ETH_HLEN);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 988c300..b457fd7 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -70,6 +70,10 @@
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
+#if defined(CONFIG_ARCH_COMCERTO)
+ skb->cb[4] = 0;
+#endif
+
/* The packet skb2 goes to the local host (NULL to skip). */
skb2 = NULL;
@@ -108,6 +112,10 @@
if (skb) {
if (dst) {
dst->used = jiffies;
+#if defined(CONFIG_ARCH_COMCERTO)
+ /* Used by ABM module */
+ skb->cb[4] = 1;
+#endif
br_forward(dst->dst, skb, skb2);
} else
br_flood_forward(br, skb, skb2);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 4d6fb62..4c6dbbd 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -359,6 +359,10 @@
extern void br_fdb_update(struct net_bridge *br,
struct net_bridge_port *source,
const unsigned char *addr);
+#if defined(CONFIG_ARCH_COMCERTO)
+ extern void br_fdb_register_can_expire_cb(int(*cb)(unsigned char *mac_addr, struct net_device *dev));
+ extern void br_fdb_deregister_can_expire_cb(void);
+#endif
extern int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb);
extern int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
extern int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 19308e3..43d67aa 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -121,6 +121,10 @@
if (br_is_root_bridge(br) && !wasroot)
br_become_root_bridge(br);
+
+#if defined(CONFIG_ARCH_COMCERTO)
+ call_brevent_notifiers(BREVENT_PORT_DOWN, p->dev);
+#endif
}
static void br_stp_start(struct net_bridge *br)
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 68bbf9f..624b6b8 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -128,6 +128,188 @@
goto out;
}
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+/*
+ * skb_copy_datagram_to_kernel_iovec - Copy a datagram to a kernel iovec structure.
+ * @skb: buffer to copy
+ * @offset: offset in the buffer to start copying from
+ * @to: io vector to copy to
+ * @len: amount of data to copy from buffer to iovec
+ *
+ * Note: the iovec is modified during the copy.
+ */
+
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+int skb_copy_datagram_to_kernel_iovec_soft(const struct sk_buff *skb, int offset,
+ struct iovec *to, int len)
+#else
+int skb_copy_datagram_to_kernel_iovec(const struct sk_buff *skb, int offset,
+ struct iovec *to, int len)
+#endif
+{
+ int i, fraglen, end = 0;
+ struct sk_buff *next = skb_shinfo(skb)->frag_list;
+
+ if (!len)
+ return 0;
+
+next_skb:
+ fraglen = skb_headlen(skb);
+ i = -1;
+
+ while (1) {
+ int start = end;
+
+ if ((end += fraglen) > offset) {
+ int copy = end - offset;
+ int o = offset - start;
+
+ if (copy > len)
+ copy = len;
+ if (i == -1)
+ memcpy_tokerneliovec(to, skb->data + o, copy);
+ else {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ struct page *page = skb_frag_page(frag);
+ void *p = kmap(page) + frag->page_offset + o;
+ memcpy_tokerneliovec(to, p, copy);
+ kunmap(page);
+ }
+
+ if (!(len -= copy))
+ return 0;
+ offset += copy;
+ }
+ if (++i >= skb_shinfo(skb)->nr_frags)
+ break;
+ fraglen = skb_shinfo(skb)->frags[i].size;
+ }
+ if (next) {
+ skb = next;
+ BUG_ON(skb_shinfo(skb)->frag_list);
+ next = skb->next;
+ goto next_skb;
+ }
+
+ return -EFAULT;
+}
+
+#if defined(CONFIG_COMCERTO_SPLICE_USE_MDMA)
+#include <mach/hardware.h>
+#include <mach/dma.h>
+int skb_copy_datagram_to_kernel_iovec(const struct sk_buff *skb, int offset,
+ struct iovec *to, int len)
+{
+ int i, ret, fraglen, copy, o, end = 0;
+ struct sk_buff *next = skb_shinfo(skb)->frag_list;
+
+ struct comcerto_dma_sg *sg;
+ unsigned int size;
+ int total_len, input_len;
+ if (!len)
+ return 0;
+
+ total_len = len;
+
+ size = sizeof(struct comcerto_dma_sg);
+ sg = kmalloc(size, GFP_ATOMIC);
+ if (!sg)
+ return skb_copy_datagram_to_kernel_iovec_soft (skb, offset, to, len);
+
+ comcerto_dma_sg_init(sg);
+
+next_skb:
+ fraglen = skb_headlen(skb);
+ i = -1;
+
+ while (1) {
+ int start = end;
+
+ if ((end += fraglen) > offset) {
+current_frag:
+ copy = end - offset;
+ o = offset - start;
+
+ if (copy > len)
+ copy = len;
+
+ // preparing input
+ if (i == -1) {
+ ret = comcerto_dma_sg_add_input(sg, skb->data + o, copy, 0);
+ } else {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ struct page *page = skb_frag_page(frag);
+ void *p = page_address(page) + frag->page_offset + o;
+ ret = comcerto_dma_sg_add_input(sg, p, copy, 0);
+ }
+ if (likely(ret == 0)) {
+ len -= copy;
+ offset += copy;
+ }
+
+ if ((len == 0) || unlikely(ret))
+ {
+ input_len = total_len - len;
+ len = input_len;
+ //preparing output
+ while (len > 0) {
+ if (to->iov_len) {
+ int copy = min_t(unsigned int, to->iov_len, len);
+
+ ret = comcerto_dma_sg_add_output(sg, to->iov_base, copy, 1);
+ if (unlikely(ret)) {
+ /* no clean way out, but this should never happen the way
+ * skb_copy_datagram_to_kernel_iovec is called currently.
+ */
+ comcerto_dma_sg_cleanup(sg, input_len);
+ kfree(sg);
+ return -EFAULT;
+ }
+ len -= copy;
+ to->iov_base += copy;
+ to->iov_len -= copy;
+ }
+ if (to->iov_len == 0)
+ to++;
+ }
+
+ //let's run the dma operation
+ comcerto_dma_get();
+ comcerto_dma_sg_setup(sg, input_len);
+ comcerto_dma_start();
+ comcerto_dma_wait();
+ comcerto_dma_put();
+ comcerto_dma_sg_cleanup(sg, input_len);
+
+ total_len = total_len - input_len;
+ if (total_len) {// Yes => last input fragment failed, add it again
+ comcerto_dma_sg_init(sg);
+ goto current_frag;
+ } else { //Everything copied, exit successfully
+ kfree(sg);
+ return 0;
+ }
+
+ }
+ }
+ if (++i >= skb_shinfo(skb)->nr_frags)
+ break;
+ fraglen = skb_shinfo(skb)->frags[i].size;
+ }
+ if (next) {
+ skb = next;
+ BUG_ON(skb_shinfo(skb)->frag_list);
+ next = skb->next;
+ goto next_skb;
+ }
+
+ comcerto_dma_sg_cleanup(sg, total_len - len);
+ kfree(sg);
+ return -EFAULT;
+}
+#endif
+#endif
+
/**
* __skb_recv_datagram - Receive a datagram skbuff
* @sk: socket
diff --git a/net/core/dev.c b/net/core/dev.c
index 5738654..35ac2f6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2460,6 +2460,16 @@
*/
int dev_queue_xmit(struct sk_buff *skb)
{
+#if defined(CONFIG_ARCH_COMCERTO)
+ if (skb->dev->flags & IFF_WIFI_OFLD)
+ skb->dev = skb->dev->wifi_offload_dev;
+
+ return original_dev_queue_xmit(skb);
+}
+
+int original_dev_queue_xmit(struct sk_buff *skb)
+{
+#endif
struct net_device *dev = skb->dev;
struct netdev_queue *txq;
struct Qdisc *q;
@@ -2539,6 +2549,10 @@
}
EXPORT_SYMBOL(dev_queue_xmit);
+#if defined(CONFIG_ARCH_COMCERTO)
+EXPORT_SYMBOL(original_dev_queue_xmit);
+#endif
+
/*=======================================================================
Receiver routines
@@ -3367,6 +3381,52 @@
}
EXPORT_SYMBOL(netif_receive_skb);
+#if defined(CONFIG_ARCH_COMCERTO)
+int capture_receive_skb(struct sk_buff *skb)
+{
+ struct net_device *null_or_orig = NULL;
+ struct packet_type *ptype, *pt_prev;
+ struct net_device *orig_dev;
+ int ret = NET_RX_DROP;
+
+ if (!netdev_tstamp_prequeue)
+ net_timestamp_check(skb);
+
+ if (!skb->skb_iif)
+ skb->skb_iif = skb->dev->ifindex;
+
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
+ skb_reset_mac_len(skb);
+
+ pt_prev = NULL;
+ orig_dev = skb->dev;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ptype, &ptype_all, list) {
+ if (!ptype->dev || ptype->dev == skb->dev) {
+ if (pt_prev)
+ ret = deliver_skb(skb, pt_prev, orig_dev);
+ pt_prev = ptype;
+ }
+ }
+
+ if (pt_prev) {
+ ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
+ } else {
+ kfree_skb(skb);
+ /* Jamal, now you will not able to escape explaining
+ * me how you were going to use this. :-)
+ */
+ ret = NET_RX_DROP;
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+EXPORT_SYMBOL(capture_receive_skb);
+#endif
+
/* Network device is going away, flush any packets still pending
* Called with irqs disabled.
*/
diff --git a/net/core/flow.c b/net/core/flow.c
index e318c7e..a0e9eaa 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -22,6 +22,9 @@
#include <linux/cpumask.h>
#include <linux/mutex.h>
#include <net/flow.h>
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+#include <net/xfrm.h>
+#endif
#include <linux/atomic.h>
#include <linux/security.h>
@@ -36,6 +39,10 @@
u32 genid;
struct flowi key;
struct flow_cache_object *object;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ u8 flags;
+ #define FLOW_CACHE_FLAG_IPSEC_OFFLOAD 0x01
+#endif
};
struct flow_cache_percpu {
@@ -44,6 +51,9 @@
u32 hash_rnd;
int hash_rnd_recalc;
struct tasklet_struct flush_tasklet;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ struct tasklet_struct flowcache_rem_tasklet;
+#endif
};
struct flow_flush_info {
@@ -61,6 +71,16 @@
struct timer_list rnd_timer;
};
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+struct flow_remove_info {
+ struct flow_cache *cache;
+ struct flowi *key;
+ unsigned short family;
+ unsigned short dir;
+ atomic_t cpuleft;
+ struct completion completion;
+};
+#endif
atomic_t flow_cache_genid = ATOMIC_INIT(0);
EXPORT_SYMBOL(flow_cache_genid);
static struct flow_cache flow_cache_global;
@@ -72,6 +92,10 @@
#define flow_cache_hash_size(cache) (1 << (cache)->hash_shift)
#define FLOW_HASH_RND_PERIOD (10 * 60 * HZ)
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+extern int ipsec_nlkey_flow_remove(struct flowi *fl, u16 family, u16 dir);
+#endif
+
static void flow_cache_new_hashrnd(unsigned long arg)
{
struct flow_cache *fc = (void *) arg;
@@ -110,8 +134,14 @@
list_splice_tail_init(&flow_cache_gc_list, &gc_list);
spin_unlock_bh(&flow_cache_gc_lock);
- list_for_each_entry_safe(fce, n, &gc_list, u.gc_list)
+ list_for_each_entry_safe(fce, n, &gc_list, u.gc_list) {
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ /*call nl_key_flow_remove*/
+ if (fce->flags & FLOW_CACHE_FLAG_IPSEC_OFFLOAD)
+ ipsec_nlkey_flow_remove(&fce->key, fce->family, fce->dir);
+#endif
flow_entry_kill(fce);
+ }
}
static DECLARE_WORK(flow_cache_gc_work, flow_cache_gc_task);
@@ -204,9 +234,15 @@
return 0;
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+struct flow_cache_object *
+flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir,
+ u8 *new_flow, flow_resolve_t resolver, void *ctx)
+#else
struct flow_cache_object *
flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir,
flow_resolve_t resolver, void *ctx)
+#endif
{
struct flow_cache *fc = &flow_cache_global;
struct flow_cache_percpu *fcp;
@@ -216,6 +252,11 @@
size_t keysize;
unsigned int hash;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if (new_flow)
+ *new_flow = 0;
+#endif
+
local_bh_disable();
fcp = this_cpu_ptr(fc->percpu);
@@ -281,8 +322,15 @@
flo = resolver(net, key, family, dir, flo, ctx);
if (fle) {
fle->genid = atomic_read(&flow_cache_genid);
- if (!IS_ERR(flo))
+ if (!IS_ERR(flo)) {
fle->object = flo;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if (new_flow) {
+ *new_flow = 1;
+ fle->flags |= FLOW_CACHE_FLAG_IPSEC_OFFLOAD;
+ }
+#endif
+ }
else
fle->genid--;
} else {
@@ -358,6 +406,83 @@
put_online_cpus();
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+static void flow_cache_remove_per_cpu(void *data)
+{
+ struct flow_remove_info* info = data;
+ int cpu;
+ struct tasklet_struct *tasklet;
+
+ cpu = smp_processor_id();
+ tasklet = &per_cpu_ptr(info->cache->percpu, cpu)->flowcache_rem_tasklet;
+ tasklet->data = (unsigned long)info;
+ tasklet_schedule(tasklet);
+}
+
+void flow_cache_remove_tasklet(unsigned long data)
+{
+ struct flow_remove_info* info = (struct flow_remove_info*) data;
+ struct flow_cache *fc = &flow_cache_global;
+ struct flow_cache_percpu *fcp;
+ struct flow_cache_entry *fle;
+ struct hlist_node *entry;
+ size_t keysize;
+ unsigned int hash;
+ struct flowi *key = info->key;
+ unsigned short family = info->family;
+ unsigned short dir = info->dir;
+
+ fcp = this_cpu_ptr(fc->percpu);
+
+ keysize = flow_key_size(family);
+ if (!keysize)
+ goto nocache;
+
+ hash = flow_hash_code(fc, fcp, key, keysize);
+
+ hlist_for_each_entry(fle, entry, &fcp->hash_table[hash], u.hlist) {
+ if((fle->family == family) && (fle->dir == dir) && (flow_key_compare(&fle->key, key, keysize) == 0)) {
+ hlist_del(&fle->u.hlist);
+ flow_entry_kill(fle);
+ break;
+ }
+ }
+
+nocache:
+ if (atomic_dec_and_test(&info->cpuleft))
+ complete(&info->completion);
+ return;
+}
+
+void flow_cache_remove(const struct flowi *key,
+ unsigned short family, unsigned short dir)
+{
+ struct flow_remove_info info;
+ static DEFINE_MUTEX(flow_rem_sem);
+
+ /* Don't want cpus going down or up during this. */
+ get_online_cpus();
+ mutex_lock(&flow_rem_sem);
+ info.cache = &flow_cache_global;
+ info.key = (struct flowi*)key;
+ //memcpy(&info.key, key, sizeof(struct flowi));
+ info.family = family;
+ info.dir = dir;
+ atomic_set(&info.cpuleft, num_online_cpus());
+ init_completion(&info.completion);
+
+ local_bh_disable();
+ smp_call_function(flow_cache_remove_per_cpu, &info, 1);
+ flow_cache_remove_tasklet((unsigned long)&info);
+ local_bh_enable();
+
+ wait_for_completion(&info.completion);
+ mutex_unlock(&flow_rem_sem);
+ put_online_cpus();
+}
+
+#endif
+
static void flow_cache_flush_task(struct work_struct *work)
{
flow_cache_flush();
@@ -384,6 +509,9 @@
fcp->hash_rnd_recalc = 1;
fcp->hash_count = 0;
tasklet_init(&fcp->flush_tasklet, flow_cache_flush_tasklet, 0);
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ tasklet_init(&fcp->flowcache_rem_tasklet, flow_cache_remove_tasklet, 0);
+#endif
}
return 0;
}
diff --git a/net/core/iovec.c b/net/core/iovec.c
index c40f27e..6665c0c 100644
--- a/net/core/iovec.c
+++ b/net/core/iovec.c
@@ -124,6 +124,28 @@
}
EXPORT_SYMBOL(memcpy_toiovecend);
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+/*
+ * In kernel copy to iovec. Returns -EFAULT on error.
+ *
+ * Note: this modifies the original iovec.
+ */
+void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len)
+{
+ while (len > 0) {
+ if (iov->iov_len) {
+ int copy = min_t(unsigned int, iov->iov_len, len);
+ memcpy(iov->iov_base, kdata, copy);
+ len -= copy;
+ kdata += copy;
+ iov->iov_base += copy;
+ iov->iov_len -= copy;
+ }
+ iov++;
+ }
+}
+#endif
+
/*
* Copy iovec to kernel. Returns -EFAULT on error.
*
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 7aafaed..fba58b8 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1138,6 +1138,10 @@
neigh->parms->reachable_time :
0)));
neigh->nud_state = new;
+#ifdef CONFIG_COMCERTO_FP
+ if ((old & (NUD_STALE | NUD_DELAY | NUD_PROBE)) && (new & NUD_REACHABLE))
+ notify = 1;
+#endif
}
if (lladdr != neigh->ha) {
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 2ef859a..403a253 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -622,7 +622,40 @@
return -EMSGSIZE;
}
EXPORT_SYMBOL(rtnetlink_put_metrics);
+#ifdef CONFIG_ARCH_COMCERTO
+int rtnetlink_put_metrics_2(struct sk_buff *skb, u32 *metrics, struct dst_entry *dst)
+{
+ struct nlattr *mx;
+ int i, valid = 0;
+ mx = nla_nest_start(skb, RTA_METRICS);
+ if (mx == NULL)
+ return -ENOBUFS;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (metrics[i]) {
+ valid++;
+ NLA_PUT_U32(skb, i+1, metrics[i]);
+ }
+ else if ((i + 1) == RTAX_MTU){
+ valid++;
+ NLA_PUT_U32(skb, i+1, dst_mtu(dst));
+ }
+ }
+
+ if (!valid) {
+ nla_nest_cancel(skb, mx);
+ return 0;
+ }
+
+ return nla_nest_end(skb, mx);
+
+nla_put_failure:
+ nla_nest_cancel(skb, mx);
+ return -EMSGSIZE;
+}
+EXPORT_SYMBOL(rtnetlink_put_metrics_2);
+#endif
int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
u32 ts, u32 tsage, long expires, u32 error)
{
@@ -1902,14 +1935,14 @@
return skb->len;
}
-void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
+void __rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, gfp_t flags)
{
struct net *net = dev_net(dev);
struct sk_buff *skb;
int err = -ENOBUFS;
size_t if_info_size;
- skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), GFP_KERNEL);
+ skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), flags);
if (skb == NULL)
goto errout;
@@ -1922,12 +1955,24 @@
kfree_skb(skb);
goto errout;
}
- rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+ rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags);
return;
errout:
if (err < 0)
rtnl_set_sk_err(net, RTNLGRP_LINK, err);
}
+#if defined(CONFIG_ARCH_COMCERTO)
+EXPORT_SYMBOL(__rtmsg_ifinfo);
+#endif
+
+void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
+{
+ __rtmsg_ifinfo(type, dev, change, GFP_KERNEL);
+}
+#if defined(CONFIG_COMCERTO_FP)
+EXPORT_SYMBOL(rtmsg_ifinfo);
+#endif
+
/* Protected by RTNL sempahore. */
static struct rtattr **rta_buf;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index af9c3c6..d325941 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -218,6 +218,10 @@
skb->mac_header = ~0U;
#endif
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+ skb->mspd_data = NULL;
+ skb->mspd_len = 0;
+#endif
/* make sure we initialize shinfo sequentially */
shinfo = skb_shinfo(skb);
memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));
@@ -244,6 +248,93 @@
}
EXPORT_SYMBOL(__alloc_skb);
+#if defined(CONFIG_ARCH_COMCERTO)
+/**
+ * __alloc_skb_header - allocate a network buffer
+ * @size: size to allocate
+ * @gfp_mask: allocation mask
+ * @fclone: allocate from fclone cache instead of head cache
+ * and allocate a cloned (child) skb
+ *
+ * Allocate a new &sk_buff. The returned buffer has no headroom and a
+ * tail room of size bytes. The object has a reference count of one.
+ * The return is the buffer. On a failure the return is %NULL.
+ *
+ * Buffers may only be allocated from interrupts using a @gfp_mask of
+ * %GFP_ATOMIC.
+ */
+struct sk_buff *__alloc_skb_header(unsigned int size, void *data, gfp_t gfp_mask,
+ int fclone, int node)
+{
+ struct kmem_cache *cache;
+ struct skb_shared_info *shinfo;
+ struct sk_buff *skb;
+
+ cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;
+
+ if (size <= SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) {
+ skb = NULL;
+ goto out;
+ }
+
+ /* Get the HEAD */
+ skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);
+ if (!skb)
+ goto out;
+ prefetchw(skb);
+
+ /* kmalloc might give us more room than requested.
+ * Put skb_shared_info exactly at the end of allocated zone,
+ * to allow max possible filling before reallocation.
+ */
+ size = SKB_WITH_OVERHEAD(ksize(data));
+ prefetchw(data + size);
+
+ /*
+ * Only clear those fields we need to clear, not those that we will
+ * actually initialise below. Hence, don't put any more fields after
+ * the tail pointer in struct sk_buff!
+ */
+ memset(skb, 0, offsetof(struct sk_buff, tail));
+ /* Account for allocated memory : skb + skb->head */
+ skb->truesize = SKB_TRUESIZE(size);
+ atomic_set(&skb->users, 1);
+ skb->head = data;
+ skb->data = data;
+ skb_reset_tail_pointer(skb);
+ skb->end = skb->tail + size;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+ skb->mac_header = ~0U;
+#endif
+
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+ skb->mspd_data = NULL;
+ skb->mspd_len = 0;
+#endif
+
+ /* make sure we initialize shinfo sequentially */
+ shinfo = skb_shinfo(skb);
+ memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));
+ atomic_set(&shinfo->dataref, 1);
+ kmemcheck_annotate_variable(shinfo->destructor_arg);
+
+ if (fclone) {
+ struct sk_buff *child = skb + 1;
+ atomic_t *fclone_ref = (atomic_t *) (child + 1);
+
+ kmemcheck_annotate_bitfield(child, flags1);
+ kmemcheck_annotate_bitfield(child, flags2);
+ skb->fclone = SKB_FCLONE_ORIG;
+ atomic_set(fclone_ref, 1);
+
+ child->fclone = SKB_FCLONE_UNAVAILABLE;
+ }
+out:
+ return skb;
+}
+EXPORT_SYMBOL(__alloc_skb_header);
+#endif
+
/**
* __netdev_alloc_skb - allocate an skbuff for rx on a specific device
* @dev: network device to receive on
@@ -356,6 +447,13 @@
skb_drop_fraglist(skb);
kfree(skb->head);
+
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+ if (skb->mspd_data) {
+ kfree(skb->mspd_data);
+ skb->mspd_data = NULL;
+ }
+#endif
}
}
@@ -497,6 +595,9 @@
{
struct skb_shared_info *shinfo;
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+ WARN_ON(skb->mspd_len);
+#endif
skb_release_head_state(skb);
shinfo = skb_shinfo(skb);
@@ -546,6 +647,9 @@
#ifdef CONFIG_XFRM
new->sp = secpath_get(old->sp);
#endif
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ new->ipsec_offload = old->ipsec_offload;
+#endif
memcpy(new->cb, old->cb, sizeof(old->cb));
new->csum = old->csum;
new->local_df = old->local_df;
@@ -583,6 +687,24 @@
{
#define C(x) n->x = skb->x
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+ if (skb->mspd_data) {
+ if (skb->mspd_len) {
+ int ofst = skb->len - skb->mspd_len;
+
+ memcpy(skb->data + ofst, skb->mspd_data + skb->mspd_ofst, skb->mspd_len);
+ skb->mspd_len = 0;
+ }
+
+ WARN_ON(skb_shared(skb));
+
+ if (!skb_shared(skb)) {
+ kfree(skb->mspd_data);
+ skb->mspd_data = NULL;
+ }
+ }
+#endif
+
n->next = n->prev = NULL;
n->sk = NULL;
__copy_skb_header(n, skb);
@@ -601,6 +723,13 @@
C(truesize);
atomic_set(&n->users, 1);
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+ WARN_ON(skb->mspd_data);
+ C(mspd_data);
+ C(mspd_len);
+ C(mspd_ofst);
+#endif
+
atomic_inc(&(skb_shinfo(skb)->dataref));
skb->cloned = 1;
@@ -777,6 +906,24 @@
if (!n)
return NULL;
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+ if (skb->mspd_data) {
+ if (skb->mspd_len) {
+ int ofst = skb->len - skb->mspd_len;
+
+ memcpy(skb->data + ofst, skb->mspd_data + skb->mspd_ofst, skb->mspd_len);
+ ((struct sk_buff *)skb)->mspd_len = 0;
+ }
+
+ WARN_ON(skb_shared(skb));
+
+ if (!skb_shared(skb)) {
+ kfree(skb->mspd_data);
+ ((struct sk_buff *)skb)->mspd_data = NULL;
+ }
+ }
+#endif
+
/* Set the data pointer */
skb_reserve(n, headerlen);
/* Set the tail pointer and length */
@@ -808,6 +955,10 @@
unsigned int size = skb_end_pointer(skb) - skb->head;
struct sk_buff *n = alloc_skb(size, gfp_mask);
+#if defined(CONFIG_COMCERTO_CUSTOM_SKB_LAYOUT)
+ WARN_ON(skb->mspd_len);
+#endif
+
if (!n)
goto out;
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index cbb505b..b787148 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -339,6 +339,12 @@
If unsure, say Y.
+config INET_IPSEC_OFFLOAD
+ bool "IPsec Fast Path Processing offload"
+ depends on (INET_ESP || INET_AH) && COMCERTO_FP
+ ---help---
+ Support for IPsec Fast Path offload.
+
config INET_IPCOMP
tristate "IP: IPComp transformation"
select INET_XFRM_TUNNEL
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index f2dc69c..3ad63e6 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -22,7 +22,8 @@
obj-$(CONFIG_NET_IPGRE) += ip_gre.o
obj-$(CONFIG_SYN_COOKIES) += syncookies.o
obj-$(CONFIG_INET_AH) += ah4.o
-obj-$(CONFIG_INET_ESP) += esp4.o
+#obj-$(CONFIG_INET_ESP) += esp4.o
+obj-$(CONFIG_INET_IPSEC_OFFLOAD) += esp4.o
obj-$(CONFIG_INET_IPCOMP) += ipcomp.o
obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o
obj-$(CONFIG_INET_XFRM_MODE_BEET) += xfrm4_mode_beet.o
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 0bc95f3..fedf053 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -238,7 +238,13 @@
return dst_output(skb);
}
#endif
+
+#if defined(CONFIG_INET_IPSEC_OFFLOAD)
+ if ((skb->ipsec_offload == 0) &&
+ skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
+#else
if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
+#endif
return ip_fragment(skb, ip_finish_output2);
else
return ip_finish_output2(skb);
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
index 4f47e06..0ab609d 100644
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -81,6 +81,16 @@
if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
return 0;
+
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ /* Mindspeed added WA: required to support 4o6 ipsec offload */
+ if(skb->ipsec_offload)
+ {
+ if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
+ return 0;
+ }
+#endif
+
if (xfrm_decode_session(skb, &fl, AF_INET) < 0)
return -1;
diff --git a/net/ipv4/netfilter/nf_nat_proto_common.c b/net/ipv4/netfilter/nf_nat_proto_common.c
index a3d9976..986a298 100644
--- a/net/ipv4/netfilter/nf_nat_proto_common.c
+++ b/net/ipv4/netfilter/nf_nat_proto_common.c
@@ -83,14 +83,123 @@
else
off = *rover;
- for (i = 0; ; ++off) {
- *portptr = htons(min + off % range_size);
- if (++i != range_size && nf_nat_used_tuple(tuple, ct))
- continue;
- if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM))
- *rover = off;
- return;
- }
+ /* 4rd specific range NAPT - start */
+ if ((range->flags & IP_NAT_RANGE_4RD_NAPT) && (maniptype == IP_NAT_MANIP_SRC)){
+ __be16 fix_port;
+ __be16 port_min, port_max;
+ u_int16_t port_set_id;
+ u_int16_t mbitlen = 0 ;
+ u_int16_t offsetlen = 0 ;
+ u_int16_t psidlen = 0 ;
+ u_int16_t range_total_size = 0 ;
+ u_int16_t offset_4rd;
+ u_int16_t o_state;
+ u_int16_t m_state;
+
+ u_int16_t offset_min;
+ u_int16_t offset_max;
+ u_int16_t offset_cnt;
+ u_int16_t mbit_cnt;
+
+ # define MAXBITLEN 16
+ port_min = ntohs(range->min.all);
+ port_max = ntohs(range->max.all);
+
+ for(i = 0; i < MAXBITLEN; i++){
+ if( ((port_min >> i) & 0x0001) == ((port_max >> i) & 0x0001) ){
+ psidlen++;
+ i++;
+ break;
+ }
+ else{
+ mbitlen++;
+ }
+ }
+
+ for( ; i < MAXBITLEN; i++){
+ if( ((port_min >> i) & 0x0001) == ((port_max >> i) & 0x0001) ){
+ psidlen++;
+ }
+ else{
+ break;
+ }
+ }
+
+ offsetlen = MAXBITLEN - psidlen - mbitlen;
+
+ if((psidlen == 0) || (mbitlen == 0)){
+ /* ERROR */
+ printk(KERN_INFO "4rd parameter INVALID");
+ }
+
+ port_set_id = (port_min & port_max) >> mbitlen;
+
+ if( offsetlen ){
+ if(port_min < 4096){
+ if(offsetlen > 4){
+
+
+ offset_min = 0x1000 >> (psidlen + mbitlen);
+ offset_max = (1 << offsetlen) - 1;
+ offset_cnt = offset_max - offset_min + 1;
+ mbit_cnt = 1 << mbitlen;
+ range_total_size = offset_cnt * mbit_cnt;
+ }
+ else{
+ offset_min = 0x0001;
+ offset_cnt = (1 << offsetlen) - 1;
+ mbit_cnt = 1 << mbitlen;
+ range_total_size = offset_cnt * mbit_cnt;
+ }
+ }
+ else{
+ offset_min = 0x0000;
+ offset_cnt = 1 << offsetlen;
+ mbit_cnt = 1 << mbitlen;
+ range_total_size = offset_cnt * mbit_cnt;
+ }
+ }
+ else{
+ offset_min = 0;
+ range_total_size = ( 1 << mbitlen ) ;
+ }
+
+ for (i = 0; ; ++off) {
+
+ offset_4rd = off % range_total_size ;
+
+ if ( mbitlen != 0 ){
+ o_state = offset_4rd / ( 1 << mbitlen ) ;
+ m_state = offset_4rd % ( 1 << mbitlen ) ;
+ }else{
+ o_state = offset_4rd / ( 1 << mbitlen ) ;
+ m_state = 0 ;
+ }
+
+ fix_port = (( offset_min + o_state ) << ( mbitlen + psidlen ));
+ fix_port |= ( port_set_id << mbitlen );
+ fix_port |= m_state;
+
+ *portptr = htons(fix_port);
+
+ if ((++i != range_total_size) && nf_nat_used_tuple(tuple, ct))
+ continue;
+
+ //printk(KERN_INFO "fix_port = %d ",fix_port);
+ return ;
+ }
+ } /* 4rd specific range NAPT - end */
+ else{
+
+ for (i = 0; ; ++off) {
+ *portptr = htons(min + off % range_size);
+ if (++i != range_size && nf_nat_used_tuple(tuple, ct))
+ continue;
+ if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM))
+ *rover = off;
+ return;
+ }
+ }
return;
}
EXPORT_SYMBOL_GPL(nf_nat_proto_unique_tuple);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 94cdbc5..3427f9d 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -3013,7 +3013,11 @@
if (rt->rt_dst != rt->rt_gateway)
NLA_PUT_BE32(skb, RTA_GATEWAY, rt->rt_gateway);
+#ifdef CONFIG_ARCH_COMCERTO
+ if (rtnetlink_put_metrics_2(skb, dst_metrics_ptr(&rt->dst), &rt->dst) < 0)
+#else
if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
+#endif
goto nla_put_failure;
if (rt->rt_mark)
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 11ba922..132e9fd 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -594,7 +594,15 @@
return tcp_read_sock(sk, &rd_desc, tcp_splice_data_recv);
}
-
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+unsigned int splicer_time_counter[256];
+unsigned int splicer_reqtime_counter[256];
+unsigned int splicer_data_counter[256];
+unsigned int splicer_tcp_rsock_counter[64];
+static struct timeval last_splicer;
+unsigned int init_splicer_prof = 0;
+extern unsigned int enable_splice_prof;
+#endif
/**
* tcp_splice_read - splice data from TCP socket to a pipe
* @sock: socket to splice from
@@ -620,7 +628,28 @@
long timeo;
ssize_t spliced;
int ret;
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+ struct timeval now;
+ int diff_time_ms;
+ if (enable_splice_prof) {
+ do_gettimeofday(&now);
+ if (init_splicer_prof) {
+ diff_time_ms = ((now.tv_sec - last_splicer.tv_sec) * 1000) + ((now.tv_usec - last_splicer.tv_usec) / 1000);
+ if (diff_time_ms < 1000) {
+ splicer_time_counter[diff_time_ms >> 3]++;
+ }
+ else {
+ splicer_time_counter[255]++;
+ }
+ }
+ if (len < (1 <<21))
+ splicer_data_counter[(len >> 13) & 0xFF]++;
+ else
+ splicer_data_counter[255]++;
+ last_splicer = now;
+ }
+#endif
sock_rps_record_flow(sk);
/*
* We can't seek on a socket input
@@ -631,7 +660,18 @@
ret = spliced = 0;
lock_sock(sk);
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+ /* Need locked socket*/
+ if (enable_splice_prof) {
+ const struct tcp_sock *tp = tcp_sk(sk);
+ int rsock_qsize = tp->rcv_nxt - tp->copied_seq;
+ if (rsock_qsize < (4 * 1024 * 1024))
+ splicer_tcp_rsock_counter[(rsock_qsize >> 16) & 0x3F]++;
+ else
+ splicer_tcp_rsock_counter[63]++;
+ }
+#endif
timeo = sock_rcvtimeo(sk, sock->file->f_flags & O_NONBLOCK);
while (tss.len) {
ret = __tcp_splice_read(sk, &tss);
@@ -684,6 +724,23 @@
release_sock(sk);
+#if defined(CONFIG_COMCERTO_SPLICE_PROF)
+ if (enable_splice_prof) {
+ do_gettimeofday(&now);
+
+ diff_time_ms = ((now.tv_sec - last_splicer.tv_sec) * 1000) + ((now.tv_usec - last_splicer.tv_usec) / 1000);
+ if (diff_time_ms < 1000) {//Don't record useless data
+ splicer_reqtime_counter[diff_time_ms >> 3]++;
+ }
+ else
+ splicer_reqtime_counter[255]++;
+
+ if(!init_splicer_prof)
+ init_splicer_prof = 1;
+
+ last_splicer = now;
+ }
+#endif
if (spliced)
return spliced;
@@ -1459,6 +1516,23 @@
do {
u32 offset;
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+ if (flags & MSG_NOCATCHSIG) {
+ if (signal_pending(current)) {
+ if (sigismember(¤t->pending.signal, SIGQUIT) ||
+ sigismember(¤t->pending.signal, SIGABRT) ||
+ sigismember(¤t->pending.signal, SIGKILL) ||
+ sigismember(¤t->pending.signal, SIGTERM) ||
+ sigismember(¤t->pending.signal, SIGSTOP)) {
+
+ if (copied)
+ break;
+ copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
+ break;
+ }
+ }
+ } else
+#endif
/* Are we at urgent data? Stop if we have read anything or have SIGURG pending. */
if (tp->urg_data && tp->urg_seq == *seq) {
if (copied)
@@ -1687,8 +1761,16 @@
} else
#endif
{
- err = skb_copy_datagram_iovec(skb, offset,
- msg->msg_iov, used);
+#if defined(CONFIG_COMCERTO_IMPROVED_SPLICE)
+ if (msg->msg_flags & MSG_KERNSPACE)
+ {
+ err = skb_copy_datagram_to_kernel_iovec(skb,
+ offset, msg->msg_iov, used);
+ }
+ else
+#endif
+ err = skb_copy_datagram_iovec(skb, offset,
+ msg->msg_iov, used);
if (err) {
/* Exception. Bailout! */
if (!copied)
diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c
index 8280645..2b89f0d 100644
--- a/net/ipv4/xfrm4_tunnel.c
+++ b/net/ipv4/xfrm4_tunnel.c
@@ -3,6 +3,7 @@
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
*/
+
#include <linux/skbuff.h>
#include <linux/module.h>
#include <linux/mutex.h>
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 36d7437..a348cf9 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -94,6 +94,12 @@
If unsure, say Y.
+config INET6_IPSEC_OFFLOAD
+ bool "IPsec IPv6 Fast Path Processing offload"
+ depends on (INET6_ESP && COMCERTO_FP)
+ ---help---
+ Support for IPsec IPv6 Fast Path offload.
+
config INET6_IPCOMP
tristate "IPv6: IPComp transformation"
select INET6_XFRM_TUNNEL
@@ -222,6 +228,15 @@
If unsure, say N.
+config IPV6_ETHERIP
+ tristate "EtherIP over IPv6: EtherIP-in-IPv6 tunnel"
+ depends on IPV6
+ select INET6_TUNNEL
+ ---help---
+ Support for EtherIP-in-IPv6 tunnels described in RFC 3378.
+
+ If unsure, say N.
+
config IPV6_MROUTE
bool "IPv6: multicast routing (EXPERIMENTAL)"
depends on IPV6 && EXPERIMENTAL
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 049f35e..08de35a 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -23,7 +23,8 @@
ipv6-objs += $(ipv6-y)
obj-$(CONFIG_INET6_AH) += ah6.o
-obj-$(CONFIG_INET6_ESP) += esp6.o
+#obj-$(CONFIG_INET6_ESP) += esp6.o
+obj-$(CONFIG_INET6_IPSEC_OFFLOAD) += esp6.o
obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o
obj-$(CONFIG_INET6_XFRM_TUNNEL) += xfrm6_tunnel.o
obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o
@@ -36,6 +37,7 @@
obj-$(CONFIG_IPV6_SIT) += sit.o
obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
+obj-$(CONFIG_IPV6_ETHERIP) += ethipip6.o
obj-y += addrconf_core.o exthdrs_core.o
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 65dd543..0b9e148 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -138,11 +138,26 @@
kfree(ESP_SKB_CB(skb)->tmp);
xfrm_output_resume(skb, err);
}
+static void udp_v6_send_check(struct sk_buff *skb)
+{
+ struct ipv6hdr *ipv6h=ipv6_hdr(skb);
+ struct udphdr *uh=udp_hdr(skb);
+ uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
+ IPPROTO_UDP, 0);
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ skb->ip_summed = CHECKSUM_PARTIAL;
+
+ if (uh->check == 0)
+ uh->check = CSUM_MANGLED_0;
+
+}
static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
struct ip_esp_hdr *esph;
+ struct udphdr *uh=NULL;
struct crypto_aead *aead;
struct aead_givcrypt_request *req;
struct scatterlist *sg;
@@ -228,13 +243,46 @@
esph = ip_esp_hdr(skb);
*skb_mac_header(skb) = IPPROTO_ESP;
+ /* NAT-T changes Start */
+ /* this is non-NULL only with UDP Encapsulation */
+ if (x->encap) {
+ struct xfrm_encap_tmpl *encap = x->encap;
+ __be32 *udpdata32;
+ __be16 sport, dport;
+ int encap_type;
+ spin_lock_bh(&x->lock);
+ sport = encap->encap_sport;
+ dport = encap->encap_dport;
+ encap_type = encap->encap_type;
+ spin_unlock_bh(&x->lock);
+ uh = (struct udphdr *)esph;
+ uh->source = sport;
+ uh->dest = dport;
+ uh->len = htons(skb->len - skb_transport_offset(skb));
+ uh->check = 0;
+
+ switch (encap_type) {
+ default:
+ case UDP_ENCAP_ESPINUDP:
+ esph = (struct ip_esp_hdr *)(uh + 1);
+ break;
+ case UDP_ENCAP_ESPINUDP_NON_IKE:
+ udpdata32 = (__be32 *)(uh + 1);
+ udpdata32[0] = udpdata32[1] = 0;
+ esph = (struct ip_esp_hdr *)(udpdata32 + 2);
+ break;
+ }
+ *skb_mac_header(skb) = IPPROTO_UDP;
+ }
+
+
esph->spi = x->id.spi;
esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
sg_init_table(sg, nfrags);
skb_to_sgvec(skb, sg,
- esph->enc_data + crypto_aead_ivsize(aead) - skb->data,
- clen + alen);
+ esph->enc_data + crypto_aead_ivsize(aead) - skb->data,
+ clen + alen);
if ((x->props.flags & XFRM_STATE_ESN)) {
sg_init_table(asg, 3);
@@ -249,7 +297,7 @@
aead_givcrypt_set_crypt(req, sg, sg, clen, iv);
aead_givcrypt_set_assoc(req, asg, assoclen);
aead_givcrypt_set_giv(req, esph->enc_data,
- XFRM_SKB_CB(skb)->seq.output.low);
+ XFRM_SKB_CB(skb)->seq.output.low);
ESP_SKB_CB(skb)->tmp = tmp;
err = crypto_aead_givencrypt(req);
@@ -259,6 +307,13 @@
if (err == -EBUSY)
err = NET_XMIT_DROP;
+
+ /* NAT-T changes Start */
+ if (x->encap) {
+ udp_v6_send_check(skb);
+ }
+ /* NAT-T changes End */
+
kfree(tmp);
error:
@@ -267,6 +322,7 @@
static int esp_input_done2(struct sk_buff *skb, int err)
{
+ struct ipv6hdr *ipv6h;
struct xfrm_state *x = xfrm_input_state(skb);
struct esp_data *esp = x->data;
struct crypto_aead *aead = esp->aead;
@@ -289,11 +345,48 @@
padlen = nexthdr[0];
if (padlen + 2 + alen >= elen) {
LIMIT_NETDEBUG(KERN_WARNING "ipsec esp packet is garbage "
- "padlen=%d, elen=%d\n", padlen + 2, elen - alen);
+ "padlen=%d, elen=%d\n", padlen + 2, elen - alen);
goto out;
}
/* ... check padding bits here. Silly. :-) */
+ /* NAT-T changes Start */
+ ipv6h = ipv6_hdr(skb);
+ if (x->encap) {
+ struct xfrm_encap_tmpl *encap = x->encap;
+ struct udphdr *uh = (void *)(skb_network_header(skb) + hdr_len);
+
+ /* 1) if the NAT-T peer's IP or port changed then
+ * advertize the change to the keying daemon.
+ * This is an inbound SA, so just compare
+ * SRC ports.
+ */
+ if (memcmp(&ipv6h->saddr, x->props.saddr.a6 ,sizeof(ipv6h->saddr)) ||
+ uh->source != encap->encap_sport) {
+ xfrm_address_t ipaddr;
+ memcpy(ipaddr.a6, &ipv6h->saddr, sizeof(ipaddr.a6));
+ km_new_mapping(x, &ipaddr, uh->source);
+ /* XXX: perhaps add an extra
+ * policy check here, to see
+ * if we should allow or
+ * reject a packet from a
+ * different source
+ * address/port.
+ */
+ }
+
+ /*
+ * 2) ignore UDP/TCP checksums in case
+ * of NAT-T in Transport Mode, or
+ * perform other post-processing fixes
+ * as per draft-ietf-ipsec-udp-encaps-06,
+ * section 3.1.2
+ */
+ if (x->props.mode == XFRM_MODE_TRANSPORT || x->props.mode == XFRM_MODE_BEET)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ }
+ /*NAT-T changes End*/
pskb_trim(skb, skb->len - alen - padlen - 2);
__skb_pull(skb, hlen);
@@ -577,8 +670,8 @@
u32 align;
int err;
- if (x->encap)
- return -EINVAL;
+ //if (x->encap)
+ // return -EINVAL;
esp = kzalloc(sizeof(*esp), GFP_KERNEL);
if (esp == NULL)
@@ -615,6 +708,22 @@
goto error;
}
+ /* NAT-T changes Start */
+ if (x->encap) {
+ struct xfrm_encap_tmpl *encap = x->encap;
+ switch (encap->encap_type) {
+ default:
+ goto error;
+ case UDP_ENCAP_ESPINUDP:
+ x->props.header_len += sizeof(struct udphdr);
+ break;
+ case UDP_ENCAP_ESPINUDP_NON_IKE:
+ x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32) * 4;
+ break;
+ }
+ }
+ /* NAT-T changes End */
+
align = ALIGN(crypto_aead_blocksize(aead), 4);
if (esp->padlen)
align = max_t(u32, align, esp->padlen);
diff --git a/net/ipv6/ethipip6.c b/net/ipv6/ethipip6.c
new file mode 100644
index 0000000..c1765c7
--- /dev/null
+++ b/net/ipv6/ethipip6.c
@@ -0,0 +1,1594 @@
+/*
+ * EtherIP tunnel over IPv6 link
+ * IPv6 tunneling device
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Ville Nuorvala <vnuorval@tcs.hut.fi>
+ * Yasuyuki Kozakai <kozakai@linux-ipv6.org>
+ *
+ * Based on:
+ * linux/net/ipv6/ip6_tunnel.c, linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c
+ *
+ * RFC 3378
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/capability.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/sockios.h>
+#include <linux/icmp.h>
+#include <linux/if.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/icmpv6.h>
+#include <linux/init.h>
+#include <linux/route.h>
+#include <linux/rtnetlink.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/etherdevice.h>
+
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/ip6_tunnel.h>
+#include <net/xfrm.h>
+#include <net/dsfield.h>
+#include <net/inet_ecn.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/protocol.h>
+
+MODULE_AUTHOR("Ville Nuorvala");
+MODULE_DESCRIPTION("EtherIP-in-IPv6 tunneling device");
+MODULE_LICENSE("GPL");
+
+#define ETHERIP_VERSION 0x3000
+
+#define IPV6_TNL_F_TOS_TO_TC 128
+#define IPV6_TLV_TEL_DST_SIZE 8
+
+#ifdef ETHIPIP6_TNL_DEBUG
+#define ETHIPIP6_TNL_TRACE(x...) printk(KERN_DEBUG "%s:" x "\n", __func__)
+#else
+#define ETHIPIP6_TNL_TRACE(x...) do {;} while(0)
+#endif
+
+#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
+#define IPV6_TCLASS_SHIFT 20
+
+#define HASH_SIZE 32
+
+#define HASH(addr) ((__force u32)((addr)->s6_addr32[0] ^ (addr)->s6_addr32[1] ^ \
+ (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \
+ (HASH_SIZE - 1))
+
+static void ethipip6_fb_tnl_dev_init(struct net_device *dev);
+static int ethipip6_tnl_dev_init(struct net_device *dev);
+static void ethipip6_tnl_dev_setup(struct net_device *dev);
+
+static int ethipip6_tnl_net_id __read_mostly;
+struct ip6_tnl_net {
+ /* the IPv6 tunnel fallback device */
+ struct net_device *fb_tnl_dev;
+ /* lists for storing tunnels in use */
+ struct ip6_tnl *tnls_r_l[HASH_SIZE];
+ struct ip6_tnl *tnls_wc[1];
+ struct ip6_tnl **tnls[2];
+};
+
+/*
+ * Locking : hash tables are protected by RCU and a spinlock
+ */
+static DEFINE_SPINLOCK(ethipip6_tnl_lock);
+
+static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
+{
+ struct dst_entry *dst = t->dst_cache;
+
+ if (dst && dst->obsolete &&
+ dst->ops->check(dst, t->dst_cookie) == NULL) {
+ t->dst_cache = NULL;
+ dst_release(dst);
+ return NULL;
+ }
+
+ return dst;
+}
+
+static inline void ip6_tnl_dst_reset(struct ip6_tnl *t)
+{
+ dst_release(t->dst_cache);
+ t->dst_cache = NULL;
+}
+
+static inline void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst)
+{
+ struct rt6_info *rt = (struct rt6_info *) dst;
+ t->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
+ dst_release(t->dst_cache);
+ t->dst_cache = dst;
+}
+
+/**
+ * ethipip6_tnl_lookup - fetch tunnel matching the end-point addresses
+ * @remote: the address of the tunnel exit-point
+ * @local: the address of the tunnel entry-point
+ *
+ * Return:
+ * tunnel matching given end-points if found,
+ * else fallback tunnel if its device is up,
+ * else %NULL
+ **/
+
+#define for_each_ethip6_tunnel_rcu(start) \
+ for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
+
+static struct ip6_tnl *
+ethipip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local)
+{
+ unsigned h0 = HASH(remote);
+ unsigned h1 = HASH(local);
+ struct ip6_tnl *t;
+ struct ip6_tnl_net *ip6n = net_generic(net, ethipip6_tnl_net_id);
+
+ for_each_ethip6_tunnel_rcu(ip6n->tnls_r_l[h0 ^ h1]) {
+ if (ipv6_addr_equal(local, &t->parms.laddr) &&
+ ipv6_addr_equal(remote, &t->parms.raddr) &&
+ (t->dev->flags & IFF_UP))
+ return t;
+ }
+ t = rcu_dereference(ip6n->tnls_wc[0]);
+ if (t && (t->dev->flags & IFF_UP))
+ return t;
+
+ return NULL;
+}
+
+/**
+ * ethipip6_tnl_bucket - get head of list matching given tunnel parameters
+ * @p: parameters containing tunnel end-points
+ *
+ * Description:
+ * ethipip6_tnl_bucket() returns the head of the list matching the
+ * &struct in6_addr entries laddr and raddr in @p.
+ *
+ * Return: head of IPv6 tunnel list
+ **/
+
+static struct ip6_tnl **
+ethipip6_tnl_bucket(struct ip6_tnl_net *ip6n, struct ip6_tnl_parm *p)
+{
+ struct in6_addr *remote = &p->raddr;
+ struct in6_addr *local = &p->laddr;
+ unsigned h = 0;
+ int prio = 0;
+
+ if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) {
+ prio = 1;
+ h = HASH(remote) ^ HASH(local);
+ }
+ return &ip6n->tnls[prio][h];
+}
+
+/**
+ * ethipip6_tnl_link - add tunnel to hash table
+ * @t: tunnel to be added
+ **/
+
+static void
+ethipip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)
+{
+ struct ip6_tnl **tp = ethipip6_tnl_bucket(ip6n, &t->parms);
+
+ spin_lock_bh(ðipip6_tnl_lock);
+ t->next = *tp;
+ rcu_assign_pointer(*tp, t);
+ spin_unlock_bh(ðipip6_tnl_lock);
+}
+
+/**
+ * ethipip6_tnl_unlink - remove tunnel from hash table
+ * @t: tunnel to be removed
+ **/
+
+static void
+ethipip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)
+{
+ struct ip6_tnl **tp;
+
+ for (tp = ethipip6_tnl_bucket(ip6n, &t->parms); *tp; tp = &(*tp)->next) {
+ if (t == *tp) {
+ spin_lock_bh(ðipip6_tnl_lock);
+ *tp = t->next;
+ spin_unlock_bh(ðipip6_tnl_lock);
+ break;
+ }
+ }
+}
+
+/**
+ * ip6_tnl_create() - create a new tunnel
+ * @p: tunnel parameters
+ * @pt: pointer to new tunnel
+ *
+ * Description:
+ * Create tunnel matching given parameters.
+ *
+ * Return:
+ * created tunnel or NULL
+ **/
+
+static struct ip6_tnl *ip6_tnl_create(struct net *net, struct ip6_tnl_parm *p)
+{
+ struct net_device *dev;
+ struct ip6_tnl *t;
+ char name[IFNAMSIZ];
+ int err;
+ struct ip6_tnl_net *ip6n = net_generic(net, ethipip6_tnl_net_id);
+
+ if (p->name[0])
+ strlcpy(name, p->name, IFNAMSIZ);
+ else
+ sprintf(name, "ethipip6tnl%%d");
+
+ dev = alloc_netdev(sizeof (*t), name, ethipip6_tnl_dev_setup);
+ if (dev == NULL)
+ goto failed;
+
+ dev_net_set(dev, net);
+
+ if (strchr(name, '%')) {
+ if (dev_alloc_name(dev, name) < 0)
+ goto failed_free;
+ }
+
+ t = netdev_priv(dev);
+ t->parms = *p;
+
+ if ((err = register_netdevice(dev)) < 0)
+ goto failed_free;
+
+ dev_hold(dev);
+ ethipip6_tnl_link(ip6n, t);
+ return t;
+
+failed_free:
+ free_netdev(dev);
+failed:
+ return NULL;
+}
+
+/**
+ * ethipip6_tnl_locate - find or create tunnel matching given parameters
+ * @p: tunnel parameters
+ * @create: != 0 if allowed to create new tunnel if no match found
+ *
+ * Description:
+ * ethipip6_tnl_locate() first tries to locate an existing tunnel
+ * based on @parms. If this is unsuccessful, but @create is set a new
+ * tunnel device is created and registered for use.
+ *
+ * Return:
+ * matching tunnel or NULL
+ **/
+
+static struct ip6_tnl *ethipip6_tnl_locate(struct net *net,
+ struct ip6_tnl_parm *p, int create)
+{
+ struct in6_addr *remote = &p->raddr;
+ struct in6_addr *local = &p->laddr;
+ struct ip6_tnl *t;
+ struct ip6_tnl_net *ip6n = net_generic(net, ethipip6_tnl_net_id);
+
+ for (t = *ethipip6_tnl_bucket(ip6n, p); t; t = t->next) {
+ if (ipv6_addr_equal(local, &t->parms.laddr) &&
+ ipv6_addr_equal(remote, &t->parms.raddr))
+ return t;
+ }
+ if (!create)
+ return NULL;
+ return ip6_tnl_create(net, p);
+}
+
+/**
+ * ethipip6_tnl_dev_uninit - tunnel device uninitializer
+ * @dev: the device to be destroyed
+ *
+ * Description:
+ * ethipip6_tnl_dev_uninit() removes tunnel from its list
+ **/
+
+static void
+ethipip6_tnl_dev_uninit(struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct net *net = dev_net(dev);
+ struct ip6_tnl_net *ip6n = net_generic(net, ethipip6_tnl_net_id);
+
+ if (dev == ip6n->fb_tnl_dev) {
+ spin_lock_bh(ðipip6_tnl_lock);
+ ip6n->tnls_wc[0] = NULL;
+ spin_unlock_bh(ðipip6_tnl_lock);
+ } else {
+ ethipip6_tnl_unlink(ip6n, t);
+ }
+ ip6_tnl_dst_reset(t);
+ dev_put(dev);
+}
+
+/**
+ * parse_tvl_tnl_enc_lim - handle encapsulation limit option
+ * @skb: received socket buffer
+ *
+ * Return:
+ * 0 if none was found,
+ * else index to encapsulation limit
+ **/
+
+static __u16
+parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw)
+{
+ struct ipv6hdr *ipv6h = (struct ipv6hdr *) raw;
+ __u8 nexthdr = ipv6h->nexthdr;
+ __u16 off = sizeof (*ipv6h);
+
+ while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) {
+ __u16 optlen = 0;
+ struct ipv6_opt_hdr *hdr;
+ if (raw + off + sizeof (*hdr) > skb->data &&
+ !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr)))
+ break;
+
+ hdr = (struct ipv6_opt_hdr *) (raw + off);
+ if (nexthdr == NEXTHDR_FRAGMENT) {
+ struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr;
+ if (frag_hdr->frag_off)
+ break;
+ optlen = 8;
+ } else if (nexthdr == NEXTHDR_AUTH) {
+ optlen = (hdr->hdrlen + 2) << 2;
+ } else {
+ optlen = ipv6_optlen(hdr);
+ }
+ if (nexthdr == NEXTHDR_DEST) {
+ __u16 i = off + 2;
+ while (1) {
+ struct ipv6_tlv_tnl_enc_lim *tel;
+
+ /* No more room for encapsulation limit */
+ if (i + sizeof (*tel) > off + optlen)
+ break;
+
+ tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i];
+ /* return index of option if found and valid */
+ if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT &&
+ tel->length == 1)
+ return i;
+ /* else jump to next option */
+ if (tel->type)
+ i += tel->length + 2;
+ else
+ i++;
+ }
+ }
+ nexthdr = hdr->nexthdr;
+ off += optlen;
+ }
+ return 0;
+}
+
+/**
+ * ethipip6_tnl_err - tunnel error handler
+ *
+ * Description:
+ * ethipip6_tnl_err() should handle errors in the tunnel according
+ * to the specifications in RFC 2473.
+ **/
+
+static int
+ethipip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
+ u8 *type, u8 *code, int *msg, __u32 *info, int offset)
+{
+ struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data;
+ struct ip6_tnl *t;
+ int rel_msg = 0;
+ u8 rel_type = ICMPV6_DEST_UNREACH;
+ u8 rel_code = ICMPV6_ADDR_UNREACH;
+ __u32 rel_info = 0;
+ __u16 len;
+ int err = -ENOENT;
+
+ /* If the packet doesn't contain the original IPv6 header we are
+ in trouble since we might need the source address for further
+ processing of the error. */
+
+ rcu_read_lock();
+ if ((t = ethipip6_tnl_lookup(dev_net(skb->dev), &ipv6h->daddr,
+ &ipv6h->saddr)) == NULL)
+ goto out;
+
+ if (t->parms.proto != ipproto && t->parms.proto != 0)
+ goto out;
+
+ err = 0;
+
+ switch (*type) {
+ __u32 teli;
+ struct ipv6_tlv_tnl_enc_lim *tel;
+ __u32 mtu;
+ case ICMPV6_DEST_UNREACH:
+ if (net_ratelimit())
+ printk(KERN_WARNING
+ "%s: Path to destination invalid "
+ "or inactive!\n", t->parms.name);
+ rel_msg = 1;
+ break;
+ case ICMPV6_TIME_EXCEED:
+ if ((*code) == ICMPV6_EXC_HOPLIMIT) {
+ if (net_ratelimit())
+ printk(KERN_WARNING
+ "%s: Too small hop limit or "
+ "routing loop in tunnel!\n",
+ t->parms.name);
+ rel_msg = 1;
+ }
+ break;
+ case ICMPV6_PARAMPROB:
+ teli = 0;
+ if ((*code) == ICMPV6_HDR_FIELD)
+ teli = parse_tlv_tnl_enc_lim(skb, skb->data);
+
+ if (teli && teli == *info - 2) {
+ tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
+ if (tel->encap_limit == 0) {
+ if (net_ratelimit())
+ printk(KERN_WARNING
+ "%s: Too small encapsulation "
+ "limit or routing loop in "
+ "tunnel!\n", t->parms.name);
+ rel_msg = 1;
+ }
+ } else if (net_ratelimit()) {
+ printk(KERN_WARNING
+ "%s: Recipient unable to parse tunneled "
+ "packet!\n ", t->parms.name);
+ }
+ break;
+ case ICMPV6_PKT_TOOBIG:
+ mtu = *info - offset;
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+ t->dev->mtu = mtu;
+
+ if ((len = sizeof (*ipv6h) + ntohs(ipv6h->payload_len)) > mtu) {
+ rel_type = ICMPV6_PKT_TOOBIG;
+ rel_code = 0;
+ rel_info = mtu;
+ rel_msg = 1;
+ }
+ break;
+ }
+
+ *type = rel_type;
+ *code = rel_code;
+ *info = rel_info;
+ *msg = rel_msg;
+
+out:
+ rcu_read_unlock();
+ return err;
+}
+
+static int
+ip4ethipip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
+{
+ int rel_msg = 0;
+ u8 rel_type = type;
+ u8 rel_code = code;
+ __u32 rel_info = ntohl(info);
+ int err;
+ struct sk_buff *skb2;
+ struct iphdr *eiph;
+ struct flowi fl;
+ struct rtable *rt;
+
+ err = ethipip6_tnl_err(skb, IPPROTO_IPIP, opt, &rel_type, &rel_code,
+ &rel_msg, &rel_info, offset);
+ if (err < 0)
+ return err;
+
+ if (rel_msg == 0)
+ return 0;
+
+ switch (rel_type) {
+ case ICMPV6_DEST_UNREACH:
+ if (rel_code != ICMPV6_ADDR_UNREACH)
+ return 0;
+ rel_type = ICMP_DEST_UNREACH;
+ rel_code = ICMP_HOST_UNREACH;
+ break;
+ case ICMPV6_PKT_TOOBIG:
+ if (rel_code != 0)
+ return 0;
+ rel_type = ICMP_DEST_UNREACH;
+ rel_code = ICMP_FRAG_NEEDED;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!pskb_may_pull(skb, offset + sizeof(struct iphdr)))
+ return 0;
+
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (!skb2)
+ return 0;
+
+ skb_dst_drop(skb2);
+
+ skb_pull(skb2, offset);
+ skb_reset_network_header(skb2);
+ eiph = ip_hdr(skb2);
+
+ /* Try to guess incoming interface */
+ memset(&fl, 0, sizeof(fl));
+ fl.u.ip4.daddr = eiph->saddr;
+ fl.flowi_tos = RT_TOS(eiph->tos);
+ fl.flowi_proto = IPPROTO_IPIP;
+ rt = ip_route_output_key(dev_net(skb->dev), &fl.u.ip4);
+ if (IS_ERR(rt))
+ goto out;
+
+ skb2->dev = rt->dst.dev;
+
+ /* route "incoming" packet */
+ if (rt->rt_flags & RTCF_LOCAL) {
+ ip_rt_put(rt);
+ fl.u.ip4.daddr = eiph->daddr;
+ fl.u.ip4.saddr = eiph->saddr;
+ fl.flowi_tos = eiph->tos;
+ rt = ip_route_output_key(dev_net(skb->dev), &fl.u.ip4);
+ if (IS_ERR(rt))
+ goto out;
+ if (rt->dst.dev->type != ARPHRD_TUNNEL) {
+ ip_rt_put(rt);
+ goto out;
+ }
+ skb_dst_set(skb2, (struct dst_entry *)rt);
+ } else {
+ ip_rt_put(rt);
+ if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos,
+ skb2->dev) ||
+ skb_dst(skb2)->dev->type != ARPHRD_TUNNEL)
+ goto out;
+ }
+
+ /* change mtu on this route */
+ if (rel_type == ICMP_DEST_UNREACH && rel_code == ICMP_FRAG_NEEDED) {
+ if (rel_info > dst_mtu(skb_dst(skb2)))
+ goto out;
+
+ skb_dst(skb2)->ops->update_pmtu(skb_dst(skb2), rel_info);
+ }
+
+ icmp_send(skb2, rel_type, rel_code, htonl(rel_info));
+
+out:
+ kfree_skb(skb2);
+ return 0;
+}
+
+static int
+ip6ethipip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
+{
+ int rel_msg = 0;
+ u8 rel_type = type;
+ u8 rel_code = code;
+ __u32 rel_info = ntohl(info);
+ int err;
+
+ err = ethipip6_tnl_err(skb, IPPROTO_IPV6, opt, &rel_type, &rel_code,
+ &rel_msg, &rel_info, offset);
+ if (err < 0)
+ return err;
+
+ if (rel_msg && pskb_may_pull(skb, offset + sizeof(struct ipv6hdr))) {
+ struct rt6_info *rt;
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+
+ if (!skb2)
+ return 0;
+
+ skb_dst_drop(skb2);
+ skb_pull(skb2, offset);
+ skb_reset_network_header(skb2);
+
+ /* Try to guess incoming interface */
+ rt = rt6_lookup(dev_net(skb->dev), &ipv6_hdr(skb2)->saddr,
+ NULL, 0, 0);
+
+ if (rt && rt->rt6i_dev)
+ skb2->dev = rt->rt6i_dev;
+
+ icmpv6_send(skb2, rel_type, rel_code, rel_info);
+
+ if (rt)
+ dst_release(&rt->dst);
+
+ kfree_skb(skb2);
+ }
+
+ return 0;
+}
+
+static void ip4ethipip6_dscp_ecn_decapsulate(struct ip6_tnl *t,
+ struct ipv6hdr *ipv6h,
+ struct sk_buff *skb)
+{
+ __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK;
+
+ if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
+ ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield);
+
+ if (INET_ECN_is_ce(dsfield))
+ IP_ECN_set_ce(ip_hdr(skb));
+}
+
+static void ip6ethipip6_dscp_ecn_decapsulate(struct ip6_tnl *t,
+ struct ipv6hdr *ipv6h,
+ struct sk_buff *skb)
+{
+ if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
+ ipv6_copy_dscp(ipv6_get_dsfield(ipv6h), ipv6_hdr(skb));
+
+ if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h)))
+ IP6_ECN_set_ce(ipv6_hdr(skb));
+}
+
+/* called with rcu_read_lock() */
+static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t)
+{
+ struct ip6_tnl_parm *p = &t->parms;
+ int ret = 0;
+ struct net *net = dev_net(t->dev);
+
+ if (p->flags & IP6_TNL_F_CAP_RCV) {
+ struct net_device *ldev = NULL;
+
+ if (p->link)
+ ldev = dev_get_by_index_rcu(net, p->link);
+
+ if ((ipv6_addr_is_multicast(&p->laddr) ||
+ likely(ipv6_chk_addr(net, &p->laddr, ldev, 0))) &&
+ likely(!ipv6_chk_addr(net, &p->raddr, NULL, 0)))
+ ret = 1;
+
+ }
+ return ret;
+}
+
+/**
+ * ethipip6_tnl_rcv - decapsulate IPv6 packet and retransmit it locally
+ * @skb: received socket buffer
+ * @protocol: ethernet protocol ID
+ * @dscp_ecn_decapsulate: the function to decapsulate DSCP code and ECN
+ *
+ * Return: 0
+ **/
+static int ethipip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
+ __u8 ipproto,
+ void (*dscp_ecn_decapsulate)(struct ip6_tnl *t,
+ struct ipv6hdr *ipv6h,
+ struct sk_buff *skb))
+{
+ struct ip6_tnl *t;
+ struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+
+ __u16 *etherip_ver;
+
+ if (!pskb_may_pull(skb, ETH_IPHLEN+ETH_HLEN))
+ goto discard;
+
+ etherip_ver = (__u16 *)skb->data;
+ if (*etherip_ver != htons(ETHERIP_VERSION))
+ goto discard;
+
+ rcu_read_lock();
+
+ if ((t = ethipip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr,
+ &ipv6h->daddr)) != NULL) {
+ if (t->parms.proto != ipproto && t->parms.proto != 0) {
+ rcu_read_unlock();
+ goto discard;
+ }
+
+ /* Check the xfrm policy for decrypted packets */
+ if (skb->sp && !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+ rcu_read_unlock();
+ goto discard;
+ }
+
+ if (!ip6_tnl_rcv_ctl(t)) {
+ t->dev->stats.rx_dropped++;
+ rcu_read_unlock();
+ goto discard;
+ }
+ secpath_reset(skb);
+ skb_pull(skb, ETH_IPHLEN);
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ skb_reset_network_header(skb);
+
+
+ memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+ skb->dev = t->dev;
+ skb_dst_drop(skb);
+ nf_reset(skb);
+
+ dscp_ecn_decapsulate(t, ipv6h, skb);
+
+ t->dev->stats.rx_packets++;
+ t->dev->stats.rx_bytes += skb->len;
+ netif_rx(skb);
+ rcu_read_unlock();
+ return 0;
+ }
+ rcu_read_unlock();
+
+discard:
+ kfree_skb(skb);
+ return 0;
+}
+
+static int ip4ethipip6_rcv(struct sk_buff *skb)
+{
+ return ethipip6_tnl_rcv(skb, ETH_P_IP, IPPROTO_ETHERIP,
+ ip4ethipip6_dscp_ecn_decapsulate);
+}
+
+static int ip6ethipip6_rcv(struct sk_buff *skb)
+{
+ return ethipip6_tnl_rcv(skb, ETH_P_IPV6, IPPROTO_ETHERIP,
+ ip6ethipip6_dscp_ecn_decapsulate);
+}
+
+struct ipv6_tel_txoption {
+ struct ipv6_txoptions ops;
+ __u8 dst_opt[8];
+};
+
+static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit)
+{
+ memset(opt, 0, sizeof(struct ipv6_tel_txoption));
+
+ opt->dst_opt[2] = IPV6_TLV_TNL_ENCAP_LIMIT;
+ opt->dst_opt[3] = 1;
+ opt->dst_opt[4] = encap_limit;
+ opt->dst_opt[5] = IPV6_TLV_PADN;
+ opt->dst_opt[6] = 1;
+
+ opt->ops.dst0opt = (struct ipv6_opt_hdr *) opt->dst_opt;
+ opt->ops.opt_nflen = 8;
+}
+
+/**
+ * ethipip6_tnl_addr_conflict - compare packet addresses to tunnel's own
+ * @t: the outgoing tunnel device
+ * @hdr: IPv6 header from the incoming packet
+ *
+ * Description:
+ * Avoid trivial tunneling loop by checking that tunnel exit-point
+ * doesn't match source of incoming packet.
+ *
+ * Return:
+ * 1 if conflict,
+ * 0 else
+ **/
+
+static inline int
+ethipip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr)
+{
+ return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
+}
+
+static inline int ethipip6_tnl_xmit_ctl(struct ip6_tnl *t)
+{
+ struct ip6_tnl_parm *p = &t->parms;
+ int ret = 0;
+ struct net *net = dev_net(t->dev);
+
+ if (p->flags & IP6_TNL_F_CAP_XMIT) {
+ struct net_device *ldev = NULL;
+
+ rcu_read_lock();
+ if (p->link)
+ ldev = dev_get_by_index_rcu(net, p->link);
+
+ if (unlikely(!ipv6_chk_addr(net, &p->laddr, ldev, 0)))
+ printk(KERN_WARNING
+ "%s xmit: Local address not yet configured!\n",
+ p->name);
+ else if (!ipv6_addr_is_multicast(&p->raddr) &&
+ unlikely(ipv6_chk_addr(net, &p->raddr, NULL, 0)))
+ printk(KERN_WARNING
+ "%s xmit: Routing loop! "
+ "Remote address found on this node!\n",
+ p->name);
+ else
+ ret = 1;
+ rcu_read_unlock();
+ }
+ return ret;
+}
+/**
+ * ethipip6_tnl_xmit2 - encapsulate packet and send
+ * @skb: the outgoing socket buffer
+ * @dev: the outgoing tunnel device
+ * @dsfield: dscp code for outer header
+ * @fl: flow of tunneled packet
+ * @encap_limit: encapsulation limit
+ * @pmtu: Path MTU is stored if packet is too big
+ *
+ * Description:
+ * Build new header and do some sanity checks on the packet before sending
+ * it.
+ *
+ * Return:
+ * 0 on success
+ * -1 fail
+ * %-EMSGSIZE message too big. return mtu in this case.
+ **/
+
+static int ethipip6_tnl_xmit2(struct sk_buff *skb,
+ struct net_device *dev,
+ __u8 dsfield,
+ struct flowi *fl,
+ int encap_limit,
+ __u32 *pmtu)
+{
+ struct net *net = dev_net(dev);
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct net_device_stats *stats = &t->dev->stats;
+ struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ struct ipv6_tel_txoption opt;
+ struct dst_entry *dst;
+ struct net_device *tdev;
+ int mtu;
+ unsigned int max_headroom = sizeof(struct ipv6hdr);
+ u8 proto;
+ int err = -1;
+ int pkt_len;
+ __u16 *etherip_ver;
+
+ if (((dst = ip6_tnl_dst_check(t)) != NULL)
+#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ && (t->genid == atomic_read(&flow_cache_genid))
+#endif
+ ) {
+ dst_hold(dst);
+ } else {
+ dst = ip6_route_output(net, NULL, &fl->u.ip6);
+
+ if(dst->error)
+ goto tx_err_link_failure;
+ dst = xfrm_lookup(net, dst, fl, NULL, 0);
+ if(IS_ERR(dst))
+ {
+ err = PTR_ERR(dst);
+ dst = NULL;
+ goto tx_err_link_failure;
+ }
+#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ t->genid = atomic_read(&flow_cache_genid);
+#endif
+ }
+
+ /* donot allow wifi specific pkts to be bridged,
+ * is there any better why to identify this condition!!!
+ * if (skb->len < 42) some thing like this...
+ * For regular ethernet pkts, iphdr is always aligned to 4bytes,
+ * so one way is to check the address alignment */
+ if (!((unsigned int)skb->data & 0x3)) {
+ stats->rx_length_errors++;
+ goto tx_err_dst_release;
+ }
+
+ tdev = dst->dev;
+
+ if (tdev == dev) {
+ stats->collisions++;
+ if (net_ratelimit())
+ printk(KERN_WARNING
+ "%s: Local routing loop detected!\n",
+ t->parms.name);
+ goto tx_err_dst_release;
+ }
+ mtu = dst_mtu(dst) - (sizeof (*ipv6h) + ETH_IPHLEN + ETH_HLEN);
+ if (encap_limit >= 0) {
+ max_headroom += 8;
+ mtu -= 8;
+ }
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+ if (skb_dst(skb))
+ skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);
+
+ /*
+ * Okay, now see if we can stuff it in the buffer as-is.
+ */
+ max_headroom += LL_RESERVED_SPACE(tdev);
+
+ if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
+ (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
+ struct sk_buff *new_skb;
+
+ if (!(new_skb = skb_realloc_headroom(skb, max_headroom)))
+ goto tx_err_dst_release;
+
+ if (skb->sk)
+ skb_set_owner_w(new_skb, skb->sk);
+ kfree_skb(skb);
+ skb = new_skb;
+ }
+ skb_dst_drop(skb);
+ skb_dst_set(skb, dst_clone(dst));
+
+ skb->transport_header = skb->network_header;
+ IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
+
+ etherip_ver = (__u16 *)skb_push(skb, ETH_IPHLEN);
+ *etherip_ver = htons(ETHERIP_VERSION);
+
+ proto = fl->flowi_proto;
+ if (encap_limit >= 0) {
+ init_tel_txopt(&opt, encap_limit);
+ ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
+ }
+ skb_push(skb, sizeof(struct ipv6hdr));
+ skb_reset_network_header(skb);
+ ipv6h = ipv6_hdr(skb);
+ *(__be32*)ipv6h = fl->u.ip6.flowlabel | htonl(0x60000000);
+ //dsfield = INET_ECN_encapsulate(0, dsfield);
+ //ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield);
+ ipv6h->hop_limit = t->parms.hop_limit;
+ ipv6h->nexthdr = proto;
+ ipv6_addr_copy(&ipv6h->saddr, &fl->u.ip6.saddr);
+ ipv6_addr_copy(&ipv6h->daddr, &fl->u.ip6.daddr);
+ nf_reset(skb);
+ pkt_len = skb->len;
+ skb->local_df = 1;
+ err = ip6_local_out(skb);
+
+ if (net_xmit_eval(err) == 0) {
+ stats->tx_bytes += pkt_len;
+ stats->tx_packets++;
+ } else {
+ stats->tx_errors++;
+ stats->tx_aborted_errors++;
+ }
+ ip6_tnl_dst_store(t, dst);
+ return 0;
+tx_err_link_failure:
+ stats->tx_carrier_errors++;
+ dst_link_failure(skb);
+tx_err_dst_release:
+ dst_release(dst);
+ return err;
+}
+
+static inline int
+__ethipip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct flowi fl;
+ __u32 mtu = 0;
+ int err;
+
+ memcpy(&fl, &t->fl, sizeof (fl));
+ fl.flowi_proto = IPPROTO_ETHERIP;
+
+ err = ethipip6_tnl_xmit2(skb, dev, 0, &fl, -1, &mtu);
+ if (err != 0) {
+ /* XXX: send ICMP error even if DF is not set. */
+ if (err == -EMSGSIZE) {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline int
+ip4ethipip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct iphdr *iph = ip_hdr(skb);
+ int encap_limit = -1;
+ struct flowi fl;
+ __u8 dsfield;
+ __u32 mtu = 0;
+ int err;
+
+
+ memcpy(&fl, &t->fl, sizeof (fl));
+ fl.flowi_proto = IPPROTO_ETHERIP;
+
+ dsfield = ipv4_get_dsfield(iph);
+
+
+ err = ethipip6_tnl_xmit2(skb, dev, dsfield, &fl, encap_limit, &mtu);
+ if (err != 0) {
+ /* XXX: send ICMP error even if DF is not set. */
+ if (err == -EMSGSIZE)
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+ htonl(mtu));
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline int
+ip6ethipip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ int encap_limit = -1;
+ //__u16 offset;
+ struct flowi fl;
+ __u8 dsfield;
+ __u32 mtu = 0;
+ int err;
+
+ memcpy(&fl, &t->fl, sizeof (fl));
+ fl.flowi_proto = IPPROTO_ETHERIP;
+
+ dsfield = ipv6_get_dsfield(ipv6h);
+
+ err = ethipip6_tnl_xmit2(skb, dev, dsfield, &fl, encap_limit, &mtu);
+ if (err != 0) {
+ if (err == -EMSGSIZE)
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ return -1;
+ }
+
+ return 0;
+}
+
+static netdev_tx_t
+ethipip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct net_device_stats *stats = &t->dev->stats;
+ int ret;
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ ret = ip4ethipip6_tnl_xmit(skb, dev);
+ break;
+ case htons(ETH_P_IPV6):
+ ret = ip6ethipip6_tnl_xmit(skb, dev);
+ break;
+ default:
+ ret = __ethipip6_tnl_xmit(skb, dev);
+ break;
+ }
+
+ if (ret < 0)
+ goto tx_err;
+
+ return NETDEV_TX_OK;
+
+tx_err:
+ stats->tx_errors++;
+ stats->tx_dropped++;
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static void ip6_tnl_set_cap(struct ip6_tnl *t)
+{
+ struct ip6_tnl_parm *p = &t->parms;
+ int ltype = ipv6_addr_type(&p->laddr);
+ int rtype = ipv6_addr_type(&p->raddr);
+
+ p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV);
+
+ if (ltype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) &&
+ rtype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) &&
+ !((ltype|rtype) & IPV6_ADDR_LOOPBACK) &&
+ (!((ltype|rtype) & IPV6_ADDR_LINKLOCAL) || p->link)) {
+ if (ltype&IPV6_ADDR_UNICAST)
+ p->flags |= IP6_TNL_F_CAP_XMIT;
+ if (rtype&IPV6_ADDR_UNICAST)
+ p->flags |= IP6_TNL_F_CAP_RCV;
+ }
+}
+
+static void ethipip6_tnl_link_config(struct ip6_tnl *t)
+{
+ struct net_device *dev = t->dev;
+ struct ip6_tnl_parm *p = &t->parms;
+ struct flowi *fl = &t->fl;
+ struct net_device *ldev = NULL;
+ struct net *net = dev_net(t->dev);
+
+ memcpy(dev->dev_addr, &p->laddr, dev->addr_len);
+ /* Make sure that dev_addr is nither mcast nor all zeros */
+ dev->dev_addr[0] &= 0xfe;
+ dev->dev_addr[0] |= 0x2;
+
+ memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
+
+ /* Set up flowi template */
+ ipv6_addr_copy(&fl->u.ip6.saddr, &p->laddr);
+ ipv6_addr_copy(&fl->u.ip6.daddr, &p->raddr);
+ fl->flowi_oif = p->link;
+ //fl->u.ip6.flowlabel = 0;
+
+ if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS))
+ fl->u.ip6.flowlabel |= IPV6_TCLASS_MASK & p->flowinfo;
+ if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL))
+ fl->u.ip6.flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo;
+
+ ip6_tnl_set_cap(t);
+
+ if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV)
+ dev->flags |= IFF_POINTOPOINT;
+ else
+ dev->flags &= ~IFF_POINTOPOINT;
+
+ dev->iflink = p->link;
+
+ /* Initialize the default mtu of tunnel with it's parent interface
+ mtu */
+ rcu_read_lock();
+ if (p->link)
+ {
+ ldev = dev_get_by_index_rcu(net, p->link);
+ if (ldev)
+ dev->mtu = ldev->mtu;
+ }
+ rcu_read_unlock();
+
+ if (p->flags & IP6_TNL_F_CAP_XMIT) {
+ int strict = (ipv6_addr_type(&p->raddr) &
+ (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
+
+ struct rt6_info *rt = rt6_lookup(dev_net(dev),
+ &p->raddr, &p->laddr,
+ p->link, strict);
+
+ if (rt == NULL)
+ return;
+
+ if (rt->rt6i_dev) {
+ dev->hard_header_len = rt->rt6i_dev->hard_header_len +
+ sizeof (struct ipv6hdr);
+
+ dev->mtu = rt->rt6i_dev->mtu; //To make bridge happy
+
+ if (dev->mtu < IPV6_MIN_MTU)
+ dev->mtu = IPV6_MIN_MTU;
+ }
+ dst_release(&rt->dst);
+ }
+}
+
+/**
+ * ethipip6_tnl_change - update the tunnel parameters
+ * @t: tunnel to be changed
+ * @p: tunnel configuration parameters
+ *
+ * Description:
+ * ethipip6_tnl_change() updates the tunnel parameters
+ **/
+
+static int
+ethipip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p)
+{
+ ipv6_addr_copy(&t->parms.laddr, &p->laddr);
+ ipv6_addr_copy(&t->parms.raddr, &p->raddr);
+ t->parms.flags = p->flags;
+ t->parms.hop_limit = p->hop_limit;
+ t->parms.encap_limit = p->encap_limit;
+ t->parms.flowinfo = p->flowinfo;
+ t->parms.link = p->link;
+ t->parms.proto = p->proto;
+ ip6_tnl_dst_reset(t);
+ ethipip6_tnl_link_config(t);
+ return 0;
+}
+
+/**
+ * ehtipip6_tnl_ioctl - configure ipv6 tunnels from userspace
+ * @dev: virtual device associated with tunnel
+ * @ifr: parameters passed from userspace
+ * @cmd: command to be performed
+ *
+ * Description:
+ * ethipip6_tnl_ioctl() is used for managing IPv6 tunnels
+ * from userspace.
+ *
+ * The possible commands are the following:
+ * %SIOCGETTUNNEL: get tunnel parameters for device
+ * %SIOCADDTUNNEL: add tunnel matching given tunnel parameters
+ * %SIOCCHGTUNNEL: change tunnel parameters to those given
+ * %SIOCDELTUNNEL: delete tunnel
+ *
+ * The fallback device "ethipip6tnl0", created during module
+ * initialization, can be used for creating other tunnel devices.
+ *
+ * Return:
+ * 0 on success,
+ * %-EFAULT if unable to copy data to or from userspace,
+ * %-EPERM if current process hasn't %CAP_NET_ADMIN set
+ * %-EINVAL if passed tunnel parameters are invalid,
+ * %-EEXIST if changing a tunnel's parameters would cause a conflict
+ * %-ENODEV if attempting to change or delete a nonexisting device
+ **/
+
+static int
+ethipip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ int err = 0;
+ struct ip6_tnl_parm p;
+ struct ip6_tnl *t = NULL;
+ struct net *net = dev_net(dev);
+ struct ip6_tnl_net *ip6n = net_generic(net, ethipip6_tnl_net_id);
+
+ switch (cmd) {
+ case SIOCGETTUNNEL:
+ if (dev == ip6n->fb_tnl_dev) {
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) {
+ err = -EFAULT;
+ break;
+ }
+ t = ethipip6_tnl_locate(net, &p, 0);
+ }
+ if (t == NULL)
+ t = netdev_priv(dev);
+ memcpy(&p, &t->parms, sizeof (p));
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) {
+ err = -EFAULT;
+ }
+ break;
+ case SIOCADDTUNNEL:
+ case SIOCCHGTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ break;
+ err = -EFAULT;
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
+ break;
+ err = -EINVAL;
+ if (p.proto != IPPROTO_ETHERIP && p.proto != 0)
+ break;
+ t = ethipip6_tnl_locate(net, &p, cmd == SIOCADDTUNNEL);
+ if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
+ if (t != NULL) {
+ if (t->dev != dev) {
+ err = -EEXIST;
+ break;
+ }
+ } else
+ t = netdev_priv(dev);
+
+ ethipip6_tnl_unlink(ip6n, t);
+ err = ethipip6_tnl_change(t, &p);
+ ethipip6_tnl_link(ip6n, t);
+ netdev_state_change(dev);
+ }
+ if (t) {
+ err = 0;
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof (p)))
+ err = -EFAULT;
+
+ } else
+ err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
+ break;
+ case SIOCDELTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ break;
+
+ if (dev == ip6n->fb_tnl_dev) {
+ err = -EFAULT;
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
+ break;
+ err = -ENOENT;
+ if ((t = ethipip6_tnl_locate(net, &p, 0)) == NULL)
+ break;
+ err = -EPERM;
+ if (t->dev == ip6n->fb_tnl_dev)
+ break;
+ dev = t->dev;
+ }
+ err = 0;
+ unregister_netdevice(dev);
+ break;
+ default:
+ err = -EINVAL;
+ }
+ return err;
+}
+
+/**
+ * ethipip6_tnl_change_mtu - change mtu manually for tunnel device
+ * @dev: virtual device associated with tunnel
+ * @new_mtu: the new mtu
+ *
+ * Return:
+ * 0 on success,
+ * %-EINVAL if mtu too small
+ **/
+
+static int
+ethipip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if (new_mtu < IPV6_MIN_MTU) {
+ return -EINVAL;
+ }
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static int ethipip6_open(struct net_device *dev)
+{
+ return 0;
+}
+
+static int ethipip6_stop(struct net_device *dev)
+{
+ return 0;
+}
+
+static const struct net_device_ops ip6_tnl_netdev_ops = {
+ .ndo_init = ethipip6_tnl_dev_init,
+ .ndo_uninit = ethipip6_tnl_dev_uninit,
+ .ndo_start_xmit = ethipip6_tnl_xmit,
+ .ndo_do_ioctl = ethipip6_tnl_ioctl,
+ .ndo_change_mtu = ethipip6_tnl_change_mtu,
+ .ndo_open = ethipip6_open,
+ .ndo_stop = ethipip6_stop,
+};
+
+/**
+ * ethipip6_tnl_dev_setup - setup virtual tunnel device
+ * @dev: virtual device associated with tunnel
+ *
+ * Description:
+ * Initialize function pointers and device parameters
+ **/
+
+static void ethipip6_tnl_dev_setup(struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+
+ dev->netdev_ops = &ip6_tnl_netdev_ops;
+ dev->destructor = free_netdev;
+
+ dev->type = ARPHRD_IPV6_IPV6_TUNNEL;
+ dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr);
+ dev->mtu = ETH_DATA_LEN - sizeof (struct ipv6hdr) - ETH_IPHLEN - ETH_HLEN;
+ dev->flags |= IFF_NOARP;
+
+ if (ipv6_addr_type(&t->parms.raddr) & IPV6_ADDR_UNICAST)
+ dev->flags |= IFF_POINTOPOINT;
+ dev->iflink = 0;
+
+ dev->addr_len = ETH_ALEN; //To make bridge happy while adding etherip iface to bridge
+ dev->features |= NETIF_F_NETNS_LOCAL;
+}
+
+
+/**
+ * ethipip6_tnl_dev_init_gen - general initializer for all tunnel devices
+ * @dev: virtual device associated with tunnel
+ **/
+
+static inline void
+ethipip6_tnl_dev_init_gen(struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ t->dev = dev;
+ strcpy(t->parms.name, dev->name);
+}
+
+/**
+ * ethipip6_tnl_dev_init - initializer for all non fallback tunnel devices
+ * @dev: virtual device associated with tunnel
+ **/
+
+static int ethipip6_tnl_dev_init(struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ ethipip6_tnl_dev_init_gen(dev);
+ ethipip6_tnl_link_config(t);
+
+ return 0;
+}
+
+/**
+ * ethipip6_fb_tnl_dev_init - initializer for fallback tunnel device
+ * @dev: fallback device
+ *
+ * Return: 0
+ **/
+
+static void ethipip6_fb_tnl_dev_init(struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct net *net = dev_net(dev);
+ struct ip6_tnl_net *ip6n = net_generic(net, ethipip6_tnl_net_id);
+
+ ethipip6_tnl_dev_init_gen(dev);
+ t->parms.proto = IPPROTO_ETHERIP;
+ dev_hold(dev);
+ ip6n->tnls_wc[0] = t;
+}
+
+static struct xfrm6_tunnel ip4ethipip6_handler = {
+ .handler = ip4ethipip6_rcv,
+ .err_handler = ip4ethipip6_err,
+ .priority = 3,
+};
+
+static struct xfrm6_tunnel ip6ethipip6_handler = {
+ .handler = ip6ethipip6_rcv,
+ .err_handler = ip6ethipip6_err,
+ .priority = 3,
+};
+
+static void ethipip6_tnl_destroy_tunnels(struct ip6_tnl_net *ip6n)
+{
+ int h;
+ struct ip6_tnl *t;
+ LIST_HEAD(list);
+
+ for (h = 0; h < HASH_SIZE; h++) {
+ t = ip6n->tnls_r_l[h];
+ while (t != NULL) {
+ unregister_netdevice_queue(t->dev, &list);
+ t = t->next;
+ }
+ }
+
+ t = ip6n->tnls_wc[0];
+ unregister_netdevice_queue(t->dev, &list);
+ unregister_netdevice_many(&list);
+}
+
+static int ethipip6_tnl_init_net(struct net *net)
+{
+ struct ip6_tnl_net *ip6n = net_generic(net, ethipip6_tnl_net_id);
+ int err;
+
+ ip6n->tnls[0] = ip6n->tnls_wc;
+ ip6n->tnls[1] = ip6n->tnls_r_l;
+
+ err = -ENOMEM;
+ ip6n->fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ethipip6tnl0",
+ ethipip6_tnl_dev_setup);
+
+ if (!ip6n->fb_tnl_dev)
+ goto err_alloc_dev;
+ dev_net_set(ip6n->fb_tnl_dev, net);
+
+ ethipip6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
+
+ err = register_netdev(ip6n->fb_tnl_dev);
+ if (err < 0)
+ goto err_register;
+ return 0;
+
+err_register:
+ free_netdev(ip6n->fb_tnl_dev);
+err_alloc_dev:
+ return err;
+}
+
+static void ethipip6_tnl_exit_net(struct net *net)
+{
+ struct ip6_tnl_net *ip6n = net_generic(net, ethipip6_tnl_net_id);
+
+ rtnl_lock();
+ ethipip6_tnl_destroy_tunnels(ip6n);
+ rtnl_unlock();
+}
+
+static struct pernet_operations ethipip6_tnl_net_ops = {
+ .init = ethipip6_tnl_init_net,
+ .exit = ethipip6_tnl_exit_net,
+ .id = ðipip6_tnl_net_id,
+ .size = sizeof(struct ip6_tnl_net),
+};
+
+static const struct inet6_protocol ethipip6_protocol = {
+ .handler = ip4ethipip6_rcv,
+ .err_handler = ip4ethipip6_err,
+ .flags = IPPROTO_ETHERIP,
+};
+
+/**
+ * ethipip6_tunnel_init - register protocol and reserve needed resources
+ *
+ * Return: 0 on success
+ **/
+
+static int __init ethipip6_tunnel_init(void)
+{
+ int err;
+
+ if (xfrm6_tunnel_register(&ip4ethipip6_handler, AF_INET)) {
+ printk(KERN_ERR "ip6_tunnel init: can't register ip4ethipip6\n");
+ err = -EAGAIN;
+ goto out;
+ }
+
+ if (xfrm6_tunnel_register(&ip6ethipip6_handler, AF_INET6)) {
+ printk(KERN_ERR "ip6_tunnel init: can't register ip6ethipip6\n");
+ err = -EAGAIN;
+ goto unreg_ip4ip6;
+ }
+
+ err = register_pernet_device(ðipip6_tnl_net_ops);
+ if (err < 0)
+ goto err_pernet;
+
+ if (inet6_add_protocol(ðipip6_protocol, IPPROTO_ETHERIP)) {
+ return -EAGAIN;
+ }
+ return 0;
+
+err_pernet:
+ xfrm6_tunnel_deregister(&ip6ethipip6_handler, AF_INET6);
+unreg_ip4ip6:
+ xfrm6_tunnel_deregister(&ip4ethipip6_handler, AF_INET);
+out:
+ return err;
+}
+
+/**
+ * ethipip6_tunnel_cleanup - free resources and unregister protocol
+ **/
+
+static void __exit ethipip6_tunnel_cleanup(void)
+{
+
+ if (inet6_del_protocol(ðipip6_protocol, IPPROTO_ETHERIP)) {
+ printk(KERN_ERR "ethipip6: can't del protocol IPPROTO_ETHERIP\n");
+ }
+
+ if (xfrm6_tunnel_deregister(&ip4ethipip6_handler, AF_INET))
+ printk(KERN_INFO "ip6_tunnel close: can't deregister ip4ethipip6\n");
+
+ if (xfrm6_tunnel_deregister(&ip6ethipip6_handler, AF_INET6))
+ printk(KERN_INFO "ip6_tunnel close: can't deregister ip6ethipip6\n");
+
+ unregister_pernet_device(ðipip6_tnl_net_ops);
+}
+
+module_init(ethipip6_tunnel_init);
+module_exit(ethipip6_tunnel_cleanup);
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index ae98e09..5d1eec4 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -152,8 +152,15 @@
static int ip6_finish_output(struct sk_buff *skb)
{
+
+#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if ((skb->ipsec_offload == 0) &&
+ (skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
+ dst_allfrag(skb_dst(skb)))
+#else
if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
- dst_allfrag(skb_dst(skb)))
+ dst_allfrag(skb_dst(skb)))
+#endif
return ip6_fragment(skb, ip6_finish_output2);
else
return ip6_finish_output2(skb);
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index d19f499..d0cf5b5 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -37,6 +37,7 @@
#include <linux/route.h>
#include <linux/rtnetlink.h>
#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter/nf_conntrack_proto_gre.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
@@ -74,6 +75,10 @@
(addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \
(HASH_SIZE - 1))
+
+#define for_each_ip6_tunnel_rcu(start) \
+ for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
+
static int ip6_tnl_dev_init(struct net_device *dev);
static void ip6_tnl_dev_setup(struct net_device *dev);
@@ -119,6 +124,550 @@
* Locking : hash tables are protected by RCU and RTNL
*/
+static struct kmem_cache *mr_kmem __read_mostly;
+int mr_kmem_alloced = 0;
+
+static inline size_t ip6_4rd_nlmsg_size(void)
+{
+ return NLMSG_ALIGN(sizeof(struct ip6_4rd_map_msg));
+}
+
+static int ip6_4rd_fill_node( struct sk_buff *skb, struct ip6_tnl_4rd_map_rule *mr,
+ u32 pid, u32 seq,int type, unsigned int flags, int reset, unsigned int ifindex)
+{
+ struct ip6_4rd_map_msg *mr_msg;
+ struct nlmsghdr *nlh;
+
+ nlh = nlmsg_put(skb, pid , seq, type, sizeof(*mr_msg), flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ mr_msg = nlmsg_data(nlh);
+ if(reset)
+ {
+ memset(mr_msg,0,sizeof(*mr_msg));
+ mr_msg->reset = 1;
+ mr_msg->ifindex = ifindex;
+
+ }
+ else
+ {
+ // memcpy(mr_msg,mr, sizeof(*mr_msg));
+ memset(mr_msg,0,sizeof(*mr_msg));
+ mr_msg->prefix = mr->prefix;
+ mr_msg->prefixlen = mr->prefixlen ;
+ ipv6_addr_copy(&mr_msg->relay_prefix, &mr->relay_prefix);
+ ipv6_addr_copy(&mr_msg->relay_suffix, &mr->relay_suffix);
+ mr_msg->relay_prefixlen = mr->relay_prefixlen ;
+ mr_msg->relay_suffixlen = mr->relay_suffixlen ;
+ mr_msg->psid_offsetlen = mr->psid_offsetlen ;
+ mr_msg->eabit_len = mr->eabit_len ;
+ mr_msg->entry_num = mr->entry_num ;
+ mr_msg->ifindex = ifindex;
+ }
+ return nlmsg_end(skb, nlh);
+
+}
+
+
+static int inet6_dump4rd_mrule(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ unsigned int h, s_h;
+ int s_idx, s_ip_idx;
+ int idx, ip_idx;
+ struct ip6_tnl_4rd_map_rule *mr ;
+ int err = 0;
+
+
+ struct ip6_tnl *t;
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+
+ s_h = cb->args[0];
+ s_idx = idx = cb->args[1];
+ s_ip_idx = ip_idx = cb->args[2];
+
+ for (h = s_h; h < HASH_SIZE ; h++, s_idx = 0) {
+ idx = 0;
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[h])
+ {
+ if (idx < s_idx)
+ goto cont_tunnel;
+ if (idx > s_idx)
+ s_ip_idx = 0;
+ ip_idx = 0;
+ read_lock(&t->ip4rd.map_lock);
+ list_for_each_entry (mr, &t->ip4rd.map_list, mr_list){
+ if (ip_idx < s_ip_idx)
+ goto cont_mr;
+ err = ip6_4rd_fill_node(skb, mr,NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,RTM_NEW4RD, NLM_F_MULTI , 0, t->dev->ifindex);
+ if (err < 0) {
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ read_unlock(&t->ip4rd.map_lock);
+ goto out;
+ }
+cont_mr:
+ ip_idx++;
+ }
+ read_unlock(&t->ip4rd.map_lock);
+cont_tunnel:
+ idx++;
+ }
+ }
+out:
+ cb->args[0] = h;
+ cb->args[1] = idx;
+ cb->args[2] = ip_idx;
+
+ return skb->len;
+}
+
+
+void ip6_4rd_notify(int event, struct ip6_tnl_4rd_map_rule *mr ,struct net_device *dev, int reset)
+{
+ struct sk_buff *skb;
+ struct net *net = dev_net(dev);
+ int err;
+
+ err = -ENOBUFS;
+
+ skb = nlmsg_new(ip6_4rd_nlmsg_size(), gfp_any());
+ if (skb == NULL)
+ goto errout;
+
+ err = ip6_4rd_fill_node(skb, mr,0,0,event, 0, reset, dev->ifindex);
+ if (err < 0) {
+ /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto errout;
+ }
+ rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR,
+ NULL, gfp_any());
+ return;
+errout:
+ if (err < 0)
+ rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err);
+}
+
+static inline void
+ip6_tnl_4rd_mr_destroy(char *f, struct ip6_tnl_4rd_map_rule *mr)
+{
+ list_del(&mr->mr_list);
+ kmem_cache_free(mr_kmem, mr);
+ --mr_kmem_alloced;
+}
+
+static int
+ip6_tnl_4rd_mr_create(struct ip6_tnl_4rd *ip4rd, struct ip6_tnl_4rd_parm *parm, struct net_device *dev)
+{
+ struct ip6_tnl_4rd_map_rule *mr ;
+ int err = 0;
+
+ write_lock_bh(&parm->map_lock);
+ list_for_each_entry (mr, &parm->map_list, mr_list){
+ if( mr->entry_num == ip4rd->entry_num ){
+ printk(KERN_DEBUG "ip6_tnl_4rd_mr_create: map rule found update");
+ mr->prefix = ip4rd->prefix ;
+ ipv6_addr_copy(&mr->relay_prefix, &ip4rd->relay_prefix);
+ ipv6_addr_copy(&mr->relay_suffix, &ip4rd->relay_suffix);
+ mr->prefixlen = ip4rd->prefixlen ;
+ mr->relay_prefixlen = ip4rd->relay_prefixlen ;
+ mr->relay_suffixlen = ip4rd->relay_suffixlen ;
+ mr->psid_offsetlen = ip4rd->psid_offsetlen ;
+ mr->eabit_len = ip4rd->eabit_len ;
+ mr->entry_num = ip4rd->entry_num ;
+ goto out;
+ }
+ }
+
+ mr = kmem_cache_alloc(mr_kmem, GFP_KERNEL);
+
+ if (!mr) {
+ printk(KERN_INFO "ip6_tnl_4rd_mr_create: kmem_cache_alloc fail");
+ err = -1 ;
+ goto out;
+ }
+
+ mr->prefix = ip4rd->prefix ;
+ ipv6_addr_copy(&mr->relay_prefix, &ip4rd->relay_prefix);
+ ipv6_addr_copy(&mr->relay_suffix, &ip4rd->relay_suffix);
+ mr->prefixlen = ip4rd->prefixlen ;
+ mr->relay_prefixlen = ip4rd->relay_prefixlen ;
+ mr->relay_suffixlen = ip4rd->relay_suffixlen ;
+ mr->psid_offsetlen = ip4rd->psid_offsetlen ;
+ mr->eabit_len = ip4rd->eabit_len ;
+ mr->entry_num = ip4rd->entry_num ;
+
+ ++mr_kmem_alloced;
+ list_add_tail(&mr->mr_list, &parm->map_list);
+
+out:
+ ip6_4rd_notify(RTM_NEW4RD,mr, dev,0); /* modified by MSPD */
+ write_unlock_bh(&parm->map_lock);
+ return err;
+}
+
+static void
+ip6_tnl_4rd_mr_delete_all(struct ip6_tnl_4rd_parm *parm, struct net_device *dev)
+{
+ struct ip6_tnl_4rd_map_rule *mr, *mr_rule;
+
+ write_lock_bh(&parm->map_lock);
+ list_for_each_entry_safe (mr, mr_rule, &parm->map_list, mr_list){
+ ip6_tnl_4rd_mr_destroy("all", mr);
+ }
+ ip6_4rd_notify(RTM_DEL4RD,mr, dev ,1);
+ write_unlock_bh(&parm->map_lock);
+
+}
+
+static int
+ip6_tnl_4rd_mr_delete(__u16 entry_num , struct ip6_tnl_4rd_parm *parm, struct net_device *dev)
+{
+ struct ip6_tnl_4rd_map_rule *mr, *mr_rule;
+ int err = -1 ;
+
+ write_lock_bh(&parm->map_lock);
+ list_for_each_entry_safe (mr, mr_rule, &parm->map_list, mr_list){
+ if( mr->entry_num == entry_num ){
+ printk(KERN_DEBUG "ip6_tnl_4rd_mr_delete: map rule found delete");
+ ip6_tnl_4rd_mr_destroy("one", mr);
+ err = 0 ;
+ break;
+ }
+ }
+ ip6_4rd_notify(RTM_DEL4RD,mr,dev,0);
+ write_unlock_bh(&parm->map_lock);
+ return err ;
+}
+
+static void
+ip6_tnl_4rd_mr_show(struct ip6_tnl_4rd_parm *parm)
+{
+ struct ip6_tnl_4rd_map_rule *mr;
+
+ printk(KERN_DEBUG "-- 4rd mapping rule list\n");
+ printk(KERN_DEBUG "-- entry num = %d \n",mr_kmem_alloced);
+
+ read_lock(&parm->map_lock);
+ list_for_each_entry(mr, &parm->map_list, mr_list){
+ printk(KERN_DEBUG "%03d : %03d.%03d.%03d.%03d/%02d %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%03d %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%03d eabit:%03d offset:%03d \n",
+ mr->entry_num,
+ (ntohl(mr->prefix) >> 24) & 0xff,
+ (ntohl(mr->prefix) >> 16) & 0xff,
+ (ntohl(mr->prefix) >> 8) & 0xff,
+ ntohl(mr->prefix) & 0xff,
+ mr->prefixlen,
+ mr->relay_prefix.s6_addr[0],
+ mr->relay_prefix.s6_addr[1],
+ mr->relay_prefix.s6_addr[2],
+ mr->relay_prefix.s6_addr[3],
+ mr->relay_prefix.s6_addr[4],
+ mr->relay_prefix.s6_addr[5],
+ mr->relay_prefix.s6_addr[6],
+ mr->relay_prefix.s6_addr[7],
+ mr->relay_prefix.s6_addr[8],
+ mr->relay_prefix.s6_addr[9],
+ mr->relay_prefix.s6_addr[10],
+ mr->relay_prefix.s6_addr[11],
+ mr->relay_prefix.s6_addr[12],
+ mr->relay_prefix.s6_addr[13],
+ mr->relay_prefix.s6_addr[14],
+ mr->relay_prefix.s6_addr[15],
+ mr->relay_prefixlen,
+ mr->relay_suffix.s6_addr[0],
+ mr->relay_suffix.s6_addr[1],
+ mr->relay_suffix.s6_addr[2],
+ mr->relay_suffix.s6_addr[3],
+ mr->relay_suffix.s6_addr[4],
+ mr->relay_suffix.s6_addr[5],
+ mr->relay_suffix.s6_addr[6],
+ mr->relay_suffix.s6_addr[7],
+ mr->relay_suffix.s6_addr[8],
+ mr->relay_suffix.s6_addr[9],
+ mr->relay_suffix.s6_addr[10],
+ mr->relay_suffix.s6_addr[11],
+ mr->relay_suffix.s6_addr[12],
+ mr->relay_suffix.s6_addr[13],
+ mr->relay_suffix.s6_addr[14],
+ mr->relay_suffix.s6_addr[15],
+ mr->relay_suffixlen,
+ mr->eabit_len,
+ mr->psid_offsetlen );
+ }
+ read_unlock(&parm->map_lock);
+}
+
+static int
+ip6_tnl_4rd_modify_daddr(struct in6_addr *daddr6, __be32 daddr4, __be16 dport4,
+ struct ip6_tnl_4rd_map_rule *mr)
+{
+ int i, pbw0, pbi0, pbi1;
+ __u32 daddr[4];
+ __u32 port_set_id = 0;
+ __u32 mask;
+ __u32 da = ntohl(daddr4);
+ __u16 dp = ntohs(dport4);
+ __u32 diaddr[4];
+ int port_set_id_len = ( mr->eabit_len ) - ( 32 - mr->prefixlen ) ;
+
+ if ( port_set_id_len < 0) {
+ printk(KERN_DEBUG "ip6_tnl_4rd_modify_daddr: PSID length ERROR %d\n", port_set_id_len);
+ return -1;
+ }
+
+ if ( port_set_id_len > 0) {
+ mask = 0xffffffff >> (32 - port_set_id_len);
+ port_set_id = ( dp >> (16 - mr->psid_offsetlen - port_set_id_len ) & mask ) ;
+ }
+
+ for (i = 0; i < 4; ++i)
+ daddr[i] = ntohl(mr->relay_prefix.s6_addr32[i])
+ | ntohl(mr->relay_suffix.s6_addr32[i]);
+
+ if( mr->prefixlen < 32 ) {
+ pbw0 = mr->relay_prefixlen >> 5;
+ pbi0 = mr->relay_prefixlen & 0x1f;
+ daddr[pbw0] |= (da << mr->prefixlen) >> pbi0;
+ pbi1 = pbi0 - mr->prefixlen;
+ if (pbi1 > 0)
+ daddr[pbw0+1] |= da << (32 - pbi1);
+ }
+ if ( port_set_id_len > 0) {
+ pbw0 = (mr->relay_prefixlen + 32 - mr->prefixlen) >> 5;
+ pbi0 = (mr->relay_prefixlen + 32 - mr->prefixlen) & 0x1f;
+ daddr[pbw0] |= (port_set_id << (32 - port_set_id_len)) >> pbi0;
+ pbi1 = pbi0 - (32 - port_set_id_len);
+ if (pbi1 > 0)
+ daddr[pbw0+1] |= port_set_id << (32 - pbi1);
+ }
+
+ memset(diaddr, 0, sizeof(diaddr));
+
+ diaddr[2] = ( da >> 8 ) ;
+ diaddr[3] = ( da << 24 ) ;
+ diaddr[3] |= ( port_set_id << 8 ) ;
+
+ for (i = 0; i < 4; ++i)
+ daddr[i] = daddr[i] | diaddr[i] ;
+
+ for (i = 0; i < 4; ++i)
+ daddr6->s6_addr32[i] = htonl(daddr[i]);
+
+ /* DBG */
+ printk(KERN_DEBUG "ip6_tnl_4rd_modify_daddr: %08x %08x %08x %08x PSID:%04x\n",
+ daddr[0], daddr[1], daddr[2], daddr[3], port_set_id);
+
+ return 0;
+}
+
+/**
+ * ip6_tnl_4rd_rcv_helper -
+ * @skb: received socket buffer
+ * @t: tunnel device
+ **/
+
+static int
+ip6_tnl_4rd_rcv_helper(struct sk_buff *skb, struct ip6_tnl *t)
+{
+ int err = 0;
+ struct iphdr *iph;
+
+ iph = ip_hdr(skb);
+
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ case IPPROTO_ICMP:
+ case IPPROTO_GRE:
+ break;
+ default:
+ err = -1;
+ break;
+ }
+
+ return err;
+}
+
+static int
+ip6_tnl_4rd_xmit_helper(struct sk_buff *skb, struct flowi6 *fl6,
+ struct ip6_tnl *t)
+{
+ int err = 0;
+ struct iphdr *iph, *icmpiph;
+ __be16 *idp;
+ struct tcphdr *tcph, *icmptcph;
+ struct udphdr *udph, *icmpudph;
+ struct icmphdr *icmph;
+ struct gre_hdr *greh;
+ __u32 mask;
+ __be16 *sportp = NULL;
+ __be32 daddr;
+ __be16 dport;
+ u8 *ptr;
+ int no_dst_chg = 0;
+ struct ip6_tnl_4rd_map_rule *mr,*mr_tmp;
+ int mr_prefixlen ;
+ int count ;
+
+ iph = ip_hdr(skb);
+
+ daddr = iph->daddr;
+ idp = &iph->id;
+
+ ptr = (u8 *)iph;
+ ptr += iph->ihl * 4;
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ tcph = (struct tcphdr *)ptr;
+ sportp = &tcph->source;
+ dport = tcph->dest;
+ break;
+ case IPPROTO_UDP:
+ udph = (struct udphdr *)ptr;
+ sportp = &udph->source;
+ dport = udph->dest;
+ break;
+ case IPPROTO_ICMP:
+ icmph = (struct icmphdr *)ptr;
+ switch (icmph->type) {
+ case ICMP_DEST_UNREACH:
+ case ICMP_SOURCE_QUENCH:
+ case ICMP_REDIRECT:
+ case ICMP_TIME_EXCEEDED:
+ case ICMP_PARAMETERPROB:
+ ptr = (u8 *)icmph;
+ ptr += sizeof(struct icmphdr);
+ icmpiph = (struct iphdr*)ptr;
+ if (ntohs(iph->tot_len) < icmpiph->ihl * 4 + 12) {
+ err = -1;
+ goto out;
+ }
+ daddr = icmpiph->saddr;
+ ptr += icmpiph->ihl * 4;
+ switch (icmpiph->protocol) {
+ case IPPROTO_TCP:
+ icmptcph = (struct tcphdr *)ptr;
+ sportp = &icmptcph->dest;
+ dport = icmptcph->source;
+ break;
+ case IPPROTO_UDP:
+ icmpudph = (struct udphdr *)ptr;
+ sportp = &icmpudph->dest;
+ dport = icmpudph->source;
+ break;
+ default:
+ err = -1;
+ goto out;
+ }
+ break;
+ default:
+ no_dst_chg = 1;
+ break;
+ }
+ break;
+ case IPPROTO_GRE:
+ greh = (struct gre_hdr *)ptr;
+ if(greh->protocol != GRE_PROTOCOL_PPTP){
+ err = -1;
+ goto out;
+ }
+ no_dst_chg = 1;
+ break;
+ default:
+ err = -1;
+ goto out;
+ }
+
+ if ( no_dst_chg == 0 ){
+
+ count = 0;
+ mr_prefixlen = 0;
+
+ read_lock(&t->ip4rd.map_lock);
+ list_for_each_entry (mr, &t->ip4rd.map_list, mr_list){
+ mask = 0xffffffff << (32 - mr->prefixlen) ;
+ if( (htonl(daddr) & mask ) == htonl( mr->prefix) ) {
+ if ( mr->prefixlen >= mr_prefixlen ){
+ mr_prefixlen = mr->prefixlen ;
+ mr_tmp = mr;
+ count++;
+ }
+ }
+ }
+
+ if (count){
+ err = ip6_tnl_4rd_modify_daddr(&fl6->daddr, daddr, dport, mr_tmp );
+ if (err){
+ read_unlock(&t->ip4rd.map_lock);
+ goto out;
+ }
+ }
+ read_unlock(&t->ip4rd.map_lock);
+
+ if(sportp && idp){
+ *idp=*sportp;
+ }
+ }
+
+ iph->check = 0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+
+ /* XXX: */
+ skb->local_df = 1;
+
+out:
+ return err;
+}
+
+/**
+ * ip6_tnl_4rd_update_parms - update 4rd parameters
+ * @t: tunnel to be updated
+ *
+ * Description:
+ * ip6_tnl_4rd_update_parms() updates 4rd parameters
+ **/
+static void
+ip6_tnl_4rd_update_parms(struct ip6_tnl *t)
+{
+ int pbw0, pbi0, pbi1;
+ __u32 d;
+
+ t->ip4rd.port_set_id_len = t->ip4rd.relay_suffixlen
+ - t->ip4rd.relay_prefixlen
+ - (32 - t->ip4rd.prefixlen);
+ pbw0 = (t->ip4rd.relay_suffixlen - t->ip4rd.port_set_id_len) >> 5;
+ pbi0 = (t->ip4rd.relay_suffixlen - t->ip4rd.port_set_id_len) & 0x1f;
+ d = (ntohl(t->parms.laddr.s6_addr32[pbw0]) << pbi0)
+ >> (32 - t->ip4rd.port_set_id_len);
+ pbi1 = pbi0 - (32 - t->ip4rd.port_set_id_len);
+
+ if (pbi1 > 0)
+ d |= ntohl(t->parms.laddr.s6_addr32[pbw0+1]) >> (32 - pbi1);
+ t->ip4rd.port_set_id = d;
+
+ /* local v4 address */
+ t->ip4rd.laddr4 = t->ip4rd.prefix;
+ pbw0 = t->ip4rd.relay_prefixlen >> 5;
+ pbi0 = t->ip4rd.relay_prefixlen & 0x1f;
+ d = (ntohl(t->parms.laddr.s6_addr32[pbw0]) << pbi0)
+ >> t->ip4rd.prefixlen;
+ pbi1 = pbi0 - t->ip4rd.prefixlen;
+ if (pbi1 > 0)
+ d |= ntohl(t->parms.laddr.s6_addr32[pbw0+1]) >> (32 - pbi1);
+ t->ip4rd.laddr4 |= htonl(d);
+ if (t->ip4rd.port_set_id_len < 0) {
+ d = ntohl(t->ip4rd.laddr4);
+ d &= 0xffffffff << -t->ip4rd.port_set_id_len;
+ t->ip4rd.laddr4 = htonl(d);
+ }
+
+}
+
+
static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
{
struct dst_entry *dst = t->dst_cache;
@@ -158,22 +707,26 @@
* else %NULL
**/
-#define for_each_ip6_tunnel_rcu(start) \
- for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
static struct ip6_tnl *
ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_addr *local)
{
- unsigned int h0 = HASH(remote);
- unsigned int h1 = HASH(local);
+// unsigned int h0 = HASH(remote); /* 4rd support, remote could be any, so hashing to be done only based on local address */
+ unsigned int h1 = HASH(local);
struct ip6_tnl *t;
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
- for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[h0 ^ h1]) {
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[h1]) {
if (ipv6_addr_equal(local, &t->parms.laddr) &&
ipv6_addr_equal(remote, &t->parms.raddr) &&
(t->dev->flags & IFF_UP))
return t;
+ if (t->ip4rd.prefix &&
+ ipv6_addr_equal(local, &t->parms.laddr) &&
+ /* ipv6_prefix_equal(remote, &t->ip4rd.relay_prefix,
+ t->ip4rd.relay_prefixlen) && 4RD D */
+ (t->dev->flags & IFF_UP))
+ return t;
}
t = rcu_dereference(ip6n->tnls_wc[0]);
if (t && (t->dev->flags & IFF_UP))
@@ -196,14 +749,14 @@
static struct ip6_tnl __rcu **
ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct ip6_tnl_parm *p)
{
- const struct in6_addr *remote = &p->raddr;
+// const struct in6_addr *remote = &p->raddr;
const struct in6_addr *local = &p->laddr;
unsigned h = 0;
int prio = 0;
- if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) {
+ if (!ipv6_addr_any(local)) { /* 4rd support, since remote address could be any address, refrain from checking based on remote */
prio = 1;
- h = HASH(remote) ^ HASH(local);
+ h = HASH(local);
}
return &ip6n->tnls[prio][h];
}
@@ -291,6 +844,9 @@
strcpy(t->parms.name, dev->name);
+ rwlock_init(&t->ip4rd.map_lock);
+ INIT_LIST_HEAD(&t->ip4rd.map_list);
+
dev_hold(dev);
ip6_tnl_link(ip6n, t);
return t;
@@ -766,6 +1322,10 @@
__skb_tunnel_rx(skb, t->dev);
dscp_ecn_decapsulate(t, ipv6h, skb);
+ if (ip6_tnl_4rd_rcv_helper(skb, t)) {
+ rcu_read_unlock();
+ goto discard;
+ }
netif_rx(skb);
@@ -898,6 +1458,7 @@
u8 proto;
int err = -1;
int pkt_len;
+ __u8 hop_limit;
if (!fl6->flowi6_mark)
dst = ip6_tnl_dst_check(t);
@@ -934,10 +1495,21 @@
mtu = IPV6_MIN_MTU;
if (skb_dst(skb))
skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);
- if (skb->len > mtu) {
- *pmtu = mtu;
- err = -EMSGSIZE;
- goto tx_err_dst_release;
+ if (!t->ip4rd.prefix) { /* 4rd support requires Post Fragmentation, so skb->len could be greater than MTU */
+ if (skb->len > mtu) {
+ *pmtu = mtu;
+ err = -EMSGSIZE;
+ goto tx_err_dst_release;
+ }
+ }
+
+ if (t->ip4rd.prefix) {
+ struct iphdr *iph;
+ iph = ip_hdr(skb);
+ hop_limit = iph->ttl;
+ }
+ else {
+ hop_limit = t->parms.hop_limit;
}
/*
@@ -977,7 +1549,7 @@
*(__be32*)ipv6h = fl6->flowlabel | htonl(0x60000000);
dsfield = INET_ECN_encapsulate(0, dsfield);
ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield);
- ipv6h->hop_limit = t->parms.hop_limit;
+ ipv6h->hop_limit = hop_limit;
ipv6h->nexthdr = proto;
ipv6_addr_copy(&ipv6h->saddr, &fl6->saddr);
ipv6_addr_copy(&ipv6h->daddr, &fl6->daddr);
@@ -1034,6 +1606,10 @@
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
+
+ if (t->ip4rd.prefix && ip6_tnl_4rd_xmit_helper(skb, &fl6, t))
+ return -1;
+
err = ip6_tnl_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
@@ -1105,6 +1681,8 @@
switch (skb->protocol) {
case htons(ETH_P_IP):
+ if (t->ip4rd.prefix && ip_defrag(skb, IP_DEFRAG_IP6_TNL_4RD))
+ return NETDEV_TX_OK;
ret = ip4ip6_tnl_xmit(skb, dev);
break;
case htons(ETH_P_IPV6):
@@ -1143,6 +1721,11 @@
if (rtype&IPV6_ADDR_UNICAST)
p->flags |= IP6_TNL_F_CAP_RCV;
}
+
+ if (t->ip4rd.prefix) {
+ p->flags |= IP6_TNL_F_CAP_XMIT;
+ p->flags |= IP6_TNL_F_CAP_RCV;
+ }
}
static void ip6_tnl_link_config(struct ip6_tnl *t)
@@ -1220,6 +1803,7 @@
t->parms.flowinfo = p->flowinfo;
t->parms.link = p->link;
t->parms.proto = p->proto;
+ ip6_tnl_4rd_update_parms(t);
ip6_tnl_dst_reset(t);
ip6_tnl_link_config(t);
return 0;
@@ -1261,6 +1845,8 @@
struct ip6_tnl *t = NULL;
struct net *net = dev_net(dev);
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ struct ip6_tnl_4rd ip4rd, *ip4rdp;
+ struct ip6_tnl_4rd_map_rule *mr;
switch (cmd) {
case SIOCGETTUNNEL:
@@ -1334,9 +1920,115 @@
err = 0;
unregister_netdevice(dev);
break;
+ case SIOCADD4RD:
+ case SIOCDEL4RD:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto done;
+
+ err = -EFAULT;
+ if (copy_from_user(&ip4rd, ifr->ifr_ifru.ifru_data, sizeof(ip4rd)))
+ goto done;
+
+ t = netdev_priv(dev);
+
+ if (cmd == SIOCADD4RD) {
+
+ __be32 prefix;
+ struct in6_addr relay_prefix, relay_suffix;
+
+ err = -EINVAL;
+
+ if (ip4rd.relay_suffixlen > 64)
+ goto done;
+
+ if (ip4rd.relay_suffixlen <= ip4rd.relay_prefixlen)
+ goto done;
+
+ prefix = ip4rd.prefix & htonl(0xffffffffUL << (32 - ip4rd.prefixlen));
+ if (prefix != ip4rd.prefix)
+ goto done;
+
+ ipv6_addr_prefix(&relay_prefix, &ip4rd.relay_prefix, ip4rd.relay_prefixlen);
+ if (!ipv6_addr_equal(&relay_prefix, &ip4rd.relay_prefix))
+ goto done;
+
+ ipv6_addr_prefix(&relay_suffix, &ip4rd.relay_suffix, ip4rd.relay_suffixlen);
+ if (!ipv6_addr_equal(&relay_suffix, &ip4rd.relay_suffix))
+ goto done;
+
+
+ err = ip6_tnl_4rd_mr_create(&ip4rd, &t->ip4rd, t->dev); /* modified by MSPD */
+
+ if ( ip4rd.entry_num == 0 ){
+
+ t->ip4rd.prefix = prefix;
+ ipv6_addr_copy(&t->ip4rd.relay_prefix, &relay_prefix);
+ ipv6_addr_copy(&t->ip4rd.relay_suffix, &relay_suffix);
+ t->ip4rd.prefixlen = ip4rd.prefixlen;
+ t->ip4rd.relay_prefixlen = ip4rd.relay_prefixlen;
+ t->ip4rd.relay_suffixlen = ip4rd.relay_suffixlen;
+ t->ip4rd.psid_offsetlen = ip4rd.psid_offsetlen;
+
+ ip6_tnl_4rd_update_parms(t);
+ ip6_tnl_dst_reset(t);
+ ip6_tnl_link_config(t);
+ }
+ /* DBG */
+ ip6_tnl_4rd_mr_show(&t->ip4rd);
+ }else if(cmd == SIOCDEL4RD){
+ if ( ip4rd.entry_num == 0 ){
+ ip6_tnl_4rd_mr_delete_all(&t->ip4rd, t->dev);
+ t->ip4rd.prefix = 0;
+ memset(&t->ip4rd.relay_prefix, 0, sizeof(t->ip4rd.relay_prefix));
+ memset(&t->ip4rd.relay_suffix, 0, sizeof(t->ip4rd.relay_suffix));
+ t->ip4rd.prefixlen = 0;
+ t->ip4rd.relay_prefixlen = 0;
+ t->ip4rd.relay_suffixlen = 0;
+ t->ip4rd.psid_offsetlen = 0;
+ t->ip4rd.laddr4 = 0;
+ t->ip4rd.port_set_id = t->ip4rd.port_set_id_len = 0;
+
+ ip6_tnl_dst_reset(t);
+ ip6_tnl_link_config(t);
+ }else{
+ err = ip6_tnl_4rd_mr_delete( ip4rd.entry_num , &t->ip4rd, t->dev); /* modified by MSPD */
+ }
+ /* DBG */
+ ip6_tnl_4rd_mr_show(&t->ip4rd);
+ }else{
+ printk(KERN_ERR "=== ioctl_cmd 0x%x \n",cmd );
+ }
+ err = 0;
+ break;
+ case SIOCGET4RD:
+ t = netdev_priv(dev);
+ ip4rdp = (struct ip6_tnl_4r *)ifr->ifr_ifru.ifru_data;
+
+ read_lock(&t->ip4rd.map_lock);
+ list_for_each_entry (mr, &t->ip4rd.map_list, mr_list){
+ ipv6_addr_copy(&ip4rd.relay_prefix, &mr->relay_prefix);
+ ipv6_addr_copy(&ip4rd.relay_suffix, &mr->relay_suffix);
+ ip4rd.prefix = mr->prefix;
+ ip4rd.relay_prefixlen = mr->relay_prefixlen;
+ ip4rd.relay_suffixlen = mr->relay_suffixlen;
+ ip4rd.prefixlen = mr->prefixlen;
+ ip4rd.eabit_len = mr->eabit_len;
+ ip4rd.psid_offsetlen = mr->psid_offsetlen;
+ ip4rd.entry_num = mr->entry_num;
+
+ if (copy_to_user(ip4rdp, &ip4rd, sizeof(ip4rd))) {
+ read_unlock(&t->ip4rd.map_lock);
+ err = -EFAULT;
+ }
+ ip4rdp++;
+ }
+ read_unlock(&t->ip4rd.map_lock);
+ break;
default:
err = -EINVAL;
}
+done:
return err;
}
@@ -1545,12 +2237,22 @@
static int __init ip6_tunnel_init(void)
{
- int err;
+ int err = 0;
+
+ mr_kmem = kmem_cache_create("ip6_tnl_4rd_map_rule",
+ sizeof(struct ip6_tnl_4rd_map_rule), 0, SLAB_HWCACHE_ALIGN,
+ NULL);
+ if (!mr_kmem)
+ {
+ err= -ENOMEM;
+ goto out_pernet;
+ }
err = register_pernet_device(&ip6_tnl_net_ops);
if (err < 0)
- goto out_pernet;
+ goto out_kmem;
+
err = xfrm6_tunnel_register(&ip4ip6_handler, AF_INET);
if (err < 0) {
printk(KERN_ERR "ip6_tunnel init: can't register ip4ip6\n");
@@ -1563,12 +2265,18 @@
goto out_ip6ip6;
}
+ err =__rtnl_register(PF_UNSPEC, RTM_GET4RD, NULL , inet6_dump4rd_mrule, NULL);
+ if(err < 0)
+ goto out_ip6ip6;
+
return 0;
out_ip6ip6:
xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET);
out_ip4ip6:
unregister_pernet_device(&ip6_tnl_net_ops);
+out_kmem:
+ kmem_cache_destroy(mr_kmem);
out_pernet:
return err;
}
@@ -1585,7 +2293,12 @@
if (xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6))
printk(KERN_INFO "ip6_tunnel close: can't deregister ip6ip6\n");
+ kmem_cache_destroy(mr_kmem);
+
unregister_pernet_device(&ip6_tnl_net_ops);
+
+ rtnl_unregister(PF_UNSPEC, RTM_GET4RD);
+
}
module_init(ip6_tunnel_init);
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index f792b34..95d651c 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -205,6 +205,14 @@
If unsure, say N.
+config IP6_NF_TARGET_NPT
+ tristate "NPT (Network Prefix translation) target support"
+ help
+ This option adds the `SNPT' and `DNPT' target, which perform
+ stateless IPv6-to-IPv6 Network Prefix Translation per RFC 6296.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
endif # IP6_NF_IPTABLES
endmenu
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index abfee91..91b0806 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -31,4 +31,5 @@
# targets
obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
+obj-$(CONFIG_IP6_NF_TARGET_NPT) += ip6t_NPT.o
obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o
diff --git a/net/ipv6/netfilter/ip6t_NPT.c b/net/ipv6/netfilter/ip6t_NPT.c
new file mode 100644
index 0000000..2698150
--- /dev/null
+++ b/net/ipv6/netfilter/ip6t_NPT.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2011, 2012 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter_ipv6/ip6t_NPT.h>
+#include <linux/netfilter/x_tables.h>
+#include <net/netfilter/nf_conntrack.h>
+
+static int ip6t_npt_checkentry(const struct xt_tgchk_param *par)
+{
+ struct ip6t_npt_tginfo *npt = par->targinfo;
+ __wsum src_sum = 0, dst_sum = 0;
+ struct in6_addr pfx;
+ unsigned int i;
+
+ if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64)
+ return -EINVAL;
+
+ /* Ensure that LSB of prefix is zero */
+ ipv6_addr_prefix(&pfx, &npt->src_pfx.in6, npt->src_pfx_len);
+ if (!ipv6_addr_equal(&pfx, &npt->src_pfx.in6))
+ return -EINVAL;
+ ipv6_addr_prefix(&pfx, &npt->dst_pfx.in6, npt->dst_pfx_len);
+ if (!ipv6_addr_equal(&pfx, &npt->dst_pfx.in6))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(npt->src_pfx.in6.s6_addr16); i++) {
+ src_sum = csum_add(src_sum,
+ (__force __wsum)npt->src_pfx.in6.s6_addr16[i]);
+ dst_sum = csum_add(dst_sum,
+ (__force __wsum)npt->dst_pfx.in6.s6_addr16[i]);
+ }
+
+ npt->adjustment = ~csum_fold(csum_sub(src_sum, dst_sum));
+ return 0;
+}
+
+static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt,
+ struct in6_addr *addr)
+{
+ unsigned int pfx_len;
+ unsigned int i, idx;
+ __be32 mask;
+ __sum16 sum;
+
+ pfx_len = max(npt->src_pfx_len, npt->dst_pfx_len);
+ for (i = 0; i < pfx_len; i += 32) {
+ if (pfx_len - i >= 32)
+ mask = 0;
+ else
+ mask = htonl((1 << (i - pfx_len + 32)) - 1);
+
+ idx = i / 32;
+ addr->s6_addr32[idx] &= mask;
+ addr->s6_addr32[idx] |= ~mask & npt->dst_pfx.in6.s6_addr32[idx];
+ }
+
+ if (pfx_len <= 48)
+ idx = 3;
+ else {
+ for (idx = 4; idx < ARRAY_SIZE(addr->s6_addr16); idx++) {
+ if ((__force __sum16)addr->s6_addr16[idx] !=
+ CSUM_MANGLED_0)
+ break;
+ }
+ if (idx == ARRAY_SIZE(addr->s6_addr16))
+ return false;
+ }
+
+ sum = ~csum_fold(csum_add(csum_unfold((__force __sum16)addr->s6_addr16[idx]),
+ csum_unfold(npt->adjustment)));
+ if (sum == CSUM_MANGLED_0)
+ sum = 0;
+ *(__force __sum16 *)&addr->s6_addr16[idx] = sum;
+
+ return true;
+}
+
+static unsigned int
+ip6t_snpt_tg(struct sk_buff *skb, const struct xt_action_param *par)
+{
+ const struct ip6t_npt_tginfo *npt = par->targinfo;
+ struct nf_conn *ct;
+ enum ip_conntrack_info ctinfo;
+
+ if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->saddr)) {
+ icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD,
+ offsetof(struct ipv6hdr, saddr));
+ return NF_DROP;
+ }
+#ifdef CONFIG_COMCERTO_FP
+ ct = nf_ct_get(skb, &ctinfo);
+ if (ct && ((ctinfo == IP_CT_NEW) || (ctinfo == IP_CT_RELATED))) {
+ rcu_read_lock();
+ memcpy(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.in6, &ipv6_hdr(skb)->saddr, sizeof(struct in6_addr));
+ rcu_read_unlock();
+ }
+#endif
+ return XT_CONTINUE;
+}
+
+static unsigned int
+ip6t_dnpt_tg(struct sk_buff *skb, const struct xt_action_param *par)
+{
+ const struct ip6t_npt_tginfo *npt = par->targinfo;
+ struct nf_conn *ct;
+ enum ip_conntrack_info ctinfo;
+
+ if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->daddr)) {
+ icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD,
+ offsetof(struct ipv6hdr, daddr));
+ return NF_DROP;
+ }
+#ifdef CONFIG_COMCERTO_FP
+ ct = nf_ct_get(skb, &ctinfo);
+ if (ct && ((ctinfo == IP_CT_NEW) || (ctinfo == IP_CT_RELATED))) {
+ rcu_read_lock();
+ memcpy(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.in6, &ipv6_hdr(skb)->daddr, sizeof(struct in6_addr));
+ rcu_read_unlock();
+ }
+#endif
+ return XT_CONTINUE;
+}
+
+static struct xt_target ip6t_npt_target_reg[] __read_mostly = {
+ {
+ .name = "SNPT",
+ .target = ip6t_snpt_tg,
+ .targetsize = sizeof(struct ip6t_npt_tginfo),
+ .checkentry = ip6t_npt_checkentry,
+ .family = NFPROTO_IPV6,
+ .hooks = (1 << NF_INET_LOCAL_IN) |
+ (1 << NF_INET_POST_ROUTING),
+ .me = THIS_MODULE,
+ },
+ {
+ .name = "DNPT",
+ .target = ip6t_dnpt_tg,
+ .targetsize = sizeof(struct ip6t_npt_tginfo),
+ .checkentry = ip6t_npt_checkentry,
+ .family = NFPROTO_IPV6,
+ .hooks = (1 << NF_INET_PRE_ROUTING) |
+ (1 << NF_INET_LOCAL_OUT),
+ .me = THIS_MODULE,
+ },
+};
+
+static int __init ip6t_npt_init(void)
+{
+ return xt_register_targets(ip6t_npt_target_reg,
+ ARRAY_SIZE(ip6t_npt_target_reg));
+}
+
+static void __exit ip6t_npt_exit(void)
+{
+ xt_unregister_targets(ip6t_npt_target_reg,
+ ARRAY_SIZE(ip6t_npt_target_reg));
+}
+
+module_init(ip6t_npt_init);
+module_exit(ip6t_npt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6-to-IPv6 Network Prefix Translation (RFC 6296)");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS("ip6t_SNPT");
+MODULE_ALIAS("ip6t_DNPT");
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 2e21751..eb608ef 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2440,8 +2440,11 @@
ipv6_addr_copy(&saddr_buf, &rt->rt6i_prefsrc.addr);
NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
}
-
+#ifdef CONFIG_ARCH_COMCERTO
+ if (rtnetlink_put_metrics_2(skb, dst_metrics_ptr(&rt->dst), &rt->dst) < 0)
+#else
if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
+#endif
goto nla_put_failure;
rcu_read_lock();
@@ -2503,6 +2506,7 @@
struct rtmsg *rtm;
struct flowi6 fl6;
int err, iif = 0;
+ int flags = 0;
err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy);
if (err < 0)
@@ -2531,6 +2535,9 @@
if (tb[RTA_OIF])
fl6.flowi6_oif = nla_get_u32(tb[RTA_OIF]);
+ if (tb[RTA_MARK])
+ fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
+
if (iif) {
struct net_device *dev;
dev = __dev_get_by_index(net, iif);
@@ -2538,6 +2545,14 @@
err = -ENODEV;
goto errout;
}
+
+ fl6.flowi6_iif = iif;
+
+ if (!ipv6_addr_any(&fl6.saddr))
+ flags |= RT6_LOOKUP_F_HAS_SADDR;
+
+ if (rt6_need_strict(&fl6.daddr) && dev->type != ARPHRD_PIMREG)
+ flags |= RT6_LOOKUP_F_IFACE;
}
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
@@ -2552,7 +2567,11 @@
skb_reset_mac_header(skb);
skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
- rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6);
+ if (iif)
+ rt = (struct rt6_info*) fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_input);
+ else
+ rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6);
+
skb_dst_set(skb, &rt->dst);
err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif,
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 8c25419..2be1dee 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -515,6 +515,36 @@
if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
goto drop;
+
+ /* NAT-T changes Start */
+ if ( up->encap_type ) {
+ /*
+ * This is an encapsulation socket, so pass the skb to
+ * the socket's udp6_encap_rcv() hook.Otherwise, just
+ * fall through and pass this up the UDP socket.
+ * up->encap_rcv() returns the following value:
+ * =0 if skb was successfully passed to the encap
+ * handler or was discarded by it.
+ * >0 if skb should be passed on to UDP.
+ * <0 if skb should be resubmitted as proto -N
+ */
+
+ /* if we're overly short, let UDP handle it */
+ if (skb->len > sizeof(struct udphdr) &&
+ up->encap_rcv != NULL) {
+ int ret;
+
+ ret = (*up->encap_rcv)(sk, skb);
+ if (ret <= 0) {
+ UDP_INC_STATS_BH(sock_net(sk),
+ UDP_MIB_INDATAGRAMS,
+ is_udplite);
+ return -ret;
+ }
+ /* FALLTHROUGH -- it's a UDP Packet */
+ }
+ }
+
/*
* UDP-Lite specific tests, ignored on UDP sockets (see net/ipv4/udp.c).
*/
@@ -1240,24 +1270,111 @@
}
/*
+* Socket option code for UDP for IPV6
+*/
+int udp6_lib_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, unsigned int optlen,
+ int (*push_pending_frames)(struct sock *))
+{
+ struct udp_sock *up = udp_sk(sk);
+ int val;
+ int err = 0;
+ int is_udplite = IS_UDPLITE(sk);
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+
+ switch (optname) {
+ case UDP_CORK:
+ if (val != 0) {
+ up->corkflag = 1;
+ } else {
+ up->corkflag = 0;
+ lock_sock(sk);
+ (*push_pending_frames)(sk);
+ release_sock(sk);
+ }
+ break;
+
+ case UDP_ENCAP:
+ switch (val) {
+ case 0:
+ case UDP_ENCAP_ESPINUDP:
+ case UDP_ENCAP_ESPINUDP_NON_IKE:
+ up->encap_rcv = xfrm6_udp_encap_rcv;
+ /* FALLTHROUGH */
+ case UDP_ENCAP_L2TPINUDP:
+ up->encap_type = val;
+ break;
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+ break;
+
+ /*
+ * UDP-Lite's partial checksum coverage (RFC 3828).
+ */
+ /* The sender sets actual checksum coverage length via this option.
+ * The case coverage > packet length is handled by send module. */
+ case UDPLITE_SEND_CSCOV:
+ if (!is_udplite) /* Disable the option on UDP sockets */
+ return -ENOPROTOOPT;
+ if (val != 0 && val < 8) /* Illegal coverage: use default (8) */
+ val = 8;
+ else if (val > USHRT_MAX)
+ val = USHRT_MAX;
+ up->pcslen = val;
+ up->pcflag |= UDPLITE_SEND_CC;
+ break;
+
+ /* The receiver specifies a minimum checksum coverage value. To make
+ * sense, this should be set to at least 8 (as done below). If zero is
+ * used, this again means full checksum coverage. */
+ case UDPLITE_RECV_CSCOV:
+ if (!is_udplite) /* Disable the option on UDP sockets */
+ return -ENOPROTOOPT;
+ if (val != 0 && val < 8) /* Avoid silly minimal values. */
+ val = 8;
+ else if (val > USHRT_MAX)
+ val = USHRT_MAX;
+ up->pcrlen = val;
+ up->pcflag |= UDPLITE_RECV_CC;
+ break;
+
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ return err;
+}
+
+EXPORT_SYMBOL(udp6_lib_setsockopt);
+
+
+/*
* Socket option code for UDP
*/
int udpv6_setsockopt(struct sock *sk, int level, int optname,
- char __user *optval, unsigned int optlen)
+ char __user *optval, unsigned int optlen)
{
if (level == SOL_UDP || level == SOL_UDPLITE)
- return udp_lib_setsockopt(sk, level, optname, optval, optlen,
- udp_v6_push_pending_frames);
+ return udp6_lib_setsockopt(sk, level, optname, optval, optlen,
+ udp_v6_push_pending_frames);
return ipv6_setsockopt(sk, level, optname, optval, optlen);
}
#ifdef CONFIG_COMPAT
int compat_udpv6_setsockopt(struct sock *sk, int level, int optname,
- char __user *optval, unsigned int optlen)
+ char __user *optval, unsigned int optlen)
{
if (level == SOL_UDP || level == SOL_UDPLITE)
- return udp_lib_setsockopt(sk, level, optname, optval, optlen,
- udp_v6_push_pending_frames);
+ return udp6_lib_setsockopt(sk, level, optname, optval, optlen,
+ udp_v6_push_pending_frames);
return compat_ipv6_setsockopt(sk, level, optname, optval, optlen);
}
#endif
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index f8c3cf8..e23b7ef 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -21,6 +21,15 @@
return xfrm6_extract_header(skb);
}
+int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
+ int encap_type)
+{
+ XFRM_SPI_SKB_CB(skb)->family = AF_INET6;
+ XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
+ return xfrm_input(skb, nexthdr, spi, encap_type);
+}
+EXPORT_SYMBOL(xfrm6_rcv_encap);
+
int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
{
XFRM_SPI_SKB_CB(skb)->family = AF_INET6;
@@ -46,6 +55,106 @@
ip6_rcv_finish);
return -1;
}
+/* If it's a keepalive packet, then just eat it.
+ * If it's an encapsulated packet, then pass it to the
+ * IPsec xfrm input.
+ * Returns 0 if skb passed to xfrm or was dropped.
+ * Returns >0 if skb should be passed to UDP.
+ * Returns <0 if skb should be resubmitted (-ret is protocol)
+ */
+int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
+{
+
+#ifndef CONFIG_XFRM
+ return 1;
+#else
+ struct udp_sock *up = udp_sk(sk);
+ struct udphdr *uh;
+ struct ipv6hdr *iph;
+ int iphlen, len;
+
+ __u8 *udpdata;
+ __be32 *udpdata32;
+ __u16 encap_type = up->encap_type;
+
+
+
+ /* if this is not encapsulated socket, then just return now */
+ if (!encap_type)
+ return 1;
+
+ /* If this is a paged skb, make sure we pull up
+ * whatever data we need to look at. */
+ len = skb->len - sizeof(struct udphdr);
+ if (!pskb_may_pull(skb, sizeof(struct udphdr) + min(len, 8)))
+ return 1;
+
+ /* Now we can get the pointers */
+ uh = udp_hdr(skb);
+ udpdata = (__u8 *)uh + sizeof(struct udphdr);
+ udpdata32 = (__be32 *)udpdata;
+
+ switch (encap_type) {
+ default:
+ case UDP_ENCAP_ESPINUDP:
+ /* Check if this is a keepalive packet. If so, eat it. */
+ if (len == 1 && udpdata[0] == 0xff) {
+ goto drop;
+ } else if (len > sizeof(struct ip_esp_hdr) && udpdata32[0] != 0) {
+ /* ESP Packet without Non-ESP header */
+ len = sizeof(struct udphdr);
+ } else
+ /* Must be an IKE packet.. pass it through */
+ return 1;
+ break;
+ case UDP_ENCAP_ESPINUDP_NON_IKE:
+ /* Check if this is a keepalive packet. If so, eat it. */
+ if (len == 1 && udpdata[0] == 0xff) {
+ goto drop;
+ } else if (len > 2 * sizeof(u32) + sizeof(struct ip_esp_hdr) &&
+ udpdata32[0] == 0 && udpdata32[1] == 0) {
+
+ /* ESP Packet with Non-IKE marker */
+ len = sizeof(struct udphdr) + 2 * (sizeof(u32) * 4);
+ } else
+ /* Must be an IKE packet.. pass it through */
+ return 1;
+ break;
+ }
+
+ /* At this point we are sure that this is an ESPinUDP packet,
+ * so we need to remove 'len' bytes from the packet (the UDP
+ * header and optional ESP marker bytes) and then modify the
+ * protocol to ESP, and then call into the transform receiver.
+ */
+ if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)){
+ goto drop;
+ }
+
+ /* Now we can update and verify the packet length... */
+ iph = ipv6_hdr(skb);
+ iphlen = ntohs(iph->payload_len);
+ if (skb->len < iphlen) {
+ /* packet is too small!?! */
+ goto drop;
+ }
+
+ /* pull the data buffer up to the ESP header and set the
+ * transport header to point to ESP. Keep UDP on the stack
+ * for later.
+ */
+ __skb_pull(skb, len);
+ skb_reset_transport_header(skb);
+
+ /* process ESP */
+ return xfrm6_rcv_encap(skb, IPPROTO_ESP, 0, encap_type);
+
+drop:
+ kfree_skb(skb);
+ return 0;
+#endif
+}
+
int xfrm6_rcv(struct sk_buff *skb)
{
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index d879f7e..aa83e48 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -123,11 +123,17 @@
{
struct flowi6 *fl6 = &fl->u.ip6;
int onlyproto = 0;
- u16 offset = skb_network_header_len(skb);
- const struct ipv6hdr *hdr = ipv6_hdr(skb);
+ /* use the reassembled packet if conntrack has done the reassembly */
+#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
+ struct sk_buff *whole_skb = (skb->nfct_reasm) ? skb->nfct_reasm : skb;
+#else
+ struct sk_buff *whole_skb = skb;
+#endif
+ u16 offset = skb_network_header_len(whole_skb);
+ const struct ipv6hdr *hdr = ipv6_hdr(whole_skb);
struct ipv6_opt_hdr *exthdr;
- const unsigned char *nh = skb_network_header(skb);
- u8 nexthdr = nh[IP6CB(skb)->nhoff];
+ const unsigned char *nh = skb_network_header(whole_skb);
+ u8 nexthdr = nh[IP6CB(whole_skb)->nhoff];
memset(fl6, 0, sizeof(struct flowi6));
fl6->flowi6_mark = skb->mark;
@@ -135,9 +141,9 @@
ipv6_addr_copy(&fl6->daddr, reverse ? &hdr->saddr : &hdr->daddr);
ipv6_addr_copy(&fl6->saddr, reverse ? &hdr->daddr : &hdr->saddr);
- while (nh + offset + 1 < skb->data ||
- pskb_may_pull(skb, nh + offset + 1 - skb->data)) {
- nh = skb_network_header(skb);
+ while (nh + offset + 1 < whole_skb->data ||
+ pskb_may_pull(whole_skb, nh + offset + 1 - whole_skb->data)) {
+ nh = skb_network_header(whole_skb);
exthdr = (struct ipv6_opt_hdr *)(nh + offset);
switch (nexthdr) {
@@ -156,8 +162,8 @@
case IPPROTO_TCP:
case IPPROTO_SCTP:
case IPPROTO_DCCP:
- if (!onlyproto && (nh + offset + 4 < skb->data ||
- pskb_may_pull(skb, nh + offset + 4 - skb->data))) {
+ if (!onlyproto && (nh + offset + 4 < whole_skb->data ||
+ pskb_may_pull(whole_skb, nh + offset + 4 - whole_skb->data))) {
__be16 *ports = (__be16 *)exthdr;
fl6->fl6_sport = ports[!!reverse];
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 1e733e9..1a72eb2 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -30,8 +30,184 @@
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/xfrm.h>
+#include <net/netlink.h>
#include <net/sock.h>
+#include <net/ip6_route.h>
+
+#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD)
+#define NLKEY_SUPPORT 1
+#else
+#undef NLKEY_SUPPORT
+#endif
+
+#ifdef NLKEY_SUPPORT
+#include <net/dsfield.h>
+#include <net/inet_ecn.h>
+#include <net/ipv6.h>
+
+
+extern struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos,
+ xfrm_address_t *prev_saddr,
+ xfrm_address_t *prev_daddr,
+ int family);
+extern int xfrm_get_tos(struct flowi *fl, int family);
+extern int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle,
+ const struct flowi *fl, u16 family, u16 dir);
+
+
+#define NLKEY_SA_CREATE 0x0A01
+#define NLKEY_SA_DELETE 0x0A02
+#define NLKEY_SA_FLUSH 0x0A03
+#define NLKEY_SA_SET_KEYS 0x0A04
+#define NLKEY_SA_SET_TUNNEL 0x0A05
+#define NLKEY_SA_SET_NATT 0x0A06
+#define NLKEY_SA_SET_STATE 0x0A07
+#define NLKEY_SA_SET_LIFETIME 0x0A08
+#define NLKEY_SA_NOTIFY 0x0A09
+#define NLKEY_SA_INFO_UPDATE 0x0A0C
+#define NLKEY_FLOW_ADD 0x0A11
+#define NLKEY_FLOW_REMOVE 0x0A12
+#define NLKEY_FLOW_NOTIFY 0x0A13
+#define NLKEY_NULL_MSG 0x0000
+
+#define NLKEY_HDR_LEN 4
+#define NLKEY_MSG_LEN 256
+
+#define NLKEY_MAX_NUM_KEYS 2
+#define NLKEY_MAX_KEY_LEN (256 / 8)
+
+struct nlkey_msg {
+ /* message data */
+ unsigned short fcode;
+ unsigned short length;
+ unsigned short payload[(NLKEY_MSG_LEN /sizeof(unsigned short))];
+};
+/* sizeof(nlkey_msg) = 4 + 256 */
+
+struct nlkey_sa_id {
+ unsigned int spi;
+ unsigned char sa_type;
+ unsigned char proto_family;
+ unsigned char replay_window;
+#define NLKEY_SAFLAGS_ESN 0x1
+ unsigned char flags;
+ unsigned int dst_ip[4];
+ unsigned int src_ip[4];
+ unsigned short mtu;
+ unsigned short dev_mtu;
+
+};
+/* sizeof(nlkey_sa_id) = 24 */
+
+struct nlkey_sa_create {
+ unsigned short sagd;
+ unsigned short rsvd;
+ struct nlkey_sa_id said;
+};
+/* sizeof(nlkey_sa_delete) = 28 */
+
+struct nlkey_sa_delete {
+ unsigned short sagd;
+ unsigned short rsvd;
+};
+/* sizeof(nlkey_sa_delete) = 4 */
+
+struct nlkey_sa_set_tunnel {
+ unsigned short sagd;
+ unsigned char rsvd;
+ unsigned char proto_family;
+ union {
+ struct iphdr ipv4h;
+ struct ipv6hdr ipv6h;
+ } h;
+};
+/* sizeof(nlkey_sa_set_tunnel) = 36 */
+
+struct nlkey_sa_set_natt {
+ unsigned short sagd;
+ unsigned short sport;
+ unsigned short dport;
+ unsigned short rsvd;
+};
+/* sizeof(nlkey_sa_set_natt) = 4 */
+
+struct nlkey_sa_set_state {
+ unsigned short sagd;
+ unsigned short rsvd;
+ unsigned short state;
+ unsigned short rsvd2;
+};
+/* sizeof(nlkey_sa_set_natt) = 8 */
+
+struct nlkey_key_desc {
+ unsigned short key_bits;
+ unsigned char key_alg;
+ unsigned char key_type;
+ unsigned char key[NLKEY_MAX_KEY_LEN];
+};
+/* sizeof(nlkey_key_desc) = 36 */
+
+struct nlkey_sa_set_keys {
+ unsigned short sagd;
+ unsigned short rsvd;
+ unsigned short num_keys;
+ unsigned short rsvd2;
+ struct nlkey_key_desc keys[NLKEY_MAX_NUM_KEYS];
+};
+/* sizeof(nlkey_sa_set_keys) = 80 */
+
+struct nlkey_lifetime_desc {
+ unsigned int allocations;
+ unsigned int bytes[2];
+};
+/* sizeof(nlkey_sa_set_lifetime) = 12 */
+
+struct nlkey_sa_set_lifetime {
+ unsigned short sagd;
+ unsigned short rsvd;
+ struct nlkey_lifetime_desc hard_time;
+ struct nlkey_lifetime_desc soft_time;
+ struct nlkey_lifetime_desc current_time;
+};
+/* sizeof(nlkey_sa_set_lifetime) = 40 */
+
+/* SA notifications */
+#define IPSEC_SOFT_EXPIRE 0
+#define IPSEC_HARD_EXPIRE 1
+
+struct nlkey_sa_notify {
+ unsigned short sagd;
+ unsigned short rsvd;
+ unsigned int action;
+};
+/* sizeof(nlkey_sa_notify) = 8 */
+
+/* SA Info update */
+
+struct nlkey_sa_info {
+ unsigned short sagd;
+ unsigned short rsvd;
+ unsigned long long bytes;
+ unsigned long long packets;
+};
+/* sizeof(nlkey_sa_info) = */
+
+
+static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c);
+static void ipsec_nlkey_rcv(struct sk_buff *skb);
+static void ipsec_nlkey_init(void);
+static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code);
+static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x,
+ const struct km_event *c, unsigned short *msg_id);
+static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x, const struct km_event *c, struct nlkey_sa_id *said);
+
+/* netlink NETLINK_KEY socket */
+struct sock *nlkey_socket = NULL;
+
+#endif
+/************************************************************************************/
+
#define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))
#define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))
@@ -837,6 +1013,8 @@
sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP;
if (x->props.flags & XFRM_STATE_NOPMTUDISC)
sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC;
+ if (x->props.flags & XFRM_STATE_ESN)
+ sa->sadb_sa_flags |= SADB_SAFLAGS_ESN;
/* hard time */
if (hsc & 2) {
@@ -1104,6 +1282,8 @@
x->props.flags |= XFRM_STATE_DECAP_DSCP;
if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC)
x->props.flags |= XFRM_STATE_NOPMTUDISC;
+ if (sa->sadb_sa_flags & SADB_SAFLAGS_ESN)
+ x->props.flags |= XFRM_STATE_ESN;
lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD - 1];
if (lifetime != NULL) {
@@ -2959,6 +3139,7 @@
out_hdr->sadb_msg_pid = 0;
pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
+
return 0;
}
@@ -2967,6 +3148,12 @@
struct net *net = x ? xs_net(x) : c->net;
struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
+#ifdef NLKEY_SUPPORT
+ /* send message to the user space through NETLINK_KEY socket*/
+ ipsec_nlkey_send(net, x, c);
+#endif
+
if (atomic_read(&net_pfkey->socks_nr) == 0)
return 0;
@@ -3748,6 +3935,593 @@
.migrate = pfkey_send_migrate,
};
+
+#ifdef NLKEY_SUPPORT
+extern struct xfrm_state *xfrm_state_lookup_byhandle(struct net *net, u16 handle);
+
+static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code)
+{
+ unsigned nlkey_code;
+
+ switch (sa_code)
+ {
+ case XFRM_MSG_DELSA:
+ nlkey_code = NLKEY_SA_DELETE;
+ break;
+ case XFRM_MSG_NEWSA:
+ case XFRM_MSG_UPDSA:
+ nlkey_code = NLKEY_SA_CREATE;
+ break;
+ case XFRM_MSG_FLUSHSA:
+ nlkey_code = NLKEY_SA_FLUSH;
+ break;
+ case XFRM_MSG_EXPIRE:
+ nlkey_code = NLKEY_SA_SET_STATE;
+ break;
+ default:
+ nlkey_code = NLKEY_NULL_MSG;
+ break;
+ }
+
+ return nlkey_code;
+}
+
+static void ipsec_nlkey_rcv(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh = NULL;
+ struct nlkey_msg *msg = NULL;
+ struct flowi flow;
+ unsigned short *p;
+ unsigned short family, dir;
+ struct xfrm_state *x;
+ struct nlkey_sa_notify sa_notify_msg;
+ struct nlkey_sa_info sa_info_msg;
+
+ /* extract message from skb */
+ nlh = (struct nlmsghdr *)skb->data;
+
+ msg = (struct nlkey_msg *)NLMSG_DATA(nlh);
+
+ //printk(KERN_INFO "ipsec_nlkey_rcv fcode: 0x%x length: %d bytes\n",msg->fcode,msg->length);
+
+ /* process command received from user space */
+ switch(msg->fcode)
+ {
+ case NLKEY_FLOW_REMOVE:
+ //printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_FLOW_REMOVE\n");
+ p = msg->payload;
+ memcpy(&flow, p, sizeof(struct flowi)); p += sizeof(struct flowi)/2;
+ family = *p; p++;
+ dir = *p; p++;
+ flow_cache_remove(&flow, family, dir);
+ break;
+
+ case NLKEY_SA_NOTIFY:
+ //printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_SA_NOTIFY\n");
+ memcpy(&sa_notify_msg, msg->payload, sizeof(struct nlkey_sa_notify));
+ x = xfrm_state_lookup_byhandle(&init_net, sa_notify_msg.sagd);
+ if (x) {
+ spin_lock(&x->lock);
+
+ if (sa_notify_msg.action) {
+ // hard expired
+ x->km.state = XFRM_STATE_EXPIRED;
+ tasklet_hrtimer_start(&x->mtimer, ktime_set(0,0), HRTIMER_MODE_REL);
+ }
+ else if (!x->km.dying) {
+ x->km.dying = 1;
+ km_state_expired(x, 0, 0);
+ }
+
+ spin_unlock(&x->lock);
+ xfrm_state_put(x);
+ }
+ break;
+
+ case NLKEY_SA_INFO_UPDATE:
+ memcpy(&sa_info_msg, msg->payload, sizeof(struct nlkey_sa_info));
+
+ x = xfrm_state_lookup_byhandle(&init_net,sa_info_msg.sagd);
+ if (x) {
+ spin_lock(&x->lock);
+
+ x->curlft.bytes = sa_info_msg.bytes;
+ x->curlft.packets = sa_info_msg.packets;
+
+ spin_unlock(&x->lock);
+ xfrm_state_put(x);
+ }
+ break;
+ default:
+ //printk(KERN_INFO "ipsec_nlkey_rcv fcode 0x%x not supported\n", msg->fcode);
+ break;
+ }
+
+}
+
+extern struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos,
+ xfrm_address_t *saddr,
+ xfrm_address_t *daddr,
+ int family);
+static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x,
+ const struct km_event *c, struct nlkey_sa_id *said)
+{
+
+ struct flowi fl;
+ int tos;
+ xfrm_address_t saddr, daddr;
+ struct dst_entry *dst;
+ int rc = 0;
+
+ memset(&fl, 0, sizeof(struct flowi));
+
+ /* SPI */
+ said->spi = x->id.spi;
+ /* SA Type (AH or ESP) */
+ said->sa_type = x->id.proto;
+ /* Protocol Family (IPv4 or IPv6) */
+ said->proto_family = x->props.family;
+ /* Replay window */
+ said->replay_window = x->props.replay_window;
+ /* Destination IP Address */
+ if(x->props.family == AF_INET6) {
+ memcpy(&said->dst_ip, x->id.daddr.a6, sizeof(struct in6_addr));
+ ipv6_addr_copy(&fl.u.ip6.daddr, (struct in6_addr *)x->id.daddr.a6);
+ memcpy(&said->src_ip, x->props.saddr.a6, sizeof(struct in6_addr));
+ }
+ else {
+ said->dst_ip[0] = x->id.daddr.a4;
+ fl.u.ip4.daddr = x->id.daddr.a4;
+ said->src_ip[0] = x->props.saddr.a4;
+ }
+ said->mtu = 0;
+
+ if(x->props.flags & XFRM_STATE_ESN)
+ said->flags = NLKEY_SAFLAGS_ESN;
+ xfrm_flowi_addr_get(&fl, &saddr, &daddr, x->props.family);
+
+ tos = xfrm_get_tos(&fl, x->props.family);
+ if (tos < 0) {
+ printk(KERN_ERR "%s:%d: FIXME\n",__FUNCTION__,__LINE__);
+ rc = -1;
+ goto error;
+ }
+
+ dst = __xfrm_dst_lookup(net, tos, NULL, &daddr, x->props.family);
+ if (IS_ERR(dst)) {
+ printk(KERN_ERR "%s:%d: FIXME\n",__FUNCTION__,__LINE__);
+ rc = -1;
+ goto error;
+ }
+ said->dev_mtu = dst_mtu(dst);
+ said->mtu = xfrm_state_mtu(x,dst_mtu(dst));
+
+ dst_release(dst);
+error:
+ return rc;
+}
+
+static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x,
+ const struct km_event *c, unsigned short *msg_id)
+{
+ struct nlkey_sa_id sa_id_msg;
+ struct nlkey_sa_create sa_create_msg;
+ struct nlkey_sa_delete sa_delete_msg;
+ struct nlkey_sa_set_keys sa_set_keys_msg;
+ struct nlkey_sa_set_tunnel sa_set_tunnel_msg;
+ struct nlkey_sa_set_natt sa_set_natt_msg;
+ struct nlkey_sa_set_state sa_set_state_msg;
+ struct nlkey_sa_set_lifetime sa_set_lifetime_msg;
+ struct nlkey_msg msg;
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh = NULL;
+ gfp_t allocation = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
+ unsigned char tunnel, keys, natt, state, lifetime;
+
+ /* supported SA informations */
+ keys = 1; state = 1; tunnel = 1; lifetime = 1; natt = 1;
+
+ /* next message to build */
+ memset(&msg, 0, sizeof(struct nlkey_msg));
+ msg.fcode = *msg_id;
+
+ //printk(KERN_INFO "\n\nipsec_xfrm2nlkey: processing event 0x%x\n", msg.fcode);
+
+ switch (msg.fcode)
+ {
+ case NLKEY_SA_CREATE:
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_CREATE\n");
+ if(x) {
+ /* some check before builing message */
+ if((x->id.proto != IPPROTO_ESP) && (x->id.proto != IPPROTO_AH)) {
+ printk(KERN_ERR "ipsec_xfrm2nlkey: protocol %d not supported\n", x->id.proto);
+ *msg_id = NLKEY_NULL_MSG;
+ goto exit;
+ }
+ memset(&sa_create_msg, 0, sizeof(struct nlkey_sa_create));
+
+ /* SA global handler */
+ sa_create_msg.sagd = x->handle;
+
+ /* SA identifier */
+ if(ipsec_nlkey_set_said(net, x, c, &sa_create_msg.said) < 0)
+ {
+ printk(KERN_ERR "%s: set sa ID failed\n", __func__);
+ *msg_id = NLKEY_NULL_MSG; /* next message */
+ goto exit;
+ }
+ memcpy(msg.payload, &sa_create_msg, sizeof(struct nlkey_sa_create));
+ msg.length = sizeof(struct nlkey_sa_create);
+ *msg_id = NLKEY_SA_SET_KEYS; /* next message */
+ } else {
+ *msg_id = NLKEY_NULL_MSG; /* next message */
+ goto exit;
+ }
+
+ break;
+
+ case NLKEY_SA_SET_KEYS:
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_KEYS\n");
+ if(keys) {
+ memset(&sa_set_keys_msg, 0, sizeof(struct nlkey_sa_set_keys));
+
+ /* SA global handler */
+ sa_set_keys_msg.sagd = x->handle;
+
+ /* auth key */
+ if(x->aalg) {
+ if (x->aalg->alg_key_len) {
+ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->aalg->alg_key_len;
+ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.aalgo;
+ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 0;
+ memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->aalg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8));
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: AUTH - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits);
+ sa_set_keys_msg.num_keys++;
+ }
+ }
+ /* encrypt key */
+ if(x->ealg) {
+ if (x->ealg->alg_key_len) {
+
+ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->ealg->alg_key_len;
+ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.ealgo;
+ sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 1;
+ memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->ealg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8));
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: ENCRYPT - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits);
+ sa_set_keys_msg.num_keys++;
+ }
+ }
+ memcpy(msg.payload, &sa_set_keys_msg, sizeof(struct nlkey_sa_set_keys));
+ msg.length = sizeof(struct nlkey_sa_set_keys);
+ *msg_id = NLKEY_SA_SET_TUNNEL; /* next message */
+ } else {
+ *msg_id = NLKEY_SA_SET_TUNNEL; /* next message */
+ goto exit;
+ }
+ break;
+
+ case NLKEY_SA_SET_TUNNEL:
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_TUNNEL\n");
+ if(tunnel && (x->props.mode == XFRM_MODE_TUNNEL)) {
+ memset(&sa_set_tunnel_msg, 0, sizeof(struct nlkey_sa_set_tunnel));
+
+ /* SA global handler */
+ sa_set_tunnel_msg.sagd = x->handle;
+
+ /* Tunnel */
+ sa_set_tunnel_msg.proto_family = x->props.family;
+ if(x->props.family == AF_INET6) {
+ struct ipv6hdr *top_iph = &sa_set_tunnel_msg.h.ipv6h;
+ int dsfield;
+ top_iph->version = 6;
+ top_iph->priority = 0;
+ top_iph->flow_lbl[0] = 0;
+ top_iph->flow_lbl[1] = 0;
+ top_iph->flow_lbl[2] = 0;
+ top_iph->nexthdr = IPPROTO_IPIP;
+ dsfield = ipv6_get_dsfield(top_iph);
+ dsfield = INET_ECN_encapsulate(dsfield, dsfield);
+ if (x->props.flags & XFRM_STATE_NOECN)
+ dsfield &= ~INET_ECN_MASK;
+ ipv6_change_dsfield(top_iph, 0, dsfield);
+ top_iph->hop_limit = 64;
+ memcpy(&top_iph->daddr, x->id.daddr.a6, sizeof(struct in6_addr));
+ memcpy(&top_iph->saddr, x->props.saddr.a6, sizeof(struct in6_addr));
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: IPv6 tunnel\n");
+ //printk(KERN_INFO "dst: %x %x %x %x\n", x->id.daddr.a6[0], x->id.daddr.a6[1], x->id.daddr.a6[2], x->id.daddr.a6[3]);
+ //(KERN_INFO "src: %x %x %x %x\n", x->props.saddr.a6[0], x->props.saddr.a6[1], x->props.saddr.a6[2], x->props.saddr.a6[3]);
+ }
+ else {
+ struct iphdr *top_iph = &sa_set_tunnel_msg.h.ipv4h;
+ top_iph->ihl = 5;
+ top_iph->version = 4;
+ top_iph->tos = 0;
+ top_iph->frag_off = 0;
+ top_iph->ttl = 64;
+ top_iph->saddr = x->props.saddr.a4;
+ top_iph->daddr = x->id.daddr.a4;
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: IPv4 tunnel dst:%x - src:%x \n", x->id.daddr.a4, x->props.saddr.a4);
+ }
+ memcpy(msg.payload, &sa_set_tunnel_msg, sizeof(struct nlkey_sa_set_tunnel));
+ msg.length = sizeof(struct nlkey_sa_set_tunnel);
+ *msg_id = NLKEY_SA_SET_NATT; /* next message */
+ } else {
+ *msg_id = NLKEY_SA_SET_NATT; /* next message */
+ goto exit;
+ }
+ break;
+
+ case NLKEY_SA_SET_NATT:
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_NATT\n");
+ if((natt) && (x->encap)){
+ memset(&sa_set_natt_msg, 0, sizeof(struct nlkey_sa_set_natt));
+
+ /* SA global handler */
+ sa_set_natt_msg.sagd = x->handle;
+ sa_set_natt_msg.sport = x->encap->encap_sport;
+ sa_set_natt_msg.dport = x->encap->encap_dport;
+ //printk(KERN_INFO "src port: %d dst port: %d \n", ntohs(sa_set_natt_msg.sport), ntohs( sa_set_natt_msg.dport));
+ memcpy(msg.payload, &sa_set_natt_msg, sizeof(struct nlkey_sa_set_natt));
+ msg.length = sizeof(struct nlkey_sa_set_natt);
+ *msg_id = NLKEY_SA_SET_LIFETIME; /* next message */
+ } else {
+ *msg_id = NLKEY_SA_SET_LIFETIME; /* next message */
+ goto exit;
+ }
+ break;
+
+ case NLKEY_SA_SET_LIFETIME:
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_LIFETIME\n");
+ if(lifetime) {
+ memset(&sa_set_lifetime_msg, 0, sizeof(struct nlkey_sa_set_lifetime));
+
+ /* SA global handler */
+ sa_set_lifetime_msg.sagd = x->handle;
+
+ /* hard time */
+ sa_set_lifetime_msg.hard_time.allocations = _X2KEY(x->lft.hard_packet_limit);
+ if(_X2KEY(x->lft.hard_byte_limit))
+ memcpy(sa_set_lifetime_msg.hard_time.bytes, &x->lft.hard_byte_limit, sizeof(uint64_t));
+
+ /* soft time */
+ sa_set_lifetime_msg.soft_time.allocations = _X2KEY(x->lft.soft_packet_limit);
+ if(_X2KEY(x->lft.soft_byte_limit))
+ memcpy(sa_set_lifetime_msg.soft_time.bytes, &x->lft.soft_byte_limit, sizeof(uint64_t));
+
+ /* current time */
+ sa_set_lifetime_msg.current_time.allocations = x->curlft.packets;
+ memcpy(sa_set_lifetime_msg.current_time.bytes, &x->curlft.bytes, sizeof(uint64_t));
+
+ memcpy(msg.payload, &sa_set_lifetime_msg, sizeof(struct nlkey_sa_set_lifetime));
+ msg.length = sizeof(struct nlkey_sa_set_lifetime);
+ *msg_id = NLKEY_SA_SET_STATE; /* next message */
+ } else {
+ *msg_id = NLKEY_SA_SET_STATE; /* next message */
+ goto exit;
+ }
+ break;
+
+ case NLKEY_SA_SET_STATE:
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SET_STATE\n");
+ if(state) {
+ memset(&sa_set_state_msg, 0, sizeof(struct nlkey_sa_set_state));
+ memset(&sa_id_msg, 0, sizeof(struct nlkey_sa_id));
+
+ /* SA global handler */
+ sa_set_state_msg.sagd = x->handle;
+ /* State */
+ sa_set_state_msg.state = x->km.state;
+ // TODO: set the offloaded state once ack received !
+ x->offloaded = 1;
+ atomic_inc(&flow_cache_genid);
+
+ memcpy(msg.payload, &sa_set_state_msg, sizeof(struct nlkey_sa_set_state));
+ msg.length = sizeof(struct nlkey_sa_set_state);
+ *msg_id = NLKEY_NULL_MSG; /* next message */
+ } else {
+ *msg_id = NLKEY_NULL_MSG; /* next message */
+ goto exit;
+ }
+ break;
+
+ case NLKEY_SA_DELETE:
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_DELETE\n");
+ memset(&sa_delete_msg, 0, sizeof(struct nlkey_sa_delete));
+
+ /* SA global handler */
+ sa_delete_msg.sagd = x->handle;
+ memcpy(msg.payload, &sa_delete_msg, sizeof(struct nlkey_sa_delete));
+ msg.length = sizeof(struct nlkey_sa_delete);
+ atomic_inc(&flow_cache_genid);
+
+
+ *msg_id = NLKEY_NULL_MSG; /* next message */
+ break;
+
+ case NLKEY_SA_FLUSH:
+ //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_FLUSH\n");
+ /* No data required for flush SA command */
+ atomic_inc(&flow_cache_genid);
+
+ *msg_id = NLKEY_NULL_MSG; /* next message */
+ break;
+
+ default:
+ printk(KERN_ERR "ipsec_xfrm2nlkey: event 0x%x not supported\n", c->event);
+ *msg_id = NLKEY_NULL_MSG; /* next message */
+ break;
+ }
+
+ /* prepare netlink message for kernel to user space direction */
+ if(msg.length > NLKEY_MSG_LEN)
+ {
+ printk(KERN_ERR "ipsec_xfrm2nlkey: maximum message size reached (%d bytes)\n", msg.length);
+ goto exit;
+ }
+
+ skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation);
+ if (skb == NULL)
+ goto exit;
+
+ nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length));
+ memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length));
+
+ /* whole length of the message i.e. header + payload */
+ nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length);
+
+ /* from kernel */
+ nlh->nlmsg_pid = 0;
+ nlh->nlmsg_flags = 0;
+ nlh->nlmsg_type = 0;
+ NETLINK_CB(skb).pid = 0;
+ NETLINK_CB(skb).dst_group = 1;
+exit:
+ return skb;
+}
+
+static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c)
+{
+ struct sk_buff *skb;
+ unsigned short msg_type;
+ int rc = 0;
+
+ /* We may generate more than one message when adding new SA (sa_create + sa_set_state + sa_set_tunnel...) */
+ msg_type = ipsec_sacode_to_nlkeycode((unsigned short)c->event);
+
+ while(msg_type != NLKEY_NULL_MSG)
+ {
+ /* build nlkey message */
+ skb = ipsec_xfrm2nlkey(net, x, c, &msg_type);
+
+ if(skb != NULL)
+ if((rc = netlink_broadcast(nlkey_socket, skb, 0, 1, GFP_ATOMIC)) < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
+
+int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle, const struct flowi *fl, u16 family, u16 dir)
+{
+ struct sk_buff *skb;
+ struct nlkey_msg msg;
+ struct nlmsghdr *nlh = NULL;
+ unsigned short *p;
+ gfp_t allocation = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
+
+ //printk(KERN_INFO "ipsec_nlkey_flow \n");
+
+ /* next message to build */
+ memset(&msg, 0, sizeof(struct nlkey_msg));
+ msg.fcode = NLKEY_FLOW_ADD;
+
+ // Number of SA for this flow
+ p = msg.payload;
+ *p++ = xfrm_nr;
+ msg.length += sizeof(unsigned short);
+ // SA handles list
+ memcpy(p, xfrm_handle, xfrm_nr*sizeof(unsigned short));
+ msg.length += xfrm_nr*sizeof(unsigned short);
+ p+=xfrm_nr;
+ // flow family
+ *p++ = family;
+ msg.length += sizeof(unsigned short);
+ // flow family
+ *p++ = dir;
+ msg.length += sizeof(unsigned short);
+ // flow descriptor
+ memcpy(p, fl, sizeof(struct flowi));
+ msg.length +=sizeof(struct flowi);
+ p+=sizeof(struct flowi) / sizeof(u16);
+
+ skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ /* prepare netlink message for kernel to user space direction */
+ nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length));
+ memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length));
+
+ /* whole length of the message i.e. header + payload */
+ nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length);
+
+ /* from kernel */
+ nlh->nlmsg_pid = 0;
+ nlh->nlmsg_flags = 0;
+ nlh->nlmsg_type = 0;
+ NETLINK_CB(skb).pid = 0;
+ NETLINK_CB(skb).dst_group = 1;
+
+ return(netlink_broadcast(nlkey_socket, skb, 0, 1, allocation));
+}
+EXPORT_SYMBOL(ipsec_nlkey_flow);
+
+
+int ipsec_nlkey_flow_remove(struct flowi *fl, u16 family, u16 dir)
+{
+ struct sk_buff *skb;
+ struct nlkey_msg msg;
+ struct nlmsghdr *nlh = NULL;
+ unsigned short *p;
+ gfp_t allocation = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
+
+
+ //printk(KERN_INFO "ipsec_nlkey_flow_remove\n");
+
+ /* next message to build */
+ memset(&msg, 0, sizeof(struct nlkey_msg));
+ msg.fcode = NLKEY_FLOW_REMOVE;
+
+ p = msg.payload;
+ // flow family
+ *p++ = family;
+ msg.length += sizeof(unsigned short);
+ // flow family
+ *p++ = dir;
+ msg.length += sizeof(unsigned short);
+ // flow descriptor
+ memcpy(p, fl, sizeof(struct flowi));
+ msg.length +=sizeof(struct flowi);
+ p+=sizeof(struct flowi) / sizeof(u16);
+
+ skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ /* prepare netlink message for kernel to user space direction */
+ nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length));
+ memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length));
+
+ /* whole length of the message i.e. header + payload */
+ nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length);
+
+ /* from kernel */
+ nlh->nlmsg_pid = 0;
+ nlh->nlmsg_flags = 0;
+ nlh->nlmsg_type = 0;
+ NETLINK_CB(skb).pid = 0;
+ NETLINK_CB(skb).dst_group = 1;
+
+
+ return(netlink_broadcast(nlkey_socket, skb, 0, 1, allocation));
+
+
+}
+EXPORT_SYMBOL(ipsec_nlkey_flow_remove);
+
+
+
+static void ipsec_nlkey_init(void)
+{
+ printk(KERN_INFO "Initializing NETLINK_KEY socket\n");
+
+ nlkey_socket = netlink_kernel_create(&init_net, NETLINK_KEY, 1,
+ ipsec_nlkey_rcv, NULL, THIS_MODULE);
+}
+#endif
+
+
static int __net_init pfkey_net_init(struct net *net)
{
struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
@@ -3782,6 +4556,11 @@
sock_unregister(PF_KEY);
unregister_pernet_subsys(&pfkey_net_ops);
proto_unregister(&key_proto);
+
+#ifdef NLKEY_SUPPORT
+ /* release NETLINK_KEY socket */
+ sock_release(nlkey_socket->sk_socket);
+#endif
}
static int __init ipsec_pfkey_init(void)
@@ -3800,6 +4579,12 @@
err = xfrm_register_km(&pfkeyv2_mgr);
if (err != 0)
goto out_sock_unregister;
+
+#ifdef NLKEY_SUPPORT
+ /* create NETLINK_KEY socket for IPSec offload on Comcerto */
+ ipsec_nlkey_init();
+#endif
+
out:
return err;
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 89ff8c6..7501b22 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1253,11 +1253,10 @@
/* Remove from tunnel list */
spin_lock_bh(&pn->l2tp_tunnel_list_lock);
list_del_rcu(&tunnel->list);
+ kfree_rcu(tunnel, rcu);
spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
- synchronize_rcu();
atomic_dec(&l2tp_tunnel_count);
- kfree(tunnel);
}
/* Create a socket for the tunnel, if one isn't set up by
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index a16a48e..4393794 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -157,6 +157,7 @@
struct l2tp_tunnel {
int magic; /* Should be L2TP_TUNNEL_MAGIC */
+ struct rcu_head rcu;
rwlock_t hlist_lock; /* protect session_hlist */
struct hlist_head session_hlist[L2TP_HASH_SIZE];
/* hashed list of sessions,
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 0427cbd..e1e1ced 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -6,6 +6,8 @@
obj-$(CONFIG_NETFILTER) = netfilter.o
+obj-$(CONFIG_COMCERTO_FP) += comcerto_fp_netfilter.o
+
obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
diff --git a/net/netfilter/comcerto_fp_netfilter.c b/net/netfilter/comcerto_fp_netfilter.c
new file mode 100644
index 0000000..bc6ef68
--- /dev/null
+++ b/net/netfilter/comcerto_fp_netfilter.c
@@ -0,0 +1,137 @@
+/*
+ * linux/drivers/net/comcerto/fp_netfilter.c
+ *
+ * Copyright (C) 2010 Mindspeed Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter.h>
+#include <net/netfilter/nf_conntrack.h>
+
+static unsigned int fp_netfilter_pre_routing(int family, unsigned int hooknum, struct sk_buff *skb)
+{
+ struct nf_conn *ct;
+ u_int8_t protonum;
+ enum ip_conntrack_info ctinfo;
+ struct comcerto_fp_info *fp_info;
+ int dir;
+
+ ct = nf_ct_get(skb, &ctinfo);
+ if (!ct)
+ goto done;
+
+ protonum = nf_ct_protonum(ct);
+ if ((protonum != IPPROTO_TCP) && (protonum != IPPROTO_UDP) && (protonum != IPPROTO_IPIP))
+ goto done;
+
+ dir = CTINFO2DIR(ctinfo);
+
+// if (printk_ratelimit())
+// printk(KERN_INFO "ct: %lx, dir: %x, mark: %x, ifindex: %d iif: %d\n", (unsigned long)ct, dir, skb->mark, skb->dev->ifindex, skb->skb_iif);
+
+ /* We could also check for changes and notify userspace (or print message) */
+ if (dir == IP_CT_DIR_ORIGINAL) {
+ fp_info = &ct->fp_info[IP_CT_DIR_ORIGINAL];
+ } else {
+ fp_info = &ct->fp_info[IP_CT_DIR_REPLY];
+ }
+
+ if (fp_info->mark && (fp_info->mark != skb->mark))
+ if (printk_ratelimit())
+ printk(KERN_INFO "ct: mark changed %x, %x\n", fp_info->mark, skb->mark);
+
+ if (fp_info->ifindex && (fp_info->ifindex != skb->dev->ifindex))
+ if (printk_ratelimit())
+ printk(KERN_INFO "ct: ifindex changed %d, %d\n", fp_info->ifindex, skb->dev->ifindex);
+
+ if (fp_info->iif && (fp_info->iif != skb->skb_iif))
+ if (printk_ratelimit())
+ printk(KERN_INFO "ct: iif changed %d, %d\n", fp_info->iif, skb->skb_iif);
+
+ fp_info->mark = skb->mark;
+ fp_info->ifindex = skb->dev->ifindex;
+ fp_info->iif = skb->skb_iif;
+
+done:
+ return NF_ACCEPT;
+}
+
+static unsigned int fp_ipv4_netfilter_pre_routing(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+
+ return fp_netfilter_pre_routing(PF_INET, hooknum, skb);
+}
+
+static unsigned int fp_ipv6_netfilter_pre_routing(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+
+ return fp_netfilter_pre_routing(PF_INET6, hooknum, skb);
+}
+
+
+static struct nf_hook_ops fp_netfilter_ops[] __read_mostly = {
+ {
+ .hook = fp_ipv4_netfilter_pre_routing,
+ .owner = THIS_MODULE,
+ .pf = NFPROTO_IPV4,
+ .hooknum = NF_INET_PRE_ROUTING,
+ .priority = NF_IP_PRI_LAST,
+ },
+ {
+ .hook = fp_ipv6_netfilter_pre_routing,
+ .owner = THIS_MODULE,
+ .pf = NFPROTO_IPV6,
+ .hooknum = NF_INET_PRE_ROUTING,
+ .priority = NF_IP_PRI_LAST,
+ },
+};
+
+static int __init fp_netfilter_init(void)
+{
+ int rc;
+
+ rc = nf_register_hooks(fp_netfilter_ops, ARRAY_SIZE(fp_netfilter_ops));
+ if (rc < 0) {
+ printk(KERN_ERR "fp_netfilter_ops: can't register hooks.\n");
+ goto err0;
+ }
+
+ return 0;
+
+err0:
+ return rc;
+}
+
+
+static void __exit fp_netfilter_exit(void)
+{
+ nf_unregister_hooks(fp_netfilter_ops, ARRAY_SIZE(fp_netfilter_ops));
+}
+
+module_init(fp_netfilter_init);
+module_exit(fp_netfilter_exit);
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 884e227..3d3622ac 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -255,12 +255,15 @@
{
struct nf_conn *ct = (void *)ul_conntrack;
struct net *net = nf_ct_net(ct);
+ struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct);
+
+ BUG_ON(ecache == NULL);
if (nf_conntrack_event(IPCT_DESTROY, ct) < 0) {
/* bad luck, let's retry again */
- ct->timeout.expires = jiffies +
+ ecache->timeout.expires = jiffies +
(random32() % net->ct.sysctl_events_retry_timeout);
- add_timer(&ct->timeout);
+ add_timer(&ecache->timeout);
return;
}
/* we've got the event delivered, now it's dying */
@@ -274,6 +277,9 @@
void nf_ct_insert_dying_list(struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
+ struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct);
+
+ BUG_ON(ecache == NULL);
/* add this conntrack to the dying list */
spin_lock_bh(&nf_conntrack_lock);
@@ -281,10 +287,10 @@
&net->ct.dying);
spin_unlock_bh(&nf_conntrack_lock);
/* set a new timer to retry event delivery */
- setup_timer(&ct->timeout, death_by_event, (unsigned long)ct);
- ct->timeout.expires = jiffies +
+ setup_timer(&ecache->timeout, death_by_event, (unsigned long)ct);
+ ecache->timeout.expires = jiffies +
(random32() % net->ct.sysctl_events_retry_timeout);
- add_timer(&ct->timeout);
+ add_timer(&ecache->timeout);
}
EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list);
@@ -292,11 +298,36 @@
{
struct nf_conn *ct = (void *)ul_conntrack;
struct nf_conn_tstamp *tstamp;
+#ifdef CONFIG_COMCERTO_FP
+ struct nf_conntrack_l4proto *l4proto;
+#endif
tstamp = nf_conn_tstamp_find(ct);
if (tstamp && tstamp->stop == 0)
tstamp->stop = ktime_to_ns(ktime_get_real());
+#ifdef CONFIG_COMCERTO_FP
+ l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
+
+ if (test_bit(IPS_DYING_BIT, &ct->status) ||
+ (!test_bit(IPS_PERMANENT_BIT, &ct->status)) ||
+ ((l4proto->l4proto == IPPROTO_TCP) && (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED))) {
+ if (!test_bit(IPS_DYING_BIT, &ct->status) &&
+ unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) {
+ /* destroy event was not delivered */
+ nf_ct_delete_from_lists(ct);
+ nf_ct_insert_dying_list(ct);
+ return;
+ }
+ set_bit(IPS_DYING_BIT, &ct->status);
+ nf_ct_delete_from_lists(ct);
+ nf_ct_put(ct);
+ } else {
+ ct->timeout.expires = jiffies + COMCERTO_PERMANENT_TIMEOUT * HZ;
+ add_timer(&ct->timeout);
+ }
+#else
+
if (!test_bit(IPS_DYING_BIT, &ct->status) &&
unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) {
/* destroy event was not delivered */
@@ -307,6 +338,7 @@
set_bit(IPS_DYING_BIT, &ct->status);
nf_ct_delete_from_lists(ct);
nf_ct_put(ct);
+#endif
}
/*
@@ -610,10 +642,20 @@
if (!ct)
return dropped;
+#ifdef CONFIG_COMCERTO_FP
+ clear_bit(IPS_PERMANENT_BIT, &ct->status);
+ /* Avoid race with timer expiration */
+ if (del_timer_sync(&ct->timeout)) {
+#else
if (del_timer(&ct->timeout)) {
+#endif
death_by_timeout((unsigned long)ct);
- dropped = 1;
- NF_CT_STAT_INC_ATOMIC(net, early_drop);
+ /* Check if we indeed killed this entry. Reliable event
+ delivery may have inserted it into the dying list. */
+ if (test_bit(IPS_DYING_BIT, &ct->status)) {
+ dropped = 1;
+ NF_CT_STAT_INC_ATOMIC(net, early_drop);
+ }
}
nf_ct_put(ct);
return dropped;
@@ -1079,7 +1121,13 @@
}
}
+#ifdef CONFIG_COMCERTO_FP
+ clear_bit(IPS_PERMANENT_BIT, &ct->status);
+ /* Avoid race with timer expiration */
+ if (del_timer_sync(&ct->timeout)) {
+#else
if (del_timer(&ct->timeout)) {
+#endif
ct->timeout.function((unsigned long)ct);
return true;
}
@@ -1200,7 +1248,14 @@
while ((ct = get_next_corpse(net, iter, data, &bucket)) != NULL) {
/* Time to push up daises... */
+
+#ifdef CONFIG_COMCERTO_FP
+ clear_bit(IPS_PERMANENT_BIT, &ct->status);
+ /* Avoid race with timer expiration */
+ if (del_timer_sync(&ct->timeout))
+#else
if (del_timer(&ct->timeout))
+#endif
death_by_timeout((unsigned long)ct);
/* ... else the timer will get him soon. */
@@ -1362,6 +1417,46 @@
}
EXPORT_SYMBOL_GPL(nf_ct_alloc_hashtable);
+#ifdef CONFIG_COMCERTO_FP
+int nf_conntrack_set_dpi_allow_report(struct sk_buff *skb)
+{
+ int err = 0;
+ struct nf_conn *ct = (struct nf_conn *)skb->nfct;
+
+ nf_conntrack_get(skb->nfct);
+
+ set_bit(IPS_DPI_ALLOWED_BIT, &ct->status);
+
+ nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
+
+ nf_conntrack_put(skb->nfct);
+
+ return err;
+}
+EXPORT_SYMBOL(nf_conntrack_set_dpi_allow_report);
+
+int nf_conntrack_set_dpi_allow_and_mark(struct sk_buff *skb, int mark)
+{
+ int err = 0;
+ struct nf_conn *ct = (struct nf_conn *)skb->nfct;
+
+ nf_conntrack_get(skb->nfct);
+
+ set_bit(IPS_DPI_ALLOWED_BIT, &ct->status);
+
+#ifdef CONFIG_NF_CONNTRACK_MARK
+ ct->mark = mark;
+#endif
+
+ nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
+
+ nf_conntrack_put(skb->nfct);
+
+ return err;
+}
+EXPORT_SYMBOL(nf_conntrack_set_dpi_allow_and_mark);
+#endif
+
int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
{
int i, bucket;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 782cdcd..0683213 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -301,6 +301,41 @@
#define ctnetlink_dump_secctx(a, b) (0)
#endif
+#if defined(CONFIG_COMCERTO_FP)
+static int
+ctnetlink_dump_comcerto_fp(struct sk_buff *skb, const struct nf_conn *ct)
+{
+ struct nlattr *nest_count;
+
+ nest_count = nla_nest_start(skb, CTA_COMCERTO_FP_ORIG | NLA_F_NESTED);
+ if (!nest_count)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_ORIGINAL].mark);
+ NLA_PUT_U32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_ORIGINAL].ifindex);
+ NLA_PUT_U32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_ORIGINAL].iif);
+
+ nla_nest_end(skb, nest_count);
+
+ nest_count = nla_nest_start(skb, CTA_COMCERTO_FP_REPLY | NLA_F_NESTED);
+ if (!nest_count)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_REPLY].mark);
+ NLA_PUT_U32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_REPLY].ifindex);
+ NLA_PUT_U32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_REPLY].iif);
+
+ nla_nest_end(skb, nest_count);
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+#else
+#define ctnetlink_dump_comcerto_fp(a, b) (0)
+#endif
+
#define master_tuple(ct) &(ct->master->tuplehash[IP_CT_DIR_ORIGINAL].tuple)
static inline int
@@ -437,6 +472,7 @@
ctnetlink_dump_helpinfo(skb, ct) < 0 ||
ctnetlink_dump_mark(skb, ct) < 0 ||
ctnetlink_dump_secctx(skb, ct) < 0 ||
+ ctnetlink_dump_comcerto_fp(skb, ct) < 0 ||
ctnetlink_dump_id(skb, ct) < 0 ||
ctnetlink_dump_use(skb, ct) < 0 ||
ctnetlink_dump_master(skb, ct) < 0 ||
@@ -528,6 +564,12 @@
+ nla_total_size(0) /* CTA_HELP */
+ nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */
+ ctnetlink_secctx_size(ct)
+#ifdef CONFIG_COMCERTO_FP
+ + 2 * nla_total_size(0) /* CTA_COMCERTO_FP_ORIG|REPL */
+ + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_MARK */
+ + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IFINDEX */
+ + 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IIF */
+#endif
#ifdef CONFIG_NF_NAT_NEEDED
+ 2 * nla_total_size(0) /* CTA_NAT_SEQ_ADJ_ORIG|REPL */
+ 6 * nla_total_size(sizeof(u_int32_t)) /* CTA_NAT_SEQ_OFFSET */
@@ -605,6 +647,9 @@
if (nf_ct_zone(ct))
NLA_PUT_BE16(skb, CTA_ZONE, htons(nf_ct_zone(ct)));
+ if (ctnetlink_dump_comcerto_fp(skb, ct) < 0)
+ goto nla_put_failure;
+
if (ctnetlink_dump_id(skb, ct) < 0)
goto nla_put_failure;
@@ -1078,7 +1123,36 @@
ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK);
return 0;
}
+#if defined(CONFIG_COMCERTO_FP)
+/*
+ * This function detects ctnetlink messages that require
+ * to set the conntrack status to IPS_PERMANENT.
+ * It updates only this bit regardless of other possible
+ * changes.
+ * Return 0 if succesfull
+ */
+static int
+ctnetlink_change_permanent(struct nf_conn *ct, const struct nlattr * const cda[])
+{
+ unsigned int status;
+ u_int32_t id;
+ if (cda[CTA_STATUS] && cda[CTA_ID]) {
+ status = ntohl(nla_get_be32(cda[CTA_STATUS]));
+ id = ntohl(nla_get_be32(cda[CTA_ID]));
+
+ if (status & IPS_PERMANENT) {
+ if ((u32)(unsigned long)ct == id) {
+ ct->status |= IPS_PERMANENT;
+ return 0;
+ }
+ else
+ return -ENOENT;
+ }
+ }
+ return -1;
+}
+#endif
static int
ctnetlink_change_nat(struct nf_conn *ct, const struct nlattr * const cda[])
{
@@ -1549,6 +1623,13 @@
if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
+#if defined(CONFIG_COMCERTO_FP)
+ /* If the permanent status has been set, this is a specific
+ * message. Don't broadcast the event and don't update the ct */
+ err = ctnetlink_change_permanent(ct, cda);
+ if ((err == 0) || (err == -ENOENT))
+ goto out_unlock;
+#endif
err = ctnetlink_change_conntrack(ct, cda);
if (err == 0) {
nf_conntrack_get(&ct->ct_general);
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 621e2da..0758b88 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -223,6 +223,12 @@
if (seq_printf(s, "[ASSURED] "))
goto release;
+#ifdef CONFIG_COMCERTO_FP
+ if (test_bit(IPS_PERMANENT_BIT, &ct->status))
+ if (seq_printf(s, "[PERMANENT] "))
+ goto release;
+#endif
+
#if defined(CONFIG_NF_CONNTRACK_MARK)
if (seq_printf(s, "mark=%u ", ct->mark))
goto release;
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index 47bacd8..bd1c628 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -43,11 +43,32 @@
struct dst_entry *dst = skb_dst(skb);
struct xfrm_state *x = dst->xfrm;
struct net *net = xs_net(x);
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
+ int xfrm_nr = 0;
+ int i;
+#endif
if (err <= 0)
goto resume;
do {
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if (x->offloaded) {
+
+ if (xfrm_nr == XFRM_MAX_DEPTH) {
+ err = -ENOBUFS;
+ goto out_exit;
+ }
+
+ if (!x->curlft.use_time)
+ x->curlft.use_time = get_seconds();
+
+ xfrm_vec[xfrm_nr++] = x;
+ skb->ipsec_offload = 1;
+ goto next_dst;
+ }
+#endif
err = xfrm_state_check_space(x, skb);
if (err) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
@@ -90,6 +111,9 @@
goto error_nolock;
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+next_dst:
+#endif
dst = skb_dst_pop(skb);
if (!dst) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
@@ -101,6 +125,26 @@
} while (x && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL));
err = 0;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
+ struct sec_path *sp;
+
+ sp = secpath_dup(skb->sp);
+ if (!sp)
+ goto error_nolock;
+ if (skb->sp)
+ secpath_put(skb->sp);
+ skb->sp = sp;
+ }
+ if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
+ goto error_nolock;
+
+ memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec,
+ xfrm_nr * sizeof(xfrm_vec[0]));
+ skb->sp->len += xfrm_nr;
+ for (i = 0; i < skb->sp->len; i++)
+ xfrm_state_hold(skb->sp->xvec[i]);
+#endif
out_exit:
return err;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 0174034..12b1cf5 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -46,7 +46,15 @@
static struct kmem_cache *xfrm_dst_cache __read_mostly;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+extern int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle,
+ const struct flowi *fl, u16 family, u16 dir);
+#endif
+
+#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD)
static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
+#endif
+
static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
static void xfrm_init_pmtu(struct dst_entry *dst);
static int stale_bundle(struct dst_entry *dst);
@@ -94,7 +102,10 @@
return 0;
}
-static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos,
+#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD)
+static inline
+#endif
+struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos,
const xfrm_address_t *saddr,
const xfrm_address_t *daddr,
int family)
@@ -112,8 +123,14 @@
return dst;
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+EXPORT_SYMBOL(__xfrm_dst_lookup);
+#endif
-static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos,
+#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD)
+static inline
+#endif
+struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos,
xfrm_address_t *prev_saddr,
xfrm_address_t *prev_daddr,
int family)
@@ -143,6 +160,9 @@
return dst;
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+EXPORT_SYMBOL(xfrm_dst_lookup);
+#endif
static inline unsigned long make_jiffies(long secs)
{
@@ -1264,7 +1284,10 @@
* still valid.
*/
-static inline int xfrm_get_tos(const struct flowi *fl, int family)
+#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD)
+static inline
+#endif
+int xfrm_get_tos(const struct flowi *fl, int family)
{
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
int tos;
@@ -1278,6 +1301,9 @@
return tos;
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+EXPORT_SYMBOL(xfrm_get_tos);
+#endif
static struct flow_cache_object *xfrm_bundle_flo_get(struct flow_cache_object *flo)
{
@@ -1461,7 +1487,12 @@
xdst->route = dst;
dst_copy_metrics(dst1, dst);
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if ((xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) &&
+ (!xfrm[i]->offloaded)) {
+#else
if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
+#endif
family = xfrm[i]->props.family;
dst = xfrm_dst_lookup(xfrm[i], tos, &saddr, &daddr,
family);
@@ -1786,6 +1817,9 @@
u16 family = dst_orig->ops->family;
u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
int i, err, num_pols, num_xfrms = 0, drop_pols = 0;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ u8 new_flow = 0;
+#endif
restart:
dst = NULL;
@@ -1836,8 +1870,13 @@
!net->xfrm.policy_count[XFRM_POLICY_OUT])
goto nopol;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ flo = flow_cache_lookup(net, fl, family, dir, &new_flow,
+ xfrm_bundle_lookup, dst_orig);
+#else
flo = flow_cache_lookup(net, fl, family, dir,
xfrm_bundle_lookup, dst_orig);
+#endif
if (flo == NULL)
goto nopol;
if (IS_ERR(flo)) {
@@ -1917,6 +1956,30 @@
dst_release(dst);
dst = dst_orig;
}
+
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if (new_flow) {
+ struct dst_entry *dst1 = dst;
+ struct xfrm_state *x;
+ u16 xfrm_handle[XFRM_POLICY_TYPE_MAX];
+
+ num_xfrms = 0;
+ memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX*sizeof(u16));
+ while((x = dst1->xfrm) != NULL) {
+ if (!x->offloaded)
+ goto ok;
+ xfrm_handle[num_xfrms++] = x->handle;
+ dst1 = dst1->child;
+ if (dst1 == NULL) {
+ err = -EHOSTUNREACH;
+ goto error;
+ }
+ }
+ // sent flow notification to cmm with sa_handle
+ ipsec_nlkey_flow(num_xfrms, xfrm_handle, fl, family, (unsigned short)dir);
+ }
+#endif
+
ok:
xfrm_pols_put(pols, drop_pols);
if (dst && dst->xfrm &&
@@ -2042,9 +2105,14 @@
int xfrm_nr;
int pi;
int reverse;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ u8 new_flow = 0;
+#endif
struct flowi fl;
u8 fl_dir;
int xerr_idx = -1;
+
+ //printk(KERN_INFO "%s\n", __func__);
reverse = dir & ~XFRM_POLICY_MASK;
dir &= XFRM_POLICY_MASK;
@@ -2081,9 +2149,13 @@
if (!pol) {
struct flow_cache_object *flo;
-
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ flo = flow_cache_lookup(net, &fl, family, fl_dir, &new_flow,
+ xfrm_policy_lookup, NULL);
+#else
flo = flow_cache_lookup(net, &fl, family, fl_dir,
xfrm_policy_lookup, NULL);
+#endif
if (IS_ERR_OR_NULL(flo))
pol = ERR_CAST(flo);
else
@@ -2177,6 +2249,28 @@
goto reject;
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if (new_flow) {
+ struct xfrm_state *x;
+ u16 xfrm_handle[XFRM_POLICY_TYPE_MAX];
+
+ xfrm_nr = 0;
+ memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX*sizeof(u16));
+ for (i=skb->sp->len-1; i>=0; i--)
+ {
+ x = skb->sp->xvec[i];
+
+ if (!x->offloaded)
+ goto std_path;
+
+ xfrm_handle[xfrm_nr++] = x->handle;
+ }
+ // sent flow notification to cmm with sa_handle
+ ipsec_nlkey_flow(xfrm_nr, xfrm_handle, (const struct flowi *)&fl, family, fl_dir);
+ }
+
+std_path:
+#endif
xfrm_pols_put(pols, npols);
return 1;
}
@@ -2506,7 +2600,10 @@
read_unlock_bh(&xfrm_policy_afinfo_lock);
}
-static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
+#if !defined(CONFIG_INET_IPSEC_OFFLOAD) && !defined(CONFIG_INET6_IPSEC_OFFLOAD)
+static
+#endif
+struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
{
struct xfrm_policy_afinfo *afinfo;
if (unlikely(family >= NPROTO))
@@ -2517,6 +2614,9 @@
read_unlock(&xfrm_policy_afinfo_lock);
return afinfo;
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+EXPORT_SYMBOL(xfrm_policy_get_afinfo);
+#endif
static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
{
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 9414b9c..520035a 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -39,6 +39,10 @@
static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+static unsigned short xfrm_state_handle;
+#endif
+
static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
@@ -66,11 +70,20 @@
return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+static void xfrm_hash_transfer(struct hlist_head *list,
+ struct hlist_head *ndsttable,
+ struct hlist_head *nsrctable,
+ struct hlist_head *nspitable,
+ struct hlist_head *nhtable,
+ unsigned int nhashmask)
+#else
static void xfrm_hash_transfer(struct hlist_head *list,
struct hlist_head *ndsttable,
struct hlist_head *nsrctable,
struct hlist_head *nspitable,
unsigned int nhashmask)
+#endif
{
struct hlist_node *entry, *tmp;
struct xfrm_state *x;
@@ -94,6 +107,11 @@
nhashmask);
hlist_add_head(&x->byspi, nspitable+h);
}
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if (x->handle) {
+ hlist_add_head(&x->byh, nhtable+(x->handle & nhashmask));
+ }
+#endif
}
}
@@ -108,6 +126,9 @@
{
struct net *net = container_of(work, struct net, xfrm.state_hash_work);
struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ struct hlist_head *nh, *oh;
+#endif
unsigned long nsize, osize;
unsigned int nhashmask, ohashmask;
int i;
@@ -129,22 +150,41 @@
xfrm_hash_free(nsrc, nsize);
goto out_unlock;
}
-
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ nh = xfrm_hash_alloc(nsize);
+ if (!nh) {
+ xfrm_hash_free(ndst, nsize);
+ xfrm_hash_free(nsrc, nsize);
+ xfrm_hash_free(nspi, nsize);
+ goto out_unlock;
+ }
+#endif
spin_lock_bh(&xfrm_state_lock);
nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
for (i = net->xfrm.state_hmask; i >= 0; i--)
- xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi,
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi, nh,
nhashmask);
+#else
+ xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi,
+ nhashmask);
+#endif
odst = net->xfrm.state_bydst;
osrc = net->xfrm.state_bysrc;
ospi = net->xfrm.state_byspi;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ oh = net->xfrm.state_byh;
+#endif
ohashmask = net->xfrm.state_hmask;
net->xfrm.state_bydst = ndst;
net->xfrm.state_bysrc = nsrc;
net->xfrm.state_byspi = nspi;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ net->xfrm.state_byh = nh;
+#endif
net->xfrm.state_hmask = nhashmask;
spin_unlock_bh(&xfrm_state_lock);
@@ -153,6 +193,9 @@
xfrm_hash_free(odst, osize);
xfrm_hash_free(osrc, osize);
xfrm_hash_free(ospi, osize);
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ xfrm_hash_free(oh, osize);
+#endif
out_unlock:
mutex_unlock(&hash_resize_mutex);
@@ -494,6 +537,9 @@
INIT_HLIST_NODE(&x->bydst);
INIT_HLIST_NODE(&x->bysrc);
INIT_HLIST_NODE(&x->byspi);
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ INIT_HLIST_NODE(&x->byh);
+#endif
tasklet_hrtimer_init(&x->mtimer, xfrm_timer_handler, CLOCK_REALTIME, HRTIMER_MODE_ABS);
setup_timer(&x->rtimer, xfrm_replay_timer_handler,
(unsigned long)x);
@@ -504,6 +550,12 @@
x->lft.hard_packet_limit = XFRM_INF;
x->replay_maxage = 0;
x->replay_maxdiff = 0;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ x->handle = xfrm_state_handle++;
+ if (x->handle == 0)
+ x->handle = xfrm_state_handle++;
+ hlist_add_head(&x->byh, net->xfrm.state_byh+(x->handle & net->xfrm.state_hmask));
+#endif
x->inner_mode = NULL;
x->inner_mode_iaf = NULL;
spin_lock_init(&x->lock);
@@ -538,6 +590,10 @@
hlist_del(&x->bysrc);
if (x->id.spi)
hlist_del(&x->byspi);
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ if (x->handle)
+ hlist_del(&x->byh);
+#endif
net->xfrm.state_num--;
spin_unlock(&xfrm_state_lock);
@@ -1408,6 +1464,37 @@
}
EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+struct xfrm_state *__xfrm_state_lookup_byhandle(struct net *net, u16 handle)
+{
+ unsigned int h = (handle & net->xfrm.state_hmask);
+ struct xfrm_state *x;
+ struct hlist_node *entry;
+
+ hlist_for_each_entry(x, entry, net->xfrm.state_byh+h, byh) {
+ if (x->handle != handle)
+ continue;
+
+ xfrm_state_hold(x);
+ return x;
+ }
+
+ return NULL;
+}
+
+struct xfrm_state *
+xfrm_state_lookup_byhandle(struct net *net, u16 handle)
+{
+ struct xfrm_state *x;
+
+ spin_lock_bh(&xfrm_state_lock);
+ x = __xfrm_state_lookup_byhandle(net, handle);
+ spin_unlock_bh(&xfrm_state_lock);
+ return x;
+}
+EXPORT_SYMBOL(xfrm_state_lookup_byhandle);
+#endif
+
struct xfrm_state *
xfrm_find_acq(struct net *net, struct xfrm_mark *mark, u8 mode, u32 reqid, u8 proto,
const xfrm_address_t *daddr, const xfrm_address_t *saddr,
@@ -2026,6 +2113,12 @@
net->xfrm.state_byspi = xfrm_hash_alloc(sz);
if (!net->xfrm.state_byspi)
goto out_byspi;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+ net->xfrm.state_byh = xfrm_hash_alloc(sz);
+ if (!net->xfrm.state_byh)
+ goto out_byh;
+ get_random_bytes(&xfrm_state_handle, sizeof(xfrm_state_handle));
+#endif
net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
net->xfrm.state_num = 0;
@@ -2035,6 +2128,10 @@
init_waitqueue_head(&net->xfrm.km_waitq);
return 0;
+#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
+out_byh:
+ xfrm_hash_free(net->xfrm.state_byspi, sz);
+#endif
out_byspi:
xfrm_hash_free(net->xfrm.state_bysrc, sz);
out_bysrc: