All of lore.kernel.org
 help / color / mirror / Atom feed
* ARM: add initial support for Picochip picoXcell SoC
@ 2010-11-23 10:06 Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 01/13] picoxcell: add support for picoXcell Jamie Iles
                   ` (14 more replies)
  0 siblings, 15 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This patch series adds support for the Picochip picoXcell series of
femotcell SoC's. There are currently two device families - PC3X2 and
PC3X3. Both include an ARM1176JZ-S, 100Mb Ethernet controller, 2xDMA
controllers, eFuses, crypto offload engines and Picochip's array
processor the picoArray. This patch series adds support for the base
chip support including timers, gpio, pin muxing, clocks and pm and also
the PC7302 development board that can take either PC3X2 or PC3X3 devices.

This patch series is based off of linux-next (next-20101122) and also requires
the following patches:

    - 8250: add a UPIO_DWAPB32 for 32 bit accesses (v2):
    http://marc.info/?l=linux-serial&m=129043658715199&w=2
    - debug-8250: add a 32 bit mode:
    http://marc.info/?l=linux-arm-kernel&m=129044061221371&w=2

Jamie Iles (13):
      picoxcell: add support for picoXcell
      picoxcell: add support for AXI bus snoopers
      picoxcell: add support for the system timers
      picoxcell: add a pin muxing infrastructure
      picoxcell: add gpio infrastructure
      picoxcell: add support for system clks
      picoxcell: add common SoC devices
      picoxcell: add cpufreq support
      picoxcell: add support for pm
      picoxcell: add chained GPIO IRQ handlers
      picoxcell: add support for pc3x2 devices
      picoxcell: add support for pc3x3 devices
      picoxcell: add support for the PC7302 development board

 arch/arm/Kconfig                                   |   17 +
 arch/arm/Makefile                                  |    1 +
 arch/arm/configs/pc7302_defconfig                  |  102 ++
 arch/arm/mach-picoxcell/Kconfig                    |   38 +
 arch/arm/mach-picoxcell/Makefile                   |   11 +
 arch/arm/mach-picoxcell/Makefile.boot              |    3 +
 arch/arm/mach-picoxcell/axi2cfg.c                  |  168 +++
 arch/arm/mach-picoxcell/board_pc7302.c             |  184 +++
 arch/arm/mach-picoxcell/clk.c                      |  140 +++
 arch/arm/mach-picoxcell/cpufreq.c                  |  145 +++
 arch/arm/mach-picoxcell/devices.c                  |   77 ++
 arch/arm/mach-picoxcell/gpio.c                     |  726 +++++++++++
 arch/arm/mach-picoxcell/include/mach/clkdev.h      |   69 ++
 arch/arm/mach-picoxcell/include/mach/debug-macro.S |   18 +
 arch/arm/mach-picoxcell/include/mach/entry-macro.S |   19 +
 arch/arm/mach-picoxcell/include/mach/gpio.h        |  189 +++
 arch/arm/mach-picoxcell/include/mach/hardware.h    |   29 +
 arch/arm/mach-picoxcell/include/mach/io.h          |   32 +
 arch/arm/mach-picoxcell/include/mach/irqs.h        |   89 ++
 arch/arm/mach-picoxcell/include/mach/memory.h      |   27 +
 .../include/mach/picoxcell/axi2cfg.h               |  171 +++
 .../mach-picoxcell/include/mach/picoxcell/gpio.h   |   48 +
 .../include/mach/picoxcell/picoxcell.h             |   61 +
 .../mach-picoxcell/include/mach/picoxcell/timer.h  |   36 +
 .../mach-picoxcell/include/mach/picoxcell/wdog.h   |   43 +
 arch/arm/mach-picoxcell/include/mach/platform.h    |   32 +
 arch/arm/mach-picoxcell/include/mach/system.h      |   51 +
 arch/arm/mach-picoxcell/include/mach/timex.h       |   26 +
 arch/arm/mach-picoxcell/include/mach/uncompress.h  |   56 +
 arch/arm/mach-picoxcell/include/mach/vmalloc.h     |   18 +
 arch/arm/mach-picoxcell/mux.c                      |  302 +++++
 arch/arm/mach-picoxcell/mux.h                      |   97 ++
 arch/arm/mach-picoxcell/pc3x2.c                    |  453 +++++++
 arch/arm/mach-picoxcell/pc3x3.c                    | 1256 ++++++++++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.c           |  371 ++++++
 arch/arm/mach-picoxcell/picoxcell_core.h           |   42 +
 arch/arm/mach-picoxcell/pm.c                       |  146 +++
 arch/arm/mach-picoxcell/soc.h                      |   62 +
 arch/arm/mach-picoxcell/time.c                     |  206 ++++
 39 files changed, 5561 insertions(+), 0 deletions(-)

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH 01/13] picoxcell: add support for picoXcell
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-26 11:08   ` [PATCH] " Jamie Iles
  2010-12-08 16:09   ` [RFC PATCH 01/13] " Russell King - ARM Linux
  2010-11-23 10:06 ` [RFC PATCH 02/13] picoxcell: add support for AXI bus snoopers Jamie Iles
                   ` (13 subsequent siblings)
  14 siblings, 2 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

picoXcell is a family of femtocell SoC devices from Picochip [1] with an
ARM subsystem.  The devices have an ARM1176JZ-S core and a DSP processor
array.  Currently there are two sub families - PC3X2 and PC3X3. The
latter includes extra power and performance control along with extra
peripherals.

This initial patch adds the hardware definitions and a framework for
adding device variants.

1. http://www.picochip.com

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/Kconfig                                   |   17 ++
 arch/arm/Makefile                                  |    1 +
 arch/arm/mach-picoxcell/Kconfig                    |    4 +
 arch/arm/mach-picoxcell/Makefile                   |    1 +
 arch/arm/mach-picoxcell/Makefile.boot              |    3 +
 arch/arm/mach-picoxcell/axi2cfg.c                  |  168 +++++++++++++++++
 arch/arm/mach-picoxcell/include/mach/debug-macro.S |   18 ++
 arch/arm/mach-picoxcell/include/mach/entry-macro.S |   19 ++
 arch/arm/mach-picoxcell/include/mach/hardware.h    |   29 +++
 arch/arm/mach-picoxcell/include/mach/io.h          |   32 +++
 arch/arm/mach-picoxcell/include/mach/irqs.h        |   89 +++++++++
 arch/arm/mach-picoxcell/include/mach/memory.h      |   27 +++
 .../include/mach/picoxcell/axi2cfg.h               |  171 +++++++++++++++++
 .../mach-picoxcell/include/mach/picoxcell/gpio.h   |   48 +++++
 .../include/mach/picoxcell/picoxcell.h             |   61 ++++++
 .../mach-picoxcell/include/mach/picoxcell/timer.h  |   36 ++++
 .../mach-picoxcell/include/mach/picoxcell/wdog.h   |   43 +++++
 arch/arm/mach-picoxcell/include/mach/platform.h    |   32 +++
 arch/arm/mach-picoxcell/include/mach/system.h      |   51 +++++
 arch/arm/mach-picoxcell/include/mach/timex.h       |   26 +++
 arch/arm/mach-picoxcell/include/mach/uncompress.h  |   56 ++++++
 arch/arm/mach-picoxcell/include/mach/vmalloc.h     |   18 ++
 arch/arm/mach-picoxcell/picoxcell_core.c           |  199 ++++++++++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.h           |   21 ++
 arch/arm/mach-picoxcell/soc.h                      |   60 ++++++
 25 files changed, 1230 insertions(+), 0 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 81c71da..9c252da 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -502,6 +502,21 @@ config ARCH_ORION5X
 	  Orion-1 (5181), Orion-VoIP (5181L), Orion-NAS (5182),
 	  Orion-2 (5281), Orion-1-90 (6183).
 
+config ARCH_PICOXCELL
+	bool "Picochip picoXcell"
+	select ARM_VIC
+	select HAS_TLS_REG
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select GENERIC_GPIO
+	select ARCH_REQUIRE_GPIOLIB
+	select COMMON_CLKDEV
+	select TICK_ONESHOT
+	select CPU_V6
+	help
+	  This enables support for systems based on the Picochip picoXcell
+	  family of Femtocell devices.
+
 config ARCH_MMP
 	bool "Marvell PXA168/910/MMP2"
 	depends on MMU
@@ -924,6 +939,8 @@ source "arch/arm/mach-omap2/Kconfig"
 
 source "arch/arm/mach-orion5x/Kconfig"
 
+source "arch/arm/mach-picoxcell/Kconfig"
+
 source "arch/arm/mach-pxa/Kconfig"
 source "arch/arm/plat-pxa/Kconfig"
 
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b47baa8 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -166,6 +166,7 @@ machine-$(CONFIG_ARCH_OMAP2)		:= omap2
 machine-$(CONFIG_ARCH_OMAP3)		:= omap2
 machine-$(CONFIG_ARCH_OMAP4)		:= omap2
 machine-$(CONFIG_ARCH_ORION5X)		:= orion5x
+machine-$(CONFIG_ARCH_PICOXCELL)	:= picoxcell
 machine-$(CONFIG_ARCH_PNX4008)		:= pnx4008
 machine-$(CONFIG_ARCH_PXA)		:= pxa
 machine-$(CONFIG_ARCH_REALVIEW)		:= realview
diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig
new file mode 100644
index 0000000..3daba53
--- /dev/null
+++ b/arch/arm/mach-picoxcell/Kconfig
@@ -0,0 +1,4 @@
+menu "PICOXCELL platform type"
+	depends on ARCH_PICOXCELL
+
+endmenu
diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
new file mode 100644
index 0000000..7246a6a
--- /dev/null
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -0,0 +1 @@
+obj-y				:= picoxcell_core.o axi2cfg.o
diff --git a/arch/arm/mach-picoxcell/Makefile.boot b/arch/arm/mach-picoxcell/Makefile.boot
new file mode 100644
index 0000000..67039c3
--- /dev/null
+++ b/arch/arm/mach-picoxcell/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x00800000
diff --git a/arch/arm/mach-picoxcell/axi2cfg.c b/arch/arm/mach-picoxcell/axi2cfg.c
new file mode 100644
index 0000000..5974743
--- /dev/null
+++ b/arch/arm/mach-picoxcell/axi2cfg.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ *
+ * This file implements functions for using the axi2cfg to configure and debug
+ * picoArray systems providing configuration bus access over the axi2cfg.
+ */
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include <mach/hardware.h>
+
+/* Configuration port write bit positions. */
+#define CAEID_BIT_MASK	    (1 << 19)	/* AE ID signal. */
+#define CADDR_BIT_MASK	    (1 << 18)	/* AE ADDR signal. */
+#define CREAD_BIT_MASK	    (1 << 17)	/* READ data signal. */
+#define CWRITE_BIT_MASK     (1 << 16)	/* WRITE data signal. */
+
+#define RB_FAIL_MASK	    (1 << 17)	/* Readback failed. */
+#define RB_VALID_MASK	    (1 << 16)	/* Readback valid. */
+
+#define NR_RETRIES	    16		/* The number of retries for an
+					 * AXI2Cfg config read. */
+
+static DEFINE_SPINLOCK(axi2cfg_lock);
+
+static void __iomem *axi2cfg;
+
+#define CFG_WRITE_PORT	    0x100	/* Write port offset. */
+#define CFG_READ_PORT	    0x200	/* Read port offset. */
+
+int
+axi2cfg_config_read(u16 aeid, u16 ae_addr, u16 *buf, u16 count)
+{
+	u32 val;
+	void __iomem *write_p = axi2cfg + CFG_WRITE_PORT;
+	void __iomem *read_p = axi2cfg + CFG_READ_PORT;
+	u16 rc, to_read = count;
+	unsigned i, retries;
+	unsigned long flags;
+
+	spin_lock_irqsave(&axi2cfg_lock, flags);
+
+	val = aeid | CAEID_BIT_MASK;
+	writel(val, write_p);
+
+	while (to_read) {
+		/* Output the address to read from. */
+		val = (ae_addr + (count - to_read)) | CADDR_BIT_MASK;
+		writel(val, write_p);
+
+		/* Dispatch the read requests. We have a 64 entry FIFO. */
+		rc = min_t(u16, to_read, 64);
+		val = CREAD_BIT_MASK | rc;
+		writel(val, write_p);
+		wmb();
+
+		/* Now read the values. */
+		for (i = 0; i < rc; ++i) {
+			retries = NR_RETRIES;
+			while (retries) {
+				val = readl(read_p);
+				if (val & (RB_VALID_MASK | RB_FAIL_MASK))
+					break;
+				--retries;
+				rmb();
+			}
+
+			if (!retries || (val & RB_FAIL_MASK)) {
+				pr_warning("config read %04x@%04x failed\n",
+					   aeid,
+					   (ae_addr + (count - to_read) + i));
+				break;
+			} else
+				buf[(count - to_read) + i] = val & 0xFFFF;
+		}
+
+		if (val & RB_FAIL_MASK)
+			break;
+
+		to_read -= rc;
+	}
+
+	spin_unlock_irqrestore(&axi2cfg_lock, flags);
+
+	return !(val & RB_FAIL_MASK) ? count : -EIO;
+}
+EXPORT_SYMBOL_GPL(axi2cfg_config_read);
+
+void
+axi2cfg_config_write(u16 aeid, u16 ae_addr, const u16 *buf, u16 count)
+{
+	u32 val;
+	void __iomem *write_p = axi2cfg + CFG_WRITE_PORT;
+	unsigned i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&axi2cfg_lock, flags);
+
+	val = aeid | CAEID_BIT_MASK;
+	writel(val, write_p);
+
+	/* Output the address to read from. */
+	val = ae_addr | CADDR_BIT_MASK;
+	writel(val, write_p);
+
+	/* Now read the values. */
+	for (i = 0; i < count; ++i) {
+		val = buf[i] | CWRITE_BIT_MASK;
+		writel(val, write_p);
+	}
+
+	spin_unlock_irqrestore(&axi2cfg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(axi2cfg_config_write);
+
+void
+axi2cfg_write_buf(const u32 *buf, unsigned nr_words)
+{
+	void __iomem *write_p = axi2cfg + CFG_WRITE_PORT;
+	unsigned i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&axi2cfg_lock, flags);
+
+	/* Now read the values. */
+	for (i = 0; i < nr_words; ++i)
+		writel(*buf++, write_p);
+
+	spin_unlock_irqrestore(&axi2cfg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(axi2cfg_write_buf);
+
+unsigned long axi2cfg_readl(unsigned long offs)
+{
+	return readl(axi2cfg + offs);
+}
+
+void axi2cfg_writel(unsigned long val, unsigned long offs)
+{
+	writel(val, axi2cfg + offs);
+}
+
+u32 syscfg_read(void)
+{
+	return axi2cfg_readl(AXI2CFG_SYSCFG_REG_OFFSET);
+}
+
+void syscfg_update(u32 mask, u32 val)
+{
+	u32 tmp = syscfg_read();
+	tmp &= ~mask;
+	tmp |= (val & mask);
+	axi2cfg_writel(tmp, AXI2CFG_SYSCFG_REG_OFFSET);
+}
+
+void __init axi2cfg_init(void)
+{
+	axi2cfg = ioremap(PICOXCELL_AXI2CFG_BASE, 0x300);
+	BUG_ON(!axi2cfg);
+}
diff --git a/arch/arm/mach-picoxcell/include/mach/debug-macro.S b/arch/arm/mach-picoxcell/include/mach/debug-macro.S
new file mode 100644
index 0000000..5cbab63
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/debug-macro.S
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+*/
+
+		.macro	addruart, rp, rv
+		mov	\rv, #0x00230000
+		orr	\rp, \rv, #0x80000000
+		orr	\rv, \rv, #0xFE000000
+		.endm
+
+#define UART_SHIFT 2
+#define DEBUG_8250_ACCESS_32
+#include <asm/hardware/debug-8250.S>
diff --git a/arch/arm/mach-picoxcell/include/mach/entry-macro.S b/arch/arm/mach-picoxcell/include/mach/entry-macro.S
new file mode 100644
index 0000000..4dcda96
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/entry-macro.S
@@ -0,0 +1,19 @@
+/*
+ * entry-macro.S
+ *
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * Low-level IRQ helper macros for picoXcell platforms
+ *
+ * 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.
+ */
+#include <mach/hardware.h>
+#include <mach/io.h>
+#include <mach/irqs.h>
+
+#define VA_VIC0		IO_ADDRESS(PICOXCELL_VIC0_BASE)
+#define VA_VIC1		IO_ADDRESS(PICOXCELL_VIC1_BASE)
+
+#include <asm/entry-macro-vic2.S>
diff --git a/arch/arm/mach-picoxcell/include/mach/hardware.h b/arch/arm/mach-picoxcell/include/mach/hardware.h
new file mode 100644
index 0000000..7c203d3
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/hardware.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * This file contains the hardware definitions of the picoXcell SoC devices.
+ *
+ * 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 <mach/picoxcell/axi2cfg.h>
+#include <mach/picoxcell/gpio.h>
+#include <mach/picoxcell/picoxcell.h>
+#include <mach/picoxcell/timer.h>
+#include <mach/picoxcell/wdog.h>
+
+#endif
diff --git a/arch/arm/mach-picoxcell/include/mach/io.h b/arch/arm/mach-picoxcell/include/mach/io.h
new file mode 100644
index 0000000..8b30bf1
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/io.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#ifdef __ASSEMBLY__
+# define IO_ADDRESS(x)		    (((x) & 0x00ffffff) | 0xfe000000)
+#else /* __ASSEMBLY__ */
+# define PHYS_TO_IO(x)		    (((x) & 0x00ffffff) | 0xfe000000)
+# define IO_ADDRESS(x)		    __typesafe_io(PHYS_TO_IO((x)))
+# define IO_SPACE_LIMIT		    0xffffffff
+# define __io(a)		    __typesafe_io(a)
+# define __mem_pci(a)		    (a)
+# define iomem_ptr(a)		    (void __iomem __force *)(PHYS_TO_IO((a)))
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ARM_ARCH_IO_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/irqs.h b/arch/arm/mach-picoxcell/include/mach/irqs.h
new file mode 100644
index 0000000..0fe84ff
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/irqs.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 __IRQS_H__
+#define __IRQS_H__
+
+/* VIC0 IRQ Indexes */
+#define IRQ_VIC0_BASE	    32
+#define IRQ_EMAC	    (31 + IRQ_VIC0_BASE)
+#define IRQ_NPMUIRQ	    (30 + IRQ_VIC0_BASE)
+#define IRQ_NDMAEXTERRIRQ   (29 + IRQ_VIC0_BASE)
+#define IRQ_NDMASIRQ	    (28 + IRQ_VIC0_BASE)
+#define IRQ_NDMAIRQ	    (27 + IRQ_VIC0_BASE)
+#define IRQ_DMAC2	    (26 + IRQ_VIC0_BASE)
+#define IRQ_DMAC1	    (25 + IRQ_VIC0_BASE)
+#define IRQ_IPSEC	    (24 + IRQ_VIC0_BASE)
+#define IRQ_SRTP	    (23 + IRQ_VIC0_BASE)
+#define IRQ_AES		    (22 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO8	    (21 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO7	    (20 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO6	    (19 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO5	    (18 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO4	    (17 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO3	    (16 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO2	    (15 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO1	    (14 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO0	    (13 + IRQ_VIC0_BASE)
+#define IRQ_AXI2CFG	    (12 + IRQ_VIC0_BASE)
+#define IRQ_WDG		    (11 + IRQ_VIC0_BASE)
+#define IRQ_SSI		    (10 + IRQ_VIC0_BASE)
+#define IRQ_AXI_RD_ERR	    (9	+ IRQ_VIC0_BASE)
+#define IRQ_AXI_WR_ERR	    (8	+ IRQ_VIC0_BASE)
+#define IRQ_TIMER3	    (7	+ IRQ_VIC0_BASE)
+#define IRQ_TIMER2	    (6	+ IRQ_VIC0_BASE)
+#define IRQ_TIMER1	    (5	+ IRQ_VIC0_BASE)
+#define IRQ_TIMER0	    (4	+ IRQ_VIC0_BASE)
+#define IRQ_COMMTX	    (3	+ IRQ_VIC0_BASE)
+#define IRQ_COMMRX	    (2	+ IRQ_VIC0_BASE)
+#define IRQ_SWI		    (1	+ IRQ_VIC0_BASE)
+
+/* VIC1 IRQ Indexes */
+#define IRQ_VIC1_BASE	    0
+#define IRQ_UART1	    (10 + IRQ_VIC1_BASE)
+#define IRQ_UART2	    (9 + IRQ_VIC1_BASE)
+#define IRQ_RTC		    (8 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO7	    (7 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO6	    (6 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO5	    (5 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO4	    (4 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO3	    (3 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO2	    (2 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO1	    (1 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO0	    (0 + IRQ_VIC1_BASE)
+
+/*
+ * Virtual GPIO interrupts.
+ *
+ * We want to enable/disable interrupts for the GPIO pins through the GPIO
+ * block itself. To do this we install a chained handler. If a user requests
+ * one of the __IRQ_GPIOn interrupts then the GPIO block won't get configured.
+ * We provide these interrupts below as virtual ones that will configure the
+ * GPIO block and enable the source in the VIC.
+ */
+#define IRQ_GPIO7	    71
+#define IRQ_GPIO6	    70
+#define IRQ_GPIO5	    69
+#define IRQ_GPIO4	    68
+#define IRQ_GPIO3	    67
+#define IRQ_GPIO2	    66
+#define IRQ_GPIO1	    65
+#define IRQ_GPIO0	    64
+
+#define NR_IRQS		    72
+
+#endif /* __IRQS_H__ */
diff --git a/arch/arm/mach-picoxcell/include/mach/memory.h b/arch/arm/mach-picoxcell/include/mach/memory.h
new file mode 100644
index 0000000..47b8ba1
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/memory.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
+
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/axi2cfg.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/axi2cfg.h
new file mode 100644
index 0000000..18a4ac8
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/axi2cfg.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 PICOXCELL_AXI2CFG_H
+#define PICOXCELL_AXI2CFG_H
+
+#define AXI2CFG_SYSCFG_REG_OFFSET		0x0000
+#define AXI2CFG_JTAG_ISC_REG_OFFSET		0x0004
+#define AXI2CFG_IRQ_REG_OFFSET			0x0008
+#define AXI2CFG_PURGE_CFG_PORT_REG_OFFSET	0x000C
+#define AXI2CFG_DMA_CFG_REG_OFFSET		0x0010
+#define AXI2CFG_DEVICE_ID_REG_OFFSET		0x0014
+#define AXI2CFG_REVISION_ID_REG_OFFSET		0x0018
+#define AXI2CFG_AXI_ERR_ENABLE_REG_OFFSET	0x001C
+#define AXI2CFG_AXI_ERR_CLEAR_REG_OFFSET	0x0020
+#define AXI2CFG_AXI_ERR_MASK_REG_OFFSET		0x0024
+#define AXI2CFG_AXI_ERR_TEST_REG_OFFSET		0x0028
+#define AXI2CFG_AXI_ERR_RAW_REG_OFFSET		0x002C
+#define AXI2CFG_AXI_ERR_STATE_REG_OFFSET	0x0030
+#define AXI2CFG_CLOCK_GATING_REG_OFFSET		0x0048
+#define AXI2CFG_CONFIG_WRITE_REG_OFFSET		0x0100
+#define AXI2CFG_CONFIG_READ_REG_OFFSET		0x0200
+#define AXI2CFG_DMAC1_CONFIG_REG_OFFSET		0x0300
+
+#define AXI2CFG_SYSCFG_PA_RST_IDX		30
+#define AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_SZ	8
+#define AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_HI	23
+#define AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_LO	16
+#define AXI2CFG_SYSCFG_RW_EBI_CLK_DISABLE_IDX	15
+#define AXI2CFG_SYSCFG_RW_EXCVEC_EN_IDX		14
+#define AXI2CFG_SYSCFG_RW_RMII_EN_IDX		13
+#define AXI2CFG_SYSCFG_RW_REVMII_EN_IDX		12
+#define AXI2CFG_SYSCFG_SSI_EBI_SEL_SZ		4
+#define AXI2CFG_SYSCFG_SSI_EBI_SEL_HI		11
+#define AXI2CFG_SYSCFG_SSI_EBI_SEL_LO		8
+#define AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_IDX	7
+#define AXI2CFG_SYSCFG_MASK_AXI_ERR_IDX		6
+#define AXI2CFG_SYSCFG_RW_REMAP_IDX		5
+#define AXI2CFG_SYSCFG_WDG_PAUSE_IDX		4
+#define AXI2CFG_SYSCFG_CP15DISABLE_IDX		3
+#define AXI2CFG_SYSCFG_DMAC1_CH7_IDX		2
+#define AXI2CFG_SYSCFG_BOOT_MODE_SZ		2
+#define AXI2CFG_SYSCFG_BOOT_MODE_HI		1
+#define AXI2CFG_SYSCFG_BOOT_MODE_LO		0
+
+#define AXI2CFG_SYSCFG_PA_RST_MASK \
+	(1 << AXI2CFG_SYSCFG_PA_RST_IDX)
+#define AXI2CFG_SYSCFG_SD_ARM_GPIO_MASK	\
+	(((1 << AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_SZ) - 1) << \
+	 AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_LO)
+#define AXI2CFG_SYSCFG_RW_EXCVEC_EN_MASK \
+	(1 << AXI2CFG_SYSCFG_RW_EXCVEC_EN_IDX)
+#define AXI2CFG_SYSCFG_RW_RMII_EN_MASK \
+	(1 << AXI2CFG_SYSCFG_RW_RMII_EN_IDX)
+#define AXI2CFG_SYSCFG_RW_REVMII_EN_MASK \
+	(1 << AXI2CFG_SYSCFG_RW_REVMII_EN_IDX)
+#define AXI2CFG_SYSCFG_SSI_EBI_SEL_MASK	\
+	(((1 << AXI2CFG_SYSCFG_SSI_EBI_SEL_SZ) - 1) << \
+	 AXI2CFG_SYSCFG_SSI_EBI_SEL_LO)
+#define AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK \
+	(1 << AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_IDX)
+#define AXI2CFG_SYSCFG_MASK_AXI_ERR_MASK \
+	(1 << AXI2CFG_SYSCFG_MASK_AXI_ERR_IDX)
+#define AXI2CFG_SYSCFG_RW_REMAP_MASK \
+	(1 << AXI2CFG_SYSCFG_RW_REMAP_IDX)
+#define AXI2CFG_SYSCFG_WDG_PAUSE_MASK \
+	(1 << AXI2CFG_SYSCFG_WDG_PAUSE_IDX)
+#define AXI2CFG_SYSCFG_CP15DISABLE_MASK	\
+	(1 << AXI2CFG_SYSCFG_CP15DISABLE_IDX)
+#define AXI2CFG_SYSCFG_DMAC1_CH7_MASK \
+	(1 << AXI2CFG_SYSCFG_DMAC1_CH7_IDX)
+#define AXI2CFG_SYSCFG_BOOT_MODE_MASK \
+	(((1 << AXI2CFG_SYSCFG_BOOT_MODE_SZ) - 1) << \
+	 AXI2CFG_SYSCFG_BOOT_MODE_LO)
+
+#define AXI2CFG_AXI_RD_ERR_MASK			   0x00000FFF
+#define AXI2CFG_AXI_WR_ERR_MASK			   0x00FFF000
+#define AXI2CFG_AXI_ERR_MASK_NONE		   0
+#define AXI2CFG_AXI_ERR_ENABLE_ALL		   0x00FFFFFF
+
+#ifndef __ASSEMBLY__
+
+#include <linux/init.h>
+
+/*
+ * axi2cfg_config_read - Read a number of 16 bit words from a picoArray axi2cfg.
+ *
+ * Returns the number of words read on success, negative errno on failure.
+ *
+ * @axi2cfg_base: The base address of the upper axi2cfg.
+ * @aeid: The CAEID of the AE to read from.
+ * @ae_addr: The address to begin reading from within the AE.
+ * @buf: The buffer to store the results in.
+ * @count: The number of 16 bit words to read.
+ */
+extern int axi2cfg_config_read(u16 aeid, u16 ae_addr, u16 *buf, u16 count);
+
+/*
+ * axi2cfg_config_write - Write a number of 16 bit words to a picoArray axi2cfg.
+ *
+ * @axi2cfg_base: The base address of the upper axi2cfg.
+ * @aeid: The CAEID of the AE to write to.
+ * @ae_addr: The address to begin writing to within the AE.
+ * @buf: The buffer to read the words from.
+ * @count: The number of 16 bit words to write.
+ */
+extern void axi2cfg_config_write(u16 aeid, u16 ae_addr, const u16 *buf,
+				 u16 count);
+
+/*
+ * ax2cfg_write_buf - Write a series of configuration words to the AXI2CFG
+ *	config write port.
+ *
+ * @buf: The buffer to write.
+ * @nr_words: The number of 32 bit words to write.
+ */
+extern void axi2cfg_write_buf(const u32 *buf, unsigned nr_words);
+
+/*
+ * axi2cfg_init - initialize the AXI2CFG hardware.
+ */
+extern void __init axi2cfg_init(void);
+
+/*
+ * axi2cfg_readl - read a register in the axi2cfg.
+ *
+ * Returns the value of the register.
+ *
+ * @offs: the byte offset to read from.
+ */
+extern unsigned long axi2cfg_readl(unsigned long offs);
+
+/*
+ * axi2cfg_writel - write an axi2cfg AXI domain register.
+ *
+ * @val: the value to write.
+ * @offs: the byte offset to write to.
+ */
+extern void axi2cfg_writel(unsigned long val, unsigned long offs);
+
+/*
+ * syscfg_read - read the system configuration register.
+ */
+u32 syscfg_read(void);
+
+/*
+ * syscfg_update - update specific bits in the syscfg register
+ *
+ * @mask: the bitmask for bits to update
+ * @val: the value to write to the register.
+ */
+void syscfg_update(u32 mask, u32 val);
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* PICOXCELL_AXI2CFG_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/gpio.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/gpio.h
new file mode 100644
index 0000000..f526271
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/gpio.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 PICOXCELL_GPIO_H
+#define PICOXCELL_GPIO_H
+
+#define GPIO_SW_PORT_A_DR_REG_OFFSET	    0x00
+#define GPIO_SW_PORT_A_DDR_REG_OFFSET	    0x04
+#define GPIO_SW_PORT_A_CTL_REG_OFFSET	    0x08
+#define GPIO_SW_PORT_B_DR_REG_OFFSET	    0x0C
+#define GPIO_SW_PORT_B_DDR_REG_OFFSET	    0x10
+#define GPIO_SW_PORT_B_CTL_REG_OFFSET	    0x14
+#define GPIO_SW_PORT_C_DR_REG_OFFSET	    0x18
+#define GPIO_SW_PORT_C_DDR_REG_OFFSET	    0x1C
+#define GPIO_SW_PORT_C_CTL_REG_OFFSET	    0x20
+#define GPIO_SW_PORT_D_DR_REG_OFFSET	    0x24
+#define GPIO_SW_PORT_D_DDR_REG_OFFSET	    0x28
+#define GPIO_SW_PORT_D_CTL_REG_OFFSET	    0x2C
+
+#define GPIO_INT_EN_REG_OFFSET		    0x30
+#define GPIO_INT_MASK_REG_OFFSET	    0x34
+#define GPIO_INT_TYPE_LEVEL_REG_OFFSET	    0x38
+#define GPIO_INT_POLARITY_REG_OFFSET	    0x3c
+
+#define GPIO_INT_STATUS_REG_OFFSET	    0x40
+
+#define GPIO_PORT_A_EOI_REG_OFFSET	    0x4c
+#define GPIO_EXT_PORT_A_REG_OFFSET	    0x50
+#define GPIO_EXT_PORT_B_REG_OFFSET	    0x54
+#define GPIO_EXT_PORT_C_REG_OFFSET	    0x58
+#define GPIO_EXT_PORT_D_REG_OFFSET	    0x5C
+
+#endif /* PICOXCELL_GPIO_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/picoxcell.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/picoxcell.h
new file mode 100644
index 0000000..7f1917a
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/picoxcell.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 __PICOXCELL_H__
+#define __PICOXCELL_H__
+
+#define BOOT_ROM_BASE			0xFFFF0000
+#define BOOT_ROM_SIZE			0x400
+#define AXI2PICO_BUFFERS_BASE		0xC0000000
+#define AXI2PICO_BUFFERS_SIZE		0x00010000
+#define PICOXCELL_PERIPH_BASE		0x80000000
+#define PICOXCELL_PERIPH_LENGTH		0x00400000
+#define PICOXCELL_MEMIF_BASE		0x80000000
+#define PICOXCELL_EBI_BASE		0x80010000
+#define PICOXCELL_EMAC_BASE		0x80030000
+#define PICOXCELL_DMAC1_BASE		0x80040000
+#define PICOXCELL_DMAC2_BASE		0x80050000
+#define PICOXCELL_VIC0_BASE		0x80060000
+#define PICOXCELL_VIC1_BASE		0x80064000
+#define PICOXCELL_TZIC_BASE		0x80068000
+#define PICOXCELL_TZPC_BASE		0x80070000
+#define PICOXCELL_FUSE_BASE		0x80080000
+#define PICOXCELL_SSI_BASE		0x80090000
+#define PICOXCELL_AXI2CFG_BASE		0x800A0000
+#define PICOXCELL_IPSEC_BASE		0x80100000
+#define PICOXCELL_SRTP_BASE		0x80140000
+#define PICOXCELL_CIPHER_BASE		0x80180000
+#define PICOXCELL_RTCLK_BASE		0x80200000
+#define PICOXCELL_TIMER_BASE		0x80210000
+#define PICOXCELL_GPIO_BASE		0x80220000
+#define PICOXCELL_UART1_BASE		0x80230000
+#define PICOXCELL_UART2_BASE		0x80240000
+#define PICOXCELL_WDOG_BASE		0x80250000
+#define PC3X3_RNG_BASE			0x800B0000
+#define PC3X3_TIMER2_BASE		0x80260000
+#define PC3X3_OTP_BASE			0xFFFF8000
+
+#define EBI_CS0_BASE			0x40000000
+#define EBI_CS1_BASE			0x48000000
+#define EBI_CS2_BASE			0x50000000
+#define EBI_CS3_BASE			0x58000000
+
+#define SRAM_BASE			0x20000000
+#define SRAM_START			0x20000000
+#define SRAM_SIZE			0x00020000
+
+#endif /* __PICOXCELL_H__ */
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/timer.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/timer.h
new file mode 100644
index 0000000..3daf6b2
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/timer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 PICOXCELL_TIMER_H
+#define PICOXCELL_TIMER_H
+
+/* The spacing between individual timers. */
+#define TIMER_SPACING			    0x14
+
+#define TIMER_LOAD_COUNT_REG_OFFSET	    0x00
+#define TIMER_CONTROL_REG_OFFSET	    0x08
+#define TIMER_EOI_REG_OFFSET		    0x0c
+
+#define TIMERS_EOI_REG_OFFSET		    0xa4
+
+#define TIMER_ENABLE			    0x00000001
+#define TIMER_MODE			    0x00000002
+#define TIMER_INTERRUPT_MASK		    0x00000004
+
+#define RTCLK_CCV_REG_OFFSET		    0x00
+
+#endif /* PICOXCELL_TIMER_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/wdog.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/wdog.h
new file mode 100644
index 0000000..727780c
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/wdog.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 PICOXCELL_WDOG_H
+#define PICOXCELL_WDOG_H
+
+#define WDOG_CONTROL_REG_OFFSET		    0x00
+#define WDOG_TIMEOUT_RANGE_REG_OFFSET	    0x04
+#define WDOG_CURRENT_COUNT_REG_OFFSET	    0x08
+#define WDOG_COUNTER_RESTART_REG_OFFSET     0x0c
+#define WDOG_INT_STATUS_REG_OFFSET	    0x10
+#define WDOG_CLEAR_REG_OFFSET		    0x14
+
+#define WDOG_CONTROL_REG_RESET		    0x00000016
+#define WDOG_TIMEOUT_RANGE_REG_RESET	    0x0000000c
+#define WDOG_CURRENT_COUNT_REG_RESET	    0x0fffffff
+#define WDOG_COUNTER_RESTART_REG_RESET	    0x00000000
+#define WDOG_INT_STATUS_REG_RESET	    0x00000000
+#define WDOG_CLEAR_REG_RESET		    0x00000000
+
+#define WDOGCONTROLREGWDT_ENIDX		    0
+#define WDOGCONTROLREGRMODIDX		    1
+#define WDOGCONTROLREGRPLIDX		    2
+
+#define WDOG_CONTROL_REG_WDT_EN_MASK	    (1 << WDOGCONTROLREGWDT_ENIDX)
+#define WDOG_CONTROL_REG_RMOD_MASK	    (1 << WDOGCONTROLREGRMODIDX)
+#define WDOG_CONTROL_REG_RPL_MASK	    (0x7 << WDOGCONTROLREGRPLIDX)
+
+#endif /* PICOXCELL_WDOG_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/platform.h b/arch/arm/mach-picoxcell/include/mach/platform.h
new file mode 100644
index 0000000..ef7900f
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/platform.h
@@ -0,0 +1,32 @@
+/*
+ * platform.h
+ *
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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_PICOXCELL_PLATFORM_H
+#define __ARCH_PICOXCELL_PLATFORM_H
+
+/* Physical address of the Flash in the ARM sub-system memory map */
+#define PICOXCELL_FLASH_BASE	    0x40000000
+
+/* The clock frequency for the UARTs */
+#define PICOXCELL_BASE_BAUD	    3686400	/* 3.6864 MHz */
+
+/* The clock frequency for the timers on the various boards */
+#define PICOXCELL_TIMER_FREQ	    200000000	/* 200 MHz */
+
+#endif /* __ARCH_PICOXCELL_PLATFORM_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/system.h b/arch/arm/mach-picoxcell/include/mach/system.h
new file mode 100644
index 0000000..73d889d
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/system.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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/io.h>
+#include <mach/picoxcell/picoxcell.h>
+#include <mach/picoxcell/wdog.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(int mode, const char *cmd)
+{
+	/*
+	 * Set the watchdog to expire as soon as possible and reset the
+	 * system.
+	 */
+	writel(WDOG_CONTROL_REG_WDT_EN_MASK,
+	       IO_ADDRESS(PICOXCELL_WDOG_BASE + WDOG_CONTROL_REG_OFFSET));
+	writel(0, IO_ADDRESS(PICOXCELL_WDOG_BASE +
+			     WDOG_TIMEOUT_RANGE_REG_OFFSET));
+
+	/* Give it chance to reset. */
+	mdelay(500);
+
+	pr_crit("watchdog reset failed - entering infinite loop\n");
+}
+
+#endif /* __ASM_ARCH_SYSTEM_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/timex.h b/arch/arm/mach-picoxcell/include/mach/timex.h
new file mode 100644
index 0000000..57740c5
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 __TIMEX_H__
+#define __TIMEX_H__
+
+#include <mach/platform.h>
+
+#define CLOCK_TICK_RATE		PICOXCELL_TIMER_FREQ
+
+#endif /* __TIMEX_H__ */
+
diff --git a/arch/arm/mach-picoxcell/include/mach/uncompress.h b/arch/arm/mach-picoxcell/include/mach/uncompress.h
new file mode 100644
index 0000000..ccaf3d5
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/uncompress.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 <asm/processor.h>
+#include <linux/io.h>
+#include <linux/serial_reg.h>
+#include <mach/hardware.h>
+
+#define UART_SHIFT  2
+
+static inline void putc(int c)
+{
+	void __iomem *uart = (void __iomem *)(PICOXCELL_UART1_BASE);
+
+	while (!(readl(uart + (UART_LSR << UART_SHIFT)) & UART_LSR_THRE))
+		barrier();
+	writel(c & 0xFF, uart + (UART_TX << UART_SHIFT));
+}
+
+static inline void flush(void)
+{
+}
+
+static inline void arch_decomp_setup(void)
+{
+	void __iomem *uart = (void __iomem *)(PICOXCELL_UART1_BASE);
+
+	/* Reset and enable the FIFO's. */
+	writel(UART_FCR_ENABLE_FIFO, uart + (UART_FCR << UART_SHIFT));
+
+	/* Wait for the FIFO's to be enabled. */
+	while (!(readl(uart + (UART_FCR << UART_SHIFT)) & UART_FCR_TRIGGER_14))
+		cpu_relax();
+	/* Enable divisor access, set length to 8 bits. */
+	writel(UART_LCR_DLAB | UART_LCR_WLEN8, uart + (UART_LCR << UART_SHIFT));
+	/* Set for 115200 baud. */
+	writel(0x2, uart + (UART_DLL << UART_SHIFT));
+	writel(0x0, uart + (UART_DLM << UART_SHIFT));
+	writel(UART_LCR_WLEN8, uart + (UART_LCR << UART_SHIFT));
+}
+
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-picoxcell/include/mach/vmalloc.h b/arch/arm/mach-picoxcell/include/mach/vmalloc.h
new file mode 100644
index 0000000..a40cfa3
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/vmalloc.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 VMALLOC_END		(PAGE_OFFSET + 0x18000000)
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
new file mode 100644
index 0000000..4469a78
--- /dev/null
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support@picochip.com
+ */
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/sysdev.h>
+
+#include <asm/hardware/vic.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+
+#include <mach/hardware.h>
+
+#include "mux.h"
+#include "picoxcell_core.h"
+#include "soc.h"
+
+struct picoxcell_soc *picoxcell_get_soc(void)
+{
+	unsigned long device_id = readl(IO_ADDRESS(PICOXCELL_AXI2CFG_BASE +
+					AXI2CFG_DEVICE_ID_REG_OFFSET));
+	switch (device_id) {
+	default:
+		panic("unsupported device type %lx", device_id);
+	}
+}
+
+void __init picoxcell_init_irq(void)
+{
+	u32 vic0_resume_sources =
+		(1 << (IRQ_AXI2PICO8 & 31)) |
+		(1 << (IRQ_EMAC & 31)) |
+		(1 << (IRQ_WDG & 31));
+
+	vic_init(IO_ADDRESS(PICOXCELL_VIC0_BASE), 32, 0xFFFFFFFE,
+		 vic0_resume_sources);
+	vic_init(IO_ADDRESS(PICOXCELL_VIC1_BASE), 0, 0x7FF, 0);
+}
+
+static const char *picoxcell_get_partname(void)
+{
+	unsigned long dev_id = axi2cfg_readl(AXI2CFG_DEVICE_ID_REG_OFFSET);
+	const char *part = "<unknown>";
+
+	return part;
+}
+
+static inline unsigned long picoxcell_get_revision(void)
+{
+	return axi2cfg_readl(AXI2CFG_REVISION_ID_REG_OFFSET);
+}
+
+static struct sysdev_class soc_sysdev_class = {
+	.name		= "soc",
+};
+
+static struct sys_device soc_sysdev_device = {
+	.id		= 0,
+	.cls		= &soc_sysdev_class,
+};
+
+static ssize_t part_show(struct sys_device *sysdev,
+			 struct sysdev_attribute *attr, char *buf)
+{
+	const char *part = picoxcell_get_partname();
+	return snprintf(buf, PAGE_SIZE, "%s\n", part);
+}
+static SYSDEV_ATTR(part, 0444, part_show, NULL);
+
+static ssize_t boot_mode_show(struct sys_device *sysdev,
+			      struct sysdev_attribute *attr, char *buf)
+{
+	struct picoxcell_soc *soc = picoxcell_get_soc();
+
+	return soc->get_boot_mode(buf);
+}
+static SYSDEV_ATTR(boot_mode, 0444, boot_mode_show, NULL);
+
+static ssize_t revision_show(struct sys_device *sysdev,
+			     struct sysdev_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%lu\n", picoxcell_get_revision());
+}
+static SYSDEV_ATTR(revision, 0444, revision_show, NULL);
+
+static ssize_t die_ident_show(struct sys_device *sysdev,
+			      struct sysdev_attribute *attr, char *buf)
+{
+	int i;
+
+	for (i = 0; i < 16; ++i)
+		snprintf(buf + (i * 2), PAGE_SIZE, "%02x",
+			 readb(IO_ADDRESS(PICOXCELL_FUSE_BASE + 0x60 + i)));
+	snprintf(buf + (i * 2), PAGE_SIZE, "\n");
+
+	return 33;
+}
+static SYSDEV_ATTR(die_ident, 0444, die_ident_show, NULL);
+
+static struct __init sysdev_attribute *sysdev_attrs[] = {
+	&attr_die_ident,
+	&attr_revision,
+	&attr_boot_mode,
+	&attr_part,
+	NULL,
+};
+
+static void __init socinfo_init(void)
+{
+	if (sysdev_class_register(&soc_sysdev_class)) {
+		pr_err("unable to register sysdev class\n");
+		return;
+	}
+
+	if (sysdev_register(&soc_sysdev_device)) {
+		pr_err("unable to register sysdev device\n");
+		return;
+	}
+
+	if (sysdev_create_files(&soc_sysdev_device, sysdev_attrs))
+		pr_err("unable to add sysdev attrs\n");
+}
+
+static void __init report_chipinfo(void)
+{
+	const char *part = picoxcell_get_partname();
+	unsigned long revision = picoxcell_get_revision();
+
+	pr_info("Picochip picoXcell device: %s revision %lu\n", part, revision);
+}
+
+struct dentry *arch_debugfs_dir;
+
+static void __init picoxcell_debugfs_init(void)
+{
+	arch_debugfs_dir = debugfs_create_dir("picoxcell", NULL);
+	if (IS_ERR(arch_debugfs_dir)) {
+		/* debugfs is enabled but we failed. */
+		if (-ENODEV != PTR_ERR(arch_debugfs_dir))
+			pr_err("failed to create picoxcell debugfs entry (%ld)\n",
+			       PTR_ERR(arch_debugfs_dir));
+		arch_debugfs_dir = NULL;
+		return;
+	}
+}
+
+static struct map_desc __initdata picoxcell_io_desc[] = {
+	{
+		.virtual    = PHYS_TO_IO(AXI2PICO_BUFFERS_BASE),
+		.pfn	    = __phys_to_pfn(AXI2PICO_BUFFERS_BASE),
+		.length     = AXI2PICO_BUFFERS_SIZE,
+		.type	    = MT_DEVICE,
+	},
+	{
+		.virtual    = PHYS_TO_IO(PICOXCELL_PERIPH_BASE),
+		.pfn	    = __phys_to_pfn(PICOXCELL_PERIPH_BASE),
+		.length     = PICOXCELL_PERIPH_LENGTH,
+		.type	    = MT_DEVICE,
+	},
+};
+
+void __init picoxcell_map_io(void)
+{
+	struct picoxcell_soc *soc;
+
+	iotable_init(picoxcell_io_desc, ARRAY_SIZE(picoxcell_io_desc));
+
+	soc = picoxcell_get_soc();
+	if (soc->init_io)
+		soc->init_io();
+}
+
+void __init picoxcell_core_init(void)
+{
+	struct picoxcell_soc *soc = picoxcell_get_soc();
+
+	BUG_ON(!soc);
+
+	axi2cfg_init();
+	report_chipinfo();
+	soc->init();
+
+	/*
+	 * Add the soc/soc0 entries to /sys/devices/system to report the
+	 * device type, boot mode etc.
+	 */
+	socinfo_init();
+
+	/* Add the arch debugfs entry. */
+	picoxcell_debugfs_init();
+}
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.h b/arch/arm/mach-picoxcell/picoxcell_core.h
new file mode 100644
index 0000000..5b27e52
--- /dev/null
+++ b/arch/arm/mach-picoxcell/picoxcell_core.h
@@ -0,0 +1,21 @@
+/*
+ * linux/arch/arm/mach-picoxcell/picoxcell_core.h
+ *
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#ifndef __ASM_ARCH_PICOXCELL_CORE_H__
+#define __ASM_ARCH_PICOXCELL_CORE_H__
+
+struct picoxcell_soc;
+
+extern void __init picoxcell_core_init(void);
+extern void __init picoxcell_init_irq(void);
+extern void __init picoxcell_map_io(void);
+
+#endif /* __ASM_ARCH_PICOXCELL_CORE_H__ */
diff --git a/arch/arm/mach-picoxcell/soc.h b/arch/arm/mach-picoxcell/soc.h
new file mode 100644
index 0000000..784d714
--- /dev/null
+++ b/arch/arm/mach-picoxcell/soc.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#ifndef __PICOXCELL_SOC_H__
+#define __PICOXCELL_SOC_H__
+
+enum timer_type {
+	TIMER_TYPE_RTC,
+	TIMER_TYPE_TIMER,
+};
+
+struct picoxcell_timer {
+	const char			*name;
+	enum timer_type			type;
+	unsigned long			base;
+	int				irq;
+};
+
+enum picoxcell_features {
+	PICOXCELL_FEATURE_EBI,
+	PICOXCELL_FEATURE_SW_NAND,
+	PICOXCELL_FEATURE_PM,
+	PICOXCELL_FEATURE_CPUFREQ,
+	NR_FEAT_BITS
+};
+
+struct picoxcell_soc {
+	void				(*init)(void);
+	void				(*init_clocks)(void);
+	void				(*init_timers)(void);
+	void				(*init_muxing)(void);
+	void				(*init_io)(void);
+	ssize_t				(*get_boot_mode)(char *buf);
+	const char * const		*armgpio_pins;
+	int				nr_armgpio;
+	int				armgpio_base;
+	const char * const		*sdgpio_pins;
+	int				nr_sdgpio;
+	int				sdgpio_base;
+	const struct picoxcell_timer	*timers;
+	int				nr_timers;
+	unsigned long			features[BITS_TO_LONGS(NR_FEAT_BITS)];
+};
+
+extern struct picoxcell_soc *picoxcell_get_soc(void);
+
+static inline int picoxcell_has_feature(enum picoxcell_features feat)
+{
+	struct picoxcell_soc *soc = picoxcell_get_soc();
+
+	return test_bit(feat, soc->features);
+}
+
+#endif /* __PICOXCELL_SOC_H__ */
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 02/13] picoxcell: add support for AXI bus snoopers
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 01/13] picoxcell: add support for picoXcell Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-12-08 16:10   ` Russell King - ARM Linux
  2010-11-23 10:06 ` [RFC PATCH 03/13] picoxcell: add support for the system timers Jamie Iles
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

The picoXcell devices have a number of peripherals that may perform
master accesses to the AXI bus, but if incorrectly programmed can
generate a bus error. The AXI2CFG block has bus snoopers that monitor
the bus for errors from other masters and raises an interrupt if
detected. These errors are typically irrecoverable so panic if we see
them.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/picoxcell_core.c |   89 ++++++++++++++++++++++++++++++
 1 files changed, 89 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 4469a78..07eaa9b 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -71,6 +71,7 @@ static ssize_t part_show(struct sys_device *sysdev,
 			 struct sysdev_attribute *attr, char *buf)
 {
 	const char *part = picoxcell_get_partname();
+
 	return snprintf(buf, PAGE_SIZE, "%s\n", part);
 }
 static SYSDEV_ATTR(part, 0444, part_show, NULL);
@@ -178,6 +179,91 @@ void __init picoxcell_map_io(void)
 		soc->init_io();
 }
 
+/*
+ * AXI Bus Read / Write Error Handling
+ *
+ * Some of the peripherals on the AXI bus can generate aborts. For example, a
+ * DMAC trying to DMA from the EBI. This isn't supported and will generate an
+ * error response. This can't be recovered from so we report the error and
+ * panic.
+ *
+ * Given a bit number in the AXI2Cfg snoop AXI error IRQ post mask register,
+ * give the textual name of the operation that generated the error.
+ */
+static const char *axi_bus_error_name(int bit)
+{
+	static const char *snoop_err_names[32] = {
+		[0]	= "dmac1_channel0 (read)",
+		[1]	= "dmac1_channel1 (read)",
+		[2]	= "dmac1_channel2 (read)",
+		[3]	= "dmac1_channel3 (read)",
+		[4]	= "dmac2_channel0 (read)",
+		[5]	= "dmac2_channel1 (read)",
+		[6]	= "dmac2_channel2 (read)",
+		[7]	= "dmac2_channel3 (read)",
+		[8]	= "emac (read)",
+		[9]	= "cipher (read)",
+		[10]	= "srtp (read)",
+		[11]	= "ipsec (read)",
+		[12]	= "dmac1_channel0 (write)",
+		[13]	= "dmac1_channel1 (write)",
+		[14]	= "dmac1_channel2 (write)",
+		[15]	= "dmac1_channel3 (write)",
+		[16]	= "dmac2_channel0 (write)",
+		[17]	= "dmac2_channel1 (write)",
+		[18]	= "dmac2_channel2 (write)",
+		[19]	= "dmac2_channel3 (write)",
+		[20]	= "emac (write)",
+		[21]	= "cipher (write)",
+		[22]	= "srtp (write)",
+		[23]	= "ipsec (write)",
+	};
+
+	return snoop_err_names[bit] ?: "<INVALID SNOOP ERROR>";
+}
+
+/* AXI Bus write errors */
+static irqreturn_t picoxcell_axi_bus_error_interrupt(int irq, void *dev_id)
+{
+	/*
+	 * If we ever get one of these interrupts then we are in big trouble,
+	 * they should never happen. The error condition is non recoverable.
+	 */
+	unsigned long axi_error =
+		axi2cfg_readl(AXI2CFG_AXI_ERR_STATE_REG_OFFSET);
+	int bit;
+
+	for_each_set_bit(bit, &axi_error, 32) {
+		pr_emerg("AXI bus error [%s] detected\n",
+			 axi_bus_error_name(bit));
+	}
+	panic("unable to handle AXI bus error");
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction picoxcell_axi_error_irq = {
+	.name	    = "picoxcell axi bus error",
+	.flags	    = IRQF_DISABLED,
+	.handler    = picoxcell_axi_bus_error_interrupt,
+};
+
+/* Initialise AXI Bus error handling */
+static void __init picoxcell_axi_bus_error_init(void)
+{
+	/* Setup the irq handler for AXI read and write errors. */
+	setup_irq(IRQ_AXI_RD_ERR, &picoxcell_axi_error_irq);
+	setup_irq(IRQ_AXI_WR_ERR, &picoxcell_axi_error_irq);
+
+	/* Make sure no AXI errors are masked */
+	axi2cfg_writel(AXI2CFG_AXI_ERR_MASK_NONE,
+		       AXI2CFG_AXI_ERR_MASK_REG_OFFSET);
+
+	/* Enable interrupts for all AXI Read & Write errors */
+	axi2cfg_writel(AXI2CFG_AXI_ERR_ENABLE_ALL,
+		       AXI2CFG_AXI_ERR_ENABLE_REG_OFFSET);
+}
+
 void __init picoxcell_core_init(void)
 {
 	struct picoxcell_soc *soc = picoxcell_get_soc();
@@ -196,4 +282,7 @@ void __init picoxcell_core_init(void)
 
 	/* Add the arch debugfs entry. */
 	picoxcell_debugfs_init();
+
+	/* Add handlers for the AXI bus snooping. */
+	picoxcell_axi_bus_error_init();
 }
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 03/13] picoxcell: add support for the system timers
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 01/13] picoxcell: add support for picoXcell Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 02/13] picoxcell: add support for AXI bus snoopers Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-26 11:11   ` [RFC PATCHv2] " Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 04/13] picoxcell: add a pin muxing infrastructure Jamie Iles
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

The picoXcell devices have 4 timers capable of generating interrupts
when they reach a predefined value and restarting and a freerunning RTC.
Use one of the interrupt capable timers as the clockevent_device and the
RTC for the clocksource and sched_clock().

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile         |    3 +-
 arch/arm/mach-picoxcell/picoxcell_core.h |    2 +
 arch/arm/mach-picoxcell/time.c           |  206 ++++++++++++++++++++++++++++++
 3 files changed, 210 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 7246a6a..726c61f 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -1 +1,2 @@
-obj-y				:= picoxcell_core.o axi2cfg.o
+obj-y				:= picoxcell_core.o axi2cfg.o \
+				   time.o
\ No newline at end of file
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.h b/arch/arm/mach-picoxcell/picoxcell_core.h
index 5b27e52..941d1a6 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.h
+++ b/arch/arm/mach-picoxcell/picoxcell_core.h
@@ -13,9 +13,11 @@
 #define __ASM_ARCH_PICOXCELL_CORE_H__
 
 struct picoxcell_soc;
+struct sys_timer;
 
 extern void __init picoxcell_core_init(void);
 extern void __init picoxcell_init_irq(void);
 extern void __init picoxcell_map_io(void);
+extern struct sys_timer picoxcell_sys_timer;
 
 #endif /* __ASM_ARCH_PICOXCELL_CORE_H__ */
diff --git a/arch/arm/mach-picoxcell/time.c b/arch/arm/mach-picoxcell/time.c
new file mode 100644
index 0000000..67062cb
--- /dev/null
+++ b/arch/arm/mach-picoxcell/time.c
@@ -0,0 +1,206 @@
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/hardware.h>
+
+#include "picoxcell_core.h"
+#include "soc.h"
+
+enum timer_id {
+	TIMER_ID_CLOCKEVENT,
+	TIMER_ID_CLOCKSOURCE,
+	NR_TIMERS,
+};
+
+struct timer_instance {
+	void __iomem	    *base;
+	struct irqaction    irqaction;
+};
+
+/*
+ * We expect to have 2 timers - a freerunning one for the clock source and a
+ * periodic/oneshot one for the clock_event_device.
+ */
+static struct timer_instance timers[NR_TIMERS];
+
+static void timer_set_mode(enum clock_event_mode mode,
+			   struct clock_event_device *clk)
+{
+	struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
+	unsigned long load_count = DIV_ROUND_UP(CLOCK_TICK_RATE, HZ);
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		/*
+		 * By default, use the kernel tick rate. The reload value can
+		 * be changed with the timer_set_next_event() function.
+		 */
+		writel(load_count, timer->base + TIMER_LOAD_COUNT_REG_OFFSET);
+		writel(TIMER_ENABLE | TIMER_MODE,
+		       timer->base + TIMER_CONTROL_REG_OFFSET);
+		break;
+
+	case CLOCK_EVT_MODE_ONESHOT:
+		writel(TIMER_MODE, timer->base + TIMER_CONTROL_REG_OFFSET);
+		break;
+
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	default:
+		writel(0, timer->base + TIMER_CONTROL_REG_OFFSET);
+		break;
+	}
+}
+
+static int timer_set_next_event(unsigned long evt,
+				struct clock_event_device *clk)
+{
+	struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
+
+	/* Disable the timer, write the new event then enable it. */
+	writel(0, timer->base + TIMER_CONTROL_REG_OFFSET);
+	writel(evt, timer->base + TIMER_LOAD_COUNT_REG_OFFSET);
+	writel(TIMER_ENABLE | TIMER_MODE,
+	       timer->base + TIMER_CONTROL_REG_OFFSET);
+
+	return 0;
+}
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id);
+
+static struct clock_event_device clockevent_picoxcell = {
+	.features		= CLOCK_EVT_FEAT_PERIODIC |
+				  CLOCK_EVT_FEAT_ONESHOT,
+	.shift			= 20,
+	.set_next_event		= timer_set_next_event,
+	.set_mode		= timer_set_mode,
+};
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+	struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
+
+	/* If we are in oneshot mode, we need to stop receiving interrupts. */
+	if (CLOCK_EVT_MODE_ONESHOT == clockevent_picoxcell.mode) {
+		unsigned long val = readl(timer->base +
+					  TIMER_CONTROL_REG_OFFSET);
+		val |= TIMER_INTERRUPT_MASK;
+		writel(val, timer->base + TIMER_CONTROL_REG_OFFSET);
+	}
+
+	/* Clear the interrupt. */
+	readl(timer->base + TIMER_EOI_REG_OFFSET);
+
+	clockevent_picoxcell.event_handler(&clockevent_picoxcell);
+
+	return IRQ_HANDLED;
+}
+
+static void picoxcell_clockevent_init(struct picoxcell_soc *soc)
+{
+	struct timer_instance *inst = &timers[TIMER_ID_CLOCKEVENT];
+	const struct picoxcell_timer *timer = NULL;
+	int i;
+
+	for (i = 0; i < soc->nr_timers; ++i)
+		if (soc->timers[i].type == TIMER_TYPE_TIMER) {
+			timer = &soc->timers[i];
+			break;
+		}
+
+	BUG_ON(!timer);
+
+	/* Configure the interrupt for this timer. */
+	inst->irqaction.name	= timer->name;
+	inst->irqaction.handler	= timer_interrupt;
+	inst->irqaction.flags	= IRQF_DISABLED | IRQF_TIMER | IRQF_PROBE;
+	inst->base		= ioremap(timer->base, TIMER_SPACING);
+
+	clockevent_picoxcell.name   = timer->name;
+	clockevent_picoxcell.mult   = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC,
+					     clockevent_picoxcell.shift);
+	clockevent_picoxcell.max_delta_ns =
+		clockevent_delta2ns(0xfffffffe, &clockevent_picoxcell);
+	clockevent_picoxcell.min_delta_ns = 50000;
+	clockevent_picoxcell.cpumask = cpumask_of(0);
+
+	/* Start with the timer disabled and the interrupt enabled. */
+	writel(0, inst->base + TIMER_CONTROL_REG_OFFSET);
+	setup_irq(timer->irq, &inst->irqaction);
+
+	clockevents_register_device(&clockevent_picoxcell);
+}
+
+static cycle_t picoxcell_rtc_get_cycles(struct clocksource *cs)
+{
+	struct timer_instance *inst = &timers[TIMER_ID_CLOCKSOURCE];
+
+	return readl(inst->base + RTCLK_CCV_REG_OFFSET);
+}
+
+/*
+ * Kernel assumes that sched_clock can be called early but may not have
+ * things ready yet.
+ */
+static cycle_t read_dummy(struct clocksource *cs)
+{
+	return 0;
+}
+
+static struct clocksource clocksource_picoxcell = {
+	.name	    = "rtc",
+	.rating     = 300,
+	.read	    = read_dummy,
+	.mask	    = CLOCKSOURCE_MASK(32),
+	.shift	    = 2,
+	.flags	    = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void __init picoxcell_clocksource_init(struct picoxcell_soc *soc)
+{
+	const struct picoxcell_timer *timer = NULL;
+	int i;
+
+	for (i = 0; i < soc->nr_timers; ++i)
+		if (soc->timers[i].type == TIMER_TYPE_RTC) {
+			timer = &soc->timers[i];
+			break;
+		}
+
+	BUG_ON(!timer);
+
+	timers[TIMER_ID_CLOCKSOURCE].base = ioremap(timer->base, SZ_4K);
+
+	/* The RTC is always running. We don't need to do any initialization. */
+	clocksource_picoxcell.mult = clocksource_hz2mult(CLOCK_TICK_RATE,
+						clocksource_picoxcell.shift);
+	clocksource_picoxcell.read = picoxcell_rtc_get_cycles;
+	clocksource_register(&clocksource_picoxcell);
+}
+
+/*
+ * Overwrite weak default sched_clock with something more precise
+ */
+unsigned long long notrace sched_clock(void)
+{
+	const cycle_t cyc = clocksource_picoxcell.read(&clocksource_picoxcell);
+
+	return clocksource_cyc2ns(cyc, clocksource_picoxcell.mult,
+				  clocksource_picoxcell.shift);
+}
+
+static void __init picoxcell_timer_init(void)
+{
+	struct picoxcell_soc *soc = picoxcell_get_soc();
+
+	picoxcell_clocksource_init(soc);
+	picoxcell_clockevent_init(soc);
+}
+
+struct sys_timer picoxcell_sys_timer = {
+	.init	= picoxcell_timer_init,
+};
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 04/13] picoxcell: add a pin muxing infrastructure
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (2 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 03/13] picoxcell: add support for the system timers Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 05/13] picoxcell: add gpio infrastructure Jamie Iles
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

picoXcell devices have two types of GPIO pins - ARM and SDGPIO (the
latter are in the picoArray domain) and some of these pins are also
shared with peripherals. Provide an API for configuring the multiplexing
of each pin and a userspace sysfs interface so they can be reprogrammed
at runtime. Some of the peripherals that use the pins are driven by the
DSP array and so the kernel won't know how to configure these pins so
this is left to the userspace application.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile         |    3 +-
 arch/arm/mach-picoxcell/mux.c            |  302 ++++++++++++++++++++++++++++++
 arch/arm/mach-picoxcell/mux.h            |   97 ++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.c |    3 +
 4 files changed, 404 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 726c61f..5e9a7cd 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -1,2 +1,3 @@
 obj-y				:= picoxcell_core.o axi2cfg.o \
-				   time.o
\ No newline at end of file
+				   time.o \
+				   mux.o
diff --git a/arch/arm/mach-picoxcell/mux.c b/arch/arm/mach-picoxcell/mux.c
new file mode 100644
index 0000000..a0b1890
--- /dev/null
+++ b/arch/arm/mach-picoxcell/mux.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#define pr_fmt(fmt) "picoxcell_mux: " fmt
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/sysdev.h>
+
+#include "mux.h"
+
+static inline struct muxed_pin *
+to_multiplexed_pin(struct sysdev_attribute *attr)
+{
+	return container_of(attr, struct muxed_pin, attr);
+}
+
+static struct {
+	unsigned	    num_groups;
+	struct pin_group    **groups;
+	int		    (*sdgpio_number)(int pin);
+	int		    (*armgpio_number)(int pin);
+} mux_info;
+
+void picoxcell_mux_register(struct pin_group **groups, int nr_groups,
+			    int (*armgpio_number)(int pin),
+			    int (*sdgpio_number)(int pin))
+{
+	BUG_ON(!groups || !armgpio_number || !sdgpio_number);
+	mux_info.num_groups	= nr_groups;
+	mux_info.groups		= groups;
+	mux_info.armgpio_number = armgpio_number;
+	mux_info.sdgpio_number	= sdgpio_number;
+}
+
+int picoxcell_pin_set_mux(int pin_nr, enum mux_setting setting)
+{
+	unsigned i, j;
+	int ret = 0;
+
+	/*
+	 * Don't let users try and trick us - they can't change the hardware
+	 * that much!
+	 */
+	if (MUX_UNMUXED == setting)
+		return -EINVAL;
+
+	for (i = 0; i < mux_info.num_groups; ++i) {
+		struct pin_group *group = mux_info.groups[i];
+		for (j = 0; j < group->nr_pins; ++j) {
+			struct muxed_pin *pin = &group->pins[j];
+			/*
+			 * Dedicated GPIO pins aren't shared with a
+			 * peripheral. This is illegal!
+			 */
+			if (pin->is_dedicated_gpio && MUX_PERIPHERAL == setting)
+				return -EINVAL;
+			if (pin_nr == pin->arm_pin || pin_nr == pin->sd_pin) {
+				ret = pin->set_mux(pin, setting);
+				if (!ret)
+					goto out;
+				/*
+				 * If we failed to set the muxing of this pin,
+				 * carry on looping as we have some
+				 * many-to-many pins so we might pick it up
+				 * again on another output.
+				 */
+			}
+		}
+	}
+
+	/*
+	 * If we don't have a multiplexed pin entry for the requested pin then
+	 * we assume that the pin isn't multiplexed so we don't need to do
+	 * anything.
+	 */
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(picoxcell_pin_set_mux);
+
+int picoxcell_group_set_mux(const char *group_name, enum mux_setting setting)
+{
+	unsigned i, j;
+	int err = -ENXIO;
+
+	/*
+	 * Don't let users try and trick us - they can't change the hardware
+	 * that much!
+	 */
+	if (MUX_UNMUXED == setting)
+		return -EINVAL;
+
+	for (i = 0; i < mux_info.num_groups; ++i) {
+		struct pin_group *group = mux_info.groups[i];
+		if (strcmp(group->name, group_name))
+			continue;
+
+		for (j = 0; j < group->nr_pins; ++j) {
+			struct muxed_pin *pin = &group->pins[j];
+			/*
+			 * Dedicated GPIO pins aren't shared with a
+			 * peripheral. This is illegal!
+			 */
+			if (pin->is_dedicated_gpio && MUX_PERIPHERAL == setting)
+				return -EINVAL;
+			err = pin->set_mux(pin, setting);
+			if (err)
+				goto out;
+		}
+
+		break;
+	}
+
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(picoxcell_group_set_mux);
+
+int picoxcell_get_pin_mux(int pin_nr)
+{
+	unsigned i, j;
+	int ret = 0;
+
+	for (i = 0; i < mux_info.num_groups; ++i) {
+		struct pin_group *group = mux_info.groups[i];
+		for (j = 0; j < group->nr_pins; ++j) {
+			struct muxed_pin *pin = &group->pins[j];
+			if (pin_nr == pin->arm_pin || pin_nr == pin->sd_pin) {
+				int tmp = pin->get_mux(pin);
+				if (tmp < 0)
+					return tmp;
+				ret |= tmp;
+			}
+		}
+	}
+
+	/*
+	 * If we don't have a multiplexed pin entry for the requested pin then
+	 * we assume that the pin isn't multiplexed.
+	 */
+	return ret ? ret : MUX_UNMUXED;
+}
+EXPORT_SYMBOL_GPL(picoxcell_get_pin_mux);
+
+static const char *group_name(struct muxed_pin *pin)
+{
+	int i, j;
+
+	for (i = 0; i < mux_info.num_groups; ++i) {
+		struct pin_group *group = mux_info.groups[i];
+		for (j = 0; j < group->nr_pins; ++j)
+			if (pin == &group->pins[j])
+				return group->name;
+	}
+
+	return "<unknown>";
+}
+
+static const char *pin_setting_name(struct muxed_pin *pin)
+{
+	const char *ret;
+
+	if (MUX_SD == pin->get_mux(pin))
+		ret = "sdgpio";
+	else if (MUX_ARM == pin->get_mux(pin))
+		ret = "armgpio";
+	else if (MUX_PERIPHERAL == pin->get_mux(pin))
+		ret = "peripheral";
+	else
+		ret = "<invalid>";
+
+	return ret;
+}
+
+ssize_t pin_show(struct sys_device *dev, struct sysdev_attribute *attr,
+		 char *buf)
+{
+	struct muxed_pin *pin = to_multiplexed_pin(attr);
+	const char *setting = pin_setting_name(pin);
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%s\n", setting);
+
+	return ret;
+}
+
+ssize_t pin_store(struct sys_device *dev, struct sysdev_attribute *attr,
+		  const char *buf, size_t len)
+{
+	ssize_t ret = -EINVAL;
+	struct muxed_pin *pin = to_multiplexed_pin(attr);
+
+	if (sysfs_streq(buf, "sdgpio"))
+		ret = pin->set_mux(pin, MUX_SD);
+	else if (sysfs_streq(buf, "armgpio"))
+		ret = pin->set_mux(pin, MUX_ARM);
+	else if (sysfs_streq(buf, "peripheral") ||
+		 sysfs_streq(buf, group_name(pin)))
+		ret = pin->set_mux(pin, MUX_PERIPHERAL);
+
+	return ret ?: len;
+}
+
+static struct sysdev_class muxing_class = {
+	.name		= "io_muxing",
+};
+
+static struct sys_device muxing_device = {
+	.id		= 0,
+	.cls		= &muxing_class,
+};
+
+static void __init muxing_sysfs_init(void)
+{
+	int i, j, err = sysdev_class_register(&muxing_class);
+
+	if (err) {
+		pr_err("unable to register sysdev class (%d)\n", err);
+		return;
+	}
+
+	err = sysdev_register(&muxing_device);
+	if (err) {
+		pr_err("unable to register sysdev device (%d)\n", err);
+		return;
+	}
+
+	for (i = 0; i < mux_info.num_groups; ++i) {
+		struct pin_group *group = mux_info.groups[i];
+		for (j = 0; j < group->nr_pins; ++j) {
+			struct muxed_pin *pin = &group->pins[j];
+			err = sysdev_create_file(&muxing_device, &pin->attr);
+			if (err) {
+				pr_err("unable to create attr for %s\n",
+				       pin->name);
+				return;
+			}
+		}
+	}
+}
+
+static ssize_t io_muxing_show(struct seq_file *s, void *priv)
+{
+	unsigned i, j;
+	ssize_t ret = 0;
+
+	ret += seq_printf(s, "%16s%16s%16s%10s%10s\n\n", "group_name",
+			  "pin_name", "setting", "arm pin", "sd pin");
+	for (i = 0; i < mux_info.num_groups; ++i) {
+		struct pin_group *group = mux_info.groups[i];
+		for (j = 0; j < group->nr_pins; ++j) {
+			struct muxed_pin *pin = &group->pins[j];
+			ret += seq_printf(s, "%16s%16s%16s%10d%10d\n",
+					  group->name, pin->name,
+					  pin_setting_name(pin),
+					  mux_info.armgpio_number(pin->arm_pin),
+					  mux_info.sdgpio_number(pin->sd_pin));
+		}
+	}
+
+	return ret;
+}
+
+static int io_muxing_debugfs_open(struct inode *inode, struct file *filp)
+{
+	return single_open(filp, io_muxing_show, inode->i_private);
+}
+
+static const struct file_operations io_muxing_debugfs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= io_muxing_debugfs_open,
+	.llseek		= seq_lseek,
+	.read		= seq_read,
+	.release	= single_release,
+};
+
+static void __init picoxcell_muxing_debugfs_init(void)
+{
+	/* We only get called if debugfs is enabled and configured. */
+	struct dentry *mux_debugfs_file =
+		debugfs_create_file("io_muxing", 0444, arch_debugfs_dir, NULL,
+				    &io_muxing_debugfs_fops);
+	if (IS_ERR(mux_debugfs_file)) {
+		pr_err("failed to create io_muxing debugfs entry (%ld)\n",
+		       PTR_ERR(mux_debugfs_file));
+	}
+}
+
+void __init picoxcell_muxing_init(struct picoxcell_soc *soc)
+{
+	if (soc->init_muxing)
+		soc->init_muxing();
+
+	muxing_sysfs_init();
+
+	picoxcell_muxing_debugfs_init();
+}
diff --git a/arch/arm/mach-picoxcell/mux.h b/arch/arm/mach-picoxcell/mux.h
new file mode 100644
index 0000000..54933e6
--- /dev/null
+++ b/arch/arm/mach-picoxcell/mux.h
@@ -0,0 +1,97 @@
+/*
+ * linux/arch/arm/mach-picoxcell/mux.h
+ *
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#ifndef __MUX_H__
+#define __MUX_H__
+
+#include <linux/sysdev.h>
+
+#include "soc.h"
+
+/*
+ * Pins can typically be:
+ *	- a system function such as EBI, SSI etc,
+ *	- ARM controlled GPIO.
+ *	- picoArray controlled GPIO.
+ *	- not multiplexed at all (MUX_UNMUXED).
+ */
+enum mux_setting {
+	MUX_PERIPHERAL	= (1 << 0),
+	MUX_ARM		= (1 << 1),
+	MUX_SD		= (1 << 2),
+	MUX_UNMUXED	= (1 << 3),
+};
+
+/*
+ * A multiplexed pin. This defines the SD and ARM pins that are on the pad. If
+ * the pin does not have an SD or ARM pin then set the appropriate field to
+ * -1.
+ */
+struct muxed_pin {
+	const char		*name;
+	int			is_dedicated_gpio;
+	int			sd_pin;
+	int			arm_pin;
+	int			(*set_mux)(struct muxed_pin *pin,
+					   enum mux_setting setting);
+	int			(*get_mux)(struct muxed_pin *pin);
+	struct sysdev_attribute	attr;
+};
+
+/*
+ * A logical group of multiplexed pins. Typically this is grouped by what the
+ * pins are multiplexed with e.g. system peripheral.
+ */
+struct pin_group {
+	int		    nr_pins;
+	const char	    *name;
+	struct muxed_pin    *pins;
+};
+
+extern int picoxcell_pin_set_mux(int pin_nr, enum mux_setting setting);
+extern int picoxcell_group_set_mux(const char *group_name,
+				   enum mux_setting setting);
+extern int picoxcell_get_pin_mux(int pin_nr);
+extern void picoxcell_mux_register(struct pin_group **groups, int nr_groups,
+				   int (*arm_number)(int pin),
+				   int (*sdgpio_number)(int pin));
+extern void __init picoxcell_muxing_init(struct picoxcell_soc *soc);
+
+extern ssize_t pin_show(struct sys_device *dev, struct sysdev_attribute *attr,
+			char *buf);
+extern ssize_t pin_store(struct sys_device *dev, struct sysdev_attribute *attr,
+			 const char *buf, size_t len);
+
+
+#define __PIN(_name, _sd, _arm, _set, _get, _dedicated) {		      \
+	.name		    = __stringify(_name),			      \
+	.is_dedicated_gpio  = _dedicated,				      \
+	.sd_pin		    = (_sd),					      \
+	.arm_pin	    = (_arm),					      \
+	.set_mux	    = _set,					      \
+	.get_mux	    = _get,					      \
+	.attr		    = _SYSDEV_ATTR(_name, 0644, pin_show, pin_store), \
+}
+
+/*
+ * Declare a function pin that is also multiplexed with GPIO pins.
+ */
+#define PIN(_name, _sd, _arm, _set, _get) \
+	__PIN(_name, _sd, _arm, _set, _get, 0)
+
+/*
+ * Declare a pure GPIO pin.
+ */
+#define GPIO(_name, _sd, _arm, _set, _get) \
+	__PIN(_name, _sd, _arm, _set, _get, 1)
+
+
+#endif /* __MUX_H__ */
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 07eaa9b..8f2cc46 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -283,6 +283,9 @@ void __init picoxcell_core_init(void)
 	/* Add the arch debugfs entry. */
 	picoxcell_debugfs_init();
 
+	/* Initialise the pin muxing and gpio infrastructure. */
+	picoxcell_muxing_init(soc);
+
 	/* Add handlers for the AXI bus snooping. */
 	picoxcell_axi_bus_error_init();
 }
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 05/13] picoxcell: add gpio infrastructure
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (3 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 04/13] picoxcell: add a pin muxing infrastructure Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-30 18:08   ` [RFC PATCHv2 " Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 06/13] picoxcell: add support for system clks Jamie Iles
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

Add a gpio infrastructure for picoXcell devices. This adds support for
both the ARM GPIO peripheral and the SDGPIO pins that are in the
picoArray domain and accessed through the AXI2CFG. This implements a set
of gpiolib chips and also adds an extension to use the sigma-delta
capabilities of the SDGPIO pins in kernelspace.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile            |    3 +-
 arch/arm/mach-picoxcell/gpio.c              |  726 +++++++++++++++++++++++++++
 arch/arm/mach-picoxcell/include/mach/gpio.h |  187 +++++++
 arch/arm/mach-picoxcell/picoxcell_core.c    |    5 +
 4 files changed, 920 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 5e9a7cd..f6b3765 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -1,3 +1,4 @@
 obj-y				:= picoxcell_core.o axi2cfg.o \
 				   time.o \
-				   mux.o
+				   mux.o \
+				   gpio.o
diff --git a/arch/arm/mach-picoxcell/gpio.c b/arch/arm/mach-picoxcell/gpio.c
new file mode 100644
index 0000000..0c32dae
--- /dev/null
+++ b/arch/arm/mach-picoxcell/gpio.c
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#define pr_fmt(fmt) "picoxcellgpio: " fmt
+
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <mach/hardware.h>
+
+#include "mux.h"
+
+static struct {
+	void __iomem	*armgpio_virt;
+	int		armgpio_base;
+	int		sdgpio_base;
+	int		nr_sdgpio;
+} gpio_info;
+
+static DECLARE_BITMAP(pin_status, ARCH_NR_GPIOS);
+static DEFINE_SPINLOCK(armgpio_lock);
+
+static int armgpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	enum mux_setting mux;
+
+	if (test_and_set_bit(offset + chip->base, pin_status))
+		return -EBUSY;
+
+	/* Check the pin has been correctly multiplexed. */
+	mux = picoxcell_get_pin_mux(offset + chip->base);
+	if (!(mux & (MUX_ARM | MUX_UNMUXED))) {
+		/* The pin has an inconsistent mux setting. */
+		pr_warning("attempt to request armgpio%u which is not correctly multiplexed\n",
+			   chip->base + offset);
+		clear_bit(offset + chip->base, pin_status);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void armgpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	clear_bit(offset + chip->base, pin_status);
+}
+
+#define GPIO_SW_PORT_A_EXT_REG_OFFSET	GPIO_EXT_PORT_A_REG_OFFSET
+#define GPIO_SW_PORT_B_EXT_REG_OFFSET	GPIO_EXT_PORT_B_REG_OFFSET
+#define GPIO_SW_PORT_D_EXT_REG_OFFSET	GPIO_EXT_PORT_D_REG_OFFSET
+
+static inline int armgpio_block_nr(unsigned gpio_nr)
+{
+	return gpio_nr - gpio_info.armgpio_base;
+}
+
+#define __ARMGPIO_REG(_gpio_base, _reg)					    \
+	({								    \
+		void __iomem *ret = NULL;				    \
+		int __gpio_nr = armgpio_block_nr(_gpio_base);		    \
+		if (__gpio_nr < 8)					    \
+			ret = (gpio_info.armgpio_virt +			    \
+				GPIO_SW_PORT_A_##_reg##_REG_OFFSET);	    \
+		else if (__gpio_nr < 24)				    \
+			ret = (gpio_info.armgpio_virt +			    \
+				GPIO_SW_PORT_B_##_reg##_REG_OFFSET);	    \
+		else							    \
+			ret = (gpio_info.armgpio_virt +			    \
+				GPIO_SW_PORT_D_##_reg##_REG_OFFSET);	    \
+		ret;							    \
+	})
+
+#define ARMGPIO_DR(_gpio_base)	    __ARMGPIO_REG(_gpio_base, DR)
+#define ARMGPIO_DDR(_gpio_base)	    __ARMGPIO_REG(_gpio_base, DDR)
+#define ARMGPIO_CTL(_gpio_base)	    __ARMGPIO_REG(_gpio_base, CTL)
+#define ARMGPIO_EXT(_gpio_base)	    __ARMGPIO_REG(_gpio_base, EXT)
+
+static inline unsigned armgpio_offset(unsigned offset)
+{
+	/*
+	 * The arm gpios are controlled via three sets of registers. The
+	 * register addressing is already taken care of by the __ARMGPIO_REG
+	 * macro, this takes care of the bit offsets within each register.
+	 */
+	if (offset < 8) /* GPIO Port A*/
+		return offset;
+	else if (offset < 24) /* GPIO Port B */
+		return offset - 8;
+	else /* GPIO Port D */
+		return offset - 24;
+}
+
+static int armgpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	void __iomem *ddr = ARMGPIO_DDR(chip->base + offset);
+	void __iomem *cr = ARMGPIO_CTL(chip->base + offset);
+	unsigned long flags, val, bit_offset = armgpio_offset(offset);
+
+	spin_lock_irqsave(&armgpio_lock, flags);
+	/* Mark the pin as an output. */
+	val = readl(ddr);
+	val &= ~(1 << bit_offset);
+	writel(val, ddr);
+
+	/* Set the pin as software controlled. */
+	val = readl(cr);
+	val &= ~(1 << bit_offset);
+	writel(val, cr);
+	spin_unlock_irqrestore(&armgpio_lock, flags);
+
+	return 0;
+}
+
+static void armgpio_set(struct gpio_chip *chip, unsigned offset, int value);
+
+static int armgpio_direction_output(struct gpio_chip *chip, unsigned offset,
+				    int value)
+{
+	void __iomem *ddr = ARMGPIO_DDR(chip->base + offset);
+	void __iomem *cr = ARMGPIO_CTL(chip->base + offset);
+	unsigned long flags, val, bit_offset = armgpio_offset(offset);
+
+	/* Set the value first so we don't glitch. */
+	armgpio_set(chip, offset, value);
+
+	spin_lock_irqsave(&armgpio_lock, flags);
+	/* Mark the pin as an output. */
+	val = readl(ddr);
+	val |= (1 << bit_offset);
+	writel(val, ddr);
+
+	/* Set the pin as software controlled. */
+	val = readl(cr);
+	val &= ~(1 << bit_offset);
+	writel(val, cr);
+	spin_unlock_irqrestore(&armgpio_lock, flags);
+
+	return 0;
+}
+
+static int armgpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	void __iomem *ext = ARMGPIO_EXT(chip->base + offset);
+	unsigned long bit_offset = armgpio_offset(offset);
+
+	return !!(readl(ext) & (1 << bit_offset));
+}
+
+static void armgpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	void __iomem *dr = ARMGPIO_DR(chip->base + offset);
+	unsigned long val, flags, bit_offset = armgpio_offset(offset);
+
+	spin_lock_irqsave(&armgpio_lock, flags);
+	val = readl(dr);
+	val &= ~(1 << bit_offset);
+	val |= (!!value << bit_offset);
+	writel(val, dr);
+	spin_unlock_irqrestore(&armgpio_lock, flags);
+}
+
+static int armgpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset < gpio_info.armgpio_base + 8)
+		return IRQ_GPIO0 + offset;
+	return -EINVAL;
+}
+
+#define INT_EN_REG	    (gpio_info.armgpio_virt + GPIO_INT_EN_REG_OFFSET)
+#define INT_MASK_REG	    (gpio_info.armgpio_virt + GPIO_INT_MASK_REG_OFFSET)
+#define INT_TYPE_REG	    (gpio_info.armgpio_virt + \
+			     GPIO_INT_TYPE_LEVEL_REG_OFFSET)
+#define INT_POLARITY_REG    (gpio_info.armgpio_virt + \
+			     GPIO_INT_POLARITY_REG_OFFSET)
+#define INT_STATUS_REG	    (gpio_info.armgpio_virt + \
+			     GPIO_INT_STATUS_REG_OFFSET)
+#define EOI_REG		    (gpio_info.armgpio_virt + \
+			     GPIO_PORT_A_EOI_REG_OFFSET)
+
+static void armgpio_irq_enable(unsigned int irq)
+{
+	int gpio = irq_to_gpio(irq);
+	void __iomem *port_inten = INT_EN_REG;
+	unsigned long val;
+
+	val = readl(port_inten);
+	val |= (1 << gpio);
+	writel(val, port_inten);
+}
+
+static void armgpio_irq_disable(unsigned int irq)
+{
+	int gpio = irq_to_gpio(irq);
+	void __iomem *port_inten = INT_EN_REG;
+	unsigned long val;
+
+	val = readl(port_inten);
+	val &= ~(1 << gpio);
+	writel(val, port_inten);
+}
+
+static void armgpio_irq_mask(unsigned int irq)
+{
+	int gpio = irq_to_gpio(irq);
+	void __iomem *port_mask = INT_MASK_REG;
+	unsigned long val;
+
+	val = readl(port_mask);
+	val |= (1 << gpio);
+	writel(val, port_mask);
+}
+
+static void armgpio_irq_ack(unsigned int irq)
+{
+	int gpio = irq_to_gpio(irq);
+	void __iomem *port_intmask = INT_MASK_REG;
+	void __iomem *port_eoi = EOI_REG;
+	void __iomem *port_inttype_level = INT_TYPE_REG;
+	unsigned long val;
+
+	val = readl(port_inttype_level);
+
+	if (val & (1 << gpio)) {
+		/* Edge-sensitive */
+		val = readl(port_eoi);
+		val |= (1 << gpio);
+		writel(val, port_eoi);
+	} else {
+		/* Level-sensitive */
+		val = readl(port_intmask);
+		val |= (1 << gpio);
+		writel(val, port_intmask);
+	}
+}
+
+static void armgpio_irq_unmask(unsigned int irq)
+{
+	int gpio = irq_to_gpio(irq);
+	void __iomem *port_intmask = INT_MASK_REG;
+	unsigned long val;
+
+	val = readl(port_intmask);
+	val &= ~(1 << gpio);
+	writel(val, port_intmask);
+}
+
+static int armgpio_irq_set_type(unsigned int irq, unsigned int trigger)
+{
+	int gpio = irq_to_gpio(irq);
+	void __iomem *port_inttype_level = INT_TYPE_REG;
+	void __iomem *port_int_polarity = INT_POLARITY_REG;
+	unsigned long level, polarity;
+	struct irq_desc *desc = irq_desc + irq;
+
+	if (trigger & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
+			IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+		return -EINVAL;
+
+	level = readl(port_inttype_level);
+	polarity = readl(port_int_polarity);
+
+	if (trigger & IRQ_TYPE_EDGE_RISING) {
+		level	    |= (1 << gpio);
+		polarity    |= (1 << gpio);
+	} else if (trigger & IRQ_TYPE_EDGE_FALLING) {
+		level	    |= (1 << gpio);
+		polarity    &= ~(1 << gpio);
+	} else if (trigger & IRQ_TYPE_LEVEL_HIGH) {
+		level	    &= ~(1 << gpio);
+		polarity    |= (1 << gpio);
+	} else if (trigger & IRQ_TYPE_LEVEL_LOW) {
+		level	    &= ~(1 << gpio);
+		polarity    &= ~(1 << gpio);
+	}
+
+	writel(level, port_inttype_level);
+	writel(polarity, port_int_polarity);
+
+	if ((trigger & IRQ_TYPE_EDGE_RISING) ||
+	    (trigger & IRQ_TYPE_EDGE_RISING))
+		desc->handle_irq = handle_edge_irq;
+	else
+		desc->handle_irq = handle_level_irq;
+
+	desc->status = (desc->status & ~IRQ_TYPE_SENSE_MASK) | trigger;
+
+	return 0;
+}
+
+struct irq_chip armgpio_irqchip = {
+	.name	  = "armgpio",
+	.ack	  = armgpio_irq_ack,
+	.mask	  = armgpio_irq_mask,
+	.unmask   = armgpio_irq_unmask,
+	.enable   = armgpio_irq_enable,
+	.disable  = armgpio_irq_disable,
+	.set_type = armgpio_irq_set_type,
+};
+
+static struct gpio_chip armgpio_chip = {
+	.owner		    = THIS_MODULE,
+	.label		    = "armgpio",
+	.request	    = armgpio_request,
+	.free		    = armgpio_free,
+	.direction_input    = armgpio_direction_input,
+	.direction_output   = armgpio_direction_output,
+	.get		    = armgpio_get,
+	.set		    = armgpio_set,
+	.to_irq		    = armgpio_to_irq,
+};
+
+/* The base address of SD-GPIO config registers in the AXI2Pico. */
+#define PC3X2_GPIO_SD_PIN_CONFIG_BASE		0x9800
+/* The base address of SD-GPIO analogue value registers in the AXI2Pico. */
+#define PC3X2_GPIO_SD_PIN_ANALOGUE_VALUE_BASE	0x9801
+/* The base address of SD-GPIO analogue rate registers in the AXI2Pico. */
+#define PC3X2_GPIO_SD_PIN_ANALOGUE_RATE_BASE	0x9802
+/* The address of the control value register in the AXI2Pico. */
+#define PC3X2_GPIO_SD_CONTROL_VAL_REG		0x9882
+/* The address of the control value high register in the AXI2Pico (pc3x3). */
+#define PC3X2_GPIO_SD_CONTROL_VAL_HI_REG	0x9883
+/* The address of the output value register in the AXI2Pico. */
+#define PC3X2_GPIO_SD_OUTPUT_VAL_REG		0x9884
+/* The address of the output value high register in the AXI2Pico (pc3x3). */
+#define PC3X2_GPIO_SD_OUTPUT_HI_VAL_REG		0x9885
+/* The address of the input value register in the AXI2Pico. */
+#define PC3X2_GPIO_SD_INPUT_VAL_REG		0x9880
+/* The address of the input value high register in the AXI2Pico (pc3x3). */
+#define PC3X2_GPIO_SD_INPUT_VAL_HI_REG		0x9880
+/* The address of the sleep register in the AXI2Pico. */
+#define PICOXCELL_AXI2PICO_SLEEP_REG		0xA060
+/* The spacing between SD-GPIO config registers. */
+#define PC3X2_GPIO_SD_PIN_CONFIG_SPACING	4
+/* Control source bit. */
+#define PC3X2_GPIO_SD_CONFIG_CS_MASK		(~(1 << 15))
+/* Analogue not digital bit. */
+#define PC3X2_GPIO_SD_CONFIG_AND		(1 << 14)
+/* The mask for analogue converter size in the config register. */
+#define PC3X2_GPIO_SD_CONV_SZ_MASK		0xF
+/* Soft reset lock bit. */
+#define PC3X2_GPIO_SD_CONFIG_SR_LOCK		(1 << 13)
+/* PICOXCELL AXI2Pico CAEID. */
+#define PICOXCELL_AXI2PICO_CAEID		0x9000
+
+/*
+ * Get the address of a config register for a SD-GPIO pin.
+ *
+ * @_n The SD-GPIO pin number.
+ *
+ * Returns the base address of the register.
+ */
+#define PC3X2_GPIO_SD_PIN_CONFIG(_n) \
+	(PC3X2_GPIO_SD_PIN_CONFIG_BASE + \
+	 ((_n) * PC3X2_GPIO_SD_PIN_CONFIG_SPACING))
+
+/*
+ * Get the address of a analogue rate register for a SD-GPIO pin.
+ *
+ * @_n The SD-GPIO pin number.
+ *
+ * Returns the base address of the register.
+ */
+#define PC3X2_GPIO_SD_PIN_ANALOGUE_RATE(_n) \
+	(PC3X2_GPIO_SD_PIN_ANALOGUE_RATE_BASE + \
+	 ((_n) * PC3X2_GPIO_SD_PIN_CONFIG_SPACING))
+
+/*
+ * Get the address of a analogue value register for a SD-GPIO pin.
+ *
+ * @_n The SD-GPIO pin number.
+ *
+ * Returns the base address of the register.
+ */
+#define PC3X2_GPIO_SD_PIN_ANALOGUE_VAL(_n) \
+	(PC3X2_GPIO_SD_PIN_ANALOGUE_VALUE_BASE + \
+	 ((_n) * PC3X2_GPIO_SD_PIN_CONFIG_SPACING))
+
+static int sdgpio_reset_config(unsigned block_pin, int value)
+{
+	int ret;
+	u16 data;
+
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+				  PC3X2_GPIO_SD_PIN_CONFIG(block_pin),
+				  &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read config register for SDGPIO pin %u\n",
+		       block_pin);
+		return -EIO;
+	}
+
+	if (value)
+		data |= PC3X2_GPIO_SD_CONFIG_SR_LOCK;
+	else
+		data &= ~PC3X2_GPIO_SD_CONFIG_SR_LOCK;
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_PIN_CONFIG(block_pin),
+			     &data, 1);
+
+	return 0;
+}
+
+static inline int sdgpio_block_nr(unsigned gpio_nr)
+{
+	return gpio_nr - gpio_info.sdgpio_base;
+}
+
+static int sdgpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+	enum mux_setting mux;
+
+	if (test_and_set_bit(offset + chip->base, pin_status))
+		return -EBUSY;
+
+	if (sdgpio_reset_config(block_pin, 1)) {
+		clear_bit(offset + chip->base, pin_status);
+		return -EIO;
+	}
+
+	/* Check the pin has been correctly multiplexed. */
+	mux = picoxcell_get_pin_mux(offset + chip->base);
+	if (!(mux & (MUX_SD | MUX_UNMUXED))) {
+		/* The pin has an inconsistent mux setting. */
+		pr_warning("attempt to request sdgpio%u which is not correctly multiplexed\n",
+			   block_pin);
+		clear_bit(offset + chip->base, pin_status);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void sdgpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	clear_bit(offset + chip->base, pin_status);
+	picoxcell_gpio_configure_dac(chip->base + offset, 0, 0);
+}
+
+/*
+ * Create a map of which pins are analogue and not digital. We have a separate
+ * function for configuring pins as analogue. When we set analogue pins, we
+ * don't treat the int parameter as a boolean anymore.
+ */
+static DECLARE_BITMAP(a_not_d_map, ARCH_NR_GPIOS);
+
+static int sdgpio_get_digital_out_status(u32 *v)
+{
+	u16 data[2] = { 0, 0 };
+
+	if (1 != axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+				PC3X2_GPIO_SD_OUTPUT_VAL_REG, &data[0], 1))
+		return -EIO;
+
+	if (gpio_info.nr_sdgpio > 16) {
+		if (1 != axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+					PC3X2_GPIO_SD_OUTPUT_HI_VAL_REG,
+					&data[1], 1))
+			return -EIO;
+	}
+
+	*v = data[0] | (data[1] << 16);
+
+	return 0;
+}
+
+static void sdgpio_set_digital_out_status(u32 v)
+{
+	u16 data[2] = { (u16)(v & 0xFFFF), (u16)((v >> 16) & 0xFFFF) };
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_OUTPUT_VAL_REG, &data[0], 1);
+
+	if (gpio_info.nr_sdgpio > 16) {
+		axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+				     PC3X2_GPIO_SD_OUTPUT_HI_VAL_REG,
+				     &data[1], 1);
+	}
+}
+
+static void sdgpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	u16 data;
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+
+	if (!test_bit(chip->base + offset, a_not_d_map)) {
+		u32 status;
+
+		if (sdgpio_get_digital_out_status(&status)) {
+			pr_err("failed to read SDGPIO output value reg\n");
+			return;
+		}
+
+		status &= ~(1 << block_pin);
+		status |= (!!value) << block_pin;
+
+		sdgpio_set_digital_out_status(status);
+	} else {
+		/* Analogue mode */
+		data = (u16)value;
+		axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+				     PC3X2_GPIO_SD_PIN_ANALOGUE_VAL(block_pin),
+				     &data, 1);
+	}
+}
+
+static int sdgpio_get_digital_in_status(u32 *v)
+{
+	u16 data[2] = { 0, 0 };
+
+	if (1 != axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+				PC3X2_GPIO_SD_INPUT_VAL_REG, &data[0], 1))
+		return -EIO;
+
+	if (gpio_info.nr_sdgpio > 16) {
+		if (1 != axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+					PC3X2_GPIO_SD_INPUT_VAL_HI_REG,
+					&data[1], 1))
+			return -EIO;
+	}
+
+	*v = data[0] | (data[1] << 16);
+
+	return 0;
+}
+
+static int sdgpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	int ret;
+	u16 data;
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+
+	if (!test_bit(chip->base + offset, a_not_d_map)) {
+		u32 status;
+
+		if (sdgpio_get_digital_in_status(&status))
+			return -EIO;
+
+		return !!(status & (1 << block_pin));
+	} else {
+		/* Analogue mode */
+		ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+				PC3X2_GPIO_SD_PIN_ANALOGUE_VAL(block_pin),
+				&data, 1);
+		if (1 != ret) {
+			pr_err("failed to read the analogue value register for SDGPIO pin %u\n",
+			       block_pin);
+			return -EIO;
+		}
+
+		return (int)data;
+	}
+}
+
+static int sdgpio_set_direction(unsigned block_pin, int input)
+{
+	int ret;
+	u16 data;
+
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+			PC3X2_GPIO_SD_PIN_CONFIG(block_pin), &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read config register for SDGPIO pin %u\n",
+		       block_pin);
+		return -EIO;
+	}
+
+	data &= PC3X2_GPIO_SD_CONFIG_CS_MASK;
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_PIN_CONFIG(block_pin), &data, 1);
+
+	/* Configure the pin to drive or not drive the output as appropriate. */
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+			PC3X2_GPIO_SD_CONTROL_VAL_REG, &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read SDGPIO control value register\n");
+		return -EIO;
+	}
+
+	if (input)
+		data &= ~(1 << block_pin);
+	else
+		data |= (1 << block_pin);
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_CONTROL_VAL_REG, &data, 1);
+
+	return 0;
+}
+
+static int sdgpio_direction_output(struct gpio_chip *chip, unsigned offset,
+				   int value)
+{
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+	int ret = sdgpio_set_direction(block_pin, 0);
+
+	if (ret)
+		return ret;
+
+	sdgpio_set(chip, offset, value);
+
+	return 0;
+}
+
+static int sdgpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+
+	return sdgpio_set_direction(block_pin, 1);
+}
+
+int picoxcell_gpio_configure_dac(unsigned gpio, u8 converter_size,
+				 u16 analogue_rate)
+{
+	int ret;
+	u16 data;
+	unsigned block_pin = sdgpio_block_nr(gpio);
+
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+			PC3X2_GPIO_SD_PIN_CONFIG(block_pin), &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read config register for SDGPIO pin %u\n",
+		       block_pin);
+		return -EIO;
+	}
+
+	data &= PC3X2_GPIO_SD_CONFIG_CS_MASK;
+	data &= ~PC3X2_GPIO_SD_CONV_SZ_MASK;
+	if (!analogue_rate && !converter_size)
+		data &= ~PC3X2_GPIO_SD_CONFIG_AND;
+	else
+		data |= PC3X2_GPIO_SD_CONFIG_AND;
+	data |= (converter_size & PC3X2_GPIO_SD_CONV_SZ_MASK);
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_PIN_CONFIG(block_pin), &data, 1);
+
+	/* Configure the pin to drive the output. */
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+			PC3X2_GPIO_SD_CONTROL_VAL_REG, &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read SDGPIO control value register\n");
+		return -EIO;
+	}
+
+	data |= (1 << block_pin);
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_CONTROL_VAL_REG, &data, 1);
+
+	/* Write the analogue rate register */
+	data = analogue_rate;
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_PIN_ANALOGUE_RATE(block_pin),
+			     &data, 1);
+
+	if (analogue_rate || converter_size)
+		set_bit(gpio, a_not_d_map);
+	else
+		clear_bit(gpio, a_not_d_map);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(picoxcell_gpio_configure_dac);
+
+static struct gpio_chip sdgpio_chip = {
+	.owner		    = THIS_MODULE,
+	.label		    = "sdgpio",
+	.request	    = sdgpio_request,
+	.free		    = sdgpio_free,
+	.direction_input    = sdgpio_direction_input,
+	.direction_output   = sdgpio_direction_output,
+	.get		    = sdgpio_get,
+	.set		    = sdgpio_set,
+};
+
+int __init picoxcell_gpio_init(const char * const armgpio_pins[],
+			       int nr_armgpio, int armgpio_base,
+			       const char * const sdgpio_pins[],
+			       int nr_sdgpio, int sdgpio_base)
+{
+	int ret = 0;
+	u16 data = 0;
+
+	/*
+	 * Make sure that the AXI2Pico is awake for the SDGPIO transactions.
+	 */
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PICOXCELL_AXI2PICO_SLEEP_REG, &data, 1);
+
+	gpio_info.armgpio_virt	= ioremap(PICOXCELL_GPIO_BASE, SZ_4K);
+	gpio_info.armgpio_base	= armgpio_base;
+	gpio_info.sdgpio_base	= sdgpio_base;
+	gpio_info.nr_sdgpio	= nr_sdgpio;
+
+	armgpio_chip.ngpio	= nr_armgpio;
+	armgpio_chip.names	= armgpio_pins;
+	armgpio_chip.base	= armgpio_base;
+	sdgpio_chip.ngpio	= nr_sdgpio;
+	sdgpio_chip.names	= sdgpio_pins;
+	sdgpio_chip.base	= sdgpio_base;
+
+	ret = gpiochip_add(&armgpio_chip);
+	if (ret) {
+		pr_err("failed to register %s\n", armgpio_chip.label);
+		goto out;
+	}
+
+	ret = gpiochip_add(&sdgpio_chip);
+	if (ret) {
+		pr_err("failed to register %s\n", sdgpio_chip.label);
+		goto out;
+	}
+
+out:
+	return ret;
+}
diff --git a/arch/arm/mach-picoxcell/include/mach/gpio.h b/arch/arm/mach-picoxcell/include/mach/gpio.h
new file mode 100644
index 0000000..ce55c3d
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/gpio.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 __MACH_GPIO_H__
+#define __MACH_GPIO_H__
+
+#ifdef __KERNEL__
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm-generic/gpio.h>
+
+int __init picoxcell_gpio_init(const char * const armgpio_pins[],
+			       int nr_armgpio, int armgpio_base,
+			       const char * const sdgpio_pins[],
+			       int nr_sdgpio, int sdgpio_base);
+
+extern struct irq_chip armgpio_irqchip;
+
+#endif /* __KERNEL__ */
+
+enum {
+	PC3X2_GPIO_PIN_ARM_0,
+	PC3X2_GPIO_PIN_ARM_1,
+	PC3X2_GPIO_PIN_ARM_2,
+	PC3X2_GPIO_PIN_ARM_3,
+	PC3X2_GPIO_PIN_ARM_4,
+	PC3X2_GPIO_PIN_ARM_5,
+	PC3X2_GPIO_PIN_ARM_6,
+	PC3X2_GPIO_PIN_ARM_7,
+	PC3X2_GPIO_PIN_ARM_8,
+	PC3X2_GPIO_PIN_ARM_9,
+	PC3X2_GPIO_PIN_ARM_10,
+	PC3X2_GPIO_PIN_ARM_11,
+	PC3X2_GPIO_PIN_ARM_12,
+	PC3X2_GPIO_PIN_ARM_13,
+	PC3X2_GPIO_PIN_ARM_14,
+	PC3X2_GPIO_PIN_ARM_15,
+	PC3X2_GPIO_PIN_SDGPIO_0,
+	PC3X2_GPIO_PIN_SDGPIO_1,
+	PC3X2_GPIO_PIN_SDGPIO_2,
+	PC3X2_GPIO_PIN_SDGPIO_3,
+	PC3X2_GPIO_PIN_SDGPIO_4,
+	PC3X2_GPIO_PIN_SDGPIO_5,
+	PC3X2_GPIO_PIN_SDGPIO_6,
+	PC3X2_GPIO_PIN_SDGPIO_7,
+	PC3X2_GPIO_PIN_SDGPIO_8,
+	PC3X2_GPIO_PIN_SDGPIO_9,
+	PC3X2_GPIO_PIN_SDGPIO_10,
+	PC3X2_GPIO_PIN_SDGPIO_11,
+	PC3X2_GPIO_PIN_SDGPIO_12,
+	PC3X2_GPIO_PIN_SDGPIO_13,
+	PC3X2_GPIO_PIN_SDGPIO_14,
+	PC3X2_GPIO_PIN_SDGPIO_15,
+};
+
+enum {
+	PC3X3_GPIO_PIN_ARM_0,
+	PC3X3_GPIO_PIN_ARM_1,
+	PC3X3_GPIO_PIN_ARM_2,
+	PC3X3_GPIO_PIN_ARM_3,
+	PC3X3_GPIO_PIN_ARM_4,
+	PC3X3_GPIO_PIN_ARM_5,
+	PC3X3_GPIO_PIN_ARM_6,
+	PC3X3_GPIO_PIN_ARM_7,
+	PC3X3_GPIO_PIN_ARM_8,
+	PC3X3_GPIO_PIN_ARM_9,
+	PC3X3_GPIO_PIN_ARM_10,
+	PC3X3_GPIO_PIN_ARM_11,
+	PC3X3_GPIO_PIN_ARM_12,
+	PC3X3_GPIO_PIN_ARM_13,
+	PC3X3_GPIO_PIN_ARM_14,
+	PC3X3_GPIO_PIN_ARM_15,
+	PC3X3_GPIO_PIN_ARM_16,
+	PC3X3_GPIO_PIN_ARM_17,
+	PC3X3_GPIO_PIN_ARM_18,
+	PC3X3_GPIO_PIN_ARM_19,
+	PC3X3_GPIO_PIN_ARM_20,
+	PC3X3_GPIO_PIN_ARM_21,
+	PC3X3_GPIO_PIN_ARM_22,
+	PC3X3_GPIO_PIN_ARM_23,
+	PC3X3_GPIO_PIN_ARM_24,
+	PC3X3_GPIO_PIN_ARM_25,
+	PC3X3_GPIO_PIN_ARM_26,
+	PC3X3_GPIO_PIN_ARM_27,
+	PC3X3_GPIO_PIN_ARM_28,
+	PC3X3_GPIO_PIN_ARM_29,
+	PC3X3_GPIO_PIN_ARM_30,
+	PC3X3_GPIO_PIN_ARM_31,
+	PC3X3_GPIO_PIN_ARM_32,
+	PC3X3_GPIO_PIN_ARM_33,
+	PC3X3_GPIO_PIN_ARM_34,
+	PC3X3_GPIO_PIN_ARM_35,
+	PC3X3_GPIO_PIN_ARM_36,
+	PC3X3_GPIO_PIN_ARM_37,
+	PC3X3_GPIO_PIN_ARM_38,
+	PC3X3_GPIO_PIN_ARM_39,
+	PC3X3_GPIO_PIN_ARM_40,
+	PC3X3_GPIO_PIN_ARM_41,
+	PC3X3_GPIO_PIN_ARM_42,
+	PC3X3_GPIO_PIN_ARM_43,
+	PC3X3_GPIO_PIN_ARM_44,
+	PC3X3_GPIO_PIN_ARM_45,
+	PC3X3_GPIO_PIN_ARM_46,
+	PC3X3_GPIO_PIN_ARM_47,
+	PC3X3_GPIO_PIN_ARM_48,
+	PC3X3_GPIO_PIN_ARM_49,
+	PC3X3_GPIO_PIN_ARM_50,
+	PC3X3_GPIO_PIN_ARM_51,
+	PC3X3_GPIO_PIN_ARM_52,
+	PC3X3_GPIO_PIN_ARM_53,
+	PC3X3_GPIO_PIN_SDGPIO_0,
+	PC3X3_GPIO_PIN_SDGPIO_1,
+	PC3X3_GPIO_PIN_SDGPIO_2,
+	PC3X3_GPIO_PIN_SDGPIO_3,
+	PC3X3_GPIO_PIN_SDGPIO_4,
+	PC3X3_GPIO_PIN_SDGPIO_5,
+	PC3X3_GPIO_PIN_SDGPIO_6,
+	PC3X3_GPIO_PIN_SDGPIO_7,
+	PC3X3_GPIO_PIN_SDGPIO_8,
+	PC3X3_GPIO_PIN_SDGPIO_9,
+	PC3X3_GPIO_PIN_SDGPIO_10,
+	PC3X3_GPIO_PIN_SDGPIO_11,
+	PC3X3_GPIO_PIN_SDGPIO_12,
+	PC3X3_GPIO_PIN_SDGPIO_13,
+	PC3X3_GPIO_PIN_SDGPIO_14,
+	PC3X3_GPIO_PIN_SDGPIO_15,
+	PC3X3_GPIO_PIN_SDGPIO_16,
+	PC3X3_GPIO_PIN_SDGPIO_17,
+	PC3X3_GPIO_PIN_SDGPIO_18,
+	PC3X3_GPIO_PIN_SDGPIO_19,
+	PC3X3_GPIO_PIN_SDGPIO_20,
+	PC3X3_GPIO_PIN_SDGPIO_21,
+	PC3X3_GPIO_PIN_SDGPIO_22,
+	PC3X3_GPIO_PIN_SDGPIO_23,
+};
+
+#ifdef __KERNEL__
+
+static inline int gpio_get_value(unsigned gpio)
+{
+	return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned gpio, int value)
+{
+	__gpio_set_value(gpio, value);
+}
+
+static inline int gpio_cansleep(unsigned gpio)
+{
+	return 0;
+}
+
+static inline int gpio_to_irq(unsigned gpio)
+{
+	return __gpio_to_irq(gpio);
+}
+
+static inline int irq_to_gpio(unsigned irq)
+{
+	if (irq > IRQ_GPIO7)
+		return -EINVAL;
+	return irq - IRQ_GPIO0;
+}
+
+extern int picoxcell_gpio_configure_dac(unsigned gpio, u8 converter_size,
+					u16 analogue_rate);
+
+#endif /* __KERNEL__ */
+
+#endif /* __MACH_GPIO_H__ */
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 8f2cc46..fe2a147 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -8,6 +8,7 @@
  * All enquiries to support at picochip.com
  */
 #include <linux/debugfs.h>
+#include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/irq.h>
@@ -286,6 +287,10 @@ void __init picoxcell_core_init(void)
 	/* Initialise the pin muxing and gpio infrastructure. */
 	picoxcell_muxing_init(soc);
 
+	picoxcell_gpio_init(soc->armgpio_pins, soc->nr_armgpio,
+			    soc->armgpio_base, soc->sdgpio_pins,
+			    soc->nr_sdgpio, soc->sdgpio_base);
+
 	/* Add handlers for the AXI bus snooping. */
 	picoxcell_axi_bus_error_init();
 }
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 06/13] picoxcell: add support for system clks
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (4 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 05/13] picoxcell: add gpio infrastructure Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-12-08 16:18   ` Russell King - ARM Linux
  2010-11-23 10:06 ` [RFC PATCH 07/13] picoxcell: add common SoC devices Jamie Iles
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

The different picoXcell device variants have different numbers of clocks
and different capabilities (PC3X2 doesn't have any gateable or
controllable clocks). Add the infrastructure necessary for device
variants to provide their own clock implementation.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile              |    3 +-
 arch/arm/mach-picoxcell/clk.c                 |  140 +++++++++++++++++++++++++
 arch/arm/mach-picoxcell/include/mach/clkdev.h |   69 ++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.c      |    4 +
 4 files changed, 215 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index f6b3765..8d8b4e8 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -1,4 +1,5 @@
 obj-y				:= picoxcell_core.o axi2cfg.o \
 				   time.o \
 				   mux.o \
-				   gpio.o
+				   gpio.o \
+				   clk.o
diff --git a/arch/arm/mach-picoxcell/clk.c b/arch/arm/mach-picoxcell/clk.c
new file mode 100644
index 0000000..8bae6df
--- /dev/null
+++ b/arch/arm/mach-picoxcell/clk.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+
+#include <mach/clkdev.h>
+
+static DEFINE_SPINLOCK(clk_lock);
+static LIST_HEAD(picoxcell_clks);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+	return clk->get_rate ? clk->get_rate(clk) : clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+	return clk->round_rate ? clk->round_rate(clk, rate) : -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	return clk->set_rate ? clk->set_rate(clk, rate) : 0;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+int __clk_enable(struct clk *clk)
+{
+	if (++clk->enable_count > 0) {
+		if (clk->enable)
+			clk->enable(clk);
+	}
+
+	return 0;
+}
+
+int clk_enable(struct clk *clk)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&clk_lock, flags);
+	ret = __clk_enable(clk);
+	spin_unlock_irqrestore(&clk_lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void __clk_disable(struct clk *clk)
+{
+	if (--clk->enable_count <= 0) {
+		if (clk->disable)
+			clk->disable(clk);
+	}
+}
+
+void clk_disable(struct clk *clk)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&clk_lock, flags);
+	__clk_disable(clk);
+	spin_unlock_irqrestore(&clk_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+static int clk_is_enabled(struct clk *clk)
+{
+	return clk->is_enabled ? clk->is_enabled(clk) : 1;
+}
+
+static ssize_t clocks_show(struct seq_file *s, void *priv)
+{
+	struct clk *clk;
+	ssize_t ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&clk_lock, flags);
+
+	ret += seq_printf(s, "%16s%16s%16s\n\n", "name", "enabled", "rate");
+	list_for_each_entry(clk, &picoxcell_clks, head) {
+		ret += seq_printf(s, "%16s%16s%16d\n",
+				  clk->cl_lookup->dev_id ?:
+					clk->cl_lookup->con_id,
+				  clk_is_enabled(clk) ? "enabled" : "disabled",
+				  clk->rate >= 0 ? clk->rate :
+					clk->get_rate(clk) * 1000);
+	}
+
+	spin_unlock_irqrestore(&clk_lock, flags);
+
+	return ret;
+}
+
+static int clocks_debugfs_open(struct inode *inode, struct file *filp)
+{
+	return single_open(filp, clocks_show, inode->i_private);
+}
+
+static const struct file_operations clocks_debugfs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= clocks_debugfs_open,
+	.llseek		= seq_lseek,
+	.read		= seq_read,
+	.release	= single_release,
+};
+
+void __init picoxcell_clk_debugfs_init(void)
+{
+	/* We only get called if debugfs is enabled and configured. */
+	struct dentry *clocks_debugfs_file =
+		debugfs_create_file("clocks", 0444, arch_debugfs_dir, NULL,
+				    &clocks_debugfs_fops);
+	if (IS_ERR(clocks_debugfs_file)) {
+		pr_err("failed to create clocks debugfs entry (%ld)\n",
+		       PTR_ERR(clocks_debugfs_file));
+	}
+}
+
+void picoxcell_clk_add(struct clk_lookup *lookup)
+{
+	INIT_LIST_HEAD(&lookup->node);
+	clkdev_add(lookup);
+	lookup->clk->cl_lookup = lookup;
+	list_add_tail(&lookup->clk->head, &picoxcell_clks);
+}
diff --git a/arch/arm/mach-picoxcell/include/mach/clkdev.h b/arch/arm/mach-picoxcell/include/mach/clkdev.h
new file mode 100644
index 0000000..f2e5fb0
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/clkdev.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#ifndef __PICOXCELL_CLKDEV_H__
+#define __PICOXCELL_CLKDEV_H__
+
+#include <asm/clkdev.h>
+
+struct clk {
+	struct list_head    head;
+	struct clk_lookup   *cl_lookup;
+	int		    rate;
+	unsigned	    min, max, step; /* min, max and frequency steps for
+					       variable rate clocks in KHz. */
+	int		    enable_count;
+	int		    clk_num;
+	void		    (*enable)(struct clk *clk);
+	void		    (*disable)(struct clk *clk);
+	int		    (*is_enabled)(struct clk *clk);
+	long		    (*round_rate)(struct clk *clk, unsigned long rate);
+	int		    (*set_rate)(struct clk *clk, unsigned long rate);
+	int		    (*get_rate)(struct clk *clk);
+};
+
+static inline int __clk_get(struct clk *clk)
+{
+	return 1;
+}
+
+static inline void __clk_put(struct clk *clk)
+{
+}
+
+extern void __init picoxcell_clk_debugfs_init(void);
+extern void picoxcell_clk_add(struct clk_lookup *lookup);
+extern int __clk_enable(struct clk *clk);
+extern void __clk_disable(struct clk *clk);
+
+/*
+ * Declare a new clock with a given rate and ID. All clocks are enabled by
+ * default.
+ */
+#define FIXED_CLK(__name, __rate, __id)					\
+	static struct clk __name ## _clk = {				\
+		.rate		= __rate,				\
+		.clk_num	= __id,					\
+		.enable_count	= 1,					\
+	}
+
+#define VARIABLE_CLK(__name, __id, __min, __max, __step)		\
+	static struct clk __name ## _clk = {				\
+		.clk_num	= __id,					\
+		.enable_count	= 1,					\
+		.rate		= -1,					\
+		.min		= __min,				\
+		.max		= __max,				\
+		.step		= __step,				\
+	}
+
+#define CLK_LOOKUP(__dev_id, __con_id, __clk) \
+	{ .dev_id = __dev_id, .con_id = __con_id, .clk = __clk }
+
+#endif /* __PICOXCELL_CLKDEV_H__ */
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index fe2a147..450b3fad 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -18,6 +18,7 @@
 #include <asm/mach/map.h>
 #include <asm/mach-types.h>
 
+#include <mach/clkdev.h>
 #include <mach/hardware.h>
 
 #include "mux.h"
@@ -152,6 +153,7 @@ static void __init picoxcell_debugfs_init(void)
 		arch_debugfs_dir = NULL;
 		return;
 	}
+	picoxcell_clk_debugfs_init();
 }
 
 static struct map_desc __initdata picoxcell_io_desc[] = {
@@ -284,6 +286,8 @@ void __init picoxcell_core_init(void)
 	/* Add the arch debugfs entry. */
 	picoxcell_debugfs_init();
 
+	soc->init_clocks();
+
 	/* Initialise the pin muxing and gpio infrastructure. */
 	picoxcell_muxing_init(soc);
 
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 07/13] picoxcell: add common SoC devices
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (5 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 06/13] picoxcell: add support for system clks Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-26 11:13   ` [RFC PATCHv2] " Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 08/13] picoxcell: add cpufreq support Jamie Iles
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

Add the devices common to all picoXcell variants (UART and PMU). Other
peripherals such as DMA, SPI, fuses and EMAC will be added later
with driver support.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile         |    3 +-
 arch/arm/mach-picoxcell/devices.c        |   77 ++++++++++++++++++++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.c |    3 +
 arch/arm/mach-picoxcell/picoxcell_core.h |    1 +
 4 files changed, 83 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 8d8b4e8..371698f 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -2,4 +2,5 @@ obj-y				:= picoxcell_core.o axi2cfg.o \
 				   time.o \
 				   mux.o \
 				   gpio.o \
-				   clk.o
+				   clk.o \
+				   devices.o
diff --git a/arch/arm/mach-picoxcell/devices.c b/arch/arm/mach-picoxcell/devices.c
new file mode 100644
index 0000000..6368b92
--- /dev/null
+++ b/arch/arm/mach-picoxcell/devices.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+#include <asm/pmu.h>
+
+#include "picoxcell_core.h"
+
+#define UART_USR_REG_OFFSET			0x7C
+static struct plat_serial8250_port serial_platform_data[] = {
+	{
+		.membase	= iomem_ptr(PICOXCELL_UART1_BASE),
+		.mapbase	= PICOXCELL_UART1_BASE,
+		.irq		= IRQ_UART1,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.iotype		= UPIO_DWAPB32,
+		.regshift	= 2,
+		.uartclk	= PICOXCELL_BASE_BAUD,
+		.private_data	= (void *)(PHYS_TO_IO(PICOXCELL_UART1_BASE +
+						      UART_USR_REG_OFFSET)),
+	},
+	{
+		.membase	= iomem_ptr(PICOXCELL_UART2_BASE),
+		.mapbase	= PICOXCELL_UART2_BASE,
+		.irq		= IRQ_UART2,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.iotype		= UPIO_DWAPB32,
+		.regshift	= 2,
+		.uartclk	= PICOXCELL_BASE_BAUD,
+		.private_data	= (void *)(PHYS_TO_IO(PICOXCELL_UART2_BASE +
+						      UART_USR_REG_OFFSET)),
+	},
+	{},
+};
+
+static struct platform_device serial_device = {
+	.name			= "serial8250",
+	.id			= 0,
+	.dev.platform_data	= serial_platform_data,
+};
+
+static struct resource pmu_resource = {
+	.start			= IRQ_NPMUIRQ,
+	.end			= IRQ_NPMUIRQ,
+	.flags			= IORESOURCE_IRQ,
+};
+
+static struct platform_device pmu_device = {
+	.name			= "arm-pmu",
+	.id			= ARM_PMU_DEVICE_CPU,
+	.num_resources		= 1,
+	.resource		= &pmu_resource,
+};
+
+static struct __initdata platform_device *common_devices[] = {
+	&serial_device,
+	&pmu_device,
+};
+
+void __init picoxcell_add_devices(void)
+{
+	WARN_ON(platform_add_devices(common_devices,
+				     ARRAY_SIZE(common_devices)));
+}
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 450b3fad..7c51e0f 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -297,4 +297,7 @@ void __init picoxcell_core_init(void)
 
 	/* Add handlers for the AXI bus snooping. */
 	picoxcell_axi_bus_error_init();
+
+	/* Register the devices. */
+	picoxcell_add_devices();
 }
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.h b/arch/arm/mach-picoxcell/picoxcell_core.h
index 941d1a6..9647b7a 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.h
+++ b/arch/arm/mach-picoxcell/picoxcell_core.h
@@ -19,5 +19,6 @@ extern void __init picoxcell_core_init(void);
 extern void __init picoxcell_init_irq(void);
 extern void __init picoxcell_map_io(void);
 extern struct sys_timer picoxcell_sys_timer;
+extern void __init picoxcell_add_devices(void);
 
 #endif /* __ASM_ARCH_PICOXCELL_CORE_H__ */
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 08/13] picoxcell: add cpufreq support
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (6 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 07/13] picoxcell: add common SoC devices Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 09/13] picoxcell: add support for pm Jamie Iles
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

PC3X3 devices have a PLL to control the ARM core speed. Add a cpufreq
driver so that we can run at optimum speed and power.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile         |    1 +
 arch/arm/mach-picoxcell/cpufreq.c        |  145 ++++++++++++++++++++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.c |    3 +
 arch/arm/mach-picoxcell/picoxcell_core.h |    9 ++
 4 files changed, 158 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 371698f..6249ee8 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -4,3 +4,4 @@ obj-y				:= picoxcell_core.o axi2cfg.o \
 				   gpio.o \
 				   clk.o \
 				   devices.o
+obj-$(CONFIG_CPU_FREQ)		+= cpufreq.o
diff --git a/arch/arm/mach-picoxcell/cpufreq.c b/arch/arm/mach-picoxcell/cpufreq.c
new file mode 100644
index 0000000..7fa8719
--- /dev/null
+++ b/arch/arm/mach-picoxcell/cpufreq.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2010 picoChip Designs Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#define pr_fmt(fmt) "picoxcell_cpufreq: " fmt
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+
+#include <mach/hardware.h>
+
+#include "picoxcell_core.h"
+
+static struct {
+	struct clk  *arm_clk;
+} cpufreq;
+
+/* The minimum frequency that we'll allow the ARM to run at (in KHz). */
+#define PC3X3_PLL_MIN_KHZ			    140000
+/* The maximum frequency that we'll allow the ARM to run at (in KHz). */
+#define PC3X3_PLL_MAX_KHZ			    700000
+/* The step between frequencies that the PLL can generate. */
+#define PC3X3_PLL_STEP				    5000
+
+/*
+ * The fuse block contains an 8 bit number which is the maximum clkf value
+ * that we can program. If this isn't programmed then allow 700Mhz operation.
+ * If not, limit the maximum speed to whatever this value corresponds to.
+ */
+static unsigned int picoxcell_cpufreq_max_clkf(void)
+{
+#define MAX_CLKF_FUSE	904
+#define MAX_CLKF_REG	IO_ADDRESS(PICOXCELL_FUSE_BASE + 904 / 8)
+	u8 max_clkf = readb(MAX_CLKF_REG);
+
+	return max_clkf ? ((max_clkf + 1) * 5) / 1000 : PC3X3_PLL_MAX_KHZ;
+}
+
+/*
+ * Initialise the new policy. We allow the PLL to go to the minimum speed but
+ * limit it to either 700Mhz or the frequency that corresponds to the clkf
+ * value in ARM_PLL_M_NUMBER fuses in the fuse block (if nonzero), whichever
+ * is smallest.
+ *
+ * A change of 20% should take ~2uS so we specify the transition latency as
+ * 50uS. This should allow jumps from 400MHz->700MHz within this period.
+ */
+static int picoxcell_cpufreq_init_policy(struct cpufreq_policy *policy)
+{
+	policy->cpuinfo.min_freq		= PC3X3_PLL_MIN_KHZ;
+	policy->cpuinfo.max_freq		= picoxcell_cpufreq_max_clkf();
+	policy->cpuinfo.transition_latency	= 50000;
+	policy->cur				= clk_get_rate(cpufreq.arm_clk);
+	policy->min				= PC3X3_PLL_MIN_KHZ;
+	policy->max				= picoxcell_cpufreq_max_clkf();
+
+	return 0;
+}
+
+static int picoxcell_cpufreq_verify(struct cpufreq_policy *policy)
+{
+	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+				     policy->cpuinfo.max_freq);
+
+	policy->min = clk_round_rate(cpufreq.arm_clk, policy->min);
+	policy->max = clk_round_rate(cpufreq.arm_clk, policy->max);
+
+	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+				     policy->cpuinfo.max_freq);
+
+	return 0;
+}
+
+static int picoxcell_cpufreq_target(struct cpufreq_policy *policy,
+				    unsigned int target_freq,
+				    unsigned int relation)
+{
+	int ret = 0;
+	struct cpufreq_freqs freqs;
+
+	if (target_freq > policy->max)
+		target_freq = policy->max;
+	if (target_freq < policy->min)
+		target_freq = policy->min;
+
+	target_freq = clk_round_rate(cpufreq.arm_clk, target_freq);
+
+	freqs.old = clk_get_rate(cpufreq.arm_clk);
+	freqs.new = target_freq;
+	freqs.cpu = policy->cpu;
+
+	if (freqs.new == freqs.old)
+		return 0;
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	ret = clk_set_rate(cpufreq.arm_clk, target_freq);
+	if (ret) {
+		pr_err("unable to set cpufreq rate to %u\n", target_freq);
+		freqs.new = freqs.old;
+	}
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return ret;
+}
+
+static unsigned int picoxcell_cpufreq_get(unsigned int cpu)
+{
+	return (unsigned int)clk_get_rate(cpufreq.arm_clk);
+}
+
+static struct cpufreq_driver picoxcell_cpufreq_driver = {
+	.owner		= THIS_MODULE,
+	.flags		= CPUFREQ_STICKY,
+	.name		= "picoxcell",
+	.init		= picoxcell_cpufreq_init_policy,
+	.verify		= picoxcell_cpufreq_verify,
+	.target		= picoxcell_cpufreq_target,
+	.get		= picoxcell_cpufreq_get,
+};
+
+int __init picoxcell_cpufreq_init(void)
+{
+	cpufreq.arm_clk = clk_get(NULL, "arm");
+
+	/*
+	 * If there isn't a clock then there's nothing to do. Some devices
+	 * e.g. PC3X2 don't have scalable clocks.
+	 */
+	if (IS_ERR_OR_NULL(cpufreq.arm_clk)) {
+		pr_info("cpufreq: no arm clock available - disabling scaling\n");
+		return 0;
+	}
+
+	return cpufreq_register_driver(&picoxcell_cpufreq_driver);
+}
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 7c51e0f..f3e0279 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -300,4 +300,7 @@ void __init picoxcell_core_init(void)
 
 	/* Register the devices. */
 	picoxcell_add_devices();
+
+	if (picoxcell_has_feature(PICOXCELL_FEATURE_CPUFREQ))
+		WARN_ON(picoxcell_cpufreq_init());
 }
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.h b/arch/arm/mach-picoxcell/picoxcell_core.h
index 9647b7a..f6d90bf 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.h
+++ b/arch/arm/mach-picoxcell/picoxcell_core.h
@@ -21,4 +21,13 @@ extern void __init picoxcell_map_io(void);
 extern struct sys_timer picoxcell_sys_timer;
 extern void __init picoxcell_add_devices(void);
 
+# ifdef CONFIG_CPU_FREQ
+extern int __init picoxcell_cpufreq_init(void);
+# else /* CONFIG_CPU_FREQ */
+static inline int picoxcell_cpufreq_init(void)
+{
+	return 0;
+}
+# endif /* CONFIG_CPU_FREQ */
+
 #endif /* __ASM_ARCH_PICOXCELL_CORE_H__ */
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 09/13] picoxcell: add support for pm
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (7 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 08/13] picoxcell: add cpufreq support Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 10/13] picoxcell: add chained GPIO IRQ handlers Jamie Iles
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

PC3X3 devices support clock gating so provide support for PM on these
devices. We only support standby modes as we can't gate the ARM clock or
memory controllers.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile         |    1 +
 arch/arm/mach-picoxcell/picoxcell_core.c |    2 +
 arch/arm/mach-picoxcell/picoxcell_core.h |    9 ++
 arch/arm/mach-picoxcell/pm.c             |  146 ++++++++++++++++++++++++++++++
 4 files changed, 158 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 6249ee8..09ae582 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -5,3 +5,4 @@ obj-y				:= picoxcell_core.o axi2cfg.o \
 				   clk.o \
 				   devices.o
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq.o
+obj-$(CONFIG_PM)		+= pm.o
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index f3e0279..8c1195b 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -303,4 +303,6 @@ void __init picoxcell_core_init(void)
 
 	if (picoxcell_has_feature(PICOXCELL_FEATURE_CPUFREQ))
 		WARN_ON(picoxcell_cpufreq_init());
+	if (picoxcell_has_feature(PICOXCELL_FEATURE_PM))
+		WARN_ON(picoxcell_pm_init());
 }
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.h b/arch/arm/mach-picoxcell/picoxcell_core.h
index f6d90bf..3a673cf 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.h
+++ b/arch/arm/mach-picoxcell/picoxcell_core.h
@@ -30,4 +30,13 @@ static inline int picoxcell_cpufreq_init(void)
 }
 # endif /* CONFIG_CPU_FREQ */
 
+# ifdef CONFIG_PM
+extern int __init picoxcell_pm_init(void);
+# else /* CONFIG_PM */
+static inline int picoxcell_pm_init(void)
+{
+	return 0;
+}
+# endif /* CONFIG_PM */
+
 #endif /* __ASM_ARCH_PICOXCELL_CORE_H__ */
diff --git a/arch/arm/mach-picoxcell/pm.c b/arch/arm/mach-picoxcell/pm.c
new file mode 100644
index 0000000..1aa591a
--- /dev/null
+++ b/arch/arm/mach-picoxcell/pm.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#define pr_fmt(fmt) "picoxcell_pm: " fmt
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/suspend.h>
+
+#include <mach/hardware.h>
+
+#include "picoxcell_core.h"
+
+/*
+ * Clocks that aren't controlled by drivers and need to be explicitly
+ * controlled by the PM code. The clocks affected are:
+ *	- TZPC
+ *	- EBI
+ */
+static struct clk *tzpc_clk;
+static struct clk *ebi_clk;
+
+static int picoxcell_pm_valid(suspend_state_t state)
+{
+	/*
+	 * We only support standby mode. There is no point in doing anything
+	 * for PM_SUSPEND_MEM as we can't power down the core or the memory
+	 * interfaces.
+	 *
+	 * When we enter standby, the only thing we can do is power down some
+	 * of the peripherals.
+	 */
+	return (state == PM_SUSPEND_ON || state == PM_SUSPEND_STANDBY);
+}
+
+static void wait_for_event(void)
+{
+	pr_debug("entering sleep - wait for interrupt\n");
+	/*
+	 * Drain the writebuffer and wait for an interrupt.
+	 */
+	dsb();
+	asm volatile("mcr   p15, 0, %0, c7, c0, 4\n" : : "r"(0));
+}
+
+static void picoxcell_pm_disable_clks(void)
+{
+	pr_debug("disable ebi and tzpc clks\n");
+	clk_disable(tzpc_clk);
+	clk_disable(ebi_clk);
+
+#ifdef CONFIG_PC3X3_STOP_WDT_IN_SUSPEND
+	syscfg_update(1 << AXI2CFG_SYS_CONFIG_WDG_PAUSE_IDX,
+		      1 << AXI2CFG_SYS_CONFIG_WDG_PAUSE_IDX);
+#endif /* CONFIG_PC3X3_STOP_WDT_IN_SUSPEND */
+}
+
+static int picoxcell_pm_restore_clks(void)
+{
+	pr_debug("restore ebi and tzpc clks\n");
+
+	if (clk_enable(tzpc_clk)) {
+		pr_err("unable to restore tzpc clk\n");
+		return -EBUSY;
+	}
+
+	if (clk_enable(ebi_clk)) {
+		pr_err("unable to restore ebi clk\n");
+		return -EBUSY;
+	}
+
+#ifdef CONFIG_PC3X3_STOP_WDT_IN_SUSPEND
+	syscfg_update(1 << AXI2CFG_SYS_CONFIG_WDG_PAUSE_IDX, 0);
+#endif /* CONFIG_PC3X3_STOP_WDT_IN_SUSPEND */
+
+	return 0;
+}
+
+static int picoxcell_pm_enter(suspend_state_t state)
+{
+	int err = 0;
+
+	pr_debug("entering suspend state\n");
+
+	switch (state) {
+	case PM_SUSPEND_STANDBY:
+		/*
+		 * Disable any clocks that aren't controlled by drivers but
+		 * need to be enabled. This includes the EBI, TZPC etc.
+		 */
+		picoxcell_pm_disable_clks();
+		wait_for_event();
+		/*
+		 * Reenable any clcoks that aren't controlled by drivers.
+		 */
+		err = picoxcell_pm_restore_clks();
+
+		break;
+
+	case PM_SUSPEND_ON:
+		wait_for_event();
+		break;
+
+	default:
+		err = -EOPNOTSUPP;
+	}
+
+	pr_debug("resumed\n");
+
+	return 0;
+}
+
+static struct platform_suspend_ops picoxcell_pm_ops = {
+	.valid	    = picoxcell_pm_valid,
+	.enter	    = picoxcell_pm_enter,
+};
+
+int __init picoxcell_pm_init(void)
+{
+	ebi_clk = clk_get(NULL, "ebi");
+	if (IS_ERR_OR_NULL(ebi_clk)) {
+		pr_warning("no ebi clock\n");
+		return -ENODEV;
+	}
+
+	tzpc_clk = clk_get(NULL, "tzprot_ctl");
+	if (IS_ERR_OR_NULL(tzpc_clk)) {
+		pr_warning("no tzpc clock\n");
+		goto tzpc_clk_fail;
+	}
+
+	suspend_set_ops(&picoxcell_pm_ops);
+
+	return 0;
+
+tzpc_clk_fail:
+	clk_put(ebi_clk);
+
+	return -ENODEV;
+}
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 10/13] picoxcell: add chained GPIO IRQ handlers
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (8 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 09/13] picoxcell: add support for pm Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 11/13] picoxcell: add support for pc3x2 devices Jamie Iles
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

The 8 bits of port A on the ARM GPIO of all devices are capable of
generating interrupts. Provide chained handlers so that these pins can
be used as interrupts and the sensitivity and polarity can be set.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/include/mach/gpio.h |    2 +
 arch/arm/mach-picoxcell/picoxcell_core.c    |   41 +++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-picoxcell/include/mach/gpio.h b/arch/arm/mach-picoxcell/include/mach/gpio.h
index ce55c3d..8955e20 100644
--- a/arch/arm/mach-picoxcell/include/mach/gpio.h
+++ b/arch/arm/mach-picoxcell/include/mach/gpio.h
@@ -152,6 +152,8 @@ enum {
 
 #ifdef __KERNEL__
 
+extern struct irq_chip armgpio_irqchip;
+
 static inline int gpio_get_value(unsigned gpio)
 {
 	return __gpio_get_value(gpio);
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 8c1195b..8386c83 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -25,6 +25,19 @@
 #include "picoxcell_core.h"
 #include "soc.h"
 
+static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	unsigned real_irq = (irq - IRQ_GPIO0 + __IRQ_GPIO0);
+	struct irq_desc *real_desc = irq_desc + real_irq;
+	/*
+	 * We have 8 GPIO pins that can generate interrupts and each one has
+	 * it's own interrupt bit in the VIC. All we need to do is do a
+	 * one-to-one translation from the virtual IRQ number to the real one
+	 * and call the handler.
+	 */
+	desc_handle_irq(real_irq, real_desc);
+}
+
 struct picoxcell_soc *picoxcell_get_soc(void)
 {
 	unsigned long device_id = readl(IO_ADDRESS(PICOXCELL_AXI2CFG_BASE +
@@ -37,6 +50,11 @@ struct picoxcell_soc *picoxcell_get_soc(void)
 
 void __init picoxcell_init_irq(void)
 {
+#define GPIO_INT_EN_REG	    IO_ADDRESS(PICOXCELL_GPIO_BASE + \
+				       GPIO_INT_EN_REG_OFFSET)
+#define GPIO_EOI_REG	    IO_ADDRESS(PICOXCELL_GPIO_BASE + \
+				       GPIO_PORT_A_EOI_REG_OFFSET)
+	unsigned i;
 	u32 vic0_resume_sources =
 		(1 << (IRQ_AXI2PICO8 & 31)) |
 		(1 << (IRQ_EMAC & 31)) |
@@ -45,6 +63,29 @@ void __init picoxcell_init_irq(void)
 	vic_init(IO_ADDRESS(PICOXCELL_VIC0_BASE), 32, 0xFFFFFFFE,
 		 vic0_resume_sources);
 	vic_init(IO_ADDRESS(PICOXCELL_VIC1_BASE), 0, 0x7FF, 0);
+
+	/*
+	 * We want to enable/disable interrupts for the GPIO pins through the
+	 * GPIO block itself. To do this we install a chained handler. If a
+	 * user requests one of the __IRQ_GPIOn interrupts then the GPIO block
+	 * won't get configured.  We provide these interrupts below as virtual
+	 * ones that will configure the GPIO block and enable the source in
+	 * the VIC.
+	 *
+	 * The chained handler simply converts from the virtual IRQ handler to
+	 * the real interrupt source and calls the GPIO IRQ handler.
+	 */
+	writel(0, GPIO_INT_EN_REG);
+	writel(~0, GPIO_EOI_REG);
+	for (i = IRQ_GPIO0; i <= IRQ_GPIO7; ++i) {
+		set_irq_chip(i, &armgpio_irqchip);
+		set_irq_handler(i, handle_level_irq);
+		irq_desc[i].status |= IRQ_LEVEL;
+		set_irq_flags(i, IRQF_VALID);
+	}
+
+	for (i = __IRQ_GPIO0; i <= __IRQ_GPIO7; ++i)
+		set_irq_chained_handler(i, gpio_irq_handler);
 }
 
 static const char *picoxcell_get_partname(void)
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 11/13] picoxcell: add support for pc3x2 devices
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (9 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 10/13] picoxcell: add chained GPIO IRQ handlers Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 12/13] picoxcell: add support for pc3x3 devices Jamie Iles
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

PC3X2 is the first supported device family of picoXcell and this patch
defines the virtual clocks to allow drivers to work, the pin muxing
options, timer locations and the GPIO definitions.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Kconfig          |    7 +
 arch/arm/mach-picoxcell/Makefile         |    1 +
 arch/arm/mach-picoxcell/pc3x2.c          |  453 ++++++++++++++++++++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.c |   13 +-
 arch/arm/mach-picoxcell/soc.h            |    1 +
 5 files changed, 474 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig
index 3daba53..5141202 100644
--- a/arch/arm/mach-picoxcell/Kconfig
+++ b/arch/arm/mach-picoxcell/Kconfig
@@ -1,4 +1,11 @@
 menu "PICOXCELL platform type"
 	depends on ARCH_PICOXCELL
 
+config PICOXCELL_PC3X2
+	bool "Support PC3x2 devices"
+	default y
+	help
+	  Include support for picoChip PC3x2 family of devices. This
+	  includes PC302 and PC312.
+
 endmenu
diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 09ae582..ab5dec1 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -6,3 +6,4 @@ obj-y				:= picoxcell_core.o axi2cfg.o \
 				   devices.o
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq.o
 obj-$(CONFIG_PM)		+= pm.o
+obj-$(CONFIG_PICOXCELL_PC3X2)	+= pc3x2.o
diff --git a/arch/arm/mach-picoxcell/pc3x2.c b/arch/arm/mach-picoxcell/pc3x2.c
new file mode 100644
index 0000000..31d52e9
--- /dev/null
+++ b/arch/arm/mach-picoxcell/pc3x2.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+
+#include <asm/mach/map.h>
+
+#include <mach/clkdev.h>
+#include <mach/hardware.h>
+
+#include "mux.h"
+#include "picoxcell_core.h"
+#include "soc.h"
+
+FIXED_CLK(spi,		CLOCK_TICK_RATE, -1);
+FIXED_CLK(dmac0,	CLOCK_TICK_RATE, -1);
+FIXED_CLK(dmac1,	CLOCK_TICK_RATE, -1);
+FIXED_CLK(ebi,		CLOCK_TICK_RATE, -1);
+FIXED_CLK(ipsec,	CLOCK_TICK_RATE, -1);
+FIXED_CLK(l2_engine,	CLOCK_TICK_RATE, -1);
+FIXED_CLK(fuse,		CLOCK_TICK_RATE, -1);
+FIXED_CLK(tzpc,		CLOCK_TICK_RATE, -1);
+FIXED_CLK(wdt,		CLOCK_TICK_RATE, -1);
+
+static struct clk_lookup pc3x2_clk_lookup[] = {
+	CLK_LOOKUP("dw_spi_mmio.0",	NULL,		&spi_clk),
+	CLK_LOOKUP("dw_dmac.0",		NULL,		&dmac0_clk),
+	CLK_LOOKUP("dw_dmac.1",		NULL,		&dmac1_clk),
+	CLK_LOOKUP(NULL,		"ebi",		&ebi_clk),
+	CLK_LOOKUP(NULL,		"tzprot_ctrl",	&tzpc_clk),
+	CLK_LOOKUP("picoxcell-ipsec",	NULL,		&ipsec_clk),
+	CLK_LOOKUP("picoxcell-l2",	NULL,		&l2_engine_clk),
+	CLK_LOOKUP("picoxcell-fuse",	NULL,		&fuse_clk),
+	CLK_LOOKUP("dw_wdt",		NULL,		&wdt_clk),
+};
+
+static void pc3x2_clk_init(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pc3x2_clk_lookup); ++i) {
+		struct clk_lookup *lookup = &pc3x2_clk_lookup[i];
+
+		picoxcell_clk_add(lookup);
+	}
+}
+
+static ssize_t pc3x2_get_boot_mode(char *buf)
+{
+	unsigned long syscfg = syscfg_read();
+	const char *boot_mode;
+
+	switch (syscfg & 0x3) {
+	case 0x0:
+		boot_mode = "parallel";
+		break;
+	case 0x1:
+		boot_mode = "ssi";
+		break;
+	case 0x2:
+		boot_mode = "mii";
+		break;
+	default:
+		boot_mode = "unknown";
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", boot_mode);
+}
+
+static int
+pai_get_mux(struct muxed_pin *pin)
+{
+#define PAI_GPIO_PIN_ARM_4	0xB
+#define PAI_GPIO_PIN_ARM_5	0xA
+#define PAI_GPIO_PIN_ARM_6	0x9
+#define PAI_GPIO_PIN_ARM_7	0x8
+#define PAI_GPIO_PIN_SDGPIO_4	0x7
+#define PAI_GPIO_PIN_SDGPIO_5	0x6
+#define PAI_GPIO_PIN_SDGPIO_6	0x5
+#define PAI_GPIO_PIN_SDGPIO_7	0x4
+#define PICOXCELL_PAI_CAEID		0x8080
+#define PAI_SLEEP_REG		0xA060
+#define PAI_IO_CTRL_REG		0x0009
+
+	/* Make sure that the PAI block is awake. */
+	u16 data = 0, sd_mask = 0, arm_mask = 0;
+	int ret;
+
+	axi2cfg_config_write(PICOXCELL_PAI_CAEID, PAI_SLEEP_REG, &data, 1);
+	/* Get the current PAI muxing configuration. */
+	ret = axi2cfg_config_read(PICOXCELL_PAI_CAEID, PAI_IO_CTRL_REG,
+				  &data, 1);
+	if (1 != ret) {
+		pr_warning("unable to read PAI I/O control reg\n");
+		return -EIO;
+	}
+
+	if (pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_4) {
+		sd_mask |= PAI_GPIO_PIN_SDGPIO_4;
+	} else if (pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_5) {
+		sd_mask |= PAI_GPIO_PIN_SDGPIO_5;
+	} else if (pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_6) {
+		sd_mask |= PAI_GPIO_PIN_SDGPIO_6;
+	} else if (pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_7) {
+		sd_mask |= PAI_GPIO_PIN_SDGPIO_7;
+	} else if (pin->arm_pin == PC3X2_GPIO_PIN_ARM_4) {
+		arm_mask |= PAI_GPIO_PIN_ARM_4;
+	} else if (pin->arm_pin == PC3X2_GPIO_PIN_ARM_5) {
+		arm_mask |= PAI_GPIO_PIN_ARM_5;
+	} else if (pin->arm_pin == PC3X2_GPIO_PIN_ARM_6) {
+		arm_mask |= PAI_GPIO_PIN_ARM_6;
+	} else if (pin->arm_pin == PC3X2_GPIO_PIN_ARM_7) {
+		arm_mask |= PAI_GPIO_PIN_ARM_7;
+	} else {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (data & sd_mask)
+		ret = MUX_SD;
+	else if (data & arm_mask)
+		ret = MUX_ARM;
+	else
+		ret = MUX_PERIPHERAL;
+
+out:
+	return ret;
+}
+
+static int
+pai_set_mux(struct muxed_pin *pin,
+	    enum mux_setting setting)
+{
+	/* Make sure that the PAI block is awake. */
+	u16 data = 0;
+	int err;
+
+	axi2cfg_config_write(PICOXCELL_PAI_CAEID, PAI_SLEEP_REG, &data, 1);
+
+	/* Get the current PAI muxing configuration. */
+	err = axi2cfg_config_read(PICOXCELL_PAI_CAEID, PAI_IO_CTRL_REG,
+				  &data, 1);
+	if (1 != err) {
+		pr_warning("unable to read PAI I/O control reg\n");
+		return -EIO;
+	}
+
+	if (MUX_SD == setting) {
+		if (pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_4) {
+			data |= PAI_GPIO_PIN_SDGPIO_4;
+		} else if (pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_5) {
+			data |= PAI_GPIO_PIN_SDGPIO_5;
+		} else if (pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_6) {
+			data |= PAI_GPIO_PIN_SDGPIO_6;
+		} else if (pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_7) {
+			data |= PAI_GPIO_PIN_SDGPIO_7;
+		} else {
+			err = -EINVAL;
+			goto out;
+		}
+	} else if (MUX_ARM == setting) {
+		if (pin->arm_pin == PC3X2_GPIO_PIN_ARM_4) {
+			data |= PAI_GPIO_PIN_ARM_4;
+		} else if (pin->arm_pin == PC3X2_GPIO_PIN_ARM_5) {
+			data |= PAI_GPIO_PIN_ARM_5;
+		} else if (pin->arm_pin == PC3X2_GPIO_PIN_ARM_6) {
+			data |= PAI_GPIO_PIN_ARM_6;
+		} else if (pin->arm_pin == PC3X2_GPIO_PIN_ARM_7) {
+			data |= PAI_GPIO_PIN_ARM_7;
+		} else {
+			err = -EINVAL;
+			goto out;
+		}
+	} else if (MUX_PERIPHERAL == setting) {
+		/*
+		 * We don't do anything here. The PAI is only driven by the
+		 * picoArray so this will be automatically set in the loadfile
+		 * when the design is next loaded.
+		 */
+		err = 0;
+	} else if (MUX_UNMUXED == setting) {
+		/* This can't be changed at run-time! */
+		return -EPERM;
+	}
+
+	axi2cfg_config_write(PICOXCELL_PAI_CAEID, PAI_IO_CTRL_REG, &data, 1);
+	err = 0;
+
+out:
+	return err;
+}
+
+static struct muxed_pin pai_pins[] = {
+PIN(sdgpio4, PC3X2_GPIO_PIN_SDGPIO_4, -1, pai_set_mux, pai_get_mux),
+PIN(sdgpio5, PC3X2_GPIO_PIN_SDGPIO_5, -1, pai_set_mux, pai_get_mux),
+PIN(sdgpio6, PC3X2_GPIO_PIN_SDGPIO_6, -1, pai_set_mux, pai_get_mux),
+PIN(sdgpio7, PC3X2_GPIO_PIN_SDGPIO_7, -1, pai_set_mux, pai_get_mux),
+PIN(arm4, -1, PC3X2_GPIO_PIN_ARM_4, pai_set_mux, pai_get_mux),
+PIN(arm5, -1, PC3X2_GPIO_PIN_ARM_5, pai_set_mux, pai_get_mux),
+PIN(arm6, -1, PC3X2_GPIO_PIN_ARM_6, pai_set_mux, pai_get_mux),
+PIN(arm7, -1, PC3X2_GPIO_PIN_ARM_7, pai_set_mux, pai_get_mux),
+};
+
+static struct pin_group pai_group = {
+	.nr_pins    = ARRAY_SIZE(pai_pins),
+	.name	    = "pai/ebi",
+	.pins	    = pai_pins,
+};
+
+static int
+shd_get_mux(struct muxed_pin *pin)
+{
+	unsigned bit;
+	unsigned long syscfg = syscfg_read();
+
+	if (0 == (syscfg & 0x3) && pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_15)
+		return MUX_PERIPHERAL;
+
+	if (pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_0) {
+		if (syscfg & AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK)
+			return MUX_PERIPHERAL;
+		else
+			return MUX_SD;
+	}
+
+	bit = 1 << (AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_LO +
+			pin->sd_pin - PC3X2_GPIO_PIN_SDGPIO_8);
+
+	return syscfg & bit ? MUX_ARM : MUX_SD;
+}
+
+static int
+shd_set_mux(struct muxed_pin *pin,
+	    enum mux_setting setting)
+{
+	unsigned bit;
+	unsigned long syscfg = syscfg_read();
+
+	/*
+	 * In parallel boot mode, shared pin 7 can't be used as it is always
+	 * on the EBI.
+	 */
+	if (0 == (syscfg & 0x3) && pin->sd_pin == PC3X2_GPIO_PIN_SDGPIO_15)
+		return -EBUSY;
+
+	switch (setting) {
+	case MUX_PERIPHERAL:
+		/*
+		 * SDGPIO pin 0 is shared with the Frac-N.
+		 */
+		if (PC3X2_GPIO_PIN_SDGPIO_0 == pin->sd_pin) {
+			syscfg_update(AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK,
+				      AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK);
+			break;
+		}
+		return -EIO;
+
+	case MUX_SD:
+		syscfg_update(AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK, 0);
+		bit = 1 << (AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_LO +
+			    pin->sd_pin - PC3X2_GPIO_PIN_SDGPIO_8);
+		syscfg_update(bit, 0);
+		break;
+
+	case MUX_ARM:
+		if (PC3X2_GPIO_PIN_SDGPIO_0 == pin->sd_pin)
+			return -EINVAL;
+		syscfg_update(AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK, 0);
+		bit = 1 << (AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_LO +
+			    pin->arm_pin - PC3X2_GPIO_PIN_ARM_8);
+		syscfg_update(bit, bit);
+		break;
+
+	case MUX_UNMUXED:
+		/* This can't be changed at run-time! */
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+static struct muxed_pin shared_pins[] = {
+GPIO(shared0, PC3X2_GPIO_PIN_SDGPIO_8, PC3X2_GPIO_PIN_ARM_8, shd_set_mux,
+     shd_get_mux),
+GPIO(shared1, PC3X2_GPIO_PIN_SDGPIO_9, PC3X2_GPIO_PIN_ARM_9, shd_set_mux,
+     shd_get_mux),
+GPIO(shared2, PC3X2_GPIO_PIN_SDGPIO_10, PC3X2_GPIO_PIN_ARM_10, shd_set_mux,
+     shd_get_mux),
+GPIO(shared3, PC3X2_GPIO_PIN_SDGPIO_11, PC3X2_GPIO_PIN_ARM_11, shd_set_mux,
+     shd_get_mux),
+GPIO(shared4, PC3X2_GPIO_PIN_SDGPIO_12, PC3X2_GPIO_PIN_ARM_12, shd_set_mux,
+     shd_get_mux),
+GPIO(shared5, PC3X2_GPIO_PIN_SDGPIO_13, PC3X2_GPIO_PIN_ARM_13, shd_set_mux,
+     shd_get_mux),
+GPIO(shared6, PC3X2_GPIO_PIN_SDGPIO_14, PC3X2_GPIO_PIN_ARM_14, shd_set_mux,
+     shd_get_mux),
+PIN(shared7, PC3X2_GPIO_PIN_SDGPIO_15, PC3X2_GPIO_PIN_ARM_15, shd_set_mux,
+    shd_get_mux),
+};
+
+static struct pin_group shd_group = {
+	.nr_pins    = ARRAY_SIZE(shared_pins),
+	.name	    = "shared/ebi",
+	.pins	    = shared_pins,
+};
+
+static struct muxed_pin fracn_pins[] = {
+PIN(sdgpio0, PC3X2_GPIO_PIN_SDGPIO_0, -1, shd_set_mux, shd_get_mux),
+};
+
+static struct pin_group fracn_group = {
+	.nr_pins    = ARRAY_SIZE(fracn_pins),
+	.name	    = "fracn/sdgpio0",
+	.pins	    = fracn_pins,
+};
+
+static struct pin_group *pc3x2_groups[] = {
+	&shd_group,
+	&pai_group,
+	&fracn_group,
+};
+
+static int armgpio_number(int pin)
+{
+	if (pin >= PC3X2_GPIO_PIN_ARM_0 && pin <= PC3X2_GPIO_PIN_ARM_15)
+		return pin;
+
+	return -1;
+}
+
+static int sdgpio_number(int pin)
+{
+	if (pin >= PC3X2_GPIO_PIN_SDGPIO_0 &&
+	    pin <= PC3X2_GPIO_PIN_SDGPIO_15)
+		return pin - PC3X2_GPIO_PIN_SDGPIO_0;
+
+	return -1;
+}
+
+static void pc3x2_init_muxing(void)
+{
+	picoxcell_mux_register(pc3x2_groups, ARRAY_SIZE(pc3x2_groups),
+			       armgpio_number, sdgpio_number);
+}
+
+static const char * const pc3x2_armgpio_pins[] = {
+	"arm0",
+	"arm1",
+	"arm2",
+	"arm3",
+	"arm4",
+	"arm5",
+	"arm6",
+	"arm7",
+	"arm8",
+	"arm9",
+	"arm10",
+	"arm11",
+	"arm12",
+	"arm13",
+	"arm14",
+	"arm15",
+};
+
+static const char * const pc3x2_sdgpio_pins[] = {
+	"sdgpio0",
+	"sdgpio1",
+	"sdgpio2",
+	"sdgpio3",
+	"sdgpio4",
+	"sdgpio5",
+	"sdgpio6",
+	"sdgpio7",
+	"sdgpio8",
+	"sdgpio9",
+	"sdgpio10",
+	"sdgpio11",
+	"sdgpio12",
+	"sdgpio13",
+	"sdgpio14",
+	"sdgpio15",
+};
+
+static const struct picoxcell_timer pc3x2_timers[] = {
+	{
+		.name	= "timer0",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PICOXCELL_TIMER_BASE + 0 * TIMER_SPACING,
+		.irq	= IRQ_TIMER0,
+	},
+	{
+		.name	= "timer1",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PICOXCELL_TIMER_BASE + 1 * TIMER_SPACING,
+		.irq	= IRQ_TIMER1,
+	},
+	{
+		.name	= "timer2",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PICOXCELL_TIMER_BASE + 2 * TIMER_SPACING,
+		.irq	= IRQ_TIMER2,
+	},
+	{
+		.name	= "timer3",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PICOXCELL_TIMER_BASE + 3 * TIMER_SPACING,
+		.irq	= IRQ_TIMER3,
+	},
+	{
+		.name	= "rtc",
+		.type	= TIMER_TYPE_RTC,
+		.base	= PICOXCELL_RTCLK_BASE,
+		.irq	= IRQ_RTC,
+	},
+};
+
+static void pc3x2_init(void);
+
+struct picoxcell_soc pc3x2_soc = {
+	.init		= pc3x2_init,
+	.init_clocks	= pc3x2_clk_init,
+	.init_muxing	= pc3x2_init_muxing,
+	.get_boot_mode	= pc3x2_get_boot_mode,
+	.armgpio_pins	= pc3x2_armgpio_pins,
+	.nr_armgpio	= ARRAY_SIZE(pc3x2_armgpio_pins),
+	.armgpio_base	= PC3X2_GPIO_PIN_ARM_0,
+	.sdgpio_pins	= pc3x2_sdgpio_pins,
+	.nr_sdgpio	= ARRAY_SIZE(pc3x2_sdgpio_pins),
+	.sdgpio_base	= PC3X2_GPIO_PIN_SDGPIO_0,
+	.timers		= pc3x2_timers,
+	.nr_timers	= ARRAY_SIZE(pc3x2_timers),
+};
+
+static void pc3x2_init(void)
+{
+	/*
+	 * If we booted in parallel nor mode then we can continue to use the
+	 * EBI. If not then we only have a few bits of the EBI - just enough
+	 * to drive a NAND flash.
+	 */
+	if (syscfg_read() & 0x3)
+		set_bit(PICOXCELL_FEATURE_SW_NAND, pc3x2_soc.features);
+	else
+		set_bit(PICOXCELL_FEATURE_EBI, pc3x2_soc.features);
+}
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 8386c83..5b5f0c6 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -43,6 +43,10 @@ struct picoxcell_soc *picoxcell_get_soc(void)
 	unsigned long device_id = readl(IO_ADDRESS(PICOXCELL_AXI2CFG_BASE +
 					AXI2CFG_DEVICE_ID_REG_OFFSET));
 	switch (device_id) {
+	case 0x8003:
+	case 0x8007:
+		return &pc3x2_soc;
+
 	default:
 		panic("unsupported device type %lx", device_id);
 	}
@@ -91,7 +95,14 @@ void __init picoxcell_init_irq(void)
 static const char *picoxcell_get_partname(void)
 {
 	unsigned long dev_id = axi2cfg_readl(AXI2CFG_DEVICE_ID_REG_OFFSET);
-	const char *part = "<unknown>";
+	const char *part;
+
+	if (0x8003 == dev_id)
+		part = "pc302";
+	else if (0x8007 == dev_id)
+		part = "pc312";
+	else
+		part = "<unknown>";
 
 	return part;
 }
diff --git a/arch/arm/mach-picoxcell/soc.h b/arch/arm/mach-picoxcell/soc.h
index 784d714..ec30eef 100644
--- a/arch/arm/mach-picoxcell/soc.h
+++ b/arch/arm/mach-picoxcell/soc.h
@@ -49,6 +49,7 @@ struct picoxcell_soc {
 };
 
 extern struct picoxcell_soc *picoxcell_get_soc(void);
+extern struct picoxcell_soc pc3x2_soc;
 
 static inline int picoxcell_has_feature(enum picoxcell_features feat)
 {
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 12/13] picoxcell: add support for pc3x3 devices
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (10 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 11/13] picoxcell: add support for pc3x2 devices Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-26 11:14   ` [RFC PATCHv2] " Jamie Iles
  2010-11-23 10:06 ` [RFC PATCH 13/13] picoxcell: add support for the PC7302 development board Jamie Iles
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

PC3X3 is the second supported device family of picoXcell and this patch
defines the virtual clocks to allow drivers to work, the pin muxing
options, timer locations and the GPIO definitions. This also implements
the PLL control for the ARM PLL.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Kconfig          |   18 +
 arch/arm/mach-picoxcell/Makefile         |    1 +
 arch/arm/mach-picoxcell/pc3x3.c          | 1256 ++++++++++++++++++++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.c |   11 +
 arch/arm/mach-picoxcell/soc.h            |    1 +
 5 files changed, 1287 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig
index 5141202..671cadc 100644
--- a/arch/arm/mach-picoxcell/Kconfig
+++ b/arch/arm/mach-picoxcell/Kconfig
@@ -8,4 +8,22 @@ config PICOXCELL_PC3X2
 	  Include support for picoChip PC3x2 family of devices. This
 	  includes PC302 and PC312.
 
+config PICOXCELL_PC3X3
+	bool "Support PC3x3 devices"
+	default y
+	select ARCH_HAS_CPUFREQ
+	help
+	  Include support for picoChip PC3x3 family of devices. This includes
+	  PC313, PC323 and PC333.
+
+config PC3X3_STOP_WDT_IN_SUSPEND
+	bool "Stop WDT in PM suspend"
+	depends on PICOCHIP_PC3X3
+	default y
+	help
+	  If you say yes here, then when the platform is put into suspend mode,
+	  the watchdog timer will be paused. Saying no will cause the watchdog
+	  to keep running. If you say no here, make sure that the watchdog uses
+	  the pretimeout mode and mark the watchdog as a wakeup source.
+
 endmenu
diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index ab5dec1..d95b2b2 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -7,3 +7,4 @@ obj-y				:= picoxcell_core.o axi2cfg.o \
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq.o
 obj-$(CONFIG_PM)		+= pm.o
 obj-$(CONFIG_PICOXCELL_PC3X2)	+= pc3x2.o
+obj-$(CONFIG_PICOXCELL_PC3X3)	+= pc3x3.o
diff --git a/arch/arm/mach-picoxcell/pc3x3.c b/arch/arm/mach-picoxcell/pc3x3.c
new file mode 100644
index 0000000..506456d
--- /dev/null
+++ b/arch/arm/mach-picoxcell/pc3x3.c
@@ -0,0 +1,1256 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+
+#include <asm/mach/map.h>
+
+#include <mach/clkdev.h>
+#include <mach/hardware.h>
+
+#include "mux.h"
+#include "picoxcell_core.h"
+#include "soc.h"
+
+static DEFINE_SPINLOCK(pc3x3_clk_lock);
+
+FIXED_CLK(tzprot,	CLOCK_TICK_RATE, 0);
+FIXED_CLK(spi,		CLOCK_TICK_RATE, 1);
+FIXED_CLK(dmac0,	CLOCK_TICK_RATE, 2);
+FIXED_CLK(dmac1,	CLOCK_TICK_RATE, 3);
+FIXED_CLK(ebi,		CLOCK_TICK_RATE, 4);
+FIXED_CLK(ipsec,	CLOCK_TICK_RATE, 5);
+FIXED_CLK(l2_engine,	CLOCK_TICK_RATE, 6);
+FIXED_CLK(trng,		CLOCK_TICK_RATE, 7);
+FIXED_CLK(fuse,		CLOCK_TICK_RATE, 8);
+FIXED_CLK(otp,		CLOCK_TICK_RATE, 9);
+FIXED_CLK(wdt,		CLOCK_TICK_RATE, -1);
+VARIABLE_CLK(arm,			 -1, 140000, 700000, 5000);
+
+static struct clk_lookup pc3x3_clk_lookup[] = {
+	CLK_LOOKUP(NULL,		"tzprot_ctl",	&tzprot_clk),
+	CLK_LOOKUP("dw_spi_mmio.0",	NULL,		&spi_clk),
+	CLK_LOOKUP("dw_dmac.0",		NULL,		&dmac0_clk),
+	CLK_LOOKUP("dw_dmac.1",		NULL,		&dmac1_clk),
+	CLK_LOOKUP(NULL,		"ebi",		&ebi_clk),
+	CLK_LOOKUP("picoxcell-ipsec",	NULL,		&ipsec_clk),
+	CLK_LOOKUP("picoxcell-l2",	NULL,		&l2_engine_clk),
+	CLK_LOOKUP("picoxcell-trng",	NULL,		&trng_clk),
+	CLK_LOOKUP("picoxcell-fuse",	NULL,		&fuse_clk),
+	CLK_LOOKUP("picoxcell-otp",	NULL,		&otp_clk),
+	CLK_LOOKUP("dw_wdt",		NULL,		&wdt_clk),
+	CLK_LOOKUP(NULL,		"arm",		&arm_clk),
+};
+
+static int pc3x3_clk_is_enabled(struct clk *clk)
+{
+	unsigned long clk_gate =
+		axi2cfg_readl(AXI2CFG_CLOCK_GATING_REG_OFFSET);
+	return !(clk_gate & (1 << clk->clk_num));
+}
+
+static void pc3x3_clk_disable(struct clk *clk)
+{
+	unsigned long clk_gate;
+
+	if (clk->clk_num < 0)
+		return;
+
+	/*
+	 * Make sure that all outstanding transactions have reached the device
+	 * before we turn off the clock to prevent taking an exception.
+	 */
+	dsb();
+
+	clk_gate = axi2cfg_readl(AXI2CFG_CLOCK_GATING_REG_OFFSET);
+	clk_gate |= (1 << clk->clk_num);
+	axi2cfg_writel(clk_gate, AXI2CFG_CLOCK_GATING_REG_OFFSET);
+}
+
+static inline void pc3x3_clk_enable(struct clk *clk)
+{
+	unsigned long clk_gate;
+
+	if (clk->clk_num < 0)
+		return;
+
+	clk_gate = axi2cfg_readl(AXI2CFG_CLOCK_GATING_REG_OFFSET);
+	clk_gate &= ~(1 << clk->clk_num);
+	axi2cfg_writel(clk_gate, AXI2CFG_CLOCK_GATING_REG_OFFSET);
+}
+
+static long pc3x3_clk_round_rate(struct clk *clk,
+				 unsigned long rate)
+{
+	long ret = -EINVAL;
+	unsigned long offset = rate % clk->step;
+
+	if (WARN_ON(clk != &arm_clk))
+		goto out;
+
+	rate -= offset;
+	if (offset > clk->step - offset)
+		ret = rate + clk->step;
+	else
+		ret = rate;
+
+out:
+	return ret;
+}
+
+/* The register that the CLKF value is programmed into. */
+#define AXI2CFG_ARM_PLL_CLKF_REG_OFFS		0x0050
+/* The frequency sensing control register. */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_REG_OFFS	0x0054
+
+/* The value in the sense register is a valid frequency. */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_VALID	(1 << 29)
+/* The sensing process is active. */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_ACTIVE	(1 << 30)
+/* Write this to the sense register to start sensing. Self clearing. */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_START	(1 << 31)
+/*
+ * The frequency (in MHz) is returned in the bottom 10 bits of the sense
+ * register and is valid when bit 29 is asserted.
+ */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_FREQ_MASK	0x3FF
+
+static int __pc3x3_clk_get_rate(struct clk *clk)
+{
+	unsigned int mhz = 0;
+	unsigned long sense_val;
+	int ret = -EINVAL;
+
+	if (WARN_ON(clk != &arm_clk))
+		goto out;
+
+	while (0 == mhz) {
+		do {
+			axi2cfg_writel(AXI2CFG_ARM_PLL_FREQ_SENSE_START,
+				       AXI2CFG_ARM_PLL_FREQ_SENSE_REG_OFFS);
+
+			/* Wait for the frequency sense to complete. */
+			do {
+				sense_val = axi2cfg_readl(AXI2CFG_ARM_PLL_FREQ_SENSE_REG_OFFS);
+			} while ((sense_val &
+				  AXI2CFG_ARM_PLL_FREQ_SENSE_ACTIVE));
+		} while (!(sense_val & AXI2CFG_ARM_PLL_FREQ_SENSE_VALID));
+
+		/* The frequency sense returns the frequency in MHz. */
+		mhz = (sense_val & AXI2CFG_ARM_PLL_FREQ_SENSE_FREQ_MASK);
+	}
+	ret = mhz * 1000;
+
+out:
+	return ret;
+}
+
+static int pc3x3_clk_get_rate(struct clk *clk)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&pc3x3_clk_lock, flags);
+	ret = __pc3x3_clk_get_rate(clk);
+	spin_unlock_irqrestore(&pc3x3_clk_lock, flags);
+
+	return ret;
+}
+
+static void
+pc3x3_cpu_pll_set(unsigned int freq)
+{
+	/* Set the new frequency. */
+	axi2cfg_writel(((freq / 1000) / 5) - 1, AXI2CFG_ARM_PLL_CLKF_REG_OFFS);
+	udelay(2);
+}
+
+static int pc3x3_clk_set_rate(struct clk *clk, unsigned long target)
+{
+	int ret = -EINVAL;
+	unsigned long flags, current_khz;
+
+	if (WARN_ON(clk != &arm_clk) || target % clk->step) {
+		pr_err("unable to set rate for non-cpu clock (%lu)\n", target);
+		goto out;
+	}
+
+	pr_debug("set cpu clock rate to %luKHz\n", target);
+
+	spin_lock_irqsave(&pc3x3_clk_lock, flags);
+
+	/*
+	 * We can only reliably step by 20% at a time. We may need to
+	 * do this in several iterations.
+	 */
+	while ((current_khz = __pc3x3_clk_get_rate(clk)) != target) {
+		unsigned long next_step, next_target;
+
+		if (target < current_khz) {
+			next_step = current_khz - ((4 * current_khz) / 5);
+			next_target = current_khz -
+				min(current_khz - target, next_step);
+			next_target = roundup(next_target, clk->step);
+		} else {
+			next_step = ((6 * current_khz) / 5) - current_khz;
+			next_target =
+				min(target - current_khz, next_step) +
+				current_khz;
+			next_target =
+				(next_target / clk->step) * clk->step;
+		}
+
+		pc3x3_cpu_pll_set(next_target);
+	}
+
+	spin_unlock_irqrestore(&pc3x3_clk_lock, flags);
+	ret = 0;
+
+out:
+	return ret;
+}
+
+static void pc3x3_clk_init(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pc3x3_clk_lookup); ++i) {
+		struct clk_lookup *lookup = &pc3x3_clk_lookup[i];
+
+		lookup->clk->enable	= pc3x3_clk_enable;
+		lookup->clk->disable	= pc3x3_clk_disable;
+		lookup->clk->is_enabled	= pc3x3_clk_is_enabled;
+
+		if (lookup->clk->rate < 0) {
+			lookup->clk->round_rate	= pc3x3_clk_round_rate;
+			lookup->clk->set_rate	= pc3x3_clk_set_rate;
+			lookup->clk->get_rate	= pc3x3_clk_get_rate;
+		}
+
+		picoxcell_clk_add(lookup);
+	}
+
+	/*
+	 * For PC3x3, disable the clocks that aren't required in the core
+	 * code. The drivers will enable the clocks when they get initialised.
+	 */
+	__clk_disable(&spi_clk);
+	__clk_disable(&dmac0_clk);
+	__clk_disable(&dmac1_clk);
+	__clk_disable(&ipsec_clk);
+	__clk_disable(&l2_engine_clk);
+	__clk_disable(&trng_clk);
+	__clk_disable(&otp_clk);
+}
+
+/*
+ * IO MAPPING: we have most of the peripherals at 0x80000000, but the AXI2Pico
+ * buffers live at 0xC0000000 and the OTP at 0xFFFF8000.
+ */
+static struct map_desc pc3x3_io_desc[] = {
+	{
+		.virtual    = PHYS_TO_IO(PICOXCELL_PERIPH_BASE) +
+				PICOXCELL_PERIPH_LENGTH,
+		.pfn	    = __phys_to_pfn(PC3X3_OTP_BASE),
+		.length	    = SZ_32K,
+		.type	    = MT_DEVICE,
+	},
+};
+
+static void pc3x3_map_io(void)
+{
+	iotable_init(pc3x3_io_desc, ARRAY_SIZE(pc3x3_io_desc));
+}
+
+static ssize_t pc3x3_get_boot_mode(char *buf)
+{
+	unsigned long syscfg = syscfg_read();
+	unsigned long otp_fuses = readl(IO_ADDRESS(PICOXCELL_FUSE_BASE + 124));
+	const char *boot_mode;
+	const char *otp_boot = NULL;
+	/* We're interested in fuses 1018:1017, and otp_fuses starts at 992. */
+	unsigned otp_boot_mode = (otp_fuses >> 25) & 0x3;
+
+	switch (syscfg & 0x3) {
+	case 0x0:
+		boot_mode = "parallel";
+		break;
+	case 0x1:
+		boot_mode = "ssi";
+		break;
+	case 0x2:
+		boot_mode = "mii";
+		break;
+	default:
+		boot_mode = "unknown";
+	}
+
+	if (0x2 == otp_boot_mode)
+		otp_boot = ":otp";
+	else if (0x1 == otp_boot_mode)
+		boot_mode = "otp";
+
+	return snprintf(buf, PAGE_SIZE, "%s%s\n", boot_mode,
+			otp_boot ? otp_boot : "");
+}
+
+static DEFINE_SPINLOCK(mux_lock);
+
+#define AXI2CFG_SHD_GPIO_MUX_REG_OFFSET		0x34
+#define AXI2CFG_USE_PAI_GPIO_REG_OFFSET		0x38
+#define AXI2CFG_USE_EBI_GPIO_REG_OFFSET		0x3c
+#define AXI2CFG_USE_DECODE_GPIO_REG_OFFSET	0x40
+#define AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET	0x44
+
+/*
+ * Set the muxing of one of the shared pins.
+ *
+ * Must be called with mux_lock held.
+ */
+static void pc3x3_shd_gpio_set_mux(int arm_pin_nr, enum mux_setting setting)
+{
+	unsigned long shd_mux = axi2cfg_readl(AXI2CFG_SHD_GPIO_MUX_REG_OFFSET);
+
+	if (MUX_ARM == setting)
+		shd_mux |= (1 << arm_pin_nr);
+	else
+		shd_mux &= ~(1 << arm_pin_nr);
+
+	axi2cfg_writel(shd_mux, AXI2CFG_SHD_GPIO_MUX_REG_OFFSET);
+}
+
+static int pc3x3_get_shd_mux(struct muxed_pin *pin)
+{
+	unsigned long shd_mux = axi2cfg_readl(AXI2CFG_SHD_GPIO_MUX_REG_OFFSET);
+	unsigned long syscfg = syscfg_read();
+
+	if (pin->sd_pin == PC3X3_GPIO_PIN_SDGPIO_0) {
+		if (syscfg & AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK)
+			return MUX_PERIPHERAL;
+		else
+			return MUX_SD;
+	}
+
+	return shd_mux & (1 << pin->arm_pin) ? MUX_ARM : MUX_SD;
+}
+
+/*
+ * Shared ARM/SD gpio pins. These pins go to the arm_gpio[3:0] pads but can
+ * be arm or sdgpio.
+ */
+static int pc3x3_shd_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	if (MUX_PERIPHERAL == setting) {
+		if (pin->sd_pin != PC3X3_GPIO_PIN_SDGPIO_0)
+			return -EINVAL;
+
+		syscfg_update(AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK,
+			      AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK);
+	} else if (PC3X3_GPIO_PIN_SDGPIO_0 == pin->sd_pin) {
+		syscfg_update(AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK, 0);
+	}
+
+	if (PC3X3_GPIO_PIN_SDGPIO_0 == pin->sd_pin && MUX_ARM == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+	pc3x3_shd_gpio_set_mux(pin->arm_pin, setting);
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+static struct muxed_pin armgpio_0_4[] = {
+GPIO(arm_gpio0, PC3X3_GPIO_PIN_SDGPIO_16, PC3X3_GPIO_PIN_ARM_0, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+GPIO(arm_gpio1, PC3X3_GPIO_PIN_SDGPIO_17, PC3X3_GPIO_PIN_ARM_1, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+GPIO(arm_gpio2, PC3X3_GPIO_PIN_SDGPIO_18, PC3X3_GPIO_PIN_ARM_2, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+GPIO(arm_gpio3, PC3X3_GPIO_PIN_SDGPIO_19, PC3X3_GPIO_PIN_ARM_3, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+};
+
+static struct pin_group armgpio_0_4_group = {
+	.nr_pins    = ARRAY_SIZE(armgpio_0_4),
+	.name	    = "arm_gpio[3:0]",
+	.pins	    = armgpio_0_4,
+};
+
+static struct muxed_pin shd_gpio[] = {
+GPIO(shd_gpio, PC3X3_GPIO_PIN_SDGPIO_8, PC3X3_GPIO_PIN_ARM_8, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+};
+
+static struct pin_group pc3x3_shd_group = {
+	.nr_pins    = ARRAY_SIZE(shd_gpio),
+	.name	    = "shd_gpio",
+	.pins	    = shd_gpio,
+};
+
+/*
+ * boot_mode[1:0] pads - the pins switch to gpio automatically after boot and
+ * can be either arm or sdgpio.
+ */
+static struct muxed_pin boot_mode_0_1[] = {
+GPIO(boot_mode0, PC3X3_GPIO_PIN_SDGPIO_9, PC3X3_GPIO_PIN_ARM_9,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+GPIO(boot_mode1, PC3X3_GPIO_PIN_SDGPIO_10, PC3X3_GPIO_PIN_ARM_10,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group boot_mode_group = {
+	.nr_pins    = ARRAY_SIZE(boot_mode_0_1),
+	.name	    = "boot_mode[1:0]",
+	.pins	    = boot_mode_0_1,
+};
+
+/*
+ * sdram_speed_sel pad - automatically switches to gpio after boot and can be
+ * arm or sdgpio.
+ */
+static struct muxed_pin sdram_speed_sel[] = {
+GPIO(sdram_speed_sel, PC3X3_GPIO_PIN_SDGPIO_11, PC3X3_GPIO_PIN_ARM_11,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group sdram_speed_sel_group = {
+	.nr_pins    = ARRAY_SIZE(sdram_speed_sel),
+	.name	    = "sdram_speed_sel",
+	.pins	    = sdram_speed_sel,
+};
+
+/*
+ * mii_rev_en pad - automatically switches to gpio after boot and can be
+ * arm or sdgpio.
+ */
+static struct muxed_pin mii_rev_en[] = {
+GPIO(mii_rev_en, PC3X3_GPIO_PIN_SDGPIO_12, PC3X3_GPIO_PIN_ARM_12,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group mii_rev_en_group = {
+	.nr_pins    = ARRAY_SIZE(mii_rev_en),
+	.name	    = "mii_rev_en",
+	.pins	    = mii_rev_en,
+};
+
+/*
+ * mii_rmii_en pad - automatically switches to gpio after boot and can be
+ * arm or sdgpio.
+ */
+static struct muxed_pin mii_rmii_en[] = {
+GPIO(mii_rmii_en, PC3X3_GPIO_PIN_SDGPIO_13, PC3X3_GPIO_PIN_ARM_13,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group mii_rmii_en_group = {
+	.nr_pins    = ARRAY_SIZE(mii_rmii_en),
+	.name	    = "mii_rmii_en",
+	.pins	    = mii_rmii_en,
+};
+
+/*
+ * mii_speed_sel pad - automatically switches to gpio after boot and can be
+ * arm or sdgpio.
+ */
+static struct muxed_pin mii_speed_sel[] = {
+GPIO(mii_speed_sel, PC3X3_GPIO_PIN_SDGPIO_14, PC3X3_GPIO_PIN_ARM_14,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group mii_speed_sel_group = {
+	.nr_pins    = ARRAY_SIZE(mii_speed_sel),
+	.name	    = "mii_speed_sel",
+	.pins	    = mii_speed_sel,
+};
+
+static int pc3x3_shd_ebi_get_mux(struct muxed_pin *pin)
+{
+	int ebi_pin, err = 0, can_be_sd = 1;
+	unsigned long ebi_mux;
+
+	spin_lock(&mux_lock);
+
+	ebi_mux = axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	/*
+	 * Find out what EBI pin our GPIO maps to.
+	 */
+	if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_4 &&
+	    pin->arm_pin < PC3X3_GPIO_PIN_ARM_8) {
+		ebi_pin = 22 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_4);
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_20 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_24) {
+		ebi_pin = 18 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_20);
+	} else {
+		/* These aren't shared gpio pins. */
+		ebi_pin = 14 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_14);
+		can_be_sd = 0;
+	}
+
+	if (!(ebi_mux & (1 << (ebi_pin - 14)))) {
+		err = MUX_PERIPHERAL;
+		goto out;
+	}
+
+	if (can_be_sd)
+		err = pc3x3_get_shd_mux(pin);
+	else
+		err = MUX_ARM;
+
+out:
+	spin_unlock(&mux_lock);
+
+	return err;
+}
+
+static int pc3x3_shd_ebi_set_mux(struct muxed_pin *pin,
+				 enum mux_setting setting)
+{
+	int ebi_pin, err = 0, can_be_sd = 1;
+	unsigned long ebi_mux;
+
+	spin_lock(&mux_lock);
+
+	ebi_mux = axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	/*
+	 * Find out what EBI pin our GPIO maps to.
+	 */
+	if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_4 &&
+	    pin->arm_pin < PC3X3_GPIO_PIN_ARM_8) {
+		ebi_pin = 22 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_4);
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_20 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_24) {
+		ebi_pin = 18 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_20);
+	} else {
+		/* These aren't shared gpio pins. */
+		ebi_pin = 14 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_14);
+		can_be_sd = 0;
+	}
+
+	if (MUX_SD == setting && !can_be_sd) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	/* Use the pin as EBI. */
+	if (MUX_PERIPHERAL == setting)
+		ebi_mux &= ~(1 << (ebi_pin - 14));
+	else
+		ebi_mux |= (1 << (ebi_pin - 14));
+
+	axi2cfg_writel(ebi_mux, AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	/*
+	 * Make sure that the configuration is valid (the GPIO isn't going to
+	 * the PAI).
+	 */
+	if (axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET) != ebi_mux) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	/*
+	 * If we can be SD or ARM and we want to be gpio, pick the correct
+	 * one now.
+	 */
+	if (MUX_PERIPHERAL != setting && can_be_sd)
+		pc3x3_shd_gpio_set_mux(pin->arm_pin, setting);
+
+out:
+	spin_unlock(&mux_lock);
+
+	return err;
+}
+
+/*
+ * ebi_addr[25:18] pads - these pads can be either the EBI or arm gpio or
+ * sdgpio. Note: the gpio pins can also be routed to the pai_tx/rx_data pads.
+ *
+ * A pad may only be used for GPIO if the corresponding GPIO pin is not
+ * already routed to the pai tx/rx data pad. The same applies in the reverse
+ * direction. Hardware interlocks exist to prevent this from happening.
+ */
+static struct muxed_pin ebi_addr_18_25[] = {
+PIN(ebi_addr18, PC3X3_GPIO_PIN_SDGPIO_4, PC3X3_GPIO_PIN_ARM_20,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr19, PC3X3_GPIO_PIN_SDGPIO_5, PC3X3_GPIO_PIN_ARM_21,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr20, PC3X3_GPIO_PIN_SDGPIO_6, PC3X3_GPIO_PIN_ARM_22,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr21, PC3X3_GPIO_PIN_SDGPIO_7, PC3X3_GPIO_PIN_ARM_23,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr22, PC3X3_GPIO_PIN_SDGPIO_20, PC3X3_GPIO_PIN_ARM_4,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr23, PC3X3_GPIO_PIN_SDGPIO_21, PC3X3_GPIO_PIN_ARM_5,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr24, PC3X3_GPIO_PIN_SDGPIO_22, PC3X3_GPIO_PIN_ARM_6,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr25, PC3X3_GPIO_PIN_SDGPIO_23, PC3X3_GPIO_PIN_ARM_7,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+};
+
+static struct pin_group ebi_addr_18_25_group = {
+	.nr_pins    = ARRAY_SIZE(ebi_addr_18_25),
+	.name	    = "ebi_addr[25:18]",
+	.pins	    = ebi_addr_18_25,
+};
+
+static int pc3x3_shd_pai_get_mux(struct muxed_pin *pin)
+{
+	int bit, err = 0, can_be_sd = 1;
+	unsigned long pai_mux;
+
+	spin_lock(&mux_lock);
+
+	pai_mux = axi2cfg_readl(AXI2CFG_USE_PAI_GPIO_REG_OFFSET);
+
+	/*
+	 * Find out what pai pin our GPIO maps to.
+	 */
+	if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_4 &&
+	    pin->arm_pin < PC3X3_GPIO_PIN_ARM_8) {
+		/* pai_tx_data[3:0] */
+		bit = pin->arm_pin - PC3X3_GPIO_PIN_ARM_4;
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_20 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_24) {
+		/* pai_rx_data[3:0] */
+		bit = 8 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_20);
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_24 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_28) {
+		/* pai_tx_data[7:4] */
+		bit = 4 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_24);
+		can_be_sd = 0;
+	} else {
+		/* pai_rx_data[7:4] */
+		bit = 12 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_28);
+		can_be_sd = 0;
+	}
+
+	if (!(pai_mux & (1 << bit))) {
+		err = MUX_PERIPHERAL;
+		goto out;
+	}
+
+	if (can_be_sd)
+		err = pc3x3_get_shd_mux(pin);
+	else
+		err = MUX_ARM;
+
+out:
+	spin_unlock(&mux_lock);
+
+	return err;
+}
+
+static int pc3x3_shd_pai_set_mux(struct muxed_pin *pin,
+				 enum mux_setting setting)
+{
+	int bit, err = 0, can_be_sd = 1;
+	unsigned long pai_mux;
+
+	spin_lock(&mux_lock);
+
+	pai_mux = axi2cfg_readl(AXI2CFG_USE_PAI_GPIO_REG_OFFSET);
+
+	/*
+	 * Find out what pai pin our GPIO maps to.
+	 */
+	if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_4 &&
+	    pin->arm_pin < PC3X3_GPIO_PIN_ARM_8) {
+		/* pai_tx_data[3:0] */
+		bit = pin->arm_pin - PC3X3_GPIO_PIN_ARM_4;
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_20 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_24) {
+		/* pai_rx_data[3:0] */
+		bit = 8 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_20);
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_24 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_28) {
+		/* pai_tx_data[7:4] */
+		bit = 4 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_24);
+		can_be_sd = 0;
+	} else {
+		/* pai_rx_data[7:4] */
+		bit = 12 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_28);
+		can_be_sd = 0;
+	}
+
+	if (MUX_SD == setting && !can_be_sd) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	/* Use the pin as pai. */
+	if (MUX_PERIPHERAL == setting)
+		pai_mux &= ~(1 << bit);
+	else
+		pai_mux |= (1 << bit);
+
+	axi2cfg_writel(pai_mux, AXI2CFG_USE_PAI_GPIO_REG_OFFSET);
+
+	/*
+	 * Make sure that the configuration is valid (the GPIO isn't going to
+	 * the EBI).
+	 */
+	if (axi2cfg_readl(AXI2CFG_USE_PAI_GPIO_REG_OFFSET) != pai_mux) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	/*
+	 * If we can be SD or ARM and we want to be gpio, pick the correct
+	 * one now.
+	 */
+	if (MUX_PERIPHERAL != setting && can_be_sd)
+		pc3x3_shd_gpio_set_mux(pin->arm_pin, setting);
+
+out:
+	spin_unlock(&mux_lock);
+
+	return err;
+}
+
+/*
+ * pai_rx_data[3:0] pads - these pads can be either the pai_rx_data or arm
+ * gpio or sdgpio. Note: the gpio pins can also be routed to the
+ * ebi_addr pads.
+ *
+ * A pad may only be used for GPIO if the corresponding GPIO pin is not
+ * already routed to the ebi address pad. The same applies in the reverse
+ * direction. Hardware interlocks exist to prevent this from happening.
+ */
+static struct muxed_pin pai_rx_data_0_3[] = {
+PIN(pai_rx_data0, PC3X3_GPIO_PIN_SDGPIO_4, PC3X3_GPIO_PIN_ARM_20,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data1, PC3X3_GPIO_PIN_SDGPIO_5, PC3X3_GPIO_PIN_ARM_21,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data2, PC3X3_GPIO_PIN_SDGPIO_6, PC3X3_GPIO_PIN_ARM_22,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data3, PC3X3_GPIO_PIN_SDGPIO_7, PC3X3_GPIO_PIN_ARM_23,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+};
+
+static struct pin_group pai_rx_data_0_3_group = {
+	.nr_pins    = ARRAY_SIZE(pai_rx_data_0_3),
+	.name	    = "pai_rx_data[3:0]",
+	.pins	    = pai_rx_data_0_3,
+};
+
+/*
+ * pai_tx_data[3:0] pads - these pads can be either the pai_tx_data or arm
+ * gpio or sdgpio. Note: the gpio pins can also be routed to the
+ * ebi_addr pads.
+ *
+ * A pad may only be used for GPIO if the corresponding GPIO pin is not
+ * already routed to the ebi address pad. The same applies in the reverse
+ * direction. Hardware interlocks exist to prevent this from happening.
+ */
+static struct muxed_pin pai_tx_data_0_3[] = {
+PIN(pai_tx_data0, PC3X3_GPIO_PIN_SDGPIO_20, PC3X3_GPIO_PIN_ARM_4,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data1, PC3X3_GPIO_PIN_SDGPIO_21, PC3X3_GPIO_PIN_ARM_5,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data2, PC3X3_GPIO_PIN_SDGPIO_22, PC3X3_GPIO_PIN_ARM_6,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data3, PC3X3_GPIO_PIN_SDGPIO_23, PC3X3_GPIO_PIN_ARM_7,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+};
+
+static struct pin_group pai_tx_data_0_3_group = {
+	.nr_pins    = ARRAY_SIZE(pai_tx_data_0_3),
+	.name	    = "pai_tx_data[3:0]",
+	.pins	    = pai_tx_data_0_3,
+};
+
+/*
+ * pai_tx_data[7:4] pads - these pads can either be pai_tx_data or arm gpio.
+ */
+static struct muxed_pin pai_tx_data_4_7[] = {
+PIN(pai_tx_data4, -1, PC3X3_GPIO_PIN_ARM_24, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data5, -1, PC3X3_GPIO_PIN_ARM_25, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data6, -1, PC3X3_GPIO_PIN_ARM_26, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data7, -1, PC3X3_GPIO_PIN_ARM_27, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+};
+
+static struct pin_group pai_tx_data_4_7_group = {
+	.nr_pins    = ARRAY_SIZE(pai_tx_data_4_7),
+	.name	    = "pai_tx_data[7:4]",
+	.pins	    = pai_tx_data_4_7,
+};
+
+/*
+ * pai_rx_data[7:4] pads - these pads can either be pai_rx_data or arm gpio.
+ */
+static struct muxed_pin pai_rx_data_4_7[] = {
+PIN(pai_rx_data4, -1, PC3X3_GPIO_PIN_ARM_28, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data5, -1, PC3X3_GPIO_PIN_ARM_29, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data6, -1, PC3X3_GPIO_PIN_ARM_30, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data7, -1, PC3X3_GPIO_PIN_ARM_31, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+};
+
+static struct pin_group pai_rx_data_4_7_group = {
+	.nr_pins    = ARRAY_SIZE(pai_rx_data_4_7),
+	.name	    = "pai_rx_data[7:4]",
+	.pins	    = pai_rx_data_4_7,
+};
+
+/*
+ * ebi_addr[17:14] pads - these pads can either be ebi_addr or arm gpio.
+ */
+static struct muxed_pin ebi_addr_14_17[] = {
+PIN(ebi_addr14, -1, PC3X3_GPIO_PIN_ARM_32, pc3x3_shd_ebi_set_mux,
+    pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr15, -1, PC3X3_GPIO_PIN_ARM_33, pc3x3_shd_ebi_set_mux,
+    pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr16, -1, PC3X3_GPIO_PIN_ARM_34, pc3x3_shd_ebi_set_mux,
+    pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr17, -1, PC3X3_GPIO_PIN_ARM_35, pc3x3_shd_ebi_set_mux,
+    pc3x3_shd_ebi_get_mux),
+};
+
+static struct pin_group ebi_addr_14_17_group = {
+	.nr_pins    = ARRAY_SIZE(ebi_addr_14_17),
+	.name	    = "ebi_addr[17:14]",
+	.pins	    = ebi_addr_14_17,
+};
+
+static int decode_get_mux(struct muxed_pin *pin)
+{
+	unsigned bit = pin->arm_pin - PC3X3_GPIO_PIN_ARM_36;
+	unsigned long use_decode_gpio =
+		axi2cfg_readl(AXI2CFG_USE_DECODE_GPIO_REG_OFFSET);
+
+	return use_decode_gpio & (1 << bit) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int decode_set_mux(struct muxed_pin *pin,
+			  enum mux_setting setting)
+{
+	unsigned long use_decode_gpio;
+	unsigned bit = pin->arm_pin - PC3X3_GPIO_PIN_ARM_36;
+
+	if (MUX_SD == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+
+	use_decode_gpio = axi2cfg_readl(AXI2CFG_USE_DECODE_GPIO_REG_OFFSET);
+	if (MUX_ARM == setting)
+		use_decode_gpio |= (1 << bit);
+	else
+		use_decode_gpio &= ~(1 << bit);
+	axi2cfg_writel(use_decode_gpio, AXI2CFG_USE_DECODE_GPIO_REG_OFFSET);
+
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+/*
+ * decode[3:0] pads - these pads can either be decode pins or arm gpio.
+ */
+static struct muxed_pin decode_0_3[] = {
+PIN(decode0, -1, PC3X3_GPIO_PIN_ARM_36, decode_set_mux, decode_get_mux),
+PIN(decode1, -1, PC3X3_GPIO_PIN_ARM_37, decode_set_mux, decode_get_mux),
+PIN(decode2, -1, PC3X3_GPIO_PIN_ARM_38, decode_set_mux, decode_get_mux),
+PIN(decode3, -1, PC3X3_GPIO_PIN_ARM_39, decode_set_mux, decode_get_mux),
+};
+
+static struct pin_group decode_0_3_group = {
+	.nr_pins    = ARRAY_SIZE(decode_0_3),
+	.name	    = "decode[3:0]",
+	.pins	    = decode_0_3,
+};
+
+static int ssi_set_mux(struct muxed_pin *pin, enum mux_setting setting);
+
+static int
+ssi_get_mux(struct muxed_pin *pin);
+
+/*
+ * ssi pads - these pads can either be ssi block pins or arm gpio.
+ */
+static struct muxed_pin ssi[] = {
+PIN(ssi_clk, -1, PC3X3_GPIO_PIN_ARM_40, ssi_set_mux, ssi_get_mux),
+PIN(ssi_data_in, -1, PC3X3_GPIO_PIN_ARM_41, ssi_set_mux, ssi_get_mux),
+PIN(ssi_data_out, -1, PC3X3_GPIO_PIN_ARM_42, ssi_set_mux, ssi_get_mux),
+};
+
+static int ssi_get_mux(struct muxed_pin *pin)
+{
+	unsigned long use_misc_int_gpio =
+		axi2cfg_readl(AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+
+	return use_misc_int_gpio & (1 << 0) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int ssi_set_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	unsigned long use_misc_int_gpio;
+
+	if (MUX_SD == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+
+	use_misc_int_gpio = axi2cfg_readl(AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+	if (MUX_PERIPHERAL == setting)
+		use_misc_int_gpio &= ~(1 << 0);
+	else
+		use_misc_int_gpio |= (1 << 0);
+	axi2cfg_writel(use_misc_int_gpio, AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+static struct pin_group ssi_group = {
+	.nr_pins    = ARRAY_SIZE(ssi),
+	.name	    = "ssi",
+	.pins	    = ssi,
+};
+
+static int mii_get_mux(struct muxed_pin *pin)
+{
+	unsigned long syscfg = syscfg_read();
+
+	return syscfg & (1 << 13) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int mii_set_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	/*
+	 * These are automatically configured by hardware if we are in
+	 * reduced MII mode.
+	 */
+	return -EOPNOTSUPP;
+}
+
+/*
+ * mii pads - these pads can either be mii pins or arm gpio.
+ */
+static struct muxed_pin mii[] = {
+PIN(mii_tx_data2, -1, PC3X3_GPIO_PIN_ARM_43, mii_set_mux, mii_get_mux),
+PIN(mii_tx_data3, -1, PC3X3_GPIO_PIN_ARM_44, mii_set_mux, mii_get_mux),
+PIN(mii_rx_data2, -1, PC3X3_GPIO_PIN_ARM_45, mii_set_mux, mii_get_mux),
+PIN(mii_rx_data3, -1, PC3X3_GPIO_PIN_ARM_46, mii_set_mux, mii_get_mux),
+PIN(mii_col, -1, PC3X3_GPIO_PIN_ARM_47, mii_set_mux, mii_get_mux),
+PIN(mii_crs, -1, PC3X3_GPIO_PIN_ARM_48, mii_set_mux, mii_get_mux),
+PIN(mii_tx_clk, -1, PC3X3_GPIO_PIN_ARM_49, mii_set_mux, mii_get_mux),
+};
+
+static struct pin_group mii_group = {
+	.nr_pins    = ARRAY_SIZE(mii),
+	.name	    = "mii",
+	.pins	    = mii,
+};
+
+static int max_set_mux(struct muxed_pin *pin, enum mux_setting setting);
+
+static int max_get_mux(struct muxed_pin *pin);
+
+/*
+ * maxim pads - these pads can either be maxim pins or arm gpio.
+ */
+static struct muxed_pin max[] = {
+PIN(max_tx_ctrl, -1, PC3X3_GPIO_PIN_ARM_50, max_set_mux, max_get_mux),
+PIN(max_ref_clk, -1, PC3X3_GPIO_PIN_ARM_51, max_set_mux, max_get_mux),
+PIN(max_trig_clk, -1, PC3X3_GPIO_PIN_ARM_52, max_set_mux, max_get_mux),
+};
+
+static int max_get_mux(struct muxed_pin *pin)
+{
+	unsigned long use_misc_int_gpio =
+		axi2cfg_readl(AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+
+	return use_misc_int_gpio & (1 << 1) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int max_set_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	unsigned long use_misc_int_gpio;
+
+	if (MUX_SD == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+
+	use_misc_int_gpio = axi2cfg_readl(AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+	if (MUX_PERIPHERAL == setting)
+		use_misc_int_gpio &= ~(1 << 1);
+	else
+		use_misc_int_gpio |= (1 << 1);
+	axi2cfg_writel(use_misc_int_gpio, AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+
+static struct pin_group max_group = {
+	.nr_pins    = ARRAY_SIZE(max),
+	.name	    = "mii",
+	.pins	    = max,
+};
+
+static int ebi_clk_get_mux(struct muxed_pin *pin)
+{
+	unsigned long ebi_mux = axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	return ebi_mux & (1 << 13) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int ebi_clk_set_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	unsigned long ebi_mux;
+
+	if (MUX_SD == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+
+	ebi_mux = axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+	if (MUX_PERIPHERAL == setting)
+		ebi_mux &= ~(1 << 13);
+	else
+		ebi_mux |= (1 << 13);
+	axi2cfg_writel(ebi_mux, AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+/*
+ * ebi clock pads - this pad can either be the ebi clock or an arm gpio.
+ */
+static struct muxed_pin ebi_clk_pin[] = {
+PIN(ebi_clk_pin, -1, PC3X3_GPIO_PIN_ARM_53, ebi_clk_set_mux, ebi_clk_get_mux),
+};
+
+static struct pin_group ebi_clk_group = {
+	.nr_pins    = ARRAY_SIZE(ebi_clk_pin),
+	.name	    = "ebi_clk",
+	.pins	    = ebi_clk_pin,
+};
+
+static struct muxed_pin pc3x3_fracn_pins[] = {
+PIN(sdgpio0, PC3X3_GPIO_PIN_SDGPIO_0, -1, pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group pc3x3_fracn_group = {
+	.nr_pins    = ARRAY_SIZE(pc3x3_fracn_pins),
+	.name	    = "fracn/sdgpio0",
+	.pins	    = pc3x3_fracn_pins,
+};
+
+static struct pin_group *pc3x3_groups[] = {
+	&armgpio_0_4_group,
+	&pc3x3_shd_group,
+	&boot_mode_group,
+	&sdram_speed_sel_group,
+	&mii_rev_en_group,
+	&mii_rmii_en_group,
+	&mii_speed_sel_group,
+	&ebi_addr_18_25_group,
+	&pai_tx_data_0_3_group,
+	&pai_rx_data_0_3_group,
+	&pai_tx_data_4_7_group,
+	&pai_rx_data_4_7_group,
+	&ebi_addr_14_17_group,
+	&decode_0_3_group,
+	&ssi_group,
+	&mii_group,
+	&max_group,
+	&ebi_clk_group,
+	&pc3x3_fracn_group,
+};
+
+static int armgpio_number(int pin)
+{
+	if (pin < 0 || pin > PC3X3_GPIO_PIN_ARM_53)
+		return -1;
+	return pin;
+}
+
+static int sdgpio_number(int pin)
+{
+	if (pin < PC3X3_GPIO_PIN_SDGPIO_0 ||
+	    pin > PC3X3_GPIO_PIN_SDGPIO_23)
+		return -1;
+	return pin - PC3X3_GPIO_PIN_SDGPIO_0;
+}
+
+static void pc3x3_init_muxing(void)
+{
+	picoxcell_mux_register(pc3x3_groups, ARRAY_SIZE(pc3x3_groups),
+			       armgpio_number, sdgpio_number);
+}
+
+static const char * const pc3x3_armgpio_pins[] = {
+	"arm0",
+	"arm1",
+	"arm2",
+	"arm3",
+	"arm4",
+	"arm5",
+	"arm6",
+	"arm7",
+	"arm8",
+	"arm9",
+	"arm10",
+	"arm11",
+	"arm12",
+	"arm13",
+	"arm14",
+	"arm15",
+	"arm16",
+	"arm17",
+	"arm18",
+	"arm19",
+	"arm20",
+	"arm21",
+	"arm22",
+	"arm23",
+	"arm24",
+	"arm25",
+	"arm26",
+	"arm27",
+	"arm28",
+	"arm29",
+	"arm30",
+	"arm31",
+	"arm32",
+	"arm33",
+	"arm34",
+	"arm35",
+	"arm36",
+	"arm37",
+	"arm38",
+	"arm39",
+	"arm40",
+	"arm41",
+	"arm42",
+	"arm43",
+	"arm44",
+	"arm45",
+	"arm46",
+	"arm47",
+	"arm48",
+	"arm49",
+	"arm50",
+	"arm51",
+};
+
+static const char * const pc3x3_sdgpio_pins[] = {
+	"sdgpio0",
+	"sdgpio1",
+	"sdgpio2",
+	"sdgpio3",
+	"sdgpio4",
+	"sdgpio5",
+	"sdgpio6",
+	"sdgpio7",
+	"sdgpio8",
+	"sdgpio9",
+	"sdgpio10",
+	"sdgpio11",
+	"sdgpio12",
+	"sdgpio13",
+	"sdgpio14",
+	"sdgpio15",
+	"sdgpio16",
+	"sdgpio17",
+	"sdgpio18",
+	"sdgpio19",
+	"sdgpio20",
+	"sdgpio21",
+	"sdgpio22",
+	"sdgpio23",
+};
+
+static const struct picoxcell_timer pc3x3_timers[] = {
+	{
+		.name	= "timer0",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PICOXCELL_TIMER_BASE + 0 * TIMER_SPACING,
+		.irq	= IRQ_TIMER0,
+	},
+	{
+		.name	= "timer1",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PICOXCELL_TIMER_BASE + 1 * TIMER_SPACING,
+		.irq	= IRQ_TIMER1,
+	},
+	{
+		.name	= "timer2",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PC3X3_TIMER2_BASE + 0 * TIMER_SPACING,
+		.irq	= IRQ_TIMER2,
+	},
+	{
+		.name	= "timer3",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PC3X3_TIMER2_BASE + 1 * TIMER_SPACING,
+		.irq	= IRQ_TIMER3,
+	},
+	{
+		.name	= "rtc",
+		.type	= TIMER_TYPE_RTC,
+		.base	= PICOXCELL_RTCLK_BASE,
+		.irq	= IRQ_RTC,
+	},
+};
+
+static void pc3x3_init(void);
+
+struct picoxcell_soc pc3x3_soc = {
+	.init		= pc3x3_init,
+	.init_clocks	= pc3x3_clk_init,
+	.init_io	= pc3x3_map_io,
+	.init_muxing	= pc3x3_init_muxing,
+	.get_boot_mode	= pc3x3_get_boot_mode,
+	.armgpio_pins	= pc3x3_armgpio_pins,
+	.nr_armgpio	= ARRAY_SIZE(pc3x3_armgpio_pins),
+	.armgpio_base	= PC3X3_GPIO_PIN_ARM_0,
+	.sdgpio_pins	= pc3x3_sdgpio_pins,
+	.nr_sdgpio	= ARRAY_SIZE(pc3x3_sdgpio_pins),
+	.sdgpio_base	= PC3X3_GPIO_PIN_SDGPIO_0,
+	.timers		= pc3x3_timers,
+	.nr_timers	= ARRAY_SIZE(pc3x3_timers),
+};
+
+static void pc3x3_init(void)
+{
+	/*
+	 * If we booted in parallel nor mode then we can continue to use the
+	 * EBI. If not then we only have a few bits of the EBI - just enough
+	 * to drive a NAND flash.
+	 */
+	if (syscfg_read() & 0x3)
+		set_bit(PICOXCELL_FEATURE_SW_NAND, pc3x3_soc.features);
+	else
+		set_bit(PICOXCELL_FEATURE_EBI, pc3x3_soc.features);
+	set_bit(PICOXCELL_FEATURE_PM, pc3x3_soc.features);
+	set_bit(PICOXCELL_FEATURE_CPUFREQ, pc3x3_soc.features);
+}
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 5b5f0c6..b670f6c 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -47,6 +47,11 @@ struct picoxcell_soc *picoxcell_get_soc(void)
 	case 0x8007:
 		return &pc3x2_soc;
 
+	case 0x20:
+	case 0x21:
+	case 0x22:
+		return &pc3x3_soc;
+
 	default:
 		panic("unsupported device type %lx", device_id);
 	}
@@ -101,6 +106,12 @@ static const char *picoxcell_get_partname(void)
 		part = "pc302";
 	else if (0x8007 == dev_id)
 		part = "pc312";
+	else if (0x20 == dev_id)
+		part = "pc313";
+	else if (0x21 == dev_id)
+		part = "pc323";
+	else if (0x22 == dev_id)
+		part = "pc333";
 	else
 		part = "<unknown>";
 
diff --git a/arch/arm/mach-picoxcell/soc.h b/arch/arm/mach-picoxcell/soc.h
index ec30eef..67b79f2 100644
--- a/arch/arm/mach-picoxcell/soc.h
+++ b/arch/arm/mach-picoxcell/soc.h
@@ -50,6 +50,7 @@ struct picoxcell_soc {
 
 extern struct picoxcell_soc *picoxcell_get_soc(void);
 extern struct picoxcell_soc pc3x2_soc;
+extern struct picoxcell_soc pc3x3_soc;
 
 static inline int picoxcell_has_feature(enum picoxcell_features feat)
 {
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 13/13] picoxcell: add support for the PC7302 development board
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (11 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 12/13] picoxcell: add support for pc3x3 devices Jamie Iles
@ 2010-11-23 10:06 ` Jamie Iles
  2010-11-30 18:10 ` [RFC PATCHv2 10/13] picoxcell: add chained GPIO IRQ handlers Jamie Iles
  2010-12-06 10:25 ` ARM: add initial support for Picochip picoXcell SoC Jamie Iles
  14 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-23 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

The PC7302 development board is capable of taking both PC3X2 and PC3X3
devices and features NOR flash, NAND flash, SPI NOR flash a serial
console, 100Mb Ethernet and a number of picoArray peripherals.

This patch provides initial support for running on the PC7302 board.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/configs/pc7302_defconfig      |  102 ++++++++++++++++++
 arch/arm/mach-picoxcell/Kconfig        |    9 ++
 arch/arm/mach-picoxcell/Makefile       |    1 +
 arch/arm/mach-picoxcell/board_pc7302.c |  184 ++++++++++++++++++++++++++++++++
 4 files changed, 296 insertions(+), 0 deletions(-)

diff --git a/arch/arm/configs/pc7302_defconfig b/arch/arm/configs/pc7302_defconfig
new file mode 100644
index 0000000..23a2c72
--- /dev/null
+++ b/arch/arm/configs/pc7302_defconfig
@@ -0,0 +1,102 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_RELAY=y
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_PICOXCELL=y
+CONFIG_CPU_32v6K=y
+CONFIG_ARM_ERRATA_411920=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_UACCESS_WITH_MEMCPY=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_FPE_NWFPE=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_IPV6 is not set
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/mdev"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_GPIO=y
+CONFIG_MTD_UBI=y
+# CONFIG_BLK_DEV is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_GPIO_SYSFS=y
+# CONFIG_HWMON is not set
+# CONFIG_MFD_SUPPORT is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_TMPFS=y
+CONFIG_CONFIGFS_FS=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_UBIFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_ROOT_NFS=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_STRIP_ASM_SYMS=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_BKL is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_FTRACE is not set
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_ERRORS=y
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_CRYPTO_MANAGER=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_CCITT=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig
index 671cadc..e00e14b 100644
--- a/arch/arm/mach-picoxcell/Kconfig
+++ b/arch/arm/mach-picoxcell/Kconfig
@@ -16,6 +16,15 @@ config PICOXCELL_PC3X3
 	  Include support for picoChip PC3x3 family of devices. This includes
 	  PC313, PC323 and PC333.
 
+config BOARD_PC7302
+	bool "Support PC7302 Board"
+	depends on PICOXCELL_PC3X2 || PICOXCELL_PC3X3
+	default y
+	help
+          Include support for the picoChip PC7302 platform. This platform is
+	  can take any of the PC3X2 or PC3X3 devices and includes SPI NOR
+	  flash, parallel NOR flash and NAND flash.
+
 config PC3X3_STOP_WDT_IN_SUSPEND
 	bool "Stop WDT in PM suspend"
 	depends on PICOCHIP_PC3X3
diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index d95b2b2..0138d96 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_CPU_FREQ)		+= cpufreq.o
 obj-$(CONFIG_PM)		+= pm.o
 obj-$(CONFIG_PICOXCELL_PC3X2)	+= pc3x2.o
 obj-$(CONFIG_PICOXCELL_PC3X3)	+= pc3x3.o
+obj-$(CONFIG_BOARD_PC7302)	+= board_pc7302.o
diff --git a/arch/arm/mach-picoxcell/board_pc7302.c b/arch/arm/mach-picoxcell/board_pc7302.c
new file mode 100644
index 0000000..7250601
--- /dev/null
+++ b/arch/arm/mach-picoxcell/board_pc7302.c
@@ -0,0 +1,184 @@
+/*
+ * linux/arch/arm/mach-picoxcell/board_pc7302.c
+ *
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/nand-gpio.h>
+#include <linux/mtd/physmap.h>
+#include <linux/spi/flash.h>
+
+#include <mach/hardware.h>
+#include <asm/leds.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "mux.h"
+#include "picoxcell_core.h"
+
+static long pc7302_panic_blink(int state)
+{
+	writel(state ? 0xFF : 0, IO_ADDRESS(PICOXCELL_GPIO_BASE +
+					    GPIO_SW_PORT_C_DR_REG_OFFSET));
+	return 0;
+}
+
+static void pc7302_panic_init(void)
+{
+	/*
+	 * We have a BOOT_ERROR pin on PC7302. Reuse that for signalling when
+	 * the kernel panics. There is only 1 bit wired up to port C but it
+	 * won't hurt to configure all of them.
+	 */
+	writel(0xF, IO_ADDRESS(PICOXCELL_GPIO_BASE +
+			       GPIO_SW_PORT_C_DDR_REG_OFFSET));
+	writel(0x0, IO_ADDRESS(PICOXCELL_GPIO_BASE +
+			       GPIO_SW_PORT_C_CTL_REG_OFFSET));
+
+	panic_blink = pc7302_panic_blink;
+}
+
+static struct mtd_partition pc7302_nor_partitions[] = {
+	{
+		.name		= "Boot",
+		.size		= SZ_128K,
+		.offset		= 0,
+	},
+	{
+		.name		= "Boot Environment",
+		.size		= SZ_128K,
+		.offset		= MTDPART_OFS_APPEND,
+	},
+	{
+		.name		= "Kernel",
+		.size		= SZ_4M,
+		.offset		= MTDPART_OFS_APPEND,
+	},
+	{
+		.name		= "Application",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
+	},
+};
+
+static struct physmap_flash_data pc7302_nor_flash_data = {
+	.width		= 1,
+	.parts		= pc7302_nor_partitions,
+	.nr_parts	= ARRAY_SIZE(pc7302_nor_partitions)
+};
+
+static struct resource pc7302_nor_resource = {
+	.start	= PICOXCELL_FLASH_BASE,
+	.end	= PICOXCELL_FLASH_BASE + SZ_128M - 1,
+	.flags	= IORESOURCE_MEM,
+};
+
+static struct platform_device pc7302_nor = {
+	.name		    = "physmap-flash",
+	.id		    = -1,
+	.dev.platform_data  = &pc7302_nor_flash_data,
+	.resource	    = &pc7302_nor_resource,
+	.num_resources	    = 1,
+};
+
+static void __init pc7302_init_nor(void)
+{
+	platform_device_register(&pc7302_nor);
+}
+
+static struct resource pc7302_nand_resource = {
+	.start = EBI_CS2_BASE,
+	.end   = EBI_CS2_BASE + 2 * SZ_1K,
+	.flags = IORESOURCE_MEM,
+};
+
+static struct mtd_partition pc7302_nand_parts[] = {
+	{
+		.name	= "Boot",
+		.size	= 4 * SZ_128K,
+		.offset	= 0,
+	},
+	{
+		.name	= "Redundant Boot",
+		.size	= 4 * SZ_128K,
+		.offset	= MTDPART_OFS_APPEND,
+	},
+	{
+		.name	= "Boot Environment",
+		.size	= SZ_128K,
+		.offset	= MTDPART_OFS_APPEND,
+	},
+	{
+		.name	= "Redundant Boot Environment",
+		.size	= SZ_128K,
+		.offset	= MTDPART_OFS_APPEND,
+	},
+	{
+		.name	= "Kernel",
+		.size	= 8 * SZ_1M,
+		.offset	= (12 * SZ_128K),
+	},
+	{
+		.name	= "File System",
+		.size	= MTDPART_SIZ_FULL,
+		.offset	= MTDPART_OFS_APPEND,
+	},
+};
+
+static struct gpio_nand_platdata pc7302_nand_platdata = {
+	.gpio_rdy   = PC3X2_GPIO_PIN_ARM_1,
+	.gpio_nce   = PC3X2_GPIO_PIN_ARM_2,
+	.gpio_ale   = PC3X2_GPIO_PIN_ARM_3,
+	.gpio_cle   = PC3X2_GPIO_PIN_ARM_4,
+	.gpio_nwp   = -1,
+	.parts	    = pc7302_nand_parts,
+	.num_parts  = ARRAY_SIZE(pc7302_nand_parts),
+};
+
+static struct platform_device pc7302_nand = {
+	.name		    = "gpio-nand",
+	.num_resources	    = 1,
+	.resource	    = &pc7302_nand_resource,
+	.id		    = -1,
+	.dev.platform_data  = &pc7302_nand_platdata,
+};
+
+static void __init pc7302_init_nand(void)
+{
+	platform_device_register(&pc7302_nand);
+}
+
+static void __init pc7302_init(void)
+{
+	picoxcell_core_init();
+
+	/*
+	 * If we haven't booted in parallel mode, then allow ARM.4 GPIO pin to
+	 * be used as GPIO to drive the NAND.
+	 */
+	if (picoxcell_has_feature(PICOXCELL_FEATURE_SW_NAND)) {
+		WARN_ON(picoxcell_pin_set_mux(PC3X2_GPIO_PIN_ARM_4, MUX_ARM));
+		pc7302_init_nand();
+	} else {
+		pc7302_init_nor();
+	}
+
+	pc7302_panic_init();
+}
+
+MACHINE_START(PC7302, "PC7302")
+	.boot_params	= 0x00000100,
+	.map_io		= picoxcell_map_io,
+	.init_irq	= picoxcell_init_irq,
+	.timer		= &picoxcell_sys_timer,
+	.init_machine	= pc7302_init,
+MACHINE_END
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH] picoxcell: add support for picoXcell
  2010-11-23 10:06 ` [RFC PATCH 01/13] picoxcell: add support for picoXcell Jamie Iles
@ 2010-11-26 11:08   ` Jamie Iles
  2010-12-08 16:09   ` [RFC PATCH 01/13] " Russell King - ARM Linux
  1 sibling, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-26 11:08 UTC (permalink / raw)
  To: linux-arm-kernel

picoXcell is a family of femtocell SoC devices from Picochip [1] with an
ARM subsystem.  The devices have an ARM1176JZ-S core and a DSP processor
array.  Currently there are two sub families - PC3X2 and PC3X3. The
latter includes extra power and performance control along with extra
peripherals.

This initial patch adds the hardware definitions and a framework for
adding device variants.

v2:
	- Define VMALLOC_END as a fixed address rather than relative to
	  PAGE_OFFSET
	- Reduce the number of static IO mappings
	- Put the picoXcell entry in the correct place in
	  arch/arm/Kconfig

1. http://www.picochip.com

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/Kconfig                                   |   17 ++
 arch/arm/Makefile                                  |    1 +
 arch/arm/mach-picoxcell/Kconfig                    |    4 +
 arch/arm/mach-picoxcell/Makefile                   |    1 +
 arch/arm/mach-picoxcell/Makefile.boot              |    3 +
 arch/arm/mach-picoxcell/axi2cfg.c                  |  168 ++++++++++++++++++
 arch/arm/mach-picoxcell/include/mach/debug-macro.S |   18 ++
 arch/arm/mach-picoxcell/include/mach/entry-macro.S |   19 ++
 arch/arm/mach-picoxcell/include/mach/hardware.h    |   29 +++
 arch/arm/mach-picoxcell/include/mach/io.h          |   32 ++++
 arch/arm/mach-picoxcell/include/mach/irqs.h        |   89 +++++++++
 arch/arm/mach-picoxcell/include/mach/memory.h      |   27 +++
 .../include/mach/picoxcell/axi2cfg.h               |  171 ++++++++++++++++++
 .../mach-picoxcell/include/mach/picoxcell/gpio.h   |   48 +++++
 .../include/mach/picoxcell/picoxcell.h             |   61 +++++++
 .../mach-picoxcell/include/mach/picoxcell/timer.h  |   36 ++++
 .../mach-picoxcell/include/mach/picoxcell/wdog.h   |   43 +++++
 arch/arm/mach-picoxcell/include/mach/platform.h    |   32 ++++
 arch/arm/mach-picoxcell/include/mach/system.h      |   51 ++++++
 arch/arm/mach-picoxcell/include/mach/timex.h       |   26 +++
 arch/arm/mach-picoxcell/include/mach/uncompress.h  |   56 ++++++
 arch/arm/mach-picoxcell/include/mach/vmalloc.h     |   18 ++
 arch/arm/mach-picoxcell/picoxcell_core.c           |  187 ++++++++++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.h           |   21 +++
 arch/arm/mach-picoxcell/soc.h                      |   59 ++++++
 25 files changed, 1217 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-picoxcell/Kconfig
 create mode 100644 arch/arm/mach-picoxcell/Makefile
 create mode 100644 arch/arm/mach-picoxcell/Makefile.boot
 create mode 100644 arch/arm/mach-picoxcell/axi2cfg.c
 create mode 100644 arch/arm/mach-picoxcell/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-picoxcell/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-picoxcell/include/mach/hardware.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/io.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/irqs.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/memory.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/picoxcell/axi2cfg.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/picoxcell/gpio.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/picoxcell/picoxcell.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/picoxcell/timer.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/picoxcell/wdog.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/platform.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/system.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/timex.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-picoxcell/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-picoxcell/picoxcell_core.c
 create mode 100644 arch/arm/mach-picoxcell/picoxcell_core.h
 create mode 100644 arch/arm/mach-picoxcell/soc.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 81c71da..3806af0 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -579,6 +579,21 @@ config ARCH_PNX4008
 	help
 	  This enables support for Philips PNX4008 mobile platform.
 
+config ARCH_PICOXCELL
+	bool "Picochip picoXcell"
+	select ARM_VIC
+	select HAS_TLS_REG
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select GENERIC_GPIO
+	select ARCH_REQUIRE_GPIOLIB
+	select COMMON_CLKDEV
+	select TICK_ONESHOT
+	select CPU_V6
+	help
+	  This enables support for systems based on the Picochip picoXcell
+	  family of Femtocell devices.
+
 config ARCH_PXA
 	bool "PXA2xx/PXA3xx-based"
 	depends on MMU
@@ -924,6 +939,8 @@ source "arch/arm/mach-omap2/Kconfig"
 
 source "arch/arm/mach-orion5x/Kconfig"
 
+source "arch/arm/mach-picoxcell/Kconfig"
+
 source "arch/arm/mach-pxa/Kconfig"
 source "arch/arm/plat-pxa/Kconfig"
 
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b47baa8 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -166,6 +166,7 @@ machine-$(CONFIG_ARCH_OMAP2)		:= omap2
 machine-$(CONFIG_ARCH_OMAP3)		:= omap2
 machine-$(CONFIG_ARCH_OMAP4)		:= omap2
 machine-$(CONFIG_ARCH_ORION5X)		:= orion5x
+machine-$(CONFIG_ARCH_PICOXCELL)	:= picoxcell
 machine-$(CONFIG_ARCH_PNX4008)		:= pnx4008
 machine-$(CONFIG_ARCH_PXA)		:= pxa
 machine-$(CONFIG_ARCH_REALVIEW)		:= realview
diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig
new file mode 100644
index 0000000..3daba53
--- /dev/null
+++ b/arch/arm/mach-picoxcell/Kconfig
@@ -0,0 +1,4 @@
+menu "PICOXCELL platform type"
+	depends on ARCH_PICOXCELL
+
+endmenu
diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
new file mode 100644
index 0000000..7246a6a
--- /dev/null
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -0,0 +1 @@
+obj-y				:= picoxcell_core.o axi2cfg.o
diff --git a/arch/arm/mach-picoxcell/Makefile.boot b/arch/arm/mach-picoxcell/Makefile.boot
new file mode 100644
index 0000000..67039c3
--- /dev/null
+++ b/arch/arm/mach-picoxcell/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x00800000
diff --git a/arch/arm/mach-picoxcell/axi2cfg.c b/arch/arm/mach-picoxcell/axi2cfg.c
new file mode 100644
index 0000000..5974743
--- /dev/null
+++ b/arch/arm/mach-picoxcell/axi2cfg.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ *
+ * This file implements functions for using the axi2cfg to configure and debug
+ * picoArray systems providing configuration bus access over the axi2cfg.
+ */
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include <mach/hardware.h>
+
+/* Configuration port write bit positions. */
+#define CAEID_BIT_MASK	    (1 << 19)	/* AE ID signal. */
+#define CADDR_BIT_MASK	    (1 << 18)	/* AE ADDR signal. */
+#define CREAD_BIT_MASK	    (1 << 17)	/* READ data signal. */
+#define CWRITE_BIT_MASK     (1 << 16)	/* WRITE data signal. */
+
+#define RB_FAIL_MASK	    (1 << 17)	/* Readback failed. */
+#define RB_VALID_MASK	    (1 << 16)	/* Readback valid. */
+
+#define NR_RETRIES	    16		/* The number of retries for an
+					 * AXI2Cfg config read. */
+
+static DEFINE_SPINLOCK(axi2cfg_lock);
+
+static void __iomem *axi2cfg;
+
+#define CFG_WRITE_PORT	    0x100	/* Write port offset. */
+#define CFG_READ_PORT	    0x200	/* Read port offset. */
+
+int
+axi2cfg_config_read(u16 aeid, u16 ae_addr, u16 *buf, u16 count)
+{
+	u32 val;
+	void __iomem *write_p = axi2cfg + CFG_WRITE_PORT;
+	void __iomem *read_p = axi2cfg + CFG_READ_PORT;
+	u16 rc, to_read = count;
+	unsigned i, retries;
+	unsigned long flags;
+
+	spin_lock_irqsave(&axi2cfg_lock, flags);
+
+	val = aeid | CAEID_BIT_MASK;
+	writel(val, write_p);
+
+	while (to_read) {
+		/* Output the address to read from. */
+		val = (ae_addr + (count - to_read)) | CADDR_BIT_MASK;
+		writel(val, write_p);
+
+		/* Dispatch the read requests. We have a 64 entry FIFO. */
+		rc = min_t(u16, to_read, 64);
+		val = CREAD_BIT_MASK | rc;
+		writel(val, write_p);
+		wmb();
+
+		/* Now read the values. */
+		for (i = 0; i < rc; ++i) {
+			retries = NR_RETRIES;
+			while (retries) {
+				val = readl(read_p);
+				if (val & (RB_VALID_MASK | RB_FAIL_MASK))
+					break;
+				--retries;
+				rmb();
+			}
+
+			if (!retries || (val & RB_FAIL_MASK)) {
+				pr_warning("config read %04x@%04x failed\n",
+					   aeid,
+					   (ae_addr + (count - to_read) + i));
+				break;
+			} else
+				buf[(count - to_read) + i] = val & 0xFFFF;
+		}
+
+		if (val & RB_FAIL_MASK)
+			break;
+
+		to_read -= rc;
+	}
+
+	spin_unlock_irqrestore(&axi2cfg_lock, flags);
+
+	return !(val & RB_FAIL_MASK) ? count : -EIO;
+}
+EXPORT_SYMBOL_GPL(axi2cfg_config_read);
+
+void
+axi2cfg_config_write(u16 aeid, u16 ae_addr, const u16 *buf, u16 count)
+{
+	u32 val;
+	void __iomem *write_p = axi2cfg + CFG_WRITE_PORT;
+	unsigned i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&axi2cfg_lock, flags);
+
+	val = aeid | CAEID_BIT_MASK;
+	writel(val, write_p);
+
+	/* Output the address to read from. */
+	val = ae_addr | CADDR_BIT_MASK;
+	writel(val, write_p);
+
+	/* Now read the values. */
+	for (i = 0; i < count; ++i) {
+		val = buf[i] | CWRITE_BIT_MASK;
+		writel(val, write_p);
+	}
+
+	spin_unlock_irqrestore(&axi2cfg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(axi2cfg_config_write);
+
+void
+axi2cfg_write_buf(const u32 *buf, unsigned nr_words)
+{
+	void __iomem *write_p = axi2cfg + CFG_WRITE_PORT;
+	unsigned i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&axi2cfg_lock, flags);
+
+	/* Now read the values. */
+	for (i = 0; i < nr_words; ++i)
+		writel(*buf++, write_p);
+
+	spin_unlock_irqrestore(&axi2cfg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(axi2cfg_write_buf);
+
+unsigned long axi2cfg_readl(unsigned long offs)
+{
+	return readl(axi2cfg + offs);
+}
+
+void axi2cfg_writel(unsigned long val, unsigned long offs)
+{
+	writel(val, axi2cfg + offs);
+}
+
+u32 syscfg_read(void)
+{
+	return axi2cfg_readl(AXI2CFG_SYSCFG_REG_OFFSET);
+}
+
+void syscfg_update(u32 mask, u32 val)
+{
+	u32 tmp = syscfg_read();
+	tmp &= ~mask;
+	tmp |= (val & mask);
+	axi2cfg_writel(tmp, AXI2CFG_SYSCFG_REG_OFFSET);
+}
+
+void __init axi2cfg_init(void)
+{
+	axi2cfg = ioremap(PICOXCELL_AXI2CFG_BASE, 0x300);
+	BUG_ON(!axi2cfg);
+}
diff --git a/arch/arm/mach-picoxcell/include/mach/debug-macro.S b/arch/arm/mach-picoxcell/include/mach/debug-macro.S
new file mode 100644
index 0000000..5cbab63
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/debug-macro.S
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+*/
+
+		.macro	addruart, rp, rv
+		mov	\rv, #0x00230000
+		orr	\rp, \rv, #0x80000000
+		orr	\rv, \rv, #0xFE000000
+		.endm
+
+#define UART_SHIFT 2
+#define DEBUG_8250_ACCESS_32
+#include <asm/hardware/debug-8250.S>
diff --git a/arch/arm/mach-picoxcell/include/mach/entry-macro.S b/arch/arm/mach-picoxcell/include/mach/entry-macro.S
new file mode 100644
index 0000000..4dcda96
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/entry-macro.S
@@ -0,0 +1,19 @@
+/*
+ * entry-macro.S
+ *
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * Low-level IRQ helper macros for picoXcell platforms
+ *
+ * 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.
+ */
+#include <mach/hardware.h>
+#include <mach/io.h>
+#include <mach/irqs.h>
+
+#define VA_VIC0		IO_ADDRESS(PICOXCELL_VIC0_BASE)
+#define VA_VIC1		IO_ADDRESS(PICOXCELL_VIC1_BASE)
+
+#include <asm/entry-macro-vic2.S>
diff --git a/arch/arm/mach-picoxcell/include/mach/hardware.h b/arch/arm/mach-picoxcell/include/mach/hardware.h
new file mode 100644
index 0000000..7c203d3
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/hardware.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * This file contains the hardware definitions of the picoXcell SoC devices.
+ *
+ * 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 <mach/picoxcell/axi2cfg.h>
+#include <mach/picoxcell/gpio.h>
+#include <mach/picoxcell/picoxcell.h>
+#include <mach/picoxcell/timer.h>
+#include <mach/picoxcell/wdog.h>
+
+#endif
diff --git a/arch/arm/mach-picoxcell/include/mach/io.h b/arch/arm/mach-picoxcell/include/mach/io.h
new file mode 100644
index 0000000..3d61379
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/io.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define PHYS_TO_IO(x)		(((x) & 0x00ffffff) | 0xfe000000)
+#ifdef __ASSEMBLY__
+# define IO_ADDRESS(x)		PHYS_TO_IO((x))
+#else /* __ASSEMBLY__ */
+# define IO_ADDRESS(x)		__typesafe_io(PHYS_TO_IO((x)))
+# define IO_SPACE_LIMIT		0xffffffff
+# define __io(a)		__typesafe_io(a)
+# define __mem_pci(a)		(a)
+# define iomem_ptr(a)		(void __iomem __force *)(PHYS_TO_IO((a)))
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ARM_ARCH_IO_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/irqs.h b/arch/arm/mach-picoxcell/include/mach/irqs.h
new file mode 100644
index 0000000..0fe84ff
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/irqs.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 __IRQS_H__
+#define __IRQS_H__
+
+/* VIC0 IRQ Indexes */
+#define IRQ_VIC0_BASE	    32
+#define IRQ_EMAC	    (31 + IRQ_VIC0_BASE)
+#define IRQ_NPMUIRQ	    (30 + IRQ_VIC0_BASE)
+#define IRQ_NDMAEXTERRIRQ   (29 + IRQ_VIC0_BASE)
+#define IRQ_NDMASIRQ	    (28 + IRQ_VIC0_BASE)
+#define IRQ_NDMAIRQ	    (27 + IRQ_VIC0_BASE)
+#define IRQ_DMAC2	    (26 + IRQ_VIC0_BASE)
+#define IRQ_DMAC1	    (25 + IRQ_VIC0_BASE)
+#define IRQ_IPSEC	    (24 + IRQ_VIC0_BASE)
+#define IRQ_SRTP	    (23 + IRQ_VIC0_BASE)
+#define IRQ_AES		    (22 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO8	    (21 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO7	    (20 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO6	    (19 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO5	    (18 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO4	    (17 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO3	    (16 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO2	    (15 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO1	    (14 + IRQ_VIC0_BASE)
+#define IRQ_AXI2PICO0	    (13 + IRQ_VIC0_BASE)
+#define IRQ_AXI2CFG	    (12 + IRQ_VIC0_BASE)
+#define IRQ_WDG		    (11 + IRQ_VIC0_BASE)
+#define IRQ_SSI		    (10 + IRQ_VIC0_BASE)
+#define IRQ_AXI_RD_ERR	    (9	+ IRQ_VIC0_BASE)
+#define IRQ_AXI_WR_ERR	    (8	+ IRQ_VIC0_BASE)
+#define IRQ_TIMER3	    (7	+ IRQ_VIC0_BASE)
+#define IRQ_TIMER2	    (6	+ IRQ_VIC0_BASE)
+#define IRQ_TIMER1	    (5	+ IRQ_VIC0_BASE)
+#define IRQ_TIMER0	    (4	+ IRQ_VIC0_BASE)
+#define IRQ_COMMTX	    (3	+ IRQ_VIC0_BASE)
+#define IRQ_COMMRX	    (2	+ IRQ_VIC0_BASE)
+#define IRQ_SWI		    (1	+ IRQ_VIC0_BASE)
+
+/* VIC1 IRQ Indexes */
+#define IRQ_VIC1_BASE	    0
+#define IRQ_UART1	    (10 + IRQ_VIC1_BASE)
+#define IRQ_UART2	    (9 + IRQ_VIC1_BASE)
+#define IRQ_RTC		    (8 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO7	    (7 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO6	    (6 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO5	    (5 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO4	    (4 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO3	    (3 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO2	    (2 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO1	    (1 + IRQ_VIC1_BASE)
+#define __IRQ_GPIO0	    (0 + IRQ_VIC1_BASE)
+
+/*
+ * Virtual GPIO interrupts.
+ *
+ * We want to enable/disable interrupts for the GPIO pins through the GPIO
+ * block itself. To do this we install a chained handler. If a user requests
+ * one of the __IRQ_GPIOn interrupts then the GPIO block won't get configured.
+ * We provide these interrupts below as virtual ones that will configure the
+ * GPIO block and enable the source in the VIC.
+ */
+#define IRQ_GPIO7	    71
+#define IRQ_GPIO6	    70
+#define IRQ_GPIO5	    69
+#define IRQ_GPIO4	    68
+#define IRQ_GPIO3	    67
+#define IRQ_GPIO2	    66
+#define IRQ_GPIO1	    65
+#define IRQ_GPIO0	    64
+
+#define NR_IRQS		    72
+
+#endif /* __IRQS_H__ */
diff --git a/arch/arm/mach-picoxcell/include/mach/memory.h b/arch/arm/mach-picoxcell/include/mach/memory.h
new file mode 100644
index 0000000..47b8ba1
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/memory.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
+
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/axi2cfg.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/axi2cfg.h
new file mode 100644
index 0000000..18a4ac8
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/axi2cfg.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 PICOXCELL_AXI2CFG_H
+#define PICOXCELL_AXI2CFG_H
+
+#define AXI2CFG_SYSCFG_REG_OFFSET		0x0000
+#define AXI2CFG_JTAG_ISC_REG_OFFSET		0x0004
+#define AXI2CFG_IRQ_REG_OFFSET			0x0008
+#define AXI2CFG_PURGE_CFG_PORT_REG_OFFSET	0x000C
+#define AXI2CFG_DMA_CFG_REG_OFFSET		0x0010
+#define AXI2CFG_DEVICE_ID_REG_OFFSET		0x0014
+#define AXI2CFG_REVISION_ID_REG_OFFSET		0x0018
+#define AXI2CFG_AXI_ERR_ENABLE_REG_OFFSET	0x001C
+#define AXI2CFG_AXI_ERR_CLEAR_REG_OFFSET	0x0020
+#define AXI2CFG_AXI_ERR_MASK_REG_OFFSET		0x0024
+#define AXI2CFG_AXI_ERR_TEST_REG_OFFSET		0x0028
+#define AXI2CFG_AXI_ERR_RAW_REG_OFFSET		0x002C
+#define AXI2CFG_AXI_ERR_STATE_REG_OFFSET	0x0030
+#define AXI2CFG_CLOCK_GATING_REG_OFFSET		0x0048
+#define AXI2CFG_CONFIG_WRITE_REG_OFFSET		0x0100
+#define AXI2CFG_CONFIG_READ_REG_OFFSET		0x0200
+#define AXI2CFG_DMAC1_CONFIG_REG_OFFSET		0x0300
+
+#define AXI2CFG_SYSCFG_PA_RST_IDX		30
+#define AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_SZ	8
+#define AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_HI	23
+#define AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_LO	16
+#define AXI2CFG_SYSCFG_RW_EBI_CLK_DISABLE_IDX	15
+#define AXI2CFG_SYSCFG_RW_EXCVEC_EN_IDX		14
+#define AXI2CFG_SYSCFG_RW_RMII_EN_IDX		13
+#define AXI2CFG_SYSCFG_RW_REVMII_EN_IDX		12
+#define AXI2CFG_SYSCFG_SSI_EBI_SEL_SZ		4
+#define AXI2CFG_SYSCFG_SSI_EBI_SEL_HI		11
+#define AXI2CFG_SYSCFG_SSI_EBI_SEL_LO		8
+#define AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_IDX	7
+#define AXI2CFG_SYSCFG_MASK_AXI_ERR_IDX		6
+#define AXI2CFG_SYSCFG_RW_REMAP_IDX		5
+#define AXI2CFG_SYSCFG_WDG_PAUSE_IDX		4
+#define AXI2CFG_SYSCFG_CP15DISABLE_IDX		3
+#define AXI2CFG_SYSCFG_DMAC1_CH7_IDX		2
+#define AXI2CFG_SYSCFG_BOOT_MODE_SZ		2
+#define AXI2CFG_SYSCFG_BOOT_MODE_HI		1
+#define AXI2CFG_SYSCFG_BOOT_MODE_LO		0
+
+#define AXI2CFG_SYSCFG_PA_RST_MASK \
+	(1 << AXI2CFG_SYSCFG_PA_RST_IDX)
+#define AXI2CFG_SYSCFG_SD_ARM_GPIO_MASK	\
+	(((1 << AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_SZ) - 1) << \
+	 AXI2CFG_SYSCFG_SD_ARM_GPIO_SEL_LO)
+#define AXI2CFG_SYSCFG_RW_EXCVEC_EN_MASK \
+	(1 << AXI2CFG_SYSCFG_RW_EXCVEC_EN_IDX)
+#define AXI2CFG_SYSCFG_RW_RMII_EN_MASK \
+	(1 << AXI2CFG_SYSCFG_RW_RMII_EN_IDX)
+#define AXI2CFG_SYSCFG_RW_REVMII_EN_MASK \
+	(1 << AXI2CFG_SYSCFG_RW_REVMII_EN_IDX)
+#define AXI2CFG_SYSCFG_SSI_EBI_SEL_MASK	\
+	(((1 << AXI2CFG_SYSCFG_SSI_EBI_SEL_SZ) - 1) << \
+	 AXI2CFG_SYSCFG_SSI_EBI_SEL_LO)
+#define AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK \
+	(1 << AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_IDX)
+#define AXI2CFG_SYSCFG_MASK_AXI_ERR_MASK \
+	(1 << AXI2CFG_SYSCFG_MASK_AXI_ERR_IDX)
+#define AXI2CFG_SYSCFG_RW_REMAP_MASK \
+	(1 << AXI2CFG_SYSCFG_RW_REMAP_IDX)
+#define AXI2CFG_SYSCFG_WDG_PAUSE_MASK \
+	(1 << AXI2CFG_SYSCFG_WDG_PAUSE_IDX)
+#define AXI2CFG_SYSCFG_CP15DISABLE_MASK	\
+	(1 << AXI2CFG_SYSCFG_CP15DISABLE_IDX)
+#define AXI2CFG_SYSCFG_DMAC1_CH7_MASK \
+	(1 << AXI2CFG_SYSCFG_DMAC1_CH7_IDX)
+#define AXI2CFG_SYSCFG_BOOT_MODE_MASK \
+	(((1 << AXI2CFG_SYSCFG_BOOT_MODE_SZ) - 1) << \
+	 AXI2CFG_SYSCFG_BOOT_MODE_LO)
+
+#define AXI2CFG_AXI_RD_ERR_MASK			   0x00000FFF
+#define AXI2CFG_AXI_WR_ERR_MASK			   0x00FFF000
+#define AXI2CFG_AXI_ERR_MASK_NONE		   0
+#define AXI2CFG_AXI_ERR_ENABLE_ALL		   0x00FFFFFF
+
+#ifndef __ASSEMBLY__
+
+#include <linux/init.h>
+
+/*
+ * axi2cfg_config_read - Read a number of 16 bit words from a picoArray axi2cfg.
+ *
+ * Returns the number of words read on success, negative errno on failure.
+ *
+ * @axi2cfg_base: The base address of the upper axi2cfg.
+ * @aeid: The CAEID of the AE to read from.
+ * @ae_addr: The address to begin reading from within the AE.
+ * @buf: The buffer to store the results in.
+ * @count: The number of 16 bit words to read.
+ */
+extern int axi2cfg_config_read(u16 aeid, u16 ae_addr, u16 *buf, u16 count);
+
+/*
+ * axi2cfg_config_write - Write a number of 16 bit words to a picoArray axi2cfg.
+ *
+ * @axi2cfg_base: The base address of the upper axi2cfg.
+ * @aeid: The CAEID of the AE to write to.
+ * @ae_addr: The address to begin writing to within the AE.
+ * @buf: The buffer to read the words from.
+ * @count: The number of 16 bit words to write.
+ */
+extern void axi2cfg_config_write(u16 aeid, u16 ae_addr, const u16 *buf,
+				 u16 count);
+
+/*
+ * ax2cfg_write_buf - Write a series of configuration words to the AXI2CFG
+ *	config write port.
+ *
+ * @buf: The buffer to write.
+ * @nr_words: The number of 32 bit words to write.
+ */
+extern void axi2cfg_write_buf(const u32 *buf, unsigned nr_words);
+
+/*
+ * axi2cfg_init - initialize the AXI2CFG hardware.
+ */
+extern void __init axi2cfg_init(void);
+
+/*
+ * axi2cfg_readl - read a register in the axi2cfg.
+ *
+ * Returns the value of the register.
+ *
+ * @offs: the byte offset to read from.
+ */
+extern unsigned long axi2cfg_readl(unsigned long offs);
+
+/*
+ * axi2cfg_writel - write an axi2cfg AXI domain register.
+ *
+ * @val: the value to write.
+ * @offs: the byte offset to write to.
+ */
+extern void axi2cfg_writel(unsigned long val, unsigned long offs);
+
+/*
+ * syscfg_read - read the system configuration register.
+ */
+u32 syscfg_read(void);
+
+/*
+ * syscfg_update - update specific bits in the syscfg register
+ *
+ * @mask: the bitmask for bits to update
+ * @val: the value to write to the register.
+ */
+void syscfg_update(u32 mask, u32 val);
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* PICOXCELL_AXI2CFG_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/gpio.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/gpio.h
new file mode 100644
index 0000000..f526271
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/gpio.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 PICOXCELL_GPIO_H
+#define PICOXCELL_GPIO_H
+
+#define GPIO_SW_PORT_A_DR_REG_OFFSET	    0x00
+#define GPIO_SW_PORT_A_DDR_REG_OFFSET	    0x04
+#define GPIO_SW_PORT_A_CTL_REG_OFFSET	    0x08
+#define GPIO_SW_PORT_B_DR_REG_OFFSET	    0x0C
+#define GPIO_SW_PORT_B_DDR_REG_OFFSET	    0x10
+#define GPIO_SW_PORT_B_CTL_REG_OFFSET	    0x14
+#define GPIO_SW_PORT_C_DR_REG_OFFSET	    0x18
+#define GPIO_SW_PORT_C_DDR_REG_OFFSET	    0x1C
+#define GPIO_SW_PORT_C_CTL_REG_OFFSET	    0x20
+#define GPIO_SW_PORT_D_DR_REG_OFFSET	    0x24
+#define GPIO_SW_PORT_D_DDR_REG_OFFSET	    0x28
+#define GPIO_SW_PORT_D_CTL_REG_OFFSET	    0x2C
+
+#define GPIO_INT_EN_REG_OFFSET		    0x30
+#define GPIO_INT_MASK_REG_OFFSET	    0x34
+#define GPIO_INT_TYPE_LEVEL_REG_OFFSET	    0x38
+#define GPIO_INT_POLARITY_REG_OFFSET	    0x3c
+
+#define GPIO_INT_STATUS_REG_OFFSET	    0x40
+
+#define GPIO_PORT_A_EOI_REG_OFFSET	    0x4c
+#define GPIO_EXT_PORT_A_REG_OFFSET	    0x50
+#define GPIO_EXT_PORT_B_REG_OFFSET	    0x54
+#define GPIO_EXT_PORT_C_REG_OFFSET	    0x58
+#define GPIO_EXT_PORT_D_REG_OFFSET	    0x5C
+
+#endif /* PICOXCELL_GPIO_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/picoxcell.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/picoxcell.h
new file mode 100644
index 0000000..7f1917a
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/picoxcell.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 __PICOXCELL_H__
+#define __PICOXCELL_H__
+
+#define BOOT_ROM_BASE			0xFFFF0000
+#define BOOT_ROM_SIZE			0x400
+#define AXI2PICO_BUFFERS_BASE		0xC0000000
+#define AXI2PICO_BUFFERS_SIZE		0x00010000
+#define PICOXCELL_PERIPH_BASE		0x80000000
+#define PICOXCELL_PERIPH_LENGTH		0x00400000
+#define PICOXCELL_MEMIF_BASE		0x80000000
+#define PICOXCELL_EBI_BASE		0x80010000
+#define PICOXCELL_EMAC_BASE		0x80030000
+#define PICOXCELL_DMAC1_BASE		0x80040000
+#define PICOXCELL_DMAC2_BASE		0x80050000
+#define PICOXCELL_VIC0_BASE		0x80060000
+#define PICOXCELL_VIC1_BASE		0x80064000
+#define PICOXCELL_TZIC_BASE		0x80068000
+#define PICOXCELL_TZPC_BASE		0x80070000
+#define PICOXCELL_FUSE_BASE		0x80080000
+#define PICOXCELL_SSI_BASE		0x80090000
+#define PICOXCELL_AXI2CFG_BASE		0x800A0000
+#define PICOXCELL_IPSEC_BASE		0x80100000
+#define PICOXCELL_SRTP_BASE		0x80140000
+#define PICOXCELL_CIPHER_BASE		0x80180000
+#define PICOXCELL_RTCLK_BASE		0x80200000
+#define PICOXCELL_TIMER_BASE		0x80210000
+#define PICOXCELL_GPIO_BASE		0x80220000
+#define PICOXCELL_UART1_BASE		0x80230000
+#define PICOXCELL_UART2_BASE		0x80240000
+#define PICOXCELL_WDOG_BASE		0x80250000
+#define PC3X3_RNG_BASE			0x800B0000
+#define PC3X3_TIMER2_BASE		0x80260000
+#define PC3X3_OTP_BASE			0xFFFF8000
+
+#define EBI_CS0_BASE			0x40000000
+#define EBI_CS1_BASE			0x48000000
+#define EBI_CS2_BASE			0x50000000
+#define EBI_CS3_BASE			0x58000000
+
+#define SRAM_BASE			0x20000000
+#define SRAM_START			0x20000000
+#define SRAM_SIZE			0x00020000
+
+#endif /* __PICOXCELL_H__ */
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/timer.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/timer.h
new file mode 100644
index 0000000..3daf6b2
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/timer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 PICOXCELL_TIMER_H
+#define PICOXCELL_TIMER_H
+
+/* The spacing between individual timers. */
+#define TIMER_SPACING			    0x14
+
+#define TIMER_LOAD_COUNT_REG_OFFSET	    0x00
+#define TIMER_CONTROL_REG_OFFSET	    0x08
+#define TIMER_EOI_REG_OFFSET		    0x0c
+
+#define TIMERS_EOI_REG_OFFSET		    0xa4
+
+#define TIMER_ENABLE			    0x00000001
+#define TIMER_MODE			    0x00000002
+#define TIMER_INTERRUPT_MASK		    0x00000004
+
+#define RTCLK_CCV_REG_OFFSET		    0x00
+
+#endif /* PICOXCELL_TIMER_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/picoxcell/wdog.h b/arch/arm/mach-picoxcell/include/mach/picoxcell/wdog.h
new file mode 100644
index 0000000..727780c
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/picoxcell/wdog.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 PICOXCELL_WDOG_H
+#define PICOXCELL_WDOG_H
+
+#define WDOG_CONTROL_REG_OFFSET		    0x00
+#define WDOG_TIMEOUT_RANGE_REG_OFFSET	    0x04
+#define WDOG_CURRENT_COUNT_REG_OFFSET	    0x08
+#define WDOG_COUNTER_RESTART_REG_OFFSET     0x0c
+#define WDOG_INT_STATUS_REG_OFFSET	    0x10
+#define WDOG_CLEAR_REG_OFFSET		    0x14
+
+#define WDOG_CONTROL_REG_RESET		    0x00000016
+#define WDOG_TIMEOUT_RANGE_REG_RESET	    0x0000000c
+#define WDOG_CURRENT_COUNT_REG_RESET	    0x0fffffff
+#define WDOG_COUNTER_RESTART_REG_RESET	    0x00000000
+#define WDOG_INT_STATUS_REG_RESET	    0x00000000
+#define WDOG_CLEAR_REG_RESET		    0x00000000
+
+#define WDOGCONTROLREGWDT_ENIDX		    0
+#define WDOGCONTROLREGRMODIDX		    1
+#define WDOGCONTROLREGRPLIDX		    2
+
+#define WDOG_CONTROL_REG_WDT_EN_MASK	    (1 << WDOGCONTROLREGWDT_ENIDX)
+#define WDOG_CONTROL_REG_RMOD_MASK	    (1 << WDOGCONTROLREGRMODIDX)
+#define WDOG_CONTROL_REG_RPL_MASK	    (0x7 << WDOGCONTROLREGRPLIDX)
+
+#endif /* PICOXCELL_WDOG_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/platform.h b/arch/arm/mach-picoxcell/include/mach/platform.h
new file mode 100644
index 0000000..ef7900f
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/platform.h
@@ -0,0 +1,32 @@
+/*
+ * platform.h
+ *
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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_PICOXCELL_PLATFORM_H
+#define __ARCH_PICOXCELL_PLATFORM_H
+
+/* Physical address of the Flash in the ARM sub-system memory map */
+#define PICOXCELL_FLASH_BASE	    0x40000000
+
+/* The clock frequency for the UARTs */
+#define PICOXCELL_BASE_BAUD	    3686400	/* 3.6864 MHz */
+
+/* The clock frequency for the timers on the various boards */
+#define PICOXCELL_TIMER_FREQ	    200000000	/* 200 MHz */
+
+#endif /* __ARCH_PICOXCELL_PLATFORM_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/system.h b/arch/arm/mach-picoxcell/include/mach/system.h
new file mode 100644
index 0000000..73d889d
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/system.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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/io.h>
+#include <mach/picoxcell/picoxcell.h>
+#include <mach/picoxcell/wdog.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(int mode, const char *cmd)
+{
+	/*
+	 * Set the watchdog to expire as soon as possible and reset the
+	 * system.
+	 */
+	writel(WDOG_CONTROL_REG_WDT_EN_MASK,
+	       IO_ADDRESS(PICOXCELL_WDOG_BASE + WDOG_CONTROL_REG_OFFSET));
+	writel(0, IO_ADDRESS(PICOXCELL_WDOG_BASE +
+			     WDOG_TIMEOUT_RANGE_REG_OFFSET));
+
+	/* Give it chance to reset. */
+	mdelay(500);
+
+	pr_crit("watchdog reset failed - entering infinite loop\n");
+}
+
+#endif /* __ASM_ARCH_SYSTEM_H */
diff --git a/arch/arm/mach-picoxcell/include/mach/timex.h b/arch/arm/mach-picoxcell/include/mach/timex.h
new file mode 100644
index 0000000..57740c5
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 __TIMEX_H__
+#define __TIMEX_H__
+
+#include <mach/platform.h>
+
+#define CLOCK_TICK_RATE		PICOXCELL_TIMER_FREQ
+
+#endif /* __TIMEX_H__ */
+
diff --git a/arch/arm/mach-picoxcell/include/mach/uncompress.h b/arch/arm/mach-picoxcell/include/mach/uncompress.h
new file mode 100644
index 0000000..ccaf3d5
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/uncompress.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 <asm/processor.h>
+#include <linux/io.h>
+#include <linux/serial_reg.h>
+#include <mach/hardware.h>
+
+#define UART_SHIFT  2
+
+static inline void putc(int c)
+{
+	void __iomem *uart = (void __iomem *)(PICOXCELL_UART1_BASE);
+
+	while (!(readl(uart + (UART_LSR << UART_SHIFT)) & UART_LSR_THRE))
+		barrier();
+	writel(c & 0xFF, uart + (UART_TX << UART_SHIFT));
+}
+
+static inline void flush(void)
+{
+}
+
+static inline void arch_decomp_setup(void)
+{
+	void __iomem *uart = (void __iomem *)(PICOXCELL_UART1_BASE);
+
+	/* Reset and enable the FIFO's. */
+	writel(UART_FCR_ENABLE_FIFO, uart + (UART_FCR << UART_SHIFT));
+
+	/* Wait for the FIFO's to be enabled. */
+	while (!(readl(uart + (UART_FCR << UART_SHIFT)) & UART_FCR_TRIGGER_14))
+		cpu_relax();
+	/* Enable divisor access, set length to 8 bits. */
+	writel(UART_LCR_DLAB | UART_LCR_WLEN8, uart + (UART_LCR << UART_SHIFT));
+	/* Set for 115200 baud. */
+	writel(0x2, uart + (UART_DLL << UART_SHIFT));
+	writel(0x0, uart + (UART_DLM << UART_SHIFT));
+	writel(UART_LCR_WLEN8, uart + (UART_LCR << UART_SHIFT));
+}
+
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-picoxcell/include/mach/vmalloc.h b/arch/arm/mach-picoxcell/include/mach/vmalloc.h
new file mode 100644
index 0000000..09a7f75
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/vmalloc.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 VMALLOC_END		0xFE000000UL
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
new file mode 100644
index 0000000..f97919d
--- /dev/null
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support@picochip.com
+ */
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/sysdev.h>
+
+#include <asm/hardware/vic.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+
+#include <mach/hardware.h>
+
+#include "mux.h"
+#include "picoxcell_core.h"
+#include "soc.h"
+
+struct picoxcell_soc *picoxcell_get_soc(void)
+{
+	unsigned long device_id = readl(IO_ADDRESS(PICOXCELL_AXI2CFG_BASE +
+					AXI2CFG_DEVICE_ID_REG_OFFSET));
+	switch (device_id) {
+	default:
+		panic("unsupported device type %lx", device_id);
+	}
+}
+
+void __init picoxcell_init_irq(void)
+{
+	u32 vic0_resume_sources =
+		(1 << (IRQ_AXI2PICO8 & 31)) |
+		(1 << (IRQ_EMAC & 31)) |
+		(1 << (IRQ_WDG & 31));
+
+	vic_init(IO_ADDRESS(PICOXCELL_VIC0_BASE), 32, 0xFFFFFFFE,
+		 vic0_resume_sources);
+	vic_init(IO_ADDRESS(PICOXCELL_VIC1_BASE), 0, 0x7FF, 0);
+}
+
+static const char *picoxcell_get_partname(void)
+{
+	unsigned long dev_id = axi2cfg_readl(AXI2CFG_DEVICE_ID_REG_OFFSET);
+	const char *part = "<unknown>";
+
+	return part;
+}
+
+static inline unsigned long picoxcell_get_revision(void)
+{
+	return axi2cfg_readl(AXI2CFG_REVISION_ID_REG_OFFSET);
+}
+
+static struct sysdev_class soc_sysdev_class = {
+	.name		= "soc",
+};
+
+static struct sys_device soc_sysdev_device = {
+	.id		= 0,
+	.cls		= &soc_sysdev_class,
+};
+
+static ssize_t part_show(struct sys_device *sysdev,
+			 struct sysdev_attribute *attr, char *buf)
+{
+	const char *part = picoxcell_get_partname();
+	return snprintf(buf, PAGE_SIZE, "%s\n", part);
+}
+static SYSDEV_ATTR(part, 0444, part_show, NULL);
+
+static ssize_t boot_mode_show(struct sys_device *sysdev,
+			      struct sysdev_attribute *attr, char *buf)
+{
+	struct picoxcell_soc *soc = picoxcell_get_soc();
+
+	return soc->get_boot_mode(buf);
+}
+static SYSDEV_ATTR(boot_mode, 0444, boot_mode_show, NULL);
+
+static ssize_t revision_show(struct sys_device *sysdev,
+			     struct sysdev_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%lu\n", picoxcell_get_revision());
+}
+static SYSDEV_ATTR(revision, 0444, revision_show, NULL);
+
+static ssize_t die_ident_show(struct sys_device *sysdev,
+			      struct sysdev_attribute *attr, char *buf)
+{
+	int i;
+
+	for (i = 0; i < 16; ++i)
+		snprintf(buf + (i * 2), PAGE_SIZE, "%02x",
+			 readb(IO_ADDRESS(PICOXCELL_FUSE_BASE + 0x60 + i)));
+	snprintf(buf + (i * 2), PAGE_SIZE, "\n");
+
+	return 33;
+}
+static SYSDEV_ATTR(die_ident, 0444, die_ident_show, NULL);
+
+static struct __init sysdev_attribute *sysdev_attrs[] = {
+	&attr_die_ident,
+	&attr_revision,
+	&attr_boot_mode,
+	&attr_part,
+	NULL,
+};
+
+static void __init socinfo_init(void)
+{
+	if (sysdev_class_register(&soc_sysdev_class)) {
+		pr_err("unable to register sysdev class\n");
+		return;
+	}
+
+	if (sysdev_register(&soc_sysdev_device)) {
+		pr_err("unable to register sysdev device\n");
+		return;
+	}
+
+	if (sysdev_create_files(&soc_sysdev_device, sysdev_attrs))
+		pr_err("unable to add sysdev attrs\n");
+}
+
+static void __init report_chipinfo(void)
+{
+	const char *part = picoxcell_get_partname();
+	unsigned long revision = picoxcell_get_revision();
+
+	pr_info("Picochip picoXcell device: %s revision %lu\n", part, revision);
+}
+
+struct dentry *arch_debugfs_dir;
+
+static void __init picoxcell_debugfs_init(void)
+{
+	arch_debugfs_dir = debugfs_create_dir("picoxcell", NULL);
+	if (IS_ERR(arch_debugfs_dir)) {
+		/* debugfs is enabled but we failed. */
+		if (-ENODEV != PTR_ERR(arch_debugfs_dir))
+			pr_err("failed to create picoxcell debugfs entry (%ld)\n",
+			       PTR_ERR(arch_debugfs_dir));
+		arch_debugfs_dir = NULL;
+		return;
+	}
+}
+
+static struct map_desc __initdata picoxcell_io_desc[] = {
+	{
+		.virtual    = PHYS_TO_IO(PICOXCELL_PERIPH_BASE),
+		.pfn	    = __phys_to_pfn(PICOXCELL_PERIPH_BASE),
+		.length     = PICOXCELL_PERIPH_LENGTH,
+		.type	    = MT_DEVICE,
+	},
+};
+
+void __init picoxcell_map_io(void)
+{
+	iotable_init(picoxcell_io_desc, ARRAY_SIZE(picoxcell_io_desc));
+}
+
+void __init picoxcell_core_init(void)
+{
+	struct picoxcell_soc *soc = picoxcell_get_soc();
+
+	BUG_ON(!soc);
+
+	axi2cfg_init();
+	report_chipinfo();
+	soc->init();
+
+	/*
+	 * Add the soc/soc0 entries to /sys/devices/system to report the
+	 * device type, boot mode etc.
+	 */
+	socinfo_init();
+
+	/* Add the arch debugfs entry. */
+	picoxcell_debugfs_init();
+}
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.h b/arch/arm/mach-picoxcell/picoxcell_core.h
new file mode 100644
index 0000000..5b27e52
--- /dev/null
+++ b/arch/arm/mach-picoxcell/picoxcell_core.h
@@ -0,0 +1,21 @@
+/*
+ * linux/arch/arm/mach-picoxcell/picoxcell_core.h
+ *
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#ifndef __ASM_ARCH_PICOXCELL_CORE_H__
+#define __ASM_ARCH_PICOXCELL_CORE_H__
+
+struct picoxcell_soc;
+
+extern void __init picoxcell_core_init(void);
+extern void __init picoxcell_init_irq(void);
+extern void __init picoxcell_map_io(void);
+
+#endif /* __ASM_ARCH_PICOXCELL_CORE_H__ */
diff --git a/arch/arm/mach-picoxcell/soc.h b/arch/arm/mach-picoxcell/soc.h
new file mode 100644
index 0000000..d24a0ce
--- /dev/null
+++ b/arch/arm/mach-picoxcell/soc.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#ifndef __PICOXCELL_SOC_H__
+#define __PICOXCELL_SOC_H__
+
+enum timer_type {
+	TIMER_TYPE_RTC,
+	TIMER_TYPE_TIMER,
+};
+
+struct picoxcell_timer {
+	const char			*name;
+	enum timer_type			type;
+	unsigned long			base;
+	int				irq;
+};
+
+enum picoxcell_features {
+	PICOXCELL_FEATURE_EBI,
+	PICOXCELL_FEATURE_SW_NAND,
+	PICOXCELL_FEATURE_PM,
+	PICOXCELL_FEATURE_CPUFREQ,
+	NR_FEAT_BITS
+};
+
+struct picoxcell_soc {
+	void				(*init)(void);
+	void				(*init_clocks)(void);
+	void				(*init_timers)(void);
+	void				(*init_muxing)(void);
+	ssize_t				(*get_boot_mode)(char *buf);
+	const char * const		*armgpio_pins;
+	int				nr_armgpio;
+	int				armgpio_base;
+	const char * const		*sdgpio_pins;
+	int				nr_sdgpio;
+	int				sdgpio_base;
+	const struct picoxcell_timer	*timers;
+	int				nr_timers;
+	unsigned long			features[BITS_TO_LONGS(NR_FEAT_BITS)];
+};
+
+extern struct picoxcell_soc *picoxcell_get_soc(void);
+
+static inline int picoxcell_has_feature(enum picoxcell_features feat)
+{
+	struct picoxcell_soc *soc = picoxcell_get_soc();
+
+	return test_bit(feat, soc->features);
+}
+
+#endif /* __PICOXCELL_SOC_H__ */
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCHv2] picoxcell: add support for the system timers
  2010-11-23 10:06 ` [RFC PATCH 03/13] picoxcell: add support for the system timers Jamie Iles
@ 2010-11-26 11:11   ` Jamie Iles
  2010-12-08 16:14     ` Russell King - ARM Linux
  0 siblings, 1 reply; 29+ messages in thread
From: Jamie Iles @ 2010-11-26 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

The picoXcell devices have 4 timers capable of generating interrupts
when they reach a predefined value and restarting and a freerunning RTC.
Use one of the interrupt capable timers as the clockevent_device and the
RTC for the clocksource and sched_clock().

v2:
	- use clocksource_register_hz() and
	  clockevents_calc_mult_shift() rather than specifying .mult and
	  .shift.
	- extend sched_clock from 32 bits in the RTC to 63 with
	  cnt32_to_63() to give us a longer period before wrapping.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile         |    3 +-
 arch/arm/mach-picoxcell/picoxcell_core.h |    2 +
 arch/arm/mach-picoxcell/time.c           |  206 ++++++++++++++++++++++++++++++
 3 files changed, 210 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/mach-picoxcell/time.c

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 7246a6a..726c61f 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -1 +1,2 @@
-obj-y				:= picoxcell_core.o axi2cfg.o
+obj-y				:= picoxcell_core.o axi2cfg.o \
+				   time.o
\ No newline at end of file
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.h b/arch/arm/mach-picoxcell/picoxcell_core.h
index 5b27e52..941d1a6 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.h
+++ b/arch/arm/mach-picoxcell/picoxcell_core.h
@@ -13,9 +13,11 @@
 #define __ASM_ARCH_PICOXCELL_CORE_H__
 
 struct picoxcell_soc;
+struct sys_timer;
 
 extern void __init picoxcell_core_init(void);
 extern void __init picoxcell_init_irq(void);
 extern void __init picoxcell_map_io(void);
+extern struct sys_timer picoxcell_sys_timer;
 
 #endif /* __ASM_ARCH_PICOXCELL_CORE_H__ */
diff --git a/arch/arm/mach-picoxcell/time.c b/arch/arm/mach-picoxcell/time.c
new file mode 100644
index 0000000..1ce65f2
--- /dev/null
+++ b/arch/arm/mach-picoxcell/time.c
@@ -0,0 +1,206 @@
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/cnt32_to_63.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/hardware.h>
+
+#include "picoxcell_core.h"
+#include "soc.h"
+
+enum timer_id {
+	TIMER_ID_CLOCKEVENT,
+	TIMER_ID_CLOCKSOURCE,
+	NR_TIMERS,
+};
+
+struct timer_instance {
+	void __iomem	    *base;
+	struct irqaction    irqaction;
+};
+
+/*
+ * We expect to have 2 timers - a freerunning one for the clock source and a
+ * periodic/oneshot one for the clock_event_device.
+ */
+static struct timer_instance timers[NR_TIMERS];
+
+static void timer_set_mode(enum clock_event_mode mode,
+			   struct clock_event_device *clk)
+{
+	struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
+	unsigned long load_count = DIV_ROUND_UP(CLOCK_TICK_RATE, HZ);
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		/*
+		 * By default, use the kernel tick rate. The reload value can
+		 * be changed with the timer_set_next_event() function.
+		 */
+		writel(load_count, timer->base + TIMER_LOAD_COUNT_REG_OFFSET);
+		writel(TIMER_ENABLE | TIMER_MODE,
+		       timer->base + TIMER_CONTROL_REG_OFFSET);
+		break;
+
+	case CLOCK_EVT_MODE_ONESHOT:
+		writel(TIMER_MODE, timer->base + TIMER_CONTROL_REG_OFFSET);
+		break;
+
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	default:
+		writel(0, timer->base + TIMER_CONTROL_REG_OFFSET);
+		break;
+	}
+}
+
+static int timer_set_next_event(unsigned long evt,
+				struct clock_event_device *clk)
+{
+	struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
+
+	/* Disable the timer, write the new event then enable it. */
+	writel(0, timer->base + TIMER_CONTROL_REG_OFFSET);
+	writel(evt, timer->base + TIMER_LOAD_COUNT_REG_OFFSET);
+	writel(TIMER_ENABLE | TIMER_MODE,
+	       timer->base + TIMER_CONTROL_REG_OFFSET);
+
+	return 0;
+}
+
+static struct clock_event_device clockevent_picoxcell = {
+	.features		= CLOCK_EVT_FEAT_PERIODIC |
+				  CLOCK_EVT_FEAT_ONESHOT,
+	.set_next_event		= timer_set_next_event,
+	.set_mode		= timer_set_mode,
+};
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+	struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
+
+	/* If we are in oneshot mode, we need to stop receiving interrupts. */
+	if (CLOCK_EVT_MODE_ONESHOT == clockevent_picoxcell.mode) {
+		unsigned long val = readl(timer->base +
+					  TIMER_CONTROL_REG_OFFSET);
+		val |= TIMER_INTERRUPT_MASK;
+		writel(val, timer->base + TIMER_CONTROL_REG_OFFSET);
+	}
+
+	/* Clear the interrupt. */
+	readl(timer->base + TIMER_EOI_REG_OFFSET);
+
+	clockevent_picoxcell.event_handler(&clockevent_picoxcell);
+
+	return IRQ_HANDLED;
+}
+
+#define PICOXCELL_MIN_RANGE	4
+
+static void picoxcell_clockevent_init(struct picoxcell_soc *soc)
+{
+	struct timer_instance *inst = &timers[TIMER_ID_CLOCKEVENT];
+	const struct picoxcell_timer *timer = NULL;
+	int i;
+
+	for (i = 0; i < soc->nr_timers; ++i)
+		if (soc->timers[i].type == TIMER_TYPE_TIMER) {
+			timer = &soc->timers[i];
+			break;
+		}
+
+	BUG_ON(!timer);
+
+	/* Configure the interrupt for this timer. */
+	inst->irqaction.name	= timer->name;
+	inst->irqaction.handler	= timer_interrupt;
+	inst->irqaction.flags	= IRQF_DISABLED | IRQF_TIMER | IRQF_PROBE;
+	inst->base		= ioremap(timer->base, TIMER_SPACING);
+
+	clockevent_picoxcell.name = timer->name;
+	clockevents_calc_mult_shift(&clockevent_picoxcell, CLOCK_TICK_RATE,
+				    PICOXCELL_MIN_RANGE);
+	clockevent_picoxcell.max_delta_ns =
+		clockevent_delta2ns(0xfffffffe, &clockevent_picoxcell);
+	clockevent_picoxcell.min_delta_ns = 50000;
+	clockevent_picoxcell.cpumask = cpumask_of(0);
+
+	/* Start with the timer disabled and the interrupt enabled. */
+	writel(0, inst->base + TIMER_CONTROL_REG_OFFSET);
+	setup_irq(timer->irq, &inst->irqaction);
+
+	clockevents_register_device(&clockevent_picoxcell);
+}
+
+static cycle_t picoxcell_rtc_get_cycles(struct clocksource *cs)
+{
+	struct timer_instance *inst = &timers[TIMER_ID_CLOCKSOURCE];
+
+	return readl(inst->base + RTCLK_CCV_REG_OFFSET);
+}
+
+/*
+ * Kernel assumes that sched_clock can be called early but may not have
+ * things ready yet.
+ */
+static cycle_t read_dummy(struct clocksource *cs)
+{
+	return 0;
+}
+
+static struct clocksource clocksource_picoxcell = {
+	.name	    = "rtc",
+	.rating     = 300,
+	.read	    = read_dummy,
+	.mask	    = CLOCKSOURCE_MASK(32),
+	.flags	    = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void __init picoxcell_clocksource_init(struct picoxcell_soc *soc)
+{
+	const struct picoxcell_timer *timer = NULL;
+	int i;
+
+	for (i = 0; i < soc->nr_timers; ++i)
+		if (soc->timers[i].type == TIMER_TYPE_RTC) {
+			timer = &soc->timers[i];
+			break;
+		}
+
+	BUG_ON(!timer);
+
+	timers[TIMER_ID_CLOCKSOURCE].base = ioremap(timer->base, SZ_4K);
+
+	/* The RTC is always running. We don't need to do any initialization. */
+	clocksource_picoxcell.read = picoxcell_rtc_get_cycles;
+	clocksource_register_hz(&clocksource_picoxcell, CLOCK_TICK_RATE);
+}
+
+/*
+ * Overwrite weak default sched_clock with something more precise.
+ *
+ * On picoXcell we have a RTC that clocks at 200MHz so a multiply by 5 gives
+ * us a nanosecond count.
+ */
+unsigned long long notrace sched_clock(void)
+{
+#define CYCLES_TO_NSEC_MULT	(NSEC_PER_SEC / CLOCK_TICK_RATE)
+	return cnt32_to_63(readl(IO_ADDRESS(PICOXCELL_RTCLK_BASE) +
+				 RTCLK_CCV_REG_OFFSET)) * CYCLES_TO_NSEC_MULT;
+	return 0;
+}
+
+static void __init picoxcell_timer_init(void)
+{
+	struct picoxcell_soc *soc = picoxcell_get_soc();
+
+	picoxcell_clocksource_init(soc);
+	picoxcell_clockevent_init(soc);
+}
+
+struct sys_timer picoxcell_sys_timer = {
+	.init	= picoxcell_timer_init,
+};
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCHv2] picoxcell: add common SoC devices
  2010-11-23 10:06 ` [RFC PATCH 07/13] picoxcell: add common SoC devices Jamie Iles
@ 2010-11-26 11:13   ` Jamie Iles
  0 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-26 11:13 UTC (permalink / raw)
  To: linux-arm-kernel

Add the devices common to all picoXcell variants (UART and PMU). Other
peripherals such as DMA, SPI, fuses and EMAC will be added later
with driver support.

v2:
	- split the UARTs into two separate platform devices and use
	  PLAT8250_* as the IDs

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile         |    3 +-
 arch/arm/mach-picoxcell/devices.c        |  118 ++++++++++++++++++++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.c |    3 +
 arch/arm/mach-picoxcell/picoxcell_core.h |    1 +
 4 files changed, 124 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/mach-picoxcell/devices.c

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 8d8b4e8..371698f 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -2,4 +2,5 @@ obj-y				:= picoxcell_core.o axi2cfg.o \
 				   time.o \
 				   mux.o \
 				   gpio.o \
-				   clk.o
+				   clk.o \
+				   devices.o
diff --git a/arch/arm/mach-picoxcell/devices.c b/arch/arm/mach-picoxcell/devices.c
new file mode 100644
index 0000000..b9b8374
--- /dev/null
+++ b/arch/arm/mach-picoxcell/devices.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+#include <asm/pmu.h>
+
+#include "picoxcell_core.h"
+
+#define UART_USR_REG_OFFSET			0x7C
+static struct plat_serial8250_port serial1_platform_data[] = {
+	{
+		.membase	= iomem_ptr(PICOXCELL_UART1_BASE),
+		.mapbase	= PICOXCELL_UART1_BASE,
+		.irq		= IRQ_UART1,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.iotype		= UPIO_DWAPB32,
+		.regshift	= 2,
+		.uartclk	= PICOXCELL_BASE_BAUD,
+		.private_data	= (void *)(PHYS_TO_IO(PICOXCELL_UART1_BASE +
+						      UART_USR_REG_OFFSET)),
+	},
+	{},
+};
+
+static struct resource serial1_resources[] = {
+	{
+		.start		= PICOXCELL_UART1_BASE,
+		.end		= PICOXCELL_UART1_BASE + 0xFFFF,
+		.flags		= IORESOURCE_MEM,
+	},
+	{
+		.start		= IRQ_UART1,
+		.end		= IRQ_UART2,
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device serial1_device = {
+	.name			= "serial8250",
+	.id			= PLAT8250_DEV_PLATFORM1,
+	.dev.platform_data	= serial1_platform_data,
+	.resource		= serial1_resources,
+	.num_resources		= ARRAY_SIZE(serial1_resources),
+};
+
+static struct plat_serial8250_port serial2_platform_data[] = {
+	{
+		.membase	= iomem_ptr(PICOXCELL_UART2_BASE),
+		.mapbase	= PICOXCELL_UART2_BASE,
+		.irq		= IRQ_UART2,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.iotype		= UPIO_DWAPB32,
+		.regshift	= 2,
+		.uartclk	= PICOXCELL_BASE_BAUD,
+		.private_data	= (void *)(PHYS_TO_IO(PICOXCELL_UART2_BASE +
+						      UART_USR_REG_OFFSET)),
+	},
+	{},
+};
+
+static struct resource serial2_resources[] = {
+	{
+		.start		= PICOXCELL_UART2_BASE,
+		.end		= PICOXCELL_UART2_BASE + 0xFFFF,
+		.flags		= IORESOURCE_MEM,
+	},
+	{
+		.start		= IRQ_UART2,
+		.end		= IRQ_UART2,
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device serial2_device = {
+	.name			= "serial8250",
+	.id			= PLAT8250_DEV_PLATFORM2,
+	.dev.platform_data	= serial2_platform_data,
+	.resource		= serial2_resources,
+	.num_resources		= ARRAY_SIZE(serial2_resources),
+};
+
+static struct resource pmu_resource = {
+	.start			= IRQ_NPMUIRQ,
+	.end			= IRQ_NPMUIRQ,
+	.flags			= IORESOURCE_IRQ,
+};
+
+static struct platform_device pmu_device = {
+	.name			= "arm-pmu",
+	.id			= ARM_PMU_DEVICE_CPU,
+	.num_resources		= 1,
+	.resource		= &pmu_resource,
+};
+
+static struct __initdata platform_device *common_devices[] = {
+	&serial1_device,
+	&serial2_device,
+	&pmu_device,
+};
+
+void __init picoxcell_add_devices(void)
+{
+	WARN_ON(platform_add_devices(common_devices,
+				     ARRAY_SIZE(common_devices)));
+}
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index db79ba7..9f977fa 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -285,4 +285,7 @@ void __init picoxcell_core_init(void)
 
 	/* Add handlers for the AXI bus snooping. */
 	picoxcell_axi_bus_error_init();
+
+	/* Register the devices. */
+	picoxcell_add_devices();
 }
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.h b/arch/arm/mach-picoxcell/picoxcell_core.h
index 941d1a6..9647b7a 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.h
+++ b/arch/arm/mach-picoxcell/picoxcell_core.h
@@ -19,5 +19,6 @@ extern void __init picoxcell_core_init(void);
 extern void __init picoxcell_init_irq(void);
 extern void __init picoxcell_map_io(void);
 extern struct sys_timer picoxcell_sys_timer;
+extern void __init picoxcell_add_devices(void);
 
 #endif /* __ASM_ARCH_PICOXCELL_CORE_H__ */
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCHv2] picoxcell: add support for pc3x3 devices
  2010-11-23 10:06 ` [RFC PATCH 12/13] picoxcell: add support for pc3x3 devices Jamie Iles
@ 2010-11-26 11:14   ` Jamie Iles
  0 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-26 11:14 UTC (permalink / raw)
  To: linux-arm-kernel

PC3X3 is the second supported device family of picoXcell and this patch
defines the virtual clocks to allow drivers to work, the pin muxing
options, timer locations and the GPIO definitions. This also implements
the PLL control for the ARM PLL.

v2:
	- remove device specific IO mappings as the only peripherals not
	  accessed through ioremap() share a common address space in all
	  devices.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Kconfig          |   18 +
 arch/arm/mach-picoxcell/Makefile         |    1 +
 arch/arm/mach-picoxcell/pc3x3.c          | 1235 ++++++++++++++++++++++++++++++
 arch/arm/mach-picoxcell/picoxcell_core.c |   11 +
 arch/arm/mach-picoxcell/soc.h            |    1 +
 5 files changed, 1266 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-picoxcell/pc3x3.c

diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig
index 5141202..671cadc 100644
--- a/arch/arm/mach-picoxcell/Kconfig
+++ b/arch/arm/mach-picoxcell/Kconfig
@@ -8,4 +8,22 @@ config PICOXCELL_PC3X2
 	  Include support for picoChip PC3x2 family of devices. This
 	  includes PC302 and PC312.
 
+config PICOXCELL_PC3X3
+	bool "Support PC3x3 devices"
+	default y
+	select ARCH_HAS_CPUFREQ
+	help
+	  Include support for picoChip PC3x3 family of devices. This includes
+	  PC313, PC323 and PC333.
+
+config PC3X3_STOP_WDT_IN_SUSPEND
+	bool "Stop WDT in PM suspend"
+	depends on PICOCHIP_PC3X3
+	default y
+	help
+	  If you say yes here, then when the platform is put into suspend mode,
+	  the watchdog timer will be paused. Saying no will cause the watchdog
+	  to keep running. If you say no here, make sure that the watchdog uses
+	  the pretimeout mode and mark the watchdog as a wakeup source.
+
 endmenu
diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index ab5dec1..d95b2b2 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -7,3 +7,4 @@ obj-y				:= picoxcell_core.o axi2cfg.o \
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq.o
 obj-$(CONFIG_PM)		+= pm.o
 obj-$(CONFIG_PICOXCELL_PC3X2)	+= pc3x2.o
+obj-$(CONFIG_PICOXCELL_PC3X3)	+= pc3x3.o
diff --git a/arch/arm/mach-picoxcell/pc3x3.c b/arch/arm/mach-picoxcell/pc3x3.c
new file mode 100644
index 0000000..4ace6d3
--- /dev/null
+++ b/arch/arm/mach-picoxcell/pc3x3.c
@@ -0,0 +1,1235 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+
+#include <mach/clkdev.h>
+#include <mach/hardware.h>
+
+#include "mux.h"
+#include "picoxcell_core.h"
+#include "soc.h"
+
+static DEFINE_SPINLOCK(pc3x3_clk_lock);
+
+FIXED_CLK(tzprot,	CLOCK_TICK_RATE, 0);
+FIXED_CLK(spi,		CLOCK_TICK_RATE, 1);
+FIXED_CLK(dmac0,	CLOCK_TICK_RATE, 2);
+FIXED_CLK(dmac1,	CLOCK_TICK_RATE, 3);
+FIXED_CLK(ebi,		CLOCK_TICK_RATE, 4);
+FIXED_CLK(ipsec,	CLOCK_TICK_RATE, 5);
+FIXED_CLK(l2_engine,	CLOCK_TICK_RATE, 6);
+FIXED_CLK(trng,		CLOCK_TICK_RATE, 7);
+FIXED_CLK(fuse,		CLOCK_TICK_RATE, 8);
+FIXED_CLK(otp,		CLOCK_TICK_RATE, 9);
+FIXED_CLK(wdt,		CLOCK_TICK_RATE, -1);
+VARIABLE_CLK(arm,			 -1, 140000, 700000, 5000);
+
+static struct clk_lookup pc3x3_clk_lookup[] = {
+	CLK_LOOKUP(NULL,		"tzprot_ctl",	&tzprot_clk),
+	CLK_LOOKUP("dw_spi_mmio.0",	NULL,		&spi_clk),
+	CLK_LOOKUP("dw_dmac.0",		NULL,		&dmac0_clk),
+	CLK_LOOKUP("dw_dmac.1",		NULL,		&dmac1_clk),
+	CLK_LOOKUP(NULL,		"ebi",		&ebi_clk),
+	CLK_LOOKUP("picoxcell-ipsec",	NULL,		&ipsec_clk),
+	CLK_LOOKUP("picoxcell-l2",	NULL,		&l2_engine_clk),
+	CLK_LOOKUP("picoxcell-trng",	NULL,		&trng_clk),
+	CLK_LOOKUP("picoxcell-fuse",	NULL,		&fuse_clk),
+	CLK_LOOKUP("picoxcell-otp",	NULL,		&otp_clk),
+	CLK_LOOKUP("dw_wdt",		NULL,		&wdt_clk),
+	CLK_LOOKUP(NULL,		"arm",		&arm_clk),
+};
+
+static int pc3x3_clk_is_enabled(struct clk *clk)
+{
+	unsigned long clk_gate =
+		axi2cfg_readl(AXI2CFG_CLOCK_GATING_REG_OFFSET);
+	return !(clk_gate & (1 << clk->clk_num));
+}
+
+static void pc3x3_clk_disable(struct clk *clk)
+{
+	unsigned long clk_gate;
+
+	if (clk->clk_num < 0)
+		return;
+
+	/*
+	 * Make sure that all outstanding transactions have reached the device
+	 * before we turn off the clock to prevent taking an exception.
+	 */
+	dsb();
+
+	clk_gate = axi2cfg_readl(AXI2CFG_CLOCK_GATING_REG_OFFSET);
+	clk_gate |= (1 << clk->clk_num);
+	axi2cfg_writel(clk_gate, AXI2CFG_CLOCK_GATING_REG_OFFSET);
+}
+
+static inline void pc3x3_clk_enable(struct clk *clk)
+{
+	unsigned long clk_gate;
+
+	if (clk->clk_num < 0)
+		return;
+
+	clk_gate = axi2cfg_readl(AXI2CFG_CLOCK_GATING_REG_OFFSET);
+	clk_gate &= ~(1 << clk->clk_num);
+	axi2cfg_writel(clk_gate, AXI2CFG_CLOCK_GATING_REG_OFFSET);
+}
+
+static long pc3x3_clk_round_rate(struct clk *clk,
+				 unsigned long rate)
+{
+	long ret = -EINVAL;
+	unsigned long offset = rate % clk->step;
+
+	if (WARN_ON(clk != &arm_clk))
+		goto out;
+
+	rate -= offset;
+	if (offset > clk->step - offset)
+		ret = rate + clk->step;
+	else
+		ret = rate;
+
+out:
+	return ret;
+}
+
+/* The register that the CLKF value is programmed into. */
+#define AXI2CFG_ARM_PLL_CLKF_REG_OFFS		0x0050
+/* The frequency sensing control register. */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_REG_OFFS	0x0054
+
+/* The value in the sense register is a valid frequency. */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_VALID	(1 << 29)
+/* The sensing process is active. */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_ACTIVE	(1 << 30)
+/* Write this to the sense register to start sensing. Self clearing. */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_START	(1 << 31)
+/*
+ * The frequency (in MHz) is returned in the bottom 10 bits of the sense
+ * register and is valid when bit 29 is asserted.
+ */
+#define AXI2CFG_ARM_PLL_FREQ_SENSE_FREQ_MASK	0x3FF
+
+static int __pc3x3_clk_get_rate(struct clk *clk)
+{
+	unsigned int mhz = 0;
+	unsigned long sense_val;
+	int ret = -EINVAL;
+
+	if (WARN_ON(clk != &arm_clk))
+		goto out;
+
+	while (0 == mhz) {
+		do {
+			axi2cfg_writel(AXI2CFG_ARM_PLL_FREQ_SENSE_START,
+				       AXI2CFG_ARM_PLL_FREQ_SENSE_REG_OFFS);
+
+			/* Wait for the frequency sense to complete. */
+			do {
+				sense_val = axi2cfg_readl(AXI2CFG_ARM_PLL_FREQ_SENSE_REG_OFFS);
+			} while ((sense_val &
+				  AXI2CFG_ARM_PLL_FREQ_SENSE_ACTIVE));
+		} while (!(sense_val & AXI2CFG_ARM_PLL_FREQ_SENSE_VALID));
+
+		/* The frequency sense returns the frequency in MHz. */
+		mhz = (sense_val & AXI2CFG_ARM_PLL_FREQ_SENSE_FREQ_MASK);
+	}
+	ret = mhz * 1000;
+
+out:
+	return ret;
+}
+
+static int pc3x3_clk_get_rate(struct clk *clk)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&pc3x3_clk_lock, flags);
+	ret = __pc3x3_clk_get_rate(clk);
+	spin_unlock_irqrestore(&pc3x3_clk_lock, flags);
+
+	return ret;
+}
+
+static void
+pc3x3_cpu_pll_set(unsigned int freq)
+{
+	/* Set the new frequency. */
+	axi2cfg_writel(((freq / 1000) / 5) - 1, AXI2CFG_ARM_PLL_CLKF_REG_OFFS);
+	udelay(2);
+}
+
+static int pc3x3_clk_set_rate(struct clk *clk, unsigned long target)
+{
+	int ret = -EINVAL;
+	unsigned long flags, current_khz;
+
+	if (WARN_ON(clk != &arm_clk) || target % clk->step) {
+		pr_err("unable to set rate for non-cpu clock (%lu)\n", target);
+		goto out;
+	}
+
+	pr_debug("set cpu clock rate to %luKHz\n", target);
+
+	spin_lock_irqsave(&pc3x3_clk_lock, flags);
+
+	/*
+	 * We can only reliably step by 20% at a time. We may need to
+	 * do this in several iterations.
+	 */
+	while ((current_khz = __pc3x3_clk_get_rate(clk)) != target) {
+		unsigned long next_step, next_target;
+
+		if (target < current_khz) {
+			next_step = current_khz - ((4 * current_khz) / 5);
+			next_target = current_khz -
+				min(current_khz - target, next_step);
+			next_target = roundup(next_target, clk->step);
+		} else {
+			next_step = ((6 * current_khz) / 5) - current_khz;
+			next_target =
+				min(target - current_khz, next_step) +
+				current_khz;
+			next_target =
+				(next_target / clk->step) * clk->step;
+		}
+
+		pc3x3_cpu_pll_set(next_target);
+	}
+
+	spin_unlock_irqrestore(&pc3x3_clk_lock, flags);
+	ret = 0;
+
+out:
+	return ret;
+}
+
+static void pc3x3_clk_init(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pc3x3_clk_lookup); ++i) {
+		struct clk_lookup *lookup = &pc3x3_clk_lookup[i];
+
+		lookup->clk->enable	= pc3x3_clk_enable;
+		lookup->clk->disable	= pc3x3_clk_disable;
+		lookup->clk->is_enabled	= pc3x3_clk_is_enabled;
+
+		if (lookup->clk->rate < 0) {
+			lookup->clk->round_rate	= pc3x3_clk_round_rate;
+			lookup->clk->set_rate	= pc3x3_clk_set_rate;
+			lookup->clk->get_rate	= pc3x3_clk_get_rate;
+		}
+
+		picoxcell_clk_add(lookup);
+	}
+
+	/*
+	 * For PC3x3, disable the clocks that aren't required in the core
+	 * code. The drivers will enable the clocks when they get initialised.
+	 */
+	__clk_disable(&spi_clk);
+	__clk_disable(&dmac0_clk);
+	__clk_disable(&dmac1_clk);
+	__clk_disable(&ipsec_clk);
+	__clk_disable(&l2_engine_clk);
+	__clk_disable(&trng_clk);
+	__clk_disable(&otp_clk);
+}
+
+static ssize_t pc3x3_get_boot_mode(char *buf)
+{
+	unsigned long syscfg = syscfg_read();
+	unsigned long otp_fuses = readl(IO_ADDRESS(PICOXCELL_FUSE_BASE + 124));
+	const char *boot_mode;
+	const char *otp_boot = NULL;
+	/* We're interested in fuses 1018:1017, and otp_fuses starts at 992. */
+	unsigned otp_boot_mode = (otp_fuses >> 25) & 0x3;
+
+	switch (syscfg & 0x3) {
+	case 0x0:
+		boot_mode = "parallel";
+		break;
+	case 0x1:
+		boot_mode = "ssi";
+		break;
+	case 0x2:
+		boot_mode = "mii";
+		break;
+	default:
+		boot_mode = "unknown";
+	}
+
+	if (0x2 == otp_boot_mode)
+		otp_boot = ":otp";
+	else if (0x1 == otp_boot_mode)
+		boot_mode = "otp";
+
+	return snprintf(buf, PAGE_SIZE, "%s%s\n", boot_mode,
+			otp_boot ? otp_boot : "");
+}
+
+static DEFINE_SPINLOCK(mux_lock);
+
+#define AXI2CFG_SHD_GPIO_MUX_REG_OFFSET		0x34
+#define AXI2CFG_USE_PAI_GPIO_REG_OFFSET		0x38
+#define AXI2CFG_USE_EBI_GPIO_REG_OFFSET		0x3c
+#define AXI2CFG_USE_DECODE_GPIO_REG_OFFSET	0x40
+#define AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET	0x44
+
+/*
+ * Set the muxing of one of the shared pins.
+ *
+ * Must be called with mux_lock held.
+ */
+static void pc3x3_shd_gpio_set_mux(int arm_pin_nr, enum mux_setting setting)
+{
+	unsigned long shd_mux = axi2cfg_readl(AXI2CFG_SHD_GPIO_MUX_REG_OFFSET);
+
+	if (MUX_ARM == setting)
+		shd_mux |= (1 << arm_pin_nr);
+	else
+		shd_mux &= ~(1 << arm_pin_nr);
+
+	axi2cfg_writel(shd_mux, AXI2CFG_SHD_GPIO_MUX_REG_OFFSET);
+}
+
+static int pc3x3_get_shd_mux(struct muxed_pin *pin)
+{
+	unsigned long shd_mux = axi2cfg_readl(AXI2CFG_SHD_GPIO_MUX_REG_OFFSET);
+	unsigned long syscfg = syscfg_read();
+
+	if (pin->sd_pin == PC3X3_GPIO_PIN_SDGPIO_0) {
+		if (syscfg & AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK)
+			return MUX_PERIPHERAL;
+		else
+			return MUX_SD;
+	}
+
+	return shd_mux & (1 << pin->arm_pin) ? MUX_ARM : MUX_SD;
+}
+
+/*
+ * Shared ARM/SD gpio pins. These pins go to the arm_gpio[3:0] pads but can
+ * be arm or sdgpio.
+ */
+static int pc3x3_shd_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	if (MUX_PERIPHERAL == setting) {
+		if (pin->sd_pin != PC3X3_GPIO_PIN_SDGPIO_0)
+			return -EINVAL;
+
+		syscfg_update(AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK,
+			      AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK);
+	} else if (PC3X3_GPIO_PIN_SDGPIO_0 == pin->sd_pin) {
+		syscfg_update(AXI2CFG_SYSCFG_FREQ_SYNTH_MUX_MASK, 0);
+	}
+
+	if (PC3X3_GPIO_PIN_SDGPIO_0 == pin->sd_pin && MUX_ARM == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+	pc3x3_shd_gpio_set_mux(pin->arm_pin, setting);
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+static struct muxed_pin armgpio_0_4[] = {
+GPIO(arm_gpio0, PC3X3_GPIO_PIN_SDGPIO_16, PC3X3_GPIO_PIN_ARM_0, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+GPIO(arm_gpio1, PC3X3_GPIO_PIN_SDGPIO_17, PC3X3_GPIO_PIN_ARM_1, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+GPIO(arm_gpio2, PC3X3_GPIO_PIN_SDGPIO_18, PC3X3_GPIO_PIN_ARM_2, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+GPIO(arm_gpio3, PC3X3_GPIO_PIN_SDGPIO_19, PC3X3_GPIO_PIN_ARM_3, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+};
+
+static struct pin_group armgpio_0_4_group = {
+	.nr_pins    = ARRAY_SIZE(armgpio_0_4),
+	.name	    = "arm_gpio[3:0]",
+	.pins	    = armgpio_0_4,
+};
+
+static struct muxed_pin shd_gpio[] = {
+GPIO(shd_gpio, PC3X3_GPIO_PIN_SDGPIO_8, PC3X3_GPIO_PIN_ARM_8, pc3x3_shd_mux,
+     pc3x3_get_shd_mux),
+};
+
+static struct pin_group pc3x3_shd_group = {
+	.nr_pins    = ARRAY_SIZE(shd_gpio),
+	.name	    = "shd_gpio",
+	.pins	    = shd_gpio,
+};
+
+/*
+ * boot_mode[1:0] pads - the pins switch to gpio automatically after boot and
+ * can be either arm or sdgpio.
+ */
+static struct muxed_pin boot_mode_0_1[] = {
+GPIO(boot_mode0, PC3X3_GPIO_PIN_SDGPIO_9, PC3X3_GPIO_PIN_ARM_9,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+GPIO(boot_mode1, PC3X3_GPIO_PIN_SDGPIO_10, PC3X3_GPIO_PIN_ARM_10,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group boot_mode_group = {
+	.nr_pins    = ARRAY_SIZE(boot_mode_0_1),
+	.name	    = "boot_mode[1:0]",
+	.pins	    = boot_mode_0_1,
+};
+
+/*
+ * sdram_speed_sel pad - automatically switches to gpio after boot and can be
+ * arm or sdgpio.
+ */
+static struct muxed_pin sdram_speed_sel[] = {
+GPIO(sdram_speed_sel, PC3X3_GPIO_PIN_SDGPIO_11, PC3X3_GPIO_PIN_ARM_11,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group sdram_speed_sel_group = {
+	.nr_pins    = ARRAY_SIZE(sdram_speed_sel),
+	.name	    = "sdram_speed_sel",
+	.pins	    = sdram_speed_sel,
+};
+
+/*
+ * mii_rev_en pad - automatically switches to gpio after boot and can be
+ * arm or sdgpio.
+ */
+static struct muxed_pin mii_rev_en[] = {
+GPIO(mii_rev_en, PC3X3_GPIO_PIN_SDGPIO_12, PC3X3_GPIO_PIN_ARM_12,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group mii_rev_en_group = {
+	.nr_pins    = ARRAY_SIZE(mii_rev_en),
+	.name	    = "mii_rev_en",
+	.pins	    = mii_rev_en,
+};
+
+/*
+ * mii_rmii_en pad - automatically switches to gpio after boot and can be
+ * arm or sdgpio.
+ */
+static struct muxed_pin mii_rmii_en[] = {
+GPIO(mii_rmii_en, PC3X3_GPIO_PIN_SDGPIO_13, PC3X3_GPIO_PIN_ARM_13,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group mii_rmii_en_group = {
+	.nr_pins    = ARRAY_SIZE(mii_rmii_en),
+	.name	    = "mii_rmii_en",
+	.pins	    = mii_rmii_en,
+};
+
+/*
+ * mii_speed_sel pad - automatically switches to gpio after boot and can be
+ * arm or sdgpio.
+ */
+static struct muxed_pin mii_speed_sel[] = {
+GPIO(mii_speed_sel, PC3X3_GPIO_PIN_SDGPIO_14, PC3X3_GPIO_PIN_ARM_14,
+     pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group mii_speed_sel_group = {
+	.nr_pins    = ARRAY_SIZE(mii_speed_sel),
+	.name	    = "mii_speed_sel",
+	.pins	    = mii_speed_sel,
+};
+
+static int pc3x3_shd_ebi_get_mux(struct muxed_pin *pin)
+{
+	int ebi_pin, err = 0, can_be_sd = 1;
+	unsigned long ebi_mux;
+
+	spin_lock(&mux_lock);
+
+	ebi_mux = axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	/*
+	 * Find out what EBI pin our GPIO maps to.
+	 */
+	if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_4 &&
+	    pin->arm_pin < PC3X3_GPIO_PIN_ARM_8) {
+		ebi_pin = 22 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_4);
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_20 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_24) {
+		ebi_pin = 18 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_20);
+	} else {
+		/* These aren't shared gpio pins. */
+		ebi_pin = 14 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_14);
+		can_be_sd = 0;
+	}
+
+	if (!(ebi_mux & (1 << (ebi_pin - 14)))) {
+		err = MUX_PERIPHERAL;
+		goto out;
+	}
+
+	if (can_be_sd)
+		err = pc3x3_get_shd_mux(pin);
+	else
+		err = MUX_ARM;
+
+out:
+	spin_unlock(&mux_lock);
+
+	return err;
+}
+
+static int pc3x3_shd_ebi_set_mux(struct muxed_pin *pin,
+				 enum mux_setting setting)
+{
+	int ebi_pin, err = 0, can_be_sd = 1;
+	unsigned long ebi_mux;
+
+	spin_lock(&mux_lock);
+
+	ebi_mux = axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	/*
+	 * Find out what EBI pin our GPIO maps to.
+	 */
+	if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_4 &&
+	    pin->arm_pin < PC3X3_GPIO_PIN_ARM_8) {
+		ebi_pin = 22 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_4);
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_20 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_24) {
+		ebi_pin = 18 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_20);
+	} else {
+		/* These aren't shared gpio pins. */
+		ebi_pin = 14 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_14);
+		can_be_sd = 0;
+	}
+
+	if (MUX_SD == setting && !can_be_sd) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	/* Use the pin as EBI. */
+	if (MUX_PERIPHERAL == setting)
+		ebi_mux &= ~(1 << (ebi_pin - 14));
+	else
+		ebi_mux |= (1 << (ebi_pin - 14));
+
+	axi2cfg_writel(ebi_mux, AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	/*
+	 * Make sure that the configuration is valid (the GPIO isn't going to
+	 * the PAI).
+	 */
+	if (axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET) != ebi_mux) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	/*
+	 * If we can be SD or ARM and we want to be gpio, pick the correct
+	 * one now.
+	 */
+	if (MUX_PERIPHERAL != setting && can_be_sd)
+		pc3x3_shd_gpio_set_mux(pin->arm_pin, setting);
+
+out:
+	spin_unlock(&mux_lock);
+
+	return err;
+}
+
+/*
+ * ebi_addr[25:18] pads - these pads can be either the EBI or arm gpio or
+ * sdgpio. Note: the gpio pins can also be routed to the pai_tx/rx_data pads.
+ *
+ * A pad may only be used for GPIO if the corresponding GPIO pin is not
+ * already routed to the pai tx/rx data pad. The same applies in the reverse
+ * direction. Hardware interlocks exist to prevent this from happening.
+ */
+static struct muxed_pin ebi_addr_18_25[] = {
+PIN(ebi_addr18, PC3X3_GPIO_PIN_SDGPIO_4, PC3X3_GPIO_PIN_ARM_20,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr19, PC3X3_GPIO_PIN_SDGPIO_5, PC3X3_GPIO_PIN_ARM_21,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr20, PC3X3_GPIO_PIN_SDGPIO_6, PC3X3_GPIO_PIN_ARM_22,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr21, PC3X3_GPIO_PIN_SDGPIO_7, PC3X3_GPIO_PIN_ARM_23,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr22, PC3X3_GPIO_PIN_SDGPIO_20, PC3X3_GPIO_PIN_ARM_4,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr23, PC3X3_GPIO_PIN_SDGPIO_21, PC3X3_GPIO_PIN_ARM_5,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr24, PC3X3_GPIO_PIN_SDGPIO_22, PC3X3_GPIO_PIN_ARM_6,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr25, PC3X3_GPIO_PIN_SDGPIO_23, PC3X3_GPIO_PIN_ARM_7,
+    pc3x3_shd_ebi_set_mux, pc3x3_shd_ebi_get_mux),
+};
+
+static struct pin_group ebi_addr_18_25_group = {
+	.nr_pins    = ARRAY_SIZE(ebi_addr_18_25),
+	.name	    = "ebi_addr[25:18]",
+	.pins	    = ebi_addr_18_25,
+};
+
+static int pc3x3_shd_pai_get_mux(struct muxed_pin *pin)
+{
+	int bit, err = 0, can_be_sd = 1;
+	unsigned long pai_mux;
+
+	spin_lock(&mux_lock);
+
+	pai_mux = axi2cfg_readl(AXI2CFG_USE_PAI_GPIO_REG_OFFSET);
+
+	/*
+	 * Find out what pai pin our GPIO maps to.
+	 */
+	if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_4 &&
+	    pin->arm_pin < PC3X3_GPIO_PIN_ARM_8) {
+		/* pai_tx_data[3:0] */
+		bit = pin->arm_pin - PC3X3_GPIO_PIN_ARM_4;
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_20 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_24) {
+		/* pai_rx_data[3:0] */
+		bit = 8 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_20);
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_24 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_28) {
+		/* pai_tx_data[7:4] */
+		bit = 4 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_24);
+		can_be_sd = 0;
+	} else {
+		/* pai_rx_data[7:4] */
+		bit = 12 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_28);
+		can_be_sd = 0;
+	}
+
+	if (!(pai_mux & (1 << bit))) {
+		err = MUX_PERIPHERAL;
+		goto out;
+	}
+
+	if (can_be_sd)
+		err = pc3x3_get_shd_mux(pin);
+	else
+		err = MUX_ARM;
+
+out:
+	spin_unlock(&mux_lock);
+
+	return err;
+}
+
+static int pc3x3_shd_pai_set_mux(struct muxed_pin *pin,
+				 enum mux_setting setting)
+{
+	int bit, err = 0, can_be_sd = 1;
+	unsigned long pai_mux;
+
+	spin_lock(&mux_lock);
+
+	pai_mux = axi2cfg_readl(AXI2CFG_USE_PAI_GPIO_REG_OFFSET);
+
+	/*
+	 * Find out what pai pin our GPIO maps to.
+	 */
+	if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_4 &&
+	    pin->arm_pin < PC3X3_GPIO_PIN_ARM_8) {
+		/* pai_tx_data[3:0] */
+		bit = pin->arm_pin - PC3X3_GPIO_PIN_ARM_4;
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_20 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_24) {
+		/* pai_rx_data[3:0] */
+		bit = 8 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_20);
+	} else if (pin->arm_pin >= PC3X3_GPIO_PIN_ARM_24 &&
+		   pin->arm_pin < PC3X3_GPIO_PIN_ARM_28) {
+		/* pai_tx_data[7:4] */
+		bit = 4 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_24);
+		can_be_sd = 0;
+	} else {
+		/* pai_rx_data[7:4] */
+		bit = 12 + (pin->arm_pin - PC3X3_GPIO_PIN_ARM_28);
+		can_be_sd = 0;
+	}
+
+	if (MUX_SD == setting && !can_be_sd) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	/* Use the pin as pai. */
+	if (MUX_PERIPHERAL == setting)
+		pai_mux &= ~(1 << bit);
+	else
+		pai_mux |= (1 << bit);
+
+	axi2cfg_writel(pai_mux, AXI2CFG_USE_PAI_GPIO_REG_OFFSET);
+
+	/*
+	 * Make sure that the configuration is valid (the GPIO isn't going to
+	 * the EBI).
+	 */
+	if (axi2cfg_readl(AXI2CFG_USE_PAI_GPIO_REG_OFFSET) != pai_mux) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	/*
+	 * If we can be SD or ARM and we want to be gpio, pick the correct
+	 * one now.
+	 */
+	if (MUX_PERIPHERAL != setting && can_be_sd)
+		pc3x3_shd_gpio_set_mux(pin->arm_pin, setting);
+
+out:
+	spin_unlock(&mux_lock);
+
+	return err;
+}
+
+/*
+ * pai_rx_data[3:0] pads - these pads can be either the pai_rx_data or arm
+ * gpio or sdgpio. Note: the gpio pins can also be routed to the
+ * ebi_addr pads.
+ *
+ * A pad may only be used for GPIO if the corresponding GPIO pin is not
+ * already routed to the ebi address pad. The same applies in the reverse
+ * direction. Hardware interlocks exist to prevent this from happening.
+ */
+static struct muxed_pin pai_rx_data_0_3[] = {
+PIN(pai_rx_data0, PC3X3_GPIO_PIN_SDGPIO_4, PC3X3_GPIO_PIN_ARM_20,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data1, PC3X3_GPIO_PIN_SDGPIO_5, PC3X3_GPIO_PIN_ARM_21,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data2, PC3X3_GPIO_PIN_SDGPIO_6, PC3X3_GPIO_PIN_ARM_22,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data3, PC3X3_GPIO_PIN_SDGPIO_7, PC3X3_GPIO_PIN_ARM_23,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+};
+
+static struct pin_group pai_rx_data_0_3_group = {
+	.nr_pins    = ARRAY_SIZE(pai_rx_data_0_3),
+	.name	    = "pai_rx_data[3:0]",
+	.pins	    = pai_rx_data_0_3,
+};
+
+/*
+ * pai_tx_data[3:0] pads - these pads can be either the pai_tx_data or arm
+ * gpio or sdgpio. Note: the gpio pins can also be routed to the
+ * ebi_addr pads.
+ *
+ * A pad may only be used for GPIO if the corresponding GPIO pin is not
+ * already routed to the ebi address pad. The same applies in the reverse
+ * direction. Hardware interlocks exist to prevent this from happening.
+ */
+static struct muxed_pin pai_tx_data_0_3[] = {
+PIN(pai_tx_data0, PC3X3_GPIO_PIN_SDGPIO_20, PC3X3_GPIO_PIN_ARM_4,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data1, PC3X3_GPIO_PIN_SDGPIO_21, PC3X3_GPIO_PIN_ARM_5,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data2, PC3X3_GPIO_PIN_SDGPIO_22, PC3X3_GPIO_PIN_ARM_6,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data3, PC3X3_GPIO_PIN_SDGPIO_23, PC3X3_GPIO_PIN_ARM_7,
+    pc3x3_shd_pai_set_mux, pc3x3_shd_pai_get_mux),
+};
+
+static struct pin_group pai_tx_data_0_3_group = {
+	.nr_pins    = ARRAY_SIZE(pai_tx_data_0_3),
+	.name	    = "pai_tx_data[3:0]",
+	.pins	    = pai_tx_data_0_3,
+};
+
+/*
+ * pai_tx_data[7:4] pads - these pads can either be pai_tx_data or arm gpio.
+ */
+static struct muxed_pin pai_tx_data_4_7[] = {
+PIN(pai_tx_data4, -1, PC3X3_GPIO_PIN_ARM_24, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data5, -1, PC3X3_GPIO_PIN_ARM_25, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data6, -1, PC3X3_GPIO_PIN_ARM_26, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_tx_data7, -1, PC3X3_GPIO_PIN_ARM_27, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+};
+
+static struct pin_group pai_tx_data_4_7_group = {
+	.nr_pins    = ARRAY_SIZE(pai_tx_data_4_7),
+	.name	    = "pai_tx_data[7:4]",
+	.pins	    = pai_tx_data_4_7,
+};
+
+/*
+ * pai_rx_data[7:4] pads - these pads can either be pai_rx_data or arm gpio.
+ */
+static struct muxed_pin pai_rx_data_4_7[] = {
+PIN(pai_rx_data4, -1, PC3X3_GPIO_PIN_ARM_28, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data5, -1, PC3X3_GPIO_PIN_ARM_29, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data6, -1, PC3X3_GPIO_PIN_ARM_30, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+PIN(pai_rx_data7, -1, PC3X3_GPIO_PIN_ARM_31, pc3x3_shd_pai_set_mux,
+    pc3x3_shd_pai_get_mux),
+};
+
+static struct pin_group pai_rx_data_4_7_group = {
+	.nr_pins    = ARRAY_SIZE(pai_rx_data_4_7),
+	.name	    = "pai_rx_data[7:4]",
+	.pins	    = pai_rx_data_4_7,
+};
+
+/*
+ * ebi_addr[17:14] pads - these pads can either be ebi_addr or arm gpio.
+ */
+static struct muxed_pin ebi_addr_14_17[] = {
+PIN(ebi_addr14, -1, PC3X3_GPIO_PIN_ARM_32, pc3x3_shd_ebi_set_mux,
+    pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr15, -1, PC3X3_GPIO_PIN_ARM_33, pc3x3_shd_ebi_set_mux,
+    pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr16, -1, PC3X3_GPIO_PIN_ARM_34, pc3x3_shd_ebi_set_mux,
+    pc3x3_shd_ebi_get_mux),
+PIN(ebi_addr17, -1, PC3X3_GPIO_PIN_ARM_35, pc3x3_shd_ebi_set_mux,
+    pc3x3_shd_ebi_get_mux),
+};
+
+static struct pin_group ebi_addr_14_17_group = {
+	.nr_pins    = ARRAY_SIZE(ebi_addr_14_17),
+	.name	    = "ebi_addr[17:14]",
+	.pins	    = ebi_addr_14_17,
+};
+
+static int decode_get_mux(struct muxed_pin *pin)
+{
+	unsigned bit = pin->arm_pin - PC3X3_GPIO_PIN_ARM_36;
+	unsigned long use_decode_gpio =
+		axi2cfg_readl(AXI2CFG_USE_DECODE_GPIO_REG_OFFSET);
+
+	return use_decode_gpio & (1 << bit) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int decode_set_mux(struct muxed_pin *pin,
+			  enum mux_setting setting)
+{
+	unsigned long use_decode_gpio;
+	unsigned bit = pin->arm_pin - PC3X3_GPIO_PIN_ARM_36;
+
+	if (MUX_SD == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+
+	use_decode_gpio = axi2cfg_readl(AXI2CFG_USE_DECODE_GPIO_REG_OFFSET);
+	if (MUX_ARM == setting)
+		use_decode_gpio |= (1 << bit);
+	else
+		use_decode_gpio &= ~(1 << bit);
+	axi2cfg_writel(use_decode_gpio, AXI2CFG_USE_DECODE_GPIO_REG_OFFSET);
+
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+/*
+ * decode[3:0] pads - these pads can either be decode pins or arm gpio.
+ */
+static struct muxed_pin decode_0_3[] = {
+PIN(decode0, -1, PC3X3_GPIO_PIN_ARM_36, decode_set_mux, decode_get_mux),
+PIN(decode1, -1, PC3X3_GPIO_PIN_ARM_37, decode_set_mux, decode_get_mux),
+PIN(decode2, -1, PC3X3_GPIO_PIN_ARM_38, decode_set_mux, decode_get_mux),
+PIN(decode3, -1, PC3X3_GPIO_PIN_ARM_39, decode_set_mux, decode_get_mux),
+};
+
+static struct pin_group decode_0_3_group = {
+	.nr_pins    = ARRAY_SIZE(decode_0_3),
+	.name	    = "decode[3:0]",
+	.pins	    = decode_0_3,
+};
+
+static int ssi_set_mux(struct muxed_pin *pin, enum mux_setting setting);
+
+static int
+ssi_get_mux(struct muxed_pin *pin);
+
+/*
+ * ssi pads - these pads can either be ssi block pins or arm gpio.
+ */
+static struct muxed_pin ssi[] = {
+PIN(ssi_clk, -1, PC3X3_GPIO_PIN_ARM_40, ssi_set_mux, ssi_get_mux),
+PIN(ssi_data_in, -1, PC3X3_GPIO_PIN_ARM_41, ssi_set_mux, ssi_get_mux),
+PIN(ssi_data_out, -1, PC3X3_GPIO_PIN_ARM_42, ssi_set_mux, ssi_get_mux),
+};
+
+static int ssi_get_mux(struct muxed_pin *pin)
+{
+	unsigned long use_misc_int_gpio =
+		axi2cfg_readl(AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+
+	return use_misc_int_gpio & (1 << 0) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int ssi_set_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	unsigned long use_misc_int_gpio;
+
+	if (MUX_SD == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+
+	use_misc_int_gpio = axi2cfg_readl(AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+	if (MUX_PERIPHERAL == setting)
+		use_misc_int_gpio &= ~(1 << 0);
+	else
+		use_misc_int_gpio |= (1 << 0);
+	axi2cfg_writel(use_misc_int_gpio, AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+static struct pin_group ssi_group = {
+	.nr_pins    = ARRAY_SIZE(ssi),
+	.name	    = "ssi",
+	.pins	    = ssi,
+};
+
+static int mii_get_mux(struct muxed_pin *pin)
+{
+	unsigned long syscfg = syscfg_read();
+
+	return syscfg & (1 << 13) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int mii_set_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	/*
+	 * These are automatically configured by hardware if we are in
+	 * reduced MII mode.
+	 */
+	return -EOPNOTSUPP;
+}
+
+/*
+ * mii pads - these pads can either be mii pins or arm gpio.
+ */
+static struct muxed_pin mii[] = {
+PIN(mii_tx_data2, -1, PC3X3_GPIO_PIN_ARM_43, mii_set_mux, mii_get_mux),
+PIN(mii_tx_data3, -1, PC3X3_GPIO_PIN_ARM_44, mii_set_mux, mii_get_mux),
+PIN(mii_rx_data2, -1, PC3X3_GPIO_PIN_ARM_45, mii_set_mux, mii_get_mux),
+PIN(mii_rx_data3, -1, PC3X3_GPIO_PIN_ARM_46, mii_set_mux, mii_get_mux),
+PIN(mii_col, -1, PC3X3_GPIO_PIN_ARM_47, mii_set_mux, mii_get_mux),
+PIN(mii_crs, -1, PC3X3_GPIO_PIN_ARM_48, mii_set_mux, mii_get_mux),
+PIN(mii_tx_clk, -1, PC3X3_GPIO_PIN_ARM_49, mii_set_mux, mii_get_mux),
+};
+
+static struct pin_group mii_group = {
+	.nr_pins    = ARRAY_SIZE(mii),
+	.name	    = "mii",
+	.pins	    = mii,
+};
+
+static int max_set_mux(struct muxed_pin *pin, enum mux_setting setting);
+
+static int max_get_mux(struct muxed_pin *pin);
+
+/*
+ * maxim pads - these pads can either be maxim pins or arm gpio.
+ */
+static struct muxed_pin max[] = {
+PIN(max_tx_ctrl, -1, PC3X3_GPIO_PIN_ARM_50, max_set_mux, max_get_mux),
+PIN(max_ref_clk, -1, PC3X3_GPIO_PIN_ARM_51, max_set_mux, max_get_mux),
+PIN(max_trig_clk, -1, PC3X3_GPIO_PIN_ARM_52, max_set_mux, max_get_mux),
+};
+
+static int max_get_mux(struct muxed_pin *pin)
+{
+	unsigned long use_misc_int_gpio =
+		axi2cfg_readl(AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+
+	return use_misc_int_gpio & (1 << 1) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int max_set_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	unsigned long use_misc_int_gpio;
+
+	if (MUX_SD == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+
+	use_misc_int_gpio = axi2cfg_readl(AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+	if (MUX_PERIPHERAL == setting)
+		use_misc_int_gpio &= ~(1 << 1);
+	else
+		use_misc_int_gpio |= (1 << 1);
+	axi2cfg_writel(use_misc_int_gpio, AXI2CFG_USE_MISC_INT_GPIO_REG_OFFSET);
+
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+
+static struct pin_group max_group = {
+	.nr_pins    = ARRAY_SIZE(max),
+	.name	    = "mii",
+	.pins	    = max,
+};
+
+static int ebi_clk_get_mux(struct muxed_pin *pin)
+{
+	unsigned long ebi_mux = axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	return ebi_mux & (1 << 13) ? MUX_ARM : MUX_PERIPHERAL;
+}
+
+static int ebi_clk_set_mux(struct muxed_pin *pin, enum mux_setting setting)
+{
+	unsigned long ebi_mux;
+
+	if (MUX_SD == setting)
+		return -EINVAL;
+
+	spin_lock(&mux_lock);
+
+	ebi_mux = axi2cfg_readl(AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+	if (MUX_PERIPHERAL == setting)
+		ebi_mux &= ~(1 << 13);
+	else
+		ebi_mux |= (1 << 13);
+	axi2cfg_writel(ebi_mux, AXI2CFG_USE_EBI_GPIO_REG_OFFSET);
+
+	spin_unlock(&mux_lock);
+
+	return 0;
+}
+
+/*
+ * ebi clock pads - this pad can either be the ebi clock or an arm gpio.
+ */
+static struct muxed_pin ebi_clk_pin[] = {
+PIN(ebi_clk_pin, -1, PC3X3_GPIO_PIN_ARM_53, ebi_clk_set_mux, ebi_clk_get_mux),
+};
+
+static struct pin_group ebi_clk_group = {
+	.nr_pins    = ARRAY_SIZE(ebi_clk_pin),
+	.name	    = "ebi_clk",
+	.pins	    = ebi_clk_pin,
+};
+
+static struct muxed_pin pc3x3_fracn_pins[] = {
+PIN(sdgpio0, PC3X3_GPIO_PIN_SDGPIO_0, -1, pc3x3_shd_mux, pc3x3_get_shd_mux),
+};
+
+static struct pin_group pc3x3_fracn_group = {
+	.nr_pins    = ARRAY_SIZE(pc3x3_fracn_pins),
+	.name	    = "fracn/sdgpio0",
+	.pins	    = pc3x3_fracn_pins,
+};
+
+static struct pin_group *pc3x3_groups[] = {
+	&armgpio_0_4_group,
+	&pc3x3_shd_group,
+	&boot_mode_group,
+	&sdram_speed_sel_group,
+	&mii_rev_en_group,
+	&mii_rmii_en_group,
+	&mii_speed_sel_group,
+	&ebi_addr_18_25_group,
+	&pai_tx_data_0_3_group,
+	&pai_rx_data_0_3_group,
+	&pai_tx_data_4_7_group,
+	&pai_rx_data_4_7_group,
+	&ebi_addr_14_17_group,
+	&decode_0_3_group,
+	&ssi_group,
+	&mii_group,
+	&max_group,
+	&ebi_clk_group,
+	&pc3x3_fracn_group,
+};
+
+static int armgpio_number(int pin)
+{
+	if (pin < 0 || pin > PC3X3_GPIO_PIN_ARM_53)
+		return -1;
+	return pin;
+}
+
+static int sdgpio_number(int pin)
+{
+	if (pin < PC3X3_GPIO_PIN_SDGPIO_0 ||
+	    pin > PC3X3_GPIO_PIN_SDGPIO_23)
+		return -1;
+	return pin - PC3X3_GPIO_PIN_SDGPIO_0;
+}
+
+static void pc3x3_init_muxing(void)
+{
+	picoxcell_mux_register(pc3x3_groups, ARRAY_SIZE(pc3x3_groups),
+			       armgpio_number, sdgpio_number);
+}
+
+static const char * const pc3x3_armgpio_pins[] = {
+	"arm0",
+	"arm1",
+	"arm2",
+	"arm3",
+	"arm4",
+	"arm5",
+	"arm6",
+	"arm7",
+	"arm8",
+	"arm9",
+	"arm10",
+	"arm11",
+	"arm12",
+	"arm13",
+	"arm14",
+	"arm15",
+	"arm16",
+	"arm17",
+	"arm18",
+	"arm19",
+	"arm20",
+	"arm21",
+	"arm22",
+	"arm23",
+	"arm24",
+	"arm25",
+	"arm26",
+	"arm27",
+	"arm28",
+	"arm29",
+	"arm30",
+	"arm31",
+	"arm32",
+	"arm33",
+	"arm34",
+	"arm35",
+	"arm36",
+	"arm37",
+	"arm38",
+	"arm39",
+	"arm40",
+	"arm41",
+	"arm42",
+	"arm43",
+	"arm44",
+	"arm45",
+	"arm46",
+	"arm47",
+	"arm48",
+	"arm49",
+	"arm50",
+	"arm51",
+};
+
+static const char * const pc3x3_sdgpio_pins[] = {
+	"sdgpio0",
+	"sdgpio1",
+	"sdgpio2",
+	"sdgpio3",
+	"sdgpio4",
+	"sdgpio5",
+	"sdgpio6",
+	"sdgpio7",
+	"sdgpio8",
+	"sdgpio9",
+	"sdgpio10",
+	"sdgpio11",
+	"sdgpio12",
+	"sdgpio13",
+	"sdgpio14",
+	"sdgpio15",
+	"sdgpio16",
+	"sdgpio17",
+	"sdgpio18",
+	"sdgpio19",
+	"sdgpio20",
+	"sdgpio21",
+	"sdgpio22",
+	"sdgpio23",
+};
+
+static const struct picoxcell_timer pc3x3_timers[] = {
+	{
+		.name	= "timer0",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PICOXCELL_TIMER_BASE + 0 * TIMER_SPACING,
+		.irq	= IRQ_TIMER0,
+	},
+	{
+		.name	= "timer1",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PICOXCELL_TIMER_BASE + 1 * TIMER_SPACING,
+		.irq	= IRQ_TIMER1,
+	},
+	{
+		.name	= "timer2",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PC3X3_TIMER2_BASE + 0 * TIMER_SPACING,
+		.irq	= IRQ_TIMER2,
+	},
+	{
+		.name	= "timer3",
+		.type	= TIMER_TYPE_TIMER,
+		.base	= PC3X3_TIMER2_BASE + 1 * TIMER_SPACING,
+		.irq	= IRQ_TIMER3,
+	},
+	{
+		.name	= "rtc",
+		.type	= TIMER_TYPE_RTC,
+		.base	= PICOXCELL_RTCLK_BASE,
+		.irq	= IRQ_RTC,
+	},
+};
+
+static void pc3x3_init(void);
+
+struct picoxcell_soc pc3x3_soc = {
+	.init		= pc3x3_init,
+	.init_clocks	= pc3x3_clk_init,
+	.init_muxing	= pc3x3_init_muxing,
+	.get_boot_mode	= pc3x3_get_boot_mode,
+	.armgpio_pins	= pc3x3_armgpio_pins,
+	.nr_armgpio	= ARRAY_SIZE(pc3x3_armgpio_pins),
+	.armgpio_base	= PC3X3_GPIO_PIN_ARM_0,
+	.sdgpio_pins	= pc3x3_sdgpio_pins,
+	.nr_sdgpio	= ARRAY_SIZE(pc3x3_sdgpio_pins),
+	.sdgpio_base	= PC3X3_GPIO_PIN_SDGPIO_0,
+	.timers		= pc3x3_timers,
+	.nr_timers	= ARRAY_SIZE(pc3x3_timers),
+};
+
+static void pc3x3_init(void)
+{
+	/*
+	 * If we booted in parallel nor mode then we can continue to use the
+	 * EBI. If not then we only have a few bits of the EBI - just enough
+	 * to drive a NAND flash.
+	 */
+	if (syscfg_read() & 0x3)
+		set_bit(PICOXCELL_FEATURE_SW_NAND, pc3x3_soc.features);
+	else
+		set_bit(PICOXCELL_FEATURE_EBI, pc3x3_soc.features);
+	set_bit(PICOXCELL_FEATURE_PM, pc3x3_soc.features);
+	set_bit(PICOXCELL_FEATURE_CPUFREQ, pc3x3_soc.features);
+}
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index eb4e797..2dc4b99 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -47,6 +47,11 @@ struct picoxcell_soc *picoxcell_get_soc(void)
 	case 0x8007:
 		return &pc3x2_soc;
 
+	case 0x20:
+	case 0x21:
+	case 0x22:
+		return &pc3x3_soc;
+
 	default:
 		panic("unsupported device type %lx", device_id);
 	}
@@ -101,6 +106,12 @@ static const char *picoxcell_get_partname(void)
 		part = "pc302";
 	else if (0x8007 == dev_id)
 		part = "pc312";
+	else if (0x20 == dev_id)
+		part = "pc313";
+	else if (0x21 == dev_id)
+		part = "pc323";
+	else if (0x22 == dev_id)
+		part = "pc333";
 	else
 		part = "<unknown>";
 
diff --git a/arch/arm/mach-picoxcell/soc.h b/arch/arm/mach-picoxcell/soc.h
index fccd278..4b4d92f 100644
--- a/arch/arm/mach-picoxcell/soc.h
+++ b/arch/arm/mach-picoxcell/soc.h
@@ -49,6 +49,7 @@ struct picoxcell_soc {
 
 extern struct picoxcell_soc *picoxcell_get_soc(void);
 extern struct picoxcell_soc pc3x2_soc;
+extern struct picoxcell_soc pc3x3_soc;
 
 static inline int picoxcell_has_feature(enum picoxcell_features feat)
 {
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCHv2 05/13] picoxcell: add gpio infrastructure
  2010-11-23 10:06 ` [RFC PATCH 05/13] picoxcell: add gpio infrastructure Jamie Iles
@ 2010-11-30 18:08   ` Jamie Iles
  0 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-30 18:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add a gpio infrastructure for picoXcell devices. This adds support for
both the ARM GPIO peripheral and the SDGPIO pins that are in the
picoArray domain and accessed through the AXI2CFG. This implements a set
of gpiolib chips and also adds an extension to use the sigma-delta
capabilities of the SDGPIO pins in kernelspace.

v2:
        - Fixup the picoxcell gpio driver to use struct irq_data rather
          than irq numbers in the handlers and accessing irq_desc
          directly.
        - Fixup a typo in the set_type function where we didn't check
          correctly for IRQ_TYPE_EDGE_FALLING.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/Makefile            |    3 +-
 arch/arm/mach-picoxcell/gpio.c              |  723 +++++++++++++++++++++++++++
 arch/arm/mach-picoxcell/include/mach/gpio.h |  187 +++++++
 arch/arm/mach-picoxcell/picoxcell_core.c    |    5 +
 4 files changed, 917 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/mach-picoxcell/gpio.c
 create mode 100644 arch/arm/mach-picoxcell/include/mach/gpio.h

diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
index 5e9a7cd..f6b3765 100644
--- a/arch/arm/mach-picoxcell/Makefile
+++ b/arch/arm/mach-picoxcell/Makefile
@@ -1,3 +1,4 @@
 obj-y				:= picoxcell_core.o axi2cfg.o \
 				   time.o \
-				   mux.o
+				   mux.o \
+				   gpio.o
diff --git a/arch/arm/mach-picoxcell/gpio.c b/arch/arm/mach-picoxcell/gpio.c
new file mode 100644
index 0000000..c312796
--- /dev/null
+++ b/arch/arm/mach-picoxcell/gpio.c
@@ -0,0 +1,723 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support at picochip.com
+ */
+#define pr_fmt(fmt) "picoxcellgpio: " fmt
+
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <mach/hardware.h>
+
+#include "mux.h"
+
+static struct {
+	void __iomem	*armgpio_virt;
+	int		armgpio_base;
+	int		sdgpio_base;
+	int		nr_sdgpio;
+} gpio_info;
+
+static DECLARE_BITMAP(pin_status, ARCH_NR_GPIOS);
+static DEFINE_SPINLOCK(armgpio_lock);
+
+static int armgpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	enum mux_setting mux;
+
+	if (test_and_set_bit(offset + chip->base, pin_status))
+		return -EBUSY;
+
+	/* Check the pin has been correctly multiplexed. */
+	mux = picoxcell_get_pin_mux(offset + chip->base);
+	if (!(mux & (MUX_ARM | MUX_UNMUXED))) {
+		/* The pin has an inconsistent mux setting. */
+		pr_warning("attempt to request armgpio%u which is not correctly multiplexed\n",
+			   chip->base + offset);
+		clear_bit(offset + chip->base, pin_status);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void armgpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	clear_bit(offset + chip->base, pin_status);
+}
+
+#define GPIO_SW_PORT_A_EXT_REG_OFFSET	GPIO_EXT_PORT_A_REG_OFFSET
+#define GPIO_SW_PORT_B_EXT_REG_OFFSET	GPIO_EXT_PORT_B_REG_OFFSET
+#define GPIO_SW_PORT_D_EXT_REG_OFFSET	GPIO_EXT_PORT_D_REG_OFFSET
+
+static inline int armgpio_block_nr(unsigned gpio_nr)
+{
+	return gpio_nr - gpio_info.armgpio_base;
+}
+
+#define __ARMGPIO_REG(_gpio_base, _reg)					    \
+	({								    \
+		void __iomem *ret = NULL;				    \
+		int __gpio_nr = armgpio_block_nr(_gpio_base);		    \
+		if (__gpio_nr < 8)					    \
+			ret = (gpio_info.armgpio_virt +			    \
+				GPIO_SW_PORT_A_##_reg##_REG_OFFSET);	    \
+		else if (__gpio_nr < 24)				    \
+			ret = (gpio_info.armgpio_virt +			    \
+				GPIO_SW_PORT_B_##_reg##_REG_OFFSET);	    \
+		else							    \
+			ret = (gpio_info.armgpio_virt +			    \
+				GPIO_SW_PORT_D_##_reg##_REG_OFFSET);	    \
+		ret;							    \
+	})
+
+#define ARMGPIO_DR(_gpio_base)	    __ARMGPIO_REG(_gpio_base, DR)
+#define ARMGPIO_DDR(_gpio_base)	    __ARMGPIO_REG(_gpio_base, DDR)
+#define ARMGPIO_CTL(_gpio_base)	    __ARMGPIO_REG(_gpio_base, CTL)
+#define ARMGPIO_EXT(_gpio_base)	    __ARMGPIO_REG(_gpio_base, EXT)
+
+static inline unsigned armgpio_offset(unsigned offset)
+{
+	/*
+	 * The arm gpios are controlled via three sets of registers. The
+	 * register addressing is already taken care of by the __ARMGPIO_REG
+	 * macro, this takes care of the bit offsets within each register.
+	 */
+	if (offset < 8) /* GPIO Port A*/
+		return offset;
+	else if (offset < 24) /* GPIO Port B */
+		return offset - 8;
+	else /* GPIO Port D */
+		return offset - 24;
+}
+
+static int armgpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	void __iomem *ddr = ARMGPIO_DDR(chip->base + offset);
+	void __iomem *cr = ARMGPIO_CTL(chip->base + offset);
+	unsigned long flags, val, bit_offset = armgpio_offset(offset);
+
+	spin_lock_irqsave(&armgpio_lock, flags);
+	/* Mark the pin as an output. */
+	val = readl(ddr);
+	val &= ~(1 << bit_offset);
+	writel(val, ddr);
+
+	/* Set the pin as software controlled. */
+	val = readl(cr);
+	val &= ~(1 << bit_offset);
+	writel(val, cr);
+	spin_unlock_irqrestore(&armgpio_lock, flags);
+
+	return 0;
+}
+
+static void armgpio_set(struct gpio_chip *chip, unsigned offset, int value);
+
+static int armgpio_direction_output(struct gpio_chip *chip, unsigned offset,
+				    int value)
+{
+	void __iomem *ddr = ARMGPIO_DDR(chip->base + offset);
+	void __iomem *cr = ARMGPIO_CTL(chip->base + offset);
+	unsigned long flags, val, bit_offset = armgpio_offset(offset);
+
+	/* Set the value first so we don't glitch. */
+	armgpio_set(chip, offset, value);
+
+	spin_lock_irqsave(&armgpio_lock, flags);
+	/* Mark the pin as an output. */
+	val = readl(ddr);
+	val |= (1 << bit_offset);
+	writel(val, ddr);
+
+	/* Set the pin as software controlled. */
+	val = readl(cr);
+	val &= ~(1 << bit_offset);
+	writel(val, cr);
+	spin_unlock_irqrestore(&armgpio_lock, flags);
+
+	return 0;
+}
+
+static int armgpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	void __iomem *ext = ARMGPIO_EXT(chip->base + offset);
+	unsigned long bit_offset = armgpio_offset(offset);
+
+	return !!(readl(ext) & (1 << bit_offset));
+}
+
+static void armgpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	void __iomem *dr = ARMGPIO_DR(chip->base + offset);
+	unsigned long val, flags, bit_offset = armgpio_offset(offset);
+
+	spin_lock_irqsave(&armgpio_lock, flags);
+	val = readl(dr);
+	val &= ~(1 << bit_offset);
+	val |= (!!value << bit_offset);
+	writel(val, dr);
+	spin_unlock_irqrestore(&armgpio_lock, flags);
+}
+
+static int armgpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset < gpio_info.armgpio_base + 8)
+		return IRQ_GPIO0 + offset;
+	return -EINVAL;
+}
+
+#define INT_EN_REG	    (gpio_info.armgpio_virt + GPIO_INT_EN_REG_OFFSET)
+#define INT_MASK_REG	    (gpio_info.armgpio_virt + GPIO_INT_MASK_REG_OFFSET)
+#define INT_TYPE_REG	    (gpio_info.armgpio_virt + \
+			     GPIO_INT_TYPE_LEVEL_REG_OFFSET)
+#define INT_POLARITY_REG    (gpio_info.armgpio_virt + \
+			     GPIO_INT_POLARITY_REG_OFFSET)
+#define INT_STATUS_REG	    (gpio_info.armgpio_virt + \
+			     GPIO_INT_STATUS_REG_OFFSET)
+#define EOI_REG		    (gpio_info.armgpio_virt + \
+			     GPIO_PORT_A_EOI_REG_OFFSET)
+
+static void armgpio_irq_enable(struct irq_data *data)
+{
+	int gpio = irq_to_gpio(data->irq);
+	void __iomem *port_inten = INT_EN_REG;
+	unsigned long val;
+
+	val = readl(port_inten);
+	val |= (1 << gpio);
+	writel(val, port_inten);
+}
+
+static void armgpio_irq_disable(struct irq_data *data)
+{
+	int gpio = irq_to_gpio(data->irq);
+	void __iomem *port_inten = INT_EN_REG;
+	unsigned long val;
+
+	val = readl(port_inten);
+	val &= ~(1 << gpio);
+	writel(val, port_inten);
+}
+
+static void armgpio_irq_mask(struct irq_data *data)
+{
+	int gpio = irq_to_gpio(data->irq);
+	void __iomem *port_mask = INT_MASK_REG;
+	unsigned long val;
+
+	val = readl(port_mask);
+	val |= (1 << gpio);
+	writel(val, port_mask);
+}
+
+static void armgpio_irq_ack(struct irq_data *data)
+{
+	int gpio = irq_to_gpio(data->irq);
+	void __iomem *port_intmask = INT_MASK_REG;
+	void __iomem *port_eoi = EOI_REG;
+	void __iomem *port_inttype_level = INT_TYPE_REG;
+	unsigned long val;
+
+	val = readl(port_inttype_level);
+
+	if (val & (1 << gpio)) {
+		/* Edge-sensitive */
+		val = readl(port_eoi);
+		val |= (1 << gpio);
+		writel(val, port_eoi);
+	} else {
+		/* Level-sensitive */
+		val = readl(port_intmask);
+		val |= (1 << gpio);
+		writel(val, port_intmask);
+	}
+}
+
+static void armgpio_irq_unmask(struct irq_data *data)
+{
+	int gpio = irq_to_gpio(data->irq);
+	void __iomem *port_intmask = INT_MASK_REG;
+	unsigned long val;
+
+	val = readl(port_intmask);
+	val &= ~(1 << gpio);
+	writel(val, port_intmask);
+}
+
+static int armgpio_irq_set_type(struct irq_data *data, unsigned int trigger)
+{
+	int gpio = irq_to_gpio(data->irq);
+	void __iomem *port_inttype_level = INT_TYPE_REG;
+	void __iomem *port_int_polarity = INT_POLARITY_REG;
+	unsigned long level, polarity;
+
+	if (trigger & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
+			IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+		return -EINVAL;
+
+	level = readl(port_inttype_level);
+	polarity = readl(port_int_polarity);
+
+	if (trigger & IRQ_TYPE_EDGE_RISING) {
+		level	    |= (1 << gpio);
+		polarity    |= (1 << gpio);
+	} else if (trigger & IRQ_TYPE_EDGE_FALLING) {
+		level	    |= (1 << gpio);
+		polarity    &= ~(1 << gpio);
+	} else if (trigger & IRQ_TYPE_LEVEL_HIGH) {
+		level	    &= ~(1 << gpio);
+		polarity    |= (1 << gpio);
+	} else if (trigger & IRQ_TYPE_LEVEL_LOW) {
+		level	    &= ~(1 << gpio);
+		polarity    &= ~(1 << gpio);
+	}
+
+	writel(level, port_inttype_level);
+	writel(polarity, port_int_polarity);
+
+	if ((trigger & IRQ_TYPE_EDGE_RISING) ||
+	    (trigger & IRQ_TYPE_EDGE_FALLING))
+		set_irq_handler(data->irq, handle_edge_irq);
+	else
+		set_irq_handler(data->irq, handle_level_irq);
+
+	return 0;
+}
+
+struct irq_chip armgpio_irqchip = {
+	.name			= "armgpio",
+	.irq_ack	  	= armgpio_irq_ack,
+	.irq_mask	  	= armgpio_irq_mask,
+	.irq_unmask   		= armgpio_irq_unmask,
+	.irq_enable   		= armgpio_irq_enable,
+	.irq_disable  		= armgpio_irq_disable,
+	.irq_set_type 		= armgpio_irq_set_type,
+};
+
+static struct gpio_chip armgpio_chip = {
+	.owner		    = THIS_MODULE,
+	.label		    = "armgpio",
+	.request	    = armgpio_request,
+	.free		    = armgpio_free,
+	.direction_input    = armgpio_direction_input,
+	.direction_output   = armgpio_direction_output,
+	.get		    = armgpio_get,
+	.set		    = armgpio_set,
+	.to_irq		    = armgpio_to_irq,
+};
+
+/* The base address of SD-GPIO config registers in the AXI2Pico. */
+#define PC3X2_GPIO_SD_PIN_CONFIG_BASE		0x9800
+/* The base address of SD-GPIO analogue value registers in the AXI2Pico. */
+#define PC3X2_GPIO_SD_PIN_ANALOGUE_VALUE_BASE	0x9801
+/* The base address of SD-GPIO analogue rate registers in the AXI2Pico. */
+#define PC3X2_GPIO_SD_PIN_ANALOGUE_RATE_BASE	0x9802
+/* The address of the control value register in the AXI2Pico. */
+#define PC3X2_GPIO_SD_CONTROL_VAL_REG		0x9882
+/* The address of the control value high register in the AXI2Pico (pc3x3). */
+#define PC3X2_GPIO_SD_CONTROL_VAL_HI_REG	0x9883
+/* The address of the output value register in the AXI2Pico. */
+#define PC3X2_GPIO_SD_OUTPUT_VAL_REG		0x9884
+/* The address of the output value high register in the AXI2Pico (pc3x3). */
+#define PC3X2_GPIO_SD_OUTPUT_HI_VAL_REG		0x9885
+/* The address of the input value register in the AXI2Pico. */
+#define PC3X2_GPIO_SD_INPUT_VAL_REG		0x9880
+/* The address of the input value high register in the AXI2Pico (pc3x3). */
+#define PC3X2_GPIO_SD_INPUT_VAL_HI_REG		0x9880
+/* The address of the sleep register in the AXI2Pico. */
+#define PICOXCELL_AXI2PICO_SLEEP_REG		0xA060
+/* The spacing between SD-GPIO config registers. */
+#define PC3X2_GPIO_SD_PIN_CONFIG_SPACING	4
+/* Control source bit. */
+#define PC3X2_GPIO_SD_CONFIG_CS_MASK		(~(1 << 15))
+/* Analogue not digital bit. */
+#define PC3X2_GPIO_SD_CONFIG_AND		(1 << 14)
+/* The mask for analogue converter size in the config register. */
+#define PC3X2_GPIO_SD_CONV_SZ_MASK		0xF
+/* Soft reset lock bit. */
+#define PC3X2_GPIO_SD_CONFIG_SR_LOCK		(1 << 13)
+/* PICOXCELL AXI2Pico CAEID. */
+#define PICOXCELL_AXI2PICO_CAEID		0x9000
+
+/*
+ * Get the address of a config register for a SD-GPIO pin.
+ *
+ * @_n The SD-GPIO pin number.
+ *
+ * Returns the base address of the register.
+ */
+#define PC3X2_GPIO_SD_PIN_CONFIG(_n) \
+	(PC3X2_GPIO_SD_PIN_CONFIG_BASE + \
+	 ((_n) * PC3X2_GPIO_SD_PIN_CONFIG_SPACING))
+
+/*
+ * Get the address of a analogue rate register for a SD-GPIO pin.
+ *
+ * @_n The SD-GPIO pin number.
+ *
+ * Returns the base address of the register.
+ */
+#define PC3X2_GPIO_SD_PIN_ANALOGUE_RATE(_n) \
+	(PC3X2_GPIO_SD_PIN_ANALOGUE_RATE_BASE + \
+	 ((_n) * PC3X2_GPIO_SD_PIN_CONFIG_SPACING))
+
+/*
+ * Get the address of a analogue value register for a SD-GPIO pin.
+ *
+ * @_n The SD-GPIO pin number.
+ *
+ * Returns the base address of the register.
+ */
+#define PC3X2_GPIO_SD_PIN_ANALOGUE_VAL(_n) \
+	(PC3X2_GPIO_SD_PIN_ANALOGUE_VALUE_BASE + \
+	 ((_n) * PC3X2_GPIO_SD_PIN_CONFIG_SPACING))
+
+static int sdgpio_reset_config(unsigned block_pin, int value)
+{
+	int ret;
+	u16 data;
+
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+				  PC3X2_GPIO_SD_PIN_CONFIG(block_pin),
+				  &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read config register for SDGPIO pin %u\n",
+		       block_pin);
+		return -EIO;
+	}
+
+	if (value)
+		data |= PC3X2_GPIO_SD_CONFIG_SR_LOCK;
+	else
+		data &= ~PC3X2_GPIO_SD_CONFIG_SR_LOCK;
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_PIN_CONFIG(block_pin),
+			     &data, 1);
+
+	return 0;
+}
+
+static inline int sdgpio_block_nr(unsigned gpio_nr)
+{
+	return gpio_nr - gpio_info.sdgpio_base;
+}
+
+static int sdgpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+	enum mux_setting mux;
+
+	if (test_and_set_bit(offset + chip->base, pin_status))
+		return -EBUSY;
+
+	if (sdgpio_reset_config(block_pin, 1)) {
+		clear_bit(offset + chip->base, pin_status);
+		return -EIO;
+	}
+
+	/* Check the pin has been correctly multiplexed. */
+	mux = picoxcell_get_pin_mux(offset + chip->base);
+	if (!(mux & (MUX_SD | MUX_UNMUXED))) {
+		/* The pin has an inconsistent mux setting. */
+		pr_warning("attempt to request sdgpio%u which is not correctly multiplexed\n",
+			   block_pin);
+		clear_bit(offset + chip->base, pin_status);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void sdgpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	clear_bit(offset + chip->base, pin_status);
+	picoxcell_gpio_configure_dac(chip->base + offset, 0, 0);
+}
+
+/*
+ * Create a map of which pins are analogue and not digital. We have a separate
+ * function for configuring pins as analogue. When we set analogue pins, we
+ * don't treat the int parameter as a boolean anymore.
+ */
+static DECLARE_BITMAP(a_not_d_map, ARCH_NR_GPIOS);
+
+static int sdgpio_get_digital_out_status(u32 *v)
+{
+	u16 data[2] = { 0, 0 };
+
+	if (1 != axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+				PC3X2_GPIO_SD_OUTPUT_VAL_REG, &data[0], 1))
+		return -EIO;
+
+	if (gpio_info.nr_sdgpio > 16) {
+		if (1 != axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+					PC3X2_GPIO_SD_OUTPUT_HI_VAL_REG,
+					&data[1], 1))
+			return -EIO;
+	}
+
+	*v = data[0] | (data[1] << 16);
+
+	return 0;
+}
+
+static void sdgpio_set_digital_out_status(u32 v)
+{
+	u16 data[2] = { (u16)(v & 0xFFFF), (u16)((v >> 16) & 0xFFFF) };
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_OUTPUT_VAL_REG, &data[0], 1);
+
+	if (gpio_info.nr_sdgpio > 16) {
+		axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+				     PC3X2_GPIO_SD_OUTPUT_HI_VAL_REG,
+				     &data[1], 1);
+	}
+}
+
+static void sdgpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	u16 data;
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+
+	if (!test_bit(chip->base + offset, a_not_d_map)) {
+		u32 status;
+
+		if (sdgpio_get_digital_out_status(&status)) {
+			pr_err("failed to read SDGPIO output value reg\n");
+			return;
+		}
+
+		status &= ~(1 << block_pin);
+		status |= (!!value) << block_pin;
+
+		sdgpio_set_digital_out_status(status);
+	} else {
+		/* Analogue mode */
+		data = (u16)value;
+		axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+				     PC3X2_GPIO_SD_PIN_ANALOGUE_VAL(block_pin),
+				     &data, 1);
+	}
+}
+
+static int sdgpio_get_digital_in_status(u32 *v)
+{
+	u16 data[2] = { 0, 0 };
+
+	if (1 != axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+				PC3X2_GPIO_SD_INPUT_VAL_REG, &data[0], 1))
+		return -EIO;
+
+	if (gpio_info.nr_sdgpio > 16) {
+		if (1 != axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+					PC3X2_GPIO_SD_INPUT_VAL_HI_REG,
+					&data[1], 1))
+			return -EIO;
+	}
+
+	*v = data[0] | (data[1] << 16);
+
+	return 0;
+}
+
+static int sdgpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	int ret;
+	u16 data;
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+
+	if (!test_bit(chip->base + offset, a_not_d_map)) {
+		u32 status;
+
+		if (sdgpio_get_digital_in_status(&status))
+			return -EIO;
+
+		return !!(status & (1 << block_pin));
+	} else {
+		/* Analogue mode */
+		ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+				PC3X2_GPIO_SD_PIN_ANALOGUE_VAL(block_pin),
+				&data, 1);
+		if (1 != ret) {
+			pr_err("failed to read the analogue value register for SDGPIO pin %u\n",
+			       block_pin);
+			return -EIO;
+		}
+
+		return (int)data;
+	}
+}
+
+static int sdgpio_set_direction(unsigned block_pin, int input)
+{
+	int ret;
+	u16 data;
+
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+			PC3X2_GPIO_SD_PIN_CONFIG(block_pin), &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read config register for SDGPIO pin %u\n",
+		       block_pin);
+		return -EIO;
+	}
+
+	data &= PC3X2_GPIO_SD_CONFIG_CS_MASK;
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_PIN_CONFIG(block_pin), &data, 1);
+
+	/* Configure the pin to drive or not drive the output as appropriate. */
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+			PC3X2_GPIO_SD_CONTROL_VAL_REG, &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read SDGPIO control value register\n");
+		return -EIO;
+	}
+
+	if (input)
+		data &= ~(1 << block_pin);
+	else
+		data |= (1 << block_pin);
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_CONTROL_VAL_REG, &data, 1);
+
+	return 0;
+}
+
+static int sdgpio_direction_output(struct gpio_chip *chip, unsigned offset,
+				   int value)
+{
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+	int ret = sdgpio_set_direction(block_pin, 0);
+
+	if (ret)
+		return ret;
+
+	sdgpio_set(chip, offset, value);
+
+	return 0;
+}
+
+static int sdgpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	unsigned block_pin = sdgpio_block_nr(chip->base + offset);
+
+	return sdgpio_set_direction(block_pin, 1);
+}
+
+int picoxcell_gpio_configure_dac(unsigned gpio, u8 converter_size,
+				 u16 analogue_rate)
+{
+	int ret;
+	u16 data;
+	unsigned block_pin = sdgpio_block_nr(gpio);
+
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+			PC3X2_GPIO_SD_PIN_CONFIG(block_pin), &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read config register for SDGPIO pin %u\n",
+		       block_pin);
+		return -EIO;
+	}
+
+	data &= PC3X2_GPIO_SD_CONFIG_CS_MASK;
+	data &= ~PC3X2_GPIO_SD_CONV_SZ_MASK;
+	if (!analogue_rate && !converter_size)
+		data &= ~PC3X2_GPIO_SD_CONFIG_AND;
+	else
+		data |= PC3X2_GPIO_SD_CONFIG_AND;
+	data |= (converter_size & PC3X2_GPIO_SD_CONV_SZ_MASK);
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_PIN_CONFIG(block_pin), &data, 1);
+
+	/* Configure the pin to drive the output. */
+	ret = axi2cfg_config_read(PICOXCELL_AXI2PICO_CAEID,
+			PC3X2_GPIO_SD_CONTROL_VAL_REG, &data, 1);
+	if (1 != ret) {
+		pr_err("failed to read SDGPIO control value register\n");
+		return -EIO;
+	}
+
+	data |= (1 << block_pin);
+
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_CONTROL_VAL_REG, &data, 1);
+
+	/* Write the analogue rate register */
+	data = analogue_rate;
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PC3X2_GPIO_SD_PIN_ANALOGUE_RATE(block_pin),
+			     &data, 1);
+
+	if (analogue_rate || converter_size)
+		set_bit(gpio, a_not_d_map);
+	else
+		clear_bit(gpio, a_not_d_map);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(picoxcell_gpio_configure_dac);
+
+static struct gpio_chip sdgpio_chip = {
+	.owner		    = THIS_MODULE,
+	.label		    = "sdgpio",
+	.request	    = sdgpio_request,
+	.free		    = sdgpio_free,
+	.direction_input    = sdgpio_direction_input,
+	.direction_output   = sdgpio_direction_output,
+	.get		    = sdgpio_get,
+	.set		    = sdgpio_set,
+};
+
+int __init picoxcell_gpio_init(const char * const armgpio_pins[],
+			       int nr_armgpio, int armgpio_base,
+			       const char * const sdgpio_pins[],
+			       int nr_sdgpio, int sdgpio_base)
+{
+	int ret = 0;
+	u16 data = 0;
+
+	/*
+	 * Make sure that the AXI2Pico is awake for the SDGPIO transactions.
+	 */
+	axi2cfg_config_write(PICOXCELL_AXI2PICO_CAEID,
+			     PICOXCELL_AXI2PICO_SLEEP_REG, &data, 1);
+
+	gpio_info.armgpio_virt	= ioremap(PICOXCELL_GPIO_BASE, SZ_4K);
+	gpio_info.armgpio_base	= armgpio_base;
+	gpio_info.sdgpio_base	= sdgpio_base;
+	gpio_info.nr_sdgpio	= nr_sdgpio;
+
+	armgpio_chip.ngpio	= nr_armgpio;
+	armgpio_chip.names	= armgpio_pins;
+	armgpio_chip.base	= armgpio_base;
+	sdgpio_chip.ngpio	= nr_sdgpio;
+	sdgpio_chip.names	= sdgpio_pins;
+	sdgpio_chip.base	= sdgpio_base;
+
+	ret = gpiochip_add(&armgpio_chip);
+	if (ret) {
+		pr_err("failed to register %s\n", armgpio_chip.label);
+		goto out;
+	}
+
+	ret = gpiochip_add(&sdgpio_chip);
+	if (ret) {
+		pr_err("failed to register %s\n", sdgpio_chip.label);
+		goto out;
+	}
+
+out:
+	return ret;
+}
diff --git a/arch/arm/mach-picoxcell/include/mach/gpio.h b/arch/arm/mach-picoxcell/include/mach/gpio.h
new file mode 100644
index 0000000..ce55c3d
--- /dev/null
+++ b/arch/arm/mach-picoxcell/include/mach/gpio.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010 Picochip Ltd., Jamie Iles
+ *
+ * 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 __MACH_GPIO_H__
+#define __MACH_GPIO_H__
+
+#ifdef __KERNEL__
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm-generic/gpio.h>
+
+int __init picoxcell_gpio_init(const char * const armgpio_pins[],
+			       int nr_armgpio, int armgpio_base,
+			       const char * const sdgpio_pins[],
+			       int nr_sdgpio, int sdgpio_base);
+
+extern struct irq_chip armgpio_irqchip;
+
+#endif /* __KERNEL__ */
+
+enum {
+	PC3X2_GPIO_PIN_ARM_0,
+	PC3X2_GPIO_PIN_ARM_1,
+	PC3X2_GPIO_PIN_ARM_2,
+	PC3X2_GPIO_PIN_ARM_3,
+	PC3X2_GPIO_PIN_ARM_4,
+	PC3X2_GPIO_PIN_ARM_5,
+	PC3X2_GPIO_PIN_ARM_6,
+	PC3X2_GPIO_PIN_ARM_7,
+	PC3X2_GPIO_PIN_ARM_8,
+	PC3X2_GPIO_PIN_ARM_9,
+	PC3X2_GPIO_PIN_ARM_10,
+	PC3X2_GPIO_PIN_ARM_11,
+	PC3X2_GPIO_PIN_ARM_12,
+	PC3X2_GPIO_PIN_ARM_13,
+	PC3X2_GPIO_PIN_ARM_14,
+	PC3X2_GPIO_PIN_ARM_15,
+	PC3X2_GPIO_PIN_SDGPIO_0,
+	PC3X2_GPIO_PIN_SDGPIO_1,
+	PC3X2_GPIO_PIN_SDGPIO_2,
+	PC3X2_GPIO_PIN_SDGPIO_3,
+	PC3X2_GPIO_PIN_SDGPIO_4,
+	PC3X2_GPIO_PIN_SDGPIO_5,
+	PC3X2_GPIO_PIN_SDGPIO_6,
+	PC3X2_GPIO_PIN_SDGPIO_7,
+	PC3X2_GPIO_PIN_SDGPIO_8,
+	PC3X2_GPIO_PIN_SDGPIO_9,
+	PC3X2_GPIO_PIN_SDGPIO_10,
+	PC3X2_GPIO_PIN_SDGPIO_11,
+	PC3X2_GPIO_PIN_SDGPIO_12,
+	PC3X2_GPIO_PIN_SDGPIO_13,
+	PC3X2_GPIO_PIN_SDGPIO_14,
+	PC3X2_GPIO_PIN_SDGPIO_15,
+};
+
+enum {
+	PC3X3_GPIO_PIN_ARM_0,
+	PC3X3_GPIO_PIN_ARM_1,
+	PC3X3_GPIO_PIN_ARM_2,
+	PC3X3_GPIO_PIN_ARM_3,
+	PC3X3_GPIO_PIN_ARM_4,
+	PC3X3_GPIO_PIN_ARM_5,
+	PC3X3_GPIO_PIN_ARM_6,
+	PC3X3_GPIO_PIN_ARM_7,
+	PC3X3_GPIO_PIN_ARM_8,
+	PC3X3_GPIO_PIN_ARM_9,
+	PC3X3_GPIO_PIN_ARM_10,
+	PC3X3_GPIO_PIN_ARM_11,
+	PC3X3_GPIO_PIN_ARM_12,
+	PC3X3_GPIO_PIN_ARM_13,
+	PC3X3_GPIO_PIN_ARM_14,
+	PC3X3_GPIO_PIN_ARM_15,
+	PC3X3_GPIO_PIN_ARM_16,
+	PC3X3_GPIO_PIN_ARM_17,
+	PC3X3_GPIO_PIN_ARM_18,
+	PC3X3_GPIO_PIN_ARM_19,
+	PC3X3_GPIO_PIN_ARM_20,
+	PC3X3_GPIO_PIN_ARM_21,
+	PC3X3_GPIO_PIN_ARM_22,
+	PC3X3_GPIO_PIN_ARM_23,
+	PC3X3_GPIO_PIN_ARM_24,
+	PC3X3_GPIO_PIN_ARM_25,
+	PC3X3_GPIO_PIN_ARM_26,
+	PC3X3_GPIO_PIN_ARM_27,
+	PC3X3_GPIO_PIN_ARM_28,
+	PC3X3_GPIO_PIN_ARM_29,
+	PC3X3_GPIO_PIN_ARM_30,
+	PC3X3_GPIO_PIN_ARM_31,
+	PC3X3_GPIO_PIN_ARM_32,
+	PC3X3_GPIO_PIN_ARM_33,
+	PC3X3_GPIO_PIN_ARM_34,
+	PC3X3_GPIO_PIN_ARM_35,
+	PC3X3_GPIO_PIN_ARM_36,
+	PC3X3_GPIO_PIN_ARM_37,
+	PC3X3_GPIO_PIN_ARM_38,
+	PC3X3_GPIO_PIN_ARM_39,
+	PC3X3_GPIO_PIN_ARM_40,
+	PC3X3_GPIO_PIN_ARM_41,
+	PC3X3_GPIO_PIN_ARM_42,
+	PC3X3_GPIO_PIN_ARM_43,
+	PC3X3_GPIO_PIN_ARM_44,
+	PC3X3_GPIO_PIN_ARM_45,
+	PC3X3_GPIO_PIN_ARM_46,
+	PC3X3_GPIO_PIN_ARM_47,
+	PC3X3_GPIO_PIN_ARM_48,
+	PC3X3_GPIO_PIN_ARM_49,
+	PC3X3_GPIO_PIN_ARM_50,
+	PC3X3_GPIO_PIN_ARM_51,
+	PC3X3_GPIO_PIN_ARM_52,
+	PC3X3_GPIO_PIN_ARM_53,
+	PC3X3_GPIO_PIN_SDGPIO_0,
+	PC3X3_GPIO_PIN_SDGPIO_1,
+	PC3X3_GPIO_PIN_SDGPIO_2,
+	PC3X3_GPIO_PIN_SDGPIO_3,
+	PC3X3_GPIO_PIN_SDGPIO_4,
+	PC3X3_GPIO_PIN_SDGPIO_5,
+	PC3X3_GPIO_PIN_SDGPIO_6,
+	PC3X3_GPIO_PIN_SDGPIO_7,
+	PC3X3_GPIO_PIN_SDGPIO_8,
+	PC3X3_GPIO_PIN_SDGPIO_9,
+	PC3X3_GPIO_PIN_SDGPIO_10,
+	PC3X3_GPIO_PIN_SDGPIO_11,
+	PC3X3_GPIO_PIN_SDGPIO_12,
+	PC3X3_GPIO_PIN_SDGPIO_13,
+	PC3X3_GPIO_PIN_SDGPIO_14,
+	PC3X3_GPIO_PIN_SDGPIO_15,
+	PC3X3_GPIO_PIN_SDGPIO_16,
+	PC3X3_GPIO_PIN_SDGPIO_17,
+	PC3X3_GPIO_PIN_SDGPIO_18,
+	PC3X3_GPIO_PIN_SDGPIO_19,
+	PC3X3_GPIO_PIN_SDGPIO_20,
+	PC3X3_GPIO_PIN_SDGPIO_21,
+	PC3X3_GPIO_PIN_SDGPIO_22,
+	PC3X3_GPIO_PIN_SDGPIO_23,
+};
+
+#ifdef __KERNEL__
+
+static inline int gpio_get_value(unsigned gpio)
+{
+	return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned gpio, int value)
+{
+	__gpio_set_value(gpio, value);
+}
+
+static inline int gpio_cansleep(unsigned gpio)
+{
+	return 0;
+}
+
+static inline int gpio_to_irq(unsigned gpio)
+{
+	return __gpio_to_irq(gpio);
+}
+
+static inline int irq_to_gpio(unsigned irq)
+{
+	if (irq > IRQ_GPIO7)
+		return -EINVAL;
+	return irq - IRQ_GPIO0;
+}
+
+extern int picoxcell_gpio_configure_dac(unsigned gpio, u8 converter_size,
+					u16 analogue_rate);
+
+#endif /* __KERNEL__ */
+
+#endif /* __MACH_GPIO_H__ */
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 967f1d2..73bb8df 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -8,6 +8,7 @@
  * All enquiries to support at picochip.com
  */
 #include <linux/debugfs.h>
+#include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/irq.h>
@@ -274,6 +275,10 @@ void __init picoxcell_core_init(void)
 	/* Initialise the pin muxing and gpio infrastructure. */
 	picoxcell_muxing_init(soc);
 
+	picoxcell_gpio_init(soc->armgpio_pins, soc->nr_armgpio,
+			    soc->armgpio_base, soc->sdgpio_pins,
+			    soc->nr_sdgpio, soc->sdgpio_base);
+
 	/* Add handlers for the AXI bus snooping. */
 	picoxcell_axi_bus_error_init();
 }
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCHv2 10/13] picoxcell: add chained GPIO IRQ handlers
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (12 preceding siblings ...)
  2010-11-23 10:06 ` [RFC PATCH 13/13] picoxcell: add support for the PC7302 development board Jamie Iles
@ 2010-11-30 18:10 ` Jamie Iles
  2010-12-06 10:25 ` ARM: add initial support for Picochip picoXcell SoC Jamie Iles
  14 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-11-30 18:10 UTC (permalink / raw)
  To: linux-arm-kernel

The 8 bits of port A on the ARM GPIO of all devices are capable of
generating interrupts. Provide chained handlers so that these pins can
be used as interrupts and the sensitivity and polarity can be set.

v2:
	- Use the generic IRQ handlers rather than using the irq_desc.

Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/arm/mach-picoxcell/include/mach/gpio.h |    2 +
 arch/arm/mach-picoxcell/picoxcell_core.c    |   39 +++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-picoxcell/include/mach/gpio.h b/arch/arm/mach-picoxcell/include/mach/gpio.h
index ce55c3d..8955e20 100644
--- a/arch/arm/mach-picoxcell/include/mach/gpio.h
+++ b/arch/arm/mach-picoxcell/include/mach/gpio.h
@@ -152,6 +152,8 @@ enum {
 
 #ifdef __KERNEL__
 
+extern struct irq_chip armgpio_irqchip;
+
 static inline int gpio_get_value(unsigned gpio)
 {
 	return __gpio_get_value(gpio);
diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
index 8da0c34..1685df8 100644
--- a/arch/arm/mach-picoxcell/picoxcell_core.c
+++ b/arch/arm/mach-picoxcell/picoxcell_core.c
@@ -25,6 +25,18 @@
 #include "picoxcell_core.h"
 #include "soc.h"
 
+static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	unsigned real_irq = (irq - IRQ_GPIO0 + __IRQ_GPIO0);
+	/*
+	 * We have 8 GPIO pins that can generate interrupts and each one has
+	 * it's own interrupt bit in the VIC. All we need to do is do a
+	 * one-to-one translation from the virtual IRQ number to the real one
+	 * and call the handler.
+	 */
+	generic_handle_irq(real_irq);
+}
+
 struct picoxcell_soc *picoxcell_get_soc(void)
 {
 	unsigned long device_id = readl(IO_ADDRESS(PICOXCELL_AXI2CFG_BASE +
@@ -37,6 +49,11 @@ struct picoxcell_soc *picoxcell_get_soc(void)
 
 void __init picoxcell_init_irq(void)
 {
+#define GPIO_INT_EN_REG	    IO_ADDRESS(PICOXCELL_GPIO_BASE + \
+				       GPIO_INT_EN_REG_OFFSET)
+#define GPIO_EOI_REG	    IO_ADDRESS(PICOXCELL_GPIO_BASE + \
+				       GPIO_PORT_A_EOI_REG_OFFSET)
+	unsigned i;
 	u32 vic0_resume_sources =
 		(1 << (IRQ_AXI2PICO8 & 31)) |
 		(1 << (IRQ_EMAC & 31)) |
@@ -45,6 +62,28 @@ void __init picoxcell_init_irq(void)
 	vic_init(IO_ADDRESS(PICOXCELL_VIC0_BASE), 32, 0xFFFFFFFE,
 		 vic0_resume_sources);
 	vic_init(IO_ADDRESS(PICOXCELL_VIC1_BASE), 0, 0x7FF, 0);
+
+	/*
+	 * We want to enable/disable interrupts for the GPIO pins through the
+	 * GPIO block itself. To do this we install a chained handler. If a
+	 * user requests one of the __IRQ_GPIOn interrupts then the GPIO block
+	 * won't get configured.  We provide these interrupts below as virtual
+	 * ones that will configure the GPIO block and enable the source in
+	 * the VIC.
+	 *
+	 * The chained handler simply converts from the virtual IRQ handler to
+	 * the real interrupt source and calls the GPIO IRQ handler.
+	 */
+	writel(0, GPIO_INT_EN_REG);
+	writel(~0, GPIO_EOI_REG);
+	for (i = IRQ_GPIO0; i <= IRQ_GPIO7; ++i) {
+		set_irq_chip(i, &armgpio_irqchip);
+		set_irq_handler(i, handle_level_irq);
+		set_irq_flags(i, IRQF_VALID);
+	}
+
+	for (i = __IRQ_GPIO0; i <= __IRQ_GPIO7; ++i)
+		set_irq_chained_handler(i, gpio_irq_handler);
 }
 
 static const char *picoxcell_get_partname(void)
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* ARM: add initial support for Picochip picoXcell SoC
  2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
                   ` (13 preceding siblings ...)
  2010-11-30 18:10 ` [RFC PATCHv2 10/13] picoxcell: add chained GPIO IRQ handlers Jamie Iles
@ 2010-12-06 10:25 ` Jamie Iles
  14 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-12-06 10:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Nov 23, 2010 at 10:06:01AM +0000, Jamie Iles wrote:
> Hi,
> 
> This patch series adds support for the Picochip picoXcell series of
> femotcell SoC's. There are currently two device families - PC3X2 and
> PC3X3. Both include an ARM1176JZ-S, 100Mb Ethernet controller, 2xDMA
> controllers, eFuses, crypto offload engines and Picochip's array
> processor the picoArray. This patch series adds support for the base
> chip support including timers, gpio, pin muxing, clocks and pm and also
> the PC7302 development board that can take either PC3X2 or PC3X3 devices.
> 
> This patch series is based off of linux-next (next-20101122) and also requires
> the following patches:
> 
>     - 8250: add a UPIO_DWAPB32 for 32 bit accesses (v2):
>     http://marc.info/?l=linux-serial&m=129043658715199&w=2
>     - debug-8250: add a 32 bit mode:
>     http://marc.info/?l=linux-arm-kernel&m=129044061221371&w=2
Does anyone have any comments on this patch series?

Thanks,

Jamie

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH 01/13] picoxcell: add support for picoXcell
  2010-11-23 10:06 ` [RFC PATCH 01/13] picoxcell: add support for picoXcell Jamie Iles
  2010-11-26 11:08   ` [PATCH] " Jamie Iles
@ 2010-12-08 16:09   ` Russell King - ARM Linux
  2010-12-08 16:52     ` Jamie Iles
  1 sibling, 1 reply; 29+ messages in thread
From: Russell King - ARM Linux @ 2010-12-08 16:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Nov 23, 2010 at 10:06:02AM +0000, Jamie Iles wrote:
> picoXcell is a family of femtocell SoC devices from Picochip [1] with an
> ARM subsystem.  The devices have an ARM1176JZ-S core and a DSP processor
> array.  Currently there are two sub families - PC3X2 and PC3X3. The
> latter includes extra power and performance control along with extra
> peripherals.
> 
> This initial patch adds the hardware definitions and a framework for
> adding device variants.
> 
> 1. http://www.picochip.com
> 
> Signed-off-by: Jamie Iles <jamie@jamieiles.com>
> ---
>  arch/arm/Kconfig                                   |   17 ++
>  arch/arm/Makefile                                  |    1 +
>  arch/arm/mach-picoxcell/Kconfig                    |    4 +
>  arch/arm/mach-picoxcell/Makefile                   |    1 +
>  arch/arm/mach-picoxcell/Makefile.boot              |    3 +
>  arch/arm/mach-picoxcell/axi2cfg.c                  |  168 +++++++++++++++++
>  arch/arm/mach-picoxcell/include/mach/debug-macro.S |   18 ++
>  arch/arm/mach-picoxcell/include/mach/entry-macro.S |   19 ++
>  arch/arm/mach-picoxcell/include/mach/hardware.h    |   29 +++
>  arch/arm/mach-picoxcell/include/mach/io.h          |   32 +++
>  arch/arm/mach-picoxcell/include/mach/irqs.h        |   89 +++++++++
>  arch/arm/mach-picoxcell/include/mach/memory.h      |   27 +++
>  .../include/mach/picoxcell/axi2cfg.h               |  171 +++++++++++++++++
>  .../mach-picoxcell/include/mach/picoxcell/gpio.h   |   48 +++++
>  .../include/mach/picoxcell/picoxcell.h             |   61 ++++++
>  .../mach-picoxcell/include/mach/picoxcell/timer.h  |   36 ++++
>  .../mach-picoxcell/include/mach/picoxcell/wdog.h   |   43 +++++
>  arch/arm/mach-picoxcell/include/mach/platform.h    |   32 +++
>  arch/arm/mach-picoxcell/include/mach/system.h      |   51 +++++
>  arch/arm/mach-picoxcell/include/mach/timex.h       |   26 +++
>  arch/arm/mach-picoxcell/include/mach/uncompress.h  |   56 ++++++
>  arch/arm/mach-picoxcell/include/mach/vmalloc.h     |   18 ++
>  arch/arm/mach-picoxcell/picoxcell_core.c           |  199 ++++++++++++++++++++
>  arch/arm/mach-picoxcell/picoxcell_core.h           |   21 ++
>  arch/arm/mach-picoxcell/soc.h                      |   60 ++++++
>  25 files changed, 1230 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 81c71da..9c252da 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -502,6 +502,21 @@ config ARCH_ORION5X
>  	  Orion-1 (5181), Orion-VoIP (5181L), Orion-NAS (5182),
>  	  Orion-2 (5281), Orion-1-90 (6183).
>  
> +config ARCH_PICOXCELL
> +	bool "Picochip picoXcell"
> +	select ARM_VIC
> +	select HAS_TLS_REG

This no longer exists.

> +	select GENERIC_TIME

Doesn't exist anymore.

> +	select GENERIC_CLOCKEVENTS
> +	select GENERIC_GPIO
> +	select ARCH_REQUIRE_GPIOLIB
> +	select COMMON_CLKDEV

Now called CLKDEV_LOOKUP.

> +#ifdef __ASSEMBLY__
> +# define IO_ADDRESS(x)		    (((x) & 0x00ffffff) | 0xfe000000)
> +#else /* __ASSEMBLY__ */
> +# define PHYS_TO_IO(x)		    (((x) & 0x00ffffff) | 0xfe000000)
> +# define IO_ADDRESS(x)		    __typesafe_io(PHYS_TO_IO((x)))

Not really what __typesafe_io is meant for - please use an explicit cast
instead.

> +#ifndef __ASSEMBLY__
> +
> +#include <linux/init.h>

You don't actually need this...

> +
> +/*
> + * axi2cfg_config_read - Read a number of 16 bit words from a picoArray axi2cfg.
> + *
> + * Returns the number of words read on success, negative errno on failure.
> + *
> + * @axi2cfg_base: The base address of the upper axi2cfg.
> + * @aeid: The CAEID of the AE to read from.
> + * @ae_addr: The address to begin reading from within the AE.
> + * @buf: The buffer to store the results in.
> + * @count: The number of 16 bit words to read.
> + */
> +extern int axi2cfg_config_read(u16 aeid, u16 ae_addr, u16 *buf, u16 count);
> +
> +/*
> + * axi2cfg_config_write - Write a number of 16 bit words to a picoArray axi2cfg.
> + *
> + * @axi2cfg_base: The base address of the upper axi2cfg.
> + * @aeid: The CAEID of the AE to write to.
> + * @ae_addr: The address to begin writing to within the AE.
> + * @buf: The buffer to read the words from.
> + * @count: The number of 16 bit words to write.
> + */
> +extern void axi2cfg_config_write(u16 aeid, u16 ae_addr, const u16 *buf,
> +				 u16 count);
> +
> +/*
> + * ax2cfg_write_buf - Write a series of configuration words to the AXI2CFG
> + *	config write port.
> + *
> + * @buf: The buffer to write.
> + * @nr_words: The number of 32 bit words to write.
> + */
> +extern void axi2cfg_write_buf(const u32 *buf, unsigned nr_words);
> +
> +/*
> + * axi2cfg_init - initialize the AXI2CFG hardware.
> + */
> +extern void __init axi2cfg_init(void);

As you don't need to tell the compiler which section functions are located.

> +/* The clock frequency for the timers on the various boards */
> +#define PICOXCELL_TIMER_FREQ	    200000000	/* 200 MHz */

I'd sugges this goes in timex.h, to avoid including this file unnecessarily.

> diff --git a/arch/arm/mach-picoxcell/include/mach/uncompress.h b/arch/arm/mach-picoxcell/include/mach/uncompress.h
> new file mode 100644
> index 0000000..ccaf3d5
> --- /dev/null
> +++ b/arch/arm/mach-picoxcell/include/mach/uncompress.h
> @@ -0,0 +1,56 @@
> +/*
> + * Copyright (c) 2010 Picochip Ltd., Jamie Iles
> + *
> + * 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 <asm/processor.h>

Should be after linux/ includes.

> +#define VMALLOC_END		(PAGE_OFFSET + 0x18000000)

Shouldn't be defined in terms of PAGE_OFFSET as this symbol can move.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH 02/13] picoxcell: add support for AXI bus snoopers
  2010-11-23 10:06 ` [RFC PATCH 02/13] picoxcell: add support for AXI bus snoopers Jamie Iles
@ 2010-12-08 16:10   ` Russell King - ARM Linux
  2010-12-08 16:52     ` Jamie Iles
  0 siblings, 1 reply; 29+ messages in thread
From: Russell King - ARM Linux @ 2010-12-08 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Nov 23, 2010 at 10:06:03AM +0000, Jamie Iles wrote:
> diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
> index 4469a78..07eaa9b 100644
> --- a/arch/arm/mach-picoxcell/picoxcell_core.c
> +++ b/arch/arm/mach-picoxcell/picoxcell_core.c
> @@ -71,6 +71,7 @@ static ssize_t part_show(struct sys_device *sysdev,
>  			 struct sysdev_attribute *attr, char *buf)
>  {
>  	const char *part = picoxcell_get_partname();
> +
>  	return snprintf(buf, PAGE_SIZE, "%s\n", part);
>  }

Shouldn't this be in the first patch?

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCHv2] picoxcell: add support for the system timers
  2010-11-26 11:11   ` [RFC PATCHv2] " Jamie Iles
@ 2010-12-08 16:14     ` Russell King - ARM Linux
  2010-12-08 17:26       ` Jamie Iles
  0 siblings, 1 reply; 29+ messages in thread
From: Russell King - ARM Linux @ 2010-12-08 16:14 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Nov 26, 2010 at 11:11:16AM +0000, Jamie Iles wrote:
> +static irqreturn_t timer_interrupt(int irq, void *dev_id)
> +{
> +	struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
> +
> +	/* If we are in oneshot mode, we need to stop receiving interrupts. */
> +	if (CLOCK_EVT_MODE_ONESHOT == clockevent_picoxcell.mode) {
> +		unsigned long val = readl(timer->base +
> +					  TIMER_CONTROL_REG_OFFSET);
> +		val |= TIMER_INTERRUPT_MASK;
> +		writel(val, timer->base + TIMER_CONTROL_REG_OFFSET);
> +	}

It's normal not to emulate behaviours here which the hardware doesn't
support - that's the point of the generic clockevents/clocksource
layers.  They're there to handle whatever the hardware can support and
turn it into something the kernel wants.

> +/*
> + * Overwrite weak default sched_clock with something more precise.
> + *
> + * On picoXcell we have a RTC that clocks at 200MHz so a multiply by 5 gives
> + * us a nanosecond count.
> + */
> +unsigned long long notrace sched_clock(void)
> +{
> +#define CYCLES_TO_NSEC_MULT	(NSEC_PER_SEC / CLOCK_TICK_RATE)
> +	return cnt32_to_63(readl(IO_ADDRESS(PICOXCELL_RTCLK_BASE) +
> +				 RTCLK_CCV_REG_OFFSET)) * CYCLES_TO_NSEC_MULT;

Have you read the comments in include/linux/cnt32_to_63.h, or thought
about the '63' part of the name ?

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH 06/13] picoxcell: add support for system clks
  2010-11-23 10:06 ` [RFC PATCH 06/13] picoxcell: add support for system clks Jamie Iles
@ 2010-12-08 16:18   ` Russell King - ARM Linux
  2010-12-08 17:39     ` Jamie Iles
  0 siblings, 1 reply; 29+ messages in thread
From: Russell King - ARM Linux @ 2010-12-08 16:18 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Nov 23, 2010 at 10:06:07AM +0000, Jamie Iles wrote:
> The different picoXcell device variants have different numbers of clocks
> and different capabilities (PC3X2 doesn't have any gateable or
> controllable clocks). Add the infrastructure necessary for device
> variants to provide their own clock implementation.

Fro the looks of this, you only allow one lookup per clk?  That's
rather sub-optimal, and could cause you problems in the future when
you may want to have aliases to cater for differences in the SoC
clock layout.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH 01/13] picoxcell: add support for picoXcell
  2010-12-08 16:09   ` [RFC PATCH 01/13] " Russell King - ARM Linux
@ 2010-12-08 16:52     ` Jamie Iles
  0 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-12-08 16:52 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Russell,

Thanks for taking the time to review these patches; much appreciated. I agree 
with all of your comments and will respin this patch soon.

Jamie

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH 02/13] picoxcell: add support for AXI bus snoopers
  2010-12-08 16:10   ` Russell King - ARM Linux
@ 2010-12-08 16:52     ` Jamie Iles
  0 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-12-08 16:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Dec 08, 2010 at 04:10:13PM +0000, Russell King - ARM Linux wrote:
> On Tue, Nov 23, 2010 at 10:06:03AM +0000, Jamie Iles wrote:
> > diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
> > index 4469a78..07eaa9b 100644
> > --- a/arch/arm/mach-picoxcell/picoxcell_core.c
> > +++ b/arch/arm/mach-picoxcell/picoxcell_core.c
> > @@ -71,6 +71,7 @@ static ssize_t part_show(struct sys_device *sysdev,
> >  			 struct sysdev_attribute *attr, char *buf)
> >  {
> >  	const char *part = picoxcell_get_partname();
> > +
> >  	return snprintf(buf, PAGE_SIZE, "%s\n", part);
> >  }
> 
> Shouldn't this be in the first patch?
Indeed, not sure how I missed that.

Jamie

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCHv2] picoxcell: add support for the system timers
  2010-12-08 16:14     ` Russell King - ARM Linux
@ 2010-12-08 17:26       ` Jamie Iles
  0 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-12-08 17:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Dec 08, 2010 at 04:14:11PM +0000, Russell King - ARM Linux wrote:
> On Fri, Nov 26, 2010 at 11:11:16AM +0000, Jamie Iles wrote:
> > +static irqreturn_t timer_interrupt(int irq, void *dev_id)
> > +{
> > +	struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
> > +
> > +	/* If we are in oneshot mode, we need to stop receiving interrupts. */
> > +	if (CLOCK_EVT_MODE_ONESHOT == clockevent_picoxcell.mode) {
> > +		unsigned long val = readl(timer->base +
> > +					  TIMER_CONTROL_REG_OFFSET);
> > +		val |= TIMER_INTERRUPT_MASK;
> > +		writel(val, timer->base + TIMER_CONTROL_REG_OFFSET);
> > +	}
> 
> It's normal not to emulate behaviours here which the hardware doesn't
> support - that's the point of the generic clockevents/clocksource
> layers.  They're there to handle whatever the hardware can support and
> turn it into something the kernel wants.

Ok, I underestimated clockevents and how NO_HZ works and thought that we'd 
keep on receiving too many interrupts but that's not the case! I'll fix up the 
next revision to remove this hack.

> > +/*
> > + * Overwrite weak default sched_clock with something more precise.
> > + *
> > + * On picoXcell we have a RTC that clocks at 200MHz so a multiply by 5 gives
> > + * us a nanosecond count.
> > + */
> > +unsigned long long notrace sched_clock(void)
> > +{
> > +#define CYCLES_TO_NSEC_MULT	(NSEC_PER_SEC / CLOCK_TICK_RATE)
> > +	return cnt32_to_63(readl(IO_ADDRESS(PICOXCELL_RTCLK_BASE) +
> > +				 RTCLK_CCV_REG_OFFSET)) * CYCLES_TO_NSEC_MULT;
> 
> Have you read the comments in include/linux/cnt32_to_63.h, or thought
> about the '63' part of the name ?

Sadly not, this was one I'd taken from the mailing list for mach-tegra as we 
were missing a sched_clock() implementation before. Since then I've had a go 
at taking Linus' sched_clock() implementation for plat-nomadik and using that 
to increase the wrap period which I'll roll into the next revision.

Jamie

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH 06/13] picoxcell: add support for system clks
  2010-12-08 16:18   ` Russell King - ARM Linux
@ 2010-12-08 17:39     ` Jamie Iles
  0 siblings, 0 replies; 29+ messages in thread
From: Jamie Iles @ 2010-12-08 17:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Dec 08, 2010 at 04:18:07PM +0000, Russell King - ARM Linux wrote:
> On Tue, Nov 23, 2010 at 10:06:07AM +0000, Jamie Iles wrote:
> > The different picoXcell device variants have different numbers of clocks
> > and different capabilities (PC3X2 doesn't have any gateable or
> > controllable clocks). Add the infrastructure necessary for device
> > variants to provide their own clock implementation.
> 
> Fro the looks of this, you only allow one lookup per clk?  That's
> rather sub-optimal, and could cause you problems in the future when
> you may want to have aliases to cater for differences in the SoC
> clock layout.

Hmm, I hadn't thought of that. The number of clocks in our chips appears 
to be rapidly multiplying so that seems like a very good idea.

I guess for PC3X2 where we don't have any gateable or controllable 
clocks that means I can create one virtual clock then add a number of 
aliases where drivers require a struct clk.

Thanks,

Jamie

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2010-12-08 17:39 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-11-23 10:06 ARM: add initial support for Picochip picoXcell SoC Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 01/13] picoxcell: add support for picoXcell Jamie Iles
2010-11-26 11:08   ` [PATCH] " Jamie Iles
2010-12-08 16:09   ` [RFC PATCH 01/13] " Russell King - ARM Linux
2010-12-08 16:52     ` Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 02/13] picoxcell: add support for AXI bus snoopers Jamie Iles
2010-12-08 16:10   ` Russell King - ARM Linux
2010-12-08 16:52     ` Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 03/13] picoxcell: add support for the system timers Jamie Iles
2010-11-26 11:11   ` [RFC PATCHv2] " Jamie Iles
2010-12-08 16:14     ` Russell King - ARM Linux
2010-12-08 17:26       ` Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 04/13] picoxcell: add a pin muxing infrastructure Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 05/13] picoxcell: add gpio infrastructure Jamie Iles
2010-11-30 18:08   ` [RFC PATCHv2 " Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 06/13] picoxcell: add support for system clks Jamie Iles
2010-12-08 16:18   ` Russell King - ARM Linux
2010-12-08 17:39     ` Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 07/13] picoxcell: add common SoC devices Jamie Iles
2010-11-26 11:13   ` [RFC PATCHv2] " Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 08/13] picoxcell: add cpufreq support Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 09/13] picoxcell: add support for pm Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 10/13] picoxcell: add chained GPIO IRQ handlers Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 11/13] picoxcell: add support for pc3x2 devices Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 12/13] picoxcell: add support for pc3x3 devices Jamie Iles
2010-11-26 11:14   ` [RFC PATCHv2] " Jamie Iles
2010-11-23 10:06 ` [RFC PATCH 13/13] picoxcell: add support for the PC7302 development board Jamie Iles
2010-11-30 18:10 ` [RFC PATCHv2 10/13] picoxcell: add chained GPIO IRQ handlers Jamie Iles
2010-12-06 10:25 ` ARM: add initial support for Picochip picoXcell SoC Jamie Iles

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.