summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2012-05-19 11:57:36 -0700
committerOlof Johansson <olof@lixom.net>2012-05-19 11:57:36 -0700
commit0804dcb2afdcf014947ee98264041765f580d43f (patch)
tree7711bd9db2e31a23880b2d463066de92f0f64a52
parent19a64d9d591f9d188707e86e53bc0c4ed99ac8f0 (diff)
parent450cca47d65b7500ba05d56770adbf8c758dc87d (diff)
downloadlinux-3.10-0804dcb2afdcf014947ee98264041765f580d43f.tar.gz
linux-3.10-0804dcb2afdcf014947ee98264041765f580d43f.tar.bz2
linux-3.10-0804dcb2afdcf014947ee98264041765f580d43f.zip
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
-rw-r--r--arch/arm/boot/dts/emev2-kzm9d.dts26
-rw-r--r--arch/arm/boot/dts/emev2.dtsi63
-rw-r--r--arch/arm/mach-shmobile/Kconfig11
-rw-r--r--arch/arm/mach-shmobile/Makefile3
-rw-r--r--arch/arm/mach-shmobile/board-kzm9d.c85
-rw-r--r--arch/arm/mach-shmobile/clock-emev2.c249
-rw-r--r--arch/arm/mach-shmobile/include/mach/emev2.h19
-rw-r--r--arch/arm/mach-shmobile/platsmp.c18
-rw-r--r--arch/arm/mach-shmobile/setup-emev2.c452
-rw-r--r--arch/arm/mach-shmobile/smp-emev2.c97
-rw-r--r--drivers/gpio/Kconfig6
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-em.c418
-rw-r--r--include/linux/platform_data/gpio-em.h10
14 files changed, 1458 insertions, 0 deletions
diff --git a/arch/arm/boot/dts/emev2-kzm9d.dts b/arch/arm/boot/dts/emev2-kzm9d.dts
new file mode 100644
index 00000000000..297e3baba71
--- /dev/null
+++ b/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";
+ };
+};
diff --git a/arch/arm/boot/dts/emev2.dtsi b/arch/arm/boot/dts/emev2.dtsi
new file mode 100644
index 00000000000..eb504a6c0f4
--- /dev/null
+++ b/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>;
+ };
+};
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
index 34560cab45d..7dcf08ee979 100644
--- a/arch/arm/mach-shmobile/Kconfig
+++ b/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
diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
index e7c2590b75d..c795335931c 100644
--- a/arch/arm/mach-shmobile/Makefile
+++ b/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)
diff --git a/arch/arm/mach-shmobile/board-kzm9d.c b/arch/arm/mach-shmobile/board-kzm9d.c
new file mode 100644
index 00000000000..7bc5e7d39f9
--- /dev/null
+++ b/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
diff --git a/arch/arm/mach-shmobile/clock-emev2.c b/arch/arm/mach-shmobile/clock-emev2.c
new file mode 100644
index 00000000000..4710f1847bb
--- /dev/null
+++ b/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");
+}
diff --git a/arch/arm/mach-shmobile/include/mach/emev2.h b/arch/arm/mach-shmobile/include/mach/emev2.h
new file mode 100644
index 00000000000..e6b0c1bf4b7
--- /dev/null
+++ b/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__ */
diff --git a/arch/arm/mach-shmobile/platsmp.c b/arch/arm/mach-shmobile/platsmp.c
index 45fa3924c6a..7006cdc8b8c 100644
--- a/arch/arm/mach-shmobile/platsmp.c
+++ b/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;
}
diff --git a/arch/arm/mach-shmobile/setup-emev2.c b/arch/arm/mach-shmobile/setup-emev2.c
new file mode 100644
index 00000000000..dae9aa68bb0
--- /dev/null
+++ b/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 */
diff --git a/arch/arm/mach-shmobile/smp-emev2.c b/arch/arm/mach-shmobile/smp-emev2.c
new file mode 100644
index 00000000000..6a35c4a31e6
--- /dev/null
+++ b/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));
+}
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e03653d6935..eb80ba30045 100644
--- a/drivers/gpio/Kconfig
+++ b/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
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 007f54bd008..3f1f829260b 100644
--- a/drivers/gpio/Makefile
+++ b/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
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
new file mode 100644
index 00000000000..150d9768811
--- /dev/null
+++ b/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");
diff --git a/include/linux/platform_data/gpio-em.h b/include/linux/platform_data/gpio-em.h
new file mode 100644
index 00000000000..573edfb046c
--- /dev/null
+++ b/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__ */