Browse Source

Merge branch 'emev2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/renesas into next/soc

* 'emev2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/renesas:
  mach-shmobile: Use DT_MACHINE for KZM9D V3
  mach-shmobile: Emma Mobile EV2 DT support V3
  mach-shmobile: KZM9D board Ethernet support V3
  mach-shmobile: Emma Mobile EV2 GPIO support V3
  mach-shmobile: Emma Mobile EV2 SMP support V3
  mach-shmobile: KZM9D board support V3
  mach-shmobile: Emma Mobile EV2 SoC base support V3
  gpio: Emma Mobile GPIO driver V2
Olof Johansson 13 years ago
parent
commit
0804dcb2af

+ 26 - 0
arch/arm/boot/dts/emev2-kzm9d.dts

@@ -0,0 +1,26 @@
+/*
+ * Device Tree Source for the KZM9D board
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * 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.
+ */
+/dts-v1/;
+
+/include/ "emev2.dtsi"
+
+/ {
+	model = "EMEV2 KZM9D Board";
+	compatible = "renesas,kzm9d", "renesas,emev2";
+
+	memory {
+		device_type = "memory";
+		reg = <0x40000000 0x8000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS1,115200n81";
+	};
+};

+ 63 - 0
arch/arm/boot/dts/emev2.dtsi

@@ -0,0 +1,63 @@
+/*
+ * Device Tree Source for the EMEV2 SoC
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * 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/ "skeleton.dtsi"
+
+/ {
+	compatible = "renesas,emev2";
+	interrupt-parent = <&gic>;
+
+	cpus {
+		cpu@0 {
+			compatible = "arm,cortex-a9";
+		};
+		cpu@1 {
+			compatible = "arm,cortex-a9";
+		};
+	};
+
+	gic: interrupt-controller@e0020000 {
+		compatible = "arm,cortex-a9-gic";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		reg = <0xe0028000 0x1000>,
+		      <0xe0020000 0x0100>;
+	};
+
+	sti@e0180000 {
+		compatible = "renesas,em-sti";
+		reg = <0xe0180000 0x54>;
+		interrupts = <0 125 0>;
+	};
+
+	uart@e1020000 {
+		compatible = "renesas,em-uart";
+		reg = <0xe1020000 0x38>;
+		interrupts = <0 8 0>;
+	};
+
+	uart@e1030000 {
+		compatible = "renesas,em-uart";
+		reg = <0xe1030000 0x38>;
+		interrupts = <0 9 0>;
+	};
+
+	uart@e1040000 {
+		compatible = "renesas,em-uart";
+		reg = <0xe1040000 0x38>;
+		interrupts = <0 10 0>;
+	};
+
+	uart@e1050000 {
+		compatible = "renesas,em-uart";
+		reg = <0xe1050000 0x38>;
+		interrupts = <0 11 0>;
+	};
+};

+ 11 - 0
arch/arm/mach-shmobile/Kconfig

@@ -41,6 +41,12 @@ config ARCH_R8A7779
 	select ARM_GIC
 	select ARCH_WANT_OPTIONAL_GPIOLIB
 
+config ARCH_EMEV2
+	bool "Emma Mobile EV2"
+	select CPU_V7
+	select ARM_GIC
+	select ARCH_WANT_OPTIONAL_GPIOLIB
+
 comment "SH-Mobile Board Type"
 
 config MACH_G3EVM
@@ -98,6 +104,11 @@ config MACH_MARZEN
 	depends on ARCH_R8A7779
 	select ARCH_REQUIRE_GPIOLIB
 
+config MACH_KZM9D
+	bool "KZM9D board"
+	depends on ARCH_EMEV2
+	select USE_OF
+
 comment "SH-Mobile System Configuration"
 
 config CPU_HAS_INTEVT

+ 3 - 0
arch/arm/mach-shmobile/Makefile

@@ -12,12 +12,14 @@ obj-$(CONFIG_ARCH_SH7372)	+= setup-sh7372.o clock-sh7372.o intc-sh7372.o
 obj-$(CONFIG_ARCH_SH73A0)	+= setup-sh73a0.o clock-sh73a0.o intc-sh73a0.o
 obj-$(CONFIG_ARCH_R8A7740)	+= setup-r8a7740.o clock-r8a7740.o intc-r8a7740.o
 obj-$(CONFIG_ARCH_R8A7779)	+= setup-r8a7779.o clock-r8a7779.o intc-r8a7779.o
+obj-$(CONFIG_ARCH_EMEV2)	+= setup-emev2.o clock-emev2.o
 
 # SMP objects
 smp-y				:= platsmp.o headsmp.o
 smp-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 smp-$(CONFIG_ARCH_SH73A0)	+= smp-sh73a0.o
 smp-$(CONFIG_ARCH_R8A7779)	+= smp-r8a7779.o
+smp-$(CONFIG_ARCH_EMEV2)	+= smp-emev2.o
 
 # Pinmux setup
 pfc-y				:=
@@ -49,6 +51,7 @@ obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 obj-$(CONFIG_MACH_KOTA2)	+= board-kota2.o
 obj-$(CONFIG_MACH_BONITO)	+= board-bonito.o
 obj-$(CONFIG_MACH_MARZEN)	+= board-marzen.o
+obj-$(CONFIG_MACH_KZM9D)	+= board-kzm9d.o
 
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)

+ 85 - 0
arch/arm/mach-shmobile/board-kzm9d.c

@@ -0,0 +1,85 @@
+/*
+ * kzm9d board support
+ *
+ * Copyright (C) 2012  Renesas Solutions Corp.
+ * Copyright (C) 2012  Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/smsc911x.h>
+#include <mach/common.h>
+#include <mach/emev2.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/hardware/gic.h>
+
+/* Ether */
+static struct resource smsc911x_resources[] = {
+	[0] = {
+		.start	= 0x20000000,
+		.end	= 0x2000ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= EMEV2_GPIO_IRQ(1),
+		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,
+	},
+};
+
+static struct smsc911x_platform_config smsc911x_platdata = {
+	.flags		= SMSC911X_USE_32BIT,
+	.irq_type	= SMSC911X_IRQ_TYPE_PUSH_PULL,
+	.irq_polarity	= SMSC911X_IRQ_POLARITY_ACTIVE_HIGH,
+};
+
+static struct platform_device smsc91x_device = {
+	.name	= "smsc911x",
+	.id	= 0,
+	.dev	= {
+		  .platform_data = &smsc911x_platdata,
+		},
+	.num_resources	= ARRAY_SIZE(smsc911x_resources),
+	.resource	= smsc911x_resources,
+};
+
+static struct platform_device *kzm9d_devices[] __initdata = {
+	&smsc91x_device,
+};
+
+void __init kzm9d_add_standard_devices(void)
+{
+	emev2_add_standard_devices();
+
+	platform_add_devices(kzm9d_devices, ARRAY_SIZE(kzm9d_devices));
+}
+
+static const char *kzm9d_boards_compat_dt[] __initdata = {
+	"renesas,kzm9d",
+	NULL,
+};
+
+DT_MACHINE_START(KZM9D_DT, "kzm9d")
+	.map_io		= emev2_map_io,
+	.init_early	= emev2_add_early_devices,
+	.nr_irqs	= NR_IRQS_LEGACY,
+	.init_irq	= emev2_init_irq,
+	.handle_irq	= gic_handle_irq,
+	.init_machine	= kzm9d_add_standard_devices,
+	.timer		= &shmobile_timer,
+	.dt_compat	= kzm9d_boards_compat_dt,
+MACHINE_END

+ 249 - 0
arch/arm/mach-shmobile/clock-emev2.c

@@ -0,0 +1,249 @@
+/*
+ * Emma Mobile EV2 clock framework support
+ *
+ * Copyright (C) 2012  Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/sh_clk.h>
+#include <linux/clkdev.h>
+#include <mach/common.h>
+
+#define EMEV2_SMU_BASE 0xe0110000
+
+/* EMEV2 SMU registers */
+#define USIAU0_RSTCTRL 0x094
+#define USIBU1_RSTCTRL 0x0ac
+#define USIBU2_RSTCTRL 0x0b0
+#define USIBU3_RSTCTRL 0x0b4
+#define STI_RSTCTRL 0x124
+#define USIAU0GCLKCTRL 0x4a0
+#define USIBU1GCLKCTRL 0x4b8
+#define USIBU2GCLKCTRL 0x4bc
+#define USIBU3GCLKCTRL 0x04c0
+#define STIGCLKCTRL 0x528
+#define USIAU0SCLKDIV 0x61c
+#define USIB2SCLKDIV 0x65c
+#define USIB3SCLKDIV 0x660
+#define STI_CLKSEL 0x688
+#define SMU_GENERAL_REG0 0x7c0
+
+/* not pretty, but hey */
+static void __iomem *smu_base;
+
+static void emev2_smu_write(unsigned long value, int offs)
+{
+	BUG_ON(!smu_base || (offs >= PAGE_SIZE));
+	iowrite32(value, smu_base + offs);
+}
+
+void emev2_set_boot_vector(unsigned long value)
+{
+	emev2_smu_write(value, SMU_GENERAL_REG0);
+}
+
+static struct clk_mapping smu_mapping = {
+	.phys	= EMEV2_SMU_BASE,
+	.len	= PAGE_SIZE,
+};
+
+/* Fixed 32 KHz root clock from C32K pin */
+static struct clk c32k_clk = {
+	.rate           = 32768,
+	.mapping	= &smu_mapping,
+};
+
+/* PLL3 multiplies C32K with 7000 */
+static unsigned long pll3_recalc(struct clk *clk)
+{
+	return clk->parent->rate * 7000;
+}
+
+static struct sh_clk_ops pll3_clk_ops = {
+	.recalc		= pll3_recalc,
+};
+
+static struct clk pll3_clk = {
+	.ops		= &pll3_clk_ops,
+	.parent		= &c32k_clk,
+};
+
+static struct clk *main_clks[] = {
+	&c32k_clk,
+	&pll3_clk,
+};
+
+enum { SCLKDIV_USIAU0, SCLKDIV_USIBU2, SCLKDIV_USIBU1, SCLKDIV_USIBU3,
+	SCLKDIV_NR };
+
+#define SCLKDIV(_reg, _shift)			\
+{								\
+	.parent		= &pll3_clk,				\
+	.enable_reg	= IOMEM(EMEV2_SMU_BASE + (_reg)),	\
+	.enable_bit	= _shift,				\
+}
+
+static struct clk sclkdiv_clks[SCLKDIV_NR] = {
+	[SCLKDIV_USIAU0] = SCLKDIV(USIAU0SCLKDIV, 0),
+	[SCLKDIV_USIBU2] = SCLKDIV(USIB2SCLKDIV, 16),
+	[SCLKDIV_USIBU1] = SCLKDIV(USIB2SCLKDIV, 0),
+	[SCLKDIV_USIBU3] = SCLKDIV(USIB3SCLKDIV, 0),
+};
+
+enum { GCLK_USIAU0_SCLK, GCLK_USIBU1_SCLK, GCLK_USIBU2_SCLK, GCLK_USIBU3_SCLK,
+	GCLK_STI_SCLK,
+	GCLK_NR };
+
+#define GCLK_SCLK(_parent, _reg) \
+{								\
+	.parent		= _parent,				\
+	.enable_reg	= IOMEM(EMEV2_SMU_BASE + (_reg)),	\
+	.enable_bit	= 1, /* SCLK_GCC */			\
+}
+
+static struct clk gclk_clks[GCLK_NR] = {
+	[GCLK_USIAU0_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIAU0],
+				       USIAU0GCLKCTRL),
+	[GCLK_USIBU1_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU1],
+				       USIBU1GCLKCTRL),
+	[GCLK_USIBU2_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU2],
+				       USIBU2GCLKCTRL),
+	[GCLK_USIBU3_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU3],
+				       USIBU3GCLKCTRL),
+	[GCLK_STI_SCLK] = GCLK_SCLK(&c32k_clk, STIGCLKCTRL),
+};
+
+static int emev2_gclk_enable(struct clk *clk)
+{
+	iowrite32(ioread32(clk->mapped_reg) | (1 << clk->enable_bit),
+		  clk->mapped_reg);
+	return 0;
+}
+
+static void emev2_gclk_disable(struct clk *clk)
+{
+	iowrite32(ioread32(clk->mapped_reg) & ~(1 << clk->enable_bit),
+		  clk->mapped_reg);
+}
+
+static struct sh_clk_ops emev2_gclk_clk_ops = {
+	.enable		= emev2_gclk_enable,
+	.disable	= emev2_gclk_disable,
+	.recalc		= followparent_recalc,
+};
+
+static int __init emev2_gclk_register(struct clk *clks, int nr)
+{
+	struct clk *clkp;
+	int ret = 0;
+	int k;
+
+	for (k = 0; !ret && (k < nr); k++) {
+		clkp = clks + k;
+		clkp->ops = &emev2_gclk_clk_ops;
+		ret |= clk_register(clkp);
+	}
+
+	return ret;
+}
+
+static unsigned long emev2_sclkdiv_recalc(struct clk *clk)
+{
+	unsigned int sclk_div;
+
+	sclk_div = (ioread32(clk->mapped_reg) >> clk->enable_bit) & 0xff;
+
+	return clk->parent->rate / (sclk_div + 1);
+}
+
+static struct sh_clk_ops emev2_sclkdiv_clk_ops = {
+	.recalc		= emev2_sclkdiv_recalc,
+};
+
+static int __init emev2_sclkdiv_register(struct clk *clks, int nr)
+{
+	struct clk *clkp;
+	int ret = 0;
+	int k;
+
+	for (k = 0; !ret && (k < nr); k++) {
+		clkp = clks + k;
+		clkp->ops = &emev2_sclkdiv_clk_ops;
+		ret |= clk_register(clkp);
+	}
+
+	return ret;
+}
+
+static struct clk_lookup lookups[] = {
+	CLKDEV_DEV_ID("serial8250-em.0", &gclk_clks[GCLK_USIAU0_SCLK]),
+	CLKDEV_DEV_ID("e1020000.uart", &gclk_clks[GCLK_USIAU0_SCLK]),
+	CLKDEV_DEV_ID("serial8250-em.1", &gclk_clks[GCLK_USIBU1_SCLK]),
+	CLKDEV_DEV_ID("e1030000.uart", &gclk_clks[GCLK_USIBU1_SCLK]),
+	CLKDEV_DEV_ID("serial8250-em.2", &gclk_clks[GCLK_USIBU2_SCLK]),
+	CLKDEV_DEV_ID("e1040000.uart", &gclk_clks[GCLK_USIBU2_SCLK]),
+	CLKDEV_DEV_ID("serial8250-em.3", &gclk_clks[GCLK_USIBU3_SCLK]),
+	CLKDEV_DEV_ID("e1050000.uart", &gclk_clks[GCLK_USIBU3_SCLK]),
+	CLKDEV_DEV_ID("em_sti.0", &gclk_clks[GCLK_STI_SCLK]),
+	CLKDEV_DEV_ID("e0180000.sti", &gclk_clks[GCLK_STI_SCLK]),
+};
+
+void __init emev2_clock_init(void)
+{
+	int k, ret = 0;
+	static int is_setup;
+
+	/* yuck, this is ugly as hell, but the non-smp case of clocks
+	 * code is now designed to rely on ioremap() instead of static
+	 * entity maps. in the case of smp we need access to the SMU
+	 * register earlier than ioremap() is actually working without
+	 * any static maps. to enable SMP in ugly but with dynamic
+	 * mappings we have to call emev2_clock_init() from different
+	 * places depending on UP and SMP...
+	 */
+	if (is_setup++)
+		return;
+
+	smu_base = ioremap(EMEV2_SMU_BASE, PAGE_SIZE);
+	BUG_ON(!smu_base);
+
+	/* setup STI timer to run on 37.768 kHz and deassert reset */
+	emev2_smu_write(0, STI_CLKSEL);
+	emev2_smu_write(1, STI_RSTCTRL);
+
+	/* deassert reset for UART0->UART3 */
+	emev2_smu_write(2, USIAU0_RSTCTRL);
+	emev2_smu_write(2, USIBU1_RSTCTRL);
+	emev2_smu_write(2, USIBU2_RSTCTRL);
+	emev2_smu_write(2, USIBU3_RSTCTRL);
+
+	for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
+		ret = clk_register(main_clks[k]);
+
+	if (!ret)
+		ret = emev2_sclkdiv_register(sclkdiv_clks, SCLKDIV_NR);
+
+	if (!ret)
+		ret = emev2_gclk_register(gclk_clks, GCLK_NR);
+
+	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+	if (!ret)
+		shmobile_clk_init();
+	else
+		panic("failed to setup emev2 clocks\n");
+}

+ 19 - 0
arch/arm/mach-shmobile/include/mach/emev2.h

@@ -0,0 +1,19 @@
+#ifndef __ASM_EMEV2_H__
+#define __ASM_EMEV2_H__
+
+extern void emev2_map_io(void);
+extern void emev2_init_irq(void);
+extern void emev2_add_early_devices(void);
+extern void emev2_add_standard_devices(void);
+extern void emev2_clock_init(void);
+extern void emev2_set_boot_vector(unsigned long value);
+extern unsigned int emev2_get_core_count(void);
+extern int emev2_platform_cpu_kill(unsigned int cpu);
+extern void emev2_secondary_init(unsigned int cpu);
+extern int emev2_boot_secondary(unsigned int cpu);
+extern void emev2_smp_prepare_cpus(void);
+
+#define EMEV2_GPIO_BASE 200
+#define EMEV2_GPIO_IRQ(n) (EMEV2_GPIO_BASE + (n))
+
+#endif /* __ASM_EMEV2_H__ */

+ 18 - 0
arch/arm/mach-shmobile/platsmp.c

@@ -16,12 +16,15 @@
 #include <linux/device.h>
 #include <linux/smp.h>
 #include <linux/io.h>
+#include <linux/of.h>
 #include <asm/hardware/gic.h>
 #include <asm/mach-types.h>
 #include <mach/common.h>
+#include <mach/emev2.h>
 
 #define is_sh73a0() (machine_is_ag5evm() || machine_is_kota2())
 #define is_r8a7779() machine_is_marzen()
+#define is_emev2() of_machine_is_compatible("renesas,emev2")
 
 static unsigned int __init shmobile_smp_get_core_count(void)
 {
@@ -31,6 +34,9 @@ static unsigned int __init shmobile_smp_get_core_count(void)
 	if (is_r8a7779())
 		return r8a7779_get_core_count();
 
+	if (is_emev2())
+		return emev2_get_core_count();
+
 	return 1;
 }
 
@@ -41,6 +47,9 @@ static void __init shmobile_smp_prepare_cpus(void)
 
 	if (is_r8a7779())
 		r8a7779_smp_prepare_cpus();
+
+	if (is_emev2())
+		emev2_smp_prepare_cpus();
 }
 
 int shmobile_platform_cpu_kill(unsigned int cpu)
@@ -48,6 +57,9 @@ int shmobile_platform_cpu_kill(unsigned int cpu)
 	if (is_r8a7779())
 		return r8a7779_platform_cpu_kill(cpu);
 
+	if (is_emev2())
+		return emev2_platform_cpu_kill(cpu);
+
 	return 1;
 }
 
@@ -60,6 +72,9 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
 
 	if (is_r8a7779())
 		r8a7779_secondary_init(cpu);
+
+	if (is_emev2())
+		emev2_secondary_init(cpu);
 }
 
 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
@@ -70,6 +85,9 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
 	if (is_r8a7779())
 		return r8a7779_boot_secondary(cpu);
 
+	if (is_emev2())
+		return emev2_boot_secondary(cpu);
+
 	return -ENOSYS;
 }
 

+ 452 - 0
arch/arm/mach-shmobile/setup-emev2.c

@@ -0,0 +1,452 @@
+/*
+ * Emma Mobile EV2 processor support
+ *
+ * Copyright (C) 2012  Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/gpio-em.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <mach/hardware.h>
+#include <mach/common.h>
+#include <mach/emev2.h>
+#include <mach/irqs.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/time.h>
+#include <asm/hardware/gic.h>
+
+static struct map_desc emev2_io_desc[] __initdata = {
+#ifdef CONFIG_SMP
+	/* 128K entity map for 0xe0100000 (SMU) */
+	{
+		.virtual	= 0xe0100000,
+		.pfn		= __phys_to_pfn(0xe0100000),
+		.length		= SZ_128K,
+		.type		= MT_DEVICE
+	},
+	/* 2M mapping for SCU + L2 controller */
+	{
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0x1e000000),
+		.length		= SZ_2M,
+		.type		= MT_DEVICE
+	},
+#endif
+};
+
+void __init emev2_map_io(void)
+{
+	iotable_init(emev2_io_desc, ARRAY_SIZE(emev2_io_desc));
+}
+
+/* UART */
+static struct resource uart0_resources[] = {
+	[0]	= {
+		.start	= 0xe1020000,
+		.end	= 0xe1020037,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1]	= {
+		.start	= 40,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device uart0_device = {
+	.name		= "serial8250-em",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(uart0_resources),
+	.resource	= uart0_resources,
+};
+
+static struct resource uart1_resources[] = {
+	[0]	= {
+		.start	= 0xe1030000,
+		.end	= 0xe1030037,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1]	= {
+		.start	= 41,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device uart1_device = {
+	.name		= "serial8250-em",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(uart1_resources),
+	.resource	= uart1_resources,
+};
+
+static struct resource uart2_resources[] = {
+	[0]	= {
+		.start	= 0xe1040000,
+		.end	= 0xe1040037,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1]	= {
+		.start	= 42,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device uart2_device = {
+	.name		= "serial8250-em",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(uart2_resources),
+	.resource	= uart2_resources,
+};
+
+static struct resource uart3_resources[] = {
+	[0]	= {
+		.start	= 0xe1050000,
+		.end	= 0xe1050037,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1]	= {
+		.start	= 43,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device uart3_device = {
+	.name		= "serial8250-em",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(uart3_resources),
+	.resource	= uart3_resources,
+};
+
+/* STI */
+static struct resource sti_resources[] = {
+	[0] = {
+		.name	= "STI",
+		.start	= 0xe0180000,
+		.end	= 0xe0180053,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 157,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device sti_device = {
+	.name		= "em_sti",
+	.id		= 0,
+	.resource	= sti_resources,
+	.num_resources	= ARRAY_SIZE(sti_resources),
+};
+
+
+/* GIO */
+static struct gpio_em_config gio0_config = {
+	.gpio_base = 0,
+	.irq_base = EMEV2_GPIO_IRQ(0),
+	.number_of_pins = 32,
+};
+
+static struct resource gio0_resources[] = {
+	[0] = {
+		.name	= "GIO_000",
+		.start	= 0xe0050000,
+		.end	= 0xe005002b,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.name	= "GIO_000",
+		.start	= 0xe0050040,
+		.end	= 0xe005005f,
+		.flags	= IORESOURCE_MEM,
+	},
+	[2] = {
+		.start	= 99,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[3] = {
+		.start	= 100,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device gio0_device = {
+	.name		= "em_gio",
+	.id		= 0,
+	.resource	= gio0_resources,
+	.num_resources	= ARRAY_SIZE(gio0_resources),
+	.dev		= {
+		.platform_data	= &gio0_config,
+	},
+};
+
+static struct gpio_em_config gio1_config = {
+	.gpio_base = 32,
+	.irq_base = EMEV2_GPIO_IRQ(32),
+	.number_of_pins = 32,
+};
+
+static struct resource gio1_resources[] = {
+	[0] = {
+		.name	= "GIO_032",
+		.start	= 0xe0050080,
+		.end	= 0xe00500ab,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.name	= "GIO_032",
+		.start	= 0xe00500c0,
+		.end	= 0xe00500df,
+		.flags	= IORESOURCE_MEM,
+	},
+	[2] = {
+		.start	= 101,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[3] = {
+		.start	= 102,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device gio1_device = {
+	.name		= "em_gio",
+	.id		= 1,
+	.resource	= gio1_resources,
+	.num_resources	= ARRAY_SIZE(gio1_resources),
+	.dev		= {
+		.platform_data	= &gio1_config,
+	},
+};
+
+static struct gpio_em_config gio2_config = {
+	.gpio_base = 64,
+	.irq_base = EMEV2_GPIO_IRQ(64),
+	.number_of_pins = 32,
+};
+
+static struct resource gio2_resources[] = {
+	[0] = {
+		.name	= "GIO_064",
+		.start	= 0xe0050100,
+		.end	= 0xe005012b,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.name	= "GIO_064",
+		.start	= 0xe0050140,
+		.end	= 0xe005015f,
+		.flags	= IORESOURCE_MEM,
+	},
+	[2] = {
+		.start	= 103,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[3] = {
+		.start	= 104,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device gio2_device = {
+	.name		= "em_gio",
+	.id		= 2,
+	.resource	= gio2_resources,
+	.num_resources	= ARRAY_SIZE(gio2_resources),
+	.dev		= {
+		.platform_data	= &gio2_config,
+	},
+};
+
+static struct gpio_em_config gio3_config = {
+	.gpio_base = 96,
+	.irq_base = EMEV2_GPIO_IRQ(96),
+	.number_of_pins = 32,
+};
+
+static struct resource gio3_resources[] = {
+	[0] = {
+		.name	= "GIO_096",
+		.start	= 0xe0050100,
+		.end	= 0xe005012b,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.name	= "GIO_096",
+		.start	= 0xe0050140,
+		.end	= 0xe005015f,
+		.flags	= IORESOURCE_MEM,
+	},
+	[2] = {
+		.start	= 105,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[3] = {
+		.start	= 106,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device gio3_device = {
+	.name		= "em_gio",
+	.id		= 3,
+	.resource	= gio3_resources,
+	.num_resources	= ARRAY_SIZE(gio3_resources),
+	.dev		= {
+		.platform_data	= &gio3_config,
+	},
+};
+
+static struct gpio_em_config gio4_config = {
+	.gpio_base = 128,
+	.irq_base = EMEV2_GPIO_IRQ(128),
+	.number_of_pins = 31,
+};
+
+static struct resource gio4_resources[] = {
+	[0] = {
+		.name	= "GIO_128",
+		.start	= 0xe0050200,
+		.end	= 0xe005022b,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.name	= "GIO_128",
+		.start	= 0xe0050240,
+		.end	= 0xe005025f,
+		.flags	= IORESOURCE_MEM,
+	},
+	[2] = {
+		.start	= 107,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[3] = {
+		.start	= 108,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device gio4_device = {
+	.name		= "em_gio",
+	.id		= 4,
+	.resource	= gio4_resources,
+	.num_resources	= ARRAY_SIZE(gio4_resources),
+	.dev		= {
+		.platform_data	= &gio4_config,
+	},
+};
+
+static struct platform_device *emev2_early_devices[] __initdata = {
+	&uart0_device,
+	&uart1_device,
+	&uart2_device,
+	&uart3_device,
+};
+
+static struct platform_device *emev2_late_devices[] __initdata = {
+	&sti_device,
+	&gio0_device,
+	&gio1_device,
+	&gio2_device,
+	&gio3_device,
+	&gio4_device,
+};
+
+void __init emev2_add_standard_devices(void)
+{
+	emev2_clock_init();
+
+	platform_add_devices(emev2_early_devices,
+			     ARRAY_SIZE(emev2_early_devices));
+
+	platform_add_devices(emev2_late_devices,
+			     ARRAY_SIZE(emev2_late_devices));
+}
+
+void __init emev2_init_delay(void)
+{
+	shmobile_setup_delay(533, 1, 3); /* Cortex-A9 @ 533MHz */
+}
+
+void __init emev2_add_early_devices(void)
+{
+	emev2_init_delay();
+
+	early_platform_add_devices(emev2_early_devices,
+				   ARRAY_SIZE(emev2_early_devices));
+
+	/* setup early console here as well */
+	shmobile_setup_console();
+}
+
+void __init emev2_init_irq(void)
+{
+	void __iomem *gic_dist_base;
+	void __iomem *gic_cpu_base;
+
+	/* Static mappings, never released */
+	gic_dist_base = ioremap(0xe0028000, PAGE_SIZE);
+	gic_cpu_base = ioremap(0xe0020000, PAGE_SIZE);
+	BUG_ON(!gic_dist_base || !gic_cpu_base);
+
+	/* Use GIC to handle interrupts */
+	gic_init(0, 29, gic_dist_base, gic_cpu_base);
+}
+
+#ifdef CONFIG_USE_OF
+static const struct of_dev_auxdata emev2_auxdata_lookup[] __initconst = {
+	{ }
+};
+
+void __init emev2_add_standard_devices_dt(void)
+{
+	of_platform_populate(NULL, of_default_bus_match_table,
+			     emev2_auxdata_lookup, NULL);
+}
+
+static const struct of_device_id emev2_dt_irq_match[] = {
+	{ .compatible = "arm,cortex-a9-gic", .data = gic_of_init, },
+	{},
+};
+
+static const char *emev2_boards_compat_dt[] __initdata = {
+	"renesas,emev2",
+	NULL,
+};
+
+void __init emev2_init_irq_dt(void)
+{
+	of_irq_init(emev2_dt_irq_match);
+}
+
+DT_MACHINE_START(EMEV2_DT, "Generic Emma Mobile EV2 (Flattened Device Tree)")
+	.init_early	= emev2_init_delay,
+	.nr_irqs	= NR_IRQS_LEGACY,
+	.init_irq	= emev2_init_irq_dt,
+	.handle_irq	= gic_handle_irq,
+	.init_machine	= emev2_add_standard_devices_dt,
+	.timer		= &shmobile_timer,
+	.dt_compat	= emev2_boards_compat_dt,
+MACHINE_END
+
+#endif /* CONFIG_USE_OF */

+ 97 - 0
arch/arm/mach-shmobile/smp-emev2.c

@@ -0,0 +1,97 @@
+/*
+ * SMP support for Emma Mobile EV2
+ *
+ * Copyright (C) 2012  Renesas Solutions Corp.
+ * Copyright (C) 2012  Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <mach/common.h>
+#include <mach/emev2.h>
+#include <asm/smp_plat.h>
+#include <asm/smp_scu.h>
+#include <asm/hardware/gic.h>
+#include <asm/cacheflush.h>
+
+#define EMEV2_SCU_BASE 0x1e000000
+
+static DEFINE_SPINLOCK(scu_lock);
+static void __iomem *scu_base;
+
+static void modify_scu_cpu_psr(unsigned long set, unsigned long clr)
+{
+	unsigned long tmp;
+
+	/* we assume this code is running on a different cpu
+	 * than the one that is changing coherency setting */
+	spin_lock(&scu_lock);
+	tmp = readl(scu_base + 8);
+	tmp &= ~clr;
+	tmp |= set;
+	writel(tmp, scu_base + 8);
+	spin_unlock(&scu_lock);
+
+}
+
+unsigned int __init emev2_get_core_count(void)
+{
+	if (!scu_base) {
+		scu_base = ioremap(EMEV2_SCU_BASE, PAGE_SIZE);
+		emev2_clock_init(); /* need ioremapped SMU */
+	}
+
+	WARN_ON_ONCE(!scu_base);
+
+	return scu_base ? scu_get_core_count(scu_base) : 1;
+}
+
+int emev2_platform_cpu_kill(unsigned int cpu)
+{
+	return 0; /* not supported yet */
+}
+
+void __cpuinit emev2_secondary_init(unsigned int cpu)
+{
+	gic_secondary_init(0);
+}
+
+int __cpuinit emev2_boot_secondary(unsigned int cpu)
+{
+	cpu = cpu_logical_map(cpu);
+
+	/* enable cache coherency */
+	modify_scu_cpu_psr(0, 3 << (cpu * 8));
+
+	/* Tell ROM loader about our vector (in headsmp.S) */
+	emev2_set_boot_vector(__pa(shmobile_secondary_vector));
+
+	gic_raise_softirq(cpumask_of(cpu), 1);
+	return 0;
+}
+
+void __init emev2_smp_prepare_cpus(void)
+{
+	int cpu = cpu_logical_map(0);
+
+	scu_enable(scu_base);
+
+	/* enable cache coherency on CPU0 */
+	modify_scu_cpu_psr(0, 3 << (cpu * 8));
+}

+ 6 - 0
drivers/gpio/Kconfig

@@ -91,6 +91,12 @@ config GPIO_IT8761E
 	help
 	  Say yes here to support GPIO functionality of IT8761E super I/O chip.
 
+config GPIO_EM
+	tristate "Emma Mobile GPIO"
+	depends on ARM
+	help
+	  Say yes here to support GPIO on Renesas Emma Mobile SoCs.
+
 config GPIO_EP93XX
 	def_bool y
 	depends on ARCH_EP93XX

+ 1 - 0
drivers/gpio/Makefile

@@ -15,6 +15,7 @@ obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
 obj-$(CONFIG_GPIO_DA9052)	+= gpio-da9052.o
 obj-$(CONFIG_ARCH_DAVINCI)	+= gpio-davinci.o
+obj-$(CONFIG_GPIO_EM)		+= gpio-em.o
 obj-$(CONFIG_GPIO_EP93XX)	+= gpio-ep93xx.o
 obj-$(CONFIG_GPIO_GE_FPGA)	+= gpio-ge.o
 obj-$(CONFIG_GPIO_IT8761E)	+= gpio-it8761e.o

+ 418 - 0
drivers/gpio/gpio-em.c

@@ -0,0 +1,418 @@
+/*
+ * Emma Mobile GPIO Support - GIO
+ *
+ *  Copyright (C) 2012 Magnus Damm
+ *
+ * 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
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_data/gpio-em.h>
+
+struct em_gio_priv {
+	void __iomem *base0;
+	void __iomem *base1;
+	unsigned int irq_base;
+	spinlock_t sense_lock;
+	struct platform_device *pdev;
+	struct gpio_chip gpio_chip;
+	struct irq_chip irq_chip;
+	struct irq_domain *irq_domain;
+};
+
+#define GIO_E1 0x00
+#define GIO_E0 0x04
+#define GIO_EM 0x04
+#define GIO_OL 0x08
+#define GIO_OH 0x0c
+#define GIO_I 0x10
+#define GIO_IIA 0x14
+#define GIO_IEN 0x18
+#define GIO_IDS 0x1c
+#define GIO_IIM 0x1c
+#define GIO_RAW 0x20
+#define GIO_MST 0x24
+#define GIO_IIR 0x28
+
+#define GIO_IDT0 0x40
+#define GIO_IDT1 0x44
+#define GIO_IDT2 0x48
+#define GIO_IDT3 0x4c
+#define GIO_RAWBL 0x50
+#define GIO_RAWBH 0x54
+#define GIO_IRBL 0x58
+#define GIO_IRBH 0x5c
+
+#define GIO_IDT(n) (GIO_IDT0 + ((n) * 4))
+
+static inline unsigned long em_gio_read(struct em_gio_priv *p, int offs)
+{
+	if (offs < GIO_IDT0)
+		return ioread32(p->base0 + offs);
+	else
+		return ioread32(p->base1 + (offs - GIO_IDT0));
+}
+
+static inline void em_gio_write(struct em_gio_priv *p, int offs,
+				unsigned long value)
+{
+	if (offs < GIO_IDT0)
+		iowrite32(value, p->base0 + offs);
+	else
+		iowrite32(value, p->base1 + (offs - GIO_IDT0));
+}
+
+static inline struct em_gio_priv *irq_to_priv(struct irq_data *d)
+{
+	struct irq_chip *chip = irq_data_get_irq_chip(d);
+	return container_of(chip, struct em_gio_priv, irq_chip);
+}
+
+static void em_gio_irq_disable(struct irq_data *d)
+{
+	struct em_gio_priv *p = irq_to_priv(d);
+
+	em_gio_write(p, GIO_IDS, BIT(irqd_to_hwirq(d)));
+}
+
+static void em_gio_irq_enable(struct irq_data *d)
+{
+	struct em_gio_priv *p = irq_to_priv(d);
+
+	em_gio_write(p, GIO_IEN, BIT(irqd_to_hwirq(d)));
+}
+
+#define GIO_ASYNC(x) (x + 8)
+
+static unsigned char em_gio_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
+	[IRQ_TYPE_EDGE_RISING] = GIO_ASYNC(0x00),
+	[IRQ_TYPE_EDGE_FALLING] = GIO_ASYNC(0x01),
+	[IRQ_TYPE_LEVEL_HIGH] = GIO_ASYNC(0x02),
+	[IRQ_TYPE_LEVEL_LOW] = GIO_ASYNC(0x03),
+	[IRQ_TYPE_EDGE_BOTH] = GIO_ASYNC(0x04),
+};
+
+static int em_gio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	unsigned char value = em_gio_sense_table[type & IRQ_TYPE_SENSE_MASK];
+	struct em_gio_priv *p = irq_to_priv(d);
+	unsigned int reg, offset, shift;
+	unsigned long flags;
+	unsigned long tmp;
+
+	if (!value)
+		return -EINVAL;
+
+	offset = irqd_to_hwirq(d);
+
+	pr_debug("gio: sense irq = %d, mode = %d\n", offset, value);
+
+	/* 8 x 4 bit fields in 4 IDT registers */
+	reg = GIO_IDT(offset >> 3);
+	shift = (offset & 0x07) << 4;
+
+	spin_lock_irqsave(&p->sense_lock, flags);
+
+	/* disable the interrupt in IIA */
+	tmp = em_gio_read(p, GIO_IIA);
+	tmp &= ~BIT(offset);
+	em_gio_write(p, GIO_IIA, tmp);
+
+	/* change the sense setting in IDT */
+	tmp = em_gio_read(p, reg);
+	tmp &= ~(0xf << shift);
+	tmp |= value << shift;
+	em_gio_write(p, reg, tmp);
+
+	/* clear pending interrupts */
+	em_gio_write(p, GIO_IIR, BIT(offset));
+
+	/* enable the interrupt in IIA */
+	tmp = em_gio_read(p, GIO_IIA);
+	tmp |= BIT(offset);
+	em_gio_write(p, GIO_IIA, tmp);
+
+	spin_unlock_irqrestore(&p->sense_lock, flags);
+
+	return 0;
+}
+
+static irqreturn_t em_gio_irq_handler(int irq, void *dev_id)
+{
+	struct em_gio_priv *p = dev_id;
+	unsigned long pending;
+	unsigned int offset, irqs_handled = 0;
+
+	while ((pending = em_gio_read(p, GIO_MST))) {
+		offset = __ffs(pending);
+		em_gio_write(p, GIO_IIR, BIT(offset));
+		generic_handle_irq(irq_find_mapping(p->irq_domain, offset));
+		irqs_handled++;
+	}
+
+	return irqs_handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline struct em_gio_priv *gpio_to_priv(struct gpio_chip *chip)
+{
+	return container_of(chip, struct em_gio_priv, gpio_chip);
+}
+
+static int em_gio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	em_gio_write(gpio_to_priv(chip), GIO_E0, BIT(offset));
+	return 0;
+}
+
+static int em_gio_get(struct gpio_chip *chip, unsigned offset)
+{
+	return (int)(em_gio_read(gpio_to_priv(chip), GIO_I) & BIT(offset));
+}
+
+static void __em_gio_set(struct gpio_chip *chip, unsigned int reg,
+			 unsigned shift, int value)
+{
+	/* upper 16 bits contains mask and lower 16 actual value */
+	em_gio_write(gpio_to_priv(chip), reg,
+		     (1 << (shift + 16)) | (value << shift));
+}
+
+static void em_gio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	/* output is split into two registers */
+	if (offset < 16)
+		__em_gio_set(chip, GIO_OL, offset, value);
+	else
+		__em_gio_set(chip, GIO_OH, offset - 16, value);
+}
+
+static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
+				   int value)
+{
+	/* write GPIO value to output before selecting output mode of pin */
+	em_gio_set(chip, offset, value);
+	em_gio_write(gpio_to_priv(chip), GIO_E1, BIT(offset));
+	return 0;
+}
+
+static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	return irq_find_mapping(gpio_to_priv(chip)->irq_domain, offset);
+}
+
+static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq,
+				 irq_hw_number_t hw)
+{
+	struct em_gio_priv *p = h->host_data;
+
+	pr_debug("gio: map hw irq = %d, virq = %d\n", (int)hw, virq);
+
+	irq_set_chip_data(virq, h->host_data);
+	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
+	set_irq_flags(virq, IRQF_VALID); /* kill me now */
+	return 0;
+}
+
+static struct irq_domain_ops em_gio_irq_domain_ops = {
+	.map	= em_gio_irq_domain_map,
+};
+
+static int __devinit em_gio_irq_domain_init(struct em_gio_priv *p)
+{
+	struct platform_device *pdev = p->pdev;
+	struct gpio_em_config *pdata = pdev->dev.platform_data;
+
+	p->irq_base = irq_alloc_descs(pdata->irq_base, 0,
+				      pdata->number_of_pins, numa_node_id());
+	if (IS_ERR_VALUE(p->irq_base)) {
+		dev_err(&pdev->dev, "cannot get irq_desc\n");
+		return -ENXIO;
+	}
+	pr_debug("gio: hw base = %d, nr = %d, sw base = %d\n",
+		 pdata->gpio_base, pdata->number_of_pins, p->irq_base);
+
+	p->irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
+					      pdata->number_of_pins,
+					      p->irq_base, 0,
+					      &em_gio_irq_domain_ops, p);
+	if (!p->irq_domain) {
+		irq_free_descs(p->irq_base, pdata->number_of_pins);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void __devexit em_gio_irq_domain_cleanup(struct em_gio_priv *p)
+{
+	struct gpio_em_config *pdata = p->pdev->dev.platform_data;
+
+	irq_free_descs(p->irq_base, pdata->number_of_pins);
+	/* FIXME: irq domain wants to be freed! */
+}
+
+static int __devinit em_gio_probe(struct platform_device *pdev)
+{
+	struct gpio_em_config *pdata = pdev->dev.platform_data;
+	struct em_gio_priv *p;
+	struct resource *io[2], *irq[2];
+	struct gpio_chip *gpio_chip;
+	struct irq_chip *irq_chip;
+	const char *name = dev_name(&pdev->dev);
+	int ret;
+
+	p = kzalloc(sizeof(*p), GFP_KERNEL);
+	if (!p) {
+		dev_err(&pdev->dev, "failed to allocate driver data\n");
+		ret = -ENOMEM;
+		goto err0;
+	}
+
+	p->pdev = pdev;
+	platform_set_drvdata(pdev, p);
+	spin_lock_init(&p->sense_lock);
+
+	io[0] = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	io[1] = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+
+	if (!io[0] || !io[1] || !irq[0] || !irq[1] || !pdata) {
+		dev_err(&pdev->dev, "missing IRQ, IOMEM or configuration\n");
+		ret = -EINVAL;
+		goto err1;
+	}
+
+	p->base0 = ioremap_nocache(io[0]->start, resource_size(io[0]));
+	if (!p->base0) {
+		dev_err(&pdev->dev, "failed to remap low I/O memory\n");
+		ret = -ENXIO;
+		goto err1;
+	}
+
+	p->base1 = ioremap_nocache(io[1]->start, resource_size(io[1]));
+	if (!p->base1) {
+		dev_err(&pdev->dev, "failed to remap high I/O memory\n");
+		ret = -ENXIO;
+		goto err2;
+	}
+
+	gpio_chip = &p->gpio_chip;
+	gpio_chip->direction_input = em_gio_direction_input;
+	gpio_chip->get = em_gio_get;
+	gpio_chip->direction_output = em_gio_direction_output;
+	gpio_chip->set = em_gio_set;
+	gpio_chip->to_irq = em_gio_to_irq;
+	gpio_chip->label = name;
+	gpio_chip->owner = THIS_MODULE;
+	gpio_chip->base = pdata->gpio_base;
+	gpio_chip->ngpio = pdata->number_of_pins;
+
+	irq_chip = &p->irq_chip;
+	irq_chip->name = name;
+	irq_chip->irq_mask = em_gio_irq_disable;
+	irq_chip->irq_unmask = em_gio_irq_enable;
+	irq_chip->irq_enable = em_gio_irq_enable;
+	irq_chip->irq_disable = em_gio_irq_disable;
+	irq_chip->irq_set_type = em_gio_irq_set_type;
+	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE;
+
+	ret = em_gio_irq_domain_init(p);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot initialize irq domain\n");
+		goto err3;
+	}
+
+	if (request_irq(irq[0]->start, em_gio_irq_handler, 0, name, p)) {
+		dev_err(&pdev->dev, "failed to request low IRQ\n");
+		ret = -ENOENT;
+		goto err4;
+	}
+
+	if (request_irq(irq[1]->start, em_gio_irq_handler, 0, name, p)) {
+		dev_err(&pdev->dev, "failed to request high IRQ\n");
+		ret = -ENOENT;
+		goto err5;
+	}
+
+	ret = gpiochip_add(gpio_chip);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add GPIO controller\n");
+		goto err6;
+	}
+	return 0;
+
+err6:
+	free_irq(irq[1]->start, pdev);
+err5:
+	free_irq(irq[0]->start, pdev);
+err4:
+	em_gio_irq_domain_cleanup(p);
+err3:
+	iounmap(p->base1);
+err2:
+	iounmap(p->base0);
+err1:
+	kfree(p);
+err0:
+	return ret;
+}
+
+static int __devexit em_gio_remove(struct platform_device *pdev)
+{
+	struct em_gio_priv *p = platform_get_drvdata(pdev);
+	struct resource *irq[2];
+	int ret;
+
+	ret = gpiochip_remove(&p->gpio_chip);
+	if (ret)
+		return ret;
+
+	irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+
+	free_irq(irq[1]->start, pdev);
+	free_irq(irq[0]->start, pdev);
+	em_gio_irq_domain_cleanup(p);
+	iounmap(p->base1);
+	iounmap(p->base0);
+	kfree(p);
+	return 0;
+}
+
+static struct platform_driver em_gio_device_driver = {
+	.probe		= em_gio_probe,
+	.remove		= __devexit_p(em_gio_remove),
+	.driver		= {
+		.name	= "em_gio",
+	}
+};
+
+module_platform_driver(em_gio_device_driver);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Renesas Emma Mobile GIO Driver");
+MODULE_LICENSE("GPL v2");

+ 10 - 0
include/linux/platform_data/gpio-em.h

@@ -0,0 +1,10 @@
+#ifndef __GPIO_EM_H__
+#define __GPIO_EM_H__
+
+struct gpio_em_config {
+	unsigned int gpio_base;
+	unsigned int irq_base;
+	unsigned int number_of_pins;
+};
+
+#endif /* __GPIO_EM_H__ */