mach-shmobile: Emma Mobile EV2 SoC base support V3
authorMagnus Damm <damm@opensource.se>
Wed, 16 May 2012 06:44:58 +0000 (15:44 +0900)
committerRafael J. Wysocki <rjw@sisk.pl>
Thu, 17 May 2012 22:14:01 +0000 (00:14 +0200)
This is V3 of the Emma Mobile EV2 SoC support.
Included here is support for serial and timer
devices which is just about enough to boot a kernel.

Signed-off-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
arch/arm/mach-shmobile/Kconfig
arch/arm/mach-shmobile/Makefile
arch/arm/mach-shmobile/clock-emev2.c [new file with mode: 0644]
arch/arm/mach-shmobile/include/mach/emev2.h [new file with mode: 0644]
arch/arm/mach-shmobile/setup-emev2.c [new file with mode: 0644]

index 34560ca..fadafe3 100644 (file)
@@ -41,6 +41,11 @@ config ARCH_R8A7779
        select ARM_GIC
        select ARCH_WANT_OPTIONAL_GPIOLIB
 
+config ARCH_EMEV2
+       bool "Emma Mobile EV2"
+       select CPU_V7
+       select ARM_GIC
+
 comment "SH-Mobile Board Type"
 
 config MACH_G3EVM
index e7c2590..fd84aa2 100644 (file)
@@ -12,6 +12,7 @@ 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
diff --git a/arch/arm/mach-shmobile/clock-emev2.c b/arch/arm/mach-shmobile/clock-emev2.c
new file mode 100644 (file)
index 0000000..73a1216
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * 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
+
+/* 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);
+}
+
+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("serial8250-em.1", &gclk_clks[GCLK_USIBU1_SCLK]),
+       CLKDEV_DEV_ID("serial8250-em.2", &gclk_clks[GCLK_USIBU2_SCLK]),
+       CLKDEV_DEV_ID("serial8250-em.3", &gclk_clks[GCLK_USIBU3_SCLK]),
+       CLKDEV_DEV_ID("em_sti.0", &gclk_clks[GCLK_STI_SCLK]),
+};
+
+void __init emev2_clock_init(void)
+{
+       int k, ret = 0;
+
+       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 (file)
index 0000000..92646c1
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __ASM_EMEV2_H__
+#define __ASM_EMEV2_H__
+
+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);
+
+#endif /* __ASM_EMEV2_H__ */
diff --git a/arch/arm/mach-shmobile/setup-emev2.c b/arch/arm/mach-shmobile/setup-emev2.c
new file mode 100644 (file)
index 0000000..9fff623
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * 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/delay.h>
+#include <linux/input.h>
+#include <linux/io.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>
+
+/* 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),
+};
+
+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,
+};
+
+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_add_early_devices(void)
+{
+       shmobile_setup_delay(533, 1, 3); /* Cortex-A9 @ 533MHz */
+
+       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);
+}