summaryrefslogtreecommitdiff
path: root/arch/arm64/platforms
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm64/platforms')
-rw-r--r--arch/arm64/platforms/Kconfig9
-rw-r--r--arch/arm64/platforms/Makefile5
-rw-r--r--arch/arm64/platforms/vexpress.c271
-rw-r--r--arch/arm64/platforms/vexpress.h64
4 files changed, 349 insertions, 0 deletions
diff --git a/arch/arm64/platforms/Kconfig b/arch/arm64/platforms/Kconfig
new file mode 100644
index 00000000000..35f51a2eb73
--- /dev/null
+++ b/arch/arm64/platforms/Kconfig
@@ -0,0 +1,9 @@
+config PLAT_VEXPRESS
+ bool "ARMv8 software model (Versatile Express)"
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARM_AMBA
+ select CLKDEV_LOOKUP
+ select ARM64_GIC
+ help
+ This enables support for the ARMv8 software model (Versatile
+ Express).
diff --git a/arch/arm64/platforms/Makefile b/arch/arm64/platforms/Makefile
new file mode 100644
index 00000000000..8dc8e0f7352
--- /dev/null
+++ b/arch/arm64/platforms/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the linux kernel.
+#
+
+obj-$(CONFIG_PLAT_VEXPRESS) := vexpress.o
diff --git a/arch/arm64/platforms/vexpress.c b/arch/arm64/platforms/vexpress.c
new file mode 100644
index 00000000000..6afdad1b57d
--- /dev/null
+++ b/arch/arm64/platforms/vexpress.c
@@ -0,0 +1,271 @@
+/*
+ * Versatile Express V2M Motherboard Support
+ */
+#include <linux/export.h>
+#include <linux/amba/mmci.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/of_fdt.h>
+#include <linux/spinlock.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/mm.h>
+
+#include <asm/system_misc.h>
+
+#include "vexpress.h"
+
+/*
+ * Versatile Express System Registers.
+ */
+static const struct of_device_id v2m_sysregs_match[] __initconst = {
+ { .compatible = "arm,vexpress-sysreg", },
+ {},
+};
+
+static void __iomem *v2m_sysregs_base __read_mostly;
+static DEFINE_SPINLOCK(v2m_sysregs_cfg_lock);
+
+static int __init v2m_sysregs_probe(void)
+{
+ struct device_node *node;
+
+ node = of_find_matching_node(NULL, v2m_sysregs_match);
+ if (!node)
+ panic("unable to find compatible v2m sysregs node in dtb\n");
+
+ v2m_sysregs_base = of_iomap(node, 0);
+ if (!v2m_sysregs_base)
+ panic("unable to map v2m system registers\n");
+
+ of_node_put(node);
+
+ return 0;
+}
+
+static int v2m_sysregs_cfg_write(u32 devfn, u32 data)
+{
+ u32 val;
+
+ printk("%s: writing %08x to %08x\n", __func__, data, devfn);
+
+ devfn |= SYS_CFG_START | SYS_CFG_WRITE;
+
+ spin_lock(&v2m_sysregs_cfg_lock);
+ val = readl(v2m_sysregs_base + V2M_SYS_CFGSTAT);
+ writel(val & ~SYS_CFG_COMPLETE, v2m_sysregs_base + V2M_SYS_CFGSTAT);
+
+ writel(data, v2m_sysregs_base + V2M_SYS_CFGDATA);
+ writel(devfn, v2m_sysregs_base + V2M_SYS_CFGCTRL);
+
+ do {
+ val = readl(v2m_sysregs_base + V2M_SYS_CFGSTAT);
+ } while (val == 0);
+ spin_unlock(&v2m_sysregs_cfg_lock);
+
+ return !!(val & SYS_CFG_ERR);
+}
+
+/*
+ * Clocks.
+ */
+static unsigned long v2m_osc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return 0;
+}
+
+static long v2m_osc_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return rate;
+}
+
+static int v2m_osc1_set_rate(struct clk_hw *clk_hw, unsigned long rate,
+ unsigned long prate)
+{
+ return v2m_sysregs_cfg_write(SYS_CFG_OSC | SYS_CFG_SITE_MB | 1, rate);
+}
+
+static const struct clk_ops osc1_clk_ops = {
+ .recalc_rate = v2m_osc_recalc_rate,
+ .round_rate = v2m_osc_round_rate,
+ .set_rate = v2m_osc1_set_rate,
+};
+
+static struct clk_init_data osc1_clk_init_data = {
+ .name = "osc1_clk",
+ .ops = &osc1_clk_ops,
+ .flags = CLK_IS_ROOT,
+};
+
+static struct clk_hw osc1_clk_hw = {
+ .init = &osc1_clk_init_data,
+};
+
+static void __init v2m_clk_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0);
+ WARN_ON(clk_register_clkdev(clk, "abp_pclk", NULL));
+
+ clk = clk_register(NULL, &osc1_clk_hw);
+ WARN_ON(clk_register_clkdev(clk, NULL, "mb:clcd"));
+
+ clk = clk_register_fixed_rate(NULL, "osc2_clk", NULL, CLK_IS_ROOT,
+ 24000000);
+ WARN_ON(clk_register_clkdev(clk, NULL, "mb:mmci"));
+ WARN_ON(clk_register_clkdev(clk, NULL, "1c060000.kmi"));
+ WARN_ON(clk_register_clkdev(clk, NULL, "1c070000.kmi"));
+ WARN_ON(clk_register_clkdev(clk, NULL, "1c090000.uart"));
+ WARN_ON(clk_register_clkdev(clk, NULL, "1c0a0000.uart"));
+ WARN_ON(clk_register_clkdev(clk, NULL, "1c0b0000.uart"));
+ WARN_ON(clk_register_clkdev(clk, NULL, "1c0c0000.uart"));
+
+ clk = clk_register_fixed_rate(NULL, "v2m_ref_clk", NULL, CLK_IS_ROOT,
+ 32768);
+ WARN_ON(clk_register_clkdev(clk, NULL, "1c0f0000.wdt"));
+}
+
+/*
+ * CLCDC.
+ */
+static struct clcd_panel xvga_panel = {
+ .mode = {
+ .name = "XVGA",
+ .refresh = 60,
+ .xres = 1024,
+ .yres = 768,
+ .pixclock = 15384,
+ .left_margin = 168,
+ .right_margin = 8,
+ .upper_margin = 29,
+ .lower_margin = 3,
+ .hsync_len = 144,
+ .vsync_len = 6,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ .width = -1,
+ .height = -1,
+ .tim2 = TIM2_BCD | TIM2_IPC,
+ .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1),
+ .bpp = 16,
+};
+
+static void v2m_clcd_enable(struct clcd_fb *fb)
+{
+ v2m_sysregs_cfg_write(SYS_CFG_MUXFPGA | SYS_CFG_SITE_MB, 0);
+}
+
+static int v2m_clcd_setup(struct clcd_fb *fb)
+{
+ unsigned long framesize = 1024 * 768 * 2;
+
+ fb->panel = &xvga_panel;
+ fb->fb.screen_base = ioremap_wc(V2M_VIDEO_SRAM, framesize);
+
+ if (!fb->fb.screen_base) {
+ pr_err("CLCD: unable to map frame buffer\n");
+ return -ENOMEM;
+ }
+
+ fb->fb.fix.smem_start = V2M_VIDEO_SRAM;
+ fb->fb.fix.smem_len = framesize;
+
+ return 0;
+}
+
+static int v2m_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ unsigned long off, user_size, kern_size;
+
+ off = vma->vm_pgoff << PAGE_SHIFT;
+ user_size = vma->vm_end - vma->vm_start;
+ kern_size = fb->fb.fix.smem_len;
+
+ if (off >= kern_size || user_size > (kern_size - off))
+ return -ENXIO;
+
+ return remap_pfn_range(vma, vma->vm_start,
+ __phys_to_pfn(fb->fb.fix.smem_start) + vma->vm_pgoff,
+ user_size,
+ pgprot_writecombine(vma->vm_page_prot));
+}
+
+static void v2m_clcd_remove(struct clcd_fb *fb)
+{
+ iounmap(fb->fb.screen_base);
+}
+
+/*
+ * Platform data definitions.
+ */
+static unsigned int v2m_mmci_status(struct device *dev)
+{
+ return readl(v2m_sysregs_base + V2M_SYS_MCI) & (1 << 0);
+}
+
+static struct mmci_platform_data v2m_mmci_data = {
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+ .status = v2m_mmci_status,
+};
+
+static struct clcd_board v2m_clcd_data = {
+ .name = "V2M",
+ .check = clcdfb_check,
+ .decode = clcdfb_decode,
+ .enable = v2m_clcd_enable,
+ .setup = v2m_clcd_setup,
+ .mmap = v2m_clcd_mmap,
+ .remove = v2m_clcd_remove,
+};
+
+
+static struct of_dev_auxdata v2m_dt_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("arm,primecell", V2M_MMCI, "mb:mmci", &v2m_mmci_data),
+ OF_DEV_AUXDATA("arm,primecell", V2M_CLCD, "mb:clcd", &v2m_clcd_data),
+ {}
+};
+
+static void v2m_power_off(void)
+{
+ if (v2m_sysregs_cfg_write(SYS_CFG_SHUTDOWN | SYS_CFG_SITE_MB, 0))
+ pr_emerg("Unable to shutdown\n");
+}
+
+static void v2m_restart(const char *cmd)
+{
+ if (v2m_sysregs_cfg_write(SYS_CFG_REBOOT | SYS_CFG_SITE_MB, 0))
+ pr_emerg("Unable to reboot\n");
+}
+
+static const char *vexpress_dt_match[] __initdata = {
+ "arm,vexpress",
+ NULL,
+};
+
+static int __init v2m_probe(void)
+{
+ if (!of_flat_dt_match(of_get_flat_dt_root(), vexpress_dt_match))
+ return 0;
+
+ v2m_sysregs_probe();
+
+ v2m_clk_init();
+
+ of_platform_populate(NULL, of_default_bus_match_table,
+ v2m_dt_auxdata_lookup, NULL);
+
+ pm_power_off = v2m_power_off;
+ pm_restart = v2m_restart;
+
+ return 0;
+}
+arch_initcall(v2m_probe);
diff --git a/arch/arm64/platforms/vexpress.h b/arch/arm64/platforms/vexpress.h
new file mode 100644
index 00000000000..6ea8e6f0200
--- /dev/null
+++ b/arch/arm64/platforms/vexpress.h
@@ -0,0 +1,64 @@
+#ifndef __MACH_MOTHERBOARD_H
+#define __MACH_MOTHERBOARD_H
+
+/* CS register bases for the extended memory map. */
+#define V2M_PA_CS0 0x08000000
+#define V2M_PA_CS3 0x18000000
+#define V2M_PA_CS7 0x1c000000
+
+#define V2M_PERIPH_SHIFT 16
+#define V2M_PERIPH_OFFSET(x) (x << V2M_PERIPH_SHIFT)
+
+#define V2M_NOR0 (V2M_PA_CS0 + V2M_PERIPH_OFFSET(0))
+#define V2M_VIDEO_SRAM (V2M_PA_CS3 + V2M_PERIPH_OFFSET(0))
+#define V2M_MMCI (V2M_PA_CS7 + V2M_PERIPH_OFFSET(5))
+#define V2M_CLCD (V2M_PA_CS7 + V2M_PERIPH_OFFSET(31))
+
+/* System register offsets. */
+#define V2M_SYS_ID 0x000
+#define V2M_SYS_SW 0x004
+#define V2M_SYS_LED 0x008
+#define V2M_SYS_100HZ 0x024
+#define V2M_SYS_FLAGS 0x030
+#define V2M_SYS_FLAGSSET 0x030
+#define V2M_SYS_FLAGSCLR 0x034
+#define V2M_SYS_NVFLAGS 0x038
+#define V2M_SYS_NVFLAGSSET 0x038
+#define V2M_SYS_NVFLAGSCLR 0x03c
+#define V2M_SYS_MCI 0x048
+#define V2M_SYS_FLASH 0x03c
+#define V2M_SYS_CFGSW 0x058
+#define V2M_SYS_24MHZ 0x05c
+#define V2M_SYS_MISC 0x060
+#define V2M_SYS_DMA 0x064
+#define V2M_SYS_PROCID0 0x084
+#define V2M_SYS_PROCID1 0x088
+#define V2M_SYS_CFGDATA 0x0a0
+#define V2M_SYS_CFGCTRL 0x0a4
+#define V2M_SYS_CFGSTAT 0x0a8
+
+/*
+ * Configuration
+ */
+#define SYS_CFG_START (1 << 31)
+#define SYS_CFG_WRITE (1 << 30)
+#define SYS_CFG_OSC (1 << 20)
+#define SYS_CFG_VOLT (2 << 20)
+#define SYS_CFG_AMP (3 << 20)
+#define SYS_CFG_TEMP (4 << 20)
+#define SYS_CFG_RESET (5 << 20)
+#define SYS_CFG_SCC (6 << 20)
+#define SYS_CFG_MUXFPGA (7 << 20)
+#define SYS_CFG_SHUTDOWN (8 << 20)
+#define SYS_CFG_REBOOT (9 << 20)
+#define SYS_CFG_DVIMODE (11 << 20)
+#define SYS_CFG_POWER (12 << 20)
+#define SYS_CFG_SITE_MB (0 << 16)
+#define SYS_CFG_SITE_DB1 (1 << 16)
+#define SYS_CFG_SITE_DB2 (2 << 16)
+#define SYS_CFG_STACK(n) ((n) << 12)
+
+#define SYS_CFG_ERR (1 << 1)
+#define SYS_CFG_COMPLETE (1 << 0)
+
+#endif