linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
@ 2010-11-07 16:28 Alexey Charkov
  2010-11-07 16:28 ` [PATCH 2/6 v2] serial: Add support for UART on VIA VT8500 and compatibles Alexey Charkov
                   ` (6 more replies)
  0 siblings, 7 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-07 16:28 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, Russell King,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505. Suitable code is
selected (if compiled in) at early initialization time by reading a
platform-specific identification register, as current bootloaders
do not provide any reliable machine id to the kernel.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Please review and (if appropriate) commit to a relevant git tree for
further integration in 2.6.38.

Compared to the previous submissions, this code introduces boot-time
determintaion of SoC version (instead of just compile-time preselection),
boot-argument override for the LCD panel size (used when preallocating
frame buffer memory), better guesses for PCI I/O space configuration
(thanks to Arnd Bergmann for suggestions), as well as a rebase against
the latest changes introduced in 2.6.37 merge window.

Due credits go to the community for providing feedback, advice and
testing for the code in this patch and follow-ups.

 arch/arm/Kconfig                                |   14 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 +++
 arch/arm/mach-vt8500/Kconfig                    |   65 ++++
 arch/arm/mach-vt8500/Makefile                   |    6 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   70 ++++
 arch/arm/mach-vt8500/devices.c                  |  429 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   43 +++
 arch/arm/mach-vt8500/gpio.c                     |  230 ++++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 ++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 ++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/io.h          |   28 ++
 arch/arm/mach-vt8500/include/mach/irq_defs.h    |  124 +++++++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 ++
 arch/arm/mach-vt8500/include/mach/mmio_regs.h   |   90 +++++
 arch/arm/mach-vt8500/include/mach/system.h      |   18 +
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 ++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 +
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 ++
 arch/arm/mach-vt8500/irq.c                      |  168 +++++++++
 arch/arm/mach-vt8500/irq_defs.c                 |  173 +++++++++
 arch/arm/mach-vt8500/mmio_regs.c                |  118 +++++++
 arch/arm/mach-vt8500/pwm.c                      |  254 ++++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  166 +++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   69 ++++
 31 files changed, 2364 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irq_defs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/mmio_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/irq_defs.c
 create mode 100644 arch/arm/mach-vt8500/mmio_regs.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a19a526..e0724ac 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,18 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_CLK
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +985,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..e0c6268
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,65 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+	default n
+
+config VTWM_VERSION_WM8505
+	bool
+	default n
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..aff4159
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,6 @@
+obj-y += devices.o gpio.o irq.o irq_defs.o mmio_regs.o timer.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..83c1542
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,70 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+
+	writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+	iounmap(gpio_mux_reg);
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+	pm_power_off = &vt8500_power_off;
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= vt8500_map_io,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..156cd5f
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,429 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include <mach/vt8500fb.h>
+#include "devices.h"
+
+static struct resource resources_lcdc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources  = ARRAY_SIZE(resources_lcdc),
+	.resource       = resources_lcdc,
+};
+
+static struct resource resources_wm8505_fb[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(resources_wm8505_fb),
+	.resource       = resources_wm8505_fb,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 43,
+		.right_margin	= 2,
+		.upper_margin	= 10,
+		.lower_margin	= 2,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		int len = strlen(panels[i].mode.name);
+
+		if (memcmp(panels[i].mode.name, str, len) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+static struct resource resources_uart0[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart1[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart2[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart3[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart4[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart5[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_uart0),
+	.resource	= resources_uart0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(resources_uart1),
+	.resource	= resources_uart1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(resources_uart2),
+	.resource	= resources_uart2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(resources_uart3),
+	.resource	= resources_uart3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+	.num_resources	= ARRAY_SIZE(resources_uart4),
+	.resource	= resources_uart4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+	.num_resources	= ARRAY_SIZE(resources_uart5),
+	.resource	= resources_uart5,
+};
+
+static struct resource resources_ehci[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(resources_ehci),
+	.resource	= resources_ehci,
+};
+
+static struct resource resources_ge_rops[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_ge_rops),
+	.resource	= resources_ge_rops,
+};
+
+static struct resource resources_pwm[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+	.resource	= resources_pwm,
+	.num_resources	= ARRAY_SIZE(resources_pwm),
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+static struct resource resources_rtc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+	.resource	= resources_rtc,
+	.num_resources	= ARRAY_SIZE(resources_rtc),
+};
+
+static struct map_desc vt8500_io_desc[] __initdata = {
+	/* SoC MMIO registers, to be filled in later */
+	[0] = {
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+static inline void set_data(void)
+{
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	resources_lcdc[0].start = wmt_current_regs->lcdc;
+	resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
+	resources_lcdc[1].start = wmt_current_irqs->lcdc;
+	resources_lcdc[1].end = wmt_current_irqs->lcdc;
+
+	resources_wm8505_fb[0].start = wmt_current_regs->govr;
+	resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
+
+	resources_uart0[0].start = wmt_current_regs->uart0;
+	resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
+	resources_uart0[1].start = wmt_current_irqs->uart0;
+	resources_uart0[1].end = wmt_current_irqs->uart0;
+	resources_uart1[0].start = wmt_current_regs->uart1;
+	resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
+	resources_uart1[1].start = wmt_current_irqs->uart1;
+	resources_uart1[1].end = wmt_current_irqs->uart1;
+	resources_uart2[0].start = wmt_current_regs->uart2;
+	resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
+	resources_uart2[1].start = wmt_current_irqs->uart2;
+	resources_uart2[1].end = wmt_current_irqs->uart2;
+	resources_uart3[0].start = wmt_current_regs->uart3;
+	resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
+	resources_uart3[1].start = wmt_current_irqs->uart3;
+	resources_uart3[1].end = wmt_current_irqs->uart3;
+	resources_uart4[0].start = wmt_current_regs->uart4;
+	resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
+	resources_uart4[1].start = wmt_current_irqs->uart4;
+	resources_uart4[1].end = wmt_current_irqs->uart4;
+	resources_uart5[0].start = wmt_current_regs->uart5;
+	resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
+	resources_uart5[1].start = wmt_current_irqs->uart5;
+	resources_uart5[1].end = wmt_current_irqs->uart5;
+
+	resources_ehci[0].start = wmt_current_regs->ehci;
+	resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
+	resources_ehci[1].start = wmt_current_irqs->ehci;
+	resources_ehci[1].end = wmt_current_irqs->ehci;
+
+	resources_ge_rops[0].start = wmt_current_regs->ge;
+	resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
+
+	resources_pwm[0].start = wmt_current_regs->pwm;
+	resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
+
+	resources_rtc[0].start = wmt_current_regs->rtc;
+	resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
+	resources_rtc[1].start = wmt_current_irqs->rtc;
+	resources_rtc[1].end = wmt_current_irqs->rtc;
+	resources_rtc[2].start = wmt_current_irqs->rtc_hz;
+	resources_rtc[2].end = wmt_current_irqs->rtc_hz;
+}
+
+void __init vt8500_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
+	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
+	set_data();
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init wm8505_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
+	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
+	set_data();
+#ifdef CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..dacaf77
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,43 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_gpio_init(void);
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..f533ff0
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,230 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/gpio.h>
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	else
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) &
+			~(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
+	VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
+	VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
+	VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
+	VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
+	VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
+
+	VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
+	VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
+	VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
+	VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
+
+	VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
+	VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
+	VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
+
+	VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
+
+	VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
+
+	VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	writel(readl(regbase + 0x3c) & ~(1 << offset), regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	writel(readl(regbase + 0x3c) | (1 << offset), regbase + 0x3c);
+
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	else
+		writel(readl(regbase + 0x5c) & ~(1 << offset),
+			regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	gpio_to_irq_map[0] = wmt_current_irqs->ext0;
+	gpio_to_irq_map[1] = wmt_current_irqs->ext1;
+	gpio_to_irq_map[2] = wmt_current_irqs->ext2;
+	gpio_to_irq_map[3] = wmt_current_irqs->ext3;
+	gpio_to_irq_map[4] = wmt_current_irqs->ext4;
+	gpio_to_irq_map[5] = wmt_current_irqs->ext5;
+	gpio_to_irq_map[6] = wmt_current_irqs->ext6;
+	gpio_to_irq_map[7] = wmt_current_irqs->ext7;
+
+	regbase = ioremap(wmt_current_regs->gpio, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..8dd55c8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		((void __iomem *)((a) + 0xf0000000))
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irq_defs.h b/arch/arm/mach-vt8500/include/mach/irq_defs.h
new file mode 100644
index 0000000..fa8f4b3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irq_defs.h
@@ -0,0 +1,124 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irq_defs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef VT8500_IRQ_DEFS_H
+#define VT8500_IRQ_DEFS_H
+
+#include <linux/types.h>
+
+struct wmt_irq_srcs {
+	u8 nr_irqs;
+	u8 jpegenc;
+	u8 jpegdec;
+	u8 pata;
+	u8 dma;
+	u8 ext0;
+	u8 ext1;
+	u8 ext2;
+	u8 ext3;
+	u8 ext4;
+	u8 ext5;
+	u8 ext6;
+	u8 ext7;
+	u8 ether;
+	u8 mpegts;
+	u8 ge;
+	u8 gov;
+	u8 lcdc;
+	u8 lcdf;
+	u8 vpp;
+	u8 vpu;
+	u8 vid;
+	u8 spu;
+	u8 pip;
+	u8 dvo;
+	u8 govw;
+	u8 govrsdscd;
+	u8 govrsdmif;
+	u8 govrhdscd;
+	u8 govrhdmif;
+	u8 cipher;
+	u8 i2c0;
+	u8 i2c1;
+	u8 sdmmc;
+	u8 sdmmc_dma;
+	u8 pmc_wu;
+	u8 spi0;
+	u8 spi1;
+	u8 spi2;
+	u8 nand;
+	u8 nand_dma;
+	u8 nor;
+	u8 memstick;
+	u8 memstick_dma;
+	u8 uart0;
+	u8 uart1;
+	u8 uart2;
+	u8 uart3;
+	u8 uart4;
+	u8 uart5;
+	u8 i2s;
+	u8 pcm;
+	u8 ac97;
+	u8 timer_match0;
+	u8 timer_match1;
+	u8 timer_match2;
+	u8 timer_match3;
+	u8 ehci;
+	u8 uhci;
+	u8 udc;
+	u8 udc_dma;
+	u8 keypad;
+	u8 ps2mouse;
+	u8 ps2kbd;
+	u8 rtc;
+	u8 rtc_hz;
+	u8 adc;
+	u8 cir;
+	u8 dma0;
+	u8 dma1;
+	u8 dma2;
+	u8 dma3;
+	u8 dma4;
+	u8 dma5;
+	u8 dma6;
+	u8 dma7;
+	u8 dma8;
+	u8 dma9;
+	u8 dma10;
+	u8 dma11;
+	u8 dma12;
+	u8 dma13;
+	u8 dma14;
+	u8 dma15;
+	u8 irq0;
+	u8 irq1;
+	u8 irq2;
+	u8 irq3;
+	u8 irq4;
+	u8 irq5;
+	u8 irq6;
+	u8 irq7;
+	u8 sae;
+};
+
+extern struct wmt_irq_srcs wmt_irqs[] __initdata;
+extern struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/mmio_regs.h b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
new file mode 100644
index 0000000..76439dd
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
@@ -0,0 +1,90 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/mmio_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_MMIO_REGS_H
+#define __ASM_ARM_ARCH_MMIO_REGS_H
+
+#include <linux/init.h>
+
+struct wmt_mmio_regs {
+	unsigned long mmio_regs_start;
+	unsigned long mmio_regs_length;
+	unsigned long mmio_regs_virt;
+	unsigned long ddr;
+	unsigned long dma;
+	unsigned long vdma;
+	unsigned long sflash;
+	unsigned long ether;
+	unsigned long cipher;
+	unsigned long ehci;
+	unsigned long uhci;
+	unsigned long pata;
+	unsigned long ps2;
+	unsigned long nand;
+	unsigned long nor;
+	unsigned long sdmmc;
+	unsigned long memstick;
+	unsigned long lcdc;
+	unsigned long vpu;
+	unsigned long gov;
+	unsigned long ge;
+	unsigned long govr;
+	unsigned long scl;
+	unsigned long lcdf;
+	unsigned long vid;
+	unsigned long vpp;
+	unsigned long tsbk;
+	unsigned long jpegdec;
+	unsigned long jpegenc;
+	unsigned long rtc;
+	unsigned long gpio;
+	unsigned long scc;
+	unsigned long pmc;
+	unsigned long ic0;
+	unsigned long ic1;
+	unsigned long uart0;
+	unsigned long uart1;
+	unsigned long uart2;
+	unsigned long uart3;
+	unsigned long uart4;
+	unsigned long uart5;
+	unsigned long pwm;
+	unsigned long spi0;
+	unsigned long spi1;
+	unsigned long spi2;
+	unsigned long cir;
+	unsigned long i2c0;
+	unsigned long i2c1;
+	unsigned long ac97;
+	unsigned long pcm;
+	unsigned long i2s;
+	unsigned long adc;
+	unsigned long keypad;
+};
+
+enum {
+	VT8500_INDEX,
+	WM8505_INDEX,
+};
+
+extern struct wmt_mmio_regs wmt_regmaps[] __initdata;
+extern struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+#endif
+
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..ba4cd40
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,18 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR	0xd8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	writel(1, ioremap(VT8500_PMSR, 4));
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..75a6912
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END		(PAGE_OFFSET + 0x10000000)
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..cc7f25e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	__u32			xres_virtual;
+	__u32			yres_virtual;
+	__u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..085a9c6
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,168 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);
+	if (edge)
+		writel(readl(base
+			+ VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
+			| (1 << (irq & 0x1f)), base
+			+ VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));
+	else
+		writeb(readb(base
+			+ VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
+			base + VT8500_IC_DCTR + irq);
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	writeb(readb(base
+		+ VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
+		base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
+{
+	return -EINVAL;
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_HIGH, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_level_irq);
+		break;
+	case IRQF_TRIGGER_FALLING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_FALLING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	case IRQF_TRIGGER_RISING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_RISING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	}
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_wake  = vt8500_irq_set_wake,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+
+	/* Enable rotating priority for IRQ */
+	writel((1 << 6), ic_regbase + 0x20);
+	writel(0, ic_regbase + 0x24);
+
+	for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+		/* Disable all interrupts and route them to IRQ */
+		writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+		set_irq_chip(i, &vt8500_irq_chip);
+		set_irq_handler(i, handle_level_irq);
+		set_irq_flags(i, IRQF_VALID);
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+	sic_regbase = ioremap(wmt_current_regs->ic1, SZ_64K);
+
+	/* Enable rotating priority for IRQ */
+	writel((1 << 6), ic_regbase + 0x20);
+	writel(0, ic_regbase + 0x24);
+	writel((1 << 6), sic_regbase + 0x20);
+	writel(0, sic_regbase + 0x24);
+
+	for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+		/* Disable all interrupts and route them to IRQ */
+		if (i < 64)
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+		else
+			writeb(0x00, sic_regbase + VT8500_IC_DCTR + i - 64);
+
+		set_irq_chip(i, &vt8500_irq_chip);
+		set_irq_handler(i, handle_level_irq);
+		set_irq_flags(i, IRQF_VALID);
+	}
+}
diff --git a/arch/arm/mach-vt8500/irq_defs.c b/arch/arm/mach-vt8500/irq_defs.c
new file mode 100644
index 0000000..b338c04
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq_defs.c
@@ -0,0 +1,173 @@
+/* linux/arch/arm/mach-vt8500/irq_defs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/irq_defs.h>
+#include <mach/mmio_regs.h>
+
+struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+struct wmt_irq_srcs wmt_irqs[] __initdata = {
+	[VT8500_INDEX] = {
+		.jpegenc	= 0,
+		.jpegdec	= 1,
+		.pata		= 3,
+		.dma		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.ge		= 8,
+		.gov		= 9,
+		.ether		= 10,
+		.mpegts		= 11,
+		.lcdc		= 12,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.cipher		= 16,
+		.vpp		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.lcdf		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.memstick	= 30,
+		.memstick_dma	= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.i2s		= 34,
+		.pcm		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.vpu		= 40,
+		.vid		= 41,
+		.ac97		= 42,
+		.ehci		= 43,
+		.nor		= 44,
+		.ps2mouse	= 45,
+		.ps2kbd		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.adc		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.dma0		= 56,
+		.dma1		= 57,
+		.dma2		= 58,
+		.dma3		= 59,
+		.dma4		= 60,
+		.dma5		= 61,
+		.dma6		= 62,
+		.dma7		= 63,
+		.nr_irqs	= 64,
+	},
+	[WM8505_INDEX] = {
+		.uhci		= 0,
+		.ehci		= 1,
+		.udc_dma	= 2,
+		.ps2mouse	= 4,
+		.udc		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.keypad		= 8,
+		.dma		= 9,
+		.ether		= 10,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.dma0		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.ps2kbd		= 23,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.dma1		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.uart5		= 30,
+		.uart4		= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.dma2		= 34,
+		.i2s		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.dma3		= 40,
+		.dma4		= 41,
+		.ac97		= 42,
+		.nor		= 44,
+		.dma5		= 45,
+		.dma6		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.dma7		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.irq0		= 56,
+		.irq1		= 57,
+		.irq2		= 58,
+		.irq3		= 59,
+		.irq4		= 60,
+		.irq5		= 61,
+		.irq6		= 62,
+		.irq7		= 63,
+		.jpegdec	= 65,
+		.sae		= 66,
+		.vpu		= 79,
+		.vpp		= 80,
+		.vid		= 81,
+		.spu		= 82,
+		.pip		= 83,
+		.ge		= 84,
+		.gov		= 85,
+		.dvo		= 86,
+		.dma8		= 92,
+		.dma9		= 93,
+		.dma10		= 94,
+		.dma11		= 95,
+		.dma12		= 96,
+		.dma13		= 97,
+		.dma14		= 98,
+		.dma15		= 99,
+		.govw		= 111,
+		.govrsdscd	= 112,
+		.govrsdmif	= 113,
+		.govrhdscd	= 114,
+		.govrhdmif	= 115,
+		.nr_irqs	= 116,
+	},
+};
diff --git a/arch/arm/mach-vt8500/mmio_regs.c b/arch/arm/mach-vt8500/mmio_regs.c
new file mode 100644
index 0000000..e9b3264
--- /dev/null
+++ b/arch/arm/mach-vt8500/mmio_regs.c
@@ -0,0 +1,118 @@
+/* linux/arch/arm/mach-vt8500/mmio_regs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/mmio_regs.h>
+
+struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+struct wmt_mmio_regs wmt_regmaps[] __initdata = {
+	[VT8500_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00350000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000000,
+		.dma			= 0xd8001000,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007900,
+		.uhci			= 0xd8007b01,
+		.pata			= 0xd8008000,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.memstick		= 0xd800b400,
+		.lcdc			= 0xd800e400,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.lcdf			= 0xd8050900,
+		.vid			= 0xd8050a00,
+		.vpp			= 0xd8050b00,
+		.tsbk			= 0xd80f4000,
+		.jpegdec		= 0xd80fe000,
+		.jpegenc		= 0xd80ff000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.pcm			= 0xd82d0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.adc			= 0xd8340000,
+	},
+	[WM8505_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00390000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000400,
+		.dma			= 0xd8001800,
+		.vdma			= 0xd8001c00,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007100,
+		.uhci			= 0xd8007301,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.govr			= 0xd8050800,
+		.vid			= 0xd8050a00,
+		.scl			= 0xd8050d00,
+		.vpp			= 0xd8050f00,
+		.jpegdec		= 0xd80fe000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.ic1			= 0xd8150000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.keypad			= 0xd8260000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.uart4			= 0xd8370000,
+		.uart5			= 0xd8380000,
+	},
+};
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..47c55e6
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,254 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+/*
+ * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
+ * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ */
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	dc = pv * duty_ns / period_ns;
+
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 1))
+		/* busy-wait */;
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 2))
+		/* busy-wait */;
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 3))
+		/* busy-wait */;
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 0))
+		/* busy-wait */;
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 0))
+		/* busy-wait */;
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else
+			pwm = ERR_PTR(-EBUSY);
+	} else
+		pwm = ERR_PTR(-ENOENT);
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else
+		pr_warning("PWM device already freed\n");
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[1].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..ab7dee4
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,166 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+static void __iomem *regbase;
+
+/*
+ * Kernel assumes that sched_clock can be called early but may not have
+ * things ready yet.
+ */
+static cycle_t vt8500_timer_read_dummy(struct clocksource *cs)
+{
+	return 0;
+}
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while (readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+		/* wait and poll */;
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read_dummy,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+unsigned long long sched_clock(void)
+{
+	return clocksource_cyc2ns(clocksource.read(&clocksource),
+				clocksource.mult, clocksource.shift);
+}
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while (readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+		/* wait and poll */;
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_current_regs->pmc + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO "
+				"registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	clocksource.read = vt8500_timer_read;
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register "
+			"failed for %s\n", clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_current_irqs->timer_match0, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq "
+			"failed for %s\n", clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..c8e65e7
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,69 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <mach/mmio_regs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+
+	writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+	iounmap(gpio_mux_reg);
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+	pm_power_off = &vt8500_power_off;
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= wm8505_map_io,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.2


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

* [PATCH 2/6 v2] serial: Add support for UART on VIA VT8500 and compatibles
  2010-11-07 16:28 [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Alexey Charkov
@ 2010-11-07 16:28 ` Alexey Charkov
  2010-11-07 23:08   ` Alan Cox
  2010-11-07 16:28 ` [PATCH 3/6 v2] input: Add support for VIA VT8500 and compatibles in i8042 Alexey Charkov
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-07 16:28 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, Greg Kroah-Hartman,
	Ben Dooks, Alan Cox, Kukjin Kim, Feng Tang, Tobias Klauser,
	linux-kernel

This adds a driver for the serial ports found in VIA and WonderMedia
Systems-on-Chip. Interrupt-driven FIFO operation is implemented.
The hardware also supports pure register-based operation (which is
slower) and DMA-based FIFO operation. As the FIFOs are only 16 bytes
long, DMA operation is probably not worth the hassle.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Please review and (if appropriate) commit to a relevant git tree for
further integration in 2.6.38.

Compared to the previous submission, this code just contains a rebase
against the latest changes introduced in 2.6.37 merge window.

Relevant platform definitions are introduced by PATCH 1/6 in this series,
so one would need that to make use of this code.

Due credits go to the community for providing feedback, advice and
testing.

 drivers/serial/Kconfig         |   10 +
 drivers/serial/Makefile        |    1 +
 drivers/serial/vt8500_serial.c |  637 ++++++++++++++++++++++++++++++++++++++++
 include/linux/serial_core.h    |    3 +
 4 files changed, 651 insertions(+), 0 deletions(-)
 create mode 100644 drivers/serial/vt8500_serial.c

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index aff9dcd..6e76a59 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1381,6 +1381,16 @@ config SERIAL_MSM_CONSOLE
 	depends on SERIAL_MSM=y
 	select SERIAL_CORE_CONSOLE
 
+config SERIAL_VT8500
+	bool "VIA VT8500 on-chip serial port support"
+	depends on ARM && ARCH_VT8500
+	select SERIAL_CORE
+
+config SERIAL_VT8500_CONSOLE
+	bool "VIA VT8500 serial console support"
+	depends on SERIAL_VT8500=y
+	select SERIAL_CORE_CONSOLE
+
 config SERIAL_NETX
 	tristate "NetX serial port support"
 	depends on ARM && ARCH_NETX
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index c570576..86b8587 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_SERIAL_TIMBERDALE)	+= timbuart.o
 obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
 obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
 obj-$(CONFIG_SERIAL_MRST_MAX3110)	+= mrst_max3110.o
 obj-$(CONFIG_SERIAL_MFD_HSU)	+= mfd.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
diff --git a/drivers/serial/vt8500_serial.c b/drivers/serial/vt8500_serial.c
new file mode 100644
index 0000000..329aff7
--- /dev/null
+++ b/drivers/serial/vt8500_serial.c
@@ -0,0 +1,637 @@
+/*
+ * drivers/serial/vt8500_serial.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on msm_serial.c, which is:
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Robert Love <rlove@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+# define SUPPORT_SYSRQ
+#endif
+
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/*
+ * UART Register offsets
+ */
+
+#define VT8500_URTDR		0x0000	/* Transmit data */
+#define VT8500_URRDR		0x0004	/* Receive data */
+#define VT8500_URDIV		0x0008	/* Clock/Baud rate divisor */
+#define VT8500_URLCR		0x000C	/* Line control */
+#define VT8500_URICR		0x0010	/* IrDA control */
+#define VT8500_URIER		0x0014	/* Interrupt enable */
+#define VT8500_URISR		0x0018	/* Interrupt status */
+#define VT8500_URUSR		0x001c	/* UART status */
+#define VT8500_URFCR		0x0020	/* FIFO control */
+#define VT8500_URFIDX		0x0024	/* FIFO index */
+#define VT8500_URBKR		0x0028	/* Break signal count */
+#define VT8500_URTOD		0x002c	/* Time out divisor */
+#define VT8500_TXFIFO		0x1000	/* Transmit FIFO (16x8) */
+#define VT8500_RXFIFO		0x1020	/* Receive FIFO (16x10) */
+
+/*
+ * Interrupt enable and status bits
+ */
+
+#define TXDE	(1 << 0)	/* Tx Data empty */
+#define RXDF	(1 << 1)	/* Rx Data full */
+#define TXFAE	(1 << 2)	/* Tx FIFO almost empty */
+#define TXFE	(1 << 3)	/* Tx FIFO empty */
+#define RXFAF	(1 << 4)	/* Rx FIFO almost full */
+#define RXFF	(1 << 5)	/* Rx FIFO full */
+#define TXUDR	(1 << 6)	/* Tx underrun */
+#define RXOVER	(1 << 7)	/* Rx overrun */
+#define PER	(1 << 8)	/* Parity error */
+#define FER	(1 << 9)	/* Frame error */
+#define TCTS	(1 << 10)	/* Toggle of CTS */
+#define RXTOUT	(1 << 11)	/* Rx timeout */
+#define BKDONE	(1 << 12)	/* Break signal done */
+#define ERR	(1 << 13)	/* AHB error response */
+
+#define RX_FIFO_INTS	(RXFAF | RXFF | RXOVER | PER | FER | RXTOUT)
+#define TX_FIFO_INTS	(TXFAE | TXFE | TXUDR)
+
+struct vt8500_port {
+	struct uart_port	uart;
+	char			name[16];
+	struct clk		*clk;
+	unsigned int		ier;
+};
+
+static inline void vt8500_write(struct uart_port *port, unsigned int val,
+			     unsigned int off)
+{
+	writel(val, port->membase + off);
+}
+
+static inline unsigned int vt8500_read(struct uart_port *port, unsigned int off)
+{
+	return readl(port->membase + off);
+}
+
+static void vt8500_stop_tx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void vt8500_stop_rx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~RX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void vt8500_enable_ms(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier |= TCTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void handle_rx(struct uart_port *port)
+{
+	struct tty_struct *tty = port->state->port.tty;
+
+	/*
+	 * Handle overrun
+	 */
+	if ((vt8500_read(port, VT8500_URISR) & RXOVER)) {
+		port->icount.overrun++;
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+	}
+
+	/* and now the main RX loop */
+	while (vt8500_read(port, VT8500_URFIDX) & 0x1f00) {
+		unsigned int c;
+		char flag = TTY_NORMAL;
+
+		c = readw(port->membase + VT8500_RXFIFO) & 0x3ff;
+
+		/* Mask conditions we're ignorning. */
+		c &= ~port->read_status_mask;
+
+		if (c & FER) {
+			port->icount.frame++;
+			flag = TTY_FRAME;
+		} else if (c & PER) {
+			port->icount.parity++;
+			flag = TTY_PARITY;
+		}
+		port->icount.rx++;
+
+		if (!uart_handle_sysrq_char(port, c))
+			tty_insert_flip_char(tty, c, flag);
+	}
+
+	tty_flip_buffer_push(tty);
+}
+
+static void handle_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		writeb(port->x_char, port->membase + VT8500_TXFIFO);
+		port->icount.tx++;
+		port->x_char = 0;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		vt8500_stop_tx(port);
+		return;
+	}
+
+	while ((vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16) {
+		if (uart_circ_empty(xmit))
+			break;
+
+		writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		vt8500_stop_tx(port);
+}
+
+static void vt8500_start_tx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+	handle_tx(port);
+	vt8500_port->ier |= TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void handle_delta_cts(struct uart_port *port)
+{
+	port->icount.cts++;
+	wake_up_interruptible(&port->state->port.delta_msr_wait);
+}
+
+static irqreturn_t vt8500_irq(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned long isr;
+
+	spin_lock(&port->lock);
+	isr = vt8500_read(port, VT8500_URISR);
+
+	/* Acknowledge active status bits */
+	vt8500_write(port, isr, VT8500_URISR);
+
+	if (isr & RX_FIFO_INTS)
+		handle_rx(port);
+	if (isr & TX_FIFO_INTS)
+		handle_tx(port);
+	if (isr & TCTS)
+		handle_delta_cts(port);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int vt8500_tx_empty(struct uart_port *port)
+{
+	return (vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16 ?
+						TIOCSER_TEMT : 0;
+}
+
+static unsigned int vt8500_get_mctrl(struct uart_port *port)
+{
+	unsigned int usr;
+
+	usr = vt8500_read(port, VT8500_URUSR);
+	if (usr & (1 << 4))
+		return TIOCM_CTS;
+	else
+		return 0;
+}
+
+static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void vt8500_break_ctl(struct uart_port *port, int break_ctl)
+{
+	if (break_ctl)
+		vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9),
+			     VT8500_URLCR);
+}
+
+static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud)
+{
+	unsigned long div;
+
+	div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff);
+
+	if (unlikely((baud < 900) || (baud > 921600)))
+		div |= 7;
+	else
+		div |= (921600 / baud) - 1;
+
+	while (vt8500_read(port, VT8500_URUSR) & (1 << 5))
+		;
+	vt8500_write(port, div, VT8500_URDIV);
+
+	return baud;
+}
+
+static int vt8500_startup(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	int ret;
+
+	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
+		 "vt8500_serial%d", port->line);
+
+	ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH,
+			  vt8500_port->name, port);
+	if (unlikely(ret))
+		return ret;
+
+	vt8500_write(port, 0x03, VT8500_URLCR);	/* enable TX & RX */
+
+	return 0;
+}
+
+static void vt8500_shutdown(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+
+	vt8500_port->ier = 0;
+
+	/* disable interrupts and FIFOs */
+	vt8500_write(&vt8500_port->uart, 0, VT8500_URIER);
+	vt8500_write(&vt8500_port->uart, 0x880, VT8500_URFCR);
+	free_irq(port->irq, port);
+}
+
+static void vt8500_set_termios(struct uart_port *port,
+			       struct ktermios *termios,
+			       struct ktermios *old)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	unsigned long flags;
+	unsigned int baud, lcr;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* calculate and set baud rate */
+	baud = uart_get_baud_rate(port, termios, old, 900, 921600);
+	baud = vt8500_set_baud_rate(port, baud);
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+
+	/* calculate parity */
+	lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR);
+	lcr &= ~((1 << 5) | (1 << 4));
+	if (termios->c_cflag & PARENB) {
+		lcr |= (1 << 4);
+		if (termios->c_cflag & PARODD)
+			lcr |= (1 << 5);
+	}
+
+	/* calculate bits per char */
+	lcr &= ~(1 << 2);
+	switch (termios->c_cflag & CSIZE) {
+	case CS7:
+		break;
+	case CS8:
+	default:
+		lcr |= (1 << 2);
+		break;
+	}
+
+	/* calculate stop bits */
+	lcr &= ~(1 << 3);
+	if (termios->c_cflag & CSTOPB)
+		lcr |= (1 << 3);
+
+	/* set parity, bits per char, and stop bit */
+	vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR);
+
+	/* Configure status bits to ignore based on termio flags. */
+	port->read_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->read_status_mask = FER | PER;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/* Reset FIFOs */
+	vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR);
+	while (vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc)
+		/* Wait for the reset to complete */;
+
+	/* Every possible FIFO-related interrupt */
+	vt8500_port->ier = RX_FIFO_INTS | TX_FIFO_INTS;
+
+	/*
+	 * CTS flow control
+	 */
+	if (UART_ENABLE_MS(&vt8500_port->uart, termios->c_cflag))
+		vt8500_port->ier |= TCTS;
+
+	vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR);
+	vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *vt8500_type(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	return vt8500_port->name;
+}
+
+static void vt8500_release_port(struct uart_port *port)
+{
+}
+
+static int vt8500_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void vt8500_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_VT8500;
+}
+
+static int vt8500_verify_port(struct uart_port *port,
+			      struct serial_struct *ser)
+{
+	if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500))
+		return -EINVAL;
+	if (unlikely(port->irq != ser->irq))
+		return -EINVAL;
+	return 0;
+}
+
+static struct vt8500_port *vt8500_uart_ports[4];
+static struct uart_driver vt8500_uart_driver;
+
+#ifdef CONFIG_SERIAL_VT8500_CONSOLE
+
+static inline void wait_for_xmitr(struct uart_port *port)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = vt8500_read(port, VT8500_URFIDX);
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while (status & 0x10);
+}
+
+static void vt8500_console_putchar(struct uart_port *port, int c)
+{
+	wait_for_xmitr(port);
+	writeb(c, port->membase + VT8500_TXFIFO);
+}
+
+static void vt8500_console_write(struct console *co, const char *s,
+			      unsigned int count)
+{
+	struct vt8500_port *vt8500_port = vt8500_uart_ports[co->index];
+	unsigned long ier;
+
+	BUG_ON(co->index < 0 || co->index >= vt8500_uart_driver.nr);
+
+	ier = vt8500_read(&vt8500_port->uart, VT8500_URIER);
+	vt8500_write(&vt8500_port->uart, VT8500_URIER, 0);
+
+	uart_console_write(&vt8500_port->uart, s, count,
+			   vt8500_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and switch back to FIFO
+	 */
+	wait_for_xmitr(&vt8500_port->uart);
+	vt8500_write(&vt8500_port->uart, VT8500_URIER, ier);
+}
+
+static int __init vt8500_console_setup(struct console *co, char *options)
+{
+	struct vt8500_port *vt8500_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (unlikely(co->index >= vt8500_uart_driver.nr || co->index < 0))
+		return -ENXIO;
+
+	vt8500_port = vt8500_uart_ports[co->index];
+
+	if (!vt8500_port)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&vt8500_port->uart,
+				 co, baud, parity, bits, flow);
+}
+
+static struct console vt8500_console = {
+	.name = "ttyS",
+	.write = vt8500_console_write,
+	.device = uart_console_device,
+	.setup = vt8500_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &vt8500_uart_driver,
+};
+
+#define VT8500_CONSOLE	(&vt8500_console)
+
+#else
+#define VT8500_CONSOLE	NULL
+#endif
+
+static struct uart_ops vt8500_uart_pops = {
+	.tx_empty	= vt8500_tx_empty,
+	.set_mctrl	= vt8500_set_mctrl,
+	.get_mctrl	= vt8500_get_mctrl,
+	.stop_tx	= vt8500_stop_tx,
+	.start_tx	= vt8500_start_tx,
+	.stop_rx	= vt8500_stop_rx,
+	.enable_ms	= vt8500_enable_ms,
+	.break_ctl	= vt8500_break_ctl,
+	.startup	= vt8500_startup,
+	.shutdown	= vt8500_shutdown,
+	.set_termios	= vt8500_set_termios,
+	.type		= vt8500_type,
+	.release_port	= vt8500_release_port,
+	.request_port	= vt8500_request_port,
+	.config_port	= vt8500_config_port,
+	.verify_port	= vt8500_verify_port,
+};
+
+static struct uart_driver vt8500_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "vt8500_serial",
+	.dev_name	= "ttyS",
+	.major		= 4,
+	.minor		= 64,
+	.nr		= 4,
+	.cons		= VT8500_CONSOLE,
+};
+
+static int __init vt8500_serial_probe(struct platform_device *pdev)
+{
+	struct vt8500_port *vt8500_port;
+	struct resource *mmres, *irqres;
+	int ret;
+
+	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!mmres || !irqres)
+		return -ENODEV;
+
+	vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
+	if (!vt8500_port)
+		return -ENOMEM;
+
+	vt8500_port->uart.type = PORT_VT8500;
+	vt8500_port->uart.iotype = UPIO_MEM;
+	vt8500_port->uart.mapbase = mmres->start;
+	vt8500_port->uart.irq = irqres->start;
+	vt8500_port->uart.fifosize = 16;
+	vt8500_port->uart.ops = &vt8500_uart_pops;
+	vt8500_port->uart.line = pdev->id;
+	vt8500_port->uart.dev = &pdev->dev;
+	vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+	vt8500_port->uart.uartclk = 24000000;
+
+	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
+		 "VT8500 UART%d", pdev->id);
+
+	vt8500_port->uart.membase = ioremap(mmres->start,
+					    mmres->end - mmres->start + 1);
+	if (!vt8500_port->uart.membase) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	vt8500_uart_ports[pdev->id] = vt8500_port;
+
+	uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
+
+	platform_set_drvdata(pdev, vt8500_port);
+
+	return 0;
+
+err:
+	kfree(vt8500_port);
+	return ret;
+}
+
+static int __devexit vt8500_serial_remove(struct platform_device *pdev)
+{
+	struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
+	kfree(vt8500_port);
+
+	return 0;
+}
+
+static struct platform_driver vt8500_platform_driver = {
+	.probe  = vt8500_serial_probe,
+	.remove = vt8500_serial_remove,
+	.driver = {
+		.name = "vt8500_serial",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init vt8500_serial_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&vt8500_uart_driver);
+	if (unlikely(ret))
+		return ret;
+
+	ret = platform_driver_register(&vt8500_platform_driver);
+
+	if (unlikely(ret))
+		uart_unregister_driver(&vt8500_uart_driver);
+
+	printk(KERN_INFO "vt8500_serial: driver initialized\n");
+
+	return ret;
+}
+
+static void __exit vt8500_serial_exit(void)
+{
+#ifdef CONFIG_SERIAL_VT8500_CONSOLE
+	unregister_console(&vt8500_console);
+#endif
+	platform_driver_unregister(&vt8500_platform_driver);
+	uart_unregister_driver(&vt8500_uart_driver);
+}
+
+module_init(vt8500_serial_init);
+module_exit(vt8500_serial_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("Driver for vt8500 serial device");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 212eb4c..41603d6 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -199,6 +199,9 @@
 /* TI OMAP-UART */
 #define PORT_OMAP	96
 
+/* VIA VT8500 SoC */
+#define PORT_VT8500	97
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>
-- 
1.7.3.2


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

* [PATCH 3/6 v2] input: Add support for VIA VT8500 and compatibles in i8042
  2010-11-07 16:28 [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Alexey Charkov
  2010-11-07 16:28 ` [PATCH 2/6 v2] serial: Add support for UART on VIA VT8500 and compatibles Alexey Charkov
@ 2010-11-07 16:28 ` Alexey Charkov
  2010-11-12 22:54   ` Alexey Charkov
  2010-11-07 16:28 ` [PATCH 4/6 v2] usb: Add support for VIA VT8500 and compatibles in EHCI HCD Alexey Charkov
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-07 16:28 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, Dmitry Torokhov,
	Tony Lindgren, Feng Tang, Janusz Krzysztofik,
	Dmitry Eremin-Solenikov, linux-input, linux-kernel

VIA and WonderMedia Systems-on-Chip feature a standard i8042-compatible
keyboard and mouse controller. This adds necessary glue to enable use
of the standard driver with these systems.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Please review and (if appropriate) commit to a relevant git tree for
further integration in 2.6.38.

Previous version of this code was 'Acked-by: Dmitry Torokhov <dtor@mail.ru>'
This one only differs by using runtime-selected IRQ definitions instead
of static compile-time preprocessor macros.

Relevant register and interrupt definitions are provided by PATCH 1/6 in
this series, so one would need that to make use of this code.

 drivers/input/serio/Kconfig        |    3 +-
 drivers/input/serio/i8042-vt8500.h |   74 ++++++++++++++++++++++++++++++++++++
 drivers/input/serio/i8042.h        |    2 +
 3 files changed, 78 insertions(+), 1 deletions(-)
 create mode 100644 drivers/input/serio/i8042-vt8500.h

diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 6256233..ff799f3 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -21,7 +21,8 @@ if SERIO
 config SERIO_I8042
 	tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
 	default y
-	depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
+	depends on !PARISC && \
+		  (!ARM || ARCH_SHARK || ARCH_VT8500 || FOOTBRIDGE_HOST) && \
 		   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
 	help
 	  i8042 is the chip over which the standard AT keyboard and PS/2
diff --git a/drivers/input/serio/i8042-vt8500.h b/drivers/input/serio/i8042-vt8500.h
new file mode 100644
index 0000000..4ff9e1c
--- /dev/null
+++ b/drivers/input/serio/i8042-vt8500.h
@@ -0,0 +1,74 @@
+#ifndef _I8042_VT8500_H
+#define _I8042_VT8500_H
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+static void __iomem *regbase;
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "vt8500ps2/serio0"
+#define I8042_AUX_PHYS_DESC "vt8500ps2/serio1"
+#define I8042_MUX_PHYS_DESC "vt8500ps2/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#define I8042_KBD_IRQ	(wmt_current_irqs->ps2kbd)
+#define I8042_AUX_IRQ	(wmt_current_irqs->ps2mouse)
+
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG	(regbase + 0x4)
+#define I8042_STATUS_REG	(regbase + 0x4)
+#define I8042_DATA_REG		(regbase + 0x0)
+
+static inline int i8042_read_data(void)
+{
+	return readl(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+	return readl(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+	writel(val, I8042_DATA_REG);
+}
+
+static inline void i8042_write_command(int val)
+{
+	writel(val, I8042_COMMAND_REG);
+}
+
+static inline int i8042_platform_init(void)
+{
+	i8042_reset = true;
+	regbase = ioremap(wmt_current_regs->ps2, SZ_1K);
+	if (!regbase)
+		return -ENODEV;
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+	iounmap(regbase);
+}
+
+#endif /* _I8042_VT8500_H */
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
index cbc1beb..bdb2aeb 100644
--- a/drivers/input/serio/i8042.h
+++ b/drivers/input/serio/i8042.h
@@ -16,6 +16,8 @@
 
 #if defined(CONFIG_MACH_JAZZ)
 #include "i8042-jazzio.h"
+#elif defined(CONFIG_ARCH_VT8500)
+#include "i8042-vt8500.h"
 #elif defined(CONFIG_SGI_HAS_I8042)
 #include "i8042-ip22io.h"
 #elif defined(CONFIG_SNI_RM)
-- 
1.7.3.2


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

* [PATCH 4/6 v2] usb: Add support for VIA VT8500 and compatibles in EHCI HCD
  2010-11-07 16:28 [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Alexey Charkov
  2010-11-07 16:28 ` [PATCH 2/6 v2] serial: Add support for UART on VIA VT8500 and compatibles Alexey Charkov
  2010-11-07 16:28 ` [PATCH 3/6 v2] input: Add support for VIA VT8500 and compatibles in i8042 Alexey Charkov
@ 2010-11-07 16:28 ` Alexey Charkov
  2010-11-07 16:28 ` [PATCH 5/6 v2] rtc: Add support for the RTC in VIA VT8500 and compatibles Alexey Charkov
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-07 16:28 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, Greg Kroah-Hartman,
	David Brownell, linux-usb, linux-kernel

VIA and WonderMedia Systems-on-Chip feature a standard EHCI host
controller. This adds necessary glue to use the standard driver
with these systems.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Please review and (if appropriate) commit to a relevant git tree for
further integration in 2.6.38.

Compared to the previous submission, this code just contains a rebase
against the latest changes introduced in 2.6.37 merge window.

Relevant platform definitions are introduced by PATCH 1/6 in this series,
so one would need that to make use of this code.

Due credits go to the community for providing feedback, advice and
testing.

 drivers/usb/Kconfig            |    1 +
 drivers/usb/host/ehci-hcd.c    |    5 +
 drivers/usb/host/ehci-vt8500.c |  172 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 178 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/host/ehci-vt8500.c

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 67eb377..7d13506 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -66,6 +66,7 @@ config USB_ARCH_HAS_EHCI
 	default y if ARCH_AT91SAM9G45
 	default y if ARCH_MXC
 	default y if ARCH_OMAP3
+	default y if ARCH_VT8500
 	default PCI
 
 # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 502a7e6..10f985d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1216,6 +1216,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER		ehci_octeon_driver
 #endif
 
+#ifdef CONFIG_ARCH_VT8500
+#include "ehci-vt8500.c"
+#define	PLATFORM_DRIVER		vt8500_ehci_driver
+#endif
+
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c
new file mode 100644
index 0000000..2016806
--- /dev/null
+++ b/drivers/usb/host/ehci-vt8500.c
@@ -0,0 +1,172 @@
+/*
+ * drivers/usb/host/ehci-vt8500.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on ehci-au1xxx.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int rc = 0;
+
+	if (!udev->parent) /* udev is root hub itself, impossible */
+		rc = -1;
+	/* we only support lpm device connected to root hub yet */
+	if (ehci->has_lpm && !udev->parent->parent) {
+		rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum);
+		if (!rc)
+			rc = ehci_lpm_check(ehci, udev->portnum);
+	}
+	return rc;
+}
+
+static const struct hc_driver vt8500_ehci_hc_driver = {
+	.description		= hcd_name,
+	.product_desc		= "VT8500 EHCI",
+	.hcd_priv_size		= sizeof(struct ehci_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq			= ehci_irq,
+	.flags			= HCD_MEMORY | HCD_USB2,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	.reset			= ehci_init,
+	.start			= ehci_run,
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	/*
+	 * call back when device connected and addressed
+	 */
+	.update_device =	ehci_update_device,
+
+	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete,
+};
+
+static int vt8500_ehci_drv_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct ehci_hcd *ehci;
+	struct resource *res;
+	int ret;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	if (pdev->resource[1].flags != IORESOURCE_IRQ) {
+		pr_debug("resource[1] is not IORESOURCE_IRQ");
+		return -ENOMEM;
+	}
+	hcd = usb_create_hcd(&vt8500_ehci_hc_driver, &pdev->dev, "VT8500");
+	if (!hcd)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+
+	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+		pr_debug("request_mem_region failed");
+		ret = -EBUSY;
+		goto err1;
+	}
+
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		pr_debug("ioremap failed");
+		ret = -ENOMEM;
+		goto err2;
+	}
+
+	ehci = hcd_to_ehci(hcd);
+	ehci->caps = hcd->regs;
+	ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
+
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* cache this readonly data; minimize chip reads */
+	ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+	ehci_port_power(ehci, 1);
+
+	ret = usb_add_hcd(hcd, pdev->resource[1].start,
+			  IRQF_DISABLED | IRQF_SHARED);
+	if (ret == 0) {
+		platform_set_drvdata(pdev, hcd);
+		return ret;
+	}
+
+	iounmap(hcd->regs);
+err2:
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+	usb_put_hcd(hcd);
+	return ret;
+}
+
+static int vt8500_ehci_drv_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+	usb_remove_hcd(hcd);
+	iounmap(hcd->regs);
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver vt8500_ehci_driver = {
+	.probe		= vt8500_ehci_drv_probe,
+	.remove		= vt8500_ehci_drv_remove,
+	.shutdown	= usb_hcd_platform_shutdown,
+	.driver = {
+		.name	= "vt8500-ehci",
+		.owner	= THIS_MODULE,
+	}
+};
+
+MODULE_ALIAS("platform:vt8500-ehci");
-- 
1.7.3.2


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

* [PATCH 5/6 v2] rtc: Add support for the RTC in VIA VT8500 and compatibles
  2010-11-07 16:28 [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Alexey Charkov
                   ` (2 preceding siblings ...)
  2010-11-07 16:28 ` [PATCH 4/6 v2] usb: Add support for VIA VT8500 and compatibles in EHCI HCD Alexey Charkov
@ 2010-11-07 16:28 ` Alexey Charkov
  2010-11-12 22:53   ` Alexey Charkov
  2010-11-13 12:14   ` Lars-Peter Clausen
  2010-11-07 16:28 ` [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505 Alexey Charkov
                   ` (2 subsequent siblings)
  6 siblings, 2 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-07 16:28 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, Alessandro Zummo,
	rtc-linux, linux-kernel

This adds a driver for the RTC devices in VIA and WonderMedia
Systems-on-Chip. Alarm, 1Hz interrupts, reading and setting time
are supported.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Please review and (if appropriate) commit to a relevant git tree for
further integration in 2.6.38.

Compared to the previous submission, this code just contains a rebase
against the latest changes introduced in 2.6.37 merge window.

Relevant platform definitions are introduced by PATCH 1/6 in this series,
so one would need that to make use of this code.

Due credits go to the community for providing feedback, advice and
testing.

 drivers/rtc/Kconfig      |    7 +
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-vt8500.c |  363 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 371 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-vt8500.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2883428..27ed129 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -865,6 +865,13 @@ config RTC_DRV_PXA
          This RTC driver uses PXA RTC registers available since pxa27x
          series (RDxR, RYxR) instead of legacy RCNR, RTAR.
 
+config RTC_DRV_VT8500
+	tristate "VIA/WonderMedia 85xx SoC RTC"
+	depends on ARCH_VT8500
+	help
+	  If you say Y here you will get access to the real time clock
+	  built into your VIA VT8500 SoC or its relatives.
+
 
 config RTC_DRV_SUN4V
 	bool "SUN4V Hypervisor RTC"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 4c2832d..1a354e1 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030)	+= rtc-twl.o
 obj-$(CONFIG_RTC_DRV_TX4939)	+= rtc-tx4939.o
 obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o
 obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o
+obj-$(CONFIG_RTC_DRV_VT8500)	+= rtc-vt8500.o
 obj-$(CONFIG_RTC_DRV_WM831X)	+= rtc-wm831x.o
 obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
new file mode 100644
index 0000000..7260c95
--- /dev/null
+++ b/drivers/rtc/rtc-vt8500.c
@@ -0,0 +1,363 @@
+/*
+ * drivers/rtc/rtc-vt8500.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on rtc-pxa.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/*
+ * Register definitions
+ */
+#define VT8500_RTC_TS		0x00	/* Time set */
+#define VT8500_RTC_DS		0x04	/* Date set */
+#define VT8500_RTC_AS		0x08	/* Alarm set */
+#define VT8500_RTC_CR		0x0c	/* Control */
+#define VT8500_RTC_TR		0x10	/* Time read */
+#define VT8500_RTC_DR		0x14	/* Date read */
+#define VT8500_RTC_WS		0x18	/* Write status */
+#define VT8500_RTC_CL		0x20	/* Calibration */
+#define VT8500_RTC_IS		0x24	/* Interrupt status */
+#define VT8500_RTC_ST		0x28	/* Status */
+
+#define INVALID_TIME_BIT	(1 << 31)
+
+#define DATE_CENTURY_S		19
+#define DATE_YEAR_S		11
+#define DATE_YEAR_MASK		(0xff << DATE_YEAR_S)
+#define DATE_MONTH_S		6
+#define DATE_MONTH_MASK		(0x1f << DATE_MONTH_S)
+#define DATE_DAY_MASK		0x3f
+
+#define TIME_DOW_S		20
+#define TIME_DOW_MASK		(0x07 << TIME_DOW_S)
+#define TIME_HOUR_S		14
+#define TIME_HOUR_MASK		(0x3f << TIME_HOUR_S)
+#define TIME_MIN_S		7
+#define TIME_MIN_MASK		(0x7f << TIME_MIN_S)
+#define TIME_SEC_MASK		0x7f
+
+#define ALARM_DAY_S		20
+#define ALARM_DAY_MASK		(0x3f << ALARM_DAY_S)
+
+#define ALARM_DAY_BIT		(1 << 29)
+#define ALARM_HOUR_BIT		(1 << 28)
+#define ALARM_MIN_BIT		(1 << 27)
+#define ALARM_SEC_BIT		(1 << 26)
+
+#define ALARM_ENABLE_MASK	(ALARM_DAY_BIT \
+				| ALARM_HOUR_BIT \
+				| ALARM_MIN_BIT \
+				| ALARM_SEC_BIT)
+
+struct vt8500_rtc {
+	void __iomem		*regbase;
+	int			irq_alarm;
+	int			irq_hz;
+	struct rtc_device	*rtc;
+	spinlock_t		lock;		/* Protects this structure */
+	struct rtc_time		rtc_alarm;
+};
+
+static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
+{
+	struct platform_device *pdev = to_platform_device(dev_id);
+	struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
+	u32 isr;
+	unsigned long events = 0;
+
+	spin_lock(&vt8500_rtc->lock);
+
+	/* clear interrupt sources */
+	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+	writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
+
+	if (isr & 1)
+		events |= RTC_AF | RTC_IRQF;
+
+	/* Only second/minute interrupts are supported */
+	if (isr & 2)
+		events |= RTC_UF | RTC_IRQF;
+
+	rtc_update_irq(vt8500_rtc->rtc, 1, events);
+
+	spin_unlock(&vt8500_rtc->lock);
+	return IRQ_HANDLED;
+}
+
+static int vt8500_rtc_open(struct device *dev)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	int ret;
+
+	ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, IRQF_DISABLED,
+			  "rtc 1Hz", dev);
+	if (ret < 0) {
+		dev_err(dev, "can't get irq %i, err %d\n", vt8500_rtc->irq_hz,
+			ret);
+		goto err_irq_hz;
+	}
+	ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, IRQF_DISABLED,
+			  "rtc alarm", dev);
+	if (ret < 0) {
+		dev_err(dev, "can't get irq %i, err %d\n",
+			vt8500_rtc->irq_alarm, ret);
+		goto err_irq_alarm;
+	}
+	return 0;
+
+err_irq_alarm:
+	free_irq(vt8500_rtc->irq_hz, dev);
+err_irq_hz:
+	return ret;
+}
+
+static void vt8500_rtc_release(struct device *dev)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+	spin_lock_irq(&vt8500_rtc->lock);
+	/* Disable alarm matching */
+	writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
+	spin_unlock_irq(&vt8500_rtc->lock);
+
+	free_irq(vt8500_rtc->irq_alarm, dev);
+	free_irq(vt8500_rtc->irq_hz, dev);
+}
+
+static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	u32 date, time;
+
+	date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
+	time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
+
+	tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
+	tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
+	tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
+	tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
+	tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
+	tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S);
+	tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
+
+	return 0;
+}
+
+static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+	writel((bin2bcd(tm->tm_year) << DATE_YEAR_S)
+		| (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
+		| (bin2bcd(tm->tm_mday)),
+		vt8500_rtc->regbase + VT8500_RTC_DS);
+	writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
+		| (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
+		| (bin2bcd(tm->tm_min) << TIME_MIN_S)
+		| (bin2bcd(tm->tm_sec)),
+		vt8500_rtc->regbase + VT8500_RTC_TS);
+
+	return 0;
+}
+
+static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	u32 isr, alarm;
+
+	alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+
+	alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
+	alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
+	alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
+	alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
+
+	alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
+
+	alrm->pending = (isr & 1) ? 1 : 0;
+	return 0;
+}
+
+static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+	writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
+		| (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
+		| (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
+		| (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
+		| (bin2bcd(alrm->time.tm_sec)),
+		vt8500_rtc->regbase + VT8500_RTC_AS);
+
+	return 0;
+}
+
+static int vt8500_rtc_ioctl(struct device *dev, unsigned int cmd,
+		unsigned long arg)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	int ret = 0;
+	unsigned long tmp;
+
+	spin_lock_irq(&vt8500_rtc->lock);
+	switch (cmd) {
+	case RTC_AIE_OFF:
+		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+		writel(tmp & ~ALARM_ENABLE_MASK,
+		       vt8500_rtc->regbase + VT8500_RTC_AS);
+		break;
+	case RTC_AIE_ON:
+		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+		writel(tmp | ALARM_ENABLE_MASK,
+		       vt8500_rtc->regbase + VT8500_RTC_AS);
+		break;
+	case RTC_UIE_OFF:
+		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
+		writel(tmp & ~(1 << 2),
+		       vt8500_rtc->regbase + VT8500_RTC_CR);
+		break;
+	case RTC_UIE_ON:
+		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
+		writel(tmp | ((1 << 3) | (1 << 2)),
+		       vt8500_rtc->regbase + VT8500_RTC_CR);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+	}
+
+	spin_unlock_irq(&vt8500_rtc->lock);
+	return ret;
+}
+
+static const struct rtc_class_ops vt8500_rtc_ops = {
+	.open = vt8500_rtc_open,
+	.release = vt8500_rtc_release,
+	.ioctl = vt8500_rtc_ioctl,
+	.read_time = vt8500_rtc_read_time,
+	.set_time = vt8500_rtc_set_time,
+	.read_alarm = vt8500_rtc_read_alarm,
+	.set_alarm = vt8500_rtc_set_alarm,
+};
+
+static int __init vt8500_rtc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct vt8500_rtc *vt8500_rtc;
+	struct resource *res;
+	int ret;
+
+	vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
+	if (!vt8500_rtc)
+		return -ENOMEM;
+
+	spin_lock_init(&vt8500_rtc->lock);
+	platform_set_drvdata(pdev, vt8500_rtc);
+
+	ret = -ENXIO;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "No I/O memory resource defined\n");
+		goto err_map;
+	}
+
+	vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
+	if (vt8500_rtc->irq_alarm < 0) {
+		dev_err(dev, "No alarm IRQ resource defined\n");
+		goto err_map;
+	}
+
+	vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
+	if (vt8500_rtc->irq_hz < 0) {
+		dev_err(dev, "No 1Hz IRQ resource defined\n");
+		goto err_map;
+	}
+
+	ret = -ENOMEM;
+	vt8500_rtc->regbase = ioremap(res->start, resource_size(res));
+	if (!vt8500_rtc->regbase) {
+		dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
+		goto err_map;
+	}
+
+	/* Enable the second/minute interrupt generation and enable RTC */
+	writel((1 << 3) | (1 << 2) | 1, vt8500_rtc->regbase + VT8500_RTC_CR);
+
+	vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
+					      &vt8500_rtc_ops, THIS_MODULE);
+	ret = PTR_ERR(vt8500_rtc->rtc);
+	if (IS_ERR(vt8500_rtc->rtc)) {
+		dev_err(dev, "Failed to register RTC device -> %d\n", ret);
+		goto err_rtc_reg;
+	}
+
+	device_init_wakeup(dev, 1);
+
+	return 0;
+
+err_rtc_reg:
+	iounmap(vt8500_rtc->regbase);
+err_map:
+	kfree(vt8500_rtc);
+	return ret;
+}
+
+static int __exit vt8500_rtc_remove(struct platform_device *pdev)
+{
+	struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
+
+	rtc_device_unregister(vt8500_rtc->rtc);
+
+	spin_lock_irq(&vt8500_rtc->lock);
+	iounmap(vt8500_rtc->regbase);
+	spin_unlock_irq(&vt8500_rtc->lock);
+
+	kfree(vt8500_rtc);
+
+	return 0;
+}
+
+static struct platform_driver vt8500_rtc_driver = {
+	.probe		= vt8500_rtc_probe,
+	.remove		= __exit_p(vt8500_rtc_remove),
+	.driver		= {
+		.name	= "vt8500-rtc"
+	},
+};
+
+static int __init vt8500_rtc_init(void)
+{
+	return platform_driver_register(&vt8500_rtc_driver);
+}
+
+static void __exit vt8500_rtc_exit(void)
+{
+	platform_driver_unregister(&vt8500_rtc_driver);
+}
+
+module_init(vt8500_rtc_init);
+module_exit(vt8500_rtc_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:vt8500-rtc");
-- 
1.7.3.2


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

* [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-07 16:28 [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Alexey Charkov
                   ` (3 preceding siblings ...)
  2010-11-07 16:28 ` [PATCH 5/6 v2] rtc: Add support for the RTC in VIA VT8500 and compatibles Alexey Charkov
@ 2010-11-07 16:28 ` Alexey Charkov
  2010-11-08  4:17   ` Paul Mundt
  2010-11-08  8:47   ` [PATCH 6/6 v2] " Arnd Bergmann
  2010-11-07 16:57 ` [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Russell King - ARM Linux
  2010-11-07 17:00 ` [PATCH 1/6 v2] " Russell King - ARM Linux
  6 siblings, 2 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-07 16:28 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, Andrew Morton,
	Guennadi Liakhovetski, Paul Mundt, Florian Tobias Schandinat,
	Ralf Baechle, David S. Miller, linux-kernel

This adds drivers for the LCD controller found in VIA VT8500 SoC,
GOVR display controller found in WonderMedia WM8505 SoC and for the
Graphics Engine present in both of them that provides hardware
accelerated raster operations (used for copyarea and fillrect).

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Please review and (if appropriate) commit to a relevant git tree for
further integration in 2.6.38.

Compared to the previous submission, this code just contains a rebase
against the latest changes introduced in 2.6.37 merge window.

Relevant platform definitions are introduced by PATCH 1/6 in this series,
so one would need that to make use of this code.

Due credits go to the community for providing feedback, advice and
testing. Driver for WM8505 is by Ed Spiridonov, as stated in the
relevant copyright header, parts modified by myself.

Invaluable work wrt the Graphics Engine documentation has been done
by Giuseppe Gatta aka nextvolume. Also special thanks go to Angus
Gratton aka projectgus for convincing VIA to partly release GPL
kernel sources for the vendor-provided images, which greatly helped
in understanding some parts of WM8505's complex video handling chain.

 drivers/video/Kconfig         |   26 +++
 drivers/video/Makefile        |    3 +
 drivers/video/vt8500lcdfb.c   |  452 +++++++++++++++++++++++++++++++++++++++++
 drivers/video/vt8500lcdfb.h   |   34 +++
 drivers/video/wm8505fb.c      |  438 +++++++++++++++++++++++++++++++++++++++
 drivers/video/wm8505fb_regs.h |   76 +++++++
 drivers/video/wmt_ge_rops.c   |  186 +++++++++++++++++
 drivers/video/wmt_ge_rops.h   |    5 +
 8 files changed, 1220 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/vt8500lcdfb.c
 create mode 100644 drivers/video/vt8500lcdfb.h
 create mode 100644 drivers/video/wm8505fb.c
 create mode 100644 drivers/video/wm8505fb_regs.h
 create mode 100644 drivers/video/wmt_ge_rops.c
 create mode 100644 drivers/video/wmt_ge_rops.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 27c1fb4..954f6e9 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -186,6 +186,14 @@ config FB_SYS_FOPS
        depends on FB
        default n
 
+config FB_WMT_GE_ROPS
+	tristate
+	depends on FB
+	default n
+	---help---
+	  Include functions for accelerated rectangle filling and area
+	  copying using WonderMedia Graphics Engine operations.
+
 config FB_DEFERRED_IO
 	bool
 	depends on FB
@@ -1722,6 +1730,24 @@ config FB_AU1200
 	  various panels and CRTs by passing in kernel cmd line option
 	  au1200fb:panel=<name>.
 
+config FB_VT8500
+	bool "VT8500 LCD Driver"
+	depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500
+	select FB_WMT_GE_ROPS
+	select FB_SYS_IMAGEBLIT
+	help
+	  This is the framebuffer driver for VIA VT8500 integrated LCD
+	  controller.
+
+config FB_WM8505
+	bool "WM8505 frame buffer support"
+	depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505
+	select FB_WMT_GE_ROPS
+	select FB_SYS_IMAGEBLIT
+	help
+	  This is the framebuffer driver for WonderMedia WM8505
+	  integrated LCD controller.
+
 source "drivers/video/geode/Kconfig"
 
 config FB_HIT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 485e8ed..8d916dc 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB)       += svgalib.o
 obj-$(CONFIG_FB_MACMODES)      += macmodes.o
 obj-$(CONFIG_FB_DDC)           += fb_ddc.o
 obj-$(CONFIG_FB_DEFERRED_IO)   += fb_defio.o
+obj-$(CONFIG_FB_WMT_GE_ROPS)   += wmt_ge_rops.o
 
 # Hardware specific drivers go first
 obj-$(CONFIG_FB_AMIGA)            += amifb.o c2p_planar.o
@@ -104,6 +105,8 @@ obj-$(CONFIG_FB_W100)		  += w100fb.o
 obj-$(CONFIG_FB_TMIO)		  += tmiofb.o
 obj-$(CONFIG_FB_AU1100)		  += au1100fb.o
 obj-$(CONFIG_FB_AU1200)		  += au1200fb.o
+obj-$(CONFIG_FB_VT8500)		  += vt8500lcdfb.o
+obj-$(CONFIG_FB_WM8505)		  += wm8505fb.o
 obj-$(CONFIG_FB_PMAG_AA)	  += pmag-aa-fb.o
 obj-$(CONFIG_FB_PMAG_BA)	  += pmag-ba-fb.o
 obj-$(CONFIG_FB_PMAGB_B)	  += pmagb-b-fb.o
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
new file mode 100644
index 0000000..3dd2296
--- /dev/null
+++ b/drivers/video/vt8500lcdfb.c
@@ -0,0 +1,452 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on skeletonfb.c and pxafb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <asm/irq.h>
+
+#include <mach/vt8500fb.h>
+
+#include "vt8500lcdfb.h"
+#include "wmt_ge_rops.h"
+
+static int vt8500lcd_set_par(struct fb_info *info)
+{
+	struct vt8500lcd_info *fbi = container_of(info,
+						  struct vt8500lcd_info,
+						  fb);
+	int reg_bpp = 5; /* 16bpp */
+	int i;
+	unsigned long control0;
+
+	if (!fbi)
+		return -EINVAL;
+
+	if (info->var.bits_per_pixel <= 8) {
+		/* palettized */
+		info->var.red.offset    = 0;
+		info->var.red.length    = info->var.bits_per_pixel;
+		info->var.red.msb_right = 0;
+
+		info->var.green.offset  = 0;
+		info->var.green.length  = info->var.bits_per_pixel;
+		info->var.green.msb_right = 0;
+
+		info->var.blue.offset   = 0;
+		info->var.blue.length   = info->var.bits_per_pixel;
+		info->var.blue.msb_right = 0;
+
+		info->var.transp.offset = 0;
+		info->var.transp.length = 0;
+		info->var.transp.msb_right = 0;
+
+		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+		info->fix.line_length = info->var.xres_virtual /
+						(8/info->var.bits_per_pixel);
+	} else {
+		/* non-palettized */
+		info->var.transp.offset = 0;
+		info->var.transp.length = 0;
+		info->var.transp.msb_right = 0;
+
+		if (info->var.bits_per_pixel == 16) {
+			/* RGB565 */
+			info->var.red.offset = 11;
+			info->var.red.length = 5;
+			info->var.red.msb_right = 0;
+			info->var.green.offset = 5;
+			info->var.green.length = 6;
+			info->var.green.msb_right = 0;
+			info->var.blue.offset = 0;
+			info->var.blue.length = 5;
+			info->var.blue.msb_right = 0;
+		} else {
+			/* Equal depths per channel */
+			info->var.red.offset = info->var.bits_per_pixel
+							* 2 / 3;
+			info->var.red.length = info->var.bits_per_pixel / 3;
+			info->var.red.msb_right = 0;
+			info->var.green.offset = info->var.bits_per_pixel / 3;
+			info->var.green.length = info->var.bits_per_pixel / 3;
+			info->var.green.msb_right = 0;
+			info->var.blue.offset = 0;
+			info->var.blue.length = info->var.bits_per_pixel / 3;
+			info->var.blue.msb_right = 0;
+		}
+
+		info->fix.visual = FB_VISUAL_TRUECOLOR;
+		info->fix.line_length = info->var.bits_per_pixel > 16 ?
+					info->var.xres_virtual << 2 :
+					info->var.xres_virtual << 1;
+	}
+
+	for (i = 0; i < 8; i++) {
+		if (bpp_values[i] == info->var.bits_per_pixel) {
+			reg_bpp = i;
+			continue;
+		}
+	}
+
+	control0 = readl(fbi->regbase) & ~0xf;
+	writel(0, fbi->regbase);
+	while (readl(fbi->regbase + 0x38) & 0x10)
+		/* wait */;
+	writel((((info->var.hsync_len - 1) & 0x3f) << 26)
+		| ((info->var.left_margin & 0xff) << 18)
+		| (((info->var.xres - 1) & 0x3ff) << 8)
+		| (info->var.right_margin & 0xff), fbi->regbase + 0x4);
+	writel((((info->var.vsync_len - 1) & 0x3f) << 26)
+		| ((info->var.upper_margin & 0xff) << 18)
+		| (((info->var.yres - 1) & 0x3ff) << 8)
+		| (info->var.lower_margin & 0xff), fbi->regbase + 0x8);
+	writel((((info->var.yres - 1) & 0x400) << 2)
+		| ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10);
+	writel(0x80000000, fbi->regbase + 0x20);
+	writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase);
+
+	return 0;
+}
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   struct fb_info *info) {
+	struct vt8500lcd_info *fbi = container_of(info,
+						  struct vt8500lcd_info,
+						  fb);
+	int ret = 1;
+	unsigned int val;
+	if (regno >= 256)
+		return -EINVAL;
+
+	if (info->var.grayscale)
+		red = green = blue =
+			(19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+	switch (fbi->fb.fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+		if (regno < 16) {
+			u32 *pal = fbi->fb.pseudo_palette;
+
+			val  = chan_to_field(red, &fbi->fb.var.red);
+			val |= chan_to_field(green, &fbi->fb.var.green);
+			val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+			pal[regno] = val;
+			ret = 0;
+		}
+		break;
+
+	case FB_VISUAL_STATIC_PSEUDOCOLOR:
+	case FB_VISUAL_PSEUDOCOLOR:
+		writew((red & 0xf800)
+		      | ((green >> 5) & 0x7e0)
+		      | ((blue >> 11) & 0x1f),
+		       fbi->palette_cpu + sizeof(u16) * regno);
+		break;
+	}
+
+	return ret;
+}
+
+static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd,
+			 unsigned long arg)
+{
+	int ret = 0;
+	struct vt8500lcd_info *fbi = container_of(info,
+						  struct vt8500lcd_info,
+						  fb);
+
+	if (cmd == FBIO_WAITFORVSYNC) {
+		/* Unmask End of Frame interrupt */
+		writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c);
+		ret = wait_event_interruptible_timeout(fbi->wait,
+			readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10);
+		/* Mask back to reduce unwanted interrupt traffic */
+		writel(0xffffffff, fbi->regbase + 0x3c);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			return -ETIMEDOUT;
+	}
+
+	return ret;
+}
+
+static int vt8500lcd_pan_display(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	unsigned pixlen = info->fix.line_length / info->var.xres_virtual;
+	unsigned off = pixlen * var->xoffset
+		      + info->fix.line_length * var->yoffset;
+	struct vt8500lcd_info *fbi = container_of(info,
+						  struct vt8500lcd_info,
+						  fb);
+	writel((1 << 31)
+		| (((var->xres_virtual - var->xres) * pixlen / 4) << 20)
+		| (off >> 2), fbi->regbase + 0x20);
+	return 0;
+}
+
+static struct fb_ops vt8500lcd_ops = {
+	.owner		= THIS_MODULE,
+	.fb_set_par	= vt8500lcd_set_par,
+	.fb_setcolreg	= vt8500lcd_setcolreg,
+	.fb_fillrect	= wmt_ge_fillrect,
+	.fb_copyarea	= wmt_ge_copyarea,
+	.fb_imageblit	= sys_imageblit,
+	.fb_sync	= wmt_ge_sync,
+	.fb_ioctl	= vt8500lcd_ioctl,
+	.fb_pan_display	= vt8500lcd_pan_display,
+};
+
+static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id)
+{
+	struct vt8500lcd_info *fbi = dev_id;
+
+	if (readl(fbi->regbase + 0x38) & (1 << 3))
+		wake_up_interruptible(&fbi->wait);
+
+	writel(0xffffffff, fbi->regbase + 0x38);
+	return IRQ_HANDLED;
+}
+
+static int __devinit vt8500lcd_probe(struct platform_device *pdev)
+{
+	struct vt8500lcd_info *fbi;
+	struct resource *res;
+	struct vt8500fb_platform_data *pdata = pdev->dev.platform_data;
+	void *addr;
+	int irq, ret;
+
+	ret = -ENOMEM;
+	fbi = NULL;
+
+	fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16,
+							GFP_KERNEL);
+	if (!fbi) {
+		dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	strcpy(fbi->fb.fix.id, "VT8500 LCD");
+
+	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
+	fbi->fb.fix.xpanstep	= 0;
+	fbi->fb.fix.ypanstep	= 1;
+	fbi->fb.fix.ywrapstep	= 0;
+	fbi->fb.fix.accel	= FB_ACCEL_NONE;
+
+	fbi->fb.var.nonstd	= 0;
+	fbi->fb.var.activate	= FB_ACTIVATE_NOW;
+	fbi->fb.var.height	= -1;
+	fbi->fb.var.width	= -1;
+	fbi->fb.var.vmode	= FB_VMODE_NONINTERLACED;
+
+	fbi->fb.fbops		= &vt8500lcd_ops;
+	fbi->fb.flags		= FBINFO_DEFAULT
+				| FBINFO_HWACCEL_COPYAREA
+				| FBINFO_HWACCEL_FILLRECT
+				| FBINFO_HWACCEL_YPAN
+				| FBINFO_VIRTFB
+				| FBINFO_PARTIAL_PAN_OK;
+	fbi->fb.node		= -1;
+
+	addr = fbi;
+	addr = addr + sizeof(struct vt8500lcd_info);
+	fbi->fb.pseudo_palette	= addr;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		ret = -ENODEV;
+		goto failed_fbi;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), "vt8500lcd");
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		ret = -EBUSY;
+		goto failed_fbi;
+	}
+
+	fbi->regbase = ioremap(res->start, resource_size(res));
+	if (fbi->regbase == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		ret = -EBUSY;
+		goto failed_free_res;
+	}
+
+	fbi->fb.fix.smem_start	= pdata->video_mem_phys;
+	fbi->fb.fix.smem_len	= pdata->video_mem_len;
+	fbi->fb.screen_base	= pdata->video_mem_virt;
+
+	fbi->palette_size	= PAGE_ALIGN(512);
+	fbi->palette_cpu	= dma_alloc_coherent(&pdev->dev,
+						     fbi->palette_size,
+						     &fbi->palette_phys,
+						     GFP_KERNEL);
+	if (fbi->fb.pseudo_palette == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate palette buffer\n");
+		ret = -ENOMEM;
+		goto failed_free_io;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no IRQ defined\n");
+		ret = -ENODEV;
+		goto failed_free_palette;
+	}
+
+	ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi);
+	if (ret) {
+		dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
+		ret = -EBUSY;
+		goto failed_free_palette;
+	}
+
+	init_waitqueue_head(&fbi->wait);
+
+	if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+		dev_err(&pdev->dev, "Failed to allocate color map\n");
+		ret = -ENOMEM;
+		goto failed_free_irq;
+	}
+
+	fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+	fbi->fb.var.bits_per_pixel	= pdata->bpp;
+	fbi->fb.var.xres_virtual	= pdata->xres_virtual;
+	fbi->fb.var.yres_virtual	= pdata->yres_virtual;
+
+	ret = vt8500lcd_set_par(&fbi->fb);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to set parameters\n");
+		goto failed_free_cmap;
+	}
+
+	writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c);
+	writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18);
+
+	platform_set_drvdata(pdev, fbi);
+
+	ret = register_framebuffer(&fbi->fb);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Failed to register framebuffer device: %d\n", ret);
+		goto failed_free_cmap;
+	}
+
+	/*
+	 * Ok, now enable the LCD controller
+	 */
+	writel(readl(fbi->regbase) | 1, fbi->regbase);
+
+	return 0;
+
+failed_free_cmap:
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_irq:
+	free_irq(irq, fbi);
+failed_free_palette:
+	dma_free_coherent(&pdev->dev, fbi->palette_size,
+			  fbi->fb.pseudo_palette, fbi->palette_phys);
+failed_free_io:
+	iounmap(fbi->regbase);
+failed_free_res:
+	release_mem_region(res->start, resource_size(res));
+failed_fbi:
+	platform_set_drvdata(pdev, NULL);
+	kfree(fbi);
+failed:
+	return ret;
+}
+
+static int __devexit vt8500lcd_remove(struct platform_device *pdev)
+{
+	struct vt8500lcd_info *fbi = platform_get_drvdata(pdev);
+	struct resource *res;
+	int irq;
+
+	if (!fbi)
+		return 0;
+
+	unregister_framebuffer(&fbi->fb);
+
+	writel(0, fbi->regbase);
+
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+
+	irq = platform_get_irq(pdev, 0);
+	free_irq(irq, fbi);
+
+	iounmap(fbi->regbase);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(fbi);
+
+	return 0;
+}
+
+static struct platform_driver vt8500lcd_driver = {
+	.probe		= vt8500lcd_probe,
+	.remove		= vt8500lcd_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "vt8500-lcd",
+	},
+};
+
+static int __init vt8500lcd_init(void)
+{
+	return platform_driver_register(&vt8500lcd_driver);
+}
+
+static void __exit vt8500lcd_exit(void)
+{
+	platform_driver_unregister(&vt8500lcd_driver);
+}
+
+module_init(vt8500lcd_init);
+module_exit(vt8500lcd_exit);
+
+MODULE_DESCRIPTION("LCD controller driver for VIA VT8500");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/vt8500lcdfb.h b/drivers/video/vt8500lcdfb.h
new file mode 100644
index 0000000..36ca3ca
--- /dev/null
+++ b/drivers/video/vt8500lcdfb.h
@@ -0,0 +1,34 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+struct vt8500lcd_info {
+	struct fb_info		fb;
+	void __iomem		*regbase;
+	void __iomem		*palette_cpu;
+	dma_addr_t		palette_phys;
+	size_t			palette_size;
+	wait_queue_head_t	wait;
+};
+
+static int bpp_values[] = {
+	1,
+	2,
+	4,
+	8,
+	12,
+	16,
+	18,
+	24,
+};
diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c
new file mode 100644
index 0000000..9a1cc70
--- /dev/null
+++ b/drivers/video/wm8505fb.c
@@ -0,0 +1,438 @@
+/*
+ *  WonderMedia WM8505 Frame Buffer device driver
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *    Based on vt8500lcdfb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <asm/irq.h>
+
+#include <mach/vt8500fb.h>
+
+#include "wm8505fb_regs.h"
+#include "wmt_ge_rops.h"
+
+#define DRIVER_NAME "wm8505-fb"
+
+struct wm8505fb_info {
+	struct fb_info		fb;
+	void __iomem		*regbase;
+	unsigned int		contrast;
+};
+
+
+static int wm8505fb_init_hw(struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	int i;
+
+	/* I know the purpose only of few registers, so clear unknown */
+	for (i = 0; i < 0x200; i += 4)
+		writel(0, fbi->regbase + i);
+
+	/* Set frame buffer address */
+	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
+	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
+
+	/* Set in-memory picture format to RGB 32bpp */
+	writel(0x1c,		       fbi->regbase + WMT_GOVR_COLORSPACE);
+	writel(1,		       fbi->regbase + WMT_GOVR_COLORSPACE1);
+
+	/* Virtual buffer size */
+	writel(info->var.xres,	       fbi->regbase + WMT_GOVR_XRES);
+	writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL);
+
+	/* black magic ;) */
+	writel(0xf,		       fbi->regbase + WMT_GOVR_FHI);
+	writel(4,		       fbi->regbase + WMT_GOVR_DVO_SET);
+	writel(1,		       fbi->regbase + WMT_GOVR_MIF_ENABLE);
+	writel(1,		       fbi->regbase + WMT_GOVR_REG_UPDATE);
+
+	return 0;
+}
+
+static int wm8505fb_set_timing(struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	int h_start = info->var.left_margin;
+	int h_end = h_start + info->var.xres;
+	int h_all = h_end + info->var.right_margin;
+	int h_sync = info->var.hsync_len;
+
+	int v_start = info->var.upper_margin;
+	int v_end = v_start + info->var.yres;
+	int v_all = v_end + info->var.lower_margin;
+	int v_sync = info->var.vsync_len + 1;
+
+	writel(0, fbi->regbase + WMT_GOVR_TG);
+
+	writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START);
+	writel(h_end,   fbi->regbase + WMT_GOVR_TIMING_H_END);
+	writel(h_all,   fbi->regbase + WMT_GOVR_TIMING_H_ALL);
+	writel(h_sync,  fbi->regbase + WMT_GOVR_TIMING_H_SYNC);
+
+	writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START);
+	writel(v_end,   fbi->regbase + WMT_GOVR_TIMING_V_END);
+	writel(v_all,   fbi->regbase + WMT_GOVR_TIMING_V_ALL);
+	writel(v_sync,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+
+	writel(1, fbi->regbase + WMT_GOVR_TG);
+
+	return 0;
+}
+
+
+static int wm8505fb_set_par(struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	if (!fbi)
+		return -EINVAL;
+
+	if (info->var.bits_per_pixel == 32) {
+		info->var.red.offset = 16;
+		info->var.red.length = 8;
+		info->var.red.msb_right = 0;
+		info->var.green.offset = 8;
+		info->var.green.length = 8;
+		info->var.green.msb_right = 0;
+		info->var.blue.offset = 0;
+		info->var.blue.length = 8;
+		info->var.blue.msb_right = 0;
+		info->fix.visual = FB_VISUAL_TRUECOLOR;
+		info->fix.line_length = info->var.xres_virtual << 2;
+	}
+
+	wm8505fb_set_timing(info);
+
+	writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast,
+		fbi->regbase + WMT_GOVR_CONTRAST);
+
+	return 0;
+}
+
+static ssize_t contrast_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	return sprintf(buf, "%d\n", fbi->contrast);
+}
+
+static ssize_t contrast_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	fbi->contrast = strict_strtoul(buf, NULL, 10) & 0xff;
+	wm8505fb_set_par(info);
+
+	return count;
+}
+
+static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store);
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   struct fb_info *info) {
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+	int ret = 1;
+	unsigned int val;
+	if (regno >= 256)
+		return -EINVAL;
+
+	if (info->var.grayscale)
+		red = green = blue =
+			(19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+	switch (fbi->fb.fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+		if (regno < 16) {
+			u32 *pal = info->pseudo_palette;
+
+			val  = chan_to_field(red, &fbi->fb.var.red);
+			val |= chan_to_field(green, &fbi->fb.var.green);
+			val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+			pal[regno] = val;
+			ret = 0;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN);
+	writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN);
+	return 0;
+}
+
+static int wm8505fb_blank(int blank, struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	switch (blank) {
+	case FB_BLANK_UNBLANK:
+		wm8505fb_set_timing(info);
+		break;
+	default:
+		writel(0,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+		break;
+	}
+
+	return 0;
+}
+
+static struct fb_ops wm8505fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_set_par	= wm8505fb_set_par,
+	.fb_setcolreg	= wm8505fb_setcolreg,
+	.fb_fillrect	= wmt_ge_fillrect,
+	.fb_copyarea	= wmt_ge_copyarea,
+	.fb_imageblit	= sys_imageblit,
+	.fb_sync	= wmt_ge_sync,
+	.fb_pan_display	= wm8505fb_pan_display,
+	.fb_blank	= wm8505fb_blank,
+};
+
+static int __devinit wm8505fb_probe(struct platform_device *pdev)
+{
+	struct wm8505fb_info	*fbi;
+	struct resource		*res;
+	void			*addr;
+	struct vt8500fb_platform_data *pdata;
+	int ret;
+
+	pdata = pdev->dev.platform_data;
+
+	ret = -ENOMEM;
+	fbi = NULL;
+
+	fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16,
+							GFP_KERNEL);
+	if (!fbi) {
+		dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	strcpy(fbi->fb.fix.id, DRIVER_NAME);
+
+	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
+	fbi->fb.fix.xpanstep	= 1;
+	fbi->fb.fix.ypanstep	= 1;
+	fbi->fb.fix.ywrapstep	= 0;
+	fbi->fb.fix.accel	= FB_ACCEL_NONE;
+
+	fbi->fb.fbops		= &wm8505fb_ops;
+	fbi->fb.flags		= FBINFO_DEFAULT
+				| FBINFO_HWACCEL_COPYAREA
+				| FBINFO_HWACCEL_FILLRECT
+				| FBINFO_HWACCEL_XPAN
+				| FBINFO_HWACCEL_YPAN
+				| FBINFO_VIRTFB
+				| FBINFO_PARTIAL_PAN_OK;
+	fbi->fb.node		= -1;
+
+	addr = fbi;
+	addr = addr + sizeof(struct wm8505fb_info);
+	fbi->fb.pseudo_palette	= addr;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		ret = -ENODEV;
+		goto failed_fbi;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), "wm8505fb");
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		ret = -EBUSY;
+		goto failed_fbi;
+	}
+
+	fbi->regbase = ioremap(res->start, resource_size(res));
+	if (fbi->regbase == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		ret = -EBUSY;
+		goto failed_free_res;
+	}
+
+	fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+
+	fbi->fb.var.nonstd		= 0;
+	fbi->fb.var.activate		= FB_ACTIVATE_NOW;
+
+	fbi->fb.var.height		= -1;
+	fbi->fb.var.width		= -1;
+	fbi->fb.var.xres_virtual	= pdata->xres_virtual;
+	fbi->fb.var.yres_virtual	= pdata->yres_virtual;
+	fbi->fb.var.bits_per_pixel	= pdata->bpp;
+
+	fbi->fb.fix.smem_start	= pdata->video_mem_phys;
+	fbi->fb.fix.smem_len	= pdata->video_mem_len;
+	fbi->fb.screen_base	= pdata->video_mem_virt;
+	fbi->fb.screen_size	= pdata->video_mem_len;
+
+	if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+		dev_err(&pdev->dev, "Failed to allocate color map\n");
+		ret = -ENOMEM;
+		goto failed_free_mem;
+	}
+
+	wm8505fb_init_hw(&fbi->fb);
+
+	fbi->contrast = 0x50;
+	ret = wm8505fb_set_par(&fbi->fb);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to set parameters\n");
+		goto failed_free_cmap;
+	}
+
+	platform_set_drvdata(pdev, fbi);
+
+	ret = register_framebuffer(&fbi->fb);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Failed to register framebuffer device: %d\n", ret);
+		goto failed_free_cmap;
+	}
+
+	ret = device_create_file(&pdev->dev, &dev_attr_contrast);
+	if (ret < 0) {
+		printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
+			fbi->fb.node, ret);
+	}
+
+	printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n",
+	       fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start,
+	       fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1);
+
+	return 0;
+
+failed_free_cmap:
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_mem:
+	free_pages_exact(fbi->fb.screen_base, fbi->fb.screen_size);
+failed_free_io:
+	iounmap(fbi->regbase);
+failed_free_res:
+	release_mem_region(res->start, resource_size(res));
+failed_fbi:
+	platform_set_drvdata(pdev, NULL);
+	kfree(fbi);
+failed:
+	return ret;
+}
+
+static int __devexit wm8505fb_remove(struct platform_device *pdev)
+{
+	struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	if (!fbi)
+		return 0;
+
+	unregister_framebuffer(&fbi->fb);
+
+	writel(0, fbi->regbase);
+
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+
+	free_pages_exact(fbi->fb.screen_base, fbi->fb.screen_size);
+
+	iounmap(fbi->regbase);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(fbi);
+
+	return 0;
+}
+
+static struct platform_driver wm8505fb_driver = {
+	.probe		= wm8505fb_probe,
+	.remove		= wm8505fb_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= DRIVER_NAME,
+	},
+};
+
+static int __init wm8505fb_init(void)
+{
+	return platform_driver_register(&wm8505fb_driver);
+}
+
+static void __exit wm8505fb_exit(void)
+{
+	platform_driver_unregister(&wm8505fb_driver);
+}
+
+module_init(wm8505fb_init);
+module_exit(wm8505fb_exit);
+
+MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wm8505fb_regs.h b/drivers/video/wm8505fb_regs.h
new file mode 100644
index 0000000..4dd4166
--- /dev/null
+++ b/drivers/video/wm8505fb_regs.h
@@ -0,0 +1,76 @@
+/*
+ *  GOVR registers list for WM8505 chips
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *   Based on VIA/WonderMedia wm8510-govrh-reg.h
+ *   http://github.com/projectgus/kernel_wm8505/blob/wm8505_2.6.29/
+ *         drivers/video/wmt/register/wm8510/wm8510-govrh-reg.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _WM8505FB_REGS_H
+#define _WM8505FB_REGS_H
+
+/*
+ * Color space select register, default value 0x1c
+ *   BIT0 GOVRH_DVO_YUV2RGB_ENABLE
+ *   BIT1 GOVRH_VGA_YUV2RGB_ENABLE
+ *   BIT2 GOVRH_RGB_MODE
+ *   BIT3 GOVRH_DAC_CLKINV
+ *   BIT4 GOVRH_BLANK_ZERO
+ */
+#define WMT_GOVR_COLORSPACE	0x1e4
+/*
+ * Another colorspace select register, default value 1
+ *   BIT0 GOVRH_DVO_RGB
+ *   BIT1 GOVRH_DVO_YUV422
+ */
+#define WMT_GOVR_COLORSPACE1	 0x30
+
+#define WMT_GOVR_CONTRAST	0x1b8
+#define WMT_GOVR_BRGHTNESS	0x1bc /* incompatible with RGB? */
+
+/* Framubeffer address */
+#define WMT_GOVR_FBADDR		 0x90
+#define WMT_GOVR_FBADDR1	 0x94 /* UV offset in YUV mode */
+
+/* Offset of visible window */
+#define WMT_GOVR_XPAN		 0xa4
+#define WMT_GOVR_YPAN		 0xa0
+
+#define WMT_GOVR_XRES		 0x98
+#define WMT_GOVR_XRES_VIRTUAL	 0x9c
+
+#define WMT_GOVR_MIF_ENABLE	 0x80
+#define WMT_GOVR_FHI		 0xa8
+#define WMT_GOVR_REG_UPDATE	 0xe4
+
+/*
+ *   BIT0 GOVRH_DVO_OUTWIDTH
+ *   BIT1 GOVRH_DVO_SYNC_POLAR
+ *   BIT2 GOVRH_DVO_ENABLE
+ */
+#define WMT_GOVR_DVO_SET	0x148
+
+/* Timing generator? */
+#define WMT_GOVR_TG		0x100
+
+/* Timings */
+#define WMT_GOVR_TIMING_H_ALL	0x108
+#define WMT_GOVR_TIMING_V_ALL	0x10c
+#define WMT_GOVR_TIMING_V_START	0x110
+#define WMT_GOVR_TIMING_V_END	0x114
+#define WMT_GOVR_TIMING_H_START	0x118
+#define WMT_GOVR_TIMING_H_END	0x11c
+#define WMT_GOVR_TIMING_V_SYNC	0x128
+#define WMT_GOVR_TIMING_H_SYNC	0x12c
+
+#endif /* _WM8505FB_REGS_H */
diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
new file mode 100644
index 0000000..c71f97e
--- /dev/null
+++ b/drivers/video/wmt_ge_rops.c
@@ -0,0 +1,186 @@
+/*
+ *  linux/drivers/video/wmt_ge_rops.c
+ *
+ *  Accelerators for raster operations using WonderMedia Graphics Engine
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include "fb_draw.h"
+
+#define GE_COMMAND_OFF		0x00
+#define GE_DEPTH_OFF		0x04
+#define GE_HIGHCOLOR_OFF	0x08
+#define GE_ROPCODE_OFF		0x14
+#define GE_FIRE_OFF		0x18
+#define GE_SRCBASE_OFF		0x20
+#define GE_SRCDISPW_OFF		0x24
+#define GE_SRCDISPH_OFF		0x28
+#define GE_SRCAREAX_OFF		0x2c
+#define GE_SRCAREAY_OFF		0x30
+#define GE_SRCAREAW_OFF		0x34
+#define GE_SRCAREAH_OFF		0x38
+#define GE_DESTBASE_OFF		0x3c
+#define GE_DESTDISPW_OFF	0x40
+#define GE_DESTDISPH_OFF	0x44
+#define GE_DESTAREAX_OFF	0x48
+#define GE_DESTAREAY_OFF	0x4c
+#define GE_DESTAREAW_OFF	0x50
+#define GE_DESTAREAH_OFF	0x54
+#define GE_PAT0C_OFF		0x88	/* Pattern 0 color */
+#define GE_ENABLE_OFF		0xec
+#define GE_INTEN_OFF		0xf0
+#define GE_STATUS_OFF		0xf8
+
+void __iomem *regbase;
+
+void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+	unsigned long fg, pat;
+
+	if (p->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+	    p->fix.visual == FB_VISUAL_DIRECTCOLOR)
+		fg = ((u32 *) (p->pseudo_palette))[rect->color];
+	else
+		fg = rect->color;
+
+	pat = pixel_to_pat(p->var.bits_per_pixel, fg);
+
+	if (p->fbops->fb_sync)
+		p->fbops->fb_sync(p);
+
+	writel(p->var.bits_per_pixel == 32 ? 3 :
+	      (p->var.bits_per_pixel == 8 ? 0 : 1), regbase + GE_DEPTH_OFF);
+	writel(p->var.bits_per_pixel == 15 ? 1 : 0, regbase + GE_HIGHCOLOR_OFF);
+	writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+	writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+	writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+	writel(rect->dx, regbase + GE_DESTAREAX_OFF);
+	writel(rect->dy, regbase + GE_DESTAREAY_OFF);
+	writel(rect->width - 1, regbase + GE_DESTAREAW_OFF);
+	writel(rect->height - 1, regbase + GE_DESTAREAH_OFF);
+
+	writel(pat, regbase + GE_PAT0C_OFF);
+	writel(1, regbase + GE_COMMAND_OFF);
+	writel(rect->rop == ROP_XOR ? 0x5a : 0xf0, regbase + GE_ROPCODE_OFF);
+	writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL(wmt_ge_fillrect);
+
+void wmt_ge_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+	if (p->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (p->fbops->fb_sync)
+		p->fbops->fb_sync(p);
+
+	writel(p->var.bits_per_pixel > 16 ? 3 :
+	      (p->var.bits_per_pixel > 8 ? 1 : 0), regbase + GE_DEPTH_OFF);
+
+	writel(p->fix.smem_start, regbase + GE_SRCBASE_OFF);
+	writel(p->var.xres_virtual - 1, regbase + GE_SRCDISPW_OFF);
+	writel(p->var.yres_virtual - 1, regbase + GE_SRCDISPH_OFF);
+	writel(area->sx, regbase + GE_SRCAREAX_OFF);
+	writel(area->sy, regbase + GE_SRCAREAY_OFF);
+	writel(area->width - 1, regbase + GE_SRCAREAW_OFF);
+	writel(area->height - 1, regbase + GE_SRCAREAH_OFF);
+
+	writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+	writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+	writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+	writel(area->dx, regbase + GE_DESTAREAX_OFF);
+	writel(area->dy, regbase + GE_DESTAREAY_OFF);
+	writel(area->width - 1, regbase + GE_DESTAREAW_OFF);
+	writel(area->height - 1, regbase + GE_DESTAREAH_OFF);
+
+	writel(0xcc, regbase + GE_ROPCODE_OFF);
+	writel(1, regbase + GE_COMMAND_OFF);
+	writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL(wmt_ge_copyarea);
+
+int wmt_ge_sync(struct fb_info *p)
+{
+	while (readl(regbase + GE_STATUS_OFF) & 4)
+		/* busy wait */;
+
+	return 0;
+}
+EXPORT_SYMBOL(wmt_ge_sync);
+
+static int __devinit wmt_ge_rops_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	regbase = ioremap(res->start, resource_size(res));
+	if (regbase == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		ret = -EBUSY;
+		goto error;
+	}
+
+	writel(1, regbase + GE_ENABLE_OFF);
+	printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n");
+
+	return 0;
+
+error:
+	return ret;
+}
+
+static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
+{
+	iounmap(regbase);
+	return 0;
+}
+
+static struct platform_driver wmt_ge_rops_driver = {
+	.probe		= wmt_ge_rops_probe,
+	.remove		= wmt_ge_rops_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "wmt_ge_rops",
+	},
+};
+
+static int __init wmt_ge_rops_init(void)
+{
+	return platform_driver_register(&wmt_ge_rops_driver);
+}
+
+static void __exit wmt_ge_rops_exit(void)
+{
+	platform_driver_unregister(&wmt_ge_rops_driver);
+}
+
+module_init(wmt_ge_rops_init);
+module_exit(wmt_ge_rops_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com");
+MODULE_DESCRIPTION("Accelerators for raster operations using "
+		   "WonderMedia Graphics Engine");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h
new file mode 100644
index 0000000..8738075
--- /dev/null
+++ b/drivers/video/wmt_ge_rops.h
@@ -0,0 +1,5 @@
+extern void wmt_ge_fillrect(struct fb_info *info,
+			    const struct fb_fillrect *rect);
+extern void wmt_ge_copyarea(struct fb_info *info,
+			    const struct fb_copyarea *area);
+extern int wmt_ge_sync(struct fb_info *info);
-- 
1.7.3.2


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

* Re: [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-07 16:28 [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Alexey Charkov
                   ` (4 preceding siblings ...)
  2010-11-07 16:28 ` [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505 Alexey Charkov
@ 2010-11-07 16:57 ` Russell King - ARM Linux
  2010-11-07 17:08   ` Alexey Charkov
  2010-11-07 17:00 ` [PATCH 1/6 v2] " Russell King - ARM Linux
  6 siblings, 1 reply; 91+ messages in thread
From: Russell King - ARM Linux @ 2010-11-07 16:57 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Sun, Nov 07, 2010 at 07:28:52PM +0300, Alexey Charkov wrote:
> +static inline void preallocate_fb(struct vt8500fb_platform_data *p,
> +				  unsigned long align) {
> +	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
> +			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
> +					(8 / p->bpp) + 1));
> +	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
> +							  align);
> +	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
> +}
...
> +void __init vt8500_map_io(void)
> +{
> +	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
> +	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
> +	set_data();
> +#ifdef CONFIG_FB_VT8500
> +	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
> +	preallocate_fb(&panels[current_panel_idx], SZ_4M);
> +	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
> +#endif
> +	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
> +}
...
> +void __init wm8505_map_io(void)
> +{
> +	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
> +	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
> +	set_data();
> +#ifdef CONFIG_FB_WM8505
> +	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
> +	preallocate_fb(&panels[current_panel_idx], 32);
> +	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
> +#endif
> +	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
> +}

I'd much prefer that the allocation of memblock memory is done via the
'reserve' machine callback, rather than trying to group it into the IO
mapping callback.  Is there a reason it can't be?

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

* Re: [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-07 16:28 [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Alexey Charkov
                   ` (5 preceding siblings ...)
  2010-11-07 16:57 ` [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Russell King - ARM Linux
@ 2010-11-07 17:00 ` Russell King - ARM Linux
  2010-11-07 17:16   ` Alexey Charkov
  6 siblings, 1 reply; 91+ messages in thread
From: Russell King - ARM Linux @ 2010-11-07 17:00 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

A couple of other points - sorry, should've been in the last mail.

On Sun, Nov 07, 2010 at 07:28:52PM +0300, Alexey Charkov wrote:
> diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
> new file mode 100644
> index 0000000..e0c6268
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/Kconfig
> @@ -0,0 +1,65 @@
> +if ARCH_VT8500
> +
> +config VTWM_VERSION_VT8500
> +	bool
> +	default n

n is the default anyway, so specifying this is redundant.

> +void __init bv07_init(void)
> +{
> +#ifdef CONFIG_FB_VT8500
> +	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
> +					     + 0x200, 4);

ioremap() is generally regarded as a function which can fail, and therefore
needs its return value checked.  There seems to be multiple instances of
this through this patch.

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

* Re: [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-07 16:57 ` [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Russell King - ARM Linux
@ 2010-11-07 17:08   ` Alexey Charkov
  2010-11-07 17:17     ` Russell King - ARM Linux
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-07 17:08 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

2010/11/7 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Sun, Nov 07, 2010 at 07:28:52PM +0300, Alexey Charkov wrote:
>> +static inline void preallocate_fb(struct vt8500fb_platform_data *p,
>> +                               unsigned long align) {
>> +     p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
>> +                     (p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
>> +                                     (8 / p->bpp) + 1));
>> +     p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
>> +                                                       align);
>> +     p->video_mem_virt = phys_to_virt(p->video_mem_phys);
>> +}
> ...
>> +void __init vt8500_map_io(void)
>> +{
>> +     wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
>> +     wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
>> +     set_data();
>> +#ifdef CONFIG_FB_VT8500
>> +     panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
>> +     preallocate_fb(&panels[current_panel_idx], SZ_4M);
>> +     vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
>> +#endif
>> +     iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
>> +}
> ...
>> +void __init wm8505_map_io(void)
>> +{
>> +     wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
>> +     wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
>> +     set_data();
>> +#ifdef CONFIG_FB_WM8505
>> +     panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
>> +     preallocate_fb(&panels[current_panel_idx], 32);
>> +     vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
>> +#endif
>> +     iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
>> +}
>
> I'd much prefer that the allocation of memblock memory is done via the
> 'reserve' machine callback, rather than trying to group it into the IO
> mapping callback.  Is there a reason it can't be?
>

I believe it can. Thanks for the info, I just didn't know about that.
Will fix this shortly.

Is it OK to leave resources assignment in map_io or is there a better
place to do that as well?

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

* Re: [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-07 17:00 ` [PATCH 1/6 v2] " Russell King - ARM Linux
@ 2010-11-07 17:16   ` Alexey Charkov
  0 siblings, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-07 17:16 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

2010/11/7 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> A couple of other points - sorry, should've been in the last mail.
>
> On Sun, Nov 07, 2010 at 07:28:52PM +0300, Alexey Charkov wrote:
>> diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
>> new file mode 100644
>> index 0000000..e0c6268
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/Kconfig
>> @@ -0,0 +1,65 @@
>> +if ARCH_VT8500
>> +
>> +config VTWM_VERSION_VT8500
>> +     bool
>> +     default n
>
> n is the default anyway, so specifying this is redundant.
>

Great, I'll drop it then.

>> +void __init bv07_init(void)
>> +{
>> +#ifdef CONFIG_FB_VT8500
>> +     void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
>> +                                          + 0x200, 4);
>
> ioremap() is generally regarded as a function which can fail, and therefore
> needs its return value checked.  There seems to be multiple instances of
> this through this patch.
>

Is it OK to simply skip the code that uses the relevant pointer if
ioremap fails (possibly issuing an error via printk)? The problem is
that these are void functions, so I can't just return -ENODEV on
failure.

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

* Re: [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-07 17:08   ` Alexey Charkov
@ 2010-11-07 17:17     ` Russell King - ARM Linux
  2010-11-07 18:25       ` [PATCH 1/6 v3] " Alexey Charkov
  2010-11-08 17:19       ` [PATCH 1/6 v4] " Alexey Charkov
  0 siblings, 2 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2010-11-07 17:17 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Sun, Nov 07, 2010 at 08:08:45PM +0300, Alexey Charkov wrote:
> I believe it can. Thanks for the info, I just didn't know about that.
> Will fix this shortly.
> 
> Is it OK to leave resources assignment in map_io or is there a better
> place to do that as well?

Well, surely resources don't need to be set this early, because
they're not going to be used before their associated devices are
registered - which presumably happens in your init_machine callback.
So, can you not call a function from the init_machine callbacks to
setup these resources?

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

* [PATCH 1/6 v3] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-07 17:17     ` Russell King - ARM Linux
@ 2010-11-07 18:25       ` Alexey Charkov
  2010-11-08 17:19       ` [PATCH 1/6 v4] " Alexey Charkov
  1 sibling, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-07 18:25 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505. Suitable code is
selected (if compiled in) at early initialization time by reading a
platform-specific identification register, as current bootloaders
do not provide any reliable machine id to the kernel.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This incorporates fixes for the issue that Russel has just identified,
namely reserving memblock memory from the appropriate callback,
checking ioremap return values for errors in all instances and not
specifying the unneeded 'default n' in Kconfig.

 arch/arm/Kconfig                                |   14 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 +++
 arch/arm/mach-vt8500/Kconfig                    |   63 ++++
 arch/arm/mach-vt8500/Makefile                   |    6 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   81 ++++
 arch/arm/mach-vt8500/devices.c                  |  442 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   46 +++
 arch/arm/mach-vt8500/gpio.c                     |  230 ++++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 ++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 ++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/io.h          |   28 ++
 arch/arm/mach-vt8500/include/mach/irq_defs.h    |  124 +++++++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 ++
 arch/arm/mach-vt8500/include/mach/mmio_regs.h   |   90 +++++
 arch/arm/mach-vt8500/include/mach/system.h      |   20 +
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 ++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 +
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 ++
 arch/arm/mach-vt8500/irq.c                      |  178 +++++++++
 arch/arm/mach-vt8500/irq_defs.c                 |  173 +++++++++
 arch/arm/mach-vt8500/mmio_regs.c                |  118 ++++++
 arch/arm/mach-vt8500/pwm.c                      |  254 +++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  166 +++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   80 ++++
 31 files changed, 2412 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irq_defs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/mmio_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/irq_defs.c
 create mode 100644 arch/arm/mach-vt8500/mmio_regs.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a19a526..e0724ac 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,18 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_CLK
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +985,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..a462869
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,63 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..aff4159
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,6 @@
+obj-y += devices.o gpio.o irq.o irq_defs.o mmio_regs.o timer.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..25aea3d
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,81 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= vt8500_map_io,
+	.reserve	= vt8500_reserve_mem,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..820aad2
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,442 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include <mach/vt8500fb.h>
+#include "devices.h"
+
+static struct resource resources_lcdc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources  = ARRAY_SIZE(resources_lcdc),
+	.resource       = resources_lcdc,
+};
+
+static struct resource resources_wm8505_fb[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(resources_wm8505_fb),
+	.resource       = resources_wm8505_fb,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 43,
+		.right_margin	= 2,
+		.upper_margin	= 10,
+		.lower_margin	= 2,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		int len = strlen(panels[i].mode.name);
+
+		if (memcmp(panels[i].mode.name, str, len) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+static struct resource resources_uart0[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart1[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart2[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart3[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart4[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart5[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_uart0),
+	.resource	= resources_uart0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(resources_uart1),
+	.resource	= resources_uart1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(resources_uart2),
+	.resource	= resources_uart2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(resources_uart3),
+	.resource	= resources_uart3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+	.num_resources	= ARRAY_SIZE(resources_uart4),
+	.resource	= resources_uart4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+	.num_resources	= ARRAY_SIZE(resources_uart5),
+	.resource	= resources_uart5,
+};
+
+static struct resource resources_ehci[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(resources_ehci),
+	.resource	= resources_ehci,
+};
+
+static struct resource resources_ge_rops[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_ge_rops),
+	.resource	= resources_ge_rops,
+};
+
+static struct resource resources_pwm[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+	.resource	= resources_pwm,
+	.num_resources	= ARRAY_SIZE(resources_pwm),
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+static struct resource resources_rtc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+	.resource	= resources_rtc,
+	.num_resources	= ARRAY_SIZE(resources_rtc),
+};
+
+static struct map_desc vt8500_io_desc[] __initdata = {
+	/* SoC MMIO registers, to be filled in later */
+	[0] = {
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init wmt_set_resources(void)
+{
+	resources_lcdc[0].start = wmt_current_regs->lcdc;
+	resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
+	resources_lcdc[1].start = wmt_current_irqs->lcdc;
+	resources_lcdc[1].end = wmt_current_irqs->lcdc;
+
+	resources_wm8505_fb[0].start = wmt_current_regs->govr;
+	resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
+
+	resources_uart0[0].start = wmt_current_regs->uart0;
+	resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
+	resources_uart0[1].start = wmt_current_irqs->uart0;
+	resources_uart0[1].end = wmt_current_irqs->uart0;
+	resources_uart1[0].start = wmt_current_regs->uart1;
+	resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
+	resources_uart1[1].start = wmt_current_irqs->uart1;
+	resources_uart1[1].end = wmt_current_irqs->uart1;
+	resources_uart2[0].start = wmt_current_regs->uart2;
+	resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
+	resources_uart2[1].start = wmt_current_irqs->uart2;
+	resources_uart2[1].end = wmt_current_irqs->uart2;
+	resources_uart3[0].start = wmt_current_regs->uart3;
+	resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
+	resources_uart3[1].start = wmt_current_irqs->uart3;
+	resources_uart3[1].end = wmt_current_irqs->uart3;
+	resources_uart4[0].start = wmt_current_regs->uart4;
+	resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
+	resources_uart4[1].start = wmt_current_irqs->uart4;
+	resources_uart4[1].end = wmt_current_irqs->uart4;
+	resources_uart5[0].start = wmt_current_regs->uart5;
+	resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
+	resources_uart5[1].start = wmt_current_irqs->uart5;
+	resources_uart5[1].end = wmt_current_irqs->uart5;
+
+	resources_ehci[0].start = wmt_current_regs->ehci;
+	resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
+	resources_ehci[1].start = wmt_current_irqs->ehci;
+	resources_ehci[1].end = wmt_current_irqs->ehci;
+
+	resources_ge_rops[0].start = wmt_current_regs->ge;
+	resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
+
+	resources_pwm[0].start = wmt_current_regs->pwm;
+	resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
+
+	resources_rtc[0].start = wmt_current_regs->rtc;
+	resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
+	resources_rtc[1].start = wmt_current_irqs->rtc;
+	resources_rtc[1].end = wmt_current_irqs->rtc;
+	resources_rtc[2].start = wmt_current_irqs->rtc_hz;
+	resources_rtc[2].end = wmt_current_irqs->rtc_hz;
+}
+
+void __init vt8500_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
+	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init wm8505_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
+	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..428809e
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,46 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init wmt_set_resources(void);
+void __init vt8500_gpio_init(void);
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..f533ff0
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,230 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/gpio.h>
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	else
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) &
+			~(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
+	VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
+	VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
+	VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
+	VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
+	VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
+
+	VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
+	VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
+	VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
+	VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
+
+	VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
+	VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
+	VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
+
+	VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
+
+	VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
+
+	VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	writel(readl(regbase + 0x3c) & ~(1 << offset), regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	writel(readl(regbase + 0x3c) | (1 << offset), regbase + 0x3c);
+
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	else
+		writel(readl(regbase + 0x5c) & ~(1 << offset),
+			regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	gpio_to_irq_map[0] = wmt_current_irqs->ext0;
+	gpio_to_irq_map[1] = wmt_current_irqs->ext1;
+	gpio_to_irq_map[2] = wmt_current_irqs->ext2;
+	gpio_to_irq_map[3] = wmt_current_irqs->ext3;
+	gpio_to_irq_map[4] = wmt_current_irqs->ext4;
+	gpio_to_irq_map[5] = wmt_current_irqs->ext5;
+	gpio_to_irq_map[6] = wmt_current_irqs->ext6;
+	gpio_to_irq_map[7] = wmt_current_irqs->ext7;
+
+	regbase = ioremap(wmt_current_regs->gpio, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..8dd55c8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		((void __iomem *)((a) + 0xf0000000))
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irq_defs.h b/arch/arm/mach-vt8500/include/mach/irq_defs.h
new file mode 100644
index 0000000..fa8f4b3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irq_defs.h
@@ -0,0 +1,124 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irq_defs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef VT8500_IRQ_DEFS_H
+#define VT8500_IRQ_DEFS_H
+
+#include <linux/types.h>
+
+struct wmt_irq_srcs {
+	u8 nr_irqs;
+	u8 jpegenc;
+	u8 jpegdec;
+	u8 pata;
+	u8 dma;
+	u8 ext0;
+	u8 ext1;
+	u8 ext2;
+	u8 ext3;
+	u8 ext4;
+	u8 ext5;
+	u8 ext6;
+	u8 ext7;
+	u8 ether;
+	u8 mpegts;
+	u8 ge;
+	u8 gov;
+	u8 lcdc;
+	u8 lcdf;
+	u8 vpp;
+	u8 vpu;
+	u8 vid;
+	u8 spu;
+	u8 pip;
+	u8 dvo;
+	u8 govw;
+	u8 govrsdscd;
+	u8 govrsdmif;
+	u8 govrhdscd;
+	u8 govrhdmif;
+	u8 cipher;
+	u8 i2c0;
+	u8 i2c1;
+	u8 sdmmc;
+	u8 sdmmc_dma;
+	u8 pmc_wu;
+	u8 spi0;
+	u8 spi1;
+	u8 spi2;
+	u8 nand;
+	u8 nand_dma;
+	u8 nor;
+	u8 memstick;
+	u8 memstick_dma;
+	u8 uart0;
+	u8 uart1;
+	u8 uart2;
+	u8 uart3;
+	u8 uart4;
+	u8 uart5;
+	u8 i2s;
+	u8 pcm;
+	u8 ac97;
+	u8 timer_match0;
+	u8 timer_match1;
+	u8 timer_match2;
+	u8 timer_match3;
+	u8 ehci;
+	u8 uhci;
+	u8 udc;
+	u8 udc_dma;
+	u8 keypad;
+	u8 ps2mouse;
+	u8 ps2kbd;
+	u8 rtc;
+	u8 rtc_hz;
+	u8 adc;
+	u8 cir;
+	u8 dma0;
+	u8 dma1;
+	u8 dma2;
+	u8 dma3;
+	u8 dma4;
+	u8 dma5;
+	u8 dma6;
+	u8 dma7;
+	u8 dma8;
+	u8 dma9;
+	u8 dma10;
+	u8 dma11;
+	u8 dma12;
+	u8 dma13;
+	u8 dma14;
+	u8 dma15;
+	u8 irq0;
+	u8 irq1;
+	u8 irq2;
+	u8 irq3;
+	u8 irq4;
+	u8 irq5;
+	u8 irq6;
+	u8 irq7;
+	u8 sae;
+};
+
+extern struct wmt_irq_srcs wmt_irqs[] __initdata;
+extern struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/mmio_regs.h b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
new file mode 100644
index 0000000..76439dd
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
@@ -0,0 +1,90 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/mmio_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_MMIO_REGS_H
+#define __ASM_ARM_ARCH_MMIO_REGS_H
+
+#include <linux/init.h>
+
+struct wmt_mmio_regs {
+	unsigned long mmio_regs_start;
+	unsigned long mmio_regs_length;
+	unsigned long mmio_regs_virt;
+	unsigned long ddr;
+	unsigned long dma;
+	unsigned long vdma;
+	unsigned long sflash;
+	unsigned long ether;
+	unsigned long cipher;
+	unsigned long ehci;
+	unsigned long uhci;
+	unsigned long pata;
+	unsigned long ps2;
+	unsigned long nand;
+	unsigned long nor;
+	unsigned long sdmmc;
+	unsigned long memstick;
+	unsigned long lcdc;
+	unsigned long vpu;
+	unsigned long gov;
+	unsigned long ge;
+	unsigned long govr;
+	unsigned long scl;
+	unsigned long lcdf;
+	unsigned long vid;
+	unsigned long vpp;
+	unsigned long tsbk;
+	unsigned long jpegdec;
+	unsigned long jpegenc;
+	unsigned long rtc;
+	unsigned long gpio;
+	unsigned long scc;
+	unsigned long pmc;
+	unsigned long ic0;
+	unsigned long ic1;
+	unsigned long uart0;
+	unsigned long uart1;
+	unsigned long uart2;
+	unsigned long uart3;
+	unsigned long uart4;
+	unsigned long uart5;
+	unsigned long pwm;
+	unsigned long spi0;
+	unsigned long spi1;
+	unsigned long spi2;
+	unsigned long cir;
+	unsigned long i2c0;
+	unsigned long i2c1;
+	unsigned long ac97;
+	unsigned long pcm;
+	unsigned long i2s;
+	unsigned long adc;
+	unsigned long keypad;
+};
+
+enum {
+	VT8500_INDEX,
+	WM8505_INDEX,
+};
+
+extern struct wmt_mmio_regs wmt_regmaps[] __initdata;
+extern struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+#endif
+
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..4d812e8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR	0xd8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	void __iomem *pmsr = ioremap(VT8500_PMSR, 4);
+	if (pmsr)
+		writel(1, pmsr);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..75a6912
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END		(PAGE_OFFSET + 0x10000000)
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..cc7f25e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	__u32			xres_virtual;
+	__u32			yres_virtual;
+	__u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..2609f3c
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,178 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);
+	if (edge)
+		writel(readl(base
+			+ VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
+			| (1 << (irq & 0x1f)), base
+			+ VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));
+	else
+		writeb(readb(base
+			+ VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
+			base + VT8500_IC_DCTR + irq);
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	writeb(readb(base
+		+ VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
+		base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
+{
+	return -EINVAL;
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_HIGH, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_level_irq);
+		break;
+	case IRQF_TRIGGER_FALLING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_FALLING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	case IRQF_TRIGGER_RISING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_RISING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	}
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_wake  = vt8500_irq_set_wake,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+	sic_regbase = ioremap(wmt_current_regs->ic1, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR + i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/irq_defs.c b/arch/arm/mach-vt8500/irq_defs.c
new file mode 100644
index 0000000..b338c04
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq_defs.c
@@ -0,0 +1,173 @@
+/* linux/arch/arm/mach-vt8500/irq_defs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/irq_defs.h>
+#include <mach/mmio_regs.h>
+
+struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+struct wmt_irq_srcs wmt_irqs[] __initdata = {
+	[VT8500_INDEX] = {
+		.jpegenc	= 0,
+		.jpegdec	= 1,
+		.pata		= 3,
+		.dma		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.ge		= 8,
+		.gov		= 9,
+		.ether		= 10,
+		.mpegts		= 11,
+		.lcdc		= 12,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.cipher		= 16,
+		.vpp		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.lcdf		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.memstick	= 30,
+		.memstick_dma	= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.i2s		= 34,
+		.pcm		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.vpu		= 40,
+		.vid		= 41,
+		.ac97		= 42,
+		.ehci		= 43,
+		.nor		= 44,
+		.ps2mouse	= 45,
+		.ps2kbd		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.adc		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.dma0		= 56,
+		.dma1		= 57,
+		.dma2		= 58,
+		.dma3		= 59,
+		.dma4		= 60,
+		.dma5		= 61,
+		.dma6		= 62,
+		.dma7		= 63,
+		.nr_irqs	= 64,
+	},
+	[WM8505_INDEX] = {
+		.uhci		= 0,
+		.ehci		= 1,
+		.udc_dma	= 2,
+		.ps2mouse	= 4,
+		.udc		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.keypad		= 8,
+		.dma		= 9,
+		.ether		= 10,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.dma0		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.ps2kbd		= 23,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.dma1		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.uart5		= 30,
+		.uart4		= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.dma2		= 34,
+		.i2s		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.dma3		= 40,
+		.dma4		= 41,
+		.ac97		= 42,
+		.nor		= 44,
+		.dma5		= 45,
+		.dma6		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.dma7		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.irq0		= 56,
+		.irq1		= 57,
+		.irq2		= 58,
+		.irq3		= 59,
+		.irq4		= 60,
+		.irq5		= 61,
+		.irq6		= 62,
+		.irq7		= 63,
+		.jpegdec	= 65,
+		.sae		= 66,
+		.vpu		= 79,
+		.vpp		= 80,
+		.vid		= 81,
+		.spu		= 82,
+		.pip		= 83,
+		.ge		= 84,
+		.gov		= 85,
+		.dvo		= 86,
+		.dma8		= 92,
+		.dma9		= 93,
+		.dma10		= 94,
+		.dma11		= 95,
+		.dma12		= 96,
+		.dma13		= 97,
+		.dma14		= 98,
+		.dma15		= 99,
+		.govw		= 111,
+		.govrsdscd	= 112,
+		.govrsdmif	= 113,
+		.govrhdscd	= 114,
+		.govrhdmif	= 115,
+		.nr_irqs	= 116,
+	},
+};
diff --git a/arch/arm/mach-vt8500/mmio_regs.c b/arch/arm/mach-vt8500/mmio_regs.c
new file mode 100644
index 0000000..e9b3264
--- /dev/null
+++ b/arch/arm/mach-vt8500/mmio_regs.c
@@ -0,0 +1,118 @@
+/* linux/arch/arm/mach-vt8500/mmio_regs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/mmio_regs.h>
+
+struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+struct wmt_mmio_regs wmt_regmaps[] __initdata = {
+	[VT8500_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00350000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000000,
+		.dma			= 0xd8001000,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007900,
+		.uhci			= 0xd8007b01,
+		.pata			= 0xd8008000,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.memstick		= 0xd800b400,
+		.lcdc			= 0xd800e400,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.lcdf			= 0xd8050900,
+		.vid			= 0xd8050a00,
+		.vpp			= 0xd8050b00,
+		.tsbk			= 0xd80f4000,
+		.jpegdec		= 0xd80fe000,
+		.jpegenc		= 0xd80ff000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.pcm			= 0xd82d0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.adc			= 0xd8340000,
+	},
+	[WM8505_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00390000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000400,
+		.dma			= 0xd8001800,
+		.vdma			= 0xd8001c00,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007100,
+		.uhci			= 0xd8007301,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.govr			= 0xd8050800,
+		.vid			= 0xd8050a00,
+		.scl			= 0xd8050d00,
+		.vpp			= 0xd8050f00,
+		.jpegdec		= 0xd80fe000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.ic1			= 0xd8150000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.keypad			= 0xd8260000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.uart4			= 0xd8370000,
+		.uart5			= 0xd8380000,
+	},
+};
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..47c55e6
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,254 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+/*
+ * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
+ * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ */
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	dc = pv * duty_ns / period_ns;
+
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 1))
+		/* busy-wait */;
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 2))
+		/* busy-wait */;
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 3))
+		/* busy-wait */;
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 0))
+		/* busy-wait */;
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	while (readb(pwm->regbase + 0x40 + pwm->pwm_id) & (1 << 0))
+		/* busy-wait */;
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else
+			pwm = ERR_PTR(-EBUSY);
+	} else
+		pwm = ERR_PTR(-ENOENT);
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else
+		pr_warning("PWM device already freed\n");
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[1].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..ab7dee4
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,166 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+static void __iomem *regbase;
+
+/*
+ * Kernel assumes that sched_clock can be called early but may not have
+ * things ready yet.
+ */
+static cycle_t vt8500_timer_read_dummy(struct clocksource *cs)
+{
+	return 0;
+}
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while (readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+		/* wait and poll */;
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read_dummy,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+unsigned long long sched_clock(void)
+{
+	return clocksource_cyc2ns(clocksource.read(&clocksource),
+				clocksource.mult, clocksource.shift);
+}
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while (readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+		/* wait and poll */;
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_current_regs->pmc + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO "
+				"registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	clocksource.read = vt8500_timer_read;
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register "
+			"failed for %s\n", clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_current_irqs->timer_match0, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq "
+			"failed for %s\n", clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..b07cba6
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,80 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <mach/mmio_regs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= wm8505_map_io,
+	.reserve	= wm8505_reserve_mem,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.2


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

* Re: [PATCH 2/6 v2] serial: Add support for UART on VIA VT8500 and compatibles
  2010-11-07 16:28 ` [PATCH 2/6 v2] serial: Add support for UART on VIA VT8500 and compatibles Alexey Charkov
@ 2010-11-07 23:08   ` Alan Cox
  2010-11-08  0:58     ` [PATCH 2/6 v3] " Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Alan Cox @ 2010-11-07 23:08 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Greg Kroah-Hartman,
	Ben Dooks, Kukjin Kim, Feng Tang, Tobias Klauser, linux-kernel



> +static void handle_rx(struct uart_port *port)
> +{
> +	struct tty_struct *tty = port->state->port.tty;

What is your locking for this  versus a hangup ? - tty can go NULL ?

(use tty_port_tty_get/tty_kref_put)


> +static int vt8500_set_baud_rate(struct uart_port *port, unsigned int
> baud) +{
> +	unsigned long div;
> +
> +	div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff);
> +
> +	if (unlikely((baud < 900) || (baud > 921600)))
> +		div |= 7;
> +	else
> +		div |= (921600 / baud) - 1;
> +
> +	while (vt8500_read(port, VT8500_URUSR) & (1 << 5))
> +		;

cpu_relax() and needs a timeout ideally ?


> +	lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR);
> +	lcr &= ~((1 << 5) | (1 << 4));
> +	if (termios->c_cflag & PARENB) {
> +		lcr |= (1 << 4);
> +		if (termios->c_cflag & PARODD)
> +			lcr |= (1 << 5);
> +	}
> +
> +	/* calculate bits per char */
> +	lcr &= ~(1 << 2);
> +	switch (termios->c_cflag & CSIZE) {
> +	case CS7:
> +		break;
> +	case CS8:
> +	default:
> +		lcr |= (1 << 2);
> +		break;

If you don't support other CS values then also do
	termios->c_flag &=~CSIZE;
	termios->c_cflag |= CS8;

here.. so the app knows,

likewise if you don't support mark/space (CMSPAR) then clear the CMSPAR
bit


> +	vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR);
> +	while (vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc)
> +		/* Wait for the reset to complete */;

cpu_relax/timeout


> +static struct console vt8500_console = {
> +	.name = "ttyS",

ttyS is the 8250 style devices

> +	.dev_name	= "ttyS",
> +	.major		= 4,
> +	.minor		= 64,

These major/minors belong to an existing device - use new ones, or in
fact unless they must be fixed use dynamic ones.

If they need fixed ones then we probably want to assign four from the
range for small ports.

);

> +
> +	if (unlikely(ret))
> +		uart_unregister_driver(&vt8500_uart_driver);
> +
> +	printk(KERN_INFO "vt8500_serial: driver initialized\n");

The world really doesn't need to know about each driver being loaded.
pr_debug() should be fine

Looks pretty good.


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

* [PATCH 2/6 v3] serial: Add support for UART on VIA VT8500 and compatibles
  2010-11-07 23:08   ` Alan Cox
@ 2010-11-08  0:58     ` Alexey Charkov
  2010-11-08 10:46       ` Alan Cox
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-08  0:58 UTC (permalink / raw)
  To: Alan Cox
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Greg Kroah-Hartman, Ben Dooks, Kukjin Kim, Feng Tang,
	Tobias Klauser, linux-kernel

This adds a driver for the serial ports found in VIA and WonderMedia
Systems-on-Chip. Interrupt-driven FIFO operation is implemented.
The hardware also supports pure register-based operation (which is
slower) and DMA-based FIFO operation. As the FIFOs are only 16 bytes
long, DMA operation is probably not worth the hassle.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Thanks for the comments, Alan! Hopefully, I've fixed the issues in a right
way.

Best regards,
Alexey

 drivers/serial/Kconfig         |   10 +
 drivers/serial/Makefile        |    1 +
 drivers/serial/vt8500_serial.c |  647 ++++++++++++++++++++++++++++++++++++++++
 include/linux/serial_core.h    |    3 +
 4 files changed, 661 insertions(+), 0 deletions(-)
 create mode 100644 drivers/serial/vt8500_serial.c

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index aff9dcd..6e76a59 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1381,6 +1381,16 @@ config SERIAL_MSM_CONSOLE
 	depends on SERIAL_MSM=y
 	select SERIAL_CORE_CONSOLE
 
+config SERIAL_VT8500
+	bool "VIA VT8500 on-chip serial port support"
+	depends on ARM && ARCH_VT8500
+	select SERIAL_CORE
+
+config SERIAL_VT8500_CONSOLE
+	bool "VIA VT8500 serial console support"
+	depends on SERIAL_VT8500=y
+	select SERIAL_CORE_CONSOLE
+
 config SERIAL_NETX
 	tristate "NetX serial port support"
 	depends on ARM && ARCH_NETX
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index c570576..86b8587 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_SERIAL_TIMBERDALE)	+= timbuart.o
 obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
 obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
 obj-$(CONFIG_SERIAL_MRST_MAX3110)	+= mrst_max3110.o
 obj-$(CONFIG_SERIAL_MFD_HSU)	+= mfd.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
diff --git a/drivers/serial/vt8500_serial.c b/drivers/serial/vt8500_serial.c
new file mode 100644
index 0000000..9eaf36e
--- /dev/null
+++ b/drivers/serial/vt8500_serial.c
@@ -0,0 +1,647 @@
+/*
+ * drivers/serial/vt8500_serial.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on msm_serial.c, which is:
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Robert Love <rlove@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+# define SUPPORT_SYSRQ
+#endif
+
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/*
+ * UART Register offsets
+ */
+
+#define VT8500_URTDR		0x0000	/* Transmit data */
+#define VT8500_URRDR		0x0004	/* Receive data */
+#define VT8500_URDIV		0x0008	/* Clock/Baud rate divisor */
+#define VT8500_URLCR		0x000C	/* Line control */
+#define VT8500_URICR		0x0010	/* IrDA control */
+#define VT8500_URIER		0x0014	/* Interrupt enable */
+#define VT8500_URISR		0x0018	/* Interrupt status */
+#define VT8500_URUSR		0x001c	/* UART status */
+#define VT8500_URFCR		0x0020	/* FIFO control */
+#define VT8500_URFIDX		0x0024	/* FIFO index */
+#define VT8500_URBKR		0x0028	/* Break signal count */
+#define VT8500_URTOD		0x002c	/* Time out divisor */
+#define VT8500_TXFIFO		0x1000	/* Transmit FIFO (16x8) */
+#define VT8500_RXFIFO		0x1020	/* Receive FIFO (16x10) */
+
+/*
+ * Interrupt enable and status bits
+ */
+
+#define TXDE	(1 << 0)	/* Tx Data empty */
+#define RXDF	(1 << 1)	/* Rx Data full */
+#define TXFAE	(1 << 2)	/* Tx FIFO almost empty */
+#define TXFE	(1 << 3)	/* Tx FIFO empty */
+#define RXFAF	(1 << 4)	/* Rx FIFO almost full */
+#define RXFF	(1 << 5)	/* Rx FIFO full */
+#define TXUDR	(1 << 6)	/* Tx underrun */
+#define RXOVER	(1 << 7)	/* Rx overrun */
+#define PER	(1 << 8)	/* Parity error */
+#define FER	(1 << 9)	/* Frame error */
+#define TCTS	(1 << 10)	/* Toggle of CTS */
+#define RXTOUT	(1 << 11)	/* Rx timeout */
+#define BKDONE	(1 << 12)	/* Break signal done */
+#define ERR	(1 << 13)	/* AHB error response */
+
+#define RX_FIFO_INTS	(RXFAF | RXFF | RXOVER | PER | FER | RXTOUT)
+#define TX_FIFO_INTS	(TXFAE | TXFE | TXUDR)
+
+struct vt8500_port {
+	struct uart_port	uart;
+	char			name[16];
+	struct clk		*clk;
+	unsigned int		ier;
+};
+
+static inline void vt8500_write(struct uart_port *port, unsigned int val,
+			     unsigned int off)
+{
+	writel(val, port->membase + off);
+}
+
+static inline unsigned int vt8500_read(struct uart_port *port, unsigned int off)
+{
+	return readl(port->membase + off);
+}
+
+static void vt8500_stop_tx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void vt8500_stop_rx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~RX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void vt8500_enable_ms(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier |= TCTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void handle_rx(struct uart_port *port)
+{
+	struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+	if (!tty) {
+		/* Discard data: no tty available */
+		int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8;
+		u16 ch;
+		while (count--)
+			ch = readw(port->membase + VT8500_RXFIFO);
+		return;
+	}
+
+	/*
+	 * Handle overrun
+	 */
+	if ((vt8500_read(port, VT8500_URISR) & RXOVER)) {
+		port->icount.overrun++;
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+	}
+
+	/* and now the main RX loop */
+	while (vt8500_read(port, VT8500_URFIDX) & 0x1f00) {
+		unsigned int c;
+		char flag = TTY_NORMAL;
+
+		c = readw(port->membase + VT8500_RXFIFO) & 0x3ff;
+
+		/* Mask conditions we're ignorning. */
+		c &= ~port->read_status_mask;
+
+		if (c & FER) {
+			port->icount.frame++;
+			flag = TTY_FRAME;
+		} else if (c & PER) {
+			port->icount.parity++;
+			flag = TTY_PARITY;
+		}
+		port->icount.rx++;
+
+		if (!uart_handle_sysrq_char(port, c))
+			tty_insert_flip_char(tty, c, flag);
+	}
+
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static void handle_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		writeb(port->x_char, port->membase + VT8500_TXFIFO);
+		port->icount.tx++;
+		port->x_char = 0;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		vt8500_stop_tx(port);
+		return;
+	}
+
+	while ((vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16) {
+		if (uart_circ_empty(xmit))
+			break;
+
+		writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		vt8500_stop_tx(port);
+}
+
+static void vt8500_start_tx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+	handle_tx(port);
+	vt8500_port->ier |= TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void handle_delta_cts(struct uart_port *port)
+{
+	port->icount.cts++;
+	wake_up_interruptible(&port->state->port.delta_msr_wait);
+}
+
+static irqreturn_t vt8500_irq(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned long isr;
+
+	spin_lock(&port->lock);
+	isr = vt8500_read(port, VT8500_URISR);
+
+	/* Acknowledge active status bits */
+	vt8500_write(port, isr, VT8500_URISR);
+
+	if (isr & RX_FIFO_INTS)
+		handle_rx(port);
+	if (isr & TX_FIFO_INTS)
+		handle_tx(port);
+	if (isr & TCTS)
+		handle_delta_cts(port);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int vt8500_tx_empty(struct uart_port *port)
+{
+	return (vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16 ?
+						TIOCSER_TEMT : 0;
+}
+
+static unsigned int vt8500_get_mctrl(struct uart_port *port)
+{
+	unsigned int usr;
+
+	usr = vt8500_read(port, VT8500_URUSR);
+	if (usr & (1 << 4))
+		return TIOCM_CTS;
+	else
+		return 0;
+}
+
+static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void vt8500_break_ctl(struct uart_port *port, int break_ctl)
+{
+	if (break_ctl)
+		vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9),
+			     VT8500_URLCR);
+}
+
+static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud)
+{
+	unsigned long div;
+	unsigned int loops = 1000;
+
+	div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff);
+
+	if (unlikely((baud < 900) || (baud > 921600)))
+		div |= 7;
+	else
+		div |= (921600 / baud) - 1;
+
+	while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops)
+		cpu_relax();
+	vt8500_write(port, div, VT8500_URDIV);
+
+	return baud;
+}
+
+static int vt8500_startup(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	int ret;
+
+	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
+		 "vt8500_serial%d", port->line);
+
+	ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH,
+			  vt8500_port->name, port);
+	if (unlikely(ret))
+		return ret;
+
+	vt8500_write(port, 0x03, VT8500_URLCR);	/* enable TX & RX */
+
+	return 0;
+}
+
+static void vt8500_shutdown(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+
+	vt8500_port->ier = 0;
+
+	/* disable interrupts and FIFOs */
+	vt8500_write(&vt8500_port->uart, 0, VT8500_URIER);
+	vt8500_write(&vt8500_port->uart, 0x880, VT8500_URFCR);
+	free_irq(port->irq, port);
+}
+
+static void vt8500_set_termios(struct uart_port *port,
+			       struct ktermios *termios,
+			       struct ktermios *old)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	unsigned long flags;
+	unsigned int baud, lcr;
+	unsigned int loops = 1000;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* calculate and set baud rate */
+	baud = uart_get_baud_rate(port, termios, old, 900, 921600);
+	baud = vt8500_set_baud_rate(port, baud);
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+
+	/* calculate parity */
+	lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR);
+	lcr &= ~((1 << 5) | (1 << 4));
+	if (termios->c_cflag & PARENB) {
+		lcr |= (1 << 4);
+		termios->c_cflag &= ~CMSPAR;
+		if (termios->c_cflag & PARODD)
+			lcr |= (1 << 5);
+	}
+
+	/* calculate bits per char */
+	lcr &= ~(1 << 2);
+	switch (termios->c_cflag & CSIZE) {
+	case CS7:
+		break;
+	case CS8:
+	default:
+		lcr |= (1 << 2);
+		termios->c_cflag &= ~CSIZE;
+		termios->c_cflag |= CS8;
+		break;
+	}
+
+	/* calculate stop bits */
+	lcr &= ~(1 << 3);
+	if (termios->c_cflag & CSTOPB)
+		lcr |= (1 << 3);
+
+	/* set parity, bits per char, and stop bit */
+	vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR);
+
+	/* Configure status bits to ignore based on termio flags. */
+	port->read_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->read_status_mask = FER | PER;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/* Reset FIFOs */
+	vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR);
+	while ((vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc)
+							&& --loops)
+		cpu_relax();
+
+	/* Every possible FIFO-related interrupt */
+	vt8500_port->ier = RX_FIFO_INTS | TX_FIFO_INTS;
+
+	/*
+	 * CTS flow control
+	 */
+	if (UART_ENABLE_MS(&vt8500_port->uart, termios->c_cflag))
+		vt8500_port->ier |= TCTS;
+
+	vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR);
+	vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *vt8500_type(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	return vt8500_port->name;
+}
+
+static void vt8500_release_port(struct uart_port *port)
+{
+}
+
+static int vt8500_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void vt8500_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_VT8500;
+}
+
+static int vt8500_verify_port(struct uart_port *port,
+			      struct serial_struct *ser)
+{
+	if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500))
+		return -EINVAL;
+	if (unlikely(port->irq != ser->irq))
+		return -EINVAL;
+	return 0;
+}
+
+static struct vt8500_port *vt8500_uart_ports[4];
+static struct uart_driver vt8500_uart_driver;
+
+#ifdef CONFIG_SERIAL_VT8500_CONSOLE
+
+static inline void wait_for_xmitr(struct uart_port *port)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = vt8500_read(port, VT8500_URFIDX);
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while (status & 0x10);
+}
+
+static void vt8500_console_putchar(struct uart_port *port, int c)
+{
+	wait_for_xmitr(port);
+	writeb(c, port->membase + VT8500_TXFIFO);
+}
+
+static void vt8500_console_write(struct console *co, const char *s,
+			      unsigned int count)
+{
+	struct vt8500_port *vt8500_port = vt8500_uart_ports[co->index];
+	unsigned long ier;
+
+	BUG_ON(co->index < 0 || co->index >= vt8500_uart_driver.nr);
+
+	ier = vt8500_read(&vt8500_port->uart, VT8500_URIER);
+	vt8500_write(&vt8500_port->uart, VT8500_URIER, 0);
+
+	uart_console_write(&vt8500_port->uart, s, count,
+			   vt8500_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and switch back to FIFO
+	 */
+	wait_for_xmitr(&vt8500_port->uart);
+	vt8500_write(&vt8500_port->uart, VT8500_URIER, ier);
+}
+
+static int __init vt8500_console_setup(struct console *co, char *options)
+{
+	struct vt8500_port *vt8500_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (unlikely(co->index >= vt8500_uart_driver.nr || co->index < 0))
+		return -ENXIO;
+
+	vt8500_port = vt8500_uart_ports[co->index];
+
+	if (!vt8500_port)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&vt8500_port->uart,
+				 co, baud, parity, bits, flow);
+}
+
+static struct console vt8500_console = {
+	.name = "ttyS",
+	.write = vt8500_console_write,
+	.device = uart_console_device,
+	.setup = vt8500_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &vt8500_uart_driver,
+};
+
+#define VT8500_CONSOLE	(&vt8500_console)
+
+#else
+#define VT8500_CONSOLE	NULL
+#endif
+
+static struct uart_ops vt8500_uart_pops = {
+	.tx_empty	= vt8500_tx_empty,
+	.set_mctrl	= vt8500_set_mctrl,
+	.get_mctrl	= vt8500_get_mctrl,
+	.stop_tx	= vt8500_stop_tx,
+	.start_tx	= vt8500_start_tx,
+	.stop_rx	= vt8500_stop_rx,
+	.enable_ms	= vt8500_enable_ms,
+	.break_ctl	= vt8500_break_ctl,
+	.startup	= vt8500_startup,
+	.shutdown	= vt8500_shutdown,
+	.set_termios	= vt8500_set_termios,
+	.type		= vt8500_type,
+	.release_port	= vt8500_release_port,
+	.request_port	= vt8500_request_port,
+	.config_port	= vt8500_config_port,
+	.verify_port	= vt8500_verify_port,
+};
+
+static struct uart_driver vt8500_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "vt8500_serial",
+	.dev_name	= "ttyWMT",
+	.cons		= VT8500_CONSOLE,
+};
+
+static int __init vt8500_serial_probe(struct platform_device *pdev)
+{
+	struct vt8500_port *vt8500_port;
+	struct resource *mmres, *irqres;
+	int ret;
+
+	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!mmres || !irqres)
+		return -ENODEV;
+
+	vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
+	if (!vt8500_port)
+		return -ENOMEM;
+
+	vt8500_port->uart.type = PORT_VT8500;
+	vt8500_port->uart.iotype = UPIO_MEM;
+	vt8500_port->uart.mapbase = mmres->start;
+	vt8500_port->uart.irq = irqres->start;
+	vt8500_port->uart.fifosize = 16;
+	vt8500_port->uart.ops = &vt8500_uart_pops;
+	vt8500_port->uart.line = pdev->id;
+	vt8500_port->uart.dev = &pdev->dev;
+	vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+	vt8500_port->uart.uartclk = 24000000;
+
+	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
+		 "VT8500 UART%d", pdev->id);
+
+	vt8500_port->uart.membase = ioremap(mmres->start,
+					    mmres->end - mmres->start + 1);
+	if (!vt8500_port->uart.membase) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	vt8500_uart_ports[pdev->id] = vt8500_port;
+
+	uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
+
+	platform_set_drvdata(pdev, vt8500_port);
+
+	return 0;
+
+err:
+	kfree(vt8500_port);
+	return ret;
+}
+
+static int __devexit vt8500_serial_remove(struct platform_device *pdev)
+{
+	struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
+	kfree(vt8500_port);
+
+	return 0;
+}
+
+static struct platform_driver vt8500_platform_driver = {
+	.probe  = vt8500_serial_probe,
+	.remove = vt8500_serial_remove,
+	.driver = {
+		.name = "vt8500_serial",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init vt8500_serial_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&vt8500_uart_driver);
+	if (unlikely(ret))
+		return ret;
+
+	ret = platform_driver_register(&vt8500_platform_driver);
+
+	if (unlikely(ret))
+		uart_unregister_driver(&vt8500_uart_driver);
+
+	return ret;
+}
+
+static void __exit vt8500_serial_exit(void)
+{
+#ifdef CONFIG_SERIAL_VT8500_CONSOLE
+	unregister_console(&vt8500_console);
+#endif
+	platform_driver_unregister(&vt8500_platform_driver);
+	uart_unregister_driver(&vt8500_uart_driver);
+}
+
+module_init(vt8500_serial_init);
+module_exit(vt8500_serial_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("Driver for vt8500 serial device");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 212eb4c..41603d6 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -199,6 +199,9 @@
 /* TI OMAP-UART */
 #define PORT_OMAP	96
 
+/* VIA VT8500 SoC */
+#define PORT_VT8500	97
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>
-- 
1.7.3.2


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

* Re: [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-07 16:28 ` [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505 Alexey Charkov
@ 2010-11-08  4:17   ` Paul Mundt
  2010-11-08 12:56     ` Alexey Charkov
  2010-11-08 14:14     ` [PATCH 6/6 v3] " Alexey Charkov
  2010-11-08  8:47   ` [PATCH 6/6 v2] " Arnd Bergmann
  1 sibling, 2 replies; 91+ messages in thread
From: Paul Mundt @ 2010-11-08  4:17 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Andrew Morton,
	Guennadi Liakhovetski, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

On Sun, Nov 07, 2010 at 07:28:57PM +0300, Alexey Charkov wrote:
> +static int __devinit vt8500lcd_probe(struct platform_device *pdev)
> +{

...

> +	addr = fbi;
> +	addr = addr + sizeof(struct vt8500lcd_info);
> +	fbi->fb.pseudo_palette	= addr;
> +
...

> +	fbi->palette_size	= PAGE_ALIGN(512);
> +	fbi->palette_cpu	= dma_alloc_coherent(&pdev->dev,
> +						     fbi->palette_size,
> +						     &fbi->palette_phys,
> +						     GFP_KERNEL);
> +	if (fbi->fb.pseudo_palette == NULL) {
> +		dev_err(&pdev->dev, "Failed to allocate palette buffer\n");
> +		ret = -ENOMEM;
> +		goto failed_free_io;
> +	}
> +
This looks like a bogus test, you've already allocated enough space for
the pseudo_palette and will have bailed out on the kmalloc() failing well
before this. You also don't have any error handling for fbi->palette_cpu,
which is presumably what you intended to do here.

> +static int __devexit vt8500lcd_remove(struct platform_device *pdev)
> +{
> +	struct vt8500lcd_info *fbi = platform_get_drvdata(pdev);
> +	struct resource *res;
> +	int irq;
> +
> +	if (!fbi)
> +		return 0;
> +
> +	unregister_framebuffer(&fbi->fb);
> +
> +	writel(0, fbi->regbase);
> +
> +	if (fbi->fb.cmap.len)
> +		fb_dealloc_cmap(&fbi->fb.cmap);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	free_irq(irq, fbi);
> +
> +	iounmap(fbi->regbase);
> +

You're also missing a dma_free_coherent() here.

> +static int __devinit wm8505fb_probe(struct platform_device *pdev)
> +{
> +	fbi->fb.screen_base	= pdata->video_mem_virt;
> +	fbi->fb.screen_size	= pdata->video_mem_len;
> +
...

> +failed_free_mem:
> +	free_pages_exact(fbi->fb.screen_base, fbi->fb.screen_size);

...

What in the name of all that is holy are you doing here? If you need to
have your platform deal with virtual address allocation and freeing then
you should pass in callbacks for that and hide the instrumentation
details there. Presently this is tying you down to an alloc_pages_exact()
interface buried in the board setup, which isn't going to mesh well with
other platforms that may wish to go about this an alternate way (like
memblock reservations).

> diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
> new file mode 100644
> index 0000000..c71f97e
> --- /dev/null
> +++ b/drivers/video/wmt_ge_rops.c
> +void __iomem *regbase;
> +
Uhm, no. If this is only used in this driver then just make it static.
Given that you are using the driver model here though and could
theoretically support multiple rop engines, you're much better off making
this private data and burying it under the appropriate per-device data
structures.

> +int wmt_ge_sync(struct fb_info *p)
> +{
> +	while (readl(regbase + GE_STATUS_OFF) & 4)
> +		/* busy wait */;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(wmt_ge_sync);
> +
While I admire your optimism in your hardware, experience suggests you
really want a timeout here. You may also wish to insert a cpu_relax()
here.

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

* Re: [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-07 16:28 ` [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505 Alexey Charkov
  2010-11-08  4:17   ` Paul Mundt
@ 2010-11-08  8:47   ` Arnd Bergmann
  2010-11-09 10:23     ` Alexey Charkov
  1 sibling, 1 reply; 91+ messages in thread
From: Arnd Bergmann @ 2010-11-08  8:47 UTC (permalink / raw)
  To: vt8500-wm8505-linux-kernel
  Cc: Alexey Charkov, linux-arm-kernel, Andrew Morton,
	Guennadi Liakhovetski, Paul Mundt, Florian Tobias Schandinat,
	Ralf Baechle, David S. Miller, linux-kernel

On Sunday 07 November 2010, Alexey Charkov wrote:
>  drivers/video/Kconfig         |   26 +++
>  drivers/video/Makefile        |    3 +
>  drivers/video/vt8500lcdfb.c   |  452 +++++++++++++++++++++++++++++++++++++++++
>  drivers/video/vt8500lcdfb.h   |   34 +++
>  drivers/video/wm8505fb.c      |  438 +++++++++++++++++++++++++++++++++++++++
>  drivers/video/wm8505fb_regs.h |   76 +++++++
>  drivers/video/wmt_ge_rops.c   |  186 +++++++++++++++++
>  drivers/video/wmt_ge_rops.h   |    5 +

>From a very brief look, the two drivers look rather similar. What is the
reason to have separate drivers instead of just one?

Could you perhaps take the common parts and move them into a third module
that exports symbols to be used by the two drivers?

	Arnd


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

* Re: [PATCH 2/6 v3] serial: Add support for UART on VIA VT8500 and compatibles
  2010-11-08  0:58     ` [PATCH 2/6 v3] " Alexey Charkov
@ 2010-11-08 10:46       ` Alan Cox
  2010-11-08 17:33         ` [PATCH 2/6 v4] " Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Alan Cox @ 2010-11-08 10:46 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Greg Kroah-Hartman,
	Ben Dooks, Kukjin Kim, Feng Tang, Tobias Klauser, linux-kernel


> +static struct console vt8500_console = {
> +	.name = "ttyS",

You missed a ttyS

Otherwise looks great.

Alan

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

* Re: [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08  4:17   ` Paul Mundt
@ 2010-11-08 12:56     ` Alexey Charkov
  2010-11-08 14:14     ` [PATCH 6/6 v3] " Alexey Charkov
  1 sibling, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-08 12:56 UTC (permalink / raw)
  To: Paul Mundt
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Andrew Morton,
	Guennadi Liakhovetski, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

2010/11/8 Paul Mundt <lethal@linux-sh.org>:
> On Sun, Nov 07, 2010 at 07:28:57PM +0300, Alexey Charkov wrote:
>> +static int __devinit vt8500lcd_probe(struct platform_device *pdev)
>> +{
>
> ...
>
>> +     addr = fbi;
>> +     addr = addr + sizeof(struct vt8500lcd_info);
>> +     fbi->fb.pseudo_palette  = addr;
>> +
> ...
>
>> +     fbi->palette_size       = PAGE_ALIGN(512);
>> +     fbi->palette_cpu        = dma_alloc_coherent(&pdev->dev,
>> +                                                  fbi->palette_size,
>> +                                                  &fbi->palette_phys,
>> +                                                  GFP_KERNEL);
>> +     if (fbi->fb.pseudo_palette == NULL) {
>> +             dev_err(&pdev->dev, "Failed to allocate palette buffer\n");
>> +             ret = -ENOMEM;
>> +             goto failed_free_io;
>> +     }
>> +
> This looks like a bogus test, you've already allocated enough space for
> the pseudo_palette and will have bailed out on the kmalloc() failing well
> before this. You also don't have any error handling for fbi->palette_cpu,
> which is presumably what you intended to do here.
>

True, this has to be corrected (old copy-paste error left unnoticed
somehow). It's also deallocated wrongly in the error path, which I
have just noticed.

>> +static int __devexit vt8500lcd_remove(struct platform_device *pdev)
>> +{
>> +     struct vt8500lcd_info *fbi = platform_get_drvdata(pdev);
>> +     struct resource *res;
>> +     int irq;
>> +
>> +     if (!fbi)
>> +             return 0;
>> +
>> +     unregister_framebuffer(&fbi->fb);
>> +
>> +     writel(0, fbi->regbase);
>> +
>> +     if (fbi->fb.cmap.len)
>> +             fb_dealloc_cmap(&fbi->fb.cmap);
>> +
>> +     irq = platform_get_irq(pdev, 0);
>> +     free_irq(irq, fbi);
>> +
>> +     iounmap(fbi->regbase);
>> +
>
> You're also missing a dma_free_coherent() here.
>

True, will be fixed.

>> +static int __devinit wm8505fb_probe(struct platform_device *pdev)
>> +{
>> +     fbi->fb.screen_base     = pdata->video_mem_virt;
>> +     fbi->fb.screen_size     = pdata->video_mem_len;
>> +
> ...
>
>> +failed_free_mem:
>> +     free_pages_exact(fbi->fb.screen_base, fbi->fb.screen_size);
>
> ...
>
> What in the name of all that is holy are you doing here? If you need to
> have your platform deal with virtual address allocation and freeing then
> you should pass in callbacks for that and hide the instrumentation
> details there. Presently this is tying you down to an alloc_pages_exact()
> interface buried in the board setup, which isn't going to mesh well with
> other platforms that may wish to go about this an alternate way (like
> memblock reservations).
>

Actually, this is a leftover from a previous implementation with
alloc_pages_exact(), which could not work for larger screen sizes (due
to the framebuffer growing over 4MB). Now memory is reserved via
memblock, so this should have probably been just dropped.

>> diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
>> new file mode 100644
>> index 0000000..c71f97e
>> --- /dev/null
>> +++ b/drivers/video/wmt_ge_rops.c
>> +void __iomem *regbase;
>> +
> Uhm, no. If this is only used in this driver then just make it static.
> Given that you are using the driver model here though and could
> theoretically support multiple rop engines, you're much better off making
> this private data and burying it under the appropriate per-device data
> structures.
>

Is that platform_{set,get}_drvdata() what you are talking about? Using
multiple engines seems extremely unlikely, though :)

>> +int wmt_ge_sync(struct fb_info *p)
>> +{
>> +     while (readl(regbase + GE_STATUS_OFF) & 4)
>> +             /* busy wait */;
>> +
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(wmt_ge_sync);
>> +
> While I admire your optimism in your hardware, experience suggests you
> really want a timeout here. You may also wish to insert a cpu_relax()
> here.
>

I wonder if this callback is allowed to sleep? In principle, the
hardware seems to support interrupt generation on operation
completion, so maybe wait_event_interruptible_timeout() could be used
here to remove the busy wait altogether?

Thank you for the comments, Paul!

Best regards,
Alexey

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

* [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08  4:17   ` Paul Mundt
  2010-11-08 12:56     ` Alexey Charkov
@ 2010-11-08 14:14     ` Alexey Charkov
  2010-11-08 20:43       ` Paul Mundt
  1 sibling, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-08 14:14 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Andrew Morton, Guennadi Liakhovetski, Florian Tobias Schandinat,
	Ralf Baechle, David S. Miller, linux-kernel

This adds drivers for the LCD controller found in VIA VT8500 SoC,
GOVR display controller found in WonderMedia WM8505 SoC and for the
Graphics Engine present in both of them that provides hardware
accelerated raster operations (used for copyarea and fillrect).

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This incorporates fixes to the issues that Paul has identified.
MMIO register pointer in wmt_ge_rops was just made static, as I
could not find any immediately obvious way to pass drvdata around
(the whole functionality of this driver is in exported functions
that are called from a display driver context, which does not know
about the rop engine specific data structures).

fb_sync callback has just received a (pretty large) timeout. This
seems to work fine in my use cases (fbcon on 800x480 panel).

 drivers/video/Kconfig         |   26 +++
 drivers/video/Makefile        |    3 +
 drivers/video/vt8500lcdfb.c   |  455 +++++++++++++++++++++++++++++++++++++++++
 drivers/video/vt8500lcdfb.h   |   34 +++
 drivers/video/wm8505fb.c      |  434 +++++++++++++++++++++++++++++++++++++++
 drivers/video/wm8505fb_regs.h |   76 +++++++
 drivers/video/wmt_ge_rops.c   |  186 +++++++++++++++++
 drivers/video/wmt_ge_rops.h   |    5 +
 8 files changed, 1219 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/vt8500lcdfb.c
 create mode 100644 drivers/video/vt8500lcdfb.h
 create mode 100644 drivers/video/wm8505fb.c
 create mode 100644 drivers/video/wm8505fb_regs.h
 create mode 100644 drivers/video/wmt_ge_rops.c
 create mode 100644 drivers/video/wmt_ge_rops.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 27c1fb4..954f6e9 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -186,6 +186,14 @@ config FB_SYS_FOPS
        depends on FB
        default n
 
+config FB_WMT_GE_ROPS
+	tristate
+	depends on FB
+	default n
+	---help---
+	  Include functions for accelerated rectangle filling and area
+	  copying using WonderMedia Graphics Engine operations.
+
 config FB_DEFERRED_IO
 	bool
 	depends on FB
@@ -1722,6 +1730,24 @@ config FB_AU1200
 	  various panels and CRTs by passing in kernel cmd line option
 	  au1200fb:panel=<name>.
 
+config FB_VT8500
+	bool "VT8500 LCD Driver"
+	depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500
+	select FB_WMT_GE_ROPS
+	select FB_SYS_IMAGEBLIT
+	help
+	  This is the framebuffer driver for VIA VT8500 integrated LCD
+	  controller.
+
+config FB_WM8505
+	bool "WM8505 frame buffer support"
+	depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505
+	select FB_WMT_GE_ROPS
+	select FB_SYS_IMAGEBLIT
+	help
+	  This is the framebuffer driver for WonderMedia WM8505
+	  integrated LCD controller.
+
 source "drivers/video/geode/Kconfig"
 
 config FB_HIT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 485e8ed..8d916dc 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB)       += svgalib.o
 obj-$(CONFIG_FB_MACMODES)      += macmodes.o
 obj-$(CONFIG_FB_DDC)           += fb_ddc.o
 obj-$(CONFIG_FB_DEFERRED_IO)   += fb_defio.o
+obj-$(CONFIG_FB_WMT_GE_ROPS)   += wmt_ge_rops.o
 
 # Hardware specific drivers go first
 obj-$(CONFIG_FB_AMIGA)            += amifb.o c2p_planar.o
@@ -104,6 +105,8 @@ obj-$(CONFIG_FB_W100)		  += w100fb.o
 obj-$(CONFIG_FB_TMIO)		  += tmiofb.o
 obj-$(CONFIG_FB_AU1100)		  += au1100fb.o
 obj-$(CONFIG_FB_AU1200)		  += au1200fb.o
+obj-$(CONFIG_FB_VT8500)		  += vt8500lcdfb.o
+obj-$(CONFIG_FB_WM8505)		  += wm8505fb.o
 obj-$(CONFIG_FB_PMAG_AA)	  += pmag-aa-fb.o
 obj-$(CONFIG_FB_PMAG_BA)	  += pmag-ba-fb.o
 obj-$(CONFIG_FB_PMAGB_B)	  += pmagb-b-fb.o
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
new file mode 100644
index 0000000..640d8a3
--- /dev/null
+++ b/drivers/video/vt8500lcdfb.c
@@ -0,0 +1,455 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on skeletonfb.c and pxafb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <asm/irq.h>
+
+#include <mach/vt8500fb.h>
+
+#include "vt8500lcdfb.h"
+#include "wmt_ge_rops.h"
+
+static int vt8500lcd_set_par(struct fb_info *info)
+{
+	struct vt8500lcd_info *fbi = container_of(info,
+						  struct vt8500lcd_info,
+						  fb);
+	int reg_bpp = 5; /* 16bpp */
+	int i;
+	unsigned long control0;
+
+	if (!fbi)
+		return -EINVAL;
+
+	if (info->var.bits_per_pixel <= 8) {
+		/* palettized */
+		info->var.red.offset    = 0;
+		info->var.red.length    = info->var.bits_per_pixel;
+		info->var.red.msb_right = 0;
+
+		info->var.green.offset  = 0;
+		info->var.green.length  = info->var.bits_per_pixel;
+		info->var.green.msb_right = 0;
+
+		info->var.blue.offset   = 0;
+		info->var.blue.length   = info->var.bits_per_pixel;
+		info->var.blue.msb_right = 0;
+
+		info->var.transp.offset = 0;
+		info->var.transp.length = 0;
+		info->var.transp.msb_right = 0;
+
+		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+		info->fix.line_length = info->var.xres_virtual /
+						(8/info->var.bits_per_pixel);
+	} else {
+		/* non-palettized */
+		info->var.transp.offset = 0;
+		info->var.transp.length = 0;
+		info->var.transp.msb_right = 0;
+
+		if (info->var.bits_per_pixel == 16) {
+			/* RGB565 */
+			info->var.red.offset = 11;
+			info->var.red.length = 5;
+			info->var.red.msb_right = 0;
+			info->var.green.offset = 5;
+			info->var.green.length = 6;
+			info->var.green.msb_right = 0;
+			info->var.blue.offset = 0;
+			info->var.blue.length = 5;
+			info->var.blue.msb_right = 0;
+		} else {
+			/* Equal depths per channel */
+			info->var.red.offset = info->var.bits_per_pixel
+							* 2 / 3;
+			info->var.red.length = info->var.bits_per_pixel / 3;
+			info->var.red.msb_right = 0;
+			info->var.green.offset = info->var.bits_per_pixel / 3;
+			info->var.green.length = info->var.bits_per_pixel / 3;
+			info->var.green.msb_right = 0;
+			info->var.blue.offset = 0;
+			info->var.blue.length = info->var.bits_per_pixel / 3;
+			info->var.blue.msb_right = 0;
+		}
+
+		info->fix.visual = FB_VISUAL_TRUECOLOR;
+		info->fix.line_length = info->var.bits_per_pixel > 16 ?
+					info->var.xres_virtual << 2 :
+					info->var.xres_virtual << 1;
+	}
+
+	for (i = 0; i < 8; i++) {
+		if (bpp_values[i] == info->var.bits_per_pixel) {
+			reg_bpp = i;
+			continue;
+		}
+	}
+
+	control0 = readl(fbi->regbase) & ~0xf;
+	writel(0, fbi->regbase);
+	while (readl(fbi->regbase + 0x38) & 0x10)
+		/* wait */;
+	writel((((info->var.hsync_len - 1) & 0x3f) << 26)
+		| ((info->var.left_margin & 0xff) << 18)
+		| (((info->var.xres - 1) & 0x3ff) << 8)
+		| (info->var.right_margin & 0xff), fbi->regbase + 0x4);
+	writel((((info->var.vsync_len - 1) & 0x3f) << 26)
+		| ((info->var.upper_margin & 0xff) << 18)
+		| (((info->var.yres - 1) & 0x3ff) << 8)
+		| (info->var.lower_margin & 0xff), fbi->regbase + 0x8);
+	writel((((info->var.yres - 1) & 0x400) << 2)
+		| ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10);
+	writel(0x80000000, fbi->regbase + 0x20);
+	writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase);
+
+	return 0;
+}
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   struct fb_info *info) {
+	struct vt8500lcd_info *fbi = container_of(info,
+						  struct vt8500lcd_info,
+						  fb);
+	int ret = 1;
+	unsigned int val;
+	if (regno >= 256)
+		return -EINVAL;
+
+	if (info->var.grayscale)
+		red = green = blue =
+			(19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+	switch (fbi->fb.fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+		if (regno < 16) {
+			u32 *pal = fbi->fb.pseudo_palette;
+
+			val  = chan_to_field(red, &fbi->fb.var.red);
+			val |= chan_to_field(green, &fbi->fb.var.green);
+			val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+			pal[regno] = val;
+			ret = 0;
+		}
+		break;
+
+	case FB_VISUAL_STATIC_PSEUDOCOLOR:
+	case FB_VISUAL_PSEUDOCOLOR:
+		writew((red & 0xf800)
+		      | ((green >> 5) & 0x7e0)
+		      | ((blue >> 11) & 0x1f),
+		       fbi->palette_cpu + sizeof(u16) * regno);
+		break;
+	}
+
+	return ret;
+}
+
+static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd,
+			 unsigned long arg)
+{
+	int ret = 0;
+	struct vt8500lcd_info *fbi = container_of(info,
+						  struct vt8500lcd_info,
+						  fb);
+
+	if (cmd == FBIO_WAITFORVSYNC) {
+		/* Unmask End of Frame interrupt */
+		writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c);
+		ret = wait_event_interruptible_timeout(fbi->wait,
+			readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10);
+		/* Mask back to reduce unwanted interrupt traffic */
+		writel(0xffffffff, fbi->regbase + 0x3c);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			return -ETIMEDOUT;
+	}
+
+	return ret;
+}
+
+static int vt8500lcd_pan_display(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	unsigned pixlen = info->fix.line_length / info->var.xres_virtual;
+	unsigned off = pixlen * var->xoffset
+		      + info->fix.line_length * var->yoffset;
+	struct vt8500lcd_info *fbi = container_of(info,
+						  struct vt8500lcd_info,
+						  fb);
+	writel((1 << 31)
+		| (((var->xres_virtual - var->xres) * pixlen / 4) << 20)
+		| (off >> 2), fbi->regbase + 0x20);
+	return 0;
+}
+
+static struct fb_ops vt8500lcd_ops = {
+	.owner		= THIS_MODULE,
+	.fb_set_par	= vt8500lcd_set_par,
+	.fb_setcolreg	= vt8500lcd_setcolreg,
+	.fb_fillrect	= wmt_ge_fillrect,
+	.fb_copyarea	= wmt_ge_copyarea,
+	.fb_imageblit	= sys_imageblit,
+	.fb_sync	= wmt_ge_sync,
+	.fb_ioctl	= vt8500lcd_ioctl,
+	.fb_pan_display	= vt8500lcd_pan_display,
+};
+
+static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id)
+{
+	struct vt8500lcd_info *fbi = dev_id;
+
+	if (readl(fbi->regbase + 0x38) & (1 << 3))
+		wake_up_interruptible(&fbi->wait);
+
+	writel(0xffffffff, fbi->regbase + 0x38);
+	return IRQ_HANDLED;
+}
+
+static int __devinit vt8500lcd_probe(struct platform_device *pdev)
+{
+	struct vt8500lcd_info *fbi;
+	struct resource *res;
+	struct vt8500fb_platform_data *pdata = pdev->dev.platform_data;
+	void *addr;
+	int irq, ret;
+
+	ret = -ENOMEM;
+	fbi = NULL;
+
+	fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16,
+							GFP_KERNEL);
+	if (!fbi) {
+		dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	strcpy(fbi->fb.fix.id, "VT8500 LCD");
+
+	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
+	fbi->fb.fix.xpanstep	= 0;
+	fbi->fb.fix.ypanstep	= 1;
+	fbi->fb.fix.ywrapstep	= 0;
+	fbi->fb.fix.accel	= FB_ACCEL_NONE;
+
+	fbi->fb.var.nonstd	= 0;
+	fbi->fb.var.activate	= FB_ACTIVATE_NOW;
+	fbi->fb.var.height	= -1;
+	fbi->fb.var.width	= -1;
+	fbi->fb.var.vmode	= FB_VMODE_NONINTERLACED;
+
+	fbi->fb.fbops		= &vt8500lcd_ops;
+	fbi->fb.flags		= FBINFO_DEFAULT
+				| FBINFO_HWACCEL_COPYAREA
+				| FBINFO_HWACCEL_FILLRECT
+				| FBINFO_HWACCEL_YPAN
+				| FBINFO_VIRTFB
+				| FBINFO_PARTIAL_PAN_OK;
+	fbi->fb.node		= -1;
+
+	addr = fbi;
+	addr = addr + sizeof(struct vt8500lcd_info);
+	fbi->fb.pseudo_palette	= addr;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		ret = -ENODEV;
+		goto failed_fbi;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), "vt8500lcd");
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		ret = -EBUSY;
+		goto failed_fbi;
+	}
+
+	fbi->regbase = ioremap(res->start, resource_size(res));
+	if (fbi->regbase == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		ret = -EBUSY;
+		goto failed_free_res;
+	}
+
+	fbi->fb.fix.smem_start	= pdata->video_mem_phys;
+	fbi->fb.fix.smem_len	= pdata->video_mem_len;
+	fbi->fb.screen_base	= pdata->video_mem_virt;
+
+	fbi->palette_size	= PAGE_ALIGN(512);
+	fbi->palette_cpu	= dma_alloc_coherent(&pdev->dev,
+						     fbi->palette_size,
+						     &fbi->palette_phys,
+						     GFP_KERNEL);
+	if (fbi->palette_cpu == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate palette buffer\n");
+		ret = -ENOMEM;
+		goto failed_free_io;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no IRQ defined\n");
+		ret = -ENODEV;
+		goto failed_free_palette;
+	}
+
+	ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi);
+	if (ret) {
+		dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
+		ret = -EBUSY;
+		goto failed_free_palette;
+	}
+
+	init_waitqueue_head(&fbi->wait);
+
+	if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+		dev_err(&pdev->dev, "Failed to allocate color map\n");
+		ret = -ENOMEM;
+		goto failed_free_irq;
+	}
+
+	fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+	fbi->fb.var.bits_per_pixel	= pdata->bpp;
+	fbi->fb.var.xres_virtual	= pdata->xres_virtual;
+	fbi->fb.var.yres_virtual	= pdata->yres_virtual;
+
+	ret = vt8500lcd_set_par(&fbi->fb);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to set parameters\n");
+		goto failed_free_cmap;
+	}
+
+	writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c);
+	writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18);
+
+	platform_set_drvdata(pdev, fbi);
+
+	ret = register_framebuffer(&fbi->fb);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Failed to register framebuffer device: %d\n", ret);
+		goto failed_free_cmap;
+	}
+
+	/*
+	 * Ok, now enable the LCD controller
+	 */
+	writel(readl(fbi->regbase) | 1, fbi->regbase);
+
+	return 0;
+
+failed_free_cmap:
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_irq:
+	free_irq(irq, fbi);
+failed_free_palette:
+	dma_free_coherent(&pdev->dev, fbi->palette_size,
+			  fbi->palette_cpu, fbi->palette_phys);
+failed_free_io:
+	iounmap(fbi->regbase);
+failed_free_res:
+	release_mem_region(res->start, resource_size(res));
+failed_fbi:
+	platform_set_drvdata(pdev, NULL);
+	kfree(fbi);
+failed:
+	return ret;
+}
+
+static int __devexit vt8500lcd_remove(struct platform_device *pdev)
+{
+	struct vt8500lcd_info *fbi = platform_get_drvdata(pdev);
+	struct resource *res;
+	int irq;
+
+	if (!fbi)
+		return 0;
+
+	unregister_framebuffer(&fbi->fb);
+
+	writel(0, fbi->regbase);
+
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+
+	irq = platform_get_irq(pdev, 0);
+	free_irq(irq, fbi);
+
+	dma_free_coherent(&pdev->dev, fbi->palette_size,
+			  fbi->palette_cpu, fbi->palette_phys);
+
+	iounmap(fbi->regbase);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(fbi);
+
+	return 0;
+}
+
+static struct platform_driver vt8500lcd_driver = {
+	.probe		= vt8500lcd_probe,
+	.remove		= vt8500lcd_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "vt8500-lcd",
+	},
+};
+
+static int __init vt8500lcd_init(void)
+{
+	return platform_driver_register(&vt8500lcd_driver);
+}
+
+static void __exit vt8500lcd_exit(void)
+{
+	platform_driver_unregister(&vt8500lcd_driver);
+}
+
+module_init(vt8500lcd_init);
+module_exit(vt8500lcd_exit);
+
+MODULE_DESCRIPTION("LCD controller driver for VIA VT8500");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/vt8500lcdfb.h b/drivers/video/vt8500lcdfb.h
new file mode 100644
index 0000000..36ca3ca
--- /dev/null
+++ b/drivers/video/vt8500lcdfb.h
@@ -0,0 +1,34 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+struct vt8500lcd_info {
+	struct fb_info		fb;
+	void __iomem		*regbase;
+	void __iomem		*palette_cpu;
+	dma_addr_t		palette_phys;
+	size_t			palette_size;
+	wait_queue_head_t	wait;
+};
+
+static int bpp_values[] = {
+	1,
+	2,
+	4,
+	8,
+	12,
+	16,
+	18,
+	24,
+};
diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c
new file mode 100644
index 0000000..560c926
--- /dev/null
+++ b/drivers/video/wm8505fb.c
@@ -0,0 +1,434 @@
+/*
+ *  WonderMedia WM8505 Frame Buffer device driver
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *    Based on vt8500lcdfb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <asm/irq.h>
+
+#include <mach/vt8500fb.h>
+
+#include "wm8505fb_regs.h"
+#include "wmt_ge_rops.h"
+
+#define DRIVER_NAME "wm8505-fb"
+
+struct wm8505fb_info {
+	struct fb_info		fb;
+	void __iomem		*regbase;
+	unsigned int		contrast;
+};
+
+
+static int wm8505fb_init_hw(struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	int i;
+
+	/* I know the purpose only of few registers, so clear unknown */
+	for (i = 0; i < 0x200; i += 4)
+		writel(0, fbi->regbase + i);
+
+	/* Set frame buffer address */
+	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
+	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
+
+	/* Set in-memory picture format to RGB 32bpp */
+	writel(0x1c,		       fbi->regbase + WMT_GOVR_COLORSPACE);
+	writel(1,		       fbi->regbase + WMT_GOVR_COLORSPACE1);
+
+	/* Virtual buffer size */
+	writel(info->var.xres,	       fbi->regbase + WMT_GOVR_XRES);
+	writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL);
+
+	/* black magic ;) */
+	writel(0xf,		       fbi->regbase + WMT_GOVR_FHI);
+	writel(4,		       fbi->regbase + WMT_GOVR_DVO_SET);
+	writel(1,		       fbi->regbase + WMT_GOVR_MIF_ENABLE);
+	writel(1,		       fbi->regbase + WMT_GOVR_REG_UPDATE);
+
+	return 0;
+}
+
+static int wm8505fb_set_timing(struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	int h_start = info->var.left_margin;
+	int h_end = h_start + info->var.xres;
+	int h_all = h_end + info->var.right_margin;
+	int h_sync = info->var.hsync_len;
+
+	int v_start = info->var.upper_margin;
+	int v_end = v_start + info->var.yres;
+	int v_all = v_end + info->var.lower_margin;
+	int v_sync = info->var.vsync_len + 1;
+
+	writel(0, fbi->regbase + WMT_GOVR_TG);
+
+	writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START);
+	writel(h_end,   fbi->regbase + WMT_GOVR_TIMING_H_END);
+	writel(h_all,   fbi->regbase + WMT_GOVR_TIMING_H_ALL);
+	writel(h_sync,  fbi->regbase + WMT_GOVR_TIMING_H_SYNC);
+
+	writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START);
+	writel(v_end,   fbi->regbase + WMT_GOVR_TIMING_V_END);
+	writel(v_all,   fbi->regbase + WMT_GOVR_TIMING_V_ALL);
+	writel(v_sync,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+
+	writel(1, fbi->regbase + WMT_GOVR_TG);
+
+	return 0;
+}
+
+
+static int wm8505fb_set_par(struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	if (!fbi)
+		return -EINVAL;
+
+	if (info->var.bits_per_pixel == 32) {
+		info->var.red.offset = 16;
+		info->var.red.length = 8;
+		info->var.red.msb_right = 0;
+		info->var.green.offset = 8;
+		info->var.green.length = 8;
+		info->var.green.msb_right = 0;
+		info->var.blue.offset = 0;
+		info->var.blue.length = 8;
+		info->var.blue.msb_right = 0;
+		info->fix.visual = FB_VISUAL_TRUECOLOR;
+		info->fix.line_length = info->var.xres_virtual << 2;
+	}
+
+	wm8505fb_set_timing(info);
+
+	writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast,
+		fbi->regbase + WMT_GOVR_CONTRAST);
+
+	return 0;
+}
+
+static ssize_t contrast_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	return sprintf(buf, "%d\n", fbi->contrast);
+}
+
+static ssize_t contrast_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	fbi->contrast = strict_strtoul(buf, NULL, 10) & 0xff;
+	wm8505fb_set_par(info);
+
+	return count;
+}
+
+static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store);
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   struct fb_info *info) {
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+	int ret = 1;
+	unsigned int val;
+	if (regno >= 256)
+		return -EINVAL;
+
+	if (info->var.grayscale)
+		red = green = blue =
+			(19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+	switch (fbi->fb.fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+		if (regno < 16) {
+			u32 *pal = info->pseudo_palette;
+
+			val  = chan_to_field(red, &fbi->fb.var.red);
+			val |= chan_to_field(green, &fbi->fb.var.green);
+			val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+			pal[regno] = val;
+			ret = 0;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN);
+	writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN);
+	return 0;
+}
+
+static int wm8505fb_blank(int blank, struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = container_of(info,
+						  struct wm8505fb_info,
+						  fb);
+
+	switch (blank) {
+	case FB_BLANK_UNBLANK:
+		wm8505fb_set_timing(info);
+		break;
+	default:
+		writel(0,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+		break;
+	}
+
+	return 0;
+}
+
+static struct fb_ops wm8505fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_set_par	= wm8505fb_set_par,
+	.fb_setcolreg	= wm8505fb_setcolreg,
+	.fb_fillrect	= wmt_ge_fillrect,
+	.fb_copyarea	= wmt_ge_copyarea,
+	.fb_imageblit	= sys_imageblit,
+	.fb_sync	= wmt_ge_sync,
+	.fb_pan_display	= wm8505fb_pan_display,
+	.fb_blank	= wm8505fb_blank,
+};
+
+static int __devinit wm8505fb_probe(struct platform_device *pdev)
+{
+	struct wm8505fb_info	*fbi;
+	struct resource		*res;
+	void			*addr;
+	struct vt8500fb_platform_data *pdata;
+	int ret;
+
+	pdata = pdev->dev.platform_data;
+
+	ret = -ENOMEM;
+	fbi = NULL;
+
+	fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16,
+							GFP_KERNEL);
+	if (!fbi) {
+		dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	strcpy(fbi->fb.fix.id, DRIVER_NAME);
+
+	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
+	fbi->fb.fix.xpanstep	= 1;
+	fbi->fb.fix.ypanstep	= 1;
+	fbi->fb.fix.ywrapstep	= 0;
+	fbi->fb.fix.accel	= FB_ACCEL_NONE;
+
+	fbi->fb.fbops		= &wm8505fb_ops;
+	fbi->fb.flags		= FBINFO_DEFAULT
+				| FBINFO_HWACCEL_COPYAREA
+				| FBINFO_HWACCEL_FILLRECT
+				| FBINFO_HWACCEL_XPAN
+				| FBINFO_HWACCEL_YPAN
+				| FBINFO_VIRTFB
+				| FBINFO_PARTIAL_PAN_OK;
+	fbi->fb.node		= -1;
+
+	addr = fbi;
+	addr = addr + sizeof(struct wm8505fb_info);
+	fbi->fb.pseudo_palette	= addr;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		ret = -ENODEV;
+		goto failed_fbi;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), "wm8505fb");
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		ret = -EBUSY;
+		goto failed_fbi;
+	}
+
+	fbi->regbase = ioremap(res->start, resource_size(res));
+	if (fbi->regbase == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		ret = -EBUSY;
+		goto failed_free_res;
+	}
+
+	fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+
+	fbi->fb.var.nonstd		= 0;
+	fbi->fb.var.activate		= FB_ACTIVATE_NOW;
+
+	fbi->fb.var.height		= -1;
+	fbi->fb.var.width		= -1;
+	fbi->fb.var.xres_virtual	= pdata->xres_virtual;
+	fbi->fb.var.yres_virtual	= pdata->yres_virtual;
+	fbi->fb.var.bits_per_pixel	= pdata->bpp;
+
+	fbi->fb.fix.smem_start	= pdata->video_mem_phys;
+	fbi->fb.fix.smem_len	= pdata->video_mem_len;
+	fbi->fb.screen_base	= pdata->video_mem_virt;
+	fbi->fb.screen_size	= pdata->video_mem_len;
+
+	if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+		dev_err(&pdev->dev, "Failed to allocate color map\n");
+		ret = -ENOMEM;
+		goto failed_free_io;
+	}
+
+	wm8505fb_init_hw(&fbi->fb);
+
+	fbi->contrast = 0x80;
+	ret = wm8505fb_set_par(&fbi->fb);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to set parameters\n");
+		goto failed_free_cmap;
+	}
+
+	platform_set_drvdata(pdev, fbi);
+
+	ret = register_framebuffer(&fbi->fb);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Failed to register framebuffer device: %d\n", ret);
+		goto failed_free_cmap;
+	}
+
+	ret = device_create_file(&pdev->dev, &dev_attr_contrast);
+	if (ret < 0) {
+		printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
+			fbi->fb.node, ret);
+	}
+
+	printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n",
+	       fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start,
+	       fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1);
+
+	return 0;
+
+failed_free_cmap:
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_io:
+	iounmap(fbi->regbase);
+failed_free_res:
+	release_mem_region(res->start, resource_size(res));
+failed_fbi:
+	platform_set_drvdata(pdev, NULL);
+	kfree(fbi);
+failed:
+	return ret;
+}
+
+static int __devexit wm8505fb_remove(struct platform_device *pdev)
+{
+	struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	if (!fbi)
+		return 0;
+
+	unregister_framebuffer(&fbi->fb);
+
+	writel(0, fbi->regbase);
+
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+
+	iounmap(fbi->regbase);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(fbi);
+
+	return 0;
+}
+
+static struct platform_driver wm8505fb_driver = {
+	.probe		= wm8505fb_probe,
+	.remove		= wm8505fb_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= DRIVER_NAME,
+	},
+};
+
+static int __init wm8505fb_init(void)
+{
+	return platform_driver_register(&wm8505fb_driver);
+}
+
+static void __exit wm8505fb_exit(void)
+{
+	platform_driver_unregister(&wm8505fb_driver);
+}
+
+module_init(wm8505fb_init);
+module_exit(wm8505fb_exit);
+
+MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wm8505fb_regs.h b/drivers/video/wm8505fb_regs.h
new file mode 100644
index 0000000..4dd4166
--- /dev/null
+++ b/drivers/video/wm8505fb_regs.h
@@ -0,0 +1,76 @@
+/*
+ *  GOVR registers list for WM8505 chips
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *   Based on VIA/WonderMedia wm8510-govrh-reg.h
+ *   http://github.com/projectgus/kernel_wm8505/blob/wm8505_2.6.29/
+ *         drivers/video/wmt/register/wm8510/wm8510-govrh-reg.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _WM8505FB_REGS_H
+#define _WM8505FB_REGS_H
+
+/*
+ * Color space select register, default value 0x1c
+ *   BIT0 GOVRH_DVO_YUV2RGB_ENABLE
+ *   BIT1 GOVRH_VGA_YUV2RGB_ENABLE
+ *   BIT2 GOVRH_RGB_MODE
+ *   BIT3 GOVRH_DAC_CLKINV
+ *   BIT4 GOVRH_BLANK_ZERO
+ */
+#define WMT_GOVR_COLORSPACE	0x1e4
+/*
+ * Another colorspace select register, default value 1
+ *   BIT0 GOVRH_DVO_RGB
+ *   BIT1 GOVRH_DVO_YUV422
+ */
+#define WMT_GOVR_COLORSPACE1	 0x30
+
+#define WMT_GOVR_CONTRAST	0x1b8
+#define WMT_GOVR_BRGHTNESS	0x1bc /* incompatible with RGB? */
+
+/* Framubeffer address */
+#define WMT_GOVR_FBADDR		 0x90
+#define WMT_GOVR_FBADDR1	 0x94 /* UV offset in YUV mode */
+
+/* Offset of visible window */
+#define WMT_GOVR_XPAN		 0xa4
+#define WMT_GOVR_YPAN		 0xa0
+
+#define WMT_GOVR_XRES		 0x98
+#define WMT_GOVR_XRES_VIRTUAL	 0x9c
+
+#define WMT_GOVR_MIF_ENABLE	 0x80
+#define WMT_GOVR_FHI		 0xa8
+#define WMT_GOVR_REG_UPDATE	 0xe4
+
+/*
+ *   BIT0 GOVRH_DVO_OUTWIDTH
+ *   BIT1 GOVRH_DVO_SYNC_POLAR
+ *   BIT2 GOVRH_DVO_ENABLE
+ */
+#define WMT_GOVR_DVO_SET	0x148
+
+/* Timing generator? */
+#define WMT_GOVR_TG		0x100
+
+/* Timings */
+#define WMT_GOVR_TIMING_H_ALL	0x108
+#define WMT_GOVR_TIMING_V_ALL	0x10c
+#define WMT_GOVR_TIMING_V_START	0x110
+#define WMT_GOVR_TIMING_V_END	0x114
+#define WMT_GOVR_TIMING_H_START	0x118
+#define WMT_GOVR_TIMING_H_END	0x11c
+#define WMT_GOVR_TIMING_V_SYNC	0x128
+#define WMT_GOVR_TIMING_H_SYNC	0x12c
+
+#endif /* _WM8505FB_REGS_H */
diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
new file mode 100644
index 0000000..b201a60
--- /dev/null
+++ b/drivers/video/wmt_ge_rops.c
@@ -0,0 +1,186 @@
+/*
+ *  linux/drivers/video/wmt_ge_rops.c
+ *
+ *  Accelerators for raster operations using WonderMedia Graphics Engine
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include "fb_draw.h"
+
+#define GE_COMMAND_OFF		0x00
+#define GE_DEPTH_OFF		0x04
+#define GE_HIGHCOLOR_OFF	0x08
+#define GE_ROPCODE_OFF		0x14
+#define GE_FIRE_OFF		0x18
+#define GE_SRCBASE_OFF		0x20
+#define GE_SRCDISPW_OFF		0x24
+#define GE_SRCDISPH_OFF		0x28
+#define GE_SRCAREAX_OFF		0x2c
+#define GE_SRCAREAY_OFF		0x30
+#define GE_SRCAREAW_OFF		0x34
+#define GE_SRCAREAH_OFF		0x38
+#define GE_DESTBASE_OFF		0x3c
+#define GE_DESTDISPW_OFF	0x40
+#define GE_DESTDISPH_OFF	0x44
+#define GE_DESTAREAX_OFF	0x48
+#define GE_DESTAREAY_OFF	0x4c
+#define GE_DESTAREAW_OFF	0x50
+#define GE_DESTAREAH_OFF	0x54
+#define GE_PAT0C_OFF		0x88	/* Pattern 0 color */
+#define GE_ENABLE_OFF		0xec
+#define GE_INTEN_OFF		0xf0
+#define GE_STATUS_OFF		0xf8
+
+static void __iomem *regbase;
+
+void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+	unsigned long fg, pat;
+
+	if (p->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+	    p->fix.visual == FB_VISUAL_DIRECTCOLOR)
+		fg = ((u32 *) (p->pseudo_palette))[rect->color];
+	else
+		fg = rect->color;
+
+	pat = pixel_to_pat(p->var.bits_per_pixel, fg);
+
+	if (p->fbops->fb_sync)
+		p->fbops->fb_sync(p);
+
+	writel(p->var.bits_per_pixel == 32 ? 3 :
+	      (p->var.bits_per_pixel == 8 ? 0 : 1), regbase + GE_DEPTH_OFF);
+	writel(p->var.bits_per_pixel == 15 ? 1 : 0, regbase + GE_HIGHCOLOR_OFF);
+	writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+	writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+	writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+	writel(rect->dx, regbase + GE_DESTAREAX_OFF);
+	writel(rect->dy, regbase + GE_DESTAREAY_OFF);
+	writel(rect->width - 1, regbase + GE_DESTAREAW_OFF);
+	writel(rect->height - 1, regbase + GE_DESTAREAH_OFF);
+
+	writel(pat, regbase + GE_PAT0C_OFF);
+	writel(1, regbase + GE_COMMAND_OFF);
+	writel(rect->rop == ROP_XOR ? 0x5a : 0xf0, regbase + GE_ROPCODE_OFF);
+	writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL(wmt_ge_fillrect);
+
+void wmt_ge_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+	if (p->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (p->fbops->fb_sync)
+		p->fbops->fb_sync(p);
+
+	writel(p->var.bits_per_pixel > 16 ? 3 :
+	      (p->var.bits_per_pixel > 8 ? 1 : 0), regbase + GE_DEPTH_OFF);
+
+	writel(p->fix.smem_start, regbase + GE_SRCBASE_OFF);
+	writel(p->var.xres_virtual - 1, regbase + GE_SRCDISPW_OFF);
+	writel(p->var.yres_virtual - 1, regbase + GE_SRCDISPH_OFF);
+	writel(area->sx, regbase + GE_SRCAREAX_OFF);
+	writel(area->sy, regbase + GE_SRCAREAY_OFF);
+	writel(area->width - 1, regbase + GE_SRCAREAW_OFF);
+	writel(area->height - 1, regbase + GE_SRCAREAH_OFF);
+
+	writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+	writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+	writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+	writel(area->dx, regbase + GE_DESTAREAX_OFF);
+	writel(area->dy, regbase + GE_DESTAREAY_OFF);
+	writel(area->width - 1, regbase + GE_DESTAREAW_OFF);
+	writel(area->height - 1, regbase + GE_DESTAREAH_OFF);
+
+	writel(0xcc, regbase + GE_ROPCODE_OFF);
+	writel(1, regbase + GE_COMMAND_OFF);
+	writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL(wmt_ge_copyarea);
+
+int wmt_ge_sync(struct fb_info *p)
+{
+	int loops = 5000000;
+	while ((readl(regbase + GE_STATUS_OFF) & 4) && --loops)
+		cpu_relax();
+	return loops > 0 ? 0 : -EBUSY;
+}
+EXPORT_SYMBOL(wmt_ge_sync);
+
+static int __devinit wmt_ge_rops_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	regbase = ioremap(res->start, resource_size(res));
+	if (regbase == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		ret = -EBUSY;
+		goto error;
+	}
+
+	writel(1, regbase + GE_ENABLE_OFF);
+	printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n");
+
+	return 0;
+
+error:
+	return ret;
+}
+
+static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
+{
+	iounmap(regbase);
+	return 0;
+}
+
+static struct platform_driver wmt_ge_rops_driver = {
+	.probe		= wmt_ge_rops_probe,
+	.remove		= wmt_ge_rops_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "wmt_ge_rops",
+	},
+};
+
+static int __init wmt_ge_rops_init(void)
+{
+	return platform_driver_register(&wmt_ge_rops_driver);
+}
+
+static void __exit wmt_ge_rops_exit(void)
+{
+	platform_driver_unregister(&wmt_ge_rops_driver);
+}
+
+module_init(wmt_ge_rops_init);
+module_exit(wmt_ge_rops_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com");
+MODULE_DESCRIPTION("Accelerators for raster operations using "
+		   "WonderMedia Graphics Engine");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h
new file mode 100644
index 0000000..8738075
--- /dev/null
+++ b/drivers/video/wmt_ge_rops.h
@@ -0,0 +1,5 @@
+extern void wmt_ge_fillrect(struct fb_info *info,
+			    const struct fb_fillrect *rect);
+extern void wmt_ge_copyarea(struct fb_info *info,
+			    const struct fb_copyarea *area);
+extern int wmt_ge_sync(struct fb_info *info);
-- 
1.7.3.2


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

* [PATCH 1/6 v4] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-07 17:17     ` Russell King - ARM Linux
  2010-11-07 18:25       ` [PATCH 1/6 v3] " Alexey Charkov
@ 2010-11-08 17:19       ` Alexey Charkov
  2010-11-10 15:16         ` saeed bishara
  2010-11-11 21:23         ` [PATCH 1/6 v5] " Alexey Charkov
  1 sibling, 2 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-08 17:19 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505. Suitable code is
selected (if compiled in) at early initialization time by reading a
platform-specific identification register, as current bootloaders
do not provide any reliable machine id to the kernel.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

In addition to the fixes in v3 of this patch (to those issues that
Russell has identified above), this iteration also brings in timeouts
and cpu_relax() in busy waits (PWM and timer), according to what was
suggested for device drivers in the related sub-threads.

It also incorporates a fix to the misspelling of Russell's name
introduced in the comment to v3, for which I beg his pardon :-)

 arch/arm/Kconfig                                |   14 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 +++
 arch/arm/mach-vt8500/Kconfig                    |   63 ++++
 arch/arm/mach-vt8500/Makefile                   |    6 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   81 ++++
 arch/arm/mach-vt8500/devices.c                  |  442 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   46 +++
 arch/arm/mach-vt8500/gpio.c                     |  230 ++++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 ++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 ++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/io.h          |   28 ++
 arch/arm/mach-vt8500/include/mach/irq_defs.h    |  124 +++++++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 ++
 arch/arm/mach-vt8500/include/mach/mmio_regs.h   |   90 +++++
 arch/arm/mach-vt8500/include/mach/system.h      |   20 +
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 ++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 +
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 ++
 arch/arm/mach-vt8500/irq.c                      |  179 +++++++++
 arch/arm/mach-vt8500/irq_defs.c                 |  173 +++++++++
 arch/arm/mach-vt8500/mmio_regs.c                |  118 ++++++
 arch/arm/mach-vt8500/pwm.c                      |  254 +++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  170 +++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   80 ++++
 31 files changed, 2417 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irq_defs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/mmio_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/irq_defs.c
 create mode 100644 arch/arm/mach-vt8500/mmio_regs.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a19a526..e0724ac 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,18 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_CLK
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +985,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..a462869
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,63 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..aff4159
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,6 @@
+obj-y += devices.o gpio.o irq.o irq_defs.o mmio_regs.o timer.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..25aea3d
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,81 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= vt8500_map_io,
+	.reserve	= vt8500_reserve_mem,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..820aad2
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,442 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include <mach/vt8500fb.h>
+#include "devices.h"
+
+static struct resource resources_lcdc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources  = ARRAY_SIZE(resources_lcdc),
+	.resource       = resources_lcdc,
+};
+
+static struct resource resources_wm8505_fb[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(resources_wm8505_fb),
+	.resource       = resources_wm8505_fb,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 43,
+		.right_margin	= 2,
+		.upper_margin	= 10,
+		.lower_margin	= 2,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		int len = strlen(panels[i].mode.name);
+
+		if (memcmp(panels[i].mode.name, str, len) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+static struct resource resources_uart0[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart1[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart2[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart3[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart4[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart5[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_uart0),
+	.resource	= resources_uart0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(resources_uart1),
+	.resource	= resources_uart1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(resources_uart2),
+	.resource	= resources_uart2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(resources_uart3),
+	.resource	= resources_uart3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+	.num_resources	= ARRAY_SIZE(resources_uart4),
+	.resource	= resources_uart4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+	.num_resources	= ARRAY_SIZE(resources_uart5),
+	.resource	= resources_uart5,
+};
+
+static struct resource resources_ehci[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(resources_ehci),
+	.resource	= resources_ehci,
+};
+
+static struct resource resources_ge_rops[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_ge_rops),
+	.resource	= resources_ge_rops,
+};
+
+static struct resource resources_pwm[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+	.resource	= resources_pwm,
+	.num_resources	= ARRAY_SIZE(resources_pwm),
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+static struct resource resources_rtc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+	.resource	= resources_rtc,
+	.num_resources	= ARRAY_SIZE(resources_rtc),
+};
+
+static struct map_desc vt8500_io_desc[] __initdata = {
+	/* SoC MMIO registers, to be filled in later */
+	[0] = {
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init wmt_set_resources(void)
+{
+	resources_lcdc[0].start = wmt_current_regs->lcdc;
+	resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
+	resources_lcdc[1].start = wmt_current_irqs->lcdc;
+	resources_lcdc[1].end = wmt_current_irqs->lcdc;
+
+	resources_wm8505_fb[0].start = wmt_current_regs->govr;
+	resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
+
+	resources_uart0[0].start = wmt_current_regs->uart0;
+	resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
+	resources_uart0[1].start = wmt_current_irqs->uart0;
+	resources_uart0[1].end = wmt_current_irqs->uart0;
+	resources_uart1[0].start = wmt_current_regs->uart1;
+	resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
+	resources_uart1[1].start = wmt_current_irqs->uart1;
+	resources_uart1[1].end = wmt_current_irqs->uart1;
+	resources_uart2[0].start = wmt_current_regs->uart2;
+	resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
+	resources_uart2[1].start = wmt_current_irqs->uart2;
+	resources_uart2[1].end = wmt_current_irqs->uart2;
+	resources_uart3[0].start = wmt_current_regs->uart3;
+	resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
+	resources_uart3[1].start = wmt_current_irqs->uart3;
+	resources_uart3[1].end = wmt_current_irqs->uart3;
+	resources_uart4[0].start = wmt_current_regs->uart4;
+	resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
+	resources_uart4[1].start = wmt_current_irqs->uart4;
+	resources_uart4[1].end = wmt_current_irqs->uart4;
+	resources_uart5[0].start = wmt_current_regs->uart5;
+	resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
+	resources_uart5[1].start = wmt_current_irqs->uart5;
+	resources_uart5[1].end = wmt_current_irqs->uart5;
+
+	resources_ehci[0].start = wmt_current_regs->ehci;
+	resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
+	resources_ehci[1].start = wmt_current_irqs->ehci;
+	resources_ehci[1].end = wmt_current_irqs->ehci;
+
+	resources_ge_rops[0].start = wmt_current_regs->ge;
+	resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
+
+	resources_pwm[0].start = wmt_current_regs->pwm;
+	resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
+
+	resources_rtc[0].start = wmt_current_regs->rtc;
+	resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
+	resources_rtc[1].start = wmt_current_irqs->rtc;
+	resources_rtc[1].end = wmt_current_irqs->rtc;
+	resources_rtc[2].start = wmt_current_irqs->rtc_hz;
+	resources_rtc[2].end = wmt_current_irqs->rtc_hz;
+}
+
+void __init vt8500_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
+	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init wm8505_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
+	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..428809e
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,46 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init wmt_set_resources(void);
+void __init vt8500_gpio_init(void);
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..f533ff0
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,230 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/gpio.h>
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	else
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) &
+			~(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
+	VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
+	VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
+	VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
+	VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
+	VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
+
+	VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
+	VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
+	VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
+	VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
+
+	VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
+	VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
+	VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
+
+	VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
+
+	VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
+
+	VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	writel(readl(regbase + 0x3c) & ~(1 << offset), regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	writel(readl(regbase + 0x3c) | (1 << offset), regbase + 0x3c);
+
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	else
+		writel(readl(regbase + 0x5c) & ~(1 << offset),
+			regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	gpio_to_irq_map[0] = wmt_current_irqs->ext0;
+	gpio_to_irq_map[1] = wmt_current_irqs->ext1;
+	gpio_to_irq_map[2] = wmt_current_irqs->ext2;
+	gpio_to_irq_map[3] = wmt_current_irqs->ext3;
+	gpio_to_irq_map[4] = wmt_current_irqs->ext4;
+	gpio_to_irq_map[5] = wmt_current_irqs->ext5;
+	gpio_to_irq_map[6] = wmt_current_irqs->ext6;
+	gpio_to_irq_map[7] = wmt_current_irqs->ext7;
+
+	regbase = ioremap(wmt_current_regs->gpio, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..8dd55c8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		((void __iomem *)((a) + 0xf0000000))
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irq_defs.h b/arch/arm/mach-vt8500/include/mach/irq_defs.h
new file mode 100644
index 0000000..fa8f4b3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irq_defs.h
@@ -0,0 +1,124 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irq_defs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef VT8500_IRQ_DEFS_H
+#define VT8500_IRQ_DEFS_H
+
+#include <linux/types.h>
+
+struct wmt_irq_srcs {
+	u8 nr_irqs;
+	u8 jpegenc;
+	u8 jpegdec;
+	u8 pata;
+	u8 dma;
+	u8 ext0;
+	u8 ext1;
+	u8 ext2;
+	u8 ext3;
+	u8 ext4;
+	u8 ext5;
+	u8 ext6;
+	u8 ext7;
+	u8 ether;
+	u8 mpegts;
+	u8 ge;
+	u8 gov;
+	u8 lcdc;
+	u8 lcdf;
+	u8 vpp;
+	u8 vpu;
+	u8 vid;
+	u8 spu;
+	u8 pip;
+	u8 dvo;
+	u8 govw;
+	u8 govrsdscd;
+	u8 govrsdmif;
+	u8 govrhdscd;
+	u8 govrhdmif;
+	u8 cipher;
+	u8 i2c0;
+	u8 i2c1;
+	u8 sdmmc;
+	u8 sdmmc_dma;
+	u8 pmc_wu;
+	u8 spi0;
+	u8 spi1;
+	u8 spi2;
+	u8 nand;
+	u8 nand_dma;
+	u8 nor;
+	u8 memstick;
+	u8 memstick_dma;
+	u8 uart0;
+	u8 uart1;
+	u8 uart2;
+	u8 uart3;
+	u8 uart4;
+	u8 uart5;
+	u8 i2s;
+	u8 pcm;
+	u8 ac97;
+	u8 timer_match0;
+	u8 timer_match1;
+	u8 timer_match2;
+	u8 timer_match3;
+	u8 ehci;
+	u8 uhci;
+	u8 udc;
+	u8 udc_dma;
+	u8 keypad;
+	u8 ps2mouse;
+	u8 ps2kbd;
+	u8 rtc;
+	u8 rtc_hz;
+	u8 adc;
+	u8 cir;
+	u8 dma0;
+	u8 dma1;
+	u8 dma2;
+	u8 dma3;
+	u8 dma4;
+	u8 dma5;
+	u8 dma6;
+	u8 dma7;
+	u8 dma8;
+	u8 dma9;
+	u8 dma10;
+	u8 dma11;
+	u8 dma12;
+	u8 dma13;
+	u8 dma14;
+	u8 dma15;
+	u8 irq0;
+	u8 irq1;
+	u8 irq2;
+	u8 irq3;
+	u8 irq4;
+	u8 irq5;
+	u8 irq6;
+	u8 irq7;
+	u8 sae;
+};
+
+extern struct wmt_irq_srcs wmt_irqs[] __initdata;
+extern struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/mmio_regs.h b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
new file mode 100644
index 0000000..76439dd
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
@@ -0,0 +1,90 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/mmio_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_MMIO_REGS_H
+#define __ASM_ARM_ARCH_MMIO_REGS_H
+
+#include <linux/init.h>
+
+struct wmt_mmio_regs {
+	unsigned long mmio_regs_start;
+	unsigned long mmio_regs_length;
+	unsigned long mmio_regs_virt;
+	unsigned long ddr;
+	unsigned long dma;
+	unsigned long vdma;
+	unsigned long sflash;
+	unsigned long ether;
+	unsigned long cipher;
+	unsigned long ehci;
+	unsigned long uhci;
+	unsigned long pata;
+	unsigned long ps2;
+	unsigned long nand;
+	unsigned long nor;
+	unsigned long sdmmc;
+	unsigned long memstick;
+	unsigned long lcdc;
+	unsigned long vpu;
+	unsigned long gov;
+	unsigned long ge;
+	unsigned long govr;
+	unsigned long scl;
+	unsigned long lcdf;
+	unsigned long vid;
+	unsigned long vpp;
+	unsigned long tsbk;
+	unsigned long jpegdec;
+	unsigned long jpegenc;
+	unsigned long rtc;
+	unsigned long gpio;
+	unsigned long scc;
+	unsigned long pmc;
+	unsigned long ic0;
+	unsigned long ic1;
+	unsigned long uart0;
+	unsigned long uart1;
+	unsigned long uart2;
+	unsigned long uart3;
+	unsigned long uart4;
+	unsigned long uart5;
+	unsigned long pwm;
+	unsigned long spi0;
+	unsigned long spi1;
+	unsigned long spi2;
+	unsigned long cir;
+	unsigned long i2c0;
+	unsigned long i2c1;
+	unsigned long ac97;
+	unsigned long pcm;
+	unsigned long i2s;
+	unsigned long adc;
+	unsigned long keypad;
+};
+
+enum {
+	VT8500_INDEX,
+	WM8505_INDEX,
+};
+
+extern struct wmt_mmio_regs wmt_regmaps[] __initdata;
+extern struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+#endif
+
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..4d812e8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR	0xd8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	void __iomem *pmsr = ioremap(VT8500_PMSR, 4);
+	if (pmsr)
+		writel(1, pmsr);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..75a6912
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END		(PAGE_OFFSET + 0x10000000)
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..cc7f25e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	__u32			xres_virtual;
+	__u32			yres_virtual;
+	__u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..c988a4d
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,179 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);
+	if (edge)
+		writel(readl(base
+			+ VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
+			| (1 << (irq & 0x1f)), base
+			+ VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));
+	else
+		writeb(readb(base
+			+ VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
+			base + VT8500_IC_DCTR + irq);
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	writeb(readb(base
+		+ VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
+		base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
+{
+	return -EINVAL;
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_HIGH, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_level_irq);
+		break;
+	case IRQF_TRIGGER_FALLING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_FALLING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	case IRQF_TRIGGER_RISING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_RISING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	}
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_wake  = vt8500_irq_set_wake,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+	sic_regbase = ioremap(wmt_current_regs->ic1, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR
+								+ i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/irq_defs.c b/arch/arm/mach-vt8500/irq_defs.c
new file mode 100644
index 0000000..b338c04
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq_defs.c
@@ -0,0 +1,173 @@
+/* linux/arch/arm/mach-vt8500/irq_defs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/irq_defs.h>
+#include <mach/mmio_regs.h>
+
+struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+struct wmt_irq_srcs wmt_irqs[] __initdata = {
+	[VT8500_INDEX] = {
+		.jpegenc	= 0,
+		.jpegdec	= 1,
+		.pata		= 3,
+		.dma		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.ge		= 8,
+		.gov		= 9,
+		.ether		= 10,
+		.mpegts		= 11,
+		.lcdc		= 12,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.cipher		= 16,
+		.vpp		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.lcdf		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.memstick	= 30,
+		.memstick_dma	= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.i2s		= 34,
+		.pcm		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.vpu		= 40,
+		.vid		= 41,
+		.ac97		= 42,
+		.ehci		= 43,
+		.nor		= 44,
+		.ps2mouse	= 45,
+		.ps2kbd		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.adc		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.dma0		= 56,
+		.dma1		= 57,
+		.dma2		= 58,
+		.dma3		= 59,
+		.dma4		= 60,
+		.dma5		= 61,
+		.dma6		= 62,
+		.dma7		= 63,
+		.nr_irqs	= 64,
+	},
+	[WM8505_INDEX] = {
+		.uhci		= 0,
+		.ehci		= 1,
+		.udc_dma	= 2,
+		.ps2mouse	= 4,
+		.udc		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.keypad		= 8,
+		.dma		= 9,
+		.ether		= 10,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.dma0		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.ps2kbd		= 23,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.dma1		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.uart5		= 30,
+		.uart4		= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.dma2		= 34,
+		.i2s		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.dma3		= 40,
+		.dma4		= 41,
+		.ac97		= 42,
+		.nor		= 44,
+		.dma5		= 45,
+		.dma6		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.dma7		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.irq0		= 56,
+		.irq1		= 57,
+		.irq2		= 58,
+		.irq3		= 59,
+		.irq4		= 60,
+		.irq5		= 61,
+		.irq6		= 62,
+		.irq7		= 63,
+		.jpegdec	= 65,
+		.sae		= 66,
+		.vpu		= 79,
+		.vpp		= 80,
+		.vid		= 81,
+		.spu		= 82,
+		.pip		= 83,
+		.ge		= 84,
+		.gov		= 85,
+		.dvo		= 86,
+		.dma8		= 92,
+		.dma9		= 93,
+		.dma10		= 94,
+		.dma11		= 95,
+		.dma12		= 96,
+		.dma13		= 97,
+		.dma14		= 98,
+		.dma15		= 99,
+		.govw		= 111,
+		.govrsdscd	= 112,
+		.govrsdmif	= 113,
+		.govrhdscd	= 114,
+		.govrhdmif	= 115,
+		.nr_irqs	= 116,
+	},
+};
diff --git a/arch/arm/mach-vt8500/mmio_regs.c b/arch/arm/mach-vt8500/mmio_regs.c
new file mode 100644
index 0000000..e9b3264
--- /dev/null
+++ b/arch/arm/mach-vt8500/mmio_regs.c
@@ -0,0 +1,118 @@
+/* linux/arch/arm/mach-vt8500/mmio_regs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/mmio_regs.h>
+
+struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+struct wmt_mmio_regs wmt_regmaps[] __initdata = {
+	[VT8500_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00350000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000000,
+		.dma			= 0xd8001000,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007900,
+		.uhci			= 0xd8007b01,
+		.pata			= 0xd8008000,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.memstick		= 0xd800b400,
+		.lcdc			= 0xd800e400,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.lcdf			= 0xd8050900,
+		.vid			= 0xd8050a00,
+		.vpp			= 0xd8050b00,
+		.tsbk			= 0xd80f4000,
+		.jpegdec		= 0xd80fe000,
+		.jpegenc		= 0xd80ff000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.pcm			= 0xd82d0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.adc			= 0xd8340000,
+	},
+	[WM8505_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00390000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000400,
+		.dma			= 0xd8001800,
+		.vdma			= 0xd8001c00,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007100,
+		.uhci			= 0xd8007301,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.govr			= 0xd8050800,
+		.vid			= 0xd8050a00,
+		.scl			= 0xd8050d00,
+		.vpp			= 0xd8050f00,
+		.jpegdec		= 0xd80fe000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.ic1			= 0xd8150000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.keypad			= 0xd8260000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.uart4			= 0xd8370000,
+		.uart5			= 0xd8380000,
+	},
+};
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..d1356a1
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,254 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+	int loops = 1000;
+	while ((readb(reg) & bitmask) && --loops)
+		cpu_relax();
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	dc = pv * duty_ns / period_ns;
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else
+			pwm = ERR_PTR(-EBUSY);
+	} else
+		pwm = ERR_PTR(-ENOENT);
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else
+		pr_warning("PWM device already freed\n");
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[0].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..f65ffbd
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,170 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+static void __iomem *regbase;
+
+/*
+ * Kernel assumes that sched_clock can be called early but may not have
+ * things ready yet.
+ */
+static cycle_t vt8500_timer_read_dummy(struct clocksource *cs)
+{
+	return 0;
+}
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	int loops = 1000;
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read_dummy,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+unsigned long long sched_clock(void)
+{
+	return clocksource_cyc2ns(clocksource.read(&clocksource),
+				clocksource.mult, clocksource.shift);
+}
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	int loops = 1000;
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_current_regs->pmc + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO "
+				"registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	clocksource.read = vt8500_timer_read;
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register "
+			"failed for %s\n", clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_current_irqs->timer_match0, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq "
+			"failed for %s\n", clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..b07cba6
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,80 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <mach/mmio_regs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= wm8505_map_io,
+	.reserve	= wm8505_reserve_mem,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.2


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

* [PATCH 2/6 v4] serial: Add support for UART on VIA VT8500 and compatibles
  2010-11-08 10:46       ` Alan Cox
@ 2010-11-08 17:33         ` Alexey Charkov
  0 siblings, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-08 17:33 UTC (permalink / raw)
  To: Alan Cox
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Greg Kroah-Hartman, Ben Dooks, Kukjin Kim, Feng Tang,
	Tobias Klauser, linux-kernel

This adds a driver for the serial ports found in VIA and WonderMedia
Systems-on-Chip. Interrupt-driven FIFO operation is implemented.
The hardware also supports pure register-based operation (which is
slower) and DMA-based FIFO operation. As the FIFOs are only 16 bytes
long, DMA operation is probably not worth the hassle.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This fixes the last remaining "ttyS" mentioned by Alan and also adds
6 as the number of UARTs (which is the case for WM8505, VT8500 has 4).

 drivers/serial/Kconfig         |   10 +
 drivers/serial/Makefile        |    1 +
 drivers/serial/vt8500_serial.c |  648 ++++++++++++++++++++++++++++++++++++++++
 include/linux/serial_core.h    |    3 +
 4 files changed, 662 insertions(+), 0 deletions(-)
 create mode 100644 drivers/serial/vt8500_serial.c

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index aff9dcd..6e76a59 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1381,6 +1381,16 @@ config SERIAL_MSM_CONSOLE
 	depends on SERIAL_MSM=y
 	select SERIAL_CORE_CONSOLE
 
+config SERIAL_VT8500
+	bool "VIA VT8500 on-chip serial port support"
+	depends on ARM && ARCH_VT8500
+	select SERIAL_CORE
+
+config SERIAL_VT8500_CONSOLE
+	bool "VIA VT8500 serial console support"
+	depends on SERIAL_VT8500=y
+	select SERIAL_CORE_CONSOLE
+
 config SERIAL_NETX
 	tristate "NetX serial port support"
 	depends on ARM && ARCH_NETX
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index c570576..86b8587 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_SERIAL_TIMBERDALE)	+= timbuart.o
 obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
 obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
 obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
 obj-$(CONFIG_SERIAL_MRST_MAX3110)	+= mrst_max3110.o
 obj-$(CONFIG_SERIAL_MFD_HSU)	+= mfd.o
 obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
diff --git a/drivers/serial/vt8500_serial.c b/drivers/serial/vt8500_serial.c
new file mode 100644
index 0000000..322bf56
--- /dev/null
+++ b/drivers/serial/vt8500_serial.c
@@ -0,0 +1,648 @@
+/*
+ * drivers/serial/vt8500_serial.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on msm_serial.c, which is:
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Robert Love <rlove@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+# define SUPPORT_SYSRQ
+#endif
+
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/*
+ * UART Register offsets
+ */
+
+#define VT8500_URTDR		0x0000	/* Transmit data */
+#define VT8500_URRDR		0x0004	/* Receive data */
+#define VT8500_URDIV		0x0008	/* Clock/Baud rate divisor */
+#define VT8500_URLCR		0x000C	/* Line control */
+#define VT8500_URICR		0x0010	/* IrDA control */
+#define VT8500_URIER		0x0014	/* Interrupt enable */
+#define VT8500_URISR		0x0018	/* Interrupt status */
+#define VT8500_URUSR		0x001c	/* UART status */
+#define VT8500_URFCR		0x0020	/* FIFO control */
+#define VT8500_URFIDX		0x0024	/* FIFO index */
+#define VT8500_URBKR		0x0028	/* Break signal count */
+#define VT8500_URTOD		0x002c	/* Time out divisor */
+#define VT8500_TXFIFO		0x1000	/* Transmit FIFO (16x8) */
+#define VT8500_RXFIFO		0x1020	/* Receive FIFO (16x10) */
+
+/*
+ * Interrupt enable and status bits
+ */
+
+#define TXDE	(1 << 0)	/* Tx Data empty */
+#define RXDF	(1 << 1)	/* Rx Data full */
+#define TXFAE	(1 << 2)	/* Tx FIFO almost empty */
+#define TXFE	(1 << 3)	/* Tx FIFO empty */
+#define RXFAF	(1 << 4)	/* Rx FIFO almost full */
+#define RXFF	(1 << 5)	/* Rx FIFO full */
+#define TXUDR	(1 << 6)	/* Tx underrun */
+#define RXOVER	(1 << 7)	/* Rx overrun */
+#define PER	(1 << 8)	/* Parity error */
+#define FER	(1 << 9)	/* Frame error */
+#define TCTS	(1 << 10)	/* Toggle of CTS */
+#define RXTOUT	(1 << 11)	/* Rx timeout */
+#define BKDONE	(1 << 12)	/* Break signal done */
+#define ERR	(1 << 13)	/* AHB error response */
+
+#define RX_FIFO_INTS	(RXFAF | RXFF | RXOVER | PER | FER | RXTOUT)
+#define TX_FIFO_INTS	(TXFAE | TXFE | TXUDR)
+
+struct vt8500_port {
+	struct uart_port	uart;
+	char			name[16];
+	struct clk		*clk;
+	unsigned int		ier;
+};
+
+static inline void vt8500_write(struct uart_port *port, unsigned int val,
+			     unsigned int off)
+{
+	writel(val, port->membase + off);
+}
+
+static inline unsigned int vt8500_read(struct uart_port *port, unsigned int off)
+{
+	return readl(port->membase + off);
+}
+
+static void vt8500_stop_tx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void vt8500_stop_rx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~RX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void vt8500_enable_ms(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier |= TCTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void handle_rx(struct uart_port *port)
+{
+	struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+	if (!tty) {
+		/* Discard data: no tty available */
+		int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8;
+		u16 ch;
+		while (count--)
+			ch = readw(port->membase + VT8500_RXFIFO);
+		return;
+	}
+
+	/*
+	 * Handle overrun
+	 */
+	if ((vt8500_read(port, VT8500_URISR) & RXOVER)) {
+		port->icount.overrun++;
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+	}
+
+	/* and now the main RX loop */
+	while (vt8500_read(port, VT8500_URFIDX) & 0x1f00) {
+		unsigned int c;
+		char flag = TTY_NORMAL;
+
+		c = readw(port->membase + VT8500_RXFIFO) & 0x3ff;
+
+		/* Mask conditions we're ignorning. */
+		c &= ~port->read_status_mask;
+
+		if (c & FER) {
+			port->icount.frame++;
+			flag = TTY_FRAME;
+		} else if (c & PER) {
+			port->icount.parity++;
+			flag = TTY_PARITY;
+		}
+		port->icount.rx++;
+
+		if (!uart_handle_sysrq_char(port, c))
+			tty_insert_flip_char(tty, c, flag);
+	}
+
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static void handle_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		writeb(port->x_char, port->membase + VT8500_TXFIFO);
+		port->icount.tx++;
+		port->x_char = 0;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		vt8500_stop_tx(port);
+		return;
+	}
+
+	while ((vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16) {
+		if (uart_circ_empty(xmit))
+			break;
+
+		writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		vt8500_stop_tx(port);
+}
+
+static void vt8500_start_tx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+	handle_tx(port);
+	vt8500_port->ier |= TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void handle_delta_cts(struct uart_port *port)
+{
+	port->icount.cts++;
+	wake_up_interruptible(&port->state->port.delta_msr_wait);
+}
+
+static irqreturn_t vt8500_irq(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned long isr;
+
+	spin_lock(&port->lock);
+	isr = vt8500_read(port, VT8500_URISR);
+
+	/* Acknowledge active status bits */
+	vt8500_write(port, isr, VT8500_URISR);
+
+	if (isr & RX_FIFO_INTS)
+		handle_rx(port);
+	if (isr & TX_FIFO_INTS)
+		handle_tx(port);
+	if (isr & TCTS)
+		handle_delta_cts(port);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int vt8500_tx_empty(struct uart_port *port)
+{
+	return (vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16 ?
+						TIOCSER_TEMT : 0;
+}
+
+static unsigned int vt8500_get_mctrl(struct uart_port *port)
+{
+	unsigned int usr;
+
+	usr = vt8500_read(port, VT8500_URUSR);
+	if (usr & (1 << 4))
+		return TIOCM_CTS;
+	else
+		return 0;
+}
+
+static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void vt8500_break_ctl(struct uart_port *port, int break_ctl)
+{
+	if (break_ctl)
+		vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9),
+			     VT8500_URLCR);
+}
+
+static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud)
+{
+	unsigned long div;
+	unsigned int loops = 1000;
+
+	div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff);
+
+	if (unlikely((baud < 900) || (baud > 921600)))
+		div |= 7;
+	else
+		div |= (921600 / baud) - 1;
+
+	while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops)
+		cpu_relax();
+	vt8500_write(port, div, VT8500_URDIV);
+
+	return baud;
+}
+
+static int vt8500_startup(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	int ret;
+
+	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
+		 "vt8500_serial%d", port->line);
+
+	ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH,
+			  vt8500_port->name, port);
+	if (unlikely(ret))
+		return ret;
+
+	vt8500_write(port, 0x03, VT8500_URLCR);	/* enable TX & RX */
+
+	return 0;
+}
+
+static void vt8500_shutdown(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+
+	vt8500_port->ier = 0;
+
+	/* disable interrupts and FIFOs */
+	vt8500_write(&vt8500_port->uart, 0, VT8500_URIER);
+	vt8500_write(&vt8500_port->uart, 0x880, VT8500_URFCR);
+	free_irq(port->irq, port);
+}
+
+static void vt8500_set_termios(struct uart_port *port,
+			       struct ktermios *termios,
+			       struct ktermios *old)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	unsigned long flags;
+	unsigned int baud, lcr;
+	unsigned int loops = 1000;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* calculate and set baud rate */
+	baud = uart_get_baud_rate(port, termios, old, 900, 921600);
+	baud = vt8500_set_baud_rate(port, baud);
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+
+	/* calculate parity */
+	lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR);
+	lcr &= ~((1 << 5) | (1 << 4));
+	if (termios->c_cflag & PARENB) {
+		lcr |= (1 << 4);
+		termios->c_cflag &= ~CMSPAR;
+		if (termios->c_cflag & PARODD)
+			lcr |= (1 << 5);
+	}
+
+	/* calculate bits per char */
+	lcr &= ~(1 << 2);
+	switch (termios->c_cflag & CSIZE) {
+	case CS7:
+		break;
+	case CS8:
+	default:
+		lcr |= (1 << 2);
+		termios->c_cflag &= ~CSIZE;
+		termios->c_cflag |= CS8;
+		break;
+	}
+
+	/* calculate stop bits */
+	lcr &= ~(1 << 3);
+	if (termios->c_cflag & CSTOPB)
+		lcr |= (1 << 3);
+
+	/* set parity, bits per char, and stop bit */
+	vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR);
+
+	/* Configure status bits to ignore based on termio flags. */
+	port->read_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->read_status_mask = FER | PER;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/* Reset FIFOs */
+	vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR);
+	while ((vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc)
+							&& --loops)
+		cpu_relax();
+
+	/* Every possible FIFO-related interrupt */
+	vt8500_port->ier = RX_FIFO_INTS | TX_FIFO_INTS;
+
+	/*
+	 * CTS flow control
+	 */
+	if (UART_ENABLE_MS(&vt8500_port->uart, termios->c_cflag))
+		vt8500_port->ier |= TCTS;
+
+	vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR);
+	vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *vt8500_type(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	return vt8500_port->name;
+}
+
+static void vt8500_release_port(struct uart_port *port)
+{
+}
+
+static int vt8500_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void vt8500_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_VT8500;
+}
+
+static int vt8500_verify_port(struct uart_port *port,
+			      struct serial_struct *ser)
+{
+	if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500))
+		return -EINVAL;
+	if (unlikely(port->irq != ser->irq))
+		return -EINVAL;
+	return 0;
+}
+
+static struct vt8500_port *vt8500_uart_ports[4];
+static struct uart_driver vt8500_uart_driver;
+
+#ifdef CONFIG_SERIAL_VT8500_CONSOLE
+
+static inline void wait_for_xmitr(struct uart_port *port)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = vt8500_read(port, VT8500_URFIDX);
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while (status & 0x10);
+}
+
+static void vt8500_console_putchar(struct uart_port *port, int c)
+{
+	wait_for_xmitr(port);
+	writeb(c, port->membase + VT8500_TXFIFO);
+}
+
+static void vt8500_console_write(struct console *co, const char *s,
+			      unsigned int count)
+{
+	struct vt8500_port *vt8500_port = vt8500_uart_ports[co->index];
+	unsigned long ier;
+
+	BUG_ON(co->index < 0 || co->index >= vt8500_uart_driver.nr);
+
+	ier = vt8500_read(&vt8500_port->uart, VT8500_URIER);
+	vt8500_write(&vt8500_port->uart, VT8500_URIER, 0);
+
+	uart_console_write(&vt8500_port->uart, s, count,
+			   vt8500_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and switch back to FIFO
+	 */
+	wait_for_xmitr(&vt8500_port->uart);
+	vt8500_write(&vt8500_port->uart, VT8500_URIER, ier);
+}
+
+static int __init vt8500_console_setup(struct console *co, char *options)
+{
+	struct vt8500_port *vt8500_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (unlikely(co->index >= vt8500_uart_driver.nr || co->index < 0))
+		return -ENXIO;
+
+	vt8500_port = vt8500_uart_ports[co->index];
+
+	if (!vt8500_port)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&vt8500_port->uart,
+				 co, baud, parity, bits, flow);
+}
+
+static struct console vt8500_console = {
+	.name = "ttyWMT",
+	.write = vt8500_console_write,
+	.device = uart_console_device,
+	.setup = vt8500_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &vt8500_uart_driver,
+};
+
+#define VT8500_CONSOLE	(&vt8500_console)
+
+#else
+#define VT8500_CONSOLE	NULL
+#endif
+
+static struct uart_ops vt8500_uart_pops = {
+	.tx_empty	= vt8500_tx_empty,
+	.set_mctrl	= vt8500_set_mctrl,
+	.get_mctrl	= vt8500_get_mctrl,
+	.stop_tx	= vt8500_stop_tx,
+	.start_tx	= vt8500_start_tx,
+	.stop_rx	= vt8500_stop_rx,
+	.enable_ms	= vt8500_enable_ms,
+	.break_ctl	= vt8500_break_ctl,
+	.startup	= vt8500_startup,
+	.shutdown	= vt8500_shutdown,
+	.set_termios	= vt8500_set_termios,
+	.type		= vt8500_type,
+	.release_port	= vt8500_release_port,
+	.request_port	= vt8500_request_port,
+	.config_port	= vt8500_config_port,
+	.verify_port	= vt8500_verify_port,
+};
+
+static struct uart_driver vt8500_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "vt8500_serial",
+	.dev_name	= "ttyWMT",
+	.nr		= 6,
+	.cons		= VT8500_CONSOLE,
+};
+
+static int __init vt8500_serial_probe(struct platform_device *pdev)
+{
+	struct vt8500_port *vt8500_port;
+	struct resource *mmres, *irqres;
+	int ret;
+
+	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!mmres || !irqres)
+		return -ENODEV;
+
+	vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
+	if (!vt8500_port)
+		return -ENOMEM;
+
+	vt8500_port->uart.type = PORT_VT8500;
+	vt8500_port->uart.iotype = UPIO_MEM;
+	vt8500_port->uart.mapbase = mmres->start;
+	vt8500_port->uart.irq = irqres->start;
+	vt8500_port->uart.fifosize = 16;
+	vt8500_port->uart.ops = &vt8500_uart_pops;
+	vt8500_port->uart.line = pdev->id;
+	vt8500_port->uart.dev = &pdev->dev;
+	vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+	vt8500_port->uart.uartclk = 24000000;
+
+	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
+		 "VT8500 UART%d", pdev->id);
+
+	vt8500_port->uart.membase = ioremap(mmres->start,
+					    mmres->end - mmres->start + 1);
+	if (!vt8500_port->uart.membase) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	vt8500_uart_ports[pdev->id] = vt8500_port;
+
+	uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
+
+	platform_set_drvdata(pdev, vt8500_port);
+
+	return 0;
+
+err:
+	kfree(vt8500_port);
+	return ret;
+}
+
+static int __devexit vt8500_serial_remove(struct platform_device *pdev)
+{
+	struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
+	kfree(vt8500_port);
+
+	return 0;
+}
+
+static struct platform_driver vt8500_platform_driver = {
+	.probe  = vt8500_serial_probe,
+	.remove = vt8500_serial_remove,
+	.driver = {
+		.name = "vt8500_serial",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init vt8500_serial_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&vt8500_uart_driver);
+	if (unlikely(ret))
+		return ret;
+
+	ret = platform_driver_register(&vt8500_platform_driver);
+
+	if (unlikely(ret))
+		uart_unregister_driver(&vt8500_uart_driver);
+
+	return ret;
+}
+
+static void __exit vt8500_serial_exit(void)
+{
+#ifdef CONFIG_SERIAL_VT8500_CONSOLE
+	unregister_console(&vt8500_console);
+#endif
+	platform_driver_unregister(&vt8500_platform_driver);
+	uart_unregister_driver(&vt8500_uart_driver);
+}
+
+module_init(vt8500_serial_init);
+module_exit(vt8500_serial_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("Driver for vt8500 serial device");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 212eb4c..41603d6 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -199,6 +199,9 @@
 /* TI OMAP-UART */
 #define PORT_OMAP	96
 
+/* VIA VT8500 SoC */
+#define PORT_VT8500	97
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>
-- 
1.7.3.2


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

* Re: [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08 14:14     ` [PATCH 6/6 v3] " Alexey Charkov
@ 2010-11-08 20:43       ` Paul Mundt
  2010-11-08 21:15         ` Alexey Charkov
  2010-11-09 10:33         ` [PATCH 6/6 v3] " Russell King - ARM Linux
  0 siblings, 2 replies; 91+ messages in thread
From: Paul Mundt @ 2010-11-08 20:43 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Andrew Morton,
	Guennadi Liakhovetski, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

On Mon, Nov 08, 2010 at 05:14:07PM +0300, Alexey Charkov wrote:
> This incorporates fixes to the issues that Paul has identified.
> MMIO register pointer in wmt_ge_rops was just made static, as I
> could not find any immediately obvious way to pass drvdata around
> (the whole functionality of this driver is in exported functions
> that are called from a display driver context, which does not know
> about the rop engine specific data structures).
> 
Looking better.. just a few minor things left.

> diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
> new file mode 100644
> index 0000000..640d8a3
> --- /dev/null
> +++ b/drivers/video/vt8500lcdfb.c
> +#include <asm/irq.h>
> +
linux/irq.h is preferred here.

> diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c
> new file mode 100644
> index 0000000..560c926
> --- /dev/null
> +++ b/drivers/video/wm8505fb.c
> +#include <asm/irq.h>
> +
Likewise.

> +static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
> +				struct fb_info *info)
> +{
> +	struct wm8505fb_info *fbi = container_of(info,
> +						  struct wm8505fb_info,
> +						  fb);
> +
Sice you are open-coding this container_of() all over the place you may
simply want to make a wrapper for this. ie,

#define to_wm8505fb_info(info)	container_of(info, struct wm8505fb_info, fb)

and then just doing struct wm855fb_info *fbi = to_wm8505fb_info(info);

This wiwll also save you from having that ugly multi-line split in the
container_of() and keep you well within 80 characters.

> +static int __devinit wm8505fb_probe(struct platform_device *pdev)
> +{
...

> +	ret = register_framebuffer(&fbi->fb);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev,
> +			"Failed to register framebuffer device: %d\n", ret);
> +		goto failed_free_cmap;
> +	}
> +
> +	ret = device_create_file(&pdev->dev, &dev_attr_contrast);
> +	if (ret < 0) {
> +		printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
> +			fbi->fb.node, ret);
> +	}
> +
...
> +}
> +
> +static int __devexit wm8505fb_remove(struct platform_device *pdev)
> +{
> +	struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
> +	struct resource *res;
> +
> +	if (!fbi)
> +		return 0;
> +
I would kill this test as well. If this ever triggers, something horribly
wrong has happened and you likely have bigger things to worry about.

> +	unregister_framebuffer(&fbi->fb);
> +
You're missing a device_remove_file().

> +static struct platform_driver wm8505fb_driver = {
> +	.probe		= wm8505fb_probe,
> +	.remove		= wm8505fb_remove,

As your remove function is flagged devexit, this ought to be wrapped with
__devexit_p(). This will save you a bit of space as the kernel will
discard the remove function outright if you're not using this as a module
or with hotplug. The same applies to your other remove functions too.

> diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
> new file mode 100644
> index 0000000..b201a60
> --- /dev/null
> +++ b/drivers/video/wmt_ge_rops.c
> +EXPORT_SYMBOL(wmt_ge_fillrect);
> +EXPORT_SYMBOL(wmt_ge_copyarea);
> +EXPORT_SYMBOL(wmt_ge_sync);
> +
...

Is there a particular reason why you are favouring EXPORT_SYMBOL? In
general we prefer that new infrastructure patches and the like stick with
EXPORT_SYMBOL_GPL, as this discourages use by non-GPLed modules going
forward.

> +static int __devinit wmt_ge_rops_probe(struct platform_device *pdev)
> +{
...
> +	regbase = ioremap(res->start, resource_size(res));
> +	if (regbase == NULL) {
> +		dev_err(&pdev->dev, "failed to map I/O memory\n");
> +		ret = -EBUSY;
> +		goto error;
> +	}
> +
You might also want to do something like:

	/* Only one ROP engine is presently supported. */
	if (unlikely(regbase)) {
		WARN_ON(1);
		return -EBUSY;
	}

	regbase = ioremap(...);
	...

> +	writel(1, regbase + GE_ENABLE_OFF);
> +	printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n");
> +
> +	return 0;
> +
> +error:
> +	return ret;
> +}
> +
> +static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
> +{
> +	iounmap(regbase);
> +	return 0;
> +}
> +
You're missing a:

	writel(0, regbase + GE_ENABLE_OFF);

here?

> +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com");
> +MODULE_DESCRIPTION("Accelerators for raster operations using "
> +		   "WonderMedia Graphics Engine");
> +MODULE_LICENSE("GPL");

Your other drivers appear to be lacking AUTHOR and DESCRIPTION
definitions.

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

* Re: [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08 20:43       ` Paul Mundt
@ 2010-11-08 21:15         ` Alexey Charkov
  2010-11-08 21:30           ` Paul Mundt
  2010-11-09 10:33         ` [PATCH 6/6 v3] " Russell King - ARM Linux
  1 sibling, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-08 21:15 UTC (permalink / raw)
  To: Paul Mundt
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Andrew Morton,
	Guennadi Liakhovetski, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

2010/11/8 Paul Mundt <lethal@linux-sh.org>:
> On Mon, Nov 08, 2010 at 05:14:07PM +0300, Alexey Charkov wrote:
>> This incorporates fixes to the issues that Paul has identified.
>> MMIO register pointer in wmt_ge_rops was just made static, as I
>> could not find any immediately obvious way to pass drvdata around
>> (the whole functionality of this driver is in exported functions
>> that are called from a display driver context, which does not know
>> about the rop engine specific data structures).
>>
> Looking better.. just a few minor things left.
>
>> diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
>> new file mode 100644
>> index 0000000..640d8a3
>> --- /dev/null
>> +++ b/drivers/video/vt8500lcdfb.c
>> +#include <asm/irq.h>
>> +
> linux/irq.h is preferred here.
>
>> diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c
>> new file mode 100644
>> index 0000000..560c926
>> --- /dev/null
>> +++ b/drivers/video/wm8505fb.c
>> +#include <asm/irq.h>
>> +
> Likewise.
>

Ok.

>> +static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
>> +                             struct fb_info *info)
>> +{
>> +     struct wm8505fb_info *fbi = container_of(info,
>> +                                               struct wm8505fb_info,
>> +                                               fb);
>> +
> Sice you are open-coding this container_of() all over the place you may
> simply want to make a wrapper for this. ie,
>
> #define to_wm8505fb_info(info)  container_of(info, struct wm8505fb_info, fb)
>
> and then just doing struct wm855fb_info *fbi = to_wm8505fb_info(info);
>
> This wiwll also save you from having that ugly multi-line split in the
> container_of() and keep you well within 80 characters.
>

That's true, thanks.

>> +static int __devinit wm8505fb_probe(struct platform_device *pdev)
>> +{
> ...
>
>> +     ret = register_framebuffer(&fbi->fb);
>> +     if (ret < 0) {
>> +             dev_err(&pdev->dev,
>> +                     "Failed to register framebuffer device: %d\n", ret);
>> +             goto failed_free_cmap;
>> +     }
>> +
>> +     ret = device_create_file(&pdev->dev, &dev_attr_contrast);
>> +     if (ret < 0) {
>> +             printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
>> +                     fbi->fb.node, ret);
>> +     }
>> +
> ...
>> +}
>> +
>> +static int __devexit wm8505fb_remove(struct platform_device *pdev)
>> +{
>> +     struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
>> +     struct resource *res;
>> +
>> +     if (!fbi)
>> +             return 0;
>> +
> I would kill this test as well. If this ever triggers, something horribly
> wrong has happened and you likely have bigger things to worry about.
>

But a couple of extra instructions for error handling to hold in the
kernel binary should not hurt, should they? Are there benefits aside
from code compaction?

>> +     unregister_framebuffer(&fbi->fb);
>> +
> You're missing a device_remove_file().
>

True, will be fixed.

>> +static struct platform_driver wm8505fb_driver = {
>> +     .probe          = wm8505fb_probe,
>> +     .remove         = wm8505fb_remove,
>
> As your remove function is flagged devexit, this ought to be wrapped with
> __devexit_p(). This will save you a bit of space as the kernel will
> discard the remove function outright if you're not using this as a module
> or with hotplug. The same applies to your other remove functions too.
>

Ok, thanks for pointing out!

>> diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
>> new file mode 100644
>> index 0000000..b201a60
>> --- /dev/null
>> +++ b/drivers/video/wmt_ge_rops.c
>> +EXPORT_SYMBOL(wmt_ge_fillrect);
>> +EXPORT_SYMBOL(wmt_ge_copyarea);
>> +EXPORT_SYMBOL(wmt_ge_sync);
>> +
> ...
>
> Is there a particular reason why you are favouring EXPORT_SYMBOL? In
> general we prefer that new infrastructure patches and the like stick with
> EXPORT_SYMBOL_GPL, as this discourages use by non-GPLed modules going
> forward.
>

Well, I have no personal preference towards these, so I just took what
was in cfb*.c as a guidance. If the *_GPL variant is more welcome, it
can be changed.

>> +static int __devinit wmt_ge_rops_probe(struct platform_device *pdev)
>> +{
> ...
>> +     regbase = ioremap(res->start, resource_size(res));
>> +     if (regbase == NULL) {
>> +             dev_err(&pdev->dev, "failed to map I/O memory\n");
>> +             ret = -EBUSY;
>> +             goto error;
>> +     }
>> +
> You might also want to do something like:
>
>        /* Only one ROP engine is presently supported. */
>        if (unlikely(regbase)) {
>                WARN_ON(1);
>                return -EBUSY;
>        }
>
>        regbase = ioremap(...);
>        ...
>

But for that I'd have to initialize regbase to NULL (so as not to use
an uninitialized variable), wouldn't I? checkpatch.pl complains on
that...

>> +     writel(1, regbase + GE_ENABLE_OFF);
>> +     printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n");
>> +
>> +     return 0;
>> +
>> +error:
>> +     return ret;
>> +}
>> +
>> +static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
>> +{
>> +     iounmap(regbase);
>> +     return 0;
>> +}
>> +
> You're missing a:
>
>        writel(0, regbase + GE_ENABLE_OFF);
>
> here?
>

In fact, this module only uses a subset of GE functions, so I'm
somewhat reluctant to disable the hardware altogether when unloading
the module. And should the hardware really be disabled when the driver
is removed?

>> +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com");
>> +MODULE_DESCRIPTION("Accelerators for raster operations using "
>> +                "WonderMedia Graphics Engine");
>> +MODULE_LICENSE("GPL");
>
> Your other drivers appear to be lacking AUTHOR and DESCRIPTION
> definitions.
>

Will be fixed.

Thanks,
Alexey

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

* Re: [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08 21:15         ` Alexey Charkov
@ 2010-11-08 21:30           ` Paul Mundt
  2010-11-08 23:42             ` [PATCH 6/6 v4] " Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Paul Mundt @ 2010-11-08 21:30 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Andrew Morton,
	Guennadi Liakhovetski, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

On Tue, Nov 09, 2010 at 12:15:19AM +0300, Alexey Charkov wrote:
> 2010/11/8 Paul Mundt <lethal@linux-sh.org>:
> >> + ?? ?? if (!fbi)
> >> + ?? ?? ?? ?? ?? ?? return 0;
> >> +
> > I would kill this test as well. If this ever triggers, something horribly
> > wrong has happened and you likely have bigger things to worry about.
> 
> But a couple of extra instructions for error handling to hold in the
> kernel binary should not hurt, should they? Are there benefits aside
> from code compaction?
> 
It's not a realistic situation. The only way this would trigger is if the
pointer you got handed is one that didn't go through the probe path or
was otherwise corrupted. If it was corrupted, you're going to notice
regardless. The driver core does sensible refcounting already, there's no
need to second guess it.

> >> diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
> >> new file mode 100644
> >> index 0000000..b201a60
> >> --- /dev/null
> >> +++ b/drivers/video/wmt_ge_rops.c
> >> +EXPORT_SYMBOL(wmt_ge_fillrect);
> >> +EXPORT_SYMBOL(wmt_ge_copyarea);
> >> +EXPORT_SYMBOL(wmt_ge_sync);
> >> +
> > ...
> >
> > Is there a particular reason why you are favouring EXPORT_SYMBOL? In
> > general we prefer that new infrastructure patches and the like stick with
> > EXPORT_SYMBOL_GPL, as this discourages use by non-GPLed modules going
> > forward.
> >
> 
> Well, I have no personal preference towards these, so I just took what
> was in cfb*.c as a guidance. If the *_GPL variant is more welcome, it
> can be changed.
> 
Those exports go back a ways. The idea with the _GPL exports was not to
change symbol export behaviour retroactively, so the old ones stay the
way they were and newer stuff can choose which way it wants to go. If you
are not using this driver with an out-of-tree non-GPLed module then you
are advised to use the GPL variants so others don't either.

> >> +static int __devinit wmt_ge_rops_probe(struct platform_device *pdev)
> >> +{
> > ...
> >> + ?? ?? regbase = ioremap(res->start, resource_size(res));
> >> + ?? ?? if (regbase == NULL) {
> >> + ?? ?? ?? ?? ?? ?? dev_err(&pdev->dev, "failed to map I/O memory\n");
> >> + ?? ?? ?? ?? ?? ?? ret = -EBUSY;
> >> + ?? ?? ?? ?? ?? ?? goto error;
> >> + ?? ?? }
> >> +
> > You might also want to do something like:
> >
> > ?? ?? ?? ??/* Only one ROP engine is presently supported. */
> > ?? ?? ?? ??if (unlikely(regbase)) {
> > ?? ?? ?? ?? ?? ?? ?? ??WARN_ON(1);
> > ?? ?? ?? ?? ?? ?? ?? ??return -EBUSY;
> > ?? ?? ?? ??}
> >
> > ?? ?? ?? ??regbase = ioremap(...);
> > ?? ?? ?? ??...
> >
> 
> But for that I'd have to initialize regbase to NULL (so as not to use
> an uninitialized variable), wouldn't I? checkpatch.pl complains on
> that...
> 
No, regbase is BSS initialized, so it will already be cleared.

> >> +static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
> >> +{
> >> + ?? ?? iounmap(regbase);
> >> + ?? ?? return 0;
> >> +}
> >> +
> > You're missing a:
> >
> > ?? ?? ?? ??writel(0, regbase + GE_ENABLE_OFF);
> >
> > here?
> >
> 
> In fact, this module only uses a subset of GE functions, so I'm
> somewhat reluctant to disable the hardware altogether when unloading
> the module. And should the hardware really be disabled when the driver
> is removed?
> 
You're the only one who can answer that. I just noticed in your other
drivers that this is the pattern that you opted for, so I thought
that perhaps this was an oversight in the rop engine code.

Having said that, the general expectation is that a remove will balance
out the probe. If the probe is enabling random blocks then the remove
should be disabling them. If this driver is primarily used as a client by
the other drivers and you're concerned from it being ripped out
underneath them, then a bit more thinking and refcounting is needed. This
is however something that can be done later if its a direction you wish
to head in.

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

* [PATCH 6/6 v4] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08 21:30           ` Paul Mundt
@ 2010-11-08 23:42             ` Alexey Charkov
  2010-11-08 23:54               ` Paul Mundt
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-08 23:42 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Andrew Morton, Guennadi Liakhovetski, Florian Tobias Schandinat,
	Ralf Baechle, David S. Miller, linux-kernel

>From 95942cc0cd0909d700c236ba63d3488ec9fccf1a Mon Sep 17 00:00:00 2001
From: Alexey Charkov <alchark@gmail.com>
Date: Tue, 9 Nov 2010 02:25:40 +0300
Subject: [PATCH 3/3] ARM: Add support for the display controllers in VT8500 and WM8505

This adds drivers for the LCD controller found in VIA VT8500 SoC,
GOVR display controller found in WonderMedia WM8505 SoC and for the
Graphics Engine present in both of them that provides hardware
accelerated raster operations (used for copyarea and fillrect).

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This incorporates fixes to the issues last mentioned by Paul.

I just dropped <asm/irq.h>, as that seems to be not used at all currently
(must have been some old cut-and-paste as well). As for the Graphics
Engine, I decided not to disable it for now at device exit time, at least
until we reach a conclusion on how or whether to implement support for its
alpha-mixing capabilities. Unlike with display drivers, this should have no
user-visible consequences. This can be changed later, when time comes for
device clocks and power management support.

Thanks,
Alexey

 drivers/video/Kconfig         |   26 +++
 drivers/video/Makefile        |    3 +
 drivers/video/vt8500lcdfb.c   |  447 +++++++++++++++++++++++++++++++++++++++++
 drivers/video/vt8500lcdfb.h   |   34 +++
 drivers/video/wm8505fb.c      |  422 ++++++++++++++++++++++++++++++++++++++
 drivers/video/wm8505fb_regs.h |   76 +++++++
 drivers/video/wmt_ge_rops.c   |  192 ++++++++++++++++++
 drivers/video/wmt_ge_rops.h   |    5 +
 8 files changed, 1205 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/vt8500lcdfb.c
 create mode 100644 drivers/video/vt8500lcdfb.h
 create mode 100644 drivers/video/wm8505fb.c
 create mode 100644 drivers/video/wm8505fb_regs.h
 create mode 100644 drivers/video/wmt_ge_rops.c
 create mode 100644 drivers/video/wmt_ge_rops.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 27c1fb4..954f6e9 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -186,6 +186,14 @@ config FB_SYS_FOPS
        depends on FB
        default n
 
+config FB_WMT_GE_ROPS
+	tristate
+	depends on FB
+	default n
+	---help---
+	  Include functions for accelerated rectangle filling and area
+	  copying using WonderMedia Graphics Engine operations.
+
 config FB_DEFERRED_IO
 	bool
 	depends on FB
@@ -1722,6 +1730,24 @@ config FB_AU1200
 	  various panels and CRTs by passing in kernel cmd line option
 	  au1200fb:panel=<name>.
 
+config FB_VT8500
+	bool "VT8500 LCD Driver"
+	depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500
+	select FB_WMT_GE_ROPS
+	select FB_SYS_IMAGEBLIT
+	help
+	  This is the framebuffer driver for VIA VT8500 integrated LCD
+	  controller.
+
+config FB_WM8505
+	bool "WM8505 frame buffer support"
+	depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505
+	select FB_WMT_GE_ROPS
+	select FB_SYS_IMAGEBLIT
+	help
+	  This is the framebuffer driver for WonderMedia WM8505
+	  integrated LCD controller.
+
 source "drivers/video/geode/Kconfig"
 
 config FB_HIT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 485e8ed..8d916dc 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB)       += svgalib.o
 obj-$(CONFIG_FB_MACMODES)      += macmodes.o
 obj-$(CONFIG_FB_DDC)           += fb_ddc.o
 obj-$(CONFIG_FB_DEFERRED_IO)   += fb_defio.o
+obj-$(CONFIG_FB_WMT_GE_ROPS)   += wmt_ge_rops.o
 
 # Hardware specific drivers go first
 obj-$(CONFIG_FB_AMIGA)            += amifb.o c2p_planar.o
@@ -104,6 +105,8 @@ obj-$(CONFIG_FB_W100)		  += w100fb.o
 obj-$(CONFIG_FB_TMIO)		  += tmiofb.o
 obj-$(CONFIG_FB_AU1100)		  += au1100fb.o
 obj-$(CONFIG_FB_AU1200)		  += au1200fb.o
+obj-$(CONFIG_FB_VT8500)		  += vt8500lcdfb.o
+obj-$(CONFIG_FB_WM8505)		  += wm8505fb.o
 obj-$(CONFIG_FB_PMAG_AA)	  += pmag-aa-fb.o
 obj-$(CONFIG_FB_PMAG_BA)	  += pmag-ba-fb.o
 obj-$(CONFIG_FB_PMAGB_B)	  += pmagb-b-fb.o
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
new file mode 100644
index 0000000..7617f12
--- /dev/null
+++ b/drivers/video/vt8500lcdfb.c
@@ -0,0 +1,447 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on skeletonfb.c and pxafb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <mach/vt8500fb.h>
+
+#include "vt8500lcdfb.h"
+#include "wmt_ge_rops.h"
+
+#define to_vt8500lcd_info(__info) container_of(__info, \
+						struct vt8500lcd_info, fb)
+
+static int vt8500lcd_set_par(struct fb_info *info)
+{
+	struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+	int reg_bpp = 5; /* 16bpp */
+	int i;
+	unsigned long control0;
+
+	if (!fbi)
+		return -EINVAL;
+
+	if (info->var.bits_per_pixel <= 8) {
+		/* palettized */
+		info->var.red.offset    = 0;
+		info->var.red.length    = info->var.bits_per_pixel;
+		info->var.red.msb_right = 0;
+
+		info->var.green.offset  = 0;
+		info->var.green.length  = info->var.bits_per_pixel;
+		info->var.green.msb_right = 0;
+
+		info->var.blue.offset   = 0;
+		info->var.blue.length   = info->var.bits_per_pixel;
+		info->var.blue.msb_right = 0;
+
+		info->var.transp.offset = 0;
+		info->var.transp.length = 0;
+		info->var.transp.msb_right = 0;
+
+		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+		info->fix.line_length = info->var.xres_virtual /
+						(8/info->var.bits_per_pixel);
+	} else {
+		/* non-palettized */
+		info->var.transp.offset = 0;
+		info->var.transp.length = 0;
+		info->var.transp.msb_right = 0;
+
+		if (info->var.bits_per_pixel == 16) {
+			/* RGB565 */
+			info->var.red.offset = 11;
+			info->var.red.length = 5;
+			info->var.red.msb_right = 0;
+			info->var.green.offset = 5;
+			info->var.green.length = 6;
+			info->var.green.msb_right = 0;
+			info->var.blue.offset = 0;
+			info->var.blue.length = 5;
+			info->var.blue.msb_right = 0;
+		} else {
+			/* Equal depths per channel */
+			info->var.red.offset = info->var.bits_per_pixel
+							* 2 / 3;
+			info->var.red.length = info->var.bits_per_pixel / 3;
+			info->var.red.msb_right = 0;
+			info->var.green.offset = info->var.bits_per_pixel / 3;
+			info->var.green.length = info->var.bits_per_pixel / 3;
+			info->var.green.msb_right = 0;
+			info->var.blue.offset = 0;
+			info->var.blue.length = info->var.bits_per_pixel / 3;
+			info->var.blue.msb_right = 0;
+		}
+
+		info->fix.visual = FB_VISUAL_TRUECOLOR;
+		info->fix.line_length = info->var.bits_per_pixel > 16 ?
+					info->var.xres_virtual << 2 :
+					info->var.xres_virtual << 1;
+	}
+
+	for (i = 0; i < 8; i++) {
+		if (bpp_values[i] == info->var.bits_per_pixel) {
+			reg_bpp = i;
+			continue;
+		}
+	}
+
+	control0 = readl(fbi->regbase) & ~0xf;
+	writel(0, fbi->regbase);
+	while (readl(fbi->regbase + 0x38) & 0x10)
+		/* wait */;
+	writel((((info->var.hsync_len - 1) & 0x3f) << 26)
+		| ((info->var.left_margin & 0xff) << 18)
+		| (((info->var.xres - 1) & 0x3ff) << 8)
+		| (info->var.right_margin & 0xff), fbi->regbase + 0x4);
+	writel((((info->var.vsync_len - 1) & 0x3f) << 26)
+		| ((info->var.upper_margin & 0xff) << 18)
+		| (((info->var.yres - 1) & 0x3ff) << 8)
+		| (info->var.lower_margin & 0xff), fbi->regbase + 0x8);
+	writel((((info->var.yres - 1) & 0x400) << 2)
+		| ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10);
+	writel(0x80000000, fbi->regbase + 0x20);
+	writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase);
+
+	return 0;
+}
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   struct fb_info *info) {
+	struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+	int ret = 1;
+	unsigned int val;
+	if (regno >= 256)
+		return -EINVAL;
+
+	if (info->var.grayscale)
+		red = green = blue =
+			(19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+	switch (fbi->fb.fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+		if (regno < 16) {
+			u32 *pal = fbi->fb.pseudo_palette;
+
+			val  = chan_to_field(red, &fbi->fb.var.red);
+			val |= chan_to_field(green, &fbi->fb.var.green);
+			val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+			pal[regno] = val;
+			ret = 0;
+		}
+		break;
+
+	case FB_VISUAL_STATIC_PSEUDOCOLOR:
+	case FB_VISUAL_PSEUDOCOLOR:
+		writew((red & 0xf800)
+		      | ((green >> 5) & 0x7e0)
+		      | ((blue >> 11) & 0x1f),
+		       fbi->palette_cpu + sizeof(u16) * regno);
+		break;
+	}
+
+	return ret;
+}
+
+static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd,
+			 unsigned long arg)
+{
+	int ret = 0;
+	struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+
+	if (cmd == FBIO_WAITFORVSYNC) {
+		/* Unmask End of Frame interrupt */
+		writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c);
+		ret = wait_event_interruptible_timeout(fbi->wait,
+			readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10);
+		/* Mask back to reduce unwanted interrupt traffic */
+		writel(0xffffffff, fbi->regbase + 0x3c);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			return -ETIMEDOUT;
+	}
+
+	return ret;
+}
+
+static int vt8500lcd_pan_display(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	unsigned pixlen = info->fix.line_length / info->var.xres_virtual;
+	unsigned off = pixlen * var->xoffset
+		      + info->fix.line_length * var->yoffset;
+	struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+
+	writel((1 << 31)
+		| (((var->xres_virtual - var->xres) * pixlen / 4) << 20)
+		| (off >> 2), fbi->regbase + 0x20);
+	return 0;
+}
+
+static struct fb_ops vt8500lcd_ops = {
+	.owner		= THIS_MODULE,
+	.fb_set_par	= vt8500lcd_set_par,
+	.fb_setcolreg	= vt8500lcd_setcolreg,
+	.fb_fillrect	= wmt_ge_fillrect,
+	.fb_copyarea	= wmt_ge_copyarea,
+	.fb_imageblit	= sys_imageblit,
+	.fb_sync	= wmt_ge_sync,
+	.fb_ioctl	= vt8500lcd_ioctl,
+	.fb_pan_display	= vt8500lcd_pan_display,
+};
+
+static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id)
+{
+	struct vt8500lcd_info *fbi = dev_id;
+
+	if (readl(fbi->regbase + 0x38) & (1 << 3))
+		wake_up_interruptible(&fbi->wait);
+
+	writel(0xffffffff, fbi->regbase + 0x38);
+	return IRQ_HANDLED;
+}
+
+static int __devinit vt8500lcd_probe(struct platform_device *pdev)
+{
+	struct vt8500lcd_info *fbi;
+	struct resource *res;
+	struct vt8500fb_platform_data *pdata = pdev->dev.platform_data;
+	void *addr;
+	int irq, ret;
+
+	ret = -ENOMEM;
+	fbi = NULL;
+
+	fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16,
+							GFP_KERNEL);
+	if (!fbi) {
+		dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	strcpy(fbi->fb.fix.id, "VT8500 LCD");
+
+	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
+	fbi->fb.fix.xpanstep	= 0;
+	fbi->fb.fix.ypanstep	= 1;
+	fbi->fb.fix.ywrapstep	= 0;
+	fbi->fb.fix.accel	= FB_ACCEL_NONE;
+
+	fbi->fb.var.nonstd	= 0;
+	fbi->fb.var.activate	= FB_ACTIVATE_NOW;
+	fbi->fb.var.height	= -1;
+	fbi->fb.var.width	= -1;
+	fbi->fb.var.vmode	= FB_VMODE_NONINTERLACED;
+
+	fbi->fb.fbops		= &vt8500lcd_ops;
+	fbi->fb.flags		= FBINFO_DEFAULT
+				| FBINFO_HWACCEL_COPYAREA
+				| FBINFO_HWACCEL_FILLRECT
+				| FBINFO_HWACCEL_YPAN
+				| FBINFO_VIRTFB
+				| FBINFO_PARTIAL_PAN_OK;
+	fbi->fb.node		= -1;
+
+	addr = fbi;
+	addr = addr + sizeof(struct vt8500lcd_info);
+	fbi->fb.pseudo_palette	= addr;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		ret = -ENODEV;
+		goto failed_fbi;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), "vt8500lcd");
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		ret = -EBUSY;
+		goto failed_fbi;
+	}
+
+	fbi->regbase = ioremap(res->start, resource_size(res));
+	if (fbi->regbase == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		ret = -EBUSY;
+		goto failed_free_res;
+	}
+
+	fbi->fb.fix.smem_start	= pdata->video_mem_phys;
+	fbi->fb.fix.smem_len	= pdata->video_mem_len;
+	fbi->fb.screen_base	= pdata->video_mem_virt;
+
+	fbi->palette_size	= PAGE_ALIGN(512);
+	fbi->palette_cpu	= dma_alloc_coherent(&pdev->dev,
+						     fbi->palette_size,
+						     &fbi->palette_phys,
+						     GFP_KERNEL);
+	if (fbi->palette_cpu == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate palette buffer\n");
+		ret = -ENOMEM;
+		goto failed_free_io;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no IRQ defined\n");
+		ret = -ENODEV;
+		goto failed_free_palette;
+	}
+
+	ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi);
+	if (ret) {
+		dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
+		ret = -EBUSY;
+		goto failed_free_palette;
+	}
+
+	init_waitqueue_head(&fbi->wait);
+
+	if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+		dev_err(&pdev->dev, "Failed to allocate color map\n");
+		ret = -ENOMEM;
+		goto failed_free_irq;
+	}
+
+	fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+	fbi->fb.var.bits_per_pixel	= pdata->bpp;
+	fbi->fb.var.xres_virtual	= pdata->xres_virtual;
+	fbi->fb.var.yres_virtual	= pdata->yres_virtual;
+
+	ret = vt8500lcd_set_par(&fbi->fb);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to set parameters\n");
+		goto failed_free_cmap;
+	}
+
+	writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c);
+	writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18);
+
+	platform_set_drvdata(pdev, fbi);
+
+	ret = register_framebuffer(&fbi->fb);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Failed to register framebuffer device: %d\n", ret);
+		goto failed_free_cmap;
+	}
+
+	/*
+	 * Ok, now enable the LCD controller
+	 */
+	writel(readl(fbi->regbase) | 1, fbi->regbase);
+
+	return 0;
+
+failed_free_cmap:
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_irq:
+	free_irq(irq, fbi);
+failed_free_palette:
+	dma_free_coherent(&pdev->dev, fbi->palette_size,
+			  fbi->palette_cpu, fbi->palette_phys);
+failed_free_io:
+	iounmap(fbi->regbase);
+failed_free_res:
+	release_mem_region(res->start, resource_size(res));
+failed_fbi:
+	platform_set_drvdata(pdev, NULL);
+	kfree(fbi);
+failed:
+	return ret;
+}
+
+static int __devexit vt8500lcd_remove(struct platform_device *pdev)
+{
+	struct vt8500lcd_info *fbi = platform_get_drvdata(pdev);
+	struct resource *res;
+	int irq;
+
+	unregister_framebuffer(&fbi->fb);
+
+	writel(0, fbi->regbase);
+
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+
+	irq = platform_get_irq(pdev, 0);
+	free_irq(irq, fbi);
+
+	dma_free_coherent(&pdev->dev, fbi->palette_size,
+			  fbi->palette_cpu, fbi->palette_phys);
+
+	iounmap(fbi->regbase);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(fbi);
+
+	return 0;
+}
+
+static struct platform_driver vt8500lcd_driver = {
+	.probe		= vt8500lcd_probe,
+	.remove		= __devexit_p(vt8500lcd_remove),
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "vt8500-lcd",
+	},
+};
+
+static int __init vt8500lcd_init(void)
+{
+	return platform_driver_register(&vt8500lcd_driver);
+}
+
+static void __exit vt8500lcd_exit(void)
+{
+	platform_driver_unregister(&vt8500lcd_driver);
+}
+
+module_init(vt8500lcd_init);
+module_exit(vt8500lcd_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("LCD controller driver for VIA VT8500");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/vt8500lcdfb.h b/drivers/video/vt8500lcdfb.h
new file mode 100644
index 0000000..36ca3ca
--- /dev/null
+++ b/drivers/video/vt8500lcdfb.h
@@ -0,0 +1,34 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+struct vt8500lcd_info {
+	struct fb_info		fb;
+	void __iomem		*regbase;
+	void __iomem		*palette_cpu;
+	dma_addr_t		palette_phys;
+	size_t			palette_size;
+	wait_queue_head_t	wait;
+};
+
+static int bpp_values[] = {
+	1,
+	2,
+	4,
+	8,
+	12,
+	16,
+	18,
+	24,
+};
diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c
new file mode 100644
index 0000000..e37251b
--- /dev/null
+++ b/drivers/video/wm8505fb.c
@@ -0,0 +1,422 @@
+/*
+ *  WonderMedia WM8505 Frame Buffer device driver
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *    Based on vt8500lcdfb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <mach/vt8500fb.h>
+
+#include "wm8505fb_regs.h"
+#include "wmt_ge_rops.h"
+
+#define DRIVER_NAME "wm8505-fb"
+
+#define to_wm8505fb_info(__info) container_of(__info, \
+						struct wm8505fb_info, fb)
+struct wm8505fb_info {
+	struct fb_info		fb;
+	void __iomem		*regbase;
+	unsigned int		contrast;
+};
+
+
+static int wm8505fb_init_hw(struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+	int i;
+
+	/* I know the purpose only of few registers, so clear unknown */
+	for (i = 0; i < 0x200; i += 4)
+		writel(0, fbi->regbase + i);
+
+	/* Set frame buffer address */
+	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
+	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
+
+	/* Set in-memory picture format to RGB 32bpp */
+	writel(0x1c,		       fbi->regbase + WMT_GOVR_COLORSPACE);
+	writel(1,		       fbi->regbase + WMT_GOVR_COLORSPACE1);
+
+	/* Virtual buffer size */
+	writel(info->var.xres,	       fbi->regbase + WMT_GOVR_XRES);
+	writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL);
+
+	/* black magic ;) */
+	writel(0xf,		       fbi->regbase + WMT_GOVR_FHI);
+	writel(4,		       fbi->regbase + WMT_GOVR_DVO_SET);
+	writel(1,		       fbi->regbase + WMT_GOVR_MIF_ENABLE);
+	writel(1,		       fbi->regbase + WMT_GOVR_REG_UPDATE);
+
+	return 0;
+}
+
+static int wm8505fb_set_timing(struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+	int h_start = info->var.left_margin;
+	int h_end = h_start + info->var.xres;
+	int h_all = h_end + info->var.right_margin;
+	int h_sync = info->var.hsync_len;
+
+	int v_start = info->var.upper_margin;
+	int v_end = v_start + info->var.yres;
+	int v_all = v_end + info->var.lower_margin;
+	int v_sync = info->var.vsync_len + 1;
+
+	writel(0, fbi->regbase + WMT_GOVR_TG);
+
+	writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START);
+	writel(h_end,   fbi->regbase + WMT_GOVR_TIMING_H_END);
+	writel(h_all,   fbi->regbase + WMT_GOVR_TIMING_H_ALL);
+	writel(h_sync,  fbi->regbase + WMT_GOVR_TIMING_H_SYNC);
+
+	writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START);
+	writel(v_end,   fbi->regbase + WMT_GOVR_TIMING_V_END);
+	writel(v_all,   fbi->regbase + WMT_GOVR_TIMING_V_ALL);
+	writel(v_sync,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+
+	writel(1, fbi->regbase + WMT_GOVR_TG);
+
+	return 0;
+}
+
+
+static int wm8505fb_set_par(struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+	if (!fbi)
+		return -EINVAL;
+
+	if (info->var.bits_per_pixel == 32) {
+		info->var.red.offset = 16;
+		info->var.red.length = 8;
+		info->var.red.msb_right = 0;
+		info->var.green.offset = 8;
+		info->var.green.length = 8;
+		info->var.green.msb_right = 0;
+		info->var.blue.offset = 0;
+		info->var.blue.length = 8;
+		info->var.blue.msb_right = 0;
+		info->fix.visual = FB_VISUAL_TRUECOLOR;
+		info->fix.line_length = info->var.xres_virtual << 2;
+	}
+
+	wm8505fb_set_timing(info);
+
+	writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast,
+		fbi->regbase + WMT_GOVR_CONTRAST);
+
+	return 0;
+}
+
+static ssize_t contrast_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+	return sprintf(buf, "%d\n", fbi->contrast);
+}
+
+static ssize_t contrast_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+	unsigned long tmp;
+
+	if (strict_strtoul(buf, 10, &tmp) || (tmp > 0xff))
+		return -EINVAL;
+	fbi->contrast = tmp;
+
+	wm8505fb_set_par(info);
+
+	return count;
+}
+
+static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store);
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   struct fb_info *info) {
+	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+	int ret = 1;
+	unsigned int val;
+	if (regno >= 256)
+		return -EINVAL;
+
+	if (info->var.grayscale)
+		red = green = blue =
+			(19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+	switch (fbi->fb.fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+		if (regno < 16) {
+			u32 *pal = info->pseudo_palette;
+
+			val  = chan_to_field(red, &fbi->fb.var.red);
+			val |= chan_to_field(green, &fbi->fb.var.green);
+			val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+			pal[regno] = val;
+			ret = 0;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+	writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN);
+	writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN);
+	return 0;
+}
+
+static int wm8505fb_blank(int blank, struct fb_info *info)
+{
+	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+	switch (blank) {
+	case FB_BLANK_UNBLANK:
+		wm8505fb_set_timing(info);
+		break;
+	default:
+		writel(0,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+		break;
+	}
+
+	return 0;
+}
+
+static struct fb_ops wm8505fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_set_par	= wm8505fb_set_par,
+	.fb_setcolreg	= wm8505fb_setcolreg,
+	.fb_fillrect	= wmt_ge_fillrect,
+	.fb_copyarea	= wmt_ge_copyarea,
+	.fb_imageblit	= sys_imageblit,
+	.fb_sync	= wmt_ge_sync,
+	.fb_pan_display	= wm8505fb_pan_display,
+	.fb_blank	= wm8505fb_blank,
+};
+
+static int __devinit wm8505fb_probe(struct platform_device *pdev)
+{
+	struct wm8505fb_info	*fbi;
+	struct resource		*res;
+	void			*addr;
+	struct vt8500fb_platform_data *pdata;
+	int ret;
+
+	pdata = pdev->dev.platform_data;
+
+	ret = -ENOMEM;
+	fbi = NULL;
+
+	fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16,
+							GFP_KERNEL);
+	if (!fbi) {
+		dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	strcpy(fbi->fb.fix.id, DRIVER_NAME);
+
+	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
+	fbi->fb.fix.xpanstep	= 1;
+	fbi->fb.fix.ypanstep	= 1;
+	fbi->fb.fix.ywrapstep	= 0;
+	fbi->fb.fix.accel	= FB_ACCEL_NONE;
+
+	fbi->fb.fbops		= &wm8505fb_ops;
+	fbi->fb.flags		= FBINFO_DEFAULT
+				| FBINFO_HWACCEL_COPYAREA
+				| FBINFO_HWACCEL_FILLRECT
+				| FBINFO_HWACCEL_XPAN
+				| FBINFO_HWACCEL_YPAN
+				| FBINFO_VIRTFB
+				| FBINFO_PARTIAL_PAN_OK;
+	fbi->fb.node		= -1;
+
+	addr = fbi;
+	addr = addr + sizeof(struct wm8505fb_info);
+	fbi->fb.pseudo_palette	= addr;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		ret = -ENODEV;
+		goto failed_fbi;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), "wm8505fb");
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		ret = -EBUSY;
+		goto failed_fbi;
+	}
+
+	fbi->regbase = ioremap(res->start, resource_size(res));
+	if (fbi->regbase == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		ret = -EBUSY;
+		goto failed_free_res;
+	}
+
+	fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+
+	fbi->fb.var.nonstd		= 0;
+	fbi->fb.var.activate		= FB_ACTIVATE_NOW;
+
+	fbi->fb.var.height		= -1;
+	fbi->fb.var.width		= -1;
+	fbi->fb.var.xres_virtual	= pdata->xres_virtual;
+	fbi->fb.var.yres_virtual	= pdata->yres_virtual;
+	fbi->fb.var.bits_per_pixel	= pdata->bpp;
+
+	fbi->fb.fix.smem_start	= pdata->video_mem_phys;
+	fbi->fb.fix.smem_len	= pdata->video_mem_len;
+	fbi->fb.screen_base	= pdata->video_mem_virt;
+	fbi->fb.screen_size	= pdata->video_mem_len;
+
+	if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+		dev_err(&pdev->dev, "Failed to allocate color map\n");
+		ret = -ENOMEM;
+		goto failed_free_io;
+	}
+
+	wm8505fb_init_hw(&fbi->fb);
+
+	fbi->contrast = 0x80;
+	ret = wm8505fb_set_par(&fbi->fb);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to set parameters\n");
+		goto failed_free_cmap;
+	}
+
+	platform_set_drvdata(pdev, fbi);
+
+	ret = register_framebuffer(&fbi->fb);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Failed to register framebuffer device: %d\n", ret);
+		goto failed_free_cmap;
+	}
+
+	ret = device_create_file(&pdev->dev, &dev_attr_contrast);
+	if (ret < 0) {
+		printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
+			fbi->fb.node, ret);
+	}
+
+	printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n",
+	       fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start,
+	       fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1);
+
+	return 0;
+
+failed_free_cmap:
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_io:
+	iounmap(fbi->regbase);
+failed_free_res:
+	release_mem_region(res->start, resource_size(res));
+failed_fbi:
+	platform_set_drvdata(pdev, NULL);
+	kfree(fbi);
+failed:
+	return ret;
+}
+
+static int __devexit wm8505fb_remove(struct platform_device *pdev)
+{
+	struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	device_remove_file(&pdev->dev, &dev_attr_contrast);
+
+	unregister_framebuffer(&fbi->fb);
+
+	writel(0, fbi->regbase);
+
+	if (fbi->fb.cmap.len)
+		fb_dealloc_cmap(&fbi->fb.cmap);
+
+	iounmap(fbi->regbase);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(fbi);
+
+	return 0;
+}
+
+static struct platform_driver wm8505fb_driver = {
+	.probe		= wm8505fb_probe,
+	.remove		= __devexit_p(wm8505fb_remove),
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= DRIVER_NAME,
+	},
+};
+
+static int __init wm8505fb_init(void)
+{
+	return platform_driver_register(&wm8505fb_driver);
+}
+
+static void __exit wm8505fb_exit(void)
+{
+	platform_driver_unregister(&wm8505fb_driver);
+}
+
+module_init(wm8505fb_init);
+module_exit(wm8505fb_exit);
+
+MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
+MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wm8505fb_regs.h b/drivers/video/wm8505fb_regs.h
new file mode 100644
index 0000000..4dd4166
--- /dev/null
+++ b/drivers/video/wm8505fb_regs.h
@@ -0,0 +1,76 @@
+/*
+ *  GOVR registers list for WM8505 chips
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *   Based on VIA/WonderMedia wm8510-govrh-reg.h
+ *   http://github.com/projectgus/kernel_wm8505/blob/wm8505_2.6.29/
+ *         drivers/video/wmt/register/wm8510/wm8510-govrh-reg.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _WM8505FB_REGS_H
+#define _WM8505FB_REGS_H
+
+/*
+ * Color space select register, default value 0x1c
+ *   BIT0 GOVRH_DVO_YUV2RGB_ENABLE
+ *   BIT1 GOVRH_VGA_YUV2RGB_ENABLE
+ *   BIT2 GOVRH_RGB_MODE
+ *   BIT3 GOVRH_DAC_CLKINV
+ *   BIT4 GOVRH_BLANK_ZERO
+ */
+#define WMT_GOVR_COLORSPACE	0x1e4
+/*
+ * Another colorspace select register, default value 1
+ *   BIT0 GOVRH_DVO_RGB
+ *   BIT1 GOVRH_DVO_YUV422
+ */
+#define WMT_GOVR_COLORSPACE1	 0x30
+
+#define WMT_GOVR_CONTRAST	0x1b8
+#define WMT_GOVR_BRGHTNESS	0x1bc /* incompatible with RGB? */
+
+/* Framubeffer address */
+#define WMT_GOVR_FBADDR		 0x90
+#define WMT_GOVR_FBADDR1	 0x94 /* UV offset in YUV mode */
+
+/* Offset of visible window */
+#define WMT_GOVR_XPAN		 0xa4
+#define WMT_GOVR_YPAN		 0xa0
+
+#define WMT_GOVR_XRES		 0x98
+#define WMT_GOVR_XRES_VIRTUAL	 0x9c
+
+#define WMT_GOVR_MIF_ENABLE	 0x80
+#define WMT_GOVR_FHI		 0xa8
+#define WMT_GOVR_REG_UPDATE	 0xe4
+
+/*
+ *   BIT0 GOVRH_DVO_OUTWIDTH
+ *   BIT1 GOVRH_DVO_SYNC_POLAR
+ *   BIT2 GOVRH_DVO_ENABLE
+ */
+#define WMT_GOVR_DVO_SET	0x148
+
+/* Timing generator? */
+#define WMT_GOVR_TG		0x100
+
+/* Timings */
+#define WMT_GOVR_TIMING_H_ALL	0x108
+#define WMT_GOVR_TIMING_V_ALL	0x10c
+#define WMT_GOVR_TIMING_V_START	0x110
+#define WMT_GOVR_TIMING_V_END	0x114
+#define WMT_GOVR_TIMING_H_START	0x118
+#define WMT_GOVR_TIMING_H_END	0x11c
+#define WMT_GOVR_TIMING_V_SYNC	0x128
+#define WMT_GOVR_TIMING_H_SYNC	0x12c
+
+#endif /* _WM8505FB_REGS_H */
diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
new file mode 100644
index 0000000..f31883f
--- /dev/null
+++ b/drivers/video/wmt_ge_rops.c
@@ -0,0 +1,192 @@
+/*
+ *  linux/drivers/video/wmt_ge_rops.c
+ *
+ *  Accelerators for raster operations using WonderMedia Graphics Engine
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include "fb_draw.h"
+
+#define GE_COMMAND_OFF		0x00
+#define GE_DEPTH_OFF		0x04
+#define GE_HIGHCOLOR_OFF	0x08
+#define GE_ROPCODE_OFF		0x14
+#define GE_FIRE_OFF		0x18
+#define GE_SRCBASE_OFF		0x20
+#define GE_SRCDISPW_OFF		0x24
+#define GE_SRCDISPH_OFF		0x28
+#define GE_SRCAREAX_OFF		0x2c
+#define GE_SRCAREAY_OFF		0x30
+#define GE_SRCAREAW_OFF		0x34
+#define GE_SRCAREAH_OFF		0x38
+#define GE_DESTBASE_OFF		0x3c
+#define GE_DESTDISPW_OFF	0x40
+#define GE_DESTDISPH_OFF	0x44
+#define GE_DESTAREAX_OFF	0x48
+#define GE_DESTAREAY_OFF	0x4c
+#define GE_DESTAREAW_OFF	0x50
+#define GE_DESTAREAH_OFF	0x54
+#define GE_PAT0C_OFF		0x88	/* Pattern 0 color */
+#define GE_ENABLE_OFF		0xec
+#define GE_INTEN_OFF		0xf0
+#define GE_STATUS_OFF		0xf8
+
+static void __iomem *regbase;
+
+void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+	unsigned long fg, pat;
+
+	if (p->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+	    p->fix.visual == FB_VISUAL_DIRECTCOLOR)
+		fg = ((u32 *) (p->pseudo_palette))[rect->color];
+	else
+		fg = rect->color;
+
+	pat = pixel_to_pat(p->var.bits_per_pixel, fg);
+
+	if (p->fbops->fb_sync)
+		p->fbops->fb_sync(p);
+
+	writel(p->var.bits_per_pixel == 32 ? 3 :
+	      (p->var.bits_per_pixel == 8 ? 0 : 1), regbase + GE_DEPTH_OFF);
+	writel(p->var.bits_per_pixel == 15 ? 1 : 0, regbase + GE_HIGHCOLOR_OFF);
+	writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+	writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+	writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+	writel(rect->dx, regbase + GE_DESTAREAX_OFF);
+	writel(rect->dy, regbase + GE_DESTAREAY_OFF);
+	writel(rect->width - 1, regbase + GE_DESTAREAW_OFF);
+	writel(rect->height - 1, regbase + GE_DESTAREAH_OFF);
+
+	writel(pat, regbase + GE_PAT0C_OFF);
+	writel(1, regbase + GE_COMMAND_OFF);
+	writel(rect->rop == ROP_XOR ? 0x5a : 0xf0, regbase + GE_ROPCODE_OFF);
+	writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL_GPL(wmt_ge_fillrect);
+
+void wmt_ge_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+	if (p->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (p->fbops->fb_sync)
+		p->fbops->fb_sync(p);
+
+	writel(p->var.bits_per_pixel > 16 ? 3 :
+	      (p->var.bits_per_pixel > 8 ? 1 : 0), regbase + GE_DEPTH_OFF);
+
+	writel(p->fix.smem_start, regbase + GE_SRCBASE_OFF);
+	writel(p->var.xres_virtual - 1, regbase + GE_SRCDISPW_OFF);
+	writel(p->var.yres_virtual - 1, regbase + GE_SRCDISPH_OFF);
+	writel(area->sx, regbase + GE_SRCAREAX_OFF);
+	writel(area->sy, regbase + GE_SRCAREAY_OFF);
+	writel(area->width - 1, regbase + GE_SRCAREAW_OFF);
+	writel(area->height - 1, regbase + GE_SRCAREAH_OFF);
+
+	writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+	writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+	writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+	writel(area->dx, regbase + GE_DESTAREAX_OFF);
+	writel(area->dy, regbase + GE_DESTAREAY_OFF);
+	writel(area->width - 1, regbase + GE_DESTAREAW_OFF);
+	writel(area->height - 1, regbase + GE_DESTAREAH_OFF);
+
+	writel(0xcc, regbase + GE_ROPCODE_OFF);
+	writel(1, regbase + GE_COMMAND_OFF);
+	writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL_GPL(wmt_ge_copyarea);
+
+int wmt_ge_sync(struct fb_info *p)
+{
+	int loops = 5000000;
+	while ((readl(regbase + GE_STATUS_OFF) & 4) && --loops)
+		cpu_relax();
+	return loops > 0 ? 0 : -EBUSY;
+}
+EXPORT_SYMBOL_GPL(wmt_ge_sync);
+
+static int __devinit wmt_ge_rops_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	/* Only one ROP engine is presently supported. */
+	if (unlikely(regbase)) {
+		WARN_ON(1);
+		return -EBUSY;
+	}
+
+	regbase = ioremap(res->start, resource_size(res));
+	if (regbase == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		ret = -EBUSY;
+		goto error;
+	}
+
+	writel(1, regbase + GE_ENABLE_OFF);
+	printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n");
+
+	return 0;
+
+error:
+	return ret;
+}
+
+static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
+{
+	iounmap(regbase);
+	return 0;
+}
+
+static struct platform_driver wmt_ge_rops_driver = {
+	.probe		= wmt_ge_rops_probe,
+	.remove		= __devexit_p(wmt_ge_rops_remove),
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "wmt_ge_rops",
+	},
+};
+
+static int __init wmt_ge_rops_init(void)
+{
+	return platform_driver_register(&wmt_ge_rops_driver);
+}
+
+static void __exit wmt_ge_rops_exit(void)
+{
+	platform_driver_unregister(&wmt_ge_rops_driver);
+}
+
+module_init(wmt_ge_rops_init);
+module_exit(wmt_ge_rops_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com");
+MODULE_DESCRIPTION("Accelerators for raster operations using "
+		   "WonderMedia Graphics Engine");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h
new file mode 100644
index 0000000..8738075
--- /dev/null
+++ b/drivers/video/wmt_ge_rops.h
@@ -0,0 +1,5 @@
+extern void wmt_ge_fillrect(struct fb_info *info,
+			    const struct fb_fillrect *rect);
+extern void wmt_ge_copyarea(struct fb_info *info,
+			    const struct fb_copyarea *area);
+extern int wmt_ge_sync(struct fb_info *info);
-- 
1.7.3.2


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

* Re: [PATCH 6/6 v4] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08 23:42             ` [PATCH 6/6 v4] " Alexey Charkov
@ 2010-11-08 23:54               ` Paul Mundt
  2010-11-09  0:03                 ` Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Paul Mundt @ 2010-11-08 23:54 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Andrew Morton,
	Guennadi Liakhovetski, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

On Tue, Nov 09, 2010 at 02:42:39AM +0300, Alexey Charkov wrote:
> From 95942cc0cd0909d700c236ba63d3488ec9fccf1a Mon Sep 17 00:00:00 2001
> From: Alexey Charkov <alchark@gmail.com>
> Date: Tue, 9 Nov 2010 02:25:40 +0300
> Subject: [PATCH 3/3] ARM: Add support for the display controllers in VT8500 and WM8505
> 
> This adds drivers for the LCD controller found in VIA VT8500 SoC,
> GOVR display controller found in WonderMedia WM8505 SoC and for the
> Graphics Engine present in both of them that provides hardware
> accelerated raster operations (used for copyarea and fillrect).
> 
> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> ---
> 
> This incorporates fixes to the issues last mentioned by Paul.
> 
> I just dropped <asm/irq.h>, as that seems to be not used at all currently
> (must have been some old cut-and-paste as well). As for the Graphics
> Engine, I decided not to disable it for now at device exit time, at least
> until we reach a conclusion on how or whether to implement support for its
> alpha-mixing capabilities. Unlike with display drivers, this should have no
> user-visible consequences. This can be changed later, when time comes for
> device clocks and power management support.
> 
Looks good to me!

Reviewed-by: Paul Mundt <lethal@linux-sh.org>

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

* Re: [PATCH 6/6 v4] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08 23:54               ` Paul Mundt
@ 2010-11-09  0:03                 ` Alexey Charkov
  2010-11-09  7:36                   ` Guennadi Liakhovetski
  2010-11-09  9:39                   ` Paul Mundt
  0 siblings, 2 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-09  0:03 UTC (permalink / raw)
  To: Paul Mundt
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Andrew Morton,
	Guennadi Liakhovetski, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

2010/11/8 Paul Mundt <lethal@linux-sh.org>:
> On Tue, Nov 09, 2010 at 02:42:39AM +0300, Alexey Charkov wrote:
>> From 95942cc0cd0909d700c236ba63d3488ec9fccf1a Mon Sep 17 00:00:00 2001
>> From: Alexey Charkov <alchark@gmail.com>
>> Date: Tue, 9 Nov 2010 02:25:40 +0300
>> Subject: [PATCH 3/3] ARM: Add support for the display controllers in VT8500 and WM8505
>>
>> This adds drivers for the LCD controller found in VIA VT8500 SoC,
>> GOVR display controller found in WonderMedia WM8505 SoC and for the
>> Graphics Engine present in both of them that provides hardware
>> accelerated raster operations (used for copyarea and fillrect).
>>
>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>> ---
>>
>> This incorporates fixes to the issues last mentioned by Paul.
>>
>> I just dropped <asm/irq.h>, as that seems to be not used at all currently
>> (must have been some old cut-and-paste as well). As for the Graphics
>> Engine, I decided not to disable it for now at device exit time, at least
>> until we reach a conclusion on how or whether to implement support for its
>> alpha-mixing capabilities. Unlike with display drivers, this should have no
>> user-visible consequences. This can be changed later, when time comes for
>> device clocks and power management support.
>>
> Looks good to me!
>
> Reviewed-by: Paul Mundt <lethal@linux-sh.org>
>

Thanks, Paul!

Is there anybody I should address specifically to get this merged?

Best regards,
Alexey

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

* Re: [PATCH 6/6 v4] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-09  0:03                 ` Alexey Charkov
@ 2010-11-09  7:36                   ` Guennadi Liakhovetski
  2010-11-09  9:39                   ` Paul Mundt
  1 sibling, 0 replies; 91+ messages in thread
From: Guennadi Liakhovetski @ 2010-11-09  7:36 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Paul Mundt, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Andrew Morton, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

On Tue, 9 Nov 2010, Alexey Charkov wrote:

> 2010/11/8 Paul Mundt <lethal@linux-sh.org>:
> > On Tue, Nov 09, 2010 at 02:42:39AM +0300, Alexey Charkov wrote:
> >> From 95942cc0cd0909d700c236ba63d3488ec9fccf1a Mon Sep 17 00:00:00 2001
> >> From: Alexey Charkov <alchark@gmail.com>
> >> Date: Tue, 9 Nov 2010 02:25:40 +0300
> >> Subject: [PATCH 3/3] ARM: Add support for the display controllers in VT8500 and WM8505
> >>
> >> This adds drivers for the LCD controller found in VIA VT8500 SoC,
> >> GOVR display controller found in WonderMedia WM8505 SoC and for the
> >> Graphics Engine present in both of them that provides hardware
> >> accelerated raster operations (used for copyarea and fillrect).
> >>
> >> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> >> ---
> >>
> >> This incorporates fixes to the issues last mentioned by Paul.
> >>
> >> I just dropped <asm/irq.h>, as that seems to be not used at all currently
> >> (must have been some old cut-and-paste as well). As for the Graphics
> >> Engine, I decided not to disable it for now at device exit time, at least
> >> until we reach a conclusion on how or whether to implement support for its
> >> alpha-mixing capabilities. Unlike with display drivers, this should have no
> >> user-visible consequences. This can be changed later, when time comes for
> >> device clocks and power management support.
> >>
> > Looks good to me!
> >
> > Reviewed-by: Paul Mundt <lethal@linux-sh.org>
> >
> 
> Thanks, Paul!
> 
> Is there anybody I should address specifically to get this merged?

In the absence of a fbdev maintainer, such patches, as long as they don't 
touch the core, apart from the obvious and trivial Makefile and Kconfig 
hunks, they normally get mainlined via respective arch trees, i.e., you 
can just push them the same way as your other SoC patches. OTOH you can 
also ask Andrew Morton to pull them.

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

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

* Re: [PATCH 6/6 v4] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-09  0:03                 ` Alexey Charkov
  2010-11-09  7:36                   ` Guennadi Liakhovetski
@ 2010-11-09  9:39                   ` Paul Mundt
  2010-11-09  9:49                     ` Alexey Charkov
  1 sibling, 1 reply; 91+ messages in thread
From: Paul Mundt @ 2010-11-09  9:39 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Andrew Morton,
	Guennadi Liakhovetski, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

On Tue, Nov 09, 2010 at 03:03:01AM +0300, Alexey Charkov wrote:
> Is there anybody I should address specifically to get this merged?
> 
I've set up an fbdev tree now, so I'll just take this through there. It
looks like the ordering shouldn't be important, so this can be applied
independent of the rest, and it simply won't show up until the other bits
have settled in .38.

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

* Re: [PATCH 6/6 v4] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-09  9:39                   ` Paul Mundt
@ 2010-11-09  9:49                     ` Alexey Charkov
  0 siblings, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-09  9:49 UTC (permalink / raw)
  To: Paul Mundt
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Andrew Morton,
	Guennadi Liakhovetski, Florian Tobias Schandinat, Ralf Baechle,
	David S. Miller, linux-kernel

2010/11/9 Paul Mundt <lethal@linux-sh.org>:
> On Tue, Nov 09, 2010 at 03:03:01AM +0300, Alexey Charkov wrote:
>> Is there anybody I should address specifically to get this merged?
>>
> I've set up an fbdev tree now, so I'll just take this through there. It
> looks like the ordering shouldn't be important, so this can be applied
> independent of the rest, and it simply won't show up until the other bits
> have settled in .38.
>

Yes, the different parts of the patch series are completely
independent, apart from the corresponding platform definitions and
memblock reservations that take place in PATCH 1/6 (which contains
basic board code).

Thanks,
Alexey

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

* Re: [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08  8:47   ` [PATCH 6/6 v2] " Arnd Bergmann
@ 2010-11-09 10:23     ` Alexey Charkov
  2010-11-09 15:03       ` Arnd Bergmann
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-09 10:23 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: vt8500-wm8505-linux-kernel, linux-arm-kernel, Andrew Morton,
	Guennadi Liakhovetski, Paul Mundt, Florian Tobias Schandinat,
	Ralf Baechle, David S. Miller, linux-kernel

2010/11/8 Arnd Bergmann <arnd@arndb.de>:
> On Sunday 07 November 2010, Alexey Charkov wrote:
>>  drivers/video/Kconfig         |   26 +++
>>  drivers/video/Makefile        |    3 +
>>  drivers/video/vt8500lcdfb.c   |  452 +++++++++++++++++++++++++++++++++++++++++
>>  drivers/video/vt8500lcdfb.h   |   34 +++
>>  drivers/video/wm8505fb.c      |  438 +++++++++++++++++++++++++++++++++++++++
>>  drivers/video/wm8505fb_regs.h |   76 +++++++
>>  drivers/video/wmt_ge_rops.c   |  186 +++++++++++++++++
>>  drivers/video/wmt_ge_rops.h   |    5 +
>
> From a very brief look, the two drivers look rather similar. What is the
> reason to have separate drivers instead of just one?
>
> Could you perhaps take the common parts and move them into a third module
> that exports symbols to be used by the two drivers?
>

Quite frankly, I would say that all SoC framebuffer drivers are quite
similar ;-) Register offsets, timing formats, accepted pixel formats,
buffer alignment requirements are all different, so I do not really
believe that there'd be much benefit from introducing another
abstraction level. This is open to debate, of course.

Alexey

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

* Re: [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-08 20:43       ` Paul Mundt
  2010-11-08 21:15         ` Alexey Charkov
@ 2010-11-09 10:33         ` Russell King - ARM Linux
  2010-11-09 10:51           ` Paul Mundt
  1 sibling, 1 reply; 91+ messages in thread
From: Russell King - ARM Linux @ 2010-11-09 10:33 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Alexey Charkov, Florian Tobias Schandinat, linux-kernel,
	Ralf Baechle, David S. Miller, vt8500-wm8505-linux-kernel,
	Andrew Morton, Guennadi Liakhovetski, linux-arm-kernel

On Tue, Nov 09, 2010 at 05:43:16AM +0900, Paul Mundt wrote:
> > diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
> > new file mode 100644
> > index 0000000..640d8a3
> > --- /dev/null
> > +++ b/drivers/video/vt8500lcdfb.c
> > +#include <asm/irq.h>
> > +
> linux/irq.h is preferred here.

I assume you've read the comment at the top of linux/irq.h ?

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

* Re: [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-09 10:33         ` [PATCH 6/6 v3] " Russell King - ARM Linux
@ 2010-11-09 10:51           ` Paul Mundt
  2010-11-09 11:04             ` Russell King - ARM Linux
  0 siblings, 1 reply; 91+ messages in thread
From: Paul Mundt @ 2010-11-09 10:51 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, Florian Tobias Schandinat, linux-kernel,
	Ralf Baechle, David S. Miller, vt8500-wm8505-linux-kernel,
	Andrew Morton, Guennadi Liakhovetski, linux-arm-kernel

On Tue, Nov 09, 2010 at 10:33:12AM +0000, Russell King - ARM Linux wrote:
> On Tue, Nov 09, 2010 at 05:43:16AM +0900, Paul Mundt wrote:
> > > diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
> > > new file mode 100644
> > > index 0000000..640d8a3
> > > --- /dev/null
> > > +++ b/drivers/video/vt8500lcdfb.c
> > > +#include <asm/irq.h>
> > > +
> > linux/irq.h is preferred here.
> 
> I assume you've read the comment at the top of linux/irq.h ?

I had forgotten about that. This comment predates git, so I'm unaware of
what the context is, could you elaborate on it?

I'm aware this won't work for s390, but as that has NO_IOMEM in the
first place it's a non-issue. I assume this was added at a time before
ARM selected GENERIC_HARDIRQS, but this is no longer the case.

The odd ones out that do support iomem are m68k and sparc32, both of
which are being converted. Given that and that s390 remains special
cased for now, what exactly is the concern?

There is a reasonable expectation that we will start to see irq_data
references in drivers, so it's not entirely obvious that the assertion
made by the comment will remain true (all of the stragglers have work in
progress patches already, as far as I'm aware).

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

* Re: [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-09 10:51           ` Paul Mundt
@ 2010-11-09 11:04             ` Russell King - ARM Linux
  2010-11-09 13:02               ` Geert Uytterhoeven
  0 siblings, 1 reply; 91+ messages in thread
From: Russell King - ARM Linux @ 2010-11-09 11:04 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Florian Tobias Schandinat, linux-kernel, Ralf Baechle,
	Guennadi Liakhovetski, Alexey Charkov,
	vt8500-wm8505-linux-kernel, Andrew Morton, David S. Miller,
	linux-arm-kernel

On Tue, Nov 09, 2010 at 07:51:45PM +0900, Paul Mundt wrote:
> On Tue, Nov 09, 2010 at 10:33:12AM +0000, Russell King - ARM Linux wrote:
> > On Tue, Nov 09, 2010 at 05:43:16AM +0900, Paul Mundt wrote:
> > > > diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
> > > > new file mode 100644
> > > > index 0000000..640d8a3
> > > > --- /dev/null
> > > > +++ b/drivers/video/vt8500lcdfb.c
> > > > +#include <asm/irq.h>
> > > > +
> > > linux/irq.h is preferred here.
> > 
> > I assume you've read the comment at the top of linux/irq.h ?
> 
> I had forgotten about that. This comment predates git, so I'm unaware of
> what the context is, could you elaborate on it?

It's from a time when we didn't have generic irq (and as I understand,
generic irq is still optional.)

When you didn't have generic irq support for an arch, having drivers
include linux/irq.h resulted in build errors, caused by nothing more
than "use linux/foo.h rather than asm/foo.h" - as those architectures
won't provide an asm/hw_irq.h.

It looks to me like the only arch in the kernel which doesn't support
generic irq is sh.

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

* Re: [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-09 11:04             ` Russell King - ARM Linux
@ 2010-11-09 13:02               ` Geert Uytterhoeven
  2010-11-09 13:33                 ` Arnd Bergmann
  0 siblings, 1 reply; 91+ messages in thread
From: Geert Uytterhoeven @ 2010-11-09 13:02 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Paul Mundt, Florian Tobias Schandinat, linux-kernel,
	Ralf Baechle, Guennadi Liakhovetski, Alexey Charkov,
	vt8500-wm8505-linux-kernel, Andrew Morton, David S. Miller,
	linux-arm-kernel

On Tue, Nov 9, 2010 at 12:04, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Tue, Nov 09, 2010 at 07:51:45PM +0900, Paul Mundt wrote:
>> On Tue, Nov 09, 2010 at 10:33:12AM +0000, Russell King - ARM Linux wrote:
>> > On Tue, Nov 09, 2010 at 05:43:16AM +0900, Paul Mundt wrote:
>> > > > diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
>> > > > new file mode 100644
>> > > > index 0000000..640d8a3
>> > > > --- /dev/null
>> > > > +++ b/drivers/video/vt8500lcdfb.c
>> > > > +#include <asm/irq.h>
>> > > > +
>> > > linux/irq.h is preferred here.
>> >
>> > I assume you've read the comment at the top of linux/irq.h ?
>>
>> I had forgotten about that. This comment predates git, so I'm unaware of
>> what the context is, could you elaborate on it?
>
> It's from a time when we didn't have generic irq (and as I understand,
> generic irq is still optional.)
>
> When you didn't have generic irq support for an arch, having drivers
> include linux/irq.h resulted in build errors, caused by nothing more
> than "use linux/foo.h rather than asm/foo.h" - as those architectures
> won't provide an asm/hw_irq.h.
>
> It looks to me like the only arch in the kernel which doesn't support
> generic irq is sh.

IIRC, m68k, sparc32, and s390.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-09 13:02               ` Geert Uytterhoeven
@ 2010-11-09 13:33                 ` Arnd Bergmann
  2010-11-09 16:20                   ` Paul Mundt
  0 siblings, 1 reply; 91+ messages in thread
From: Arnd Bergmann @ 2010-11-09 13:33 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Geert Uytterhoeven, Russell King - ARM Linux,
	Florian Tobias Schandinat, linux-kernel, Ralf Baechle,
	David S. Miller, Paul Mundt, Alexey Charkov,
	vt8500-wm8505-linux-kernel, Andrew Morton, Guennadi Liakhovetski

On Tuesday 09 November 2010, Geert Uytterhoeven wrote:
> >
> > It looks to me like the only arch in the kernel which doesn't support
> > generic irq is sh.
> 
> IIRC, m68k, sparc32, and s390.

Yes, and among those, no driver in s390 should need asm/irq.h either because
they do not have interrupt numbers -- every device on s390 has an implicit
interrupt.

	Arnd

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

* Re: [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-09 10:23     ` Alexey Charkov
@ 2010-11-09 15:03       ` Arnd Bergmann
  0 siblings, 0 replies; 91+ messages in thread
From: Arnd Bergmann @ 2010-11-09 15:03 UTC (permalink / raw)
  To: vt8500-wm8505-linux-kernel
  Cc: Alexey Charkov, linux-arm-kernel, Andrew Morton,
	Guennadi Liakhovetski, Paul Mundt, Florian Tobias Schandinat,
	Ralf Baechle, David S. Miller, linux-kernel

On Tuesday 09 November 2010, Alexey Charkov wrote:
> Quite frankly, I would say that all SoC framebuffer drivers are quite
> similar ;-) Register offsets, timing formats, accepted pixel formats,
> buffer alignment requirements are all different, so I do not really
> believe that there'd be much benefit from introducing another
> abstraction level. This is open to debate, of course.

Ok, I took a more detailed look at the code now. You're absolutely
right here.

	Arnd

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

* Re: [PATCH 6/6 v3] ARM: Add support for the display controllers in VT8500 and WM8505
  2010-11-09 13:33                 ` Arnd Bergmann
@ 2010-11-09 16:20                   ` Paul Mundt
  0 siblings, 0 replies; 91+ messages in thread
From: Paul Mundt @ 2010-11-09 16:20 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, Geert Uytterhoeven, Russell King - ARM Linux,
	Florian Tobias Schandinat, linux-kernel, Ralf Baechle,
	David S. Miller, Alexey Charkov, vt8500-wm8505-linux-kernel,
	Andrew Morton, Guennadi Liakhovetski

On Tue, Nov 09, 2010 at 02:33:45PM +0100, Arnd Bergmann wrote:
> On Tuesday 09 November 2010, Geert Uytterhoeven wrote:
> > >
> > > It looks to me like the only arch in the kernel which doesn't support
> > > generic irq is sh.
> > 
> > IIRC, m68k, sparc32, and s390.
> 
> Yes, and among those, no driver in s390 should need asm/irq.h either because
> they do not have interrupt numbers -- every device on s390 has an implicit
> interrupt.
> 
Which is exactly what I stated in my original response. Moving along
then..

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

* Re: [PATCH 1/6 v4] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-08 17:19       ` [PATCH 1/6 v4] " Alexey Charkov
@ 2010-11-10 15:16         ` saeed bishara
  2010-11-10 15:18           ` Russell King - ARM Linux
  2010-11-11 21:23         ` [PATCH 1/6 v5] " Alexey Charkov
  1 sibling, 1 reply; 91+ messages in thread
From: saeed bishara @ 2010-11-10 15:16 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

> diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
> new file mode 100644
> index 0000000..bb9e2d2
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
> @@ -0,0 +1,37 @@
> +/* arch/arm/mach-vt8500/include/mach/uncompress.h
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * Based on arch/arm/mach-dove/include/mach/uncompress.h
please  replace dove with vt8500
saeed

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

* Re: [PATCH 1/6 v4] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-10 15:16         ` saeed bishara
@ 2010-11-10 15:18           ` Russell King - ARM Linux
  2010-11-10 15:20             ` saeed bishara
  0 siblings, 1 reply; 91+ messages in thread
From: Russell King - ARM Linux @ 2010-11-10 15:18 UTC (permalink / raw)
  To: saeed bishara
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Wed, Nov 10, 2010 at 05:16:17PM +0200, saeed bishara wrote:
> > diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
> > new file mode 100644
> > index 0000000..bb9e2d2
> > --- /dev/null
> > +++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
> > @@ -0,0 +1,37 @@
> > +/* arch/arm/mach-vt8500/include/mach/uncompress.h
> > + *
> > + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> > + *
> > + * Based on arch/arm/mach-dove/include/mach/uncompress.h
> please  replace dove with vt8500

That doesn't make sense - how can a file be created, based upon itself?

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

* Re: [PATCH 1/6 v4] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-10 15:18           ` Russell King - ARM Linux
@ 2010-11-10 15:20             ` saeed bishara
  0 siblings, 0 replies; 91+ messages in thread
From: saeed bishara @ 2010-11-10 15:20 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Wed, Nov 10, 2010 at 5:18 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Wed, Nov 10, 2010 at 05:16:17PM +0200, saeed bishara wrote:
>> > diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
>> > new file mode 100644
>> > index 0000000..bb9e2d2
>> > --- /dev/null
>> > +++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
>> > @@ -0,0 +1,37 @@
>> > +/* arch/arm/mach-vt8500/include/mach/uncompress.h
>> > + *
>> > + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
>> > + *
>> > + * Based on arch/arm/mach-dove/include/mach/uncompress.h
>> please  replace dove with vt8500
>
> That doesn't make sense - how can a file be created, based upon itself?
your are right, please ignore my comment.
saeed

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

* [PATCH 1/6 v5] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-08 17:19       ` [PATCH 1/6 v4] " Alexey Charkov
  2010-11-10 15:16         ` saeed bishara
@ 2010-11-11 21:23         ` Alexey Charkov
  2010-11-11 23:49           ` Russell King - ARM Linux
  1 sibling, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-11 21:23 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505. Suitable code is
selected (if compiled in) at early initialization time by reading a
platform-specific identification register, as current bootloaders
do not provide any reliable machine id to the kernel.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

I decided to drop the override for sched_clock and just use the
default implementation instead. As the hardware timer has quite a
significant overhead when read (due to the necessity of busy-waiting
for hardware synchronization), the default implementation turns out
to perform much better (4s kernel initialization time vs. 14s on my
device), and the code is simpler.

Please consider this version instead of the previous ones.

Best regards,
Alexey

 arch/arm/Kconfig                                |   14 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 +++
 arch/arm/mach-vt8500/Kconfig                    |   63 ++++
 arch/arm/mach-vt8500/Makefile                   |    6 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   81 ++++
 arch/arm/mach-vt8500/devices.c                  |  442 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   46 +++
 arch/arm/mach-vt8500/gpio.c                     |  230 ++++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 ++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 ++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/io.h          |   28 ++
 arch/arm/mach-vt8500/include/mach/irq_defs.h    |  124 +++++++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 ++
 arch/arm/mach-vt8500/include/mach/mmio_regs.h   |   90 +++++
 arch/arm/mach-vt8500/include/mach/system.h      |   20 +
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 ++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 +
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 ++
 arch/arm/mach-vt8500/irq.c                      |  179 +++++++++
 arch/arm/mach-vt8500/irq_defs.c                 |  173 +++++++++
 arch/arm/mach-vt8500/mmio_regs.c                |  118 ++++++
 arch/arm/mach-vt8500/pwm.c                      |  254 +++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  154 ++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   80 ++++
 31 files changed, 2401 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irq_defs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/mmio_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/irq_defs.c
 create mode 100644 arch/arm/mach-vt8500/mmio_regs.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a19a526..e0724ac 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,18 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_CLK
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +985,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..a462869
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,63 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..aff4159
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,6 @@
+obj-y += devices.o gpio.o irq.o irq_defs.o mmio_regs.o timer.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..25aea3d
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,81 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= vt8500_map_io,
+	.reserve	= vt8500_reserve_mem,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..de5c5a6
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,442 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include <mach/vt8500fb.h>
+#include "devices.h"
+
+static struct resource resources_lcdc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources  = ARRAY_SIZE(resources_lcdc),
+	.resource       = resources_lcdc,
+};
+
+static struct resource resources_wm8505_fb[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(resources_wm8505_fb),
+	.resource       = resources_wm8505_fb,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 66,
+		.right_margin	= 2,
+		.upper_margin	= 19,
+		.lower_margin	= 2,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		int len = strlen(panels[i].mode.name);
+
+		if (memcmp(panels[i].mode.name, str, len) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+static struct resource resources_uart0[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart1[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart2[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart3[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart4[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart5[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_uart0),
+	.resource	= resources_uart0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(resources_uart1),
+	.resource	= resources_uart1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(resources_uart2),
+	.resource	= resources_uart2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(resources_uart3),
+	.resource	= resources_uart3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+	.num_resources	= ARRAY_SIZE(resources_uart4),
+	.resource	= resources_uart4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+	.num_resources	= ARRAY_SIZE(resources_uart5),
+	.resource	= resources_uart5,
+};
+
+static struct resource resources_ehci[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(resources_ehci),
+	.resource	= resources_ehci,
+};
+
+static struct resource resources_ge_rops[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_ge_rops),
+	.resource	= resources_ge_rops,
+};
+
+static struct resource resources_pwm[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+	.resource	= resources_pwm,
+	.num_resources	= ARRAY_SIZE(resources_pwm),
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+static struct resource resources_rtc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+	.resource	= resources_rtc,
+	.num_resources	= ARRAY_SIZE(resources_rtc),
+};
+
+static struct map_desc vt8500_io_desc[] __initdata = {
+	/* SoC MMIO registers, to be filled in later */
+	[0] = {
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init wmt_set_resources(void)
+{
+	resources_lcdc[0].start = wmt_current_regs->lcdc;
+	resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
+	resources_lcdc[1].start = wmt_current_irqs->lcdc;
+	resources_lcdc[1].end = wmt_current_irqs->lcdc;
+
+	resources_wm8505_fb[0].start = wmt_current_regs->govr;
+	resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
+
+	resources_uart0[0].start = wmt_current_regs->uart0;
+	resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
+	resources_uart0[1].start = wmt_current_irqs->uart0;
+	resources_uart0[1].end = wmt_current_irqs->uart0;
+	resources_uart1[0].start = wmt_current_regs->uart1;
+	resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
+	resources_uart1[1].start = wmt_current_irqs->uart1;
+	resources_uart1[1].end = wmt_current_irqs->uart1;
+	resources_uart2[0].start = wmt_current_regs->uart2;
+	resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
+	resources_uart2[1].start = wmt_current_irqs->uart2;
+	resources_uart2[1].end = wmt_current_irqs->uart2;
+	resources_uart3[0].start = wmt_current_regs->uart3;
+	resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
+	resources_uart3[1].start = wmt_current_irqs->uart3;
+	resources_uart3[1].end = wmt_current_irqs->uart3;
+	resources_uart4[0].start = wmt_current_regs->uart4;
+	resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
+	resources_uart4[1].start = wmt_current_irqs->uart4;
+	resources_uart4[1].end = wmt_current_irqs->uart4;
+	resources_uart5[0].start = wmt_current_regs->uart5;
+	resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
+	resources_uart5[1].start = wmt_current_irqs->uart5;
+	resources_uart5[1].end = wmt_current_irqs->uart5;
+
+	resources_ehci[0].start = wmt_current_regs->ehci;
+	resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
+	resources_ehci[1].start = wmt_current_irqs->ehci;
+	resources_ehci[1].end = wmt_current_irqs->ehci;
+
+	resources_ge_rops[0].start = wmt_current_regs->ge;
+	resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
+
+	resources_pwm[0].start = wmt_current_regs->pwm;
+	resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
+
+	resources_rtc[0].start = wmt_current_regs->rtc;
+	resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
+	resources_rtc[1].start = wmt_current_irqs->rtc;
+	resources_rtc[1].end = wmt_current_irqs->rtc;
+	resources_rtc[2].start = wmt_current_irqs->rtc_hz;
+	resources_rtc[2].end = wmt_current_irqs->rtc_hz;
+}
+
+void __init vt8500_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
+	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init wm8505_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
+	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..428809e
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,46 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init wmt_set_resources(void);
+void __init vt8500_gpio_init(void);
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..f533ff0
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,230 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/gpio.h>
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	else
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) &
+			~(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
+	VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
+	VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
+	VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
+	VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
+	VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
+
+	VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
+	VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
+	VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
+	VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
+
+	VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
+	VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
+	VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
+
+	VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
+
+	VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
+
+	VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	writel(readl(regbase + 0x3c) & ~(1 << offset), regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	writel(readl(regbase + 0x3c) | (1 << offset), regbase + 0x3c);
+
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	else
+		writel(readl(regbase + 0x5c) & ~(1 << offset),
+			regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	gpio_to_irq_map[0] = wmt_current_irqs->ext0;
+	gpio_to_irq_map[1] = wmt_current_irqs->ext1;
+	gpio_to_irq_map[2] = wmt_current_irqs->ext2;
+	gpio_to_irq_map[3] = wmt_current_irqs->ext3;
+	gpio_to_irq_map[4] = wmt_current_irqs->ext4;
+	gpio_to_irq_map[5] = wmt_current_irqs->ext5;
+	gpio_to_irq_map[6] = wmt_current_irqs->ext6;
+	gpio_to_irq_map[7] = wmt_current_irqs->ext7;
+
+	regbase = ioremap(wmt_current_regs->gpio, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..8dd55c8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		((void __iomem *)((a) + 0xf0000000))
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irq_defs.h b/arch/arm/mach-vt8500/include/mach/irq_defs.h
new file mode 100644
index 0000000..fa8f4b3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irq_defs.h
@@ -0,0 +1,124 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irq_defs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef VT8500_IRQ_DEFS_H
+#define VT8500_IRQ_DEFS_H
+
+#include <linux/types.h>
+
+struct wmt_irq_srcs {
+	u8 nr_irqs;
+	u8 jpegenc;
+	u8 jpegdec;
+	u8 pata;
+	u8 dma;
+	u8 ext0;
+	u8 ext1;
+	u8 ext2;
+	u8 ext3;
+	u8 ext4;
+	u8 ext5;
+	u8 ext6;
+	u8 ext7;
+	u8 ether;
+	u8 mpegts;
+	u8 ge;
+	u8 gov;
+	u8 lcdc;
+	u8 lcdf;
+	u8 vpp;
+	u8 vpu;
+	u8 vid;
+	u8 spu;
+	u8 pip;
+	u8 dvo;
+	u8 govw;
+	u8 govrsdscd;
+	u8 govrsdmif;
+	u8 govrhdscd;
+	u8 govrhdmif;
+	u8 cipher;
+	u8 i2c0;
+	u8 i2c1;
+	u8 sdmmc;
+	u8 sdmmc_dma;
+	u8 pmc_wu;
+	u8 spi0;
+	u8 spi1;
+	u8 spi2;
+	u8 nand;
+	u8 nand_dma;
+	u8 nor;
+	u8 memstick;
+	u8 memstick_dma;
+	u8 uart0;
+	u8 uart1;
+	u8 uart2;
+	u8 uart3;
+	u8 uart4;
+	u8 uart5;
+	u8 i2s;
+	u8 pcm;
+	u8 ac97;
+	u8 timer_match0;
+	u8 timer_match1;
+	u8 timer_match2;
+	u8 timer_match3;
+	u8 ehci;
+	u8 uhci;
+	u8 udc;
+	u8 udc_dma;
+	u8 keypad;
+	u8 ps2mouse;
+	u8 ps2kbd;
+	u8 rtc;
+	u8 rtc_hz;
+	u8 adc;
+	u8 cir;
+	u8 dma0;
+	u8 dma1;
+	u8 dma2;
+	u8 dma3;
+	u8 dma4;
+	u8 dma5;
+	u8 dma6;
+	u8 dma7;
+	u8 dma8;
+	u8 dma9;
+	u8 dma10;
+	u8 dma11;
+	u8 dma12;
+	u8 dma13;
+	u8 dma14;
+	u8 dma15;
+	u8 irq0;
+	u8 irq1;
+	u8 irq2;
+	u8 irq3;
+	u8 irq4;
+	u8 irq5;
+	u8 irq6;
+	u8 irq7;
+	u8 sae;
+};
+
+extern struct wmt_irq_srcs wmt_irqs[] __initdata;
+extern struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/mmio_regs.h b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
new file mode 100644
index 0000000..76439dd
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
@@ -0,0 +1,90 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/mmio_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_MMIO_REGS_H
+#define __ASM_ARM_ARCH_MMIO_REGS_H
+
+#include <linux/init.h>
+
+struct wmt_mmio_regs {
+	unsigned long mmio_regs_start;
+	unsigned long mmio_regs_length;
+	unsigned long mmio_regs_virt;
+	unsigned long ddr;
+	unsigned long dma;
+	unsigned long vdma;
+	unsigned long sflash;
+	unsigned long ether;
+	unsigned long cipher;
+	unsigned long ehci;
+	unsigned long uhci;
+	unsigned long pata;
+	unsigned long ps2;
+	unsigned long nand;
+	unsigned long nor;
+	unsigned long sdmmc;
+	unsigned long memstick;
+	unsigned long lcdc;
+	unsigned long vpu;
+	unsigned long gov;
+	unsigned long ge;
+	unsigned long govr;
+	unsigned long scl;
+	unsigned long lcdf;
+	unsigned long vid;
+	unsigned long vpp;
+	unsigned long tsbk;
+	unsigned long jpegdec;
+	unsigned long jpegenc;
+	unsigned long rtc;
+	unsigned long gpio;
+	unsigned long scc;
+	unsigned long pmc;
+	unsigned long ic0;
+	unsigned long ic1;
+	unsigned long uart0;
+	unsigned long uart1;
+	unsigned long uart2;
+	unsigned long uart3;
+	unsigned long uart4;
+	unsigned long uart5;
+	unsigned long pwm;
+	unsigned long spi0;
+	unsigned long spi1;
+	unsigned long spi2;
+	unsigned long cir;
+	unsigned long i2c0;
+	unsigned long i2c1;
+	unsigned long ac97;
+	unsigned long pcm;
+	unsigned long i2s;
+	unsigned long adc;
+	unsigned long keypad;
+};
+
+enum {
+	VT8500_INDEX,
+	WM8505_INDEX,
+};
+
+extern struct wmt_mmio_regs wmt_regmaps[] __initdata;
+extern struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+#endif
+
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..4d812e8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR	0xd8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	void __iomem *pmsr = ioremap(VT8500_PMSR, 4);
+	if (pmsr)
+		writel(1, pmsr);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..75a6912
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END		(PAGE_OFFSET + 0x10000000)
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..cc7f25e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	__u32			xres_virtual;
+	__u32			yres_virtual;
+	__u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..c988a4d
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,179 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);
+	if (edge)
+		writel(readl(base
+			+ VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
+			| (1 << (irq & 0x1f)), base
+			+ VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));
+	else
+		writeb(readb(base
+			+ VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
+			base + VT8500_IC_DCTR + irq);
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	writeb(readb(base
+		+ VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
+		base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
+{
+	return -EINVAL;
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_HIGH, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_level_irq);
+		break;
+	case IRQF_TRIGGER_FALLING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_FALLING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	case IRQF_TRIGGER_RISING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_RISING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	}
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_wake  = vt8500_irq_set_wake,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+	sic_regbase = ioremap(wmt_current_regs->ic1, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR
+								+ i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/irq_defs.c b/arch/arm/mach-vt8500/irq_defs.c
new file mode 100644
index 0000000..b338c04
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq_defs.c
@@ -0,0 +1,173 @@
+/* linux/arch/arm/mach-vt8500/irq_defs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/irq_defs.h>
+#include <mach/mmio_regs.h>
+
+struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+struct wmt_irq_srcs wmt_irqs[] __initdata = {
+	[VT8500_INDEX] = {
+		.jpegenc	= 0,
+		.jpegdec	= 1,
+		.pata		= 3,
+		.dma		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.ge		= 8,
+		.gov		= 9,
+		.ether		= 10,
+		.mpegts		= 11,
+		.lcdc		= 12,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.cipher		= 16,
+		.vpp		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.lcdf		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.memstick	= 30,
+		.memstick_dma	= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.i2s		= 34,
+		.pcm		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.vpu		= 40,
+		.vid		= 41,
+		.ac97		= 42,
+		.ehci		= 43,
+		.nor		= 44,
+		.ps2mouse	= 45,
+		.ps2kbd		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.adc		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.dma0		= 56,
+		.dma1		= 57,
+		.dma2		= 58,
+		.dma3		= 59,
+		.dma4		= 60,
+		.dma5		= 61,
+		.dma6		= 62,
+		.dma7		= 63,
+		.nr_irqs	= 64,
+	},
+	[WM8505_INDEX] = {
+		.uhci		= 0,
+		.ehci		= 1,
+		.udc_dma	= 2,
+		.ps2mouse	= 4,
+		.udc		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.keypad		= 8,
+		.dma		= 9,
+		.ether		= 10,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.dma0		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.ps2kbd		= 23,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.dma1		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.uart5		= 30,
+		.uart4		= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.dma2		= 34,
+		.i2s		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.dma3		= 40,
+		.dma4		= 41,
+		.ac97		= 42,
+		.nor		= 44,
+		.dma5		= 45,
+		.dma6		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.dma7		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.irq0		= 56,
+		.irq1		= 57,
+		.irq2		= 58,
+		.irq3		= 59,
+		.irq4		= 60,
+		.irq5		= 61,
+		.irq6		= 62,
+		.irq7		= 63,
+		.jpegdec	= 65,
+		.sae		= 66,
+		.vpu		= 79,
+		.vpp		= 80,
+		.vid		= 81,
+		.spu		= 82,
+		.pip		= 83,
+		.ge		= 84,
+		.gov		= 85,
+		.dvo		= 86,
+		.dma8		= 92,
+		.dma9		= 93,
+		.dma10		= 94,
+		.dma11		= 95,
+		.dma12		= 96,
+		.dma13		= 97,
+		.dma14		= 98,
+		.dma15		= 99,
+		.govw		= 111,
+		.govrsdscd	= 112,
+		.govrsdmif	= 113,
+		.govrhdscd	= 114,
+		.govrhdmif	= 115,
+		.nr_irqs	= 116,
+	},
+};
diff --git a/arch/arm/mach-vt8500/mmio_regs.c b/arch/arm/mach-vt8500/mmio_regs.c
new file mode 100644
index 0000000..e9b3264
--- /dev/null
+++ b/arch/arm/mach-vt8500/mmio_regs.c
@@ -0,0 +1,118 @@
+/* linux/arch/arm/mach-vt8500/mmio_regs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/mmio_regs.h>
+
+struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+struct wmt_mmio_regs wmt_regmaps[] __initdata = {
+	[VT8500_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00350000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000000,
+		.dma			= 0xd8001000,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007900,
+		.uhci			= 0xd8007b01,
+		.pata			= 0xd8008000,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.memstick		= 0xd800b400,
+		.lcdc			= 0xd800e400,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.lcdf			= 0xd8050900,
+		.vid			= 0xd8050a00,
+		.vpp			= 0xd8050b00,
+		.tsbk			= 0xd80f4000,
+		.jpegdec		= 0xd80fe000,
+		.jpegenc		= 0xd80ff000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.pcm			= 0xd82d0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.adc			= 0xd8340000,
+	},
+	[WM8505_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00390000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000400,
+		.dma			= 0xd8001800,
+		.vdma			= 0xd8001c00,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007100,
+		.uhci			= 0xd8007301,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.govr			= 0xd8050800,
+		.vid			= 0xd8050a00,
+		.scl			= 0xd8050d00,
+		.vpp			= 0xd8050f00,
+		.jpegdec		= 0xd80fe000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.ic1			= 0xd8150000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.keypad			= 0xd8260000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.uart4			= 0xd8370000,
+		.uart5			= 0xd8380000,
+	},
+};
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..d1356a1
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,254 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+	int loops = 1000;
+	while ((readb(reg) & bitmask) && --loops)
+		cpu_relax();
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	dc = pv * duty_ns / period_ns;
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else
+			pwm = ERR_PTR(-EBUSY);
+	} else
+		pwm = ERR_PTR(-ENOENT);
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else
+		pr_warning("PWM device already freed\n");
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[0].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..ab4f7aa
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,154 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+static void __iomem *regbase;
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	int loops = 1000;
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	int loops = 1000;
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_current_regs->pmc + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO "
+				"registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register "
+			"failed for %s\n", clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_current_irqs->timer_match0, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq "
+			"failed for %s\n", clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..b07cba6
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,80 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <mach/mmio_regs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= wm8505_map_io,
+	.reserve	= wm8505_reserve_mem,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.2


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

* Re: [PATCH 1/6 v5] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-11 21:23         ` [PATCH 1/6 v5] " Alexey Charkov
@ 2010-11-11 23:49           ` Russell King - ARM Linux
  2010-11-12 16:54             ` [PATCH 1/6 v6] " Alexey Charkov
                               ` (2 more replies)
  0 siblings, 3 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2010-11-11 23:49 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

The good news is that it's getting harder to find things wrong...
Only three points follow below.

On Fri, Nov 12, 2010 at 12:23:22AM +0300, Alexey Charkov wrote:
> diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
> new file mode 100644
> index 0000000..25aea3d
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/bv07.c
> @@ -0,0 +1,81 @@
> +/*
> + *  arch/arm/mach-vt8500/bv07.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <asm/mach-types.h>
> +#include <asm/mach/arch.h>
> +#include <linux/io.h>
> +#include <linux/pm.h>

asm/ includes after linux/ includes please (applies to multiple files).

> diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
> new file mode 100644
> index 0000000..f533ff0
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/gpio.c
> @@ -0,0 +1,230 @@
> +/* linux/arch/arm/mach-vt8500/gpio.c
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/irq.h>
> +#include <linux/io.h>
> +
> +#include <mach/gpio.h>

linux/gpio.h please.

> +static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
> +{
> +	void __iomem *base = ic_regbase;
> +	unsigned int orig_irq = irq;
> +	if (irq >= 64) {
> +		base = sic_regbase;
> +		irq -= 64;
> +	}
> +	switch (flow_type) {
> +	case IRQF_TRIGGER_LOW:
> +		return -EINVAL;
> +	case IRQF_TRIGGER_HIGH:
> +		writeb((readb(base
> +			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
> +			| VT8500_TRIGGER_HIGH, base
> +			+ VT8500_IC_DCTR + irq);
> +		set_irq_handler(orig_irq, handle_level_irq);
> +		break;
> +	case IRQF_TRIGGER_FALLING:
> +		writeb((readb(base
> +			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
> +			| VT8500_TRIGGER_FALLING, base
> +			+ VT8500_IC_DCTR + irq);
> +		set_irq_handler(orig_irq, handle_edge_irq);
> +		break;
> +	case IRQF_TRIGGER_RISING:
> +		writeb((readb(base
> +			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
> +			| VT8500_TRIGGER_RISING, base
> +			+ VT8500_IC_DCTR + irq);
> +		set_irq_handler(orig_irq, handle_edge_irq);

These set_irq_handler's probably warn if you have spinlock debugging
enabled.

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

* [PATCH 1/6 v6] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-11 23:49           ` Russell King - ARM Linux
@ 2010-11-12 16:54             ` Alexey Charkov
  2010-11-12 20:14               ` Alexey Charkov
  2010-11-23 19:50             ` [PATCH 1/6 v7] " Alexey Charkov
  2010-12-19 17:40             ` [PATCH 1/6 v8] " Alexey Charkov
  2 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-12 16:54 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505. Suitable code is
selected (if compiled in) at early initialization time by reading a
platform-specific identification register, as current bootloaders
do not provide any reliable machine id to the kernel.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This fixes asm/ versus linux/ includes ordering and changes <mach/gpio.h>
to <linux/gpio.h> as Russell has requested.

Russell, could you please elaborate on your comment regarding spinlocks
and irq handlers? I always compile my kernel with all kinds of lock
debugging, and have not run into any associated warnings. Is there
anything suspicious specifically?

Thanks,
Alexey

 arch/arm/Kconfig                                |   14 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 +++
 arch/arm/mach-vt8500/Kconfig                    |   63 ++++
 arch/arm/mach-vt8500/Makefile                   |    6 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   82 +++++
 arch/arm/mach-vt8500/devices.c                  |  442 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   46 +++
 arch/arm/mach-vt8500/gpio.c                     |  230 ++++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 ++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 ++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/io.h          |   28 ++
 arch/arm/mach-vt8500/include/mach/irq_defs.h    |  124 +++++++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 ++
 arch/arm/mach-vt8500/include/mach/mmio_regs.h   |   90 +++++
 arch/arm/mach-vt8500/include/mach/system.h      |   20 +
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 ++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 +
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 ++
 arch/arm/mach-vt8500/irq.c                      |  179 +++++++++
 arch/arm/mach-vt8500/irq_defs.c                 |  173 +++++++++
 arch/arm/mach-vt8500/mmio_regs.c                |  118 ++++++
 arch/arm/mach-vt8500/pwm.c                      |  254 +++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  154 ++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   81 ++++
 31 files changed, 2403 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irq_defs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/mmio_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/irq_defs.c
 create mode 100644 arch/arm/mach-vt8500/mmio_regs.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a19a526..e0724ac 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,18 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_CLK
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +985,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..a462869
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,63 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..aff4159
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,6 @@
+obj-y += devices.o gpio.o irq.o irq_defs.o mmio_regs.o timer.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..f041712
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,82 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= vt8500_map_io,
+	.reserve	= vt8500_reserve_mem,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..de5c5a6
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,442 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include <mach/vt8500fb.h>
+#include "devices.h"
+
+static struct resource resources_lcdc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources  = ARRAY_SIZE(resources_lcdc),
+	.resource       = resources_lcdc,
+};
+
+static struct resource resources_wm8505_fb[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(resources_wm8505_fb),
+	.resource       = resources_wm8505_fb,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 66,
+		.right_margin	= 2,
+		.upper_margin	= 19,
+		.lower_margin	= 2,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		int len = strlen(panels[i].mode.name);
+
+		if (memcmp(panels[i].mode.name, str, len) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+static struct resource resources_uart0[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart1[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart2[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart3[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart4[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart5[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_uart0),
+	.resource	= resources_uart0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(resources_uart1),
+	.resource	= resources_uart1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(resources_uart2),
+	.resource	= resources_uart2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(resources_uart3),
+	.resource	= resources_uart3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+	.num_resources	= ARRAY_SIZE(resources_uart4),
+	.resource	= resources_uart4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+	.num_resources	= ARRAY_SIZE(resources_uart5),
+	.resource	= resources_uart5,
+};
+
+static struct resource resources_ehci[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(resources_ehci),
+	.resource	= resources_ehci,
+};
+
+static struct resource resources_ge_rops[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_ge_rops),
+	.resource	= resources_ge_rops,
+};
+
+static struct resource resources_pwm[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+	.resource	= resources_pwm,
+	.num_resources	= ARRAY_SIZE(resources_pwm),
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+static struct resource resources_rtc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+	.resource	= resources_rtc,
+	.num_resources	= ARRAY_SIZE(resources_rtc),
+};
+
+static struct map_desc vt8500_io_desc[] __initdata = {
+	/* SoC MMIO registers, to be filled in later */
+	[0] = {
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init wmt_set_resources(void)
+{
+	resources_lcdc[0].start = wmt_current_regs->lcdc;
+	resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
+	resources_lcdc[1].start = wmt_current_irqs->lcdc;
+	resources_lcdc[1].end = wmt_current_irqs->lcdc;
+
+	resources_wm8505_fb[0].start = wmt_current_regs->govr;
+	resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
+
+	resources_uart0[0].start = wmt_current_regs->uart0;
+	resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
+	resources_uart0[1].start = wmt_current_irqs->uart0;
+	resources_uart0[1].end = wmt_current_irqs->uart0;
+	resources_uart1[0].start = wmt_current_regs->uart1;
+	resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
+	resources_uart1[1].start = wmt_current_irqs->uart1;
+	resources_uart1[1].end = wmt_current_irqs->uart1;
+	resources_uart2[0].start = wmt_current_regs->uart2;
+	resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
+	resources_uart2[1].start = wmt_current_irqs->uart2;
+	resources_uart2[1].end = wmt_current_irqs->uart2;
+	resources_uart3[0].start = wmt_current_regs->uart3;
+	resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
+	resources_uart3[1].start = wmt_current_irqs->uart3;
+	resources_uart3[1].end = wmt_current_irqs->uart3;
+	resources_uart4[0].start = wmt_current_regs->uart4;
+	resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
+	resources_uart4[1].start = wmt_current_irqs->uart4;
+	resources_uart4[1].end = wmt_current_irqs->uart4;
+	resources_uart5[0].start = wmt_current_regs->uart5;
+	resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
+	resources_uart5[1].start = wmt_current_irqs->uart5;
+	resources_uart5[1].end = wmt_current_irqs->uart5;
+
+	resources_ehci[0].start = wmt_current_regs->ehci;
+	resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
+	resources_ehci[1].start = wmt_current_irqs->ehci;
+	resources_ehci[1].end = wmt_current_irqs->ehci;
+
+	resources_ge_rops[0].start = wmt_current_regs->ge;
+	resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
+
+	resources_pwm[0].start = wmt_current_regs->pwm;
+	resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
+
+	resources_rtc[0].start = wmt_current_regs->rtc;
+	resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
+	resources_rtc[1].start = wmt_current_irqs->rtc;
+	resources_rtc[1].end = wmt_current_irqs->rtc;
+	resources_rtc[2].start = wmt_current_irqs->rtc_hz;
+	resources_rtc[2].end = wmt_current_irqs->rtc_hz;
+}
+
+void __init vt8500_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
+	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init wm8505_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
+	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..428809e
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,46 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init wmt_set_resources(void);
+void __init vt8500_gpio_init(void);
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..49daee6
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,230 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	else
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) &
+			~(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
+	VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
+	VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
+	VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
+	VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
+	VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
+
+	VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
+	VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
+	VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
+	VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
+
+	VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
+	VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
+	VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
+
+	VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
+
+	VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
+
+	VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	writel(readl(regbase + 0x3c) & ~(1 << offset), regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	writel(readl(regbase + 0x3c) | (1 << offset), regbase + 0x3c);
+
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	else
+		writel(readl(regbase + 0x5c) & ~(1 << offset),
+			regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	gpio_to_irq_map[0] = wmt_current_irqs->ext0;
+	gpio_to_irq_map[1] = wmt_current_irqs->ext1;
+	gpio_to_irq_map[2] = wmt_current_irqs->ext2;
+	gpio_to_irq_map[3] = wmt_current_irqs->ext3;
+	gpio_to_irq_map[4] = wmt_current_irqs->ext4;
+	gpio_to_irq_map[5] = wmt_current_irqs->ext5;
+	gpio_to_irq_map[6] = wmt_current_irqs->ext6;
+	gpio_to_irq_map[7] = wmt_current_irqs->ext7;
+
+	regbase = ioremap(wmt_current_regs->gpio, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..8dd55c8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		((void __iomem *)((a) + 0xf0000000))
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irq_defs.h b/arch/arm/mach-vt8500/include/mach/irq_defs.h
new file mode 100644
index 0000000..fa8f4b3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irq_defs.h
@@ -0,0 +1,124 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irq_defs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef VT8500_IRQ_DEFS_H
+#define VT8500_IRQ_DEFS_H
+
+#include <linux/types.h>
+
+struct wmt_irq_srcs {
+	u8 nr_irqs;
+	u8 jpegenc;
+	u8 jpegdec;
+	u8 pata;
+	u8 dma;
+	u8 ext0;
+	u8 ext1;
+	u8 ext2;
+	u8 ext3;
+	u8 ext4;
+	u8 ext5;
+	u8 ext6;
+	u8 ext7;
+	u8 ether;
+	u8 mpegts;
+	u8 ge;
+	u8 gov;
+	u8 lcdc;
+	u8 lcdf;
+	u8 vpp;
+	u8 vpu;
+	u8 vid;
+	u8 spu;
+	u8 pip;
+	u8 dvo;
+	u8 govw;
+	u8 govrsdscd;
+	u8 govrsdmif;
+	u8 govrhdscd;
+	u8 govrhdmif;
+	u8 cipher;
+	u8 i2c0;
+	u8 i2c1;
+	u8 sdmmc;
+	u8 sdmmc_dma;
+	u8 pmc_wu;
+	u8 spi0;
+	u8 spi1;
+	u8 spi2;
+	u8 nand;
+	u8 nand_dma;
+	u8 nor;
+	u8 memstick;
+	u8 memstick_dma;
+	u8 uart0;
+	u8 uart1;
+	u8 uart2;
+	u8 uart3;
+	u8 uart4;
+	u8 uart5;
+	u8 i2s;
+	u8 pcm;
+	u8 ac97;
+	u8 timer_match0;
+	u8 timer_match1;
+	u8 timer_match2;
+	u8 timer_match3;
+	u8 ehci;
+	u8 uhci;
+	u8 udc;
+	u8 udc_dma;
+	u8 keypad;
+	u8 ps2mouse;
+	u8 ps2kbd;
+	u8 rtc;
+	u8 rtc_hz;
+	u8 adc;
+	u8 cir;
+	u8 dma0;
+	u8 dma1;
+	u8 dma2;
+	u8 dma3;
+	u8 dma4;
+	u8 dma5;
+	u8 dma6;
+	u8 dma7;
+	u8 dma8;
+	u8 dma9;
+	u8 dma10;
+	u8 dma11;
+	u8 dma12;
+	u8 dma13;
+	u8 dma14;
+	u8 dma15;
+	u8 irq0;
+	u8 irq1;
+	u8 irq2;
+	u8 irq3;
+	u8 irq4;
+	u8 irq5;
+	u8 irq6;
+	u8 irq7;
+	u8 sae;
+};
+
+extern struct wmt_irq_srcs wmt_irqs[] __initdata;
+extern struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/mmio_regs.h b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
new file mode 100644
index 0000000..76439dd
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
@@ -0,0 +1,90 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/mmio_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_MMIO_REGS_H
+#define __ASM_ARM_ARCH_MMIO_REGS_H
+
+#include <linux/init.h>
+
+struct wmt_mmio_regs {
+	unsigned long mmio_regs_start;
+	unsigned long mmio_regs_length;
+	unsigned long mmio_regs_virt;
+	unsigned long ddr;
+	unsigned long dma;
+	unsigned long vdma;
+	unsigned long sflash;
+	unsigned long ether;
+	unsigned long cipher;
+	unsigned long ehci;
+	unsigned long uhci;
+	unsigned long pata;
+	unsigned long ps2;
+	unsigned long nand;
+	unsigned long nor;
+	unsigned long sdmmc;
+	unsigned long memstick;
+	unsigned long lcdc;
+	unsigned long vpu;
+	unsigned long gov;
+	unsigned long ge;
+	unsigned long govr;
+	unsigned long scl;
+	unsigned long lcdf;
+	unsigned long vid;
+	unsigned long vpp;
+	unsigned long tsbk;
+	unsigned long jpegdec;
+	unsigned long jpegenc;
+	unsigned long rtc;
+	unsigned long gpio;
+	unsigned long scc;
+	unsigned long pmc;
+	unsigned long ic0;
+	unsigned long ic1;
+	unsigned long uart0;
+	unsigned long uart1;
+	unsigned long uart2;
+	unsigned long uart3;
+	unsigned long uart4;
+	unsigned long uart5;
+	unsigned long pwm;
+	unsigned long spi0;
+	unsigned long spi1;
+	unsigned long spi2;
+	unsigned long cir;
+	unsigned long i2c0;
+	unsigned long i2c1;
+	unsigned long ac97;
+	unsigned long pcm;
+	unsigned long i2s;
+	unsigned long adc;
+	unsigned long keypad;
+};
+
+enum {
+	VT8500_INDEX,
+	WM8505_INDEX,
+};
+
+extern struct wmt_mmio_regs wmt_regmaps[] __initdata;
+extern struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+#endif
+
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..4d812e8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR	0xd8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	void __iomem *pmsr = ioremap(VT8500_PMSR, 4);
+	if (pmsr)
+		writel(1, pmsr);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..75a6912
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END		(PAGE_OFFSET + 0x10000000)
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..cc7f25e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	__u32			xres_virtual;
+	__u32			yres_virtual;
+	__u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..c988a4d
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,179 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);
+	if (edge)
+		writel(readl(base
+			+ VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
+			| (1 << (irq & 0x1f)), base
+			+ VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));
+	else
+		writeb(readb(base
+			+ VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
+			base + VT8500_IC_DCTR + irq);
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	writeb(readb(base
+		+ VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
+		base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
+{
+	return -EINVAL;
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_HIGH, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_level_irq);
+		break;
+	case IRQF_TRIGGER_FALLING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_FALLING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	case IRQF_TRIGGER_RISING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_RISING, base
+			+ VT8500_IC_DCTR + irq);
+		set_irq_handler(orig_irq, handle_edge_irq);
+		break;
+	}
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_wake  = vt8500_irq_set_wake,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+	sic_regbase = ioremap(wmt_current_regs->ic1, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR
+								+ i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/irq_defs.c b/arch/arm/mach-vt8500/irq_defs.c
new file mode 100644
index 0000000..b338c04
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq_defs.c
@@ -0,0 +1,173 @@
+/* linux/arch/arm/mach-vt8500/irq_defs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/irq_defs.h>
+#include <mach/mmio_regs.h>
+
+struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+struct wmt_irq_srcs wmt_irqs[] __initdata = {
+	[VT8500_INDEX] = {
+		.jpegenc	= 0,
+		.jpegdec	= 1,
+		.pata		= 3,
+		.dma		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.ge		= 8,
+		.gov		= 9,
+		.ether		= 10,
+		.mpegts		= 11,
+		.lcdc		= 12,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.cipher		= 16,
+		.vpp		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.lcdf		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.memstick	= 30,
+		.memstick_dma	= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.i2s		= 34,
+		.pcm		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.vpu		= 40,
+		.vid		= 41,
+		.ac97		= 42,
+		.ehci		= 43,
+		.nor		= 44,
+		.ps2mouse	= 45,
+		.ps2kbd		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.adc		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.dma0		= 56,
+		.dma1		= 57,
+		.dma2		= 58,
+		.dma3		= 59,
+		.dma4		= 60,
+		.dma5		= 61,
+		.dma6		= 62,
+		.dma7		= 63,
+		.nr_irqs	= 64,
+	},
+	[WM8505_INDEX] = {
+		.uhci		= 0,
+		.ehci		= 1,
+		.udc_dma	= 2,
+		.ps2mouse	= 4,
+		.udc		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.keypad		= 8,
+		.dma		= 9,
+		.ether		= 10,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.dma0		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.ps2kbd		= 23,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.dma1		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.uart5		= 30,
+		.uart4		= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.dma2		= 34,
+		.i2s		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.dma3		= 40,
+		.dma4		= 41,
+		.ac97		= 42,
+		.nor		= 44,
+		.dma5		= 45,
+		.dma6		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.dma7		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.irq0		= 56,
+		.irq1		= 57,
+		.irq2		= 58,
+		.irq3		= 59,
+		.irq4		= 60,
+		.irq5		= 61,
+		.irq6		= 62,
+		.irq7		= 63,
+		.jpegdec	= 65,
+		.sae		= 66,
+		.vpu		= 79,
+		.vpp		= 80,
+		.vid		= 81,
+		.spu		= 82,
+		.pip		= 83,
+		.ge		= 84,
+		.gov		= 85,
+		.dvo		= 86,
+		.dma8		= 92,
+		.dma9		= 93,
+		.dma10		= 94,
+		.dma11		= 95,
+		.dma12		= 96,
+		.dma13		= 97,
+		.dma14		= 98,
+		.dma15		= 99,
+		.govw		= 111,
+		.govrsdscd	= 112,
+		.govrsdmif	= 113,
+		.govrhdscd	= 114,
+		.govrhdmif	= 115,
+		.nr_irqs	= 116,
+	},
+};
diff --git a/arch/arm/mach-vt8500/mmio_regs.c b/arch/arm/mach-vt8500/mmio_regs.c
new file mode 100644
index 0000000..e9b3264
--- /dev/null
+++ b/arch/arm/mach-vt8500/mmio_regs.c
@@ -0,0 +1,118 @@
+/* linux/arch/arm/mach-vt8500/mmio_regs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/mmio_regs.h>
+
+struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+struct wmt_mmio_regs wmt_regmaps[] __initdata = {
+	[VT8500_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00350000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000000,
+		.dma			= 0xd8001000,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007900,
+		.uhci			= 0xd8007b01,
+		.pata			= 0xd8008000,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.memstick		= 0xd800b400,
+		.lcdc			= 0xd800e400,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.lcdf			= 0xd8050900,
+		.vid			= 0xd8050a00,
+		.vpp			= 0xd8050b00,
+		.tsbk			= 0xd80f4000,
+		.jpegdec		= 0xd80fe000,
+		.jpegenc		= 0xd80ff000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.pcm			= 0xd82d0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.adc			= 0xd8340000,
+	},
+	[WM8505_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00390000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000400,
+		.dma			= 0xd8001800,
+		.vdma			= 0xd8001c00,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007100,
+		.uhci			= 0xd8007301,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.govr			= 0xd8050800,
+		.vid			= 0xd8050a00,
+		.scl			= 0xd8050d00,
+		.vpp			= 0xd8050f00,
+		.jpegdec		= 0xd80fe000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.ic1			= 0xd8150000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.keypad			= 0xd8260000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.uart4			= 0xd8370000,
+		.uart5			= 0xd8380000,
+	},
+};
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..d1356a1
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,254 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+	int loops = 1000;
+	while ((readb(reg) & bitmask) && --loops)
+		cpu_relax();
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	dc = pv * duty_ns / period_ns;
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else
+			pwm = ERR_PTR(-EBUSY);
+	} else
+		pwm = ERR_PTR(-ENOENT);
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else
+		pr_warning("PWM device already freed\n");
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[0].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..ab4f7aa
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,154 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+static void __iomem *regbase;
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	int loops = 1000;
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	int loops = 1000;
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_current_regs->pmc + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO "
+				"registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register "
+			"failed for %s\n", clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_current_irqs->timer_match0, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq "
+			"failed for %s\n", clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..279b4bf
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,81 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/mmio_regs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= wm8505_map_io,
+	.reserve	= wm8505_reserve_mem,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.2


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

* Re: [PATCH 1/6 v6] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-12 16:54             ` [PATCH 1/6 v6] " Alexey Charkov
@ 2010-11-12 20:14               ` Alexey Charkov
  0 siblings, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-12 20:14 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

> Russell, could you please elaborate on your comment regarding spinlocks
> and irq handlers? I always compile my kernel with all kinds of lock
> debugging, and have not run into any associated warnings. Is there
> anything suspicious specifically?

My fault: actually, those were not all lock debugging functions. It
really prints errors when lock correctness proving is enabled. I thus
decided to just assign the callback functions directly to irq_desc
struct fields, as it is done in mach-msm.

There is, however, another error showing up: it complains about my use
of ioremap inside arch_reset(). Is it fine to just hardcode the
virtual address of the respective register and use that directly in
this case? Something along the lines of:

/* PM Software Reset request register */
#define VT8500_PMSR_VIRT	0xf8130060

static inline void arch_reset(char mode, const char *cmd)
{
	writel(1, VT8500_PMSR_VIRT);
}

Thanks,
Alexey

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

* Re: [PATCH 5/6 v2] rtc: Add support for the RTC in VIA VT8500 and compatibles
  2010-11-07 16:28 ` [PATCH 5/6 v2] rtc: Add support for the RTC in VIA VT8500 and compatibles Alexey Charkov
@ 2010-11-12 22:53   ` Alexey Charkov
  2010-11-13 12:14   ` Lars-Peter Clausen
  1 sibling, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-12 22:53 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, Alessandro Zummo,
	rtc-linux, linux-kernel

2010/11/7 Alexey Charkov <alchark@gmail.com>:
> This adds a driver for the RTC devices in VIA and WonderMedia
> Systems-on-Chip. Alarm, 1Hz interrupts, reading and setting time
> are supported.
>
> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> ---
>
> Please review and (if appropriate) commit to a relevant git tree for
> further integration in 2.6.38.
>
> Compared to the previous submission, this code just contains a rebase
> against the latest changes introduced in 2.6.37 merge window.
>
> Relevant platform definitions are introduced by PATCH 1/6 in this series,
> so one would need that to make use of this code.
>
> Due credits go to the community for providing feedback, advice and
> testing.
>
>  drivers/rtc/Kconfig      |    7 +
>  drivers/rtc/Makefile     |    1 +
>  drivers/rtc/rtc-vt8500.c |  363 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 371 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-vt8500.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 2883428..27ed129 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -865,6 +865,13 @@ config RTC_DRV_PXA
>          This RTC driver uses PXA RTC registers available since pxa27x
>          series (RDxR, RYxR) instead of legacy RCNR, RTAR.
>
> +config RTC_DRV_VT8500
> +       tristate "VIA/WonderMedia 85xx SoC RTC"
> +       depends on ARCH_VT8500
> +       help
> +         If you say Y here you will get access to the real time clock
> +         built into your VIA VT8500 SoC or its relatives.
> +
>
>  config RTC_DRV_SUN4V
>        bool "SUN4V Hypervisor RTC"
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 4c2832d..1a354e1 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
>  obj-$(CONFIG_RTC_DRV_TX4939)   += rtc-tx4939.o
>  obj-$(CONFIG_RTC_DRV_V3020)    += rtc-v3020.o
>  obj-$(CONFIG_RTC_DRV_VR41XX)   += rtc-vr41xx.o
> +obj-$(CONFIG_RTC_DRV_VT8500)   += rtc-vt8500.o
>  obj-$(CONFIG_RTC_DRV_WM831X)   += rtc-wm831x.o
>  obj-$(CONFIG_RTC_DRV_WM8350)   += rtc-wm8350.o
>  obj-$(CONFIG_RTC_DRV_X1205)    += rtc-x1205.o
> diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
> new file mode 100644
> index 0000000..7260c95
> --- /dev/null
> +++ b/drivers/rtc/rtc-vt8500.c
> @@ -0,0 +1,363 @@
> +/*
> + * drivers/rtc/rtc-vt8500.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * Based on rtc-pxa.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/bcd.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +/*
> + * Register definitions
> + */
> +#define VT8500_RTC_TS          0x00    /* Time set */
> +#define VT8500_RTC_DS          0x04    /* Date set */
> +#define VT8500_RTC_AS          0x08    /* Alarm set */
> +#define VT8500_RTC_CR          0x0c    /* Control */
> +#define VT8500_RTC_TR          0x10    /* Time read */
> +#define VT8500_RTC_DR          0x14    /* Date read */
> +#define VT8500_RTC_WS          0x18    /* Write status */
> +#define VT8500_RTC_CL          0x20    /* Calibration */
> +#define VT8500_RTC_IS          0x24    /* Interrupt status */
> +#define VT8500_RTC_ST          0x28    /* Status */
> +
> +#define INVALID_TIME_BIT       (1 << 31)
> +
> +#define DATE_CENTURY_S         19
> +#define DATE_YEAR_S            11
> +#define DATE_YEAR_MASK         (0xff << DATE_YEAR_S)
> +#define DATE_MONTH_S           6
> +#define DATE_MONTH_MASK                (0x1f << DATE_MONTH_S)
> +#define DATE_DAY_MASK          0x3f
> +
> +#define TIME_DOW_S             20
> +#define TIME_DOW_MASK          (0x07 << TIME_DOW_S)
> +#define TIME_HOUR_S            14
> +#define TIME_HOUR_MASK         (0x3f << TIME_HOUR_S)
> +#define TIME_MIN_S             7
> +#define TIME_MIN_MASK          (0x7f << TIME_MIN_S)
> +#define TIME_SEC_MASK          0x7f
> +
> +#define ALARM_DAY_S            20
> +#define ALARM_DAY_MASK         (0x3f << ALARM_DAY_S)
> +
> +#define ALARM_DAY_BIT          (1 << 29)
> +#define ALARM_HOUR_BIT         (1 << 28)
> +#define ALARM_MIN_BIT          (1 << 27)
> +#define ALARM_SEC_BIT          (1 << 26)
> +
> +#define ALARM_ENABLE_MASK      (ALARM_DAY_BIT \
> +                               | ALARM_HOUR_BIT \
> +                               | ALARM_MIN_BIT \
> +                               | ALARM_SEC_BIT)
> +
> +struct vt8500_rtc {
> +       void __iomem            *regbase;
> +       int                     irq_alarm;
> +       int                     irq_hz;
> +       struct rtc_device       *rtc;
> +       spinlock_t              lock;           /* Protects this structure */
> +       struct rtc_time         rtc_alarm;
> +};
> +
> +static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
> +{
> +       struct platform_device *pdev = to_platform_device(dev_id);
> +       struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
> +       u32 isr;
> +       unsigned long events = 0;
> +
> +       spin_lock(&vt8500_rtc->lock);
> +
> +       /* clear interrupt sources */
> +       isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
> +       writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
> +
> +       if (isr & 1)
> +               events |= RTC_AF | RTC_IRQF;
> +
> +       /* Only second/minute interrupts are supported */
> +       if (isr & 2)
> +               events |= RTC_UF | RTC_IRQF;
> +
> +       rtc_update_irq(vt8500_rtc->rtc, 1, events);
> +
> +       spin_unlock(&vt8500_rtc->lock);
> +       return IRQ_HANDLED;
> +}
> +
> +static int vt8500_rtc_open(struct device *dev)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +       int ret;
> +
> +       ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, IRQF_DISABLED,
> +                         "rtc 1Hz", dev);
> +       if (ret < 0) {
> +               dev_err(dev, "can't get irq %i, err %d\n", vt8500_rtc->irq_hz,
> +                       ret);
> +               goto err_irq_hz;
> +       }
> +       ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, IRQF_DISABLED,
> +                         "rtc alarm", dev);
> +       if (ret < 0) {
> +               dev_err(dev, "can't get irq %i, err %d\n",
> +                       vt8500_rtc->irq_alarm, ret);
> +               goto err_irq_alarm;
> +       }
> +       return 0;
> +
> +err_irq_alarm:
> +       free_irq(vt8500_rtc->irq_hz, dev);
> +err_irq_hz:
> +       return ret;
> +}
> +
> +static void vt8500_rtc_release(struct device *dev)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +       spin_lock_irq(&vt8500_rtc->lock);
> +       /* Disable alarm matching */
> +       writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
> +       spin_unlock_irq(&vt8500_rtc->lock);
> +
> +       free_irq(vt8500_rtc->irq_alarm, dev);
> +       free_irq(vt8500_rtc->irq_hz, dev);
> +}
> +
> +static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +       u32 date, time;
> +
> +       date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
> +       time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
> +
> +       tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
> +       tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
> +       tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
> +       tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
> +       tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
> +       tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S);
> +       tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
> +
> +       return 0;
> +}
> +
> +static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +       writel((bin2bcd(tm->tm_year) << DATE_YEAR_S)
> +               | (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
> +               | (bin2bcd(tm->tm_mday)),
> +               vt8500_rtc->regbase + VT8500_RTC_DS);
> +       writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
> +               | (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
> +               | (bin2bcd(tm->tm_min) << TIME_MIN_S)
> +               | (bin2bcd(tm->tm_sec)),
> +               vt8500_rtc->regbase + VT8500_RTC_TS);
> +
> +       return 0;
> +}
> +
> +static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +       u32 isr, alarm;
> +
> +       alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +       isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
> +
> +       alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
> +       alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
> +       alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
> +       alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
> +
> +       alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
> +
> +       alrm->pending = (isr & 1) ? 1 : 0;
> +       return 0;
> +}
> +
> +static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +       writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
> +               | (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
> +               | (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
> +               | (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
> +               | (bin2bcd(alrm->time.tm_sec)),
> +               vt8500_rtc->regbase + VT8500_RTC_AS);
> +
> +       return 0;
> +}
> +
> +static int vt8500_rtc_ioctl(struct device *dev, unsigned int cmd,
> +               unsigned long arg)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +       int ret = 0;
> +       unsigned long tmp;
> +
> +       spin_lock_irq(&vt8500_rtc->lock);
> +       switch (cmd) {
> +       case RTC_AIE_OFF:
> +               tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +               writel(tmp & ~ALARM_ENABLE_MASK,
> +                      vt8500_rtc->regbase + VT8500_RTC_AS);
> +               break;
> +       case RTC_AIE_ON:
> +               tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +               writel(tmp | ALARM_ENABLE_MASK,
> +                      vt8500_rtc->regbase + VT8500_RTC_AS);
> +               break;
> +       case RTC_UIE_OFF:
> +               tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
> +               writel(tmp & ~(1 << 2),
> +                      vt8500_rtc->regbase + VT8500_RTC_CR);
> +               break;
> +       case RTC_UIE_ON:
> +               tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
> +               writel(tmp | ((1 << 3) | (1 << 2)),
> +                      vt8500_rtc->regbase + VT8500_RTC_CR);
> +               break;
> +       default:
> +               ret = -ENOIOCTLCMD;
> +       }
> +
> +       spin_unlock_irq(&vt8500_rtc->lock);
> +       return ret;
> +}
> +
> +static const struct rtc_class_ops vt8500_rtc_ops = {
> +       .open = vt8500_rtc_open,
> +       .release = vt8500_rtc_release,
> +       .ioctl = vt8500_rtc_ioctl,
> +       .read_time = vt8500_rtc_read_time,
> +       .set_time = vt8500_rtc_set_time,
> +       .read_alarm = vt8500_rtc_read_alarm,
> +       .set_alarm = vt8500_rtc_set_alarm,
> +};
> +
> +static int __init vt8500_rtc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct vt8500_rtc *vt8500_rtc;
> +       struct resource *res;
> +       int ret;
> +
> +       vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
> +       if (!vt8500_rtc)
> +               return -ENOMEM;
> +
> +       spin_lock_init(&vt8500_rtc->lock);
> +       platform_set_drvdata(pdev, vt8500_rtc);
> +
> +       ret = -ENXIO;
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res) {
> +               dev_err(dev, "No I/O memory resource defined\n");
> +               goto err_map;
> +       }
> +
> +       vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
> +       if (vt8500_rtc->irq_alarm < 0) {
> +               dev_err(dev, "No alarm IRQ resource defined\n");
> +               goto err_map;
> +       }
> +
> +       vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
> +       if (vt8500_rtc->irq_hz < 0) {
> +               dev_err(dev, "No 1Hz IRQ resource defined\n");
> +               goto err_map;
> +       }
> +
> +       ret = -ENOMEM;
> +       vt8500_rtc->regbase = ioremap(res->start, resource_size(res));
> +       if (!vt8500_rtc->regbase) {
> +               dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
> +               goto err_map;
> +       }
> +
> +       /* Enable the second/minute interrupt generation and enable RTC */
> +       writel((1 << 3) | (1 << 2) | 1, vt8500_rtc->regbase + VT8500_RTC_CR);
> +
> +       vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
> +                                             &vt8500_rtc_ops, THIS_MODULE);
> +       ret = PTR_ERR(vt8500_rtc->rtc);
> +       if (IS_ERR(vt8500_rtc->rtc)) {
> +               dev_err(dev, "Failed to register RTC device -> %d\n", ret);
> +               goto err_rtc_reg;
> +       }
> +
> +       device_init_wakeup(dev, 1);
> +
> +       return 0;
> +
> +err_rtc_reg:
> +       iounmap(vt8500_rtc->regbase);
> +err_map:
> +       kfree(vt8500_rtc);
> +       return ret;
> +}
> +
> +static int __exit vt8500_rtc_remove(struct platform_device *pdev)
> +{
> +       struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
> +
> +       rtc_device_unregister(vt8500_rtc->rtc);
> +
> +       spin_lock_irq(&vt8500_rtc->lock);
> +       iounmap(vt8500_rtc->regbase);
> +       spin_unlock_irq(&vt8500_rtc->lock);
> +
> +       kfree(vt8500_rtc);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver vt8500_rtc_driver = {
> +       .probe          = vt8500_rtc_probe,
> +       .remove         = __exit_p(vt8500_rtc_remove),
> +       .driver         = {
> +               .name   = "vt8500-rtc"
> +       },
> +};
> +
> +static int __init vt8500_rtc_init(void)
> +{
> +       return platform_driver_register(&vt8500_rtc_driver);
> +}
> +
> +static void __exit vt8500_rtc_exit(void)
> +{
> +       platform_driver_unregister(&vt8500_rtc_driver);
> +}
> +
> +module_init(vt8500_rtc_init);
> +module_exit(vt8500_rtc_exit);
> +
> +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
> +MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:vt8500-rtc");
> --
> 1.7.3.2
>
>

Any comments about this?

Thanks,
Alexey

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

* Re: [PATCH 3/6 v2] input: Add support for VIA VT8500 and compatibles in i8042
  2010-11-07 16:28 ` [PATCH 3/6 v2] input: Add support for VIA VT8500 and compatibles in i8042 Alexey Charkov
@ 2010-11-12 22:54   ` Alexey Charkov
  2010-11-12 23:30     ` Dmitry Torokhov
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-12 22:54 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, Dmitry Torokhov,
	Tony Lindgren, Feng Tang, Janusz Krzysztofik,
	Dmitry Eremin-Solenikov, linux-input, linux-kernel

2010/11/7 Alexey Charkov <alchark@gmail.com>:
> VIA and WonderMedia Systems-on-Chip feature a standard i8042-compatible
> keyboard and mouse controller. This adds necessary glue to enable use
> of the standard driver with these systems.
>
> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> ---
>
> Please review and (if appropriate) commit to a relevant git tree for
> further integration in 2.6.38.
>
> Previous version of this code was 'Acked-by: Dmitry Torokhov <dtor@mail.ru>'
> This one only differs by using runtime-selected IRQ definitions instead
> of static compile-time preprocessor macros.
>
> Relevant register and interrupt definitions are provided by PATCH 1/6 in
> this series, so one would need that to make use of this code.
>
>  drivers/input/serio/Kconfig        |    3 +-
>  drivers/input/serio/i8042-vt8500.h |   74 ++++++++++++++++++++++++++++++++++++
>  drivers/input/serio/i8042.h        |    2 +
>  3 files changed, 78 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/input/serio/i8042-vt8500.h
>
> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> index 6256233..ff799f3 100644
> --- a/drivers/input/serio/Kconfig
> +++ b/drivers/input/serio/Kconfig
> @@ -21,7 +21,8 @@ if SERIO
>  config SERIO_I8042
>        tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
>        default y
> -       depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
> +       depends on !PARISC && \
> +                 (!ARM || ARCH_SHARK || ARCH_VT8500 || FOOTBRIDGE_HOST) && \
>                   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
>        help
>          i8042 is the chip over which the standard AT keyboard and PS/2
> diff --git a/drivers/input/serio/i8042-vt8500.h b/drivers/input/serio/i8042-vt8500.h
> new file mode 100644
> index 0000000..4ff9e1c
> --- /dev/null
> +++ b/drivers/input/serio/i8042-vt8500.h
> @@ -0,0 +1,74 @@
> +#ifndef _I8042_VT8500_H
> +#define _I8042_VT8500_H
> +
> +#include <mach/mmio_regs.h>
> +#include <mach/irq_defs.h>
> +
> +/*
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +static void __iomem *regbase;
> +
> +/*
> + * Names.
> + */
> +
> +#define I8042_KBD_PHYS_DESC "vt8500ps2/serio0"
> +#define I8042_AUX_PHYS_DESC "vt8500ps2/serio1"
> +#define I8042_MUX_PHYS_DESC "vt8500ps2/serio%d"
> +
> +/*
> + * IRQs.
> + */
> +
> +#define I8042_KBD_IRQ  (wmt_current_irqs->ps2kbd)
> +#define I8042_AUX_IRQ  (wmt_current_irqs->ps2mouse)
> +
> +
> +/*
> + * Register numbers.
> + */
> +
> +#define I8042_COMMAND_REG      (regbase + 0x4)
> +#define I8042_STATUS_REG       (regbase + 0x4)
> +#define I8042_DATA_REG         (regbase + 0x0)
> +
> +static inline int i8042_read_data(void)
> +{
> +       return readl(I8042_DATA_REG);
> +}
> +
> +static inline int i8042_read_status(void)
> +{
> +       return readl(I8042_STATUS_REG);
> +}
> +
> +static inline void i8042_write_data(int val)
> +{
> +       writel(val, I8042_DATA_REG);
> +}
> +
> +static inline void i8042_write_command(int val)
> +{
> +       writel(val, I8042_COMMAND_REG);
> +}
> +
> +static inline int i8042_platform_init(void)
> +{
> +       i8042_reset = true;
> +       regbase = ioremap(wmt_current_regs->ps2, SZ_1K);
> +       if (!regbase)
> +               return -ENODEV;
> +
> +       return 0;
> +}
> +
> +static inline void i8042_platform_exit(void)
> +{
> +       iounmap(regbase);
> +}
> +
> +#endif /* _I8042_VT8500_H */
> diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
> index cbc1beb..bdb2aeb 100644
> --- a/drivers/input/serio/i8042.h
> +++ b/drivers/input/serio/i8042.h
> @@ -16,6 +16,8 @@
>
>  #if defined(CONFIG_MACH_JAZZ)
>  #include "i8042-jazzio.h"
> +#elif defined(CONFIG_ARCH_VT8500)
> +#include "i8042-vt8500.h"
>  #elif defined(CONFIG_SGI_HAS_I8042)
>  #include "i8042-ip22io.h"
>  #elif defined(CONFIG_SNI_RM)
> --
> 1.7.3.2
>
>

Any comments about this?

Thanks,
Alexey

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

* Re: [PATCH 3/6 v2] input: Add support for VIA VT8500 and compatibles in i8042
  2010-11-12 22:54   ` Alexey Charkov
@ 2010-11-12 23:30     ` Dmitry Torokhov
  2010-11-13  0:00       ` Alexey Charkov
  2010-12-22 21:41       ` [PATCH 3/6 v3] " Alexey Charkov
  0 siblings, 2 replies; 91+ messages in thread
From: Dmitry Torokhov @ 2010-11-12 23:30 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Tony Lindgren,
	Feng Tang, Janusz Krzysztofik, Dmitry Eremin-Solenikov,
	linux-input, linux-kernel

On Sat, Nov 13, 2010 at 01:54:14AM +0300, Alexey Charkov wrote:
> 2010/11/7 Alexey Charkov <alchark@gmail.com>:
> > VIA and WonderMedia Systems-on-Chip feature a standard i8042-compatible
> > keyboard and mouse controller. This adds necessary glue to enable use
> > of the standard driver with these systems.
> >
> > Signed-off-by: Alexey Charkov <alchark@gmail.com>
> > ---
> >
> > Please review and (if appropriate) commit to a relevant git tree for
> > further integration in 2.6.38.
> >
> > Previous version of this code was 'Acked-by: Dmitry Torokhov <dtor@mail.ru>'
> > This one only differs by using runtime-selected IRQ definitions instead
> > of static compile-time preprocessor macros.
> >
> > Relevant register and interrupt definitions are provided by PATCH 1/6 in
> > this series, so one would need that to make use of this code.
> >
> >  drivers/input/serio/Kconfig        |    3 +-
> >  drivers/input/serio/i8042-vt8500.h |   74 ++++++++++++++++++++++++++++++++++++
> >  drivers/input/serio/i8042.h        |    2 +
> >  3 files changed, 78 insertions(+), 1 deletions(-)
> >  create mode 100644 drivers/input/serio/i8042-vt8500.h
> >
> > diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> > index 6256233..ff799f3 100644
> > --- a/drivers/input/serio/Kconfig
> > +++ b/drivers/input/serio/Kconfig
> > @@ -21,7 +21,8 @@ if SERIO
> >  config SERIO_I8042
> >        tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
> >        default y
> > -       depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
> > +       depends on !PARISC && \
> > +                 (!ARM || ARCH_SHARK || ARCH_VT8500 || FOOTBRIDGE_HOST) && \
> >                   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
> >        help
> >          i8042 is the chip over which the standard AT keyboard and PS/2
> > diff --git a/drivers/input/serio/i8042-vt8500.h b/drivers/input/serio/i8042-vt8500.h
> > new file mode 100644
> > index 0000000..4ff9e1c
> > --- /dev/null
> > +++ b/drivers/input/serio/i8042-vt8500.h
> > @@ -0,0 +1,74 @@
> > +#ifndef _I8042_VT8500_H
> > +#define _I8042_VT8500_H
> > +
> > +#include <mach/mmio_regs.h>
> > +#include <mach/irq_defs.h>
> > +
> > +/*
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation.
> > + */
> > +
> > +static void __iomem *regbase;
> > +
> > +/*
> > + * Names.
> > + */
> > +
> > +#define I8042_KBD_PHYS_DESC "vt8500ps2/serio0"
> > +#define I8042_AUX_PHYS_DESC "vt8500ps2/serio1"
> > +#define I8042_MUX_PHYS_DESC "vt8500ps2/serio%d"
> > +
> > +/*
> > + * IRQs.
> > + */
> > +
> > +#define I8042_KBD_IRQ  (wmt_current_irqs->ps2kbd)
> > +#define I8042_AUX_IRQ  (wmt_current_irqs->ps2mouse)
> > +
> > +
> > +/*
> > + * Register numbers.
> > + */
> > +
> > +#define I8042_COMMAND_REG      (regbase + 0x4)
> > +#define I8042_STATUS_REG       (regbase + 0x4)
> > +#define I8042_DATA_REG         (regbase + 0x0)
> > +
> > +static inline int i8042_read_data(void)
> > +{
> > +       return readl(I8042_DATA_REG);
> > +}
> > +
> > +static inline int i8042_read_status(void)
> > +{
> > +       return readl(I8042_STATUS_REG);
> > +}
> > +
> > +static inline void i8042_write_data(int val)
> > +{
> > +       writel(val, I8042_DATA_REG);
> > +}
> > +
> > +static inline void i8042_write_command(int val)
> > +{
> > +       writel(val, I8042_COMMAND_REG);
> > +}
> > +
> > +static inline int i8042_platform_init(void)
> > +{
> > +       i8042_reset = true;
> > +       regbase = ioremap(wmt_current_regs->ps2, SZ_1K);
> > +       if (!regbase)
> > +               return -ENODEV;
> > +
> > +       return 0;
> > +}
> > +
> > +static inline void i8042_platform_exit(void)
> > +{
> > +       iounmap(regbase);
> > +}
> > +
> > +#endif /* _I8042_VT8500_H */
> > diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
> > index cbc1beb..bdb2aeb 100644
> > --- a/drivers/input/serio/i8042.h
> > +++ b/drivers/input/serio/i8042.h
> > @@ -16,6 +16,8 @@
> >
> >  #if defined(CONFIG_MACH_JAZZ)
> >  #include "i8042-jazzio.h"
> > +#elif defined(CONFIG_ARCH_VT8500)
> > +#include "i8042-vt8500.h"
> >  #elif defined(CONFIG_SGI_HAS_I8042)
> >  #include "i8042-ip22io.h"
> >  #elif defined(CONFIG_SNI_RM)
> > --
> > 1.7.3.2
> >
> >
> 
> Any comments about this?
> 

Looks good to me (ignoring the fact that whole i8042 initialization
needs to be reworked and pushed into arch/board code which is out of
scope of these series).

I expect it will be merged with the rest of VT8500 patches through
whatever tree takes them.

Thanks.

-- 
Dmitry

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

* Re: [PATCH 3/6 v2] input: Add support for VIA VT8500 and compatibles in i8042
  2010-11-12 23:30     ` Dmitry Torokhov
@ 2010-11-13  0:00       ` Alexey Charkov
  2010-12-22 21:41       ` [PATCH 3/6 v3] " Alexey Charkov
  1 sibling, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-13  0:00 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Tony Lindgren,
	Feng Tang, Janusz Krzysztofik, Dmitry Eremin-Solenikov,
	linux-input, linux-kernel

2010/11/13 Dmitry Torokhov <dmitry.torokhov@gmail.com>:
> Looks good to me (ignoring the fact that whole i8042 initialization
> needs to be reworked and pushed into arch/board code which is out of
> scope of these series).
>
> I expect it will be merged with the rest of VT8500 patches through
> whatever tree takes them.

Thanks, Dmitry!

Best regards,
Alexey

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

* Re: [PATCH 5/6 v2] rtc: Add support for the RTC in VIA VT8500 and compatibles
  2010-11-07 16:28 ` [PATCH 5/6 v2] rtc: Add support for the RTC in VIA VT8500 and compatibles Alexey Charkov
  2010-11-12 22:53   ` Alexey Charkov
@ 2010-11-13 12:14   ` Lars-Peter Clausen
  2010-11-13 23:56     ` [PATCH 5/6 v3] " Alexey Charkov
  1 sibling, 1 reply; 91+ messages in thread
From: Lars-Peter Clausen @ 2010-11-13 12:14 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Alessandro Zummo,
	linux-kernel, rtc-linux

Alexey Charkov wrote:
> This adds a driver for the RTC devices in VIA and WonderMedia
> Systems-on-Chip. Alarm, 1Hz interrupts, reading and setting time
> are supported.
> 
> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> ---
> 
> Please review and (if appropriate) commit to a relevant git tree for
> further integration in 2.6.38.
> 
> Compared to the previous submission, this code just contains a rebase
> against the latest changes introduced in 2.6.37 merge window.
> 
> Relevant platform definitions are introduced by PATCH 1/6 in this series,
> so one would need that to make use of this code.
> 
> Due credits go to the community for providing feedback, advice and
> testing.
> 
>  drivers/rtc/Kconfig      |    7 +
>  drivers/rtc/Makefile     |    1 +
>  drivers/rtc/rtc-vt8500.c |  363 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 371 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-vt8500.c
> 
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 2883428..27ed129 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -865,6 +865,13 @@ config RTC_DRV_PXA
>           This RTC driver uses PXA RTC registers available since pxa27x
>           series (RDxR, RYxR) instead of legacy RCNR, RTAR.
>  
> +config RTC_DRV_VT8500
> +	tristate "VIA/WonderMedia 85xx SoC RTC"
> +	depends on ARCH_VT8500
> +	help
> +	  If you say Y here you will get access to the real time clock
> +	  built into your VIA VT8500 SoC or its relatives.
> +
>  
>  config RTC_DRV_SUN4V
>  	bool "SUN4V Hypervisor RTC"
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 4c2832d..1a354e1 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030)	+= rtc-twl.o
>  obj-$(CONFIG_RTC_DRV_TX4939)	+= rtc-tx4939.o
>  obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o
>  obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o
> +obj-$(CONFIG_RTC_DRV_VT8500)	+= rtc-vt8500.o
>  obj-$(CONFIG_RTC_DRV_WM831X)	+= rtc-wm831x.o
>  obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
>  obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
> diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
> new file mode 100644
> index 0000000..7260c95
> --- /dev/null
> +++ b/drivers/rtc/rtc-vt8500.c
> @@ -0,0 +1,363 @@
> +/*
> + * drivers/rtc/rtc-vt8500.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * Based on rtc-pxa.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/bcd.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +/*
> + * Register definitions
> + */
> +#define VT8500_RTC_TS		0x00	/* Time set */
> +#define VT8500_RTC_DS		0x04	/* Date set */
> +#define VT8500_RTC_AS		0x08	/* Alarm set */
> +#define VT8500_RTC_CR		0x0c	/* Control */
> +#define VT8500_RTC_TR		0x10	/* Time read */
> +#define VT8500_RTC_DR		0x14	/* Date read */
> +#define VT8500_RTC_WS		0x18	/* Write status */
> +#define VT8500_RTC_CL		0x20	/* Calibration */
> +#define VT8500_RTC_IS		0x24	/* Interrupt status */
> +#define VT8500_RTC_ST		0x28	/* Status */
> +
> +#define INVALID_TIME_BIT	(1 << 31)
> +
> +#define DATE_CENTURY_S		19
> +#define DATE_YEAR_S		11
> +#define DATE_YEAR_MASK		(0xff << DATE_YEAR_S)
> +#define DATE_MONTH_S		6
> +#define DATE_MONTH_MASK		(0x1f << DATE_MONTH_S)
> +#define DATE_DAY_MASK		0x3f
> +
> +#define TIME_DOW_S		20
> +#define TIME_DOW_MASK		(0x07 << TIME_DOW_S)
> +#define TIME_HOUR_S		14
> +#define TIME_HOUR_MASK		(0x3f << TIME_HOUR_S)
> +#define TIME_MIN_S		7
> +#define TIME_MIN_MASK		(0x7f << TIME_MIN_S)
> +#define TIME_SEC_MASK		0x7f
> +
> +#define ALARM_DAY_S		20
> +#define ALARM_DAY_MASK		(0x3f << ALARM_DAY_S)
> +
> +#define ALARM_DAY_BIT		(1 << 29)
> +#define ALARM_HOUR_BIT		(1 << 28)
> +#define ALARM_MIN_BIT		(1 << 27)
> +#define ALARM_SEC_BIT		(1 << 26)
> +
> +#define ALARM_ENABLE_MASK	(ALARM_DAY_BIT \
> +				| ALARM_HOUR_BIT \
> +				| ALARM_MIN_BIT \
> +				| ALARM_SEC_BIT)
> +
> +struct vt8500_rtc {
> +	void __iomem		*regbase;
> +	int			irq_alarm;
> +	int			irq_hz;
> +	struct rtc_device	*rtc;
> +	spinlock_t		lock;		/* Protects this structure */
> +	struct rtc_time		rtc_alarm;
This field is unused.
> +};
> +
> +static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
> +{
> +	struct platform_device *pdev = to_platform_device(dev_id);
> +	struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
Wouldn't it be better to pass the vt8500_rtc struct as dev_id to request_irq?

> +	u32 isr;
> +	unsigned long events = 0;
> +
> +	spin_lock(&vt8500_rtc->lock);
> +
> +	/* clear interrupt sources */
> +	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
> +	writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
You only have to hold the lock until here.

> +
> +	if (isr & 1)
> +		events |= RTC_AF | RTC_IRQF;
> +
> +	/* Only second/minute interrupts are supported */
> +	if (isr & 2)
> +		events |= RTC_UF | RTC_IRQF;
> +
> +	rtc_update_irq(vt8500_rtc->rtc, 1, events);
> +
> +	spin_unlock(&vt8500_rtc->lock);
> +	return IRQ_HANDLED;
> +}
> +
> +static int vt8500_rtc_open(struct device *dev)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, IRQF_DISABLED,
> +			  "rtc 1Hz", dev);
IRQF_DISABLED is deprecapted and a NOOP right now. You shouldn't add new code using it.
> +	if (ret < 0) {
> +		dev_err(dev, "can't get irq %i, err %d\n", vt8500_rtc->irq_hz,
> +			ret);
> +		goto err_irq_hz;
> +	}
> +	ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, IRQF_DISABLED,
> +			  "rtc alarm", dev);
> +	if (ret < 0) {
> +		dev_err(dev, "can't get irq %i, err %d\n",
> +			vt8500_rtc->irq_alarm, ret);
> +		goto err_irq_alarm;
> +	}
> +	return 0;

Is there any specific reason why you don't keep the irq requested for the lifetime of
the device?
You wont be able to provide wakeup functionallity this way.

> +
> +err_irq_alarm:
> +	free_irq(vt8500_rtc->irq_hz, dev);
> +err_irq_hz:
> +	return ret;
> +}
> +
> +static void vt8500_rtc_release(struct device *dev)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +	spin_lock_irq(&vt8500_rtc->lock);
> +	/* Disable alarm matching */
> +	writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
> +	spin_unlock_irq(&vt8500_rtc->lock);
> +
> +	free_irq(vt8500_rtc->irq_alarm, dev);
> +	free_irq(vt8500_rtc->irq_hz, dev);
> +}
> +
> +static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +	u32 date, time;
> +
> +	date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
> +	time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
> +
> +	tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
> +	tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
> +	tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
> +	tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
> +	tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
> +	tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S);
> +	tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
> +
> +	return 0;
return rtc_valid_tm(tm);

> +}
> +
> +static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +	writel((bin2bcd(tm->tm_year) << DATE_YEAR_S)
> +		| (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
> +		| (bin2bcd(tm->tm_mday)),
> +		vt8500_rtc->regbase + VT8500_RTC_DS);
> +	writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
> +		| (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
> +		| (bin2bcd(tm->tm_min) << TIME_MIN_S)
> +		| (bin2bcd(tm->tm_sec)),
> +		vt8500_rtc->regbase + VT8500_RTC_TS);
> +
> +	return 0;
> +}
> +
> +static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +	u32 isr, alarm;
> +
> +	alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
> +
> +	alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
> +	alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
> +	alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
> +	alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
> +
> +	alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
> +
> +	alrm->pending = (isr & 1) ? 1 : 0;
> +	return 0;
return rtc_valid_tm(&alrm->time);

> +}
> +
> +static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +	writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
> +		| (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
> +		| (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
> +		| (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
> +		| (bin2bcd(alrm->time.tm_sec)),
> +		vt8500_rtc->regbase + VT8500_RTC_AS);
> +
> +	return 0;
> +}
> +
> +static int vt8500_rtc_ioctl(struct device *dev, unsigned int cmd,
> +		unsigned long arg)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +	int ret = 0;
> +	unsigned long tmp;
> +
> +	spin_lock_irq(&vt8500_rtc->lock);
> +	switch (cmd) {
> +	case RTC_AIE_OFF:
> +		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +		writel(tmp & ~ALARM_ENABLE_MASK,
> +		       vt8500_rtc->regbase + VT8500_RTC_AS);
> +		break;
> +	case RTC_AIE_ON:
> +		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +		writel(tmp | ALARM_ENABLE_MASK,
> +		       vt8500_rtc->regbase + VT8500_RTC_AS);
> +		break;
> +	case RTC_UIE_OFF:
> +		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
> +		writel(tmp & ~(1 << 2),
> +		       vt8500_rtc->regbase + VT8500_RTC_CR);
It would be better if you added descriptive defines for the bits of the CR register.
Makes reading the driver easier.
> +		break;
> +	case RTC_UIE_ON:
> +		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
> +		writel(tmp | ((1 << 3) | (1 << 2)),
> +		       vt8500_rtc->regbase + VT8500_RTC_CR);
> +		break;
> +	default:
> +		ret = -ENOIOCTLCMD;

You should rather implement these by providing the update_irq_enable and
alarm_irq_enable callbacks of your rtc_class_ops instead of handling the ioctls yourself.

> +	}
> +
> +	spin_unlock_irq(&vt8500_rtc->lock);
> +	return ret;
> +}
> +
> +static const struct rtc_class_ops vt8500_rtc_ops = {
> +	.open = vt8500_rtc_open,
> +	.release = vt8500_rtc_release,
> +	.ioctl = vt8500_rtc_ioctl,
> +	.read_time = vt8500_rtc_read_time,
> +	.set_time = vt8500_rtc_set_time,
> +	.read_alarm = vt8500_rtc_read_alarm,
> +	.set_alarm = vt8500_rtc_set_alarm,
> +};
> +
> +static int __init vt8500_rtc_probe(struct platform_device *pdev)
__devinit
> +{
> +	struct device *dev = &pdev->dev;
> +	struct vt8500_rtc *vt8500_rtc;
> +	struct resource *res;
> +	int ret;
> +
> +	vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
> +	if (!vt8500_rtc)
> +		return -ENOMEM;
> +
> +	spin_lock_init(&vt8500_rtc->lock);
> +	platform_set_drvdata(pdev, vt8500_rtc);
> +
> +	ret = -ENXIO;
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "No I/O memory resource defined\n");
> +		goto err_map;
> +	}
> +
> +	vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
> +	if (vt8500_rtc->irq_alarm < 0) {
> +		dev_err(dev, "No alarm IRQ resource defined\n");
> +		goto err_map;
> +	}
> +
> +	vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
> +	if (vt8500_rtc->irq_hz < 0) {
> +		dev_err(dev, "No 1Hz IRQ resource defined\n");
> +		goto err_map;
> +	}

You should first request the memory region before ioremapping it

> +
> +	ret = -ENOMEM;
Move the assignment inside the if statement.
> +	vt8500_rtc->regbase = ioremap(res->start, resource_size(res));
> +	if (!vt8500_rtc->regbase) {
> +		dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
> +		goto err_map;
> +	}
> +
> +	/* Enable the second/minute interrupt generation and enable RTC */
> +	writel((1 << 3) | (1 << 2) | 1, vt8500_rtc->regbase + VT8500_RTC_CR);
> +
> +	vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
> +					      &vt8500_rtc_ops, THIS_MODULE);
> +	ret = PTR_ERR(vt8500_rtc->rtc);
Move the assignment inside the if statement.
> +	if (IS_ERR(vt8500_rtc->rtc)) {
> +		dev_err(dev, "Failed to register RTC device -> %d\n", ret);
> +		goto err_rtc_reg;
> +	}
> +
> +	device_init_wakeup(dev, 1);
As far as I can see your driver does not provide any wakeup functionality.
> +
> +	return 0;
> +
> +err_rtc_reg:
> +	iounmap(vt8500_rtc->regbase);
> +err_map:
> +	kfree(vt8500_rtc);
> +	return ret;
> +}
> +
> +static int __exit vt8500_rtc_remove(struct platform_device *pdev)
__devexit
> +{
> +	struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
> +
> +	rtc_device_unregister(vt8500_rtc->rtc);
> +
> +	spin_lock_irq(&vt8500_rtc->lock);
> +	iounmap(vt8500_rtc->regbase);
> +	spin_unlock_irq(&vt8500_rtc->lock);
> +
> +	kfree(vt8500_rtc);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver vt8500_rtc_driver = {
> +	.probe		= vt8500_rtc_probe,
> +	.remove		= __exit_p(vt8500_rtc_remove),
__devexit_p
> +	.driver		= {
> +		.name	= "vt8500-rtc"
.owner = THIS_MODULE,
> +	},
> +};
> +
> +static int __init vt8500_rtc_init(void)
> +{
> +	return platform_driver_register(&vt8500_rtc_driver);
> +}
> +
> +static void __exit vt8500_rtc_exit(void)
> +{
> +	platform_driver_unregister(&vt8500_rtc_driver);
> +}
> +
> +module_init(vt8500_rtc_init);
> +module_exit(vt8500_rtc_exit);
module_{init,exit} should go right beneath the function they reference.
> +
> +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
> +MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:vt8500-rtc");

- Lars

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

* [PATCH 5/6 v3] rtc: Add support for the RTC in VIA VT8500 and compatibles
  2010-11-13 12:14   ` Lars-Peter Clausen
@ 2010-11-13 23:56     ` Alexey Charkov
  2010-11-14 15:50       ` Lars-Peter Clausen
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-13 23:56 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Alessandro Zummo, linux-kernel, rtc-linux

This adds a driver for the RTC devices in VIA and WonderMedia
Systems-on-Chip. Alarm, 1Hz interrupts, reading and setting time
are supported.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This implements the suggestions made by Lars. Reference to wakeup
was removed, as currently other code does not yet provide any
support for that.

It now also checks the status register for indication of a valid
time value before trying to read date/time registers.

Thanks,
Alexey

 drivers/rtc/Kconfig      |    7 +
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-vt8500.c |  365 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 373 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-vt8500.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2883428..27ed129 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -865,6 +865,13 @@ config RTC_DRV_PXA
          This RTC driver uses PXA RTC registers available since pxa27x
          series (RDxR, RYxR) instead of legacy RCNR, RTAR.
 
+config RTC_DRV_VT8500
+	tristate "VIA/WonderMedia 85xx SoC RTC"
+	depends on ARCH_VT8500
+	help
+	  If you say Y here you will get access to the real time clock
+	  built into your VIA VT8500 SoC or its relatives.
+
 
 config RTC_DRV_SUN4V
 	bool "SUN4V Hypervisor RTC"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 4c2832d..1a354e1 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030)	+= rtc-twl.o
 obj-$(CONFIG_RTC_DRV_TX4939)	+= rtc-tx4939.o
 obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o
 obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o
+obj-$(CONFIG_RTC_DRV_VT8500)	+= rtc-vt8500.o
 obj-$(CONFIG_RTC_DRV_WM831X)	+= rtc-wm831x.o
 obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
new file mode 100644
index 0000000..e4f2aa9
--- /dev/null
+++ b/drivers/rtc/rtc-vt8500.c
@@ -0,0 +1,365 @@
+/*
+ * drivers/rtc/rtc-vt8500.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on rtc-pxa.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/*
+ * Register definitions
+ */
+#define VT8500_RTC_TS		0x00	/* Time set */
+#define VT8500_RTC_DS		0x04	/* Date set */
+#define VT8500_RTC_AS		0x08	/* Alarm set */
+#define VT8500_RTC_CR		0x0c	/* Control */
+#define VT8500_RTC_TR		0x10	/* Time read */
+#define VT8500_RTC_DR		0x14	/* Date read */
+#define VT8500_RTC_WS		0x18	/* Write status */
+#define VT8500_RTC_CL		0x20	/* Calibration */
+#define VT8500_RTC_IS		0x24	/* Interrupt status */
+#define VT8500_RTC_ST		0x28	/* Status */
+
+#define INVALID_TIME_BIT	(1 << 31)
+
+#define DATE_CENTURY_S		19
+#define DATE_YEAR_S		11
+#define DATE_YEAR_MASK		(0xff << DATE_YEAR_S)
+#define DATE_MONTH_S		6
+#define DATE_MONTH_MASK		(0x1f << DATE_MONTH_S)
+#define DATE_DAY_MASK		0x3f
+
+#define TIME_DOW_S		20
+#define TIME_DOW_MASK		(0x07 << TIME_DOW_S)
+#define TIME_HOUR_S		14
+#define TIME_HOUR_MASK		(0x3f << TIME_HOUR_S)
+#define TIME_MIN_S		7
+#define TIME_MIN_MASK		(0x7f << TIME_MIN_S)
+#define TIME_SEC_MASK		0x7f
+
+#define ALARM_DAY_S		20
+#define ALARM_DAY_MASK		(0x3f << ALARM_DAY_S)
+
+#define ALARM_DAY_BIT		(1 << 29)
+#define ALARM_HOUR_BIT		(1 << 28)
+#define ALARM_MIN_BIT		(1 << 27)
+#define ALARM_SEC_BIT		(1 << 26)
+
+#define ALARM_ENABLE_MASK	(ALARM_DAY_BIT \
+				| ALARM_HOUR_BIT \
+				| ALARM_MIN_BIT \
+				| ALARM_SEC_BIT)
+
+#define VT8500_RTC_CR_ENABLE	(1 << 0)	/* Enable RTC */
+#define VT8500_RTC_CR_24H	(1 << 1)	/* 24h time format */
+#define VT8500_RTC_CR_SM_ENABLE	(1 << 2)	/* Enable periodic irqs */
+#define VT8500_RTC_CR_SM_SEC	(1 << 3)	/* 0: 1Hz/60, 1: 1Hz */
+#define VT8500_RTC_CR_CALIB	(1 << 4)	/* Enable calibration */
+
+struct vt8500_rtc {
+	void __iomem		*regbase;
+	int			irq_alarm;
+	int			irq_hz;
+	struct rtc_device	*rtc;
+	spinlock_t		lock;		/* Protects this structure */
+};
+
+static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_id;
+	u32 isr;
+	unsigned long events = 0;
+
+	spin_lock(&vt8500_rtc->lock);
+
+	/* clear interrupt sources */
+	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+	writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
+
+	spin_unlock(&vt8500_rtc->lock);
+
+	if (isr & 1)
+		events |= RTC_AF | RTC_IRQF;
+
+	/* Only second/minute interrupts are supported */
+	if (isr & 2)
+		events |= RTC_UF | RTC_IRQF;
+
+	rtc_update_irq(vt8500_rtc->rtc, 1, events);
+
+	return IRQ_HANDLED;
+}
+
+static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	u32 date, time;
+
+	if (!readl(vt8500_rtc->regbase + VT8500_RTC_ST)) {
+		dev_info(dev, "Clock not set!");
+		return -EINVAL;
+	}
+	date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
+	time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
+
+	tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
+	tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
+	tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
+	tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
+	tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
+	tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S);
+	tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
+
+	return rtc_valid_tm(tm);
+}
+
+static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+	writel((bin2bcd(tm->tm_year) << DATE_YEAR_S)
+		| (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
+		| (bin2bcd(tm->tm_mday)),
+		vt8500_rtc->regbase + VT8500_RTC_DS);
+	writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
+		| (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
+		| (bin2bcd(tm->tm_min) << TIME_MIN_S)
+		| (bin2bcd(tm->tm_sec)),
+		vt8500_rtc->regbase + VT8500_RTC_TS);
+
+	return 0;
+}
+
+static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	u32 isr, alarm;
+
+	alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+
+	alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
+	alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
+	alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
+	alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
+
+	alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
+
+	alrm->pending = (isr & 1) ? 1 : 0;
+	return rtc_valid_tm(&alrm->time);
+}
+
+static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+	writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
+		| (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
+		| (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
+		| (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
+		| (bin2bcd(alrm->time.tm_sec)),
+		vt8500_rtc->regbase + VT8500_RTC_AS);
+
+	return 0;
+}
+
+static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	unsigned long tmp;
+
+	if (enabled) {
+		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+		writel(tmp | ALARM_ENABLE_MASK,
+		       vt8500_rtc->regbase + VT8500_RTC_AS);
+	} else {
+		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+		writel(tmp & ~ALARM_ENABLE_MASK,
+		       vt8500_rtc->regbase + VT8500_RTC_AS);
+	}
+	return 0;
+}
+
+static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	unsigned long tmp;
+
+	if (enabled) {
+		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
+		writel(tmp | VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE,
+		       vt8500_rtc->regbase + VT8500_RTC_CR);
+	} else {
+		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
+		writel(tmp & ~VT8500_RTC_CR_SM_ENABLE,
+		       vt8500_rtc->regbase + VT8500_RTC_CR);
+	}
+	return 0;
+}
+
+static const struct rtc_class_ops vt8500_rtc_ops = {
+	.read_time = vt8500_rtc_read_time,
+	.set_time = vt8500_rtc_set_time,
+	.read_alarm = vt8500_rtc_read_alarm,
+	.set_alarm = vt8500_rtc_set_alarm,
+	.alarm_irq_enable = vt8500_alarm_irq_enable,
+	.update_irq_enable = vt8500_update_irq_enable,
+};
+
+static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
+{
+	struct vt8500_rtc *vt8500_rtc;
+	struct resource *res;
+	int ret;
+
+	vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
+	if (!vt8500_rtc)
+		return -ENOMEM;
+
+	spin_lock_init(&vt8500_rtc->lock);
+	platform_set_drvdata(pdev, vt8500_rtc);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No I/O memory resource defined\n");
+		ret = -ENXIO;
+		goto err_free;
+	}
+
+	vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
+	if (vt8500_rtc->irq_alarm < 0) {
+		dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
+		ret = -ENXIO;
+		goto err_free;
+	}
+
+	vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
+	if (vt8500_rtc->irq_hz < 0) {
+		dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n");
+		ret = -ENXIO;
+		goto err_free;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), "vt8500-rtc");
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	vt8500_rtc->regbase = ioremap(res->start, resource_size(res));
+	if (!vt8500_rtc->regbase) {
+		dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
+		ret = -ENOMEM;
+		goto err_release;
+	}
+
+	/* Enable the second/minute interrupt generation and enable RTC */
+	writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H
+		| VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC,
+	       vt8500_rtc->regbase + VT8500_RTC_CR);
+
+	vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
+					      &vt8500_rtc_ops, THIS_MODULE);
+	ret = PTR_ERR(vt8500_rtc->rtc);
+	if (IS_ERR(vt8500_rtc->rtc)) {
+		dev_err(&pdev->dev,
+			"Failed to register RTC device -> %d\n", ret);
+		ret = -ENXIO;
+		goto err_unmap;
+	}
+
+	ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0,
+			  "rtc 1Hz", vt8500_rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+			vt8500_rtc->irq_hz, ret);
+		goto err_unreg;
+	}
+
+	ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0,
+			  "rtc alarm", vt8500_rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+			vt8500_rtc->irq_alarm, ret);
+		goto err_free_hz;
+	}
+
+	return 0;
+
+err_free_hz:
+	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
+err_unreg:
+	rtc_device_unregister(vt8500_rtc->rtc);
+err_unmap:
+	iounmap(vt8500_rtc->regbase);
+err_release:
+	release_mem_region(res->start, resource_size(res));
+err_free:
+	kfree(vt8500_rtc);
+	return ret;
+}
+
+static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
+{
+	struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
+
+	rtc_device_unregister(vt8500_rtc->rtc);
+
+	spin_lock_irq(&vt8500_rtc->lock);
+	/* Disable alarm matching */
+	writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
+	iounmap(vt8500_rtc->regbase);
+	spin_unlock_irq(&vt8500_rtc->lock);
+
+	free_irq(vt8500_rtc->irq_alarm, vt8500_rtc);
+	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
+
+	kfree(vt8500_rtc);
+
+	return 0;
+}
+
+static struct platform_driver vt8500_rtc_driver = {
+	.probe		= vt8500_rtc_probe,
+	.remove		= __devexit_p(vt8500_rtc_remove),
+	.driver		= {
+		.name	= "vt8500-rtc",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init vt8500_rtc_init(void)
+{
+	return platform_driver_register(&vt8500_rtc_driver);
+}
+module_init(vt8500_rtc_init);
+
+static void __exit vt8500_rtc_exit(void)
+{
+	platform_driver_unregister(&vt8500_rtc_driver);
+}
+module_exit(vt8500_rtc_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:vt8500-rtc");
-- 
1.7.3.2


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

* Re: [PATCH 5/6 v3] rtc: Add support for the RTC in VIA VT8500 and compatibles
  2010-11-13 23:56     ` [PATCH 5/6 v3] " Alexey Charkov
@ 2010-11-14 15:50       ` Lars-Peter Clausen
  2010-11-14 17:00         ` [PATCH 5/6 v4] " Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Lars-Peter Clausen @ 2010-11-14 15:50 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Alessandro Zummo,
	linux-kernel, rtc-linux

Alexey Charkov wrote:
> This adds a driver for the RTC devices in VIA and WonderMedia
> Systems-on-Chip. Alarm, 1Hz interrupts, reading and setting time
> are supported.
> 
> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> ---
> 
> This implements the suggestions made by Lars. Reference to wakeup
> was removed, as currently other code does not yet provide any
> support for that.
> 
> It now also checks the status register for indication of a valid
> time value before trying to read date/time registers.
> 
> Thanks,
> Alexey


Hi

Looks good to me now, except for the drivers release function. And some minor
stylistic issues. Comments are inline.

- Lars

> 
>  drivers/rtc/Kconfig      |    7 +
>  drivers/rtc/Makefile     |    1 +
>  drivers/rtc/rtc-vt8500.c |  365 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 373 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-vt8500.c
> 
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 2883428..27ed129 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -865,6 +865,13 @@ config RTC_DRV_PXA
>           This RTC driver uses PXA RTC registers available since pxa27x
>           series (RDxR, RYxR) instead of legacy RCNR, RTAR.
>  
> +config RTC_DRV_VT8500
> +	tristate "VIA/WonderMedia 85xx SoC RTC"
> +	depends on ARCH_VT8500
> +	help
> +	  If you say Y here you will get access to the real time clock
> +	  built into your VIA VT8500 SoC or its relatives.
> +
>  
>  config RTC_DRV_SUN4V
>  	bool "SUN4V Hypervisor RTC"
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 4c2832d..1a354e1 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030)	+= rtc-twl.o
>  obj-$(CONFIG_RTC_DRV_TX4939)	+= rtc-tx4939.o
>  obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o
>  obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o
> +obj-$(CONFIG_RTC_DRV_VT8500)	+= rtc-vt8500.o
>  obj-$(CONFIG_RTC_DRV_WM831X)	+= rtc-wm831x.o
>  obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
>  obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
> diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
> new file mode 100644
> index 0000000..e4f2aa9
> --- /dev/null
> +++ b/drivers/rtc/rtc-vt8500.c
> @@ -0,0 +1,365 @@
> +/*
> + * drivers/rtc/rtc-vt8500.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * Based on rtc-pxa.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/bcd.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +/*
> + * Register definitions
> + */
> +#define VT8500_RTC_TS		0x00	/* Time set */
> +#define VT8500_RTC_DS		0x04	/* Date set */
> +#define VT8500_RTC_AS		0x08	/* Alarm set */
> +#define VT8500_RTC_CR		0x0c	/* Control */
> +#define VT8500_RTC_TR		0x10	/* Time read */
> +#define VT8500_RTC_DR		0x14	/* Date read */
> +#define VT8500_RTC_WS		0x18	/* Write status */
> +#define VT8500_RTC_CL		0x20	/* Calibration */
> +#define VT8500_RTC_IS		0x24	/* Interrupt status */
> +#define VT8500_RTC_ST		0x28	/* Status */
> +
> +#define INVALID_TIME_BIT	(1 << 31)
> +
> +#define DATE_CENTURY_S		19
> +#define DATE_YEAR_S		11
> +#define DATE_YEAR_MASK		(0xff << DATE_YEAR_S)
> +#define DATE_MONTH_S		6
> +#define DATE_MONTH_MASK		(0x1f << DATE_MONTH_S)
> +#define DATE_DAY_MASK		0x3f
> +
> +#define TIME_DOW_S		20
> +#define TIME_DOW_MASK		(0x07 << TIME_DOW_S)
> +#define TIME_HOUR_S		14
> +#define TIME_HOUR_MASK		(0x3f << TIME_HOUR_S)
> +#define TIME_MIN_S		7
> +#define TIME_MIN_MASK		(0x7f << TIME_MIN_S)
> +#define TIME_SEC_MASK		0x7f
> +
> +#define ALARM_DAY_S		20
> +#define ALARM_DAY_MASK		(0x3f << ALARM_DAY_S)
> +
> +#define ALARM_DAY_BIT		(1 << 29)
> +#define ALARM_HOUR_BIT		(1 << 28)
> +#define ALARM_MIN_BIT		(1 << 27)
> +#define ALARM_SEC_BIT		(1 << 26)
> +
> +#define ALARM_ENABLE_MASK	(ALARM_DAY_BIT \
> +				| ALARM_HOUR_BIT \
> +				| ALARM_MIN_BIT \
> +				| ALARM_SEC_BIT)
> +
> +#define VT8500_RTC_CR_ENABLE	(1 << 0)	/* Enable RTC */
> +#define VT8500_RTC_CR_24H	(1 << 1)	/* 24h time format */
> +#define VT8500_RTC_CR_SM_ENABLE	(1 << 2)	/* Enable periodic irqs */
> +#define VT8500_RTC_CR_SM_SEC	(1 << 3)	/* 0: 1Hz/60, 1: 1Hz */
> +#define VT8500_RTC_CR_CALIB	(1 << 4)	/* Enable calibration */
> +
> +struct vt8500_rtc {
> +	void __iomem		*regbase;
> +	int			irq_alarm;
> +	int			irq_hz;
> +	struct rtc_device	*rtc;
> +	spinlock_t		lock;		/* Protects this structure */
> +};
> +
> +static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_id;
> +	u32 isr;
> +	unsigned long events = 0;
> +
> +	spin_lock(&vt8500_rtc->lock);
> +
> +	/* clear interrupt sources */
> +	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
> +	writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
> +
> +	spin_unlock(&vt8500_rtc->lock);
> +
> +	if (isr & 1)
> +		events |= RTC_AF | RTC_IRQF;
> +
> +	/* Only second/minute interrupts are supported */
> +	if (isr & 2)
> +		events |= RTC_UF | RTC_IRQF;
> +
> +	rtc_update_irq(vt8500_rtc->rtc, 1, events);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +	u32 date, time;
> +
> +	if (!readl(vt8500_rtc->regbase + VT8500_RTC_ST)) {
> +		dev_info(dev, "Clock not set!");
> +		return -EINVAL;
> +	}
> +	date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
> +	time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
> +
> +	tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
> +	tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
> +	tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
> +	tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
> +	tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
> +	tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S);
> +	tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
> +
> +	return rtc_valid_tm(tm);
> +}
> +
> +static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +	writel((bin2bcd(tm->tm_year) << DATE_YEAR_S)
> +		| (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
> +		| (bin2bcd(tm->tm_mday)),
> +		vt8500_rtc->regbase + VT8500_RTC_DS);
> +	writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
> +		| (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
> +		| (bin2bcd(tm->tm_min) << TIME_MIN_S)
> +		| (bin2bcd(tm->tm_sec)),
> +		vt8500_rtc->regbase + VT8500_RTC_TS);
> +
> +	return 0;
> +}
> +
> +static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +	u32 isr, alarm;
> +
> +	alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
> +
> +	alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
> +	alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
> +	alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
> +	alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
> +
> +	alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
> +
> +	alrm->pending = (isr & 1) ? 1 : 0;
> +	return rtc_valid_tm(&alrm->time);
> +}
> +
> +static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +	writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
> +		| (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
> +		| (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
> +		| (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
> +		| (bin2bcd(alrm->time.tm_sec)),
> +		vt8500_rtc->regbase + VT8500_RTC_AS);
> +
> +	return 0;
> +}
> +
> +static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +	unsigned long tmp;
> +
> +	if (enabled) {
> +		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +		writel(tmp | ALARM_ENABLE_MASK,
> +		       vt8500_rtc->regbase + VT8500_RTC_AS);
> +	} else {
> +		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +		writel(tmp & ~ALARM_ENABLE_MASK,
> +		       vt8500_rtc->regbase + VT8500_RTC_AS);
> +	}
In my opinion it would be a good idea to move the read and write of the register
outside of if-else statement and just modify tmp in there. That should make the code
more readable.

> +	return 0;
> +}
> +
> +static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled)
> +{
> +	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +	unsigned long tmp;
> +
> +	if (enabled) {
> +		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
> +		writel(tmp | VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE,
> +		       vt8500_rtc->regbase + VT8500_RTC_CR);
> +	} else {
> +		tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
> +		writel(tmp & ~VT8500_RTC_CR_SM_ENABLE,
> +		       vt8500_rtc->regbase + VT8500_RTC_CR);
> +	}

Same here

> +	return 0;
> +}
> +
> +static const struct rtc_class_ops vt8500_rtc_ops = {
> +	.read_time = vt8500_rtc_read_time,
> +	.set_time = vt8500_rtc_set_time,
> +	.read_alarm = vt8500_rtc_read_alarm,
> +	.set_alarm = vt8500_rtc_set_alarm,
> +	.alarm_irq_enable = vt8500_alarm_irq_enable,
> +	.update_irq_enable = vt8500_update_irq_enable,
> +};
> +
> +static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
> +{
> +	struct vt8500_rtc *vt8500_rtc;
> +	struct resource *res;
> +	int ret;
> +
> +	vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
> +	if (!vt8500_rtc)
> +		return -ENOMEM;
> +
> +	spin_lock_init(&vt8500_rtc->lock);
> +	platform_set_drvdata(pdev, vt8500_rtc);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No I/O memory resource defined\n");
> +		ret = -ENXIO;
> +		goto err_free;
> +	}
> +
> +	vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
> +	if (vt8500_rtc->irq_alarm < 0) {
> +		dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
> +		ret = -ENXIO;
> +		goto err_free;
> +	}
> +
> +	vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
> +	if (vt8500_rtc->irq_hz < 0) {
> +		dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n");
> +		ret = -ENXIO;
> +		goto err_free;
> +	}
> +
> +	res = request_mem_region(res->start, resource_size(res), "vt8500-rtc");
> +	if (res == NULL) {
> +		dev_err(&pdev->dev, "failed to request I/O memory\n");
> +		ret = -EBUSY;
> +		goto err_free;
> +	}
> +
> +	vt8500_rtc->regbase = ioremap(res->start, resource_size(res));
> +	if (!vt8500_rtc->regbase) {
> +		dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
> +		ret = -ENOMEM;
I think this should be -EBUSY as well.

> +		goto err_release;
> +	}
> +
> +	/* Enable the second/minute interrupt generation and enable RTC */
> +	writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H
> +		| VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC,
> +	       vt8500_rtc->regbase + VT8500_RTC_CR);
> +
> +	vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
> +					      &vt8500_rtc_ops, THIS_MODULE);
> +	ret = PTR_ERR(vt8500_rtc->rtc);
Move the assignment inside the if statement...

> +	if (IS_ERR(vt8500_rtc->rtc)) {
> +		dev_err(&pdev->dev,
> +			"Failed to register RTC device -> %d\n", ret);
> +		ret = -ENXIO;
... and drop this one.

> +		goto err_unmap;
> +	}
> +
> +	ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0,
> +			  "rtc 1Hz", vt8500_rtc);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "can't get irq %i, err %d\n",
> +			vt8500_rtc->irq_hz, ret);
> +		goto err_unreg;
> +	}
> +
> +	ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0,
> +			  "rtc alarm", vt8500_rtc);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "can't get irq %i, err %d\n",
> +			vt8500_rtc->irq_alarm, ret);
> +		goto err_free_hz;
> +	}
> +
> +	return 0;
> +
> +err_free_hz:
> +	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
> +err_unreg:
> +	rtc_device_unregister(vt8500_rtc->rtc);
> +err_unmap:
> +	iounmap(vt8500_rtc->regbase);
> +err_release:
> +	release_mem_region(res->start, resource_size(res));
> +err_free:
> +	kfree(vt8500_rtc);
> +	return ret;
> +}
> +
> +static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
> +{
> +	struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
> +
> +	rtc_device_unregister(vt8500_rtc->rtc);
> +
> +	spin_lock_irq(&vt8500_rtc->lock);
> +	/* Disable alarm matching */
> +	writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
> +	iounmap(vt8500_rtc->regbase);
> +	spin_unlock_irq(&vt8500_rtc->lock);
I don't think you need to lock here.

And you need to release the mem region here, too.
> +
> +	free_irq(vt8500_rtc->irq_alarm, vt8500_rtc);
> +	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
You should free the irqs before you unregister the rtc device, otherwise it might be
possible that the irq handler tries to report an interrupt to the already
unregistered rtc device.
> +
> +	kfree(vt8500_rtc);

platform_set_drvdata(pdev, NULL);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver vt8500_rtc_driver = {
> +	.probe		= vt8500_rtc_probe,
> +	.remove		= __devexit_p(vt8500_rtc_remove),
> +	.driver		= {
> +		.name	= "vt8500-rtc",
> +		.owner	= THIS_MODULE,
> +	},
> +};
> +
> +static int __init vt8500_rtc_init(void)
> +{
> +	return platform_driver_register(&vt8500_rtc_driver);
> +}
> +module_init(vt8500_rtc_init);
> +
> +static void __exit vt8500_rtc_exit(void)
> +{
> +	platform_driver_unregister(&vt8500_rtc_driver);
> +}
> +module_exit(vt8500_rtc_exit);
> +
> +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
> +MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:vt8500-rtc");


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

* [PATCH 5/6 v4] rtc: Add support for the RTC in VIA VT8500 and compatibles
  2010-11-14 15:50       ` Lars-Peter Clausen
@ 2010-11-14 17:00         ` Alexey Charkov
  2010-11-23 19:17           ` Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-14 17:00 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Alessandro Zummo, linux-kernel, rtc-linux

This adds a driver for the RTC devices in VIA and WonderMedia
Systems-on-Chip. Alarm, 1Hz interrupts, reading and setting time
are supported.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Thanks for the comments, Lars! This version implements the latest
ones as well.

Best regards,
Alexey

 drivers/rtc/Kconfig      |    7 +
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-vt8500.c |  359 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 367 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-vt8500.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2883428..27ed129 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -865,6 +865,13 @@ config RTC_DRV_PXA
          This RTC driver uses PXA RTC registers available since pxa27x
          series (RDxR, RYxR) instead of legacy RCNR, RTAR.
 
+config RTC_DRV_VT8500
+	tristate "VIA/WonderMedia 85xx SoC RTC"
+	depends on ARCH_VT8500
+	help
+	  If you say Y here you will get access to the real time clock
+	  built into your VIA VT8500 SoC or its relatives.
+
 
 config RTC_DRV_SUN4V
 	bool "SUN4V Hypervisor RTC"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 4c2832d..1a354e1 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030)	+= rtc-twl.o
 obj-$(CONFIG_RTC_DRV_TX4939)	+= rtc-tx4939.o
 obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o
 obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o
+obj-$(CONFIG_RTC_DRV_VT8500)	+= rtc-vt8500.o
 obj-$(CONFIG_RTC_DRV_WM831X)	+= rtc-wm831x.o
 obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
new file mode 100644
index 0000000..cdf4f63
--- /dev/null
+++ b/drivers/rtc/rtc-vt8500.c
@@ -0,0 +1,359 @@
+/*
+ * drivers/rtc/rtc-vt8500.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on rtc-pxa.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/*
+ * Register definitions
+ */
+#define VT8500_RTC_TS		0x00	/* Time set */
+#define VT8500_RTC_DS		0x04	/* Date set */
+#define VT8500_RTC_AS		0x08	/* Alarm set */
+#define VT8500_RTC_CR		0x0c	/* Control */
+#define VT8500_RTC_TR		0x10	/* Time read */
+#define VT8500_RTC_DR		0x14	/* Date read */
+#define VT8500_RTC_WS		0x18	/* Write status */
+#define VT8500_RTC_CL		0x20	/* Calibration */
+#define VT8500_RTC_IS		0x24	/* Interrupt status */
+#define VT8500_RTC_ST		0x28	/* Status */
+
+#define INVALID_TIME_BIT	(1 << 31)
+
+#define DATE_CENTURY_S		19
+#define DATE_YEAR_S		11
+#define DATE_YEAR_MASK		(0xff << DATE_YEAR_S)
+#define DATE_MONTH_S		6
+#define DATE_MONTH_MASK		(0x1f << DATE_MONTH_S)
+#define DATE_DAY_MASK		0x3f
+
+#define TIME_DOW_S		20
+#define TIME_DOW_MASK		(0x07 << TIME_DOW_S)
+#define TIME_HOUR_S		14
+#define TIME_HOUR_MASK		(0x3f << TIME_HOUR_S)
+#define TIME_MIN_S		7
+#define TIME_MIN_MASK		(0x7f << TIME_MIN_S)
+#define TIME_SEC_MASK		0x7f
+
+#define ALARM_DAY_S		20
+#define ALARM_DAY_MASK		(0x3f << ALARM_DAY_S)
+
+#define ALARM_DAY_BIT		(1 << 29)
+#define ALARM_HOUR_BIT		(1 << 28)
+#define ALARM_MIN_BIT		(1 << 27)
+#define ALARM_SEC_BIT		(1 << 26)
+
+#define ALARM_ENABLE_MASK	(ALARM_DAY_BIT \
+				| ALARM_HOUR_BIT \
+				| ALARM_MIN_BIT \
+				| ALARM_SEC_BIT)
+
+#define VT8500_RTC_CR_ENABLE	(1 << 0)	/* Enable RTC */
+#define VT8500_RTC_CR_24H	(1 << 1)	/* 24h time format */
+#define VT8500_RTC_CR_SM_ENABLE	(1 << 2)	/* Enable periodic irqs */
+#define VT8500_RTC_CR_SM_SEC	(1 << 3)	/* 0: 1Hz/60, 1: 1Hz */
+#define VT8500_RTC_CR_CALIB	(1 << 4)	/* Enable calibration */
+
+struct vt8500_rtc {
+	void __iomem		*regbase;
+	int			irq_alarm;
+	int			irq_hz;
+	struct rtc_device	*rtc;
+	spinlock_t		lock;		/* Protects this structure */
+};
+
+static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_id;
+	u32 isr;
+	unsigned long events = 0;
+
+	spin_lock(&vt8500_rtc->lock);
+
+	/* clear interrupt sources */
+	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+	writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
+
+	spin_unlock(&vt8500_rtc->lock);
+
+	if (isr & 1)
+		events |= RTC_AF | RTC_IRQF;
+
+	/* Only second/minute interrupts are supported */
+	if (isr & 2)
+		events |= RTC_UF | RTC_IRQF;
+
+	rtc_update_irq(vt8500_rtc->rtc, 1, events);
+
+	return IRQ_HANDLED;
+}
+
+static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	u32 date, time;
+
+	if (!readl(vt8500_rtc->regbase + VT8500_RTC_ST)) {
+		dev_info(dev, "Clock not set!");
+		return -EINVAL;
+	}
+	date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
+	time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
+
+	tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
+	tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
+	tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
+	tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
+	tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
+	tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S);
+	tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
+
+	return rtc_valid_tm(tm);
+}
+
+static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+	writel((bin2bcd(tm->tm_year) << DATE_YEAR_S)
+		| (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
+		| (bin2bcd(tm->tm_mday)),
+		vt8500_rtc->regbase + VT8500_RTC_DS);
+	writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
+		| (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
+		| (bin2bcd(tm->tm_min) << TIME_MIN_S)
+		| (bin2bcd(tm->tm_sec)),
+		vt8500_rtc->regbase + VT8500_RTC_TS);
+
+	return 0;
+}
+
+static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	u32 isr, alarm;
+
+	alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+
+	alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
+	alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
+	alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
+	alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
+
+	alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
+
+	alrm->pending = (isr & 1) ? 1 : 0;
+	return rtc_valid_tm(&alrm->time);
+}
+
+static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+	writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
+		| (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
+		| (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
+		| (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
+		| (bin2bcd(alrm->time.tm_sec)),
+		vt8500_rtc->regbase + VT8500_RTC_AS);
+
+	return 0;
+}
+
+static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+
+	if (enabled)
+		tmp |= ALARM_ENABLE_MASK;
+	else
+		tmp &= ~ALARM_ENABLE_MASK;
+
+	writel(tmp, vt8500_rtc->regbase + VT8500_RTC_AS);
+	return 0;
+}
+
+static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
+
+	if (enabled)
+		tmp |= VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE;
+	else
+		tmp &= ~VT8500_RTC_CR_SM_ENABLE;
+
+	writel(tmp, vt8500_rtc->regbase + VT8500_RTC_CR);
+	return 0;
+}
+
+static const struct rtc_class_ops vt8500_rtc_ops = {
+	.read_time = vt8500_rtc_read_time,
+	.set_time = vt8500_rtc_set_time,
+	.read_alarm = vt8500_rtc_read_alarm,
+	.set_alarm = vt8500_rtc_set_alarm,
+	.alarm_irq_enable = vt8500_alarm_irq_enable,
+	.update_irq_enable = vt8500_update_irq_enable,
+};
+
+static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
+{
+	struct vt8500_rtc *vt8500_rtc;
+	struct resource *res;
+	int ret;
+
+	vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
+	if (!vt8500_rtc)
+		return -ENOMEM;
+
+	spin_lock_init(&vt8500_rtc->lock);
+	platform_set_drvdata(pdev, vt8500_rtc);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No I/O memory resource defined\n");
+		ret = -ENXIO;
+		goto err_free;
+	}
+
+	vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
+	if (vt8500_rtc->irq_alarm < 0) {
+		dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
+		ret = -ENXIO;
+		goto err_free;
+	}
+
+	vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
+	if (vt8500_rtc->irq_hz < 0) {
+		dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n");
+		ret = -ENXIO;
+		goto err_free;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), "vt8500-rtc");
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	vt8500_rtc->regbase = ioremap(res->start, resource_size(res));
+	if (!vt8500_rtc->regbase) {
+		dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
+		ret = -EBUSY;
+		goto err_release;
+	}
+
+	/* Enable the second/minute interrupt generation and enable RTC */
+	writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H
+		| VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC,
+	       vt8500_rtc->regbase + VT8500_RTC_CR);
+
+	vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
+					      &vt8500_rtc_ops, THIS_MODULE);
+	if (IS_ERR(vt8500_rtc->rtc)) {
+		ret = PTR_ERR(vt8500_rtc->rtc);
+		dev_err(&pdev->dev,
+			"Failed to register RTC device -> %d\n", ret);
+		goto err_unmap;
+	}
+
+	ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0,
+			  "rtc 1Hz", vt8500_rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+			vt8500_rtc->irq_hz, ret);
+		goto err_unreg;
+	}
+
+	ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0,
+			  "rtc alarm", vt8500_rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+			vt8500_rtc->irq_alarm, ret);
+		goto err_free_hz;
+	}
+
+	return 0;
+
+err_free_hz:
+	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
+err_unreg:
+	rtc_device_unregister(vt8500_rtc->rtc);
+err_unmap:
+	iounmap(vt8500_rtc->regbase);
+err_release:
+	release_mem_region(res->start, resource_size(res));
+err_free:
+	kfree(vt8500_rtc);
+	return ret;
+}
+
+static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
+{
+	struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	free_irq(vt8500_rtc->irq_alarm, vt8500_rtc);
+	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
+
+	rtc_device_unregister(vt8500_rtc->rtc);
+
+	/* Disable alarm matching */
+	writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
+	iounmap(vt8500_rtc->regbase);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(vt8500_rtc);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver vt8500_rtc_driver = {
+	.probe		= vt8500_rtc_probe,
+	.remove		= __devexit_p(vt8500_rtc_remove),
+	.driver		= {
+		.name	= "vt8500-rtc",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init vt8500_rtc_init(void)
+{
+	return platform_driver_register(&vt8500_rtc_driver);
+}
+module_init(vt8500_rtc_init);
+
+static void __exit vt8500_rtc_exit(void)
+{
+	platform_driver_unregister(&vt8500_rtc_driver);
+}
+module_exit(vt8500_rtc_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:vt8500-rtc");
-- 
1.7.3.2


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

* Re: [PATCH 5/6 v4] rtc: Add support for the RTC in VIA VT8500 and compatibles
  2010-11-14 17:00         ` [PATCH 5/6 v4] " Alexey Charkov
@ 2010-11-23 19:17           ` Alexey Charkov
  2010-11-24 19:23             ` Lars-Peter Clausen
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-11-23 19:17 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Alessandro Zummo, linux-kernel, rtc-linux

2010/11/14 Alexey Charkov <alchark@gmail.com>:
> This adds a driver for the RTC devices in VIA and WonderMedia
> Systems-on-Chip. Alarm, 1Hz interrupts, reading and setting time
> are supported.
>
> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> ---
>
> Thanks for the comments, Lars! This version implements the latest
> ones as well.
>
> Best regards,
> Alexey
>
>  drivers/rtc/Kconfig      |    7 +
>  drivers/rtc/Makefile     |    1 +
>  drivers/rtc/rtc-vt8500.c |  359 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 367 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-vt8500.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 2883428..27ed129 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -865,6 +865,13 @@ config RTC_DRV_PXA
>          This RTC driver uses PXA RTC registers available since pxa27x
>          series (RDxR, RYxR) instead of legacy RCNR, RTAR.
>
> +config RTC_DRV_VT8500
> +       tristate "VIA/WonderMedia 85xx SoC RTC"
> +       depends on ARCH_VT8500
> +       help
> +         If you say Y here you will get access to the real time clock
> +         built into your VIA VT8500 SoC or its relatives.
> +
>
>  config RTC_DRV_SUN4V
>        bool "SUN4V Hypervisor RTC"
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 4c2832d..1a354e1 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
>  obj-$(CONFIG_RTC_DRV_TX4939)   += rtc-tx4939.o
>  obj-$(CONFIG_RTC_DRV_V3020)    += rtc-v3020.o
>  obj-$(CONFIG_RTC_DRV_VR41XX)   += rtc-vr41xx.o
> +obj-$(CONFIG_RTC_DRV_VT8500)   += rtc-vt8500.o
>  obj-$(CONFIG_RTC_DRV_WM831X)   += rtc-wm831x.o
>  obj-$(CONFIG_RTC_DRV_WM8350)   += rtc-wm8350.o
>  obj-$(CONFIG_RTC_DRV_X1205)    += rtc-x1205.o
> diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
> new file mode 100644
> index 0000000..cdf4f63
> --- /dev/null
> +++ b/drivers/rtc/rtc-vt8500.c
> @@ -0,0 +1,359 @@
> +/*
> + * drivers/rtc/rtc-vt8500.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * Based on rtc-pxa.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/bcd.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +/*
> + * Register definitions
> + */
> +#define VT8500_RTC_TS          0x00    /* Time set */
> +#define VT8500_RTC_DS          0x04    /* Date set */
> +#define VT8500_RTC_AS          0x08    /* Alarm set */
> +#define VT8500_RTC_CR          0x0c    /* Control */
> +#define VT8500_RTC_TR          0x10    /* Time read */
> +#define VT8500_RTC_DR          0x14    /* Date read */
> +#define VT8500_RTC_WS          0x18    /* Write status */
> +#define VT8500_RTC_CL          0x20    /* Calibration */
> +#define VT8500_RTC_IS          0x24    /* Interrupt status */
> +#define VT8500_RTC_ST          0x28    /* Status */
> +
> +#define INVALID_TIME_BIT       (1 << 31)
> +
> +#define DATE_CENTURY_S         19
> +#define DATE_YEAR_S            11
> +#define DATE_YEAR_MASK         (0xff << DATE_YEAR_S)
> +#define DATE_MONTH_S           6
> +#define DATE_MONTH_MASK                (0x1f << DATE_MONTH_S)
> +#define DATE_DAY_MASK          0x3f
> +
> +#define TIME_DOW_S             20
> +#define TIME_DOW_MASK          (0x07 << TIME_DOW_S)
> +#define TIME_HOUR_S            14
> +#define TIME_HOUR_MASK         (0x3f << TIME_HOUR_S)
> +#define TIME_MIN_S             7
> +#define TIME_MIN_MASK          (0x7f << TIME_MIN_S)
> +#define TIME_SEC_MASK          0x7f
> +
> +#define ALARM_DAY_S            20
> +#define ALARM_DAY_MASK         (0x3f << ALARM_DAY_S)
> +
> +#define ALARM_DAY_BIT          (1 << 29)
> +#define ALARM_HOUR_BIT         (1 << 28)
> +#define ALARM_MIN_BIT          (1 << 27)
> +#define ALARM_SEC_BIT          (1 << 26)
> +
> +#define ALARM_ENABLE_MASK      (ALARM_DAY_BIT \
> +                               | ALARM_HOUR_BIT \
> +                               | ALARM_MIN_BIT \
> +                               | ALARM_SEC_BIT)
> +
> +#define VT8500_RTC_CR_ENABLE   (1 << 0)        /* Enable RTC */
> +#define VT8500_RTC_CR_24H      (1 << 1)        /* 24h time format */
> +#define VT8500_RTC_CR_SM_ENABLE        (1 << 2)        /* Enable periodic irqs */
> +#define VT8500_RTC_CR_SM_SEC   (1 << 3)        /* 0: 1Hz/60, 1: 1Hz */
> +#define VT8500_RTC_CR_CALIB    (1 << 4)        /* Enable calibration */
> +
> +struct vt8500_rtc {
> +       void __iomem            *regbase;
> +       int                     irq_alarm;
> +       int                     irq_hz;
> +       struct rtc_device       *rtc;
> +       spinlock_t              lock;           /* Protects this structure */
> +};
> +
> +static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_id;
> +       u32 isr;
> +       unsigned long events = 0;
> +
> +       spin_lock(&vt8500_rtc->lock);
> +
> +       /* clear interrupt sources */
> +       isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
> +       writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
> +
> +       spin_unlock(&vt8500_rtc->lock);
> +
> +       if (isr & 1)
> +               events |= RTC_AF | RTC_IRQF;
> +
> +       /* Only second/minute interrupts are supported */
> +       if (isr & 2)
> +               events |= RTC_UF | RTC_IRQF;
> +
> +       rtc_update_irq(vt8500_rtc->rtc, 1, events);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +       u32 date, time;
> +
> +       if (!readl(vt8500_rtc->regbase + VT8500_RTC_ST)) {
> +               dev_info(dev, "Clock not set!");
> +               return -EINVAL;
> +       }
> +       date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
> +       time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
> +
> +       tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
> +       tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
> +       tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
> +       tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
> +       tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
> +       tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S);
> +       tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
> +
> +       return rtc_valid_tm(tm);
> +}
> +
> +static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +       writel((bin2bcd(tm->tm_year) << DATE_YEAR_S)
> +               | (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
> +               | (bin2bcd(tm->tm_mday)),
> +               vt8500_rtc->regbase + VT8500_RTC_DS);
> +       writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
> +               | (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
> +               | (bin2bcd(tm->tm_min) << TIME_MIN_S)
> +               | (bin2bcd(tm->tm_sec)),
> +               vt8500_rtc->regbase + VT8500_RTC_TS);
> +
> +       return 0;
> +}
> +
> +static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +       u32 isr, alarm;
> +
> +       alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +       isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
> +
> +       alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
> +       alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
> +       alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
> +       alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
> +
> +       alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
> +
> +       alrm->pending = (isr & 1) ? 1 : 0;
> +       return rtc_valid_tm(&alrm->time);
> +}
> +
> +static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +
> +       writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
> +               | (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
> +               | (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
> +               | (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
> +               | (bin2bcd(alrm->time.tm_sec)),
> +               vt8500_rtc->regbase + VT8500_RTC_AS);
> +
> +       return 0;
> +}
> +
> +static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +       unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
> +
> +       if (enabled)
> +               tmp |= ALARM_ENABLE_MASK;
> +       else
> +               tmp &= ~ALARM_ENABLE_MASK;
> +
> +       writel(tmp, vt8500_rtc->regbase + VT8500_RTC_AS);
> +       return 0;
> +}
> +
> +static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled)
> +{
> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
> +       unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
> +
> +       if (enabled)
> +               tmp |= VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE;
> +       else
> +               tmp &= ~VT8500_RTC_CR_SM_ENABLE;
> +
> +       writel(tmp, vt8500_rtc->regbase + VT8500_RTC_CR);
> +       return 0;
> +}
> +
> +static const struct rtc_class_ops vt8500_rtc_ops = {
> +       .read_time = vt8500_rtc_read_time,
> +       .set_time = vt8500_rtc_set_time,
> +       .read_alarm = vt8500_rtc_read_alarm,
> +       .set_alarm = vt8500_rtc_set_alarm,
> +       .alarm_irq_enable = vt8500_alarm_irq_enable,
> +       .update_irq_enable = vt8500_update_irq_enable,
> +};
> +
> +static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
> +{
> +       struct vt8500_rtc *vt8500_rtc;
> +       struct resource *res;
> +       int ret;
> +
> +       vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
> +       if (!vt8500_rtc)
> +               return -ENOMEM;
> +
> +       spin_lock_init(&vt8500_rtc->lock);
> +       platform_set_drvdata(pdev, vt8500_rtc);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res) {
> +               dev_err(&pdev->dev, "No I/O memory resource defined\n");
> +               ret = -ENXIO;
> +               goto err_free;
> +       }
> +
> +       vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
> +       if (vt8500_rtc->irq_alarm < 0) {
> +               dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
> +               ret = -ENXIO;
> +               goto err_free;
> +       }
> +
> +       vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
> +       if (vt8500_rtc->irq_hz < 0) {
> +               dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n");
> +               ret = -ENXIO;
> +               goto err_free;
> +       }
> +
> +       res = request_mem_region(res->start, resource_size(res), "vt8500-rtc");
> +       if (res == NULL) {
> +               dev_err(&pdev->dev, "failed to request I/O memory\n");
> +               ret = -EBUSY;
> +               goto err_free;
> +       }
> +
> +       vt8500_rtc->regbase = ioremap(res->start, resource_size(res));
> +       if (!vt8500_rtc->regbase) {
> +               dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
> +               ret = -EBUSY;
> +               goto err_release;
> +       }
> +
> +       /* Enable the second/minute interrupt generation and enable RTC */
> +       writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H
> +               | VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC,
> +              vt8500_rtc->regbase + VT8500_RTC_CR);
> +
> +       vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
> +                                             &vt8500_rtc_ops, THIS_MODULE);
> +       if (IS_ERR(vt8500_rtc->rtc)) {
> +               ret = PTR_ERR(vt8500_rtc->rtc);
> +               dev_err(&pdev->dev,
> +                       "Failed to register RTC device -> %d\n", ret);
> +               goto err_unmap;
> +       }
> +
> +       ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0,
> +                         "rtc 1Hz", vt8500_rtc);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "can't get irq %i, err %d\n",
> +                       vt8500_rtc->irq_hz, ret);
> +               goto err_unreg;
> +       }
> +
> +       ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0,
> +                         "rtc alarm", vt8500_rtc);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "can't get irq %i, err %d\n",
> +                       vt8500_rtc->irq_alarm, ret);
> +               goto err_free_hz;
> +       }
> +
> +       return 0;
> +
> +err_free_hz:
> +       free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
> +err_unreg:
> +       rtc_device_unregister(vt8500_rtc->rtc);
> +err_unmap:
> +       iounmap(vt8500_rtc->regbase);
> +err_release:
> +       release_mem_region(res->start, resource_size(res));
> +err_free:
> +       kfree(vt8500_rtc);
> +       return ret;
> +}
> +
> +static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
> +{
> +       struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
> +       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +       free_irq(vt8500_rtc->irq_alarm, vt8500_rtc);
> +       free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
> +
> +       rtc_device_unregister(vt8500_rtc->rtc);
> +
> +       /* Disable alarm matching */
> +       writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
> +       iounmap(vt8500_rtc->regbase);
> +       release_mem_region(res->start, resource_size(res));
> +
> +       kfree(vt8500_rtc);
> +       platform_set_drvdata(pdev, NULL);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver vt8500_rtc_driver = {
> +       .probe          = vt8500_rtc_probe,
> +       .remove         = __devexit_p(vt8500_rtc_remove),
> +       .driver         = {
> +               .name   = "vt8500-rtc",
> +               .owner  = THIS_MODULE,
> +       },
> +};
> +
> +static int __init vt8500_rtc_init(void)
> +{
> +       return platform_driver_register(&vt8500_rtc_driver);
> +}
> +module_init(vt8500_rtc_init);
> +
> +static void __exit vt8500_rtc_exit(void)
> +{
> +       platform_driver_unregister(&vt8500_rtc_driver);
> +}
> +module_exit(vt8500_rtc_exit);
> +
> +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
> +MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:vt8500-rtc");
> --
> 1.7.3.2
>
>

Any further comments about this code? Could it be merged now?

Lars, sorry for double-posting this (forgot to CC the lists and others
last time).

Thanks,
Alexey

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

* [PATCH 1/6 v7] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-11 23:49           ` Russell King - ARM Linux
  2010-11-12 16:54             ` [PATCH 1/6 v6] " Alexey Charkov
@ 2010-11-23 19:50             ` Alexey Charkov
  2010-12-19 17:40             ` [PATCH 1/6 v8] " Alexey Charkov
  2 siblings, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-23 19:50 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505. Suitable code is
selected (if compiled in) at early initialization time by reading a
platform-specific identification register, as current bootloaders
do not provide any reliable machine id to the kernel.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

There were no further comments on v6 of this patch, so this just
introduces direct assignment of handler callbacks to irq_desc fields
to avoid spinlock problems. For the same reason, the ioremap call in
arch_reset() was removed and replaced with a direct write to the
relevant virtual memory address.

Please review and, if possible, merge this for inclusion in 2.6.38
together with the input-related code [1], which Dmitry Torokhov
acks and expects to be merged together with the arch code [2].

Serial, USB and fbdev-related parts have already been merged to their
respective trees, and RTC is awaiting maintainer's resolution after
having implemented the suggestions.

Thanks,
Alexey

[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2010-November/030782.html
[2] http://lists.infradead.org/pipermail/linux-arm-kernel/2010-November/031358.html

 arch/arm/Kconfig                                |   14 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 +++
 arch/arm/mach-vt8500/Kconfig                    |   63 ++++
 arch/arm/mach-vt8500/Makefile                   |    6 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   82 +++++
 arch/arm/mach-vt8500/devices.c                  |  442 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   46 +++
 arch/arm/mach-vt8500/gpio.c                     |  230 ++++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 ++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 ++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/io.h          |   28 ++
 arch/arm/mach-vt8500/include/mach/irq_defs.h    |  124 +++++++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 ++
 arch/arm/mach-vt8500/include/mach/mmio_regs.h   |   90 +++++
 arch/arm/mach-vt8500/include/mach/system.h      |   18 +
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 ++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 +
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 ++
 arch/arm/mach-vt8500/irq.c                      |  179 +++++++++
 arch/arm/mach-vt8500/irq_defs.c                 |  173 +++++++++
 arch/arm/mach-vt8500/mmio_regs.c                |  118 ++++++
 arch/arm/mach-vt8500/pwm.c                      |  254 +++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  154 ++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   81 ++++
 31 files changed, 2401 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irq_defs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/mmio_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/irq_defs.c
 create mode 100644 arch/arm/mach-vt8500/mmio_regs.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index db524e7..69261c5 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,18 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_CLK
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +985,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..a462869
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,63 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..aff4159
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,6 @@
+obj-y += devices.o gpio.o irq.o irq_defs.o mmio_regs.o timer.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..f041712
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,82 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= vt8500_map_io,
+	.reserve	= vt8500_reserve_mem,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..de5c5a6
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,442 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include <mach/vt8500fb.h>
+#include "devices.h"
+
+static struct resource resources_lcdc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources  = ARRAY_SIZE(resources_lcdc),
+	.resource       = resources_lcdc,
+};
+
+static struct resource resources_wm8505_fb[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(resources_wm8505_fb),
+	.resource       = resources_wm8505_fb,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 0,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 66,
+		.right_margin	= 2,
+		.upper_margin	= 19,
+		.lower_margin	= 2,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		int len = strlen(panels[i].mode.name);
+
+		if (memcmp(panels[i].mode.name, str, len) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+static struct resource resources_uart0[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart1[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart2[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart3[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart4[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart5[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_uart0),
+	.resource	= resources_uart0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(resources_uart1),
+	.resource	= resources_uart1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(resources_uart2),
+	.resource	= resources_uart2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(resources_uart3),
+	.resource	= resources_uart3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+	.num_resources	= ARRAY_SIZE(resources_uart4),
+	.resource	= resources_uart4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+	.num_resources	= ARRAY_SIZE(resources_uart5),
+	.resource	= resources_uart5,
+};
+
+static struct resource resources_ehci[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(resources_ehci),
+	.resource	= resources_ehci,
+};
+
+static struct resource resources_ge_rops[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_ge_rops),
+	.resource	= resources_ge_rops,
+};
+
+static struct resource resources_pwm[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+	.resource	= resources_pwm,
+	.num_resources	= ARRAY_SIZE(resources_pwm),
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+static struct resource resources_rtc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+	.resource	= resources_rtc,
+	.num_resources	= ARRAY_SIZE(resources_rtc),
+};
+
+static struct map_desc vt8500_io_desc[] __initdata = {
+	/* SoC MMIO registers, to be filled in later */
+	[0] = {
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init wmt_set_resources(void)
+{
+	resources_lcdc[0].start = wmt_current_regs->lcdc;
+	resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
+	resources_lcdc[1].start = wmt_current_irqs->lcdc;
+	resources_lcdc[1].end = wmt_current_irqs->lcdc;
+
+	resources_wm8505_fb[0].start = wmt_current_regs->govr;
+	resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
+
+	resources_uart0[0].start = wmt_current_regs->uart0;
+	resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
+	resources_uart0[1].start = wmt_current_irqs->uart0;
+	resources_uart0[1].end = wmt_current_irqs->uart0;
+	resources_uart1[0].start = wmt_current_regs->uart1;
+	resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
+	resources_uart1[1].start = wmt_current_irqs->uart1;
+	resources_uart1[1].end = wmt_current_irqs->uart1;
+	resources_uart2[0].start = wmt_current_regs->uart2;
+	resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
+	resources_uart2[1].start = wmt_current_irqs->uart2;
+	resources_uart2[1].end = wmt_current_irqs->uart2;
+	resources_uart3[0].start = wmt_current_regs->uart3;
+	resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
+	resources_uart3[1].start = wmt_current_irqs->uart3;
+	resources_uart3[1].end = wmt_current_irqs->uart3;
+	resources_uart4[0].start = wmt_current_regs->uart4;
+	resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
+	resources_uart4[1].start = wmt_current_irqs->uart4;
+	resources_uart4[1].end = wmt_current_irqs->uart4;
+	resources_uart5[0].start = wmt_current_regs->uart5;
+	resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
+	resources_uart5[1].start = wmt_current_irqs->uart5;
+	resources_uart5[1].end = wmt_current_irqs->uart5;
+
+	resources_ehci[0].start = wmt_current_regs->ehci;
+	resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
+	resources_ehci[1].start = wmt_current_irqs->ehci;
+	resources_ehci[1].end = wmt_current_irqs->ehci;
+
+	resources_ge_rops[0].start = wmt_current_regs->ge;
+	resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
+
+	resources_pwm[0].start = wmt_current_regs->pwm;
+	resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
+
+	resources_rtc[0].start = wmt_current_regs->rtc;
+	resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
+	resources_rtc[1].start = wmt_current_irqs->rtc;
+	resources_rtc[1].end = wmt_current_irqs->rtc;
+	resources_rtc[2].start = wmt_current_irqs->rtc_hz;
+	resources_rtc[2].end = wmt_current_irqs->rtc_hz;
+}
+
+void __init vt8500_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
+	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init wm8505_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
+	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..428809e
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,46 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init wmt_set_resources(void);
+void __init vt8500_gpio_init(void);
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..49daee6
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,230 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	else
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) &
+			~(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
+	VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
+	VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
+	VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
+	VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
+	VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
+
+	VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
+	VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
+	VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
+	VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
+
+	VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
+	VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
+	VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
+
+	VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
+
+	VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
+
+	VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	writel(readl(regbase + 0x3c) & ~(1 << offset), regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	writel(readl(regbase + 0x3c) | (1 << offset), regbase + 0x3c);
+
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	else
+		writel(readl(regbase + 0x5c) & ~(1 << offset),
+			regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	gpio_to_irq_map[0] = wmt_current_irqs->ext0;
+	gpio_to_irq_map[1] = wmt_current_irqs->ext1;
+	gpio_to_irq_map[2] = wmt_current_irqs->ext2;
+	gpio_to_irq_map[3] = wmt_current_irqs->ext3;
+	gpio_to_irq_map[4] = wmt_current_irqs->ext4;
+	gpio_to_irq_map[5] = wmt_current_irqs->ext5;
+	gpio_to_irq_map[6] = wmt_current_irqs->ext6;
+	gpio_to_irq_map[7] = wmt_current_irqs->ext7;
+
+	regbase = ioremap(wmt_current_regs->gpio, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..8dd55c8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		((void __iomem *)((a) + 0xf0000000))
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irq_defs.h b/arch/arm/mach-vt8500/include/mach/irq_defs.h
new file mode 100644
index 0000000..fa8f4b3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irq_defs.h
@@ -0,0 +1,124 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irq_defs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef VT8500_IRQ_DEFS_H
+#define VT8500_IRQ_DEFS_H
+
+#include <linux/types.h>
+
+struct wmt_irq_srcs {
+	u8 nr_irqs;
+	u8 jpegenc;
+	u8 jpegdec;
+	u8 pata;
+	u8 dma;
+	u8 ext0;
+	u8 ext1;
+	u8 ext2;
+	u8 ext3;
+	u8 ext4;
+	u8 ext5;
+	u8 ext6;
+	u8 ext7;
+	u8 ether;
+	u8 mpegts;
+	u8 ge;
+	u8 gov;
+	u8 lcdc;
+	u8 lcdf;
+	u8 vpp;
+	u8 vpu;
+	u8 vid;
+	u8 spu;
+	u8 pip;
+	u8 dvo;
+	u8 govw;
+	u8 govrsdscd;
+	u8 govrsdmif;
+	u8 govrhdscd;
+	u8 govrhdmif;
+	u8 cipher;
+	u8 i2c0;
+	u8 i2c1;
+	u8 sdmmc;
+	u8 sdmmc_dma;
+	u8 pmc_wu;
+	u8 spi0;
+	u8 spi1;
+	u8 spi2;
+	u8 nand;
+	u8 nand_dma;
+	u8 nor;
+	u8 memstick;
+	u8 memstick_dma;
+	u8 uart0;
+	u8 uart1;
+	u8 uart2;
+	u8 uart3;
+	u8 uart4;
+	u8 uart5;
+	u8 i2s;
+	u8 pcm;
+	u8 ac97;
+	u8 timer_match0;
+	u8 timer_match1;
+	u8 timer_match2;
+	u8 timer_match3;
+	u8 ehci;
+	u8 uhci;
+	u8 udc;
+	u8 udc_dma;
+	u8 keypad;
+	u8 ps2mouse;
+	u8 ps2kbd;
+	u8 rtc;
+	u8 rtc_hz;
+	u8 adc;
+	u8 cir;
+	u8 dma0;
+	u8 dma1;
+	u8 dma2;
+	u8 dma3;
+	u8 dma4;
+	u8 dma5;
+	u8 dma6;
+	u8 dma7;
+	u8 dma8;
+	u8 dma9;
+	u8 dma10;
+	u8 dma11;
+	u8 dma12;
+	u8 dma13;
+	u8 dma14;
+	u8 dma15;
+	u8 irq0;
+	u8 irq1;
+	u8 irq2;
+	u8 irq3;
+	u8 irq4;
+	u8 irq5;
+	u8 irq6;
+	u8 irq7;
+	u8 sae;
+};
+
+extern struct wmt_irq_srcs wmt_irqs[] __initdata;
+extern struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/mmio_regs.h b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
new file mode 100644
index 0000000..76439dd
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
@@ -0,0 +1,90 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/mmio_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_MMIO_REGS_H
+#define __ASM_ARM_ARCH_MMIO_REGS_H
+
+#include <linux/init.h>
+
+struct wmt_mmio_regs {
+	unsigned long mmio_regs_start;
+	unsigned long mmio_regs_length;
+	unsigned long mmio_regs_virt;
+	unsigned long ddr;
+	unsigned long dma;
+	unsigned long vdma;
+	unsigned long sflash;
+	unsigned long ether;
+	unsigned long cipher;
+	unsigned long ehci;
+	unsigned long uhci;
+	unsigned long pata;
+	unsigned long ps2;
+	unsigned long nand;
+	unsigned long nor;
+	unsigned long sdmmc;
+	unsigned long memstick;
+	unsigned long lcdc;
+	unsigned long vpu;
+	unsigned long gov;
+	unsigned long ge;
+	unsigned long govr;
+	unsigned long scl;
+	unsigned long lcdf;
+	unsigned long vid;
+	unsigned long vpp;
+	unsigned long tsbk;
+	unsigned long jpegdec;
+	unsigned long jpegenc;
+	unsigned long rtc;
+	unsigned long gpio;
+	unsigned long scc;
+	unsigned long pmc;
+	unsigned long ic0;
+	unsigned long ic1;
+	unsigned long uart0;
+	unsigned long uart1;
+	unsigned long uart2;
+	unsigned long uart3;
+	unsigned long uart4;
+	unsigned long uart5;
+	unsigned long pwm;
+	unsigned long spi0;
+	unsigned long spi1;
+	unsigned long spi2;
+	unsigned long cir;
+	unsigned long i2c0;
+	unsigned long i2c1;
+	unsigned long ac97;
+	unsigned long pcm;
+	unsigned long i2s;
+	unsigned long adc;
+	unsigned long keypad;
+};
+
+enum {
+	VT8500_INDEX,
+	WM8505_INDEX,
+};
+
+extern struct wmt_mmio_regs wmt_regmaps[] __initdata;
+extern struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+#endif
+
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..d6c757e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,18 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR_VIRT	0xf8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	writel(1, VT8500_PMSR_VIRT);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..75a6912
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END		(PAGE_OFFSET + 0x10000000)
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..cc7f25e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	__u32			xres_virtual;
+	__u32			yres_virtual;
+	__u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..c042e85
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,179 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);
+	if (edge)
+		writel(readl(base
+			+ VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
+			| (1 << (irq & 0x1f)), base
+			+ VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));
+	else
+		writeb(readb(base
+			+ VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
+			base + VT8500_IC_DCTR + irq);
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	writeb(readb(base
+		+ VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
+		base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
+{
+	return -EINVAL;
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_HIGH, base
+			+ VT8500_IC_DCTR + irq);
+		irq_desc[orig_irq].handle_irq = handle_level_irq;
+		break;
+	case IRQF_TRIGGER_FALLING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_FALLING, base
+			+ VT8500_IC_DCTR + irq);
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	case IRQF_TRIGGER_RISING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_RISING, base
+			+ VT8500_IC_DCTR + irq);
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	}
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_wake  = vt8500_irq_set_wake,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+	sic_regbase = ioremap(wmt_current_regs->ic1, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR
+								+ i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/irq_defs.c b/arch/arm/mach-vt8500/irq_defs.c
new file mode 100644
index 0000000..b338c04
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq_defs.c
@@ -0,0 +1,173 @@
+/* linux/arch/arm/mach-vt8500/irq_defs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/irq_defs.h>
+#include <mach/mmio_regs.h>
+
+struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+struct wmt_irq_srcs wmt_irqs[] __initdata = {
+	[VT8500_INDEX] = {
+		.jpegenc	= 0,
+		.jpegdec	= 1,
+		.pata		= 3,
+		.dma		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.ge		= 8,
+		.gov		= 9,
+		.ether		= 10,
+		.mpegts		= 11,
+		.lcdc		= 12,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.cipher		= 16,
+		.vpp		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.lcdf		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.memstick	= 30,
+		.memstick_dma	= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.i2s		= 34,
+		.pcm		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.vpu		= 40,
+		.vid		= 41,
+		.ac97		= 42,
+		.ehci		= 43,
+		.nor		= 44,
+		.ps2mouse	= 45,
+		.ps2kbd		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.adc		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.dma0		= 56,
+		.dma1		= 57,
+		.dma2		= 58,
+		.dma3		= 59,
+		.dma4		= 60,
+		.dma5		= 61,
+		.dma6		= 62,
+		.dma7		= 63,
+		.nr_irqs	= 64,
+	},
+	[WM8505_INDEX] = {
+		.uhci		= 0,
+		.ehci		= 1,
+		.udc_dma	= 2,
+		.ps2mouse	= 4,
+		.udc		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.keypad		= 8,
+		.dma		= 9,
+		.ether		= 10,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.dma0		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.ps2kbd		= 23,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.dma1		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.uart5		= 30,
+		.uart4		= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.dma2		= 34,
+		.i2s		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.dma3		= 40,
+		.dma4		= 41,
+		.ac97		= 42,
+		.nor		= 44,
+		.dma5		= 45,
+		.dma6		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.dma7		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.irq0		= 56,
+		.irq1		= 57,
+		.irq2		= 58,
+		.irq3		= 59,
+		.irq4		= 60,
+		.irq5		= 61,
+		.irq6		= 62,
+		.irq7		= 63,
+		.jpegdec	= 65,
+		.sae		= 66,
+		.vpu		= 79,
+		.vpp		= 80,
+		.vid		= 81,
+		.spu		= 82,
+		.pip		= 83,
+		.ge		= 84,
+		.gov		= 85,
+		.dvo		= 86,
+		.dma8		= 92,
+		.dma9		= 93,
+		.dma10		= 94,
+		.dma11		= 95,
+		.dma12		= 96,
+		.dma13		= 97,
+		.dma14		= 98,
+		.dma15		= 99,
+		.govw		= 111,
+		.govrsdscd	= 112,
+		.govrsdmif	= 113,
+		.govrhdscd	= 114,
+		.govrhdmif	= 115,
+		.nr_irqs	= 116,
+	},
+};
diff --git a/arch/arm/mach-vt8500/mmio_regs.c b/arch/arm/mach-vt8500/mmio_regs.c
new file mode 100644
index 0000000..e9b3264
--- /dev/null
+++ b/arch/arm/mach-vt8500/mmio_regs.c
@@ -0,0 +1,118 @@
+/* linux/arch/arm/mach-vt8500/mmio_regs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/mmio_regs.h>
+
+struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+struct wmt_mmio_regs wmt_regmaps[] __initdata = {
+	[VT8500_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00350000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000000,
+		.dma			= 0xd8001000,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007900,
+		.uhci			= 0xd8007b01,
+		.pata			= 0xd8008000,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.memstick		= 0xd800b400,
+		.lcdc			= 0xd800e400,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.lcdf			= 0xd8050900,
+		.vid			= 0xd8050a00,
+		.vpp			= 0xd8050b00,
+		.tsbk			= 0xd80f4000,
+		.jpegdec		= 0xd80fe000,
+		.jpegenc		= 0xd80ff000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.pcm			= 0xd82d0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.adc			= 0xd8340000,
+	},
+	[WM8505_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00390000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000400,
+		.dma			= 0xd8001800,
+		.vdma			= 0xd8001c00,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007100,
+		.uhci			= 0xd8007301,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.govr			= 0xd8050800,
+		.vid			= 0xd8050a00,
+		.scl			= 0xd8050d00,
+		.vpp			= 0xd8050f00,
+		.jpegdec		= 0xd80fe000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.ic1			= 0xd8150000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.keypad			= 0xd8260000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.uart4			= 0xd8370000,
+		.uart5			= 0xd8380000,
+	},
+};
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..d1356a1
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,254 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+	int loops = 1000;
+	while ((readb(reg) & bitmask) && --loops)
+		cpu_relax();
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	dc = pv * duty_ns / period_ns;
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else
+			pwm = ERR_PTR(-EBUSY);
+	} else
+		pwm = ERR_PTR(-ENOENT);
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else
+		pr_warning("PWM device already freed\n");
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[0].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..ab4f7aa
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,154 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+static void __iomem *regbase;
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	int loops = 1000;
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	int loops = 1000;
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_current_regs->pmc + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO "
+				"registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register "
+			"failed for %s\n", clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_current_irqs->timer_match0, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq "
+			"failed for %s\n", clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..279b4bf
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,81 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/mmio_regs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.map_io		= wm8505_map_io,
+	.reserve	= wm8505_reserve_mem,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.2


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

* Re: [PATCH 5/6 v4] rtc: Add support for the RTC in VIA VT8500 and compatibles
  2010-11-23 19:17           ` Alexey Charkov
@ 2010-11-24 19:23             ` Lars-Peter Clausen
  2010-11-24 22:47               ` [PATCH 5/6 v5] " Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Lars-Peter Clausen @ 2010-11-24 19:23 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Alessandro Zummo,
	linux-kernel, rtc-linux

Alexey Charkov wrote:
> 2010/11/14 Alexey Charkov <alchark@gmail.com>:
>> This adds a driver for the RTC devices in VIA and WonderMedia
>> Systems-on-Chip. Alarm, 1Hz interrupts, reading and setting time
>> are supported.
>>
>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>> ---
>>
>> Thanks for the comments, Lars! This version implements the latest
>> ones as well.
>>
>> Best regards,
>> Alexey
>>
>>  drivers/rtc/Kconfig      |    7 +
>>  drivers/rtc/Makefile     |    1 +
>>  drivers/rtc/rtc-vt8500.c |  359 ++++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 367 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/rtc/rtc-vt8500.c
>>
>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>> index 2883428..27ed129 100644
>> --- a/drivers/rtc/Kconfig
>> +++ b/drivers/rtc/Kconfig
>> @@ -865,6 +865,13 @@ config RTC_DRV_PXA
>>          This RTC driver uses PXA RTC registers available since pxa27x
>>          series (RDxR, RYxR) instead of legacy RCNR, RTAR.
>>
>> +config RTC_DRV_VT8500
>> +       tristate "VIA/WonderMedia 85xx SoC RTC"
>> +       depends on ARCH_VT8500
>> +       help
>> +         If you say Y here you will get access to the real time clock
>> +         built into your VIA VT8500 SoC or its relatives.
>> +
>>
>>  config RTC_DRV_SUN4V
>>        bool "SUN4V Hypervisor RTC"
>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>> index 4c2832d..1a354e1 100644
>> --- a/drivers/rtc/Makefile
>> +++ b/drivers/rtc/Makefile
>> @@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
>>  obj-$(CONFIG_RTC_DRV_TX4939)   += rtc-tx4939.o
>>  obj-$(CONFIG_RTC_DRV_V3020)    += rtc-v3020.o
>>  obj-$(CONFIG_RTC_DRV_VR41XX)   += rtc-vr41xx.o
>> +obj-$(CONFIG_RTC_DRV_VT8500)   += rtc-vt8500.o
>>  obj-$(CONFIG_RTC_DRV_WM831X)   += rtc-wm831x.o
>>  obj-$(CONFIG_RTC_DRV_WM8350)   += rtc-wm8350.o
>>  obj-$(CONFIG_RTC_DRV_X1205)    += rtc-x1205.o
>> diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
>> new file mode 100644
>> index 0000000..cdf4f63
>> --- /dev/null
>> +++ b/drivers/rtc/rtc-vt8500.c
>> @@ -0,0 +1,359 @@
>> +/*
>> + * drivers/rtc/rtc-vt8500.c
>> + *
>> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
>> + *
>> + * Based on rtc-pxa.c
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/rtc.h>
>> +#include <linux/init.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/bcd.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +/*
>> + * Register definitions
>> + */
>> +#define VT8500_RTC_TS          0x00    /* Time set */
>> +#define VT8500_RTC_DS          0x04    /* Date set */
>> +#define VT8500_RTC_AS          0x08    /* Alarm set */
>> +#define VT8500_RTC_CR          0x0c    /* Control */
>> +#define VT8500_RTC_TR          0x10    /* Time read */
>> +#define VT8500_RTC_DR          0x14    /* Date read */
>> +#define VT8500_RTC_WS          0x18    /* Write status */
>> +#define VT8500_RTC_CL          0x20    /* Calibration */
>> +#define VT8500_RTC_IS          0x24    /* Interrupt status */
>> +#define VT8500_RTC_ST          0x28    /* Status */
>> +
>> +#define INVALID_TIME_BIT       (1 << 31)
>> +
>> +#define DATE_CENTURY_S         19
>> +#define DATE_YEAR_S            11
>> +#define DATE_YEAR_MASK         (0xff << DATE_YEAR_S)
>> +#define DATE_MONTH_S           6
>> +#define DATE_MONTH_MASK                (0x1f << DATE_MONTH_S)
>> +#define DATE_DAY_MASK          0x3f
>> +
>> +#define TIME_DOW_S             20
>> +#define TIME_DOW_MASK          (0x07 << TIME_DOW_S)
>> +#define TIME_HOUR_S            14
>> +#define TIME_HOUR_MASK         (0x3f << TIME_HOUR_S)
>> +#define TIME_MIN_S             7
>> +#define TIME_MIN_MASK          (0x7f << TIME_MIN_S)
>> +#define TIME_SEC_MASK          0x7f
>> +
>> +#define ALARM_DAY_S            20
>> +#define ALARM_DAY_MASK         (0x3f << ALARM_DAY_S)
>> +
>> +#define ALARM_DAY_BIT          (1 << 29)
>> +#define ALARM_HOUR_BIT         (1 << 28)
>> +#define ALARM_MIN_BIT          (1 << 27)
>> +#define ALARM_SEC_BIT          (1 << 26)
>> +
>> +#define ALARM_ENABLE_MASK      (ALARM_DAY_BIT \
>> +                               | ALARM_HOUR_BIT \
>> +                               | ALARM_MIN_BIT \
>> +                               | ALARM_SEC_BIT)
>> +
>> +#define VT8500_RTC_CR_ENABLE   (1 << 0)        /* Enable RTC */
>> +#define VT8500_RTC_CR_24H      (1 << 1)        /* 24h time format */
>> +#define VT8500_RTC_CR_SM_ENABLE        (1 << 2)        /* Enable periodic irqs */
>> +#define VT8500_RTC_CR_SM_SEC   (1 << 3)        /* 0: 1Hz/60, 1: 1Hz */
>> +#define VT8500_RTC_CR_CALIB    (1 << 4)        /* Enable calibration */
>> +
>> +struct vt8500_rtc {
>> +       void __iomem            *regbase;
>> +       int                     irq_alarm;
>> +       int                     irq_hz;
>> +       struct rtc_device       *rtc;
>> +       spinlock_t              lock;           /* Protects this structure */
>> +};
>> +
>> +static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
>> +{
>> +       struct vt8500_rtc *vt8500_rtc = dev_id;
>> +       u32 isr;
>> +       unsigned long events = 0;
>> +
>> +       spin_lock(&vt8500_rtc->lock);
>> +
>> +       /* clear interrupt sources */
>> +       isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
>> +       writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
>> +
>> +       spin_unlock(&vt8500_rtc->lock);
>> +
>> +       if (isr & 1)
>> +               events |= RTC_AF | RTC_IRQF;
>> +
>> +       /* Only second/minute interrupts are supported */
>> +       if (isr & 2)
>> +               events |= RTC_UF | RTC_IRQF;
>> +
>> +       rtc_update_irq(vt8500_rtc->rtc, 1, events);
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
>> +{
>> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
>> +       u32 date, time;
>> +
>> +       if (!readl(vt8500_rtc->regbase + VT8500_RTC_ST)) {
>> +               dev_info(dev, "Clock not set!");
>> +               return -EINVAL;
>> +       }
>> +       date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
>> +       time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
>> +
>> +       tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
>> +       tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
>> +       tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
>> +       tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
>> +       tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
>> +       tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S);
>> +       tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
>> +
>> +       return rtc_valid_tm(tm);
>> +}
>> +
>> +static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
>> +{
>> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
>> +
>> +       writel((bin2bcd(tm->tm_year) << DATE_YEAR_S)
>> +               | (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
>> +               | (bin2bcd(tm->tm_mday)),
>> +               vt8500_rtc->regbase + VT8500_RTC_DS);
>> +       writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
>> +               | (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
>> +               | (bin2bcd(tm->tm_min) << TIME_MIN_S)
>> +               | (bin2bcd(tm->tm_sec)),
>> +               vt8500_rtc->regbase + VT8500_RTC_TS);
>> +
>> +       return 0;
>> +}
>> +
>> +static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>> +{
>> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
>> +       u32 isr, alarm;
>> +
>> +       alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
>> +       isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
>> +
>> +       alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
>> +       alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
>> +       alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
>> +       alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
>> +
>> +       alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
>> +
>> +       alrm->pending = (isr & 1) ? 1 : 0;
>> +       return rtc_valid_tm(&alrm->time);
>> +}
>> +
>> +static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>> +{
>> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
>> +
>> +       writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
>> +               | (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
>> +               | (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
>> +               | (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
>> +               | (bin2bcd(alrm->time.tm_sec)),
>> +               vt8500_rtc->regbase + VT8500_RTC_AS);
>> +
>> +       return 0;
>> +}
>> +
>> +static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)
>> +{
>> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
>> +       unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
>> +
>> +       if (enabled)
>> +               tmp |= ALARM_ENABLE_MASK;
>> +       else
>> +               tmp &= ~ALARM_ENABLE_MASK;
>> +
>> +       writel(tmp, vt8500_rtc->regbase + VT8500_RTC_AS);
>> +       return 0;
>> +}
>> +
>> +static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled)
>> +{
>> +       struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
>> +       unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
>> +
>> +       if (enabled)
>> +               tmp |= VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE;
>> +       else
>> +               tmp &= ~VT8500_RTC_CR_SM_ENABLE;
>> +
>> +       writel(tmp, vt8500_rtc->regbase + VT8500_RTC_CR);
>> +       return 0;
>> +}
>> +
>> +static const struct rtc_class_ops vt8500_rtc_ops = {
>> +       .read_time = vt8500_rtc_read_time,
>> +       .set_time = vt8500_rtc_set_time,
>> +       .read_alarm = vt8500_rtc_read_alarm,
>> +       .set_alarm = vt8500_rtc_set_alarm,
>> +       .alarm_irq_enable = vt8500_alarm_irq_enable,
>> +       .update_irq_enable = vt8500_update_irq_enable,
>> +};
>> +
>> +static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
>> +{
>> +       struct vt8500_rtc *vt8500_rtc;
>> +       struct resource *res;
>> +       int ret;
>> +
>> +       vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
>> +       if (!vt8500_rtc)
>> +               return -ENOMEM;
>> +
>> +       spin_lock_init(&vt8500_rtc->lock);
>> +       platform_set_drvdata(pdev, vt8500_rtc);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       if (!res) {
>> +               dev_err(&pdev->dev, "No I/O memory resource defined\n");
>> +               ret = -ENXIO;
>> +               goto err_free;
>> +       }
>> +
>> +       vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
>> +       if (vt8500_rtc->irq_alarm < 0) {
>> +               dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
>> +               ret = -ENXIO;
>> +               goto err_free;
>> +       }
>> +
>> +       vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
>> +       if (vt8500_rtc->irq_hz < 0) {
>> +               dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n");
>> +               ret = -ENXIO;
>> +               goto err_free;
>> +       }
>> +
>> +       res = request_mem_region(res->start, resource_size(res), "vt8500-rtc");
>> +       if (res == NULL) {
>> +               dev_err(&pdev->dev, "failed to request I/O memory\n");
>> +               ret = -EBUSY;
>> +               goto err_free;
>> +       }
>> +
>> +       vt8500_rtc->regbase = ioremap(res->start, resource_size(res));
>> +       if (!vt8500_rtc->regbase) {
>> +               dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
>> +               ret = -EBUSY;
>> +               goto err_release;
>> +       }
>> +
>> +       /* Enable the second/minute interrupt generation and enable RTC */
>> +       writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H
>> +               | VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC,
>> +              vt8500_rtc->regbase + VT8500_RTC_CR);
>> +
>> +       vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
>> +                                             &vt8500_rtc_ops, THIS_MODULE);
>> +       if (IS_ERR(vt8500_rtc->rtc)) {
>> +               ret = PTR_ERR(vt8500_rtc->rtc);
>> +               dev_err(&pdev->dev,
>> +                       "Failed to register RTC device -> %d\n", ret);
>> +               goto err_unmap;
>> +       }
>> +
>> +       ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0,
>> +                         "rtc 1Hz", vt8500_rtc);
>> +       if (ret < 0) {
>> +               dev_err(&pdev->dev, "can't get irq %i, err %d\n",
>> +                       vt8500_rtc->irq_hz, ret);
>> +               goto err_unreg;
>> +       }
>> +
>> +       ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0,
>> +                         "rtc alarm", vt8500_rtc);
>> +       if (ret < 0) {
>> +               dev_err(&pdev->dev, "can't get irq %i, err %d\n",
>> +                       vt8500_rtc->irq_alarm, ret);
>> +               goto err_free_hz;
>> +       }
>> +
>> +       return 0;
>> +
>> +err_free_hz:
>> +       free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
>> +err_unreg:
>> +       rtc_device_unregister(vt8500_rtc->rtc);
>> +err_unmap:
>> +       iounmap(vt8500_rtc->regbase);
>> +err_release:
>> +       release_mem_region(res->start, resource_size(res));
>> +err_free:
>> +       kfree(vt8500_rtc);
>> +       return ret;
>> +}
>> +
>> +static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
>> +{
>> +       struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
>> +       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +
>> +       free_irq(vt8500_rtc->irq_alarm, vt8500_rtc);
>> +       free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
>> +
>> +       rtc_device_unregister(vt8500_rtc->rtc);
>> +
>> +       /* Disable alarm matching */
>> +       writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
>> +       iounmap(vt8500_rtc->regbase);
>> +       release_mem_region(res->start, resource_size(res));
>> +
>> +       kfree(vt8500_rtc);
>> +       platform_set_drvdata(pdev, NULL);
>> +
>> +       return 0;
>> +}
>> +
>> +static struct platform_driver vt8500_rtc_driver = {
>> +       .probe          = vt8500_rtc_probe,
>> +       .remove         = __devexit_p(vt8500_rtc_remove),
>> +       .driver         = {
>> +               .name   = "vt8500-rtc",
>> +               .owner  = THIS_MODULE,
>> +       },
>> +};
>> +
>> +static int __init vt8500_rtc_init(void)
>> +{
>> +       return platform_driver_register(&vt8500_rtc_driver);
>> +}
>> +module_init(vt8500_rtc_init);
>> +
>> +static void __exit vt8500_rtc_exit(void)
>> +{
>> +       platform_driver_unregister(&vt8500_rtc_driver);
>> +}
>> +module_exit(vt8500_rtc_exit);
>> +
>> +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
>> +MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:vt8500-rtc");
>> --
>> 1.7.3.2
>>
>>
> 
> Any further comments about this code? Could it be merged now?
> 
> Lars, sorry for double-posting this (forgot to CC the lists and others
> last time).

Looks good to me, except one small thing: request_mem_region allocates the resource
struct it returns. You should pass the same struct to release_mem_region to free it
again.
When that is fixed feel free to add a
Reviewed-By: Lars-Peter Clausen <lars@metafoo.de>

> 
> Thanks,
> Alexey


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

* [PATCH 5/6 v5] rtc: Add support for the RTC in VIA VT8500 and compatibles
  2010-11-24 19:23             ` Lars-Peter Clausen
@ 2010-11-24 22:47               ` Alexey Charkov
  0 siblings, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-11-24 22:47 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Alessandro Zummo, linux-kernel, rtc-linux

This adds a driver for the RTC devices in VIA and WonderMedia
Systems-on-Chip. Alarm, 1Hz interrupts, reading and setting time
are supported.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This fixes the issue with releasing the resource mentioned by Lars.

In addition, I've fixed the years range to reflect what the hardware seems
to actually expect (2000-2199 instead of 1900-... as implied by prior code).
In the process, it turned out that using rtc_valid_tm() when reading time
precludes any obvious recovery path if for some reason the hardware contains
garbage at kernel initialization: `hwclock -w` tries to read time before
updating, and fails. However, bad time values are handled gracefully by the
userspace, so I just put in 'return 0' there. In-kernel hctosys also checks
the returned time, so this seems to be valid behaviour.

Reading alarm values still returns through rtc_valid_tm().

Thanks,
Alexey

 drivers/rtc/Kconfig      |    7 +
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-vt8500.c |  366 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 374 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-vt8500.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2883428..27ed129 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -865,6 +865,13 @@ config RTC_DRV_PXA
          This RTC driver uses PXA RTC registers available since pxa27x
          series (RDxR, RYxR) instead of legacy RCNR, RTAR.
 
+config RTC_DRV_VT8500
+	tristate "VIA/WonderMedia 85xx SoC RTC"
+	depends on ARCH_VT8500
+	help
+	  If you say Y here you will get access to the real time clock
+	  built into your VIA VT8500 SoC or its relatives.
+
 
 config RTC_DRV_SUN4V
 	bool "SUN4V Hypervisor RTC"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 4c2832d..1a354e1 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030)	+= rtc-twl.o
 obj-$(CONFIG_RTC_DRV_TX4939)	+= rtc-tx4939.o
 obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o
 obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o
+obj-$(CONFIG_RTC_DRV_VT8500)	+= rtc-vt8500.o
 obj-$(CONFIG_RTC_DRV_WM831X)	+= rtc-wm831x.o
 obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
new file mode 100644
index 0000000..b8bc862
--- /dev/null
+++ b/drivers/rtc/rtc-vt8500.c
@@ -0,0 +1,366 @@
+/*
+ * drivers/rtc/rtc-vt8500.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on rtc-pxa.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/*
+ * Register definitions
+ */
+#define VT8500_RTC_TS		0x00	/* Time set */
+#define VT8500_RTC_DS		0x04	/* Date set */
+#define VT8500_RTC_AS		0x08	/* Alarm set */
+#define VT8500_RTC_CR		0x0c	/* Control */
+#define VT8500_RTC_TR		0x10	/* Time read */
+#define VT8500_RTC_DR		0x14	/* Date read */
+#define VT8500_RTC_WS		0x18	/* Write status */
+#define VT8500_RTC_CL		0x20	/* Calibration */
+#define VT8500_RTC_IS		0x24	/* Interrupt status */
+#define VT8500_RTC_ST		0x28	/* Status */
+
+#define INVALID_TIME_BIT	(1 << 31)
+
+#define DATE_CENTURY_S		19
+#define DATE_YEAR_S		11
+#define DATE_YEAR_MASK		(0xff << DATE_YEAR_S)
+#define DATE_MONTH_S		6
+#define DATE_MONTH_MASK		(0x1f << DATE_MONTH_S)
+#define DATE_DAY_MASK		0x3f
+
+#define TIME_DOW_S		20
+#define TIME_DOW_MASK		(0x07 << TIME_DOW_S)
+#define TIME_HOUR_S		14
+#define TIME_HOUR_MASK		(0x3f << TIME_HOUR_S)
+#define TIME_MIN_S		7
+#define TIME_MIN_MASK		(0x7f << TIME_MIN_S)
+#define TIME_SEC_MASK		0x7f
+
+#define ALARM_DAY_S		20
+#define ALARM_DAY_MASK		(0x3f << ALARM_DAY_S)
+
+#define ALARM_DAY_BIT		(1 << 29)
+#define ALARM_HOUR_BIT		(1 << 28)
+#define ALARM_MIN_BIT		(1 << 27)
+#define ALARM_SEC_BIT		(1 << 26)
+
+#define ALARM_ENABLE_MASK	(ALARM_DAY_BIT \
+				| ALARM_HOUR_BIT \
+				| ALARM_MIN_BIT \
+				| ALARM_SEC_BIT)
+
+#define VT8500_RTC_CR_ENABLE	(1 << 0)	/* Enable RTC */
+#define VT8500_RTC_CR_24H	(1 << 1)	/* 24h time format */
+#define VT8500_RTC_CR_SM_ENABLE	(1 << 2)	/* Enable periodic irqs */
+#define VT8500_RTC_CR_SM_SEC	(1 << 3)	/* 0: 1Hz/60, 1: 1Hz */
+#define VT8500_RTC_CR_CALIB	(1 << 4)	/* Enable calibration */
+
+struct vt8500_rtc {
+	void __iomem		*regbase;
+	struct resource		*res;
+	int			irq_alarm;
+	int			irq_hz;
+	struct rtc_device	*rtc;
+	spinlock_t		lock;		/* Protects this structure */
+};
+
+static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_id;
+	u32 isr;
+	unsigned long events = 0;
+
+	spin_lock(&vt8500_rtc->lock);
+
+	/* clear interrupt sources */
+	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+	writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
+
+	spin_unlock(&vt8500_rtc->lock);
+
+	if (isr & 1)
+		events |= RTC_AF | RTC_IRQF;
+
+	/* Only second/minute interrupts are supported */
+	if (isr & 2)
+		events |= RTC_UF | RTC_IRQF;
+
+	rtc_update_irq(vt8500_rtc->rtc, 1, events);
+
+	return IRQ_HANDLED;
+}
+
+static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	u32 date, time;
+
+	date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
+	time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
+
+	tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
+	tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
+	tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
+	tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
+	tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
+	tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S)
+			+ ((date >> DATE_CENTURY_S) & 1 ? 200 : 100);
+	tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
+
+	return 0;
+}
+
+static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+	if (tm->tm_year < 100) {
+		dev_warn(dev, "Only years 2000-2199 are supported by the "
+			      "hardware!\n");
+		return -EINVAL;
+	}
+
+	writel((bin2bcd(tm->tm_year - 100) << DATE_YEAR_S)
+		| (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
+		| (bin2bcd(tm->tm_mday)),
+		vt8500_rtc->regbase + VT8500_RTC_DS);
+	writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
+		| (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
+		| (bin2bcd(tm->tm_min) << TIME_MIN_S)
+		| (bin2bcd(tm->tm_sec)),
+		vt8500_rtc->regbase + VT8500_RTC_TS);
+
+	return 0;
+}
+
+static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	u32 isr, alarm;
+
+	alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+	isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+
+	alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
+	alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
+	alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
+	alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
+
+	alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
+
+	alrm->pending = (isr & 1) ? 1 : 0;
+	return rtc_valid_tm(&alrm->time);
+}
+
+static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+	writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
+		| (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
+		| (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
+		| (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
+		| (bin2bcd(alrm->time.tm_sec)),
+		vt8500_rtc->regbase + VT8500_RTC_AS);
+
+	return 0;
+}
+
+static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+
+	if (enabled)
+		tmp |= ALARM_ENABLE_MASK;
+	else
+		tmp &= ~ALARM_ENABLE_MASK;
+
+	writel(tmp, vt8500_rtc->regbase + VT8500_RTC_AS);
+	return 0;
+}
+
+static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+	unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
+
+	if (enabled)
+		tmp |= VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE;
+	else
+		tmp &= ~VT8500_RTC_CR_SM_ENABLE;
+
+	writel(tmp, vt8500_rtc->regbase + VT8500_RTC_CR);
+	return 0;
+}
+
+static const struct rtc_class_ops vt8500_rtc_ops = {
+	.read_time = vt8500_rtc_read_time,
+	.set_time = vt8500_rtc_set_time,
+	.read_alarm = vt8500_rtc_read_alarm,
+	.set_alarm = vt8500_rtc_set_alarm,
+	.alarm_irq_enable = vt8500_alarm_irq_enable,
+	.update_irq_enable = vt8500_update_irq_enable,
+};
+
+static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
+{
+	struct vt8500_rtc *vt8500_rtc;
+	int ret;
+
+	vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
+	if (!vt8500_rtc)
+		return -ENOMEM;
+
+	spin_lock_init(&vt8500_rtc->lock);
+	platform_set_drvdata(pdev, vt8500_rtc);
+
+	vt8500_rtc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!vt8500_rtc->res) {
+		dev_err(&pdev->dev, "No I/O memory resource defined\n");
+		ret = -ENXIO;
+		goto err_free;
+	}
+
+	vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
+	if (vt8500_rtc->irq_alarm < 0) {
+		dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
+		ret = -ENXIO;
+		goto err_free;
+	}
+
+	vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
+	if (vt8500_rtc->irq_hz < 0) {
+		dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n");
+		ret = -ENXIO;
+		goto err_free;
+	}
+
+	vt8500_rtc->res = request_mem_region(vt8500_rtc->res->start,
+					     resource_size(vt8500_rtc->res),
+					     "vt8500-rtc");
+	if (vt8500_rtc->res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	vt8500_rtc->regbase = ioremap(vt8500_rtc->res->start,
+				      resource_size(vt8500_rtc->res));
+	if (!vt8500_rtc->regbase) {
+		dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
+		ret = -EBUSY;
+		goto err_release;
+	}
+
+	/* Enable the second/minute interrupt generation and enable RTC */
+	writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H
+		| VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC,
+	       vt8500_rtc->regbase + VT8500_RTC_CR);
+
+	vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
+					      &vt8500_rtc_ops, THIS_MODULE);
+	if (IS_ERR(vt8500_rtc->rtc)) {
+		ret = PTR_ERR(vt8500_rtc->rtc);
+		dev_err(&pdev->dev,
+			"Failed to register RTC device -> %d\n", ret);
+		goto err_unmap;
+	}
+
+	ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0,
+			  "rtc 1Hz", vt8500_rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+			vt8500_rtc->irq_hz, ret);
+		goto err_unreg;
+	}
+
+	ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0,
+			  "rtc alarm", vt8500_rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+			vt8500_rtc->irq_alarm, ret);
+		goto err_free_hz;
+	}
+
+	return 0;
+
+err_free_hz:
+	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
+err_unreg:
+	rtc_device_unregister(vt8500_rtc->rtc);
+err_unmap:
+	iounmap(vt8500_rtc->regbase);
+err_release:
+	release_mem_region(vt8500_rtc->res->start,
+			   resource_size(vt8500_rtc->res));
+err_free:
+	kfree(vt8500_rtc);
+	return ret;
+}
+
+static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
+{
+	struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
+
+	free_irq(vt8500_rtc->irq_alarm, vt8500_rtc);
+	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
+
+	rtc_device_unregister(vt8500_rtc->rtc);
+
+	/* Disable alarm matching */
+	writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
+	iounmap(vt8500_rtc->regbase);
+	release_mem_region(vt8500_rtc->res->start,
+			   resource_size(vt8500_rtc->res));
+
+	kfree(vt8500_rtc);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver vt8500_rtc_driver = {
+	.probe		= vt8500_rtc_probe,
+	.remove		= __devexit_p(vt8500_rtc_remove),
+	.driver		= {
+		.name	= "vt8500-rtc",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init vt8500_rtc_init(void)
+{
+	return platform_driver_register(&vt8500_rtc_driver);
+}
+module_init(vt8500_rtc_init);
+
+static void __exit vt8500_rtc_exit(void)
+{
+	platform_driver_unregister(&vt8500_rtc_driver);
+}
+module_exit(vt8500_rtc_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:vt8500-rtc");
-- 
1.7.3.2


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

* [PATCH 1/6 v8] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-11-11 23:49           ` Russell King - ARM Linux
  2010-11-12 16:54             ` [PATCH 1/6 v6] " Alexey Charkov
  2010-11-23 19:50             ` [PATCH 1/6 v7] " Alexey Charkov
@ 2010-12-19 17:40             ` Alexey Charkov
  2010-12-20 18:15               ` Arnd Bergmann
                                 ` (2 more replies)
  2 siblings, 3 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-12-19 17:40 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505. Suitable code is
selected (if compiled in) at early initialization time by reading a
platform-specific identification register, as current bootloaders
do not provide any reliable machine id to the kernel.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Compared to the previous submission, this version contains a
definition for 1024x576 panels (courtesy of John Mitchell) and slight
adjustments for other panel timings. Also, the reservation callback
is now assigned above the map_io one, and the Kconfig reference to CLK
is dropped for now (as there is no code for that yet, and the framework
is changing, as far as I could see).

Russell, are there any outstanding issues with this code that I should
fix before you could take it to your tree? The merge window is approaching,
so I'm getting a bit anxious :-)

Best regards,
Alexey

 arch/arm/Kconfig                                |   13 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 +++
 arch/arm/mach-vt8500/Kconfig                    |   73 ++++
 arch/arm/mach-vt8500/Makefile                   |    6 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   82 ++++
 arch/arm/mach-vt8500/devices.c                  |  460 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   46 +++
 arch/arm/mach-vt8500/gpio.c                     |  230 +++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 ++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 ++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/io.h          |   28 ++
 arch/arm/mach-vt8500/include/mach/irq_defs.h    |  124 ++++++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 ++
 arch/arm/mach-vt8500/include/mach/mmio_regs.h   |   90 +++++
 arch/arm/mach-vt8500/include/mach/system.h      |   18 +
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 ++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 +
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 ++
 arch/arm/mach-vt8500/irq.c                      |  179 +++++++++
 arch/arm/mach-vt8500/irq_defs.c                 |  173 +++++++++
 arch/arm/mach-vt8500/mmio_regs.c                |  118 ++++++
 arch/arm/mach-vt8500/pwm.c                      |  254 +++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  154 ++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   81 ++++
 31 files changed, 2428 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irq_defs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/mmio_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/irq_defs.c
 create mode 100644 arch/arm/mach-vt8500/mmio_regs.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 5738f2a..a2f33f0 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -863,6 +863,17 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -993,6 +1004,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 0a8f748..78fe31a 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..2c20a34
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,73 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X576
+	bool "10-inch with 1024x576 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in CherryPal netbooks and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x576' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..aff4159
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,6 @@
+obj-y += devices.o gpio.o irq.o irq_defs.o mmio_regs.o timer.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..d2de5f9
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,82 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= vt8500_reserve_mem,
+	.map_io		= vt8500_map_io,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..1ce577b
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,460 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include <mach/vt8500fb.h>
+#include "devices.h"
+
+static struct resource resources_lcdc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources  = ARRAY_SIZE(resources_lcdc),
+	.resource       = resources_lcdc,
+};
+
+static struct resource resources_wm8505_fb[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(resources_wm8505_fb),
+	.resource       = resources_wm8505_fb,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X576
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 576 * 2,
+	.mode		= {
+		.name		= "1024x576",
+		.xres		= 1024,
+		.yres		= 576,
+		.left_margin	= 40,
+		.right_margin	= 24,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 96,
+		.vsync_len	= 2,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 66,
+		.right_margin	= 2,
+		.upper_margin	= 19,
+		.lower_margin	= 1,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		int len = strlen(panels[i].mode.name);
+
+		if (memcmp(panels[i].mode.name, str, len) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+static struct resource resources_uart0[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart1[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart2[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart3[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart4[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart5[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_uart0),
+	.resource	= resources_uart0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(resources_uart1),
+	.resource	= resources_uart1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(resources_uart2),
+	.resource	= resources_uart2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(resources_uart3),
+	.resource	= resources_uart3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+	.num_resources	= ARRAY_SIZE(resources_uart4),
+	.resource	= resources_uart4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+	.num_resources	= ARRAY_SIZE(resources_uart5),
+	.resource	= resources_uart5,
+};
+
+static struct resource resources_ehci[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(resources_ehci),
+	.resource	= resources_ehci,
+};
+
+static struct resource resources_ge_rops[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_ge_rops),
+	.resource	= resources_ge_rops,
+};
+
+static struct resource resources_pwm[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+	.resource	= resources_pwm,
+	.num_resources	= ARRAY_SIZE(resources_pwm),
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+static struct resource resources_rtc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+	.resource	= resources_rtc,
+	.num_resources	= ARRAY_SIZE(resources_rtc),
+};
+
+static struct map_desc vt8500_io_desc[] __initdata = {
+	/* SoC MMIO registers, to be filled in later */
+	[0] = {
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init wmt_set_resources(void)
+{
+	resources_lcdc[0].start = wmt_current_regs->lcdc;
+	resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
+	resources_lcdc[1].start = wmt_current_irqs->lcdc;
+	resources_lcdc[1].end = wmt_current_irqs->lcdc;
+
+	resources_wm8505_fb[0].start = wmt_current_regs->govr;
+	resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
+
+	resources_uart0[0].start = wmt_current_regs->uart0;
+	resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
+	resources_uart0[1].start = wmt_current_irqs->uart0;
+	resources_uart0[1].end = wmt_current_irqs->uart0;
+	resources_uart1[0].start = wmt_current_regs->uart1;
+	resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
+	resources_uart1[1].start = wmt_current_irqs->uart1;
+	resources_uart1[1].end = wmt_current_irqs->uart1;
+	resources_uart2[0].start = wmt_current_regs->uart2;
+	resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
+	resources_uart2[1].start = wmt_current_irqs->uart2;
+	resources_uart2[1].end = wmt_current_irqs->uart2;
+	resources_uart3[0].start = wmt_current_regs->uart3;
+	resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
+	resources_uart3[1].start = wmt_current_irqs->uart3;
+	resources_uart3[1].end = wmt_current_irqs->uart3;
+	resources_uart4[0].start = wmt_current_regs->uart4;
+	resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
+	resources_uart4[1].start = wmt_current_irqs->uart4;
+	resources_uart4[1].end = wmt_current_irqs->uart4;
+	resources_uart5[0].start = wmt_current_regs->uart5;
+	resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
+	resources_uart5[1].start = wmt_current_irqs->uart5;
+	resources_uart5[1].end = wmt_current_irqs->uart5;
+
+	resources_ehci[0].start = wmt_current_regs->ehci;
+	resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
+	resources_ehci[1].start = wmt_current_irqs->ehci;
+	resources_ehci[1].end = wmt_current_irqs->ehci;
+
+	resources_ge_rops[0].start = wmt_current_regs->ge;
+	resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
+
+	resources_pwm[0].start = wmt_current_regs->pwm;
+	resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
+
+	resources_rtc[0].start = wmt_current_regs->rtc;
+	resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
+	resources_rtc[1].start = wmt_current_irqs->rtc;
+	resources_rtc[1].end = wmt_current_irqs->rtc;
+	resources_rtc[2].start = wmt_current_irqs->rtc_hz;
+	resources_rtc[2].end = wmt_current_irqs->rtc_hz;
+}
+
+void __init vt8500_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
+	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init wm8505_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
+	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..428809e
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,46 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init wmt_set_resources(void);
+void __init vt8500_gpio_init(void);
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..49daee6
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,230 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	else
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) &
+			~(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
+	VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
+	VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
+	VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
+	VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
+	VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
+
+	VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
+	VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
+	VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
+	VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
+
+	VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
+	VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
+	VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
+
+	VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
+
+	VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
+
+	VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	writel(readl(regbase + 0x3c) & ~(1 << offset), regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	writel(readl(regbase + 0x3c) | (1 << offset), regbase + 0x3c);
+
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	else
+		writel(readl(regbase + 0x5c) & ~(1 << offset),
+			regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	gpio_to_irq_map[0] = wmt_current_irqs->ext0;
+	gpio_to_irq_map[1] = wmt_current_irqs->ext1;
+	gpio_to_irq_map[2] = wmt_current_irqs->ext2;
+	gpio_to_irq_map[3] = wmt_current_irqs->ext3;
+	gpio_to_irq_map[4] = wmt_current_irqs->ext4;
+	gpio_to_irq_map[5] = wmt_current_irqs->ext5;
+	gpio_to_irq_map[6] = wmt_current_irqs->ext6;
+	gpio_to_irq_map[7] = wmt_current_irqs->ext7;
+
+	regbase = ioremap(wmt_current_regs->gpio, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..8dd55c8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		((void __iomem *)((a) + 0xf0000000))
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irq_defs.h b/arch/arm/mach-vt8500/include/mach/irq_defs.h
new file mode 100644
index 0000000..fa8f4b3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irq_defs.h
@@ -0,0 +1,124 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irq_defs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef VT8500_IRQ_DEFS_H
+#define VT8500_IRQ_DEFS_H
+
+#include <linux/types.h>
+
+struct wmt_irq_srcs {
+	u8 nr_irqs;
+	u8 jpegenc;
+	u8 jpegdec;
+	u8 pata;
+	u8 dma;
+	u8 ext0;
+	u8 ext1;
+	u8 ext2;
+	u8 ext3;
+	u8 ext4;
+	u8 ext5;
+	u8 ext6;
+	u8 ext7;
+	u8 ether;
+	u8 mpegts;
+	u8 ge;
+	u8 gov;
+	u8 lcdc;
+	u8 lcdf;
+	u8 vpp;
+	u8 vpu;
+	u8 vid;
+	u8 spu;
+	u8 pip;
+	u8 dvo;
+	u8 govw;
+	u8 govrsdscd;
+	u8 govrsdmif;
+	u8 govrhdscd;
+	u8 govrhdmif;
+	u8 cipher;
+	u8 i2c0;
+	u8 i2c1;
+	u8 sdmmc;
+	u8 sdmmc_dma;
+	u8 pmc_wu;
+	u8 spi0;
+	u8 spi1;
+	u8 spi2;
+	u8 nand;
+	u8 nand_dma;
+	u8 nor;
+	u8 memstick;
+	u8 memstick_dma;
+	u8 uart0;
+	u8 uart1;
+	u8 uart2;
+	u8 uart3;
+	u8 uart4;
+	u8 uart5;
+	u8 i2s;
+	u8 pcm;
+	u8 ac97;
+	u8 timer_match0;
+	u8 timer_match1;
+	u8 timer_match2;
+	u8 timer_match3;
+	u8 ehci;
+	u8 uhci;
+	u8 udc;
+	u8 udc_dma;
+	u8 keypad;
+	u8 ps2mouse;
+	u8 ps2kbd;
+	u8 rtc;
+	u8 rtc_hz;
+	u8 adc;
+	u8 cir;
+	u8 dma0;
+	u8 dma1;
+	u8 dma2;
+	u8 dma3;
+	u8 dma4;
+	u8 dma5;
+	u8 dma6;
+	u8 dma7;
+	u8 dma8;
+	u8 dma9;
+	u8 dma10;
+	u8 dma11;
+	u8 dma12;
+	u8 dma13;
+	u8 dma14;
+	u8 dma15;
+	u8 irq0;
+	u8 irq1;
+	u8 irq2;
+	u8 irq3;
+	u8 irq4;
+	u8 irq5;
+	u8 irq6;
+	u8 irq7;
+	u8 sae;
+};
+
+extern struct wmt_irq_srcs wmt_irqs[] __initdata;
+extern struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/mmio_regs.h b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
new file mode 100644
index 0000000..76439dd
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
@@ -0,0 +1,90 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/mmio_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_MMIO_REGS_H
+#define __ASM_ARM_ARCH_MMIO_REGS_H
+
+#include <linux/init.h>
+
+struct wmt_mmio_regs {
+	unsigned long mmio_regs_start;
+	unsigned long mmio_regs_length;
+	unsigned long mmio_regs_virt;
+	unsigned long ddr;
+	unsigned long dma;
+	unsigned long vdma;
+	unsigned long sflash;
+	unsigned long ether;
+	unsigned long cipher;
+	unsigned long ehci;
+	unsigned long uhci;
+	unsigned long pata;
+	unsigned long ps2;
+	unsigned long nand;
+	unsigned long nor;
+	unsigned long sdmmc;
+	unsigned long memstick;
+	unsigned long lcdc;
+	unsigned long vpu;
+	unsigned long gov;
+	unsigned long ge;
+	unsigned long govr;
+	unsigned long scl;
+	unsigned long lcdf;
+	unsigned long vid;
+	unsigned long vpp;
+	unsigned long tsbk;
+	unsigned long jpegdec;
+	unsigned long jpegenc;
+	unsigned long rtc;
+	unsigned long gpio;
+	unsigned long scc;
+	unsigned long pmc;
+	unsigned long ic0;
+	unsigned long ic1;
+	unsigned long uart0;
+	unsigned long uart1;
+	unsigned long uart2;
+	unsigned long uart3;
+	unsigned long uart4;
+	unsigned long uart5;
+	unsigned long pwm;
+	unsigned long spi0;
+	unsigned long spi1;
+	unsigned long spi2;
+	unsigned long cir;
+	unsigned long i2c0;
+	unsigned long i2c1;
+	unsigned long ac97;
+	unsigned long pcm;
+	unsigned long i2s;
+	unsigned long adc;
+	unsigned long keypad;
+};
+
+enum {
+	VT8500_INDEX,
+	WM8505_INDEX,
+};
+
+extern struct wmt_mmio_regs wmt_regmaps[] __initdata;
+extern struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+#endif
+
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..d6c757e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,18 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR_VIRT	0xf8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	writel(1, VT8500_PMSR_VIRT);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..4642290
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END	0xd0000000UL
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..cc7f25e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	__u32			xres_virtual;
+	__u32			yres_virtual;
+	__u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..c042e85
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,179 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);
+	if (edge)
+		writel(readl(base
+			+ VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
+			| (1 << (irq & 0x1f)), base
+			+ VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));
+	else
+		writeb(readb(base
+			+ VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
+			base + VT8500_IC_DCTR + irq);
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	writeb(readb(base
+		+ VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
+		base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
+{
+	return -EINVAL;
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_HIGH, base
+			+ VT8500_IC_DCTR + irq);
+		irq_desc[orig_irq].handle_irq = handle_level_irq;
+		break;
+	case IRQF_TRIGGER_FALLING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_FALLING, base
+			+ VT8500_IC_DCTR + irq);
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	case IRQF_TRIGGER_RISING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_RISING, base
+			+ VT8500_IC_DCTR + irq);
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	}
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_wake  = vt8500_irq_set_wake,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+	sic_regbase = ioremap(wmt_current_regs->ic1, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR
+								+ i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/irq_defs.c b/arch/arm/mach-vt8500/irq_defs.c
new file mode 100644
index 0000000..b338c04
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq_defs.c
@@ -0,0 +1,173 @@
+/* linux/arch/arm/mach-vt8500/irq_defs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/irq_defs.h>
+#include <mach/mmio_regs.h>
+
+struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+struct wmt_irq_srcs wmt_irqs[] __initdata = {
+	[VT8500_INDEX] = {
+		.jpegenc	= 0,
+		.jpegdec	= 1,
+		.pata		= 3,
+		.dma		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.ge		= 8,
+		.gov		= 9,
+		.ether		= 10,
+		.mpegts		= 11,
+		.lcdc		= 12,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.cipher		= 16,
+		.vpp		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.lcdf		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.memstick	= 30,
+		.memstick_dma	= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.i2s		= 34,
+		.pcm		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.vpu		= 40,
+		.vid		= 41,
+		.ac97		= 42,
+		.ehci		= 43,
+		.nor		= 44,
+		.ps2mouse	= 45,
+		.ps2kbd		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.adc		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.dma0		= 56,
+		.dma1		= 57,
+		.dma2		= 58,
+		.dma3		= 59,
+		.dma4		= 60,
+		.dma5		= 61,
+		.dma6		= 62,
+		.dma7		= 63,
+		.nr_irqs	= 64,
+	},
+	[WM8505_INDEX] = {
+		.uhci		= 0,
+		.ehci		= 1,
+		.udc_dma	= 2,
+		.ps2mouse	= 4,
+		.udc		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.keypad		= 8,
+		.dma		= 9,
+		.ether		= 10,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.dma0		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.ps2kbd		= 23,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.dma1		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.uart5		= 30,
+		.uart4		= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.dma2		= 34,
+		.i2s		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.dma3		= 40,
+		.dma4		= 41,
+		.ac97		= 42,
+		.nor		= 44,
+		.dma5		= 45,
+		.dma6		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.dma7		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.irq0		= 56,
+		.irq1		= 57,
+		.irq2		= 58,
+		.irq3		= 59,
+		.irq4		= 60,
+		.irq5		= 61,
+		.irq6		= 62,
+		.irq7		= 63,
+		.jpegdec	= 65,
+		.sae		= 66,
+		.vpu		= 79,
+		.vpp		= 80,
+		.vid		= 81,
+		.spu		= 82,
+		.pip		= 83,
+		.ge		= 84,
+		.gov		= 85,
+		.dvo		= 86,
+		.dma8		= 92,
+		.dma9		= 93,
+		.dma10		= 94,
+		.dma11		= 95,
+		.dma12		= 96,
+		.dma13		= 97,
+		.dma14		= 98,
+		.dma15		= 99,
+		.govw		= 111,
+		.govrsdscd	= 112,
+		.govrsdmif	= 113,
+		.govrhdscd	= 114,
+		.govrhdmif	= 115,
+		.nr_irqs	= 116,
+	},
+};
diff --git a/arch/arm/mach-vt8500/mmio_regs.c b/arch/arm/mach-vt8500/mmio_regs.c
new file mode 100644
index 0000000..e9b3264
--- /dev/null
+++ b/arch/arm/mach-vt8500/mmio_regs.c
@@ -0,0 +1,118 @@
+/* linux/arch/arm/mach-vt8500/mmio_regs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/mmio_regs.h>
+
+struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+struct wmt_mmio_regs wmt_regmaps[] __initdata = {
+	[VT8500_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00350000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000000,
+		.dma			= 0xd8001000,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007900,
+		.uhci			= 0xd8007b01,
+		.pata			= 0xd8008000,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.memstick		= 0xd800b400,
+		.lcdc			= 0xd800e400,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.lcdf			= 0xd8050900,
+		.vid			= 0xd8050a00,
+		.vpp			= 0xd8050b00,
+		.tsbk			= 0xd80f4000,
+		.jpegdec		= 0xd80fe000,
+		.jpegenc		= 0xd80ff000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.pcm			= 0xd82d0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.adc			= 0xd8340000,
+	},
+	[WM8505_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00390000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000400,
+		.dma			= 0xd8001800,
+		.vdma			= 0xd8001c00,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007100,
+		.uhci			= 0xd8007301,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.govr			= 0xd8050800,
+		.vid			= 0xd8050a00,
+		.scl			= 0xd8050d00,
+		.vpp			= 0xd8050f00,
+		.jpegdec		= 0xd80fe000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.ic1			= 0xd8150000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.keypad			= 0xd8260000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.uart4			= 0xd8370000,
+		.uart5			= 0xd8380000,
+	},
+};
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..d1356a1
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,254 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+	int loops = 1000;
+	while ((readb(reg) & bitmask) && --loops)
+		cpu_relax();
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	dc = pv * duty_ns / period_ns;
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else
+			pwm = ERR_PTR(-EBUSY);
+	} else
+		pwm = ERR_PTR(-ENOENT);
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else
+		pr_warning("PWM device already freed\n");
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[0].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..ab4f7aa
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,154 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+static void __iomem *regbase;
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	int loops = 1000;
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	int loops = 1000;
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_current_regs->pmc + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO "
+				"registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register "
+			"failed for %s\n", clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_current_irqs->timer_match0, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq "
+			"failed for %s\n", clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..181ad6f
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,81 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/mmio_regs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= wm8505_reserve_mem,
+	.map_io		= wm8505_map_io,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.3


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

* Re: [PATCH 1/6 v8] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-19 17:40             ` [PATCH 1/6 v8] " Alexey Charkov
@ 2010-12-20 18:15               ` Arnd Bergmann
  2010-12-20 19:15               ` Russell King - ARM Linux
  2011-07-06 12:34               ` [PATCH 1/6 v8] " Russell King - ARM Linux
  2 siblings, 0 replies; 91+ messages in thread
From: Arnd Bergmann @ 2010-12-20 18:15 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Alexey Charkov, Russell King - ARM Linux, Eric Miao,
	linux-kernel, Uwe Kleine-König, vt8500-wm8505-linux-kernel,
	Albin Tonnerre

On Sunday 19 December 2010 18:40:17 Alexey Charkov wrote:
> This adds support for the family of Systems-on-Chip produced initially
> by VIA and now its subsidiary WonderMedia that have recently become
> widespread in lower-end Chinese ARM-based tablets and netbooks.
> 
> Support is included for both VT8500 and WM8505. Suitable code is
> selected (if compiled in) at early initialization time by reading a
> platform-specific identification register, as current bootloaders
> do not provide any reliable machine id to the kernel.
> 
> Included are basic machine initialization files, register and
> interrupt definitions, support for the on-chip interrupt controller,
> high-precision OS timer, GPIO lines, necessary macros for early debug,
> pulse-width-modulated outputs control, as well as platform device
> configurations for the specific drivers implemented elsewhere.
> 
> Signed-off-by: Alexey Charkov <alchark@gmail.com>

Looks good to me, thanks for addressing all my previous concerns.

Reviewed-by: Arnd Bergmann <arnd@arndb.de>

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

* Re: [PATCH 1/6 v8] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-19 17:40             ` [PATCH 1/6 v8] " Alexey Charkov
  2010-12-20 18:15               ` Arnd Bergmann
@ 2010-12-20 19:15               ` Russell King - ARM Linux
  2010-12-20 19:26                 ` Alexey Charkov
  2010-12-20 19:54                 ` [PATCH 1/6 v9] " Alexey Charkov
  2011-07-06 12:34               ` [PATCH 1/6 v8] " Russell King - ARM Linux
  2 siblings, 2 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2010-12-20 19:15 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Sun, Dec 19, 2010 at 08:40:17PM +0300, Alexey Charkov wrote:
> +config ARCH_VT8500
> +	bool "VIA/WonderMedia 85xx"
> +	select CPU_ARM926T
> +	select GENERIC_GPIO
> +	select ARCH_HAS_CPUFREQ
> +	select GENERIC_TIME

Everything looks good, except for the above.  You don't need to select
GENERIC_TIME - it doesn't exist anymore.

It's also good to see that you've picked up on registering clocksources
with the _hz/_khz functions.

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

* Re: [PATCH 1/6 v8] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 19:15               ` Russell King - ARM Linux
@ 2010-12-20 19:26                 ` Alexey Charkov
  2010-12-20 19:54                 ` [PATCH 1/6 v9] " Alexey Charkov
  1 sibling, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-12-20 19:26 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

2010/12/20 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Sun, Dec 19, 2010 at 08:40:17PM +0300, Alexey Charkov wrote:
>> +config ARCH_VT8500
>> +     bool "VIA/WonderMedia 85xx"
>> +     select CPU_ARM926T
>> +     select GENERIC_GPIO
>> +     select ARCH_HAS_CPUFREQ
>> +     select GENERIC_TIME
>
> Everything looks good, except for the above.  You don't need to select
> GENERIC_TIME - it doesn't exist anymore.

Ok, that was an old copy-paste. Will resubmit a revised version in a moment.

> It's also good to see that you've picked up on registering clocksources
> with the _hz/_khz functions.

Yes, these are quite handy. I will also check if my previously spotted
performance issues with sched_clock are relieved with the latest
clocksource-related instrumentary in 'devel': maybe it wasn't all due
to evil busy-waiting on counter-to-bus synchronization. However, that
would probably be worth a separate small patch.

Thanks,
Alexey

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

* [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 19:15               ` Russell King - ARM Linux
  2010-12-20 19:26                 ` Alexey Charkov
@ 2010-12-20 19:54                 ` Alexey Charkov
  2010-12-20 20:50                   ` Ryan Mallon
  1 sibling, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-12-20 19:54 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505, selectable by a
configuration switch at kernel build time.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Dropped GENERIC_TIME selection from Kconfig. No further changes
compared to v8.

This is against current Linus' 'master' branch.

Best regards,
Alexey

 arch/arm/Kconfig                                |   12 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 +++
 arch/arm/mach-vt8500/Kconfig                    |   73 ++++
 arch/arm/mach-vt8500/Makefile                   |    6 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   82 ++++
 arch/arm/mach-vt8500/devices.c                  |  460 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   46 +++
 arch/arm/mach-vt8500/gpio.c                     |  230 +++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 ++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 ++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/io.h          |   28 ++
 arch/arm/mach-vt8500/include/mach/irq_defs.h    |  124 ++++++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 ++
 arch/arm/mach-vt8500/include/mach/mmio_regs.h   |   90 +++++
 arch/arm/mach-vt8500/include/mach/system.h      |   18 +
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 ++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 ++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 +
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 ++
 arch/arm/mach-vt8500/irq.c                      |  179 +++++++++
 arch/arm/mach-vt8500/irq_defs.c                 |  173 +++++++++
 arch/arm/mach-vt8500/mmio_regs.c                |  118 ++++++
 arch/arm/mach-vt8500/pwm.c                      |  254 +++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  154 ++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   81 ++++
 31 files changed, 2427 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irq_defs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/mmio_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/irq_defs.c
 create mode 100644 arch/arm/mach-vt8500/mmio_regs.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d56d21c..53052fa 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,16 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +983,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..2c20a34
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,73 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X576
+	bool "10-inch with 1024x576 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in CherryPal netbooks and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x576' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..aff4159
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,6 @@
+obj-y += devices.o gpio.o irq.o irq_defs.o mmio_regs.o timer.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..d2de5f9
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,82 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= vt8500_reserve_mem,
+	.map_io		= vt8500_map_io,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..1ce577b
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,460 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+#include <mach/vt8500fb.h>
+#include "devices.h"
+
+static struct resource resources_lcdc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources  = ARRAY_SIZE(resources_lcdc),
+	.resource       = resources_lcdc,
+};
+
+static struct resource resources_wm8505_fb[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(resources_wm8505_fb),
+	.resource       = resources_wm8505_fb,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X576
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 576 * 2,
+	.mode		= {
+		.name		= "1024x576",
+		.xres		= 1024,
+		.yres		= 576,
+		.left_margin	= 40,
+		.right_margin	= 24,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 96,
+		.vsync_len	= 2,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 66,
+		.right_margin	= 2,
+		.upper_margin	= 19,
+		.lower_margin	= 1,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		int len = strlen(panels[i].mode.name);
+
+		if (memcmp(panels[i].mode.name, str, len) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+static struct resource resources_uart0[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart1[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart2[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart3[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart4[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource resources_uart5[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_uart0),
+	.resource	= resources_uart0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(resources_uart1),
+	.resource	= resources_uart1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(resources_uart2),
+	.resource	= resources_uart2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(resources_uart3),
+	.resource	= resources_uart3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+	.num_resources	= ARRAY_SIZE(resources_uart4),
+	.resource	= resources_uart4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+	.num_resources	= ARRAY_SIZE(resources_uart5),
+	.resource	= resources_uart5,
+};
+
+static struct resource resources_ehci[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+	.num_resources	= ARRAY_SIZE(resources_ehci),
+	.resource	= resources_ehci,
+};
+
+static struct resource resources_ge_rops[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(resources_ge_rops),
+	.resource	= resources_ge_rops,
+};
+
+static struct resource resources_pwm[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+	.resource	= resources_pwm,
+	.num_resources	= ARRAY_SIZE(resources_pwm),
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+static struct resource resources_rtc[] = {
+	[0] = {
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+	.resource	= resources_rtc,
+	.num_resources	= ARRAY_SIZE(resources_rtc),
+};
+
+static struct map_desc vt8500_io_desc[] __initdata = {
+	/* SoC MMIO registers, to be filled in later */
+	[0] = {
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init wmt_set_resources(void)
+{
+	resources_lcdc[0].start = wmt_current_regs->lcdc;
+	resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
+	resources_lcdc[1].start = wmt_current_irqs->lcdc;
+	resources_lcdc[1].end = wmt_current_irqs->lcdc;
+
+	resources_wm8505_fb[0].start = wmt_current_regs->govr;
+	resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
+
+	resources_uart0[0].start = wmt_current_regs->uart0;
+	resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
+	resources_uart0[1].start = wmt_current_irqs->uart0;
+	resources_uart0[1].end = wmt_current_irqs->uart0;
+	resources_uart1[0].start = wmt_current_regs->uart1;
+	resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
+	resources_uart1[1].start = wmt_current_irqs->uart1;
+	resources_uart1[1].end = wmt_current_irqs->uart1;
+	resources_uart2[0].start = wmt_current_regs->uart2;
+	resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
+	resources_uart2[1].start = wmt_current_irqs->uart2;
+	resources_uart2[1].end = wmt_current_irqs->uart2;
+	resources_uart3[0].start = wmt_current_regs->uart3;
+	resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
+	resources_uart3[1].start = wmt_current_irqs->uart3;
+	resources_uart3[1].end = wmt_current_irqs->uart3;
+	resources_uart4[0].start = wmt_current_regs->uart4;
+	resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
+	resources_uart4[1].start = wmt_current_irqs->uart4;
+	resources_uart4[1].end = wmt_current_irqs->uart4;
+	resources_uart5[0].start = wmt_current_regs->uart5;
+	resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
+	resources_uart5[1].start = wmt_current_irqs->uart5;
+	resources_uart5[1].end = wmt_current_irqs->uart5;
+
+	resources_ehci[0].start = wmt_current_regs->ehci;
+	resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
+	resources_ehci[1].start = wmt_current_irqs->ehci;
+	resources_ehci[1].end = wmt_current_irqs->ehci;
+
+	resources_ge_rops[0].start = wmt_current_regs->ge;
+	resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
+
+	resources_pwm[0].start = wmt_current_regs->pwm;
+	resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
+
+	resources_rtc[0].start = wmt_current_regs->rtc;
+	resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
+	resources_rtc[1].start = wmt_current_irqs->rtc;
+	resources_rtc[1].end = wmt_current_irqs->rtc;
+	resources_rtc[2].start = wmt_current_irqs->rtc_hz;
+	resources_rtc[2].end = wmt_current_irqs->rtc_hz;
+}
+
+void __init vt8500_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
+	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init wm8505_map_io(void)
+{
+	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
+	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
+
+	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
+	vt8500_io_desc[0].pfn =
+			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
+	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
+
+	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..428809e
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,46 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init wmt_set_resources(void);
+void __init vt8500_gpio_init(void);
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..49daee6
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,230 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) &
+		~(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	writel(readl(regbase + 0x20 + vt8500_chip->regoff) |
+		(1 << vt8500_chip->shift << offset),
+		regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	if (value)
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
+			(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+	else
+		writel(readl(regbase + 0x40 + vt8500_chip->regoff) &
+			~(1 << vt8500_chip->shift << offset),
+			regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
+	VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
+	VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
+	VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
+	VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
+	VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
+
+	VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
+	VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
+	VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
+	VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
+
+	VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
+	VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
+	VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
+
+	VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
+
+	VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
+
+	VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	writel(readl(regbase + 0x3c) & ~(1 << offset), regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	writel(readl(regbase + 0x3c) | (1 << offset), regbase + 0x3c);
+
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	if (value)
+		writel(readl(regbase + 0x5c) | (1 << offset),
+		       regbase + 0x5c);
+	else
+		writel(readl(regbase + 0x5c) & ~(1 << offset),
+			regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	gpio_to_irq_map[0] = wmt_current_irqs->ext0;
+	gpio_to_irq_map[1] = wmt_current_irqs->ext1;
+	gpio_to_irq_map[2] = wmt_current_irqs->ext2;
+	gpio_to_irq_map[3] = wmt_current_irqs->ext3;
+	gpio_to_irq_map[4] = wmt_current_irqs->ext4;
+	gpio_to_irq_map[5] = wmt_current_irqs->ext5;
+	gpio_to_irq_map[6] = wmt_current_irqs->ext6;
+	gpio_to_irq_map[7] = wmt_current_irqs->ext7;
+
+	regbase = ioremap(wmt_current_regs->gpio, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..8dd55c8
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		((void __iomem *)((a) + 0xf0000000))
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irq_defs.h b/arch/arm/mach-vt8500/include/mach/irq_defs.h
new file mode 100644
index 0000000..fa8f4b3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irq_defs.h
@@ -0,0 +1,124 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irq_defs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef VT8500_IRQ_DEFS_H
+#define VT8500_IRQ_DEFS_H
+
+#include <linux/types.h>
+
+struct wmt_irq_srcs {
+	u8 nr_irqs;
+	u8 jpegenc;
+	u8 jpegdec;
+	u8 pata;
+	u8 dma;
+	u8 ext0;
+	u8 ext1;
+	u8 ext2;
+	u8 ext3;
+	u8 ext4;
+	u8 ext5;
+	u8 ext6;
+	u8 ext7;
+	u8 ether;
+	u8 mpegts;
+	u8 ge;
+	u8 gov;
+	u8 lcdc;
+	u8 lcdf;
+	u8 vpp;
+	u8 vpu;
+	u8 vid;
+	u8 spu;
+	u8 pip;
+	u8 dvo;
+	u8 govw;
+	u8 govrsdscd;
+	u8 govrsdmif;
+	u8 govrhdscd;
+	u8 govrhdmif;
+	u8 cipher;
+	u8 i2c0;
+	u8 i2c1;
+	u8 sdmmc;
+	u8 sdmmc_dma;
+	u8 pmc_wu;
+	u8 spi0;
+	u8 spi1;
+	u8 spi2;
+	u8 nand;
+	u8 nand_dma;
+	u8 nor;
+	u8 memstick;
+	u8 memstick_dma;
+	u8 uart0;
+	u8 uart1;
+	u8 uart2;
+	u8 uart3;
+	u8 uart4;
+	u8 uart5;
+	u8 i2s;
+	u8 pcm;
+	u8 ac97;
+	u8 timer_match0;
+	u8 timer_match1;
+	u8 timer_match2;
+	u8 timer_match3;
+	u8 ehci;
+	u8 uhci;
+	u8 udc;
+	u8 udc_dma;
+	u8 keypad;
+	u8 ps2mouse;
+	u8 ps2kbd;
+	u8 rtc;
+	u8 rtc_hz;
+	u8 adc;
+	u8 cir;
+	u8 dma0;
+	u8 dma1;
+	u8 dma2;
+	u8 dma3;
+	u8 dma4;
+	u8 dma5;
+	u8 dma6;
+	u8 dma7;
+	u8 dma8;
+	u8 dma9;
+	u8 dma10;
+	u8 dma11;
+	u8 dma12;
+	u8 dma13;
+	u8 dma14;
+	u8 dma15;
+	u8 irq0;
+	u8 irq1;
+	u8 irq2;
+	u8 irq3;
+	u8 irq4;
+	u8 irq5;
+	u8 irq6;
+	u8 irq7;
+	u8 sae;
+};
+
+extern struct wmt_irq_srcs wmt_irqs[] __initdata;
+extern struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/mmio_regs.h b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
new file mode 100644
index 0000000..76439dd
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
@@ -0,0 +1,90 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/mmio_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_MMIO_REGS_H
+#define __ASM_ARM_ARCH_MMIO_REGS_H
+
+#include <linux/init.h>
+
+struct wmt_mmio_regs {
+	unsigned long mmio_regs_start;
+	unsigned long mmio_regs_length;
+	unsigned long mmio_regs_virt;
+	unsigned long ddr;
+	unsigned long dma;
+	unsigned long vdma;
+	unsigned long sflash;
+	unsigned long ether;
+	unsigned long cipher;
+	unsigned long ehci;
+	unsigned long uhci;
+	unsigned long pata;
+	unsigned long ps2;
+	unsigned long nand;
+	unsigned long nor;
+	unsigned long sdmmc;
+	unsigned long memstick;
+	unsigned long lcdc;
+	unsigned long vpu;
+	unsigned long gov;
+	unsigned long ge;
+	unsigned long govr;
+	unsigned long scl;
+	unsigned long lcdf;
+	unsigned long vid;
+	unsigned long vpp;
+	unsigned long tsbk;
+	unsigned long jpegdec;
+	unsigned long jpegenc;
+	unsigned long rtc;
+	unsigned long gpio;
+	unsigned long scc;
+	unsigned long pmc;
+	unsigned long ic0;
+	unsigned long ic1;
+	unsigned long uart0;
+	unsigned long uart1;
+	unsigned long uart2;
+	unsigned long uart3;
+	unsigned long uart4;
+	unsigned long uart5;
+	unsigned long pwm;
+	unsigned long spi0;
+	unsigned long spi1;
+	unsigned long spi2;
+	unsigned long cir;
+	unsigned long i2c0;
+	unsigned long i2c1;
+	unsigned long ac97;
+	unsigned long pcm;
+	unsigned long i2s;
+	unsigned long adc;
+	unsigned long keypad;
+};
+
+enum {
+	VT8500_INDEX,
+	WM8505_INDEX,
+};
+
+extern struct wmt_mmio_regs wmt_regmaps[] __initdata;
+extern struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+#endif
+
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..d6c757e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,18 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR_VIRT	0xf8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	writel(1, VT8500_PMSR_VIRT);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..4642290
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END	0xd0000000UL
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..cc7f25e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	__u32			xres_virtual;
+	__u32			yres_virtual;
+	__u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..c042e85
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,179 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);
+	if (edge)
+		writel(readl(base
+			+ VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
+			| (1 << (irq & 0x1f)), base
+			+ VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));
+	else
+		writeb(readb(base
+			+ VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
+			base + VT8500_IC_DCTR + irq);
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	writeb(readb(base
+		+ VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
+		base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
+{
+	return -EINVAL;
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_HIGH, base
+			+ VT8500_IC_DCTR + irq);
+		irq_desc[orig_irq].handle_irq = handle_level_irq;
+		break;
+	case IRQF_TRIGGER_FALLING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_FALLING, base
+			+ VT8500_IC_DCTR + irq);
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	case IRQF_TRIGGER_RISING:
+		writeb((readb(base
+			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
+			| VT8500_TRIGGER_RISING, base
+			+ VT8500_IC_DCTR + irq);
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	}
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_wake  = vt8500_irq_set_wake,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
+	sic_regbase = ioremap(wmt_current_regs->ic1, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR
+								+ i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller "
+				"registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/irq_defs.c b/arch/arm/mach-vt8500/irq_defs.c
new file mode 100644
index 0000000..b338c04
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq_defs.c
@@ -0,0 +1,173 @@
+/* linux/arch/arm/mach-vt8500/irq_defs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/irq_defs.h>
+#include <mach/mmio_regs.h>
+
+struct wmt_irq_srcs *wmt_current_irqs __initdata;
+
+struct wmt_irq_srcs wmt_irqs[] __initdata = {
+	[VT8500_INDEX] = {
+		.jpegenc	= 0,
+		.jpegdec	= 1,
+		.pata		= 3,
+		.dma		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.ge		= 8,
+		.gov		= 9,
+		.ether		= 10,
+		.mpegts		= 11,
+		.lcdc		= 12,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.cipher		= 16,
+		.vpp		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.lcdf		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.memstick	= 30,
+		.memstick_dma	= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.i2s		= 34,
+		.pcm		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.vpu		= 40,
+		.vid		= 41,
+		.ac97		= 42,
+		.ehci		= 43,
+		.nor		= 44,
+		.ps2mouse	= 45,
+		.ps2kbd		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.adc		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.dma0		= 56,
+		.dma1		= 57,
+		.dma2		= 58,
+		.dma3		= 59,
+		.dma4		= 60,
+		.dma5		= 61,
+		.dma6		= 62,
+		.dma7		= 63,
+		.nr_irqs	= 64,
+	},
+	[WM8505_INDEX] = {
+		.uhci		= 0,
+		.ehci		= 1,
+		.udc_dma	= 2,
+		.ps2mouse	= 4,
+		.udc		= 5,
+		.ext0		= 6,
+		.ext1		= 7,
+		.keypad		= 8,
+		.dma		= 9,
+		.ether		= 10,
+		.ext2		= 13,
+		.ext3		= 14,
+		.ext4		= 15,
+		.dma0		= 17,
+		.i2c1		= 18,
+		.i2c0		= 19,
+		.sdmmc		= 20,
+		.sdmmc_dma	= 21,
+		.pmc_wu		= 22,
+		.ps2kbd		= 23,
+		.spi0		= 24,
+		.spi1		= 25,
+		.spi2		= 26,
+		.dma1		= 27,
+		.nand		= 28,
+		.nand_dma	= 29,
+		.uart5		= 30,
+		.uart4		= 31,
+		.uart0		= 32,
+		.uart1		= 33,
+		.dma2		= 34,
+		.i2s		= 35,
+		.timer_match0	= 36,
+		.timer_match1	= 37,
+		.timer_match2	= 38,
+		.timer_match3	= 39,
+		.dma3		= 40,
+		.dma4		= 41,
+		.ac97		= 42,
+		.nor		= 44,
+		.dma5		= 45,
+		.dma6		= 46,
+		.uart2		= 47,
+		.rtc		= 48,
+		.rtc_hz		= 49,
+		.uart3		= 50,
+		.dma7		= 51,
+		.ext5		= 52,
+		.ext6		= 53,
+		.ext7		= 54,
+		.cir		= 55,
+		.irq0		= 56,
+		.irq1		= 57,
+		.irq2		= 58,
+		.irq3		= 59,
+		.irq4		= 60,
+		.irq5		= 61,
+		.irq6		= 62,
+		.irq7		= 63,
+		.jpegdec	= 65,
+		.sae		= 66,
+		.vpu		= 79,
+		.vpp		= 80,
+		.vid		= 81,
+		.spu		= 82,
+		.pip		= 83,
+		.ge		= 84,
+		.gov		= 85,
+		.dvo		= 86,
+		.dma8		= 92,
+		.dma9		= 93,
+		.dma10		= 94,
+		.dma11		= 95,
+		.dma12		= 96,
+		.dma13		= 97,
+		.dma14		= 98,
+		.dma15		= 99,
+		.govw		= 111,
+		.govrsdscd	= 112,
+		.govrsdmif	= 113,
+		.govrhdscd	= 114,
+		.govrhdmif	= 115,
+		.nr_irqs	= 116,
+	},
+};
diff --git a/arch/arm/mach-vt8500/mmio_regs.c b/arch/arm/mach-vt8500/mmio_regs.c
new file mode 100644
index 0000000..e9b3264
--- /dev/null
+++ b/arch/arm/mach-vt8500/mmio_regs.c
@@ -0,0 +1,118 @@
+/* linux/arch/arm/mach-vt8500/mmio_regs.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <mach/mmio_regs.h>
+
+struct wmt_mmio_regs *wmt_current_regs __initdata;
+
+struct wmt_mmio_regs wmt_regmaps[] __initdata = {
+	[VT8500_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00350000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000000,
+		.dma			= 0xd8001000,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007900,
+		.uhci			= 0xd8007b01,
+		.pata			= 0xd8008000,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.memstick		= 0xd800b400,
+		.lcdc			= 0xd800e400,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.lcdf			= 0xd8050900,
+		.vid			= 0xd8050a00,
+		.vpp			= 0xd8050b00,
+		.tsbk			= 0xd80f4000,
+		.jpegdec		= 0xd80fe000,
+		.jpegenc		= 0xd80ff000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.pcm			= 0xd82d0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.adc			= 0xd8340000,
+	},
+	[WM8505_INDEX] = {
+		.mmio_regs_start	= 0xd8000000,
+		.mmio_regs_length	= 0x00390000,
+		.mmio_regs_virt		= 0xf8000000,
+		.ddr			= 0xd8000400,
+		.dma			= 0xd8001800,
+		.vdma			= 0xd8001c00,
+		.sflash			= 0xd8002000,
+		.ether			= 0xd8004000,
+		.cipher			= 0xd8006000,
+		.ehci			= 0xd8007100,
+		.uhci			= 0xd8007301,
+		.ps2			= 0xd8008800,
+		.nand			= 0xd8009000,
+		.nor			= 0xd8009400,
+		.sdmmc			= 0xd800a000,
+		.vpu			= 0xd8050000,
+		.gov			= 0xd8050300,
+		.ge			= 0xd8050400,
+		.govr			= 0xd8050800,
+		.vid			= 0xd8050a00,
+		.scl			= 0xd8050d00,
+		.vpp			= 0xd8050f00,
+		.jpegdec		= 0xd80fe000,
+		.rtc			= 0xd8100000,
+		.gpio			= 0xd8110000,
+		.scc			= 0xd8120000,
+		.pmc			= 0xd8130000,
+		.ic0			= 0xd8140000,
+		.ic1			= 0xd8150000,
+		.uart0			= 0xd8200000,
+		.uart2			= 0xd8210000,
+		.pwm			= 0xd8220000,
+		.spi0			= 0xd8240000,
+		.spi1			= 0xd8250000,
+		.keypad			= 0xd8260000,
+		.cir			= 0xd8270000,
+		.i2c0			= 0xd8280000,
+		.ac97			= 0xd8290000,
+		.spi2			= 0xd82a0000,
+		.uart1			= 0xd82b0000,
+		.uart3			= 0xd82c0000,
+		.i2c1			= 0xd8320000,
+		.i2s			= 0xd8330000,
+		.uart4			= 0xd8370000,
+		.uart5			= 0xd8380000,
+	},
+};
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..d1356a1
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,254 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+	int loops = 1000;
+	while ((readb(reg) & bitmask) && --loops)
+		cpu_relax();
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	dc = pv * duty_ns / period_ns;
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else
+			pwm = ERR_PTR(-EBUSY);
+	} else
+		pwm = ERR_PTR(-ENOENT);
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else
+		pr_warning("PWM device already freed\n");
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[0].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..ab4f7aa
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,154 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/mmio_regs.h>
+#include <mach/irq_defs.h>
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+static void __iomem *regbase;
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	int loops = 1000;
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	int loops = 1000;
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_current_regs->pmc + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO "
+				"registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register "
+			"failed for %s\n", clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_current_irqs->timer_match0, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq "
+			"failed for %s\n", clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..181ad6f
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,81 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/mmio_regs.h>
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
+					     + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, "
+				"display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be "
+				"remapped, not enabling power off!\n");
+
+	wmt_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= wm8505_reserve_mem,
+	.map_io		= wm8505_map_io,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.4


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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 19:54                 ` [PATCH 1/6 v9] " Alexey Charkov
@ 2010-12-20 20:50                   ` Ryan Mallon
  2010-12-20 21:48                     ` Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Ryan Mallon @ 2010-12-20 20:50 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

On 12/21/2010 08:54 AM, Alexey Charkov wrote:
> This adds support for the family of Systems-on-Chip produced initially
> by VIA and now its subsidiary WonderMedia that have recently become
> widespread in lower-end Chinese ARM-based tablets and netbooks.
> 
> Support is included for both VT8500 and WM8505, selectable by a
> configuration switch at kernel build time.
> 
> Included are basic machine initialization files, register and
> interrupt definitions, support for the on-chip interrupt controller,
> high-precision OS timer, GPIO lines, necessary macros for early debug,
> pulse-width-modulated outputs control, as well as platform device
> configurations for the specific drivers implemented elsewhere.
> 
> Signed-off-by: Alexey Charkov <alchark@gmail.com>

Hi Alexey,

Quick review below.

~Ryan

> ---
> 
> Dropped GENERIC_TIME selection from Kconfig. No further changes
> compared to v8.
> 
> This is against current Linus' 'master' branch.
> 
> Best regards,
> Alexey
> 
>  arch/arm/Kconfig                                |   12 +
>  arch/arm/Makefile                               |    1 +
>  arch/arm/boot/compressed/Makefile               |    4 +
>  arch/arm/boot/compressed/head-vt8500.S          |   46 +++
>  arch/arm/mach-vt8500/Kconfig                    |   73 ++++
>  arch/arm/mach-vt8500/Makefile                   |    6 +
>  arch/arm/mach-vt8500/Makefile.boot              |    3 +
>  arch/arm/mach-vt8500/bv07.c                     |   82 ++++
>  arch/arm/mach-vt8500/devices.c                  |  460 +++++++++++++++++++++++
>  arch/arm/mach-vt8500/devices.h                  |   46 +++
>  arch/arm/mach-vt8500/gpio.c                     |  230 +++++++++++
>  arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 ++
>  arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 ++
>  arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
>  arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
>  arch/arm/mach-vt8500/include/mach/io.h          |   28 ++
>  arch/arm/mach-vt8500/include/mach/irq_defs.h    |  124 ++++++
>  arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
>  arch/arm/mach-vt8500/include/mach/memory.h      |   28 ++
>  arch/arm/mach-vt8500/include/mach/mmio_regs.h   |   90 +++++
>  arch/arm/mach-vt8500/include/mach/system.h      |   18 +
>  arch/arm/mach-vt8500/include/mach/timex.h       |   26 ++
>  arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 ++
>  arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 +
>  arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 ++
>  arch/arm/mach-vt8500/irq.c                      |  179 +++++++++
>  arch/arm/mach-vt8500/irq_defs.c                 |  173 +++++++++
>  arch/arm/mach-vt8500/mmio_regs.c                |  118 ++++++
>  arch/arm/mach-vt8500/pwm.c                      |  254 +++++++++++++
>  arch/arm/mach-vt8500/timer.c                    |  154 ++++++++
>  arch/arm/mach-vt8500/wm8505_7in.c               |   81 ++++
>  31 files changed, 2427 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/boot/compressed/head-vt8500.S
>  create mode 100644 arch/arm/mach-vt8500/Kconfig
>  create mode 100644 arch/arm/mach-vt8500/Makefile
>  create mode 100644 arch/arm/mach-vt8500/Makefile.boot
>  create mode 100644 arch/arm/mach-vt8500/bv07.c
>  create mode 100644 arch/arm/mach-vt8500/devices.c
>  create mode 100644 arch/arm/mach-vt8500/devices.h
>  create mode 100644 arch/arm/mach-vt8500/gpio.c
>  create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
>  create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
>  create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/irq_defs.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/mmio_regs.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
>  create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
>  create mode 100644 arch/arm/mach-vt8500/irq.c
>  create mode 100644 arch/arm/mach-vt8500/irq_defs.c
>  create mode 100644 arch/arm/mach-vt8500/mmio_regs.c
>  create mode 100644 arch/arm/mach-vt8500/pwm.c
>  create mode 100644 arch/arm/mach-vt8500/timer.c
>  create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index d56d21c..53052fa 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -843,6 +843,16 @@ config PLAT_SPEAR
>  	help
>  	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
>  
> +config ARCH_VT8500
> +	bool "VIA/WonderMedia 85xx"
> +	select CPU_ARM926T
> +	select GENERIC_GPIO
> +	select ARCH_HAS_CPUFREQ
> +	select GENERIC_CLOCKEVENTS
> +	select ARCH_REQUIRE_GPIOLIB
> +	select HAVE_PWM
> +	help
> +	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
>  endchoice
>  
>  #
> @@ -973,6 +983,8 @@ source "arch/arm/mach-versatile/Kconfig"
>  
>  source "arch/arm/mach-vexpress/Kconfig"
>  
> +source "arch/arm/mach-vt8500/Kconfig"
> +
>  source "arch/arm/mach-w90x900/Kconfig"
>  
>  # Definitions to make life easier
> diff --git a/arch/arm/Makefile b/arch/arm/Makefile
> index b87aed0..b0f219a 100644
> --- a/arch/arm/Makefile
> +++ b/arch/arm/Makefile
> @@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
>  machine-$(CONFIG_ARCH_U8500)		:= ux500
>  machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
>  machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
> +machine-$(CONFIG_ARCH_VT8500)		:= vt8500
>  machine-$(CONFIG_ARCH_W90X900)		:= w90x900
>  machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
>  machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
> diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
> index 65a7c1c..62cade4 100644
> --- a/arch/arm/boot/compressed/Makefile
> +++ b/arch/arm/boot/compressed/Makefile
> @@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
>  OBJS		+= head-sa1100.o
>  endif
>  
> +ifeq ($(CONFIG_ARCH_VT8500),y)
> +OBJS		+= head-vt8500.o
> +endif
> +
>  ifeq ($(CONFIG_CPU_XSCALE),y)
>  OBJS		+= head-xscale.o
>  endif
> diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
> new file mode 100644
> index 0000000..1dc1e21
> --- /dev/null
> +++ b/arch/arm/boot/compressed/head-vt8500.S
> @@ -0,0 +1,46 @@
> +/*
> + * linux/arch/arm/boot/compressed/head-vt8500.S
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
> + *
> + */
> +
> +#include <linux/linkage.h>
> +#include <asm/mach-types.h>
> +
> +		.section        ".start", "ax"
> +
> +__VT8500_start:
> +	@ Compare the SCC ID register against a list of known values
> +	ldr	r1, .SCCID
> +	ldr	r3, [r1]
> +
> +	@ VT8500 override
> +	ldr	r4, .VT8500SCC
> +	cmp	r3, r4
> +	ldreq	r7, .ID_BV07
> +	beq	.Lendvt8500
> +
> +	@ WM8505 override
> +	ldr	r4, .WM8505SCC
> +	cmp	r3, r4
> +	ldreq	r7, .ID_8505
> +	beq	.Lendvt8500
> +
> +	@ Otherwise, leave the bootloader's machine id untouched
> +
> +.SCCID:
> +	.word	0xd8120000
> +.VT8500SCC:
> +	.word	0x34000102
> +.WM8505SCC:
> +	.word	0x34260103
> +
> +.ID_BV07:
> +	.word	MACH_TYPE_BV07
> +.ID_8505:
> +	.word	MACH_TYPE_WM8505_7IN_NETBOOK
> +
> +.Lendvt8500:
> diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
> new file mode 100644
> index 0000000..2c20a34
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/Kconfig
> @@ -0,0 +1,73 @@
> +if ARCH_VT8500
> +
> +config VTWM_VERSION_VT8500
> +	bool
> +
> +config VTWM_VERSION_WM8505
> +	bool
> +
> +config MACH_BV07
> +	bool "Benign BV07-8500 Mini Netbook"
> +	depends on ARCH_VT8500
> +	select VTWM_VERSION_VT8500
> +	help
> +	  Add support for the inexpensive 7-inch netbooks sold by many
> +	  Chinese distributors under various names. Note that there are
> +	  many hardware implementations in identical exterior, make sure
> +	  that yours is indeed based on a VIA VT8500 chip.
> +
> +config MACH_WM8505_7IN_NETBOOK
> +	bool "WM8505 7-inch generic netbook"
> +	depends on ARCH_VT8500
> +	select VTWM_VERSION_WM8505
> +	help
> +	  Add support for the inexpensive 7-inch netbooks sold by many
> +	  Chinese distributors under various names. Note that there are
> +	  many hardware implementations in identical exterior, make sure
> +	  that yours is indeed based on a WonderMedia WM8505 chip.
> +
> +comment "LCD panel size"
> +
> +config WMT_PANEL_800X480
> +	bool "7-inch with 800x480 resolution"
> +	depends on (FB_VT8500 || FB_WM8505)
> +	default y
> +	help
> +	  These are found in most of the netbooks in generic cases, as
> +	  well as in Eken M001 tablets and possibly elsewhere.
> +
> +	  To select this panel at runtime, say y here and append
> +	  'panel=800x480' to your kernel command line. Otherwise, the
> +	  largest one available will be used.
> +
> +config WMT_PANEL_800X600
> +	bool "8-inch with 800x600 resolution"
> +	depends on (FB_VT8500 || FB_WM8505)
> +	help
> +	  These are found in Eken M003 tablets and possibly elsewhere.
> +
> +	  To select this panel at runtime, say y here and append
> +	  'panel=800x600' to your kernel command line. Otherwise, the
> +	  largest one available will be used.
> +
> +config WMT_PANEL_1024X576
> +	bool "10-inch with 1024x576 resolution"
> +	depends on (FB_VT8500 || FB_WM8505)
> +	help
> +	  These are found in CherryPal netbooks and possibly elsewhere.
> +
> +	  To select this panel at runtime, say y here and append
> +	  'panel=1024x576' to your kernel command line. Otherwise, the
> +	  largest one available will be used.
> +
> +config WMT_PANEL_1024X600
> +	bool "10-inch with 1024x600 resolution"
> +	depends on (FB_VT8500 || FB_WM8505)
> +	help
> +	  These are found in Eken M006 tablets and possibly elsewhere.
> +
> +	  To select this panel at runtime, say y here and append
> +	  'panel=1024x600' to your kernel command line. Otherwise, the
> +	  largest one available will be used.
> +
> +endif
> diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
> new file mode 100644
> index 0000000..aff4159
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/Makefile
> @@ -0,0 +1,6 @@
> +obj-y += devices.o gpio.o irq.o irq_defs.o mmio_regs.o timer.o
> +
> +obj-$(CONFIG_MACH_BV07) += bv07.o
> +obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
> +
> +obj-$(CONFIG_HAVE_PWM) += pwm.o
> diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
> new file mode 100644
> index 0000000..a8acc4e
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/Makefile.boot
> @@ -0,0 +1,3 @@
> +   zreladdr-y	:= 0x00008000
> +params_phys-y	:= 0x00000100
> +initrd_phys-y	:= 0x01000000
> diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
> new file mode 100644
> index 0000000..d2de5f9
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/bv07.c
> @@ -0,0 +1,82 @@
> +/*
> + *  arch/arm/mach-vt8500/bv07.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/io.h>
> +#include <linux/pm.h>
> +
> +#include <asm/mach-types.h>
> +#include <asm/mach/arch.h>
> +
> +#include <mach/mmio_regs.h>
> +#include <mach/irq_defs.h>
> +#include "devices.h"
> +
> +static void __iomem *pmc_hiber;
> +
> +static struct platform_device *devices[] __initdata = {
> +	&vt8500_device_uart0,
> +	&vt8500_device_lcdc,
> +	&vt8500_device_ehci,
> +	&vt8500_device_ge_rops,
> +	&vt8500_device_pwm,
> +	&vt8500_device_pwmbl,
> +	&vt8500_device_rtc,
> +};
> +
> +static void vt8500_power_off(void)
> +{
> +	local_irq_disable();
> +	writew(5, pmc_hiber);
> +	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
> +}
> +
> +void __init bv07_init(void)
> +{
> +#ifdef CONFIG_FB_VT8500
> +	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
> +					     + 0x200, 4);
> +	if (gpio_mux_reg) {
> +		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
> +		iounmap(gpio_mux_reg);
> +	} else {
> +		printk(KERN_ERR "Could not remap the GPIO mux register, "
> +				"display may not work properly!\n");
> +	}
> +#endif
> +	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
> +	if (pmc_hiber)
> +		pm_power_off = &vt8500_power_off;
> +	else
> +		printk(KERN_ERR "PMC Hibernation register could not be "
> +				"remapped, not enabling power off!\n");
> +
> +	wmt_set_resources();
> +	platform_add_devices(devices, ARRAY_SIZE(devices));
> +	vt8500_gpio_init();
> +}
> +
> +MACHINE_START(BV07, "Benign BV07 Mini Netbook")
> +	.boot_params	= 0x00000100,
> +	.reserve	= vt8500_reserve_mem,
> +	.map_io		= vt8500_map_io,
> +	.init_irq	= vt8500_init_irq,
> +	.timer		= &vt8500_timer,
> +	.init_machine	= bv07_init,
> +MACHINE_END
> diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
> new file mode 100644
> index 0000000..1ce577b
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/devices.c
> @@ -0,0 +1,460 @@
> +/* linux/arch/arm/mach-vt8500/devices.c
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/io.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm_backlight.h>
> +#include <linux/memblock.h>
> +
> +#include <asm/mach/arch.h>
> +#include <asm/mach/map.h>
> +
> +#include <mach/mmio_regs.h>
> +#include <mach/irq_defs.h>
> +#include <mach/vt8500fb.h>
> +#include "devices.h"
> +
> +static struct resource resources_lcdc[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static u64 fb_dma_mask = DMA_BIT_MASK(32);
> +
> +struct platform_device vt8500_device_lcdc = {
> +	.name           = "vt8500-lcd",
> +	.id             = 0,
> +	.dev		= {
> +		.dma_mask	= &fb_dma_mask,
> +		.coherent_dma_mask = DMA_BIT_MASK(32),
> +	},
> +	.num_resources  = ARRAY_SIZE(resources_lcdc),
> +	.resource       = resources_lcdc,
> +};
> +
> +static struct resource resources_wm8505_fb[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	}
> +};
> +
> +struct platform_device vt8500_device_wm8505_fb = {
> +	.name           = "wm8505-fb",
> +	.id             = 0,
> +	.num_resources  = ARRAY_SIZE(resources_wm8505_fb),
> +	.resource       = resources_wm8505_fb,
> +};
> +
> +/* Smallest to largest */
> +static struct vt8500fb_platform_data panels[] = {
> +#ifdef CONFIG_WMT_PANEL_800X480
> +{
> +	.xres_virtual	= 800,
> +	.yres_virtual	= 480 * 2,
> +	.mode		= {
> +		.name		= "800x480",
> +		.xres		= 800,
> +		.yres		= 480,
> +		.left_margin	= 88,
> +		.right_margin	= 40,
> +		.upper_margin	= 32,
> +		.lower_margin	= 11,
> +		.hsync_len	= 0,
> +		.vsync_len	= 1,
> +		.vmode		= FB_VMODE_NONINTERLACED,
> +	},
> +},
> +#endif
> +#ifdef CONFIG_WMT_PANEL_800X600
> +{
> +	.xres_virtual	= 800,
> +	.yres_virtual	= 600 * 2,
> +	.mode		= {
> +		.name		= "800x600",
> +		.xres		= 800,
> +		.yres		= 600,
> +		.left_margin	= 88,
> +		.right_margin	= 40,
> +		.upper_margin	= 32,
> +		.lower_margin	= 11,
> +		.hsync_len	= 0,
> +		.vsync_len	= 1,
> +		.vmode		= FB_VMODE_NONINTERLACED,
> +	},
> +},
> +#endif
> +#ifdef CONFIG_WMT_PANEL_1024X576
> +{
> +	.xres_virtual	= 1024,
> +	.yres_virtual	= 576 * 2,
> +	.mode		= {
> +		.name		= "1024x576",
> +		.xres		= 1024,
> +		.yres		= 576,
> +		.left_margin	= 40,
> +		.right_margin	= 24,
> +		.upper_margin	= 32,
> +		.lower_margin	= 11,
> +		.hsync_len	= 96,
> +		.vsync_len	= 2,
> +		.vmode		= FB_VMODE_NONINTERLACED,
> +	},
> +},
> +#endif
> +#ifdef CONFIG_WMT_PANEL_1024X600
> +{
> +	.xres_virtual	= 1024,
> +	.yres_virtual	= 600 * 2,
> +	.mode		= {
> +		.name		= "1024x600",
> +		.xres		= 1024,
> +		.yres		= 600,
> +		.left_margin	= 66,
> +		.right_margin	= 2,
> +		.upper_margin	= 19,
> +		.lower_margin	= 1,
> +		.hsync_len	= 23,
> +		.vsync_len	= 8,
> +		.vmode		= FB_VMODE_NONINTERLACED,
> +	},
> +},
> +#endif
> +};
> +
> +static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
> +
> +static int __init panel_setup(char *str)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(panels); i++) {
> +		int len = strlen(panels[i].mode.name);
> +
> +		if (memcmp(panels[i].mode.name, str, len) == 0) {

Should be strcmp. If the length of str is less than panels[i].mode.name
then you will buffer overrun.

> +			current_panel_idx = i;
> +			break;
> +		}
> +	}
> +	return 0;
> +}
> +
> +early_param("panel", panel_setup);
> +
> +static inline void preallocate_fb(struct vt8500fb_platform_data *p,
> +				  unsigned long align) {
> +	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
> +			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
> +					(8 / p->bpp) + 1));
> +	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
> +							  align);
> +	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
> +}
> +
> +static struct resource resources_uart0[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};

What's happening here? Does something else fill these in? If so, there
should be a comment to that effect.

> +static struct resource resources_uart1[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct resource resources_uart2[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct resource resources_uart3[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct resource resources_uart4[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct resource resources_uart5[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +struct platform_device vt8500_device_uart0 = {
> +	.name		= "vt8500_serial",
> +	.id		= 0,
> +	.num_resources	= ARRAY_SIZE(resources_uart0),
> +	.resource	= resources_uart0,
> +};
> +
> +struct platform_device vt8500_device_uart1 = {
> +	.name		= "vt8500_serial",
> +	.id		= 1,
> +	.num_resources	= ARRAY_SIZE(resources_uart1),
> +	.resource	= resources_uart1,
> +};
> +
> +struct platform_device vt8500_device_uart2 = {
> +	.name		= "vt8500_serial",
> +	.id		= 2,
> +	.num_resources	= ARRAY_SIZE(resources_uart2),
> +	.resource	= resources_uart2,
> +};
> +
> +struct platform_device vt8500_device_uart3 = {
> +	.name		= "vt8500_serial",
> +	.id		= 3,
> +	.num_resources	= ARRAY_SIZE(resources_uart3),
> +	.resource	= resources_uart3,
> +};
> +
> +struct platform_device vt8500_device_uart4 = {
> +	.name		= "vt8500_serial",
> +	.id		= 4,
> +	.num_resources	= ARRAY_SIZE(resources_uart4),
> +	.resource	= resources_uart4,
> +};
> +
> +struct platform_device vt8500_device_uart5 = {
> +	.name		= "vt8500_serial",
> +	.id		= 5,
> +	.num_resources	= ARRAY_SIZE(resources_uart5),
> +	.resource	= resources_uart5,
> +};
> +
> +static struct resource resources_ehci[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.flags	= IORESOURCE_IRQ,
> +	}
> +};
> +
> +static u64 ehci_dma_mask = DMA_BIT_MASK(32);
> +
> +struct platform_device vt8500_device_ehci = {
> +	.name		= "vt8500-ehci",
> +	.id		= 0,
> +	.dev		= {
> +		.dma_mask	= &ehci_dma_mask,
> +		.coherent_dma_mask = DMA_BIT_MASK(32),
> +	},
> +	.num_resources	= ARRAY_SIZE(resources_ehci),
> +	.resource	= resources_ehci,
> +};
> +
> +static struct resource resources_ge_rops[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	}
> +};
> +
> +struct platform_device vt8500_device_ge_rops = {
> +	.name		= "wmt_ge_rops",
> +	.id		= 0,
> +	.num_resources	= ARRAY_SIZE(resources_ge_rops),
> +	.resource	= resources_ge_rops,
> +};
> +
> +static struct resource resources_pwm[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +};
> +
> +struct platform_device vt8500_device_pwm = {
> +	.name		= "vt8500-pwm",
> +	.id		= 0,
> +	.resource	= resources_pwm,
> +	.num_resources	= ARRAY_SIZE(resources_pwm),
> +};
> +
> +static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
> +	.pwm_id		= 0,
> +	.max_brightness	= 128,
> +	.dft_brightness = 70,
> +	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
> +};
> +
> +struct platform_device vt8500_device_pwmbl = {
> +	.name		= "pwm-backlight",
> +	.id		= 0,
> +	.dev		= {
> +		.platform_data = &vt8500_pwmbl_data,
> +	},
> +};
> +
> +static struct resource resources_rtc[] = {
> +	[0] = {
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +	[2] = {
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +struct platform_device vt8500_device_rtc = {
> +	.name		= "vt8500-rtc",
> +	.id		= 0,
> +	.resource	= resources_rtc,
> +	.num_resources	= ARRAY_SIZE(resources_rtc),
> +};
> +
> +static struct map_desc vt8500_io_desc[] __initdata = {
> +	/* SoC MMIO registers, to be filled in later */
> +	[0] = {
> +		.type		= MT_DEVICE
> +	},
> +	/* PCI I/O space, numbers tied to those in <mach/io.h> */
> +	[1] = {
> +		.virtual	= 0xf0000000,
> +		.pfn		= __phys_to_pfn(0xc0000000),
> +		.length		= SZ_64K,
> +		.type		= MT_DEVICE
> +	},
> +};
> +
> +void __init wmt_set_resources(void)
> +{
> +	resources_lcdc[0].start = wmt_current_regs->lcdc;
> +	resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
> +	resources_lcdc[1].start = wmt_current_irqs->lcdc;
> +	resources_lcdc[1].end = wmt_current_irqs->lcdc;

Ah, this makes more sense. But why have all the indirection? The
wmt_regmaps table could just be replaced with #defines and then have
separate device files for the VT8500 and the WM8505. This would also
make clearer which variants have which peripherals.

> +	resources_wm8505_fb[0].start = wmt_current_regs->govr;
> +	resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
> +
> +	resources_uart0[0].start = wmt_current_regs->uart0;
> +	resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
> +	resources_uart0[1].start = wmt_current_irqs->uart0;
> +	resources_uart0[1].end = wmt_current_irqs->uart0;
> +	resources_uart1[0].start = wmt_current_regs->uart1;
> +	resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
> +	resources_uart1[1].start = wmt_current_irqs->uart1;
> +	resources_uart1[1].end = wmt_current_irqs->uart1;
> +	resources_uart2[0].start = wmt_current_regs->uart2;
> +	resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
> +	resources_uart2[1].start = wmt_current_irqs->uart2;
> +	resources_uart2[1].end = wmt_current_irqs->uart2;
> +	resources_uart3[0].start = wmt_current_regs->uart3;
> +	resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
> +	resources_uart3[1].start = wmt_current_irqs->uart3;
> +	resources_uart3[1].end = wmt_current_irqs->uart3;
> +	resources_uart4[0].start = wmt_current_regs->uart4;
> +	resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
> +	resources_uart4[1].start = wmt_current_irqs->uart4;
> +	resources_uart4[1].end = wmt_current_irqs->uart4;
> +	resources_uart5[0].start = wmt_current_regs->uart5;
> +	resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
> +	resources_uart5[1].start = wmt_current_irqs->uart5;
> +	resources_uart5[1].end = wmt_current_irqs->uart5;
> +
> +	resources_ehci[0].start = wmt_current_regs->ehci;
> +	resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
> +	resources_ehci[1].start = wmt_current_irqs->ehci;
> +	resources_ehci[1].end = wmt_current_irqs->ehci;

There is a mix of hex and decimal constants here and exact sizes and
sizes with 1 subtracted. Please be consistent.

> +	resources_ge_rops[0].start = wmt_current_regs->ge;
> +	resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
> +
> +	resources_pwm[0].start = wmt_current_regs->pwm;
> +	resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
> +
> +	resources_rtc[0].start = wmt_current_regs->rtc;
> +	resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
> +	resources_rtc[1].start = wmt_current_irqs->rtc;
> +	resources_rtc[1].end = wmt_current_irqs->rtc;
> +	resources_rtc[2].start = wmt_current_irqs->rtc_hz;
> +	resources_rtc[2].end = wmt_current_irqs->rtc_hz;
> +}
> +
> +void __init vt8500_map_io(void)
> +{
> +	wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
> +	wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
> +
> +	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
> +	vt8500_io_desc[0].pfn =
> +			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
> +	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
> +
> +	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
> +}
> +
> +void __init wm8505_map_io(void)
> +{
> +	wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
> +	wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
> +
> +	vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
> +	vt8500_io_desc[0].pfn =
> +			__phys_to_pfn(wmt_current_regs->mmio_regs_start);
> +	vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
> +
> +	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
> +}

Separate files. If more variants get added, this file will become
unwieldy very quickly.

> +
> +void __init vt8500_reserve_mem(void)
> +{
> +#ifdef CONFIG_FB_VT8500
> +	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
> +	preallocate_fb(&panels[current_panel_idx], SZ_4M);
> +	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
> +#endif
> +}

Not sure if this should exist in the platform code or the framebuffer
driver. In the latter case it would automatically be CONFIG_FB_VT8500
and the platform_data can still be set in the platform code. Is there a
reason for this not to be in the framebuffer driver?

> +
> +void __init wm8505_reserve_mem(void)
> +{
> +#if defined CONFIG_FB_WM8505
> +	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
> +	preallocate_fb(&panels[current_panel_idx], 32);
> +	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
> +#endif
> +}
> diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
> new file mode 100644
> index 0000000..428809e
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/devices.h
> @@ -0,0 +1,46 @@
> +/* linux/arch/arm/mach-vt8500/devices.h
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
> +#define __ARCH_ARM_MACH_VT8500_DEVICES_H
> +
> +#include <linux/platform_device.h>
> +
> +void __init vt8500_init_irq(void);
> +void __init wm8505_init_irq(void);
> +void __init vt8500_map_io(void);
> +void __init wm8505_map_io(void);
> +void __init vt8500_reserve_mem(void);
> +void __init wm8505_reserve_mem(void);
> +void __init wmt_set_resources(void);
> +void __init vt8500_gpio_init(void);
> +
> +extern struct sys_timer vt8500_timer;
> +
> +extern struct platform_device vt8500_device_uart0;
> +extern struct platform_device vt8500_device_uart1;
> +extern struct platform_device vt8500_device_uart2;
> +extern struct platform_device vt8500_device_uart3;
> +extern struct platform_device vt8500_device_uart4;
> +extern struct platform_device vt8500_device_uart5;
> +
> +extern struct platform_device vt8500_device_lcdc;
> +extern struct platform_device vt8500_device_wm8505_fb;
> +extern struct platform_device vt8500_device_ehci;
> +extern struct platform_device vt8500_device_ge_rops;
> +extern struct platform_device vt8500_device_pwm;
> +extern struct platform_device vt8500_device_pwmbl;
> +extern struct platform_device vt8500_device_rtc;

This could all disappear if you had separate files for the vt8500/wm8505
peripherals since the platform_devices could all be static.

> +#endif
> diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
> new file mode 100644
> index 0000000..49daee6
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/gpio.c
> @@ -0,0 +1,230 @@
> +/* linux/arch/arm/mach-vt8500/gpio.c
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/gpio.h>
> +#include <linux/init.h>
> +#include <linux/irq.h>
> +#include <linux/io.h>
> +
> +#include <mach/mmio_regs.h>
> +#include <mach/irq_defs.h>
> +
> +#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
> +
> +static void __iomem *regbase;
> +
> +struct vt8500_gpio_chip {
> +	struct gpio_chip	chip;
> +	unsigned int		shift;
> +	unsigned int		regoff;
> +};
> +
> +static int gpio_to_irq_map[8];
> +
> +static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
> +				     unsigned offset)
> +{
> +	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
> +
> +	writel(readl(regbase + vt8500_chip->regoff) |
> +		(1 << vt8500_chip->shift << offset),
> +		regbase + vt8500_chip->regoff);

This would be more readable as:

	unsigned val;

	val  = readl(regbase + vt8500_chop->regoff);
	val |= 1 << vt8500_chop->shift << offset;
	writel(val, regbase + vt8500_chip->regoff);

It's much clearer what is actually being done. Same goes for other
functions in this file.

> +
> +	return 0;
> +}
> +
> +static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
> +				   unsigned offset)
> +{
> +	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
> +
> +	writel(readl(regbase + vt8500_chip->regoff) &
> +		~(1 << vt8500_chip->shift << offset),
> +		regbase + vt8500_chip->regoff);
> +}
> +
> +static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
> +				       unsigned offset)
> +{
> +	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
> +
> +	writel(readl(regbase + 0x20 + vt8500_chip->regoff) &
> +		~(1 << vt8500_chip->shift << offset),
> +		regbase + 0x20 + vt8500_chip->regoff);
> +
> +	return 0;
> +}
> +
> +static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
> +					unsigned offset, int value)
> +{
> +	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
> +
> +	writel(readl(regbase + 0x20 + vt8500_chip->regoff) |
> +		(1 << vt8500_chip->shift << offset),
> +		regbase + 0x20 + vt8500_chip->regoff);
> +
> +	if (value)
> +		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
> +			(1 << vt8500_chip->shift << offset),
> +			regbase + 0x40 + vt8500_chip->regoff);
> +	return 0;
> +}
> +
> +static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
> +				       unsigned offset)
> +{
> +	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
> +
> +	return (readl(regbase + 0x60 + vt8500_chip->regoff)
> +		>> vt8500_chip->shift >> offset) & 1;
> +}
> +
> +static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
> +					unsigned offset, int value)
> +{
> +	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
> +
> +	if (value)
> +		writel(readl(regbase + 0x40 + vt8500_chip->regoff) |
> +			(1 << vt8500_chip->shift << offset),
> +			regbase + 0x40 + vt8500_chip->regoff);
> +	else
> +		writel(readl(regbase + 0x40 + vt8500_chip->regoff) &
> +			~(1 << vt8500_chip->shift << offset),
> +			regbase + 0x40 + vt8500_chip->regoff);
> +}
> +
> +#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
> +{									\
> +	.chip = {							\
> +		.label			= __name,			\
> +		.request		= vt8500_muxed_gpio_request,	\
> +		.free			= vt8500_muxed_gpio_free,	\
> +		.direction_input  = vt8500_muxed_gpio_direction_input,	\
> +		.direction_output = vt8500_muxed_gpio_direction_output,	\
> +		.get			= vt8500_muxed_gpio_get_value,	\
> +		.set			= vt8500_muxed_gpio_set_value,	\
> +		.can_sleep		= 0,				\
> +		.base			= __base,			\
> +		.ngpio			= __num,			\
> +	},								\
> +	.shift		= __shift,					\
> +	.regoff		= __off,					\
> +}
> +
> +static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
> +	VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
> +	VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
> +	VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
> +	VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
> +	VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
> +	VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),

Nitpick: Line up the columns to make these mpore readable.

> +
> +	VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
> +	VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
> +	VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
> +	VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
> +
> +	VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
> +	VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
> +	VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
> +
> +	VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
> +
> +	VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
> +
> +	VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
> +};
> +
> +static int vt8500_gpio_direction_input(struct gpio_chip *chip,
> +				       unsigned offset)
> +{
> +	writel(readl(regbase + 0x3c) & ~(1 << offset), regbase + 0x3c);
> +	return 0;
> +}
> +
> +static int vt8500_gpio_direction_output(struct gpio_chip *chip,
> +					unsigned offset, int value)
> +{
> +	writel(readl(regbase + 0x3c) | (1 << offset), regbase + 0x3c);
> +
> +	if (value)
> +		writel(readl(regbase + 0x5c) | (1 << offset),
> +		       regbase + 0x5c);
> +	return 0;
> +}
> +
> +static int vt8500_gpio_get_value(struct gpio_chip *chip,
> +				       unsigned offset)
> +{
> +	return (readl(regbase + 0x7c) >> offset) & 1;
> +}
> +
> +static void vt8500_gpio_set_value(struct gpio_chip *chip,
> +					unsigned offset, int value)
> +{
> +	if (value)
> +		writel(readl(regbase + 0x5c) | (1 << offset),
> +		       regbase + 0x5c);
> +	else
> +		writel(readl(regbase + 0x5c) & ~(1 << offset),
> +			regbase + 0x5c);
> +}
> +
> +static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
> +{
> +	if (offset > 7)
> +		return -EINVAL;
> +
> +	return gpio_to_irq_map[offset];
> +}
> +
> +static struct gpio_chip vt8500_external_gpios = {
> +	.label			= "extgpio",
> +	.direction_input	= vt8500_gpio_direction_input,
> +	.direction_output	= vt8500_gpio_direction_output,
> +	.get			= vt8500_gpio_get_value,
> +	.set			= vt8500_gpio_set_value,
> +	.to_irq			= vt8500_gpio_to_irq,
> +	.can_sleep		= 0,
> +	.base			= 0,
> +	.ngpio			= 8,
> +};
> +
> +void __init vt8500_gpio_init(void)
> +{
> +	int i;
> +
> +	gpio_to_irq_map[0] = wmt_current_irqs->ext0;
> +	gpio_to_irq_map[1] = wmt_current_irqs->ext1;
> +	gpio_to_irq_map[2] = wmt_current_irqs->ext2;
> +	gpio_to_irq_map[3] = wmt_current_irqs->ext3;
> +	gpio_to_irq_map[4] = wmt_current_irqs->ext4;
> +	gpio_to_irq_map[5] = wmt_current_irqs->ext5;
> +	gpio_to_irq_map[6] = wmt_current_irqs->ext6;
> +	gpio_to_irq_map[7] = wmt_current_irqs->ext7;
> +
> +	regbase = ioremap(wmt_current_regs->gpio, SZ_64K);
> +	if (!regbase) {
> +		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
> +		return;
> +	}
> +
> +	gpiochip_add(&vt8500_external_gpios);
> +
> +	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
> +		gpiochip_add(&vt8500_muxed_gpios[i].chip);
> +}
> diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
> new file mode 100644
> index 0000000..f119162
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
> @@ -0,0 +1,31 @@
> +/*
> + * arch/arm/mach-vt8500/include/mach/debug-macro.S
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * Debugging macro include header
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> +*/
> +
> +	.macro	addruart, rp, rv
> +	mov	\rp,      #0x00200000
> +	orr	\rv, \rp, #0xf8000000
> +	orr	\rp, \rp, #0xd8000000
> +	.endm
> +
> +	.macro	senduart,rd,rx
> +	strb	\rd, [\rx, #0]
> +	.endm
> +
> +	.macro	busyuart,rd,rx
> +1001:	ldr	\rd, [\rx, #0x1c]
> +	ands	\rd, \rd, #0x2
> +	bne	1001b
> +	.endm
> +
> +	.macro	waituart,rd,rx
> +	.endm
> diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
> new file mode 100644
> index 0000000..92684c7
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
> @@ -0,0 +1,32 @@
> +/*
> + * arch/arm/mach-vt8500/include/mach/entry-macro.S
> + *
> + * Low-level IRQ helper macros for VIA VT8500
> + *
> + * 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.
> + */
> +
> +	.macro	disable_fiq
> +	.endm
> +
> +	.macro  get_irqnr_preamble, base, tmp
> +	@ physical 0xd8140000 is virtual 0xf8140000
> +	mov	\base, #0xf8000000
> +	orr	\base, \base, #0x00140000
> +	.endm
> +
> +	.macro  arch_ret_to_user, tmp1, tmp2
> +	.endm
> +
> +	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
> +	ldr	\irqnr, [\base]
> +	cmp	\irqnr, #63 @ may be false positive, check interrupt status
> +	bne	1001f
> +	ldr	\irqstat, [\base, #0x84]
> +	ands	\irqstat, #0x80000000
> +	moveq	\irqnr, #0
> +1001:
> +	.endm
> +
> diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
> new file mode 100644
> index 0000000..94ff276
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/gpio.h
> @@ -0,0 +1,6 @@
> +#include <asm-generic/gpio.h>
> +
> +#define gpio_get_value	__gpio_get_value
> +#define gpio_set_value	__gpio_set_value
> +#define gpio_cansleep	__gpio_cansleep
> +#define gpio_to_irq	__gpio_to_irq
> diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
> new file mode 100644
> index 0000000..db4163f
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/hardware.h
> @@ -0,0 +1,12 @@
> +/* arch/arm/mach-vt8500/include/mach/hardware.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
> new file mode 100644
> index 0000000..8dd55c8
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/io.h
> @@ -0,0 +1,28 @@
> +/*
> + *  arch/arm/mach-vt8500/include/mach/io.h
> + *
> + *  Copyright (C) 2010 Alexey Charkov
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +#ifndef __ASM_ARM_ARCH_IO_H
> +#define __ASM_ARM_ARCH_IO_H
> +
> +#define IO_SPACE_LIMIT 0xffff
> +
> +#define __io(a)		((void __iomem *)((a) + 0xf0000000))

Should use __typesafe_io.

> +#define __mem_pci(a)	(a)
> +
> +#endif
> diff --git a/arch/arm/mach-vt8500/include/mach/irq_defs.h b/arch/arm/mach-vt8500/include/mach/irq_defs.h
> new file mode 100644
> index 0000000..fa8f4b3
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/irq_defs.h
> @@ -0,0 +1,124 @@
> +/*
> + *  arch/arm/mach-vt8500/include/mach/irq_defs.h
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +#ifndef VT8500_IRQ_DEFS_H
> +#define VT8500_IRQ_DEFS_H
> +
> +#include <linux/types.h>
> +
> +struct wmt_irq_srcs {
> +	u8 nr_irqs;
> +	u8 jpegenc;
> +	u8 jpegdec;
> +	u8 pata;
> +	u8 dma;
> +	u8 ext0;
> +	u8 ext1;
> +	u8 ext2;
> +	u8 ext3;
> +	u8 ext4;
> +	u8 ext5;
> +	u8 ext6;
> +	u8 ext7;
> +	u8 ether;
> +	u8 mpegts;
> +	u8 ge;
> +	u8 gov;
> +	u8 lcdc;
> +	u8 lcdf;
> +	u8 vpp;
> +	u8 vpu;
> +	u8 vid;
> +	u8 spu;
> +	u8 pip;
> +	u8 dvo;
> +	u8 govw;
> +	u8 govrsdscd;
> +	u8 govrsdmif;
> +	u8 govrhdscd;
> +	u8 govrhdmif;
> +	u8 cipher;
> +	u8 i2c0;
> +	u8 i2c1;
> +	u8 sdmmc;
> +	u8 sdmmc_dma;
> +	u8 pmc_wu;
> +	u8 spi0;
> +	u8 spi1;
> +	u8 spi2;
> +	u8 nand;
> +	u8 nand_dma;
> +	u8 nor;
> +	u8 memstick;
> +	u8 memstick_dma;
> +	u8 uart0;
> +	u8 uart1;
> +	u8 uart2;
> +	u8 uart3;
> +	u8 uart4;
> +	u8 uart5;
> +	u8 i2s;
> +	u8 pcm;
> +	u8 ac97;
> +	u8 timer_match0;
> +	u8 timer_match1;
> +	u8 timer_match2;
> +	u8 timer_match3;
> +	u8 ehci;
> +	u8 uhci;
> +	u8 udc;
> +	u8 udc_dma;
> +	u8 keypad;
> +	u8 ps2mouse;
> +	u8 ps2kbd;
> +	u8 rtc;
> +	u8 rtc_hz;
> +	u8 adc;
> +	u8 cir;
> +	u8 dma0;
> +	u8 dma1;
> +	u8 dma2;
> +	u8 dma3;
> +	u8 dma4;
> +	u8 dma5;
> +	u8 dma6;
> +	u8 dma7;
> +	u8 dma8;
> +	u8 dma9;
> +	u8 dma10;
> +	u8 dma11;
> +	u8 dma12;
> +	u8 dma13;
> +	u8 dma14;
> +	u8 dma15;
> +	u8 irq0;
> +	u8 irq1;
> +	u8 irq2;
> +	u8 irq3;
> +	u8 irq4;
> +	u8 irq5;
> +	u8 irq6;
> +	u8 irq7;
> +	u8 sae;
> +};
> +
> +extern struct wmt_irq_srcs wmt_irqs[] __initdata;
> +extern struct wmt_irq_srcs *wmt_current_irqs __initdata;
> +
> +#endif
> diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
> new file mode 100644
> index 0000000..a129fd1
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/irqs.h
> @@ -0,0 +1,22 @@
> +/*
> + *  arch/arm/mach-vt8500/include/mach/irqs.h
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +/* This value is just to make the core happy, never used otherwise */
> +#define NR_IRQS 128
> diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
> new file mode 100644
> index 0000000..175f914
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/memory.h
> @@ -0,0 +1,28 @@
> +/*
> + *  arch/arm/mach-vt8500/include/mach/memory.h
> + *
> + *  Copyright (C) 2003 ARM Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +#ifndef __ASM_ARCH_MEMORY_H
> +#define __ASM_ARCH_MEMORY_H
> +
> +/*
> + * Physical DRAM offset.
> + */
> +#define PHYS_OFFSET	UL(0x00000000)

If you renamed this to PHYS_DRAM_OFFSET you wouldn't need the comment :-).

> +
> +#endif
> diff --git a/arch/arm/mach-vt8500/include/mach/mmio_regs.h b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
> new file mode 100644
> index 0000000..76439dd
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/mmio_regs.h
> @@ -0,0 +1,90 @@
> +/*
> + *  arch/arm/mach-vt8500/include/mach/mmio_regs.h
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +#ifndef __ASM_ARM_ARCH_MMIO_REGS_H
> +#define __ASM_ARM_ARCH_MMIO_REGS_H
> +
> +#include <linux/init.h>
> +
> +struct wmt_mmio_regs {
> +	unsigned long mmio_regs_start;
> +	unsigned long mmio_regs_length;
> +	unsigned long mmio_regs_virt;
> +	unsigned long ddr;
> +	unsigned long dma;
> +	unsigned long vdma;
> +	unsigned long sflash;
> +	unsigned long ether;
> +	unsigned long cipher;
> +	unsigned long ehci;
> +	unsigned long uhci;
> +	unsigned long pata;
> +	unsigned long ps2;
> +	unsigned long nand;
> +	unsigned long nor;
> +	unsigned long sdmmc;
> +	unsigned long memstick;
> +	unsigned long lcdc;
> +	unsigned long vpu;
> +	unsigned long gov;
> +	unsigned long ge;
> +	unsigned long govr;
> +	unsigned long scl;
> +	unsigned long lcdf;
> +	unsigned long vid;
> +	unsigned long vpp;
> +	unsigned long tsbk;
> +	unsigned long jpegdec;
> +	unsigned long jpegenc;
> +	unsigned long rtc;
> +	unsigned long gpio;
> +	unsigned long scc;
> +	unsigned long pmc;
> +	unsigned long ic0;
> +	unsigned long ic1;
> +	unsigned long uart0;
> +	unsigned long uart1;
> +	unsigned long uart2;
> +	unsigned long uart3;
> +	unsigned long uart4;
> +	unsigned long uart5;
> +	unsigned long pwm;
> +	unsigned long spi0;
> +	unsigned long spi1;
> +	unsigned long spi2;
> +	unsigned long cir;
> +	unsigned long i2c0;
> +	unsigned long i2c1;
> +	unsigned long ac97;
> +	unsigned long pcm;
> +	unsigned long i2s;
> +	unsigned long adc;
> +	unsigned long keypad;
> +};
> +
> +enum {
> +	VT8500_INDEX,
> +	WM8505_INDEX,
> +};
> +
> +extern struct wmt_mmio_regs wmt_regmaps[] __initdata;
> +extern struct wmt_mmio_regs *wmt_current_regs __initdata;
> +
> +#endif
> +
> diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
> new file mode 100644
> index 0000000..d6c757e
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/system.h
> @@ -0,0 +1,18 @@
> +/*
> + * arch/arm/mach-vt8500/include/mach/system.h
> + *
> + */
> +#include <asm/io.h>
> +
> +/* PM Software Reset request register */
> +#define VT8500_PMSR_VIRT	0xf8130060
> +
> +static inline void arch_idle(void)
> +{
> +	cpu_do_idle();
> +}
> +
> +static inline void arch_reset(char mode, const char *cmd)
> +{
> +	writel(1, VT8500_PMSR_VIRT);
> +}
> diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
> new file mode 100644
> index 0000000..8487e4c
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/timex.h
> @@ -0,0 +1,26 @@
> +/*
> + *  arch/arm/mach-vt8500/include/mach/timex.h
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#ifndef MACH_TIMEX_H
> +#define MACH_TIMEX_H
> +
> +#define CLOCK_TICK_RATE		(3000000)
> +
> +#endif /* MACH_TIMEX_H */
> diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
> new file mode 100644
> index 0000000..bb9e2d2
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
> @@ -0,0 +1,37 @@
> +/* arch/arm/mach-vt8500/include/mach/uncompress.h
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * Based on arch/arm/mach-dove/include/mach/uncompress.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#define UART0_PHYS 0xd8200000
> +#include <asm/io.h>
> +
> +static void putc(const char c)
> +{
> +	while (readb(UART0_PHYS + 0x1c) & 0x2)
> +		/* Tx busy, wait and poll */;
> +
> +	writeb(c, UART0_PHYS);
> +}
> +
> +static void flush(void)
> +{
> +}
> +
> +/*
> + * nothing to do
> + */
> +#define arch_decomp_setup()
> +#define arch_decomp_wdog()
> diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
> new file mode 100644
> index 0000000..4642290
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
> @@ -0,0 +1,20 @@
> +/*
> + *  arch/arm/mach-vt8500/include/mach/vmalloc.h
> + *
> + *  Copyright (C) 2000 Russell King.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +#define VMALLOC_END	0xd0000000UL
> diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
> new file mode 100644
> index 0000000..cc7f25e
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
> @@ -0,0 +1,31 @@
> +/*
> + *  VT8500/WM8505 Frame Buffer platform data definitions
> + *
> + *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#ifndef _VT8500FB_H
> +#define _VT8500FB_H
> +
> +#include <linux/fb.h>
> +
> +struct vt8500fb_platform_data {
> +	struct fb_videomode	mode;
> +	__u32			xres_virtual;
> +	__u32			yres_virtual;
> +	__u32			bpp;

Is this struct exported to user space? If not, use u32 rather than __u32.

> +	unsigned long		video_mem_phys;
> +	void			*video_mem_virt;
> +	unsigned long		video_mem_len;
> +};
> +
> +#endif /* _VT8500FB_H */
> diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
> new file mode 100644
> index 0000000..c042e85
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/irq.c
> @@ -0,0 +1,179 @@
> +/*
> + *  arch/arm/mach-vt8500/irq.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +
> +#include <asm/irq.h>
> +
> +#include <mach/mmio_regs.h>
> +#include <mach/irq_defs.h>
> +
> +#define VT8500_IC_DCTR		0x40		/* Destination control
> +						register, 64*u8 */
> +#define VT8500_INT_ENABLE	(1 << 3)
> +#define VT8500_TRIGGER_HIGH	(0 << 4)
> +#define VT8500_TRIGGER_RISING	(1 << 4)
> +#define VT8500_TRIGGER_FALLING	(2 << 4)
> +#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
> +
> +static void __iomem *ic_regbase;
> +static void __iomem *sic_regbase;
> +
> +static void vt8500_irq_mask(unsigned int irq)
> +{
> +	void __iomem *base = ic_regbase;
> +	u8 edge;

Nitpick: blank line between variable declarations and code.

> +	if (irq >= 64) {
> +		base = sic_regbase;
> +		irq -= 64;
> +	}
> +	edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);

What is (3 << 4)? Replace with a #define.

> +	if (edge)
> +		writel(readl(base
> +			+ VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
> +			| (1 << (irq & 0x1f)), base
> +			+ VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));

More unexplained magic numbers.

> +	else
> +		writeb(readb(base
> +			+ VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
> +			base + VT8500_IC_DCTR + irq);
> +}
> +
> +static void vt8500_irq_unmask(unsigned int irq)
> +{
> +	void __iomem *base = ic_regbase;
> +	if (irq >= 64) {
> +		base = sic_regbase;
> +		irq -= 64;
> +	}
> +	writeb(readb(base
> +		+ VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
> +		base + VT8500_IC_DCTR + irq);
> +}
> +
> +static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
> +{
> +	return -EINVAL;
> +}

You don't have to provide this function.

> +static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
> +{
> +	void __iomem *base = ic_regbase;
> +	unsigned int orig_irq = irq;
> +	if (irq >= 64) {
> +		base = sic_regbase;
> +		irq -= 64;
> +	}
> +	switch (flow_type) {
> +	case IRQF_TRIGGER_LOW:
> +		return -EINVAL;
> +	case IRQF_TRIGGER_HIGH:
> +		writeb((readb(base
> +			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
> +			| VT8500_TRIGGER_HIGH, base
> +			+ VT8500_IC_DCTR + irq);
> +		irq_desc[orig_irq].handle_irq = handle_level_irq;
> +		break;
> +	case IRQF_TRIGGER_FALLING:
> +		writeb((readb(base
> +			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
> +			| VT8500_TRIGGER_FALLING, base
> +			+ VT8500_IC_DCTR + irq);
> +		irq_desc[orig_irq].handle_irq = handle_edge_irq;
> +		break;
> +	case IRQF_TRIGGER_RISING:
> +		writeb((readb(base
> +			+ VT8500_IC_DCTR + irq) & ~(3 << 4))
> +			| VT8500_TRIGGER_RISING, base
> +			+ VT8500_IC_DCTR + irq);
> +		irq_desc[orig_irq].handle_irq = handle_edge_irq;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct irq_chip vt8500_irq_chip = {
> +	.name      = "vt8500",
> +	.ack       = vt8500_irq_mask,
> +	.mask      = vt8500_irq_mask,
> +	.unmask    = vt8500_irq_unmask,
> +	.set_wake  = vt8500_irq_set_wake,

Remove .set_wake.

> +	.set_type  = vt8500_irq_set_type,
> +};
> +
> +void __init vt8500_init_irq(void)
> +{
> +	unsigned int i;
> +
> +	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
> +
> +	if (ic_regbase) {
> +		/* Enable rotating priority for IRQ */
> +		writel((1 << 6), ic_regbase + 0x20);
> +		writel(0, ic_regbase + 0x24);
> +
> +		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
> +			/* Disable all interrupts and route them to IRQ */
> +			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
> +
> +			set_irq_chip(i, &vt8500_irq_chip);
> +			set_irq_handler(i, handle_level_irq);
> +			set_irq_flags(i, IRQF_VALID);
> +		}
> +	} else {
> +		printk(KERN_ERR "Unable to remap the Interrupt Controller "
> +				"registers, not enabling IRQs!\n");

printk strings should be on a single line (can be > 80 columns) to make
grepping easier. You could also use the pr_ macros with pr_fmt set.

> +	}
> +}
> +
> +void __init wm8505_init_irq(void)
> +{
> +	unsigned int i;
> +
> +	ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
> +	sic_regbase = ioremap(wmt_current_regs->ic1, SZ_64K);
> +
> +	if (ic_regbase && sic_regbase) {
> +		/* Enable rotating priority for IRQ */
> +		writel((1 << 6), ic_regbase + 0x20);
> +		writel(0, ic_regbase + 0x24);
> +		writel((1 << 6), sic_regbase + 0x20);
> +		writel(0, sic_regbase + 0x24);
> +
> +		for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
> +			/* Disable all interrupts and route them to IRQ */
> +			if (i < 64)
> +				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
> +			else
> +				writeb(0x00, sic_regbase + VT8500_IC_DCTR
> +								+ i - 64);
> +
> +			set_irq_chip(i, &vt8500_irq_chip);
> +			set_irq_handler(i, handle_level_irq);
> +			set_irq_flags(i, IRQF_VALID);
> +		}
> +	} else {
> +		printk(KERN_ERR "Unable to remap the Interrupt Controller "
> +				"registers, not enabling IRQs!\n");
> +	}
> +}
> diff --git a/arch/arm/mach-vt8500/irq_defs.c b/arch/arm/mach-vt8500/irq_defs.c
> new file mode 100644
> index 0000000..b338c04
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/irq_defs.c
> @@ -0,0 +1,173 @@
> +/* linux/arch/arm/mach-vt8500/irq_defs.c
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/init.h>
> +
> +#include <mach/irq_defs.h>
> +#include <mach/mmio_regs.h>
> +
> +struct wmt_irq_srcs *wmt_current_irqs __initdata;
> +
> +struct wmt_irq_srcs wmt_irqs[] __initdata = {
> +	[VT8500_INDEX] = {
> +		.jpegenc	= 0,
> +		.jpegdec	= 1,
> +		.pata		= 3,
> +		.dma		= 5,
> +		.ext0		= 6,
> +		.ext1		= 7,
> +		.ge		= 8,
> +		.gov		= 9,
> +		.ether		= 10,
> +		.mpegts		= 11,
> +		.lcdc		= 12,
> +		.ext2		= 13,
> +		.ext3		= 14,
> +		.ext4		= 15,
> +		.cipher		= 16,
> +		.vpp		= 17,
> +		.i2c1		= 18,
> +		.i2c0		= 19,
> +		.sdmmc		= 20,
> +		.sdmmc_dma	= 21,
> +		.pmc_wu		= 22,
> +		.spi0		= 24,
> +		.spi1		= 25,
> +		.spi2		= 26,
> +		.lcdf		= 27,
> +		.nand		= 28,
> +		.nand_dma	= 29,
> +		.memstick	= 30,
> +		.memstick_dma	= 31,
> +		.uart0		= 32,
> +		.uart1		= 33,
> +		.i2s		= 34,
> +		.pcm		= 35,
> +		.timer_match0	= 36,
> +		.timer_match1	= 37,
> +		.timer_match2	= 38,
> +		.timer_match3	= 39,
> +		.vpu		= 40,
> +		.vid		= 41,
> +		.ac97		= 42,
> +		.ehci		= 43,
> +		.nor		= 44,
> +		.ps2mouse	= 45,
> +		.ps2kbd		= 46,
> +		.uart2		= 47,
> +		.rtc		= 48,
> +		.rtc_hz		= 49,
> +		.uart3		= 50,
> +		.adc		= 51,
> +		.ext5		= 52,
> +		.ext6		= 53,
> +		.ext7		= 54,
> +		.cir		= 55,
> +		.dma0		= 56,
> +		.dma1		= 57,
> +		.dma2		= 58,
> +		.dma3		= 59,
> +		.dma4		= 60,
> +		.dma5		= 61,
> +		.dma6		= 62,
> +		.dma7		= 63,
> +		.nr_irqs	= 64,
> +	},
> +	[WM8505_INDEX] = {
> +		.uhci		= 0,
> +		.ehci		= 1,
> +		.udc_dma	= 2,
> +		.ps2mouse	= 4,
> +		.udc		= 5,
> +		.ext0		= 6,
> +		.ext1		= 7,
> +		.keypad		= 8,
> +		.dma		= 9,
> +		.ether		= 10,
> +		.ext2		= 13,
> +		.ext3		= 14,
> +		.ext4		= 15,
> +		.dma0		= 17,
> +		.i2c1		= 18,
> +		.i2c0		= 19,
> +		.sdmmc		= 20,
> +		.sdmmc_dma	= 21,
> +		.pmc_wu		= 22,
> +		.ps2kbd		= 23,
> +		.spi0		= 24,
> +		.spi1		= 25,
> +		.spi2		= 26,
> +		.dma1		= 27,
> +		.nand		= 28,
> +		.nand_dma	= 29,
> +		.uart5		= 30,
> +		.uart4		= 31,
> +		.uart0		= 32,
> +		.uart1		= 33,
> +		.dma2		= 34,
> +		.i2s		= 35,
> +		.timer_match0	= 36,
> +		.timer_match1	= 37,
> +		.timer_match2	= 38,
> +		.timer_match3	= 39,
> +		.dma3		= 40,
> +		.dma4		= 41,
> +		.ac97		= 42,
> +		.nor		= 44,
> +		.dma5		= 45,
> +		.dma6		= 46,
> +		.uart2		= 47,
> +		.rtc		= 48,
> +		.rtc_hz		= 49,
> +		.uart3		= 50,
> +		.dma7		= 51,
> +		.ext5		= 52,
> +		.ext6		= 53,
> +		.ext7		= 54,
> +		.cir		= 55,
> +		.irq0		= 56,
> +		.irq1		= 57,
> +		.irq2		= 58,
> +		.irq3		= 59,
> +		.irq4		= 60,
> +		.irq5		= 61,
> +		.irq6		= 62,
> +		.irq7		= 63,
> +		.jpegdec	= 65,
> +		.sae		= 66,
> +		.vpu		= 79,
> +		.vpp		= 80,
> +		.vid		= 81,
> +		.spu		= 82,
> +		.pip		= 83,
> +		.ge		= 84,
> +		.gov		= 85,
> +		.dvo		= 86,
> +		.dma8		= 92,
> +		.dma9		= 93,
> +		.dma10		= 94,
> +		.dma11		= 95,
> +		.dma12		= 96,
> +		.dma13		= 97,
> +		.dma14		= 98,
> +		.dma15		= 99,
> +		.govw		= 111,
> +		.govrsdscd	= 112,
> +		.govrsdmif	= 113,
> +		.govrhdscd	= 114,
> +		.govrhdmif	= 115,
> +		.nr_irqs	= 116,
> +	},
> +};
> diff --git a/arch/arm/mach-vt8500/mmio_regs.c b/arch/arm/mach-vt8500/mmio_regs.c
> new file mode 100644
> index 0000000..e9b3264
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/mmio_regs.c
> @@ -0,0 +1,118 @@
> +/* linux/arch/arm/mach-vt8500/mmio_regs.c
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/init.h>
> +
> +#include <mach/mmio_regs.h>
> +
> +struct wmt_mmio_regs *wmt_current_regs __initdata;
> +
> +struct wmt_mmio_regs wmt_regmaps[] __initdata = {
> +	[VT8500_INDEX] = {
> +		.mmio_regs_start	= 0xd8000000,
> +		.mmio_regs_length	= 0x00350000,
> +		.mmio_regs_virt		= 0xf8000000,
> +		.ddr			= 0xd8000000,
> +		.dma			= 0xd8001000,
> +		.sflash			= 0xd8002000,
> +		.ether			= 0xd8004000,
> +		.cipher			= 0xd8006000,
> +		.ehci			= 0xd8007900,
> +		.uhci			= 0xd8007b01,
> +		.pata			= 0xd8008000,
> +		.ps2			= 0xd8008800,
> +		.nand			= 0xd8009000,
> +		.nor			= 0xd8009400,
> +		.sdmmc			= 0xd800a000,
> +		.memstick		= 0xd800b400,
> +		.lcdc			= 0xd800e400,
> +		.vpu			= 0xd8050000,
> +		.gov			= 0xd8050300,
> +		.ge			= 0xd8050400,
> +		.lcdf			= 0xd8050900,
> +		.vid			= 0xd8050a00,
> +		.vpp			= 0xd8050b00,
> +		.tsbk			= 0xd80f4000,
> +		.jpegdec		= 0xd80fe000,
> +		.jpegenc		= 0xd80ff000,
> +		.rtc			= 0xd8100000,
> +		.gpio			= 0xd8110000,
> +		.scc			= 0xd8120000,
> +		.pmc			= 0xd8130000,
> +		.ic0			= 0xd8140000,
> +		.uart0			= 0xd8200000,
> +		.uart2			= 0xd8210000,
> +		.pwm			= 0xd8220000,
> +		.spi0			= 0xd8240000,
> +		.spi1			= 0xd8250000,
> +		.cir			= 0xd8270000,
> +		.i2c0			= 0xd8280000,
> +		.ac97			= 0xd8290000,
> +		.spi2			= 0xd82a0000,
> +		.uart1			= 0xd82b0000,
> +		.uart3			= 0xd82c0000,
> +		.pcm			= 0xd82d0000,
> +		.i2c1			= 0xd8320000,
> +		.i2s			= 0xd8330000,
> +		.adc			= 0xd8340000,
> +	},
> +	[WM8505_INDEX] = {
> +		.mmio_regs_start	= 0xd8000000,
> +		.mmio_regs_length	= 0x00390000,
> +		.mmio_regs_virt		= 0xf8000000,
> +		.ddr			= 0xd8000400,
> +		.dma			= 0xd8001800,
> +		.vdma			= 0xd8001c00,
> +		.sflash			= 0xd8002000,
> +		.ether			= 0xd8004000,
> +		.cipher			= 0xd8006000,
> +		.ehci			= 0xd8007100,
> +		.uhci			= 0xd8007301,
> +		.ps2			= 0xd8008800,
> +		.nand			= 0xd8009000,
> +		.nor			= 0xd8009400,
> +		.sdmmc			= 0xd800a000,
> +		.vpu			= 0xd8050000,
> +		.gov			= 0xd8050300,
> +		.ge			= 0xd8050400,
> +		.govr			= 0xd8050800,
> +		.vid			= 0xd8050a00,
> +		.scl			= 0xd8050d00,
> +		.vpp			= 0xd8050f00,
> +		.jpegdec		= 0xd80fe000,
> +		.rtc			= 0xd8100000,
> +		.gpio			= 0xd8110000,
> +		.scc			= 0xd8120000,
> +		.pmc			= 0xd8130000,
> +		.ic0			= 0xd8140000,
> +		.ic1			= 0xd8150000,
> +		.uart0			= 0xd8200000,
> +		.uart2			= 0xd8210000,
> +		.pwm			= 0xd8220000,
> +		.spi0			= 0xd8240000,
> +		.spi1			= 0xd8250000,
> +		.keypad			= 0xd8260000,
> +		.cir			= 0xd8270000,
> +		.i2c0			= 0xd8280000,
> +		.ac97			= 0xd8290000,
> +		.spi2			= 0xd82a0000,
> +		.uart1			= 0xd82b0000,
> +		.uart3			= 0xd82c0000,
> +		.i2c1			= 0xd8320000,
> +		.i2s			= 0xd8330000,
> +		.uart4			= 0xd8370000,
> +		.uart5			= 0xd8380000,
> +	},
> +};
> diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
> new file mode 100644
> index 0000000..d1356a1
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/pwm.c

I'm not sure what the state of the various efforts to provide a common
pwm framework are, but you may want to check.

> @@ -0,0 +1,254 @@
> +/*
> + * arch/arm/mach-vt8500/pwm.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/pwm.h>
> +
> +#include <asm/div64.h>
> +
> +#define VT8500_NR_PWMS 4
> +
> +struct pwm_device {
> +	struct list_head	node;
> +	struct platform_device	*pdev;
> +
> +	const char	*label;
> +
> +	void __iomem	*regbase;
> +
> +	unsigned int	use_count;
> +	unsigned int	pwm_id;
> +};
> +
> +static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
> +{
> +	int loops = 1000;
> +	while ((readb(reg) & bitmask) && --loops)
> +		cpu_relax();

Ugh. If you are going to busy wait, can't you delay for a known amount
of time? Even better, can this be replaced with wait_event or some
equivalent?

> +}
> +
> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
> +{
> +	unsigned long long c;
> +	unsigned long period_cycles, prescale, pv, dc;
> +
> +	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
> +		return -EINVAL;
> +
> +	c = 25000000/2; /* wild guess --- need to implement clocks */
> +	c = c * period_ns;
> +	do_div(c, 1000000000);
> +	period_cycles = c;

This looks like it could be reworked to remove the do_div call.

> +
> +	if (period_cycles < 1)
> +		period_cycles = 1;
> +	prescale = (period_cycles - 1) / 4096;
> +	pv = period_cycles / (prescale + 1) - 1;
> +	if (pv > 4095)
> +		pv = 4095;
> +
> +	if (prescale > 1023)
> +		return -EINVAL;
> +
> +	dc = pv * duty_ns / period_ns;
> +
> +	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
> +	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
> +
> +	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
> +	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
> +
> +	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
> +	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(pwm_config);
> +
> +int pwm_enable(struct pwm_device *pwm)
> +{
> +	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
> +	writel(5, pwm->regbase + (pwm->pwm_id << 4));
> +	return 0;
> +}
> +EXPORT_SYMBOL(pwm_enable);
> +
> +void pwm_disable(struct pwm_device *pwm)
> +{
> +	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
> +	writel(0, pwm->regbase + (pwm->pwm_id << 4));
> +}
> +EXPORT_SYMBOL(pwm_disable);
> +
> +static DEFINE_MUTEX(pwm_lock);
> +static LIST_HEAD(pwm_list);

These should be at the top of the file.

> +struct pwm_device *pwm_request(int pwm_id, const char *label)
> +{
> +	struct pwm_device *pwm;
> +	int found = 0;
> +
> +	mutex_lock(&pwm_lock);
> +
> +	list_for_each_entry(pwm, &pwm_list, node) {
> +		if (pwm->pwm_id == pwm_id) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +
> +	if (found) {
> +		if (pwm->use_count == 0) {
> +			pwm->use_count++;
> +			pwm->label = label;
> +		} else
> +			pwm = ERR_PTR(-EBUSY);
> +	} else
> +		pwm = ERR_PTR(-ENOENT);
> +
> +	mutex_unlock(&pwm_lock);
> +	return pwm;
> +}

Maybe a bit clearer and more concise like this? Also replaces -ENOENT
(No such file or directory) with -ENODEV (No such device):

	pwm = ERR_PTR(-ENODEV);
	mutex_lock(&pwm_lock);

	list_for_each_entry(pwm, &pwm_list, node) {
		if (pwm->pwm_id == pwm_id) {
			if (pwm->use_count != 0) {
				pwm = ERR_PTR(-EBUSY);
				break;
			}

			pwm->use_count++;
			pwm->label = label;
			break;
		}
	}

	mutex_unlock(&pwm_lock);
	return pwm;	

> +EXPORT_SYMBOL(pwm_request);
> +
> +void pwm_free(struct pwm_device *pwm)
> +{
> +	mutex_lock(&pwm_lock);
> +
> +	if (pwm->use_count) {
> +		pwm->use_count--;
> +		pwm->label = NULL;
> +	} else
> +		pr_warning("PWM device already freed\n");
> +

Nitpick: Single line else should have braces if the if has braces

> +	mutex_unlock(&pwm_lock);
> +}
> +EXPORT_SYMBOL(pwm_free);
> +
> +static inline void __add_pwm(struct pwm_device *pwm)
> +{
> +	mutex_lock(&pwm_lock);
> +	list_add_tail(&pwm->node, &pwm_list);
> +	mutex_unlock(&pwm_lock);
> +}
> +
> +static int __devinit pwm_probe(struct platform_device *pdev)
> +{
> +	struct pwm_device *pwms;
> +	struct resource *r;
> +	int ret = 0;
> +	int i;
> +
> +	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
> +	if (pwms == NULL) {
> +		dev_err(&pdev->dev, "failed to allocate memory\n");
> +		return -ENOMEM;
> +	}

Devices should ideally be a single entity, so one platform device per pwm.

> +
> +	for (i = 0; i < VT8500_NR_PWMS; i++) {
> +		pwms[i].use_count = 0;
> +		pwms[i].pwm_id = i;
> +		pwms[i].pdev = pdev;
> +	}
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (r == NULL) {
> +		dev_err(&pdev->dev, "no memory resource defined\n");
> +		ret = -ENODEV;
> +		goto err_free;
> +	}
> +
> +	r = request_mem_region(r->start, resource_size(r), pdev->name);
> +	if (r == NULL) {
> +		dev_err(&pdev->dev, "failed to request memory resource\n");
> +		ret = -EBUSY;
> +		goto err_free;
> +	}
> +
> +	pwms[0].regbase = ioremap(r->start, resource_size(r));
> +	if (pwms[0].regbase == NULL) {
> +		dev_err(&pdev->dev, "failed to ioremap() registers\n");
> +		ret = -ENODEV;
> +		goto err_free_mem;
> +	}
> +
> +	for (i = 1; i < VT8500_NR_PWMS; i++)
> +		pwms[i].regbase = pwms[0].regbase;
> +
> +	for (i = 0; i < VT8500_NR_PWMS; i++)
> +		__add_pwm(&pwms[i]);
> +
> +	platform_set_drvdata(pdev, pwms);
> +	return 0;
> +
> +err_free_mem:
> +	release_mem_region(r->start, resource_size(r));
> +err_free:
> +	kfree(pwms);
> +	return ret;
> +}
> +
> +static int __devexit pwm_remove(struct platform_device *pdev)
> +{
> +	struct pwm_device *pwms;
> +	struct resource *r;
> +	int i;
> +
> +	pwms = platform_get_drvdata(pdev);
> +	if (pwms == NULL)
> +		return -ENODEV;
> +
> +	mutex_lock(&pwm_lock);
> +
> +	for (i = 0; i < VT8500_NR_PWMS; i++)
> +		list_del(&pwms[i].node);
> +	mutex_unlock(&pwm_lock);
> +
> +	iounmap(pwms[0].regbase);
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	release_mem_region(r->start, resource_size(r));
> +
> +	kfree(pwms);
> +	return 0;
> +}
> +
> +static struct platform_driver pwm_driver = {
> +	.driver		= {
> +		.name	= "vt8500-pwm",
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= pwm_probe,
> +	.remove		= __devexit_p(pwm_remove),
> +};
> +
> +static int __init pwm_init(void)
> +{
> +	return platform_driver_register(&pwm_driver);
> +}
> +arch_initcall(pwm_init);
> +
> +static void __exit pwm_exit(void)
> +{
> +	platform_driver_unregister(&pwm_driver);
> +}
> +module_exit(pwm_exit);
> +
> +MODULE_LICENSE("GPL");
> diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
> new file mode 100644
> index 0000000..ab4f7aa
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/timer.c
> @@ -0,0 +1,154 @@
> +/*
> + *  arch/arm/mach-vt8500/timer.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/clocksource.h>
> +#include <linux/clockchips.h>
> +
> +#include <asm/mach/time.h>
> +
> +#include <mach/mmio_regs.h>
> +#include <mach/irq_defs.h>
> +
> +#define VT8500_TIMER_OFFSET	0x0100
> +#define TIMER_MATCH_VAL		0x0000
> +#define TIMER_COUNT_VAL		0x0010
> +#define TIMER_STATUS_VAL	0x0014
> +#define TIMER_IER_VAL		0x001c		/* interrupt enable */
> +#define TIMER_CTRL_VAL		0x0020
> +#define TIMER_AS_VAL		0x0024		/* access status */
> +#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
> +#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
> +#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
> +#define VT8500_TIMER_HZ		3000000
> +
> +static void __iomem *regbase;
> +
> +static cycle_t vt8500_timer_read(struct clocksource *cs)
> +{
> +	int loops = 1000;
> +	writel(3, regbase + TIMER_CTRL_VAL);
> +	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
> +						&& --loops)
> +		cpu_relax();

More loop counting? Surely there is a better solution?

> +	return readl(regbase + TIMER_COUNT_VAL);
> +}
> +
> +struct clocksource clocksource = {
> +	.name           = "vt8500_timer",
> +	.rating         = 200,
> +	.read           = vt8500_timer_read,
> +	.mask           = CLOCKSOURCE_MASK(32),
> +	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
> +};
> +
> +static int vt8500_timer_set_next_event(unsigned long cycles,
> +				    struct clock_event_device *evt)
> +{
> +	int loops = 1000;
> +	cycle_t alarm = clocksource.read(&clocksource) + cycles;
> +	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
> +						&& --loops)
> +		cpu_relax();
> +	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
> +
> +	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
> +		return -ETIME;
> +
> +	writel(1, regbase + TIMER_IER_VAL);
> +
> +	return 0;
> +}
> +
> +static void vt8500_timer_set_mode(enum clock_event_mode mode,
> +			      struct clock_event_device *evt)
> +{
> +	switch (mode) {
> +	case CLOCK_EVT_MODE_RESUME:
> +	case CLOCK_EVT_MODE_PERIODIC:
> +		break;
> +	case CLOCK_EVT_MODE_ONESHOT:
> +	case CLOCK_EVT_MODE_UNUSED:
> +	case CLOCK_EVT_MODE_SHUTDOWN:
> +		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
> +			regbase + TIMER_CTRL_VAL);
> +		writel(0, regbase + TIMER_IER_VAL);
> +		break;
> +	}
> +}
> +
> +struct clock_event_device clockevent = {
> +	.name           = "vt8500_timer",
> +	.features       = CLOCK_EVT_FEAT_ONESHOT,
> +	.rating         = 200,
> +	.set_next_event = vt8500_timer_set_next_event,
> +	.set_mode       = vt8500_timer_set_mode,
> +};
> +
> +static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
> +{
> +	struct clock_event_device *evt = dev_id;
> +	writel(0xf, regbase + TIMER_STATUS_VAL);
> +	evt->event_handler(evt);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +struct irqaction irq = {
> +	.name    = "vt8500_timer",
> +	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
> +	.handler = vt8500_timer_interrupt,
> +	.dev_id  = &clockevent,
> +};
> +
> +static void __init vt8500_timer_init(void)
> +{
> +	regbase = ioremap(wmt_current_regs->pmc + VT8500_TIMER_OFFSET, 0x28);
> +	if (!regbase)
> +		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO "
> +				"registers\n");
> +
> +	writel(1, regbase + TIMER_CTRL_VAL);
> +	writel(0xf, regbase + TIMER_STATUS_VAL);
> +	writel(~0, regbase + TIMER_MATCH_VAL);
> +
> +	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
> +		printk(KERN_ERR "vt8500_timer_init: clocksource_register "
> +			"failed for %s\n", clocksource.name);
> +
> +	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
> +
> +	/* copy-pasted from mach-msm; no idea */
> +	clockevent.max_delta_ns =
> +		clockevent_delta2ns(0xf0000000, &clockevent);
> +	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
> +	clockevent.cpumask = cpumask_of(0);
> +
> +	if (setup_irq(wmt_current_irqs->timer_match0, &irq))
> +		printk(KERN_ERR "vt8500_timer_init: setup_irq "
> +			"failed for %s\n", clockevent.name);
> +	clockevents_register_device(&clockevent);
> +}
> +
> +struct sys_timer vt8500_timer = {
> +	.init = vt8500_timer_init
> +};
> diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
> new file mode 100644
> index 0000000..181ad6f
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/wm8505_7in.c
> @@ -0,0 +1,81 @@
> +/*
> + *  arch/arm/mach-vt8500/wm8505_7in.c
> + *
> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/io.h>
> +#include <linux/pm.h>
> +
> +#include <asm/mach-types.h>
> +#include <asm/mach/arch.h>
> +
> +#include <mach/mmio_regs.h>
> +#include "devices.h"
> +
> +static void __iomem *pmc_hiber;
> +
> +static struct platform_device *devices[] __initdata = {
> +	&vt8500_device_uart0,
> +	&vt8500_device_ehci,
> +	&vt8500_device_wm8505_fb,
> +	&vt8500_device_ge_rops,
> +	&vt8500_device_pwm,
> +	&vt8500_device_pwmbl,
> +	&vt8500_device_rtc,
> +};
> +
> +static void vt8500_power_off(void)
> +{
> +	local_irq_disable();

Is this necessary?

> +	writew(5, pmc_hiber);
> +	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
> +}
> +
> +void __init wm8505_7in_init(void)
> +{
> +#ifdef CONFIG_FB_WM8505
> +	void __iomem *gpio_mux_reg = ioremap(wmt_current_regs->gpio
> +					     + 0x200, 4);
> +	if (gpio_mux_reg) {
> +		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
> +		iounmap(gpio_mux_reg);
> +	} else {
> +		printk(KERN_ERR "Could not remap the GPIO mux register, "
> +				"display may not work properly!\n");
> +	}
> +#endif
> +	pmc_hiber = ioremap(wmt_current_regs->pmc + 0x12, 2);
> +	if (pmc_hiber)
> +		pm_power_off = &vt8500_power_off;
> +	else
> +		printk(KERN_ERR "PMC Hibernation register could not be "
> +				"remapped, not enabling power off!\n");
> +
> +	wmt_set_resources();
> +	platform_add_devices(devices, ARRAY_SIZE(devices));
> +	vt8500_gpio_init();
> +}
> +
> +MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
> +	.boot_params	= 0x00000100,
> +	.reserve	= wm8505_reserve_mem,
> +	.map_io		= wm8505_map_io,
> +	.init_irq	= wm8505_init_irq,
> +	.timer		= &vt8500_timer,
> +	.init_machine	= wm8505_7in_init,
> +MACHINE_END

~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 20:50                   ` Ryan Mallon
@ 2010-12-20 21:48                     ` Alexey Charkov
  2010-12-20 22:23                       ` Ryan Mallon
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-12-20 21:48 UTC (permalink / raw)
  To: Ryan Mallon
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

2010/12/20 Ryan Mallon <ryan@bluewatersys.com>:
> On 12/21/2010 08:54 AM, Alexey Charkov wrote:
>> This adds support for the family of Systems-on-Chip produced initially
>> by VIA and now its subsidiary WonderMedia that have recently become
>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>
>> Support is included for both VT8500 and WM8505, selectable by a
>> configuration switch at kernel build time.
>>
>> Included are basic machine initialization files, register and
>> interrupt definitions, support for the on-chip interrupt controller,
>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>> pulse-width-modulated outputs control, as well as platform device
>> configurations for the specific drivers implemented elsewhere.
>>
>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>
> Hi Alexey,
>
> Quick review below.
>
> ~Ryan
>

<snip>

>> +static int __init panel_setup(char *str)
>> +{
>> +     int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(panels); i++) {
>> +             int len = strlen(panels[i].mode.name);
>> +
>> +             if (memcmp(panels[i].mode.name, str, len) == 0) {
>
> Should be strcmp. If the length of str is less than panels[i].mode.name
> then you will buffer overrun.
>

Ok, will change this.

>> +                     current_panel_idx = i;
>> +                     break;
>> +             }
>> +     }
>> +     return 0;
>> +}
>> +
>> +early_param("panel", panel_setup);
>> +
>> +static inline void preallocate_fb(struct vt8500fb_platform_data *p,
>> +                               unsigned long align) {
>> +     p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
>> +                     (p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
>> +                                     (8 / p->bpp) + 1));
>> +     p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
>> +                                                       align);
>> +     p->video_mem_virt = phys_to_virt(p->video_mem_phys);
>> +}
>> +
>> +static struct resource resources_uart0[] = {
>> +     [0] = {
>> +             .flags  = IORESOURCE_MEM,
>> +     },
>> +     [1] = {
>> +             .flags  = IORESOURCE_IRQ,
>> +     },
>> +};
>
> What's happening here? Does something else fill these in? If so, there
> should be a comment to that effect.
>

Ok, will add a comment.

<snip>
>> +void __init wmt_set_resources(void)
>> +{
>> +     resources_lcdc[0].start = wmt_current_regs->lcdc;
>> +     resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
>> +     resources_lcdc[1].start = wmt_current_irqs->lcdc;
>> +     resources_lcdc[1].end = wmt_current_irqs->lcdc;
>
> Ah, this makes more sense. But why have all the indirection? The
> wmt_regmaps table could just be replaced with #defines and then have
> separate device files for the VT8500 and the WM8505. This would also
> make clearer which variants have which peripherals.
>

This was the way I implemented it originally. However, Arnd made quite
a valid suggestion to allow runtime selection of the chip variant,
thus registers and interrupts need to be held in an indexed data type
instead of just compile-time macros. In addition, there is now some
overall movement towards unification of binary kernel images for
different ARM variants (as far as I can see), so this would be
required in any case.

Furthermore, as with many unbranded Chinese products, it's somewhat
difficult to reliably determine the exact chip version used in your
netbook without disassembling it. Reading a hardware register for
identification is easier :)

>> +     resources_wm8505_fb[0].start = wmt_current_regs->govr;
>> +     resources_wm8505_fb[0].end = wmt_current_regs->govr + 512 - 1;
>> +
>> +     resources_uart0[0].start = wmt_current_regs->uart0;
>> +     resources_uart0[0].end = wmt_current_regs->uart0 + 0x103f;
>> +     resources_uart0[1].start = wmt_current_irqs->uart0;
>> +     resources_uart0[1].end = wmt_current_irqs->uart0;
>> +     resources_uart1[0].start = wmt_current_regs->uart1;
>> +     resources_uart1[0].end = wmt_current_regs->uart1 + 0x103f;
>> +     resources_uart1[1].start = wmt_current_irqs->uart1;
>> +     resources_uart1[1].end = wmt_current_irqs->uart1;
>> +     resources_uart2[0].start = wmt_current_regs->uart2;
>> +     resources_uart2[0].end = wmt_current_regs->uart2 + 0x103f;
>> +     resources_uart2[1].start = wmt_current_irqs->uart2;
>> +     resources_uart2[1].end = wmt_current_irqs->uart2;
>> +     resources_uart3[0].start = wmt_current_regs->uart3;
>> +     resources_uart3[0].end = wmt_current_regs->uart3 + 0x103f;
>> +     resources_uart3[1].start = wmt_current_irqs->uart3;
>> +     resources_uart3[1].end = wmt_current_irqs->uart3;
>> +     resources_uart4[0].start = wmt_current_regs->uart4;
>> +     resources_uart4[0].end = wmt_current_regs->uart4 + 0x103f;
>> +     resources_uart4[1].start = wmt_current_irqs->uart4;
>> +     resources_uart4[1].end = wmt_current_irqs->uart4;
>> +     resources_uart5[0].start = wmt_current_regs->uart5;
>> +     resources_uart5[0].end = wmt_current_regs->uart5 + 0x103f;
>> +     resources_uart5[1].start = wmt_current_irqs->uart5;
>> +     resources_uart5[1].end = wmt_current_irqs->uart5;
>> +
>> +     resources_ehci[0].start = wmt_current_regs->ehci;
>> +     resources_ehci[0].end = wmt_current_regs->ehci + 512 - 1;
>> +     resources_ehci[1].start = wmt_current_irqs->ehci;
>> +     resources_ehci[1].end = wmt_current_irqs->ehci;
>
> There is a mix of hex and decimal constants here and exact sizes and
> sizes with 1 subtracted. Please be consistent.
>

Ok, will convert to hex with exact sizes.

>> +     resources_ge_rops[0].start = wmt_current_regs->ge;
>> +     resources_ge_rops[0].end = wmt_current_regs->ge + 0xff;
>> +
>> +     resources_pwm[0].start = wmt_current_regs->pwm;
>> +     resources_pwm[0].end = wmt_current_regs->pwm + 0x43;
>> +
>> +     resources_rtc[0].start = wmt_current_regs->rtc;
>> +     resources_rtc[0].end = wmt_current_regs->rtc + 0x2c - 1;
>> +     resources_rtc[1].start = wmt_current_irqs->rtc;
>> +     resources_rtc[1].end = wmt_current_irqs->rtc;
>> +     resources_rtc[2].start = wmt_current_irqs->rtc_hz;
>> +     resources_rtc[2].end = wmt_current_irqs->rtc_hz;
>> +}
>> +
>> +void __init vt8500_map_io(void)
>> +{
>> +     wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
>> +     wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
>> +
>> +     vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
>> +     vt8500_io_desc[0].pfn =
>> +                     __phys_to_pfn(wmt_current_regs->mmio_regs_start);
>> +     vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
>> +
>> +     iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
>> +}
>> +
>> +void __init wm8505_map_io(void)
>> +{
>> +     wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
>> +     wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
>> +
>> +     vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
>> +     vt8500_io_desc[0].pfn =
>> +                     __phys_to_pfn(wmt_current_regs->mmio_regs_start);
>> +     vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
>> +
>> +     iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
>> +}
>
> Separate files. If more variants get added, this file will become
> unwieldy very quickly.
>

Is it really worthwhile to create separate files for single 12-line
functions? After all, WonderMedia does not release new chips every now
and then :)

>> +
>> +void __init vt8500_reserve_mem(void)
>> +{
>> +#ifdef CONFIG_FB_VT8500
>> +     panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
>> +     preallocate_fb(&panels[current_panel_idx], SZ_4M);
>> +     vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
>> +#endif
>> +}
>
> Not sure if this should exist in the platform code or the framebuffer
> driver. In the latter case it would automatically be CONFIG_FB_VT8500
> and the platform_data can still be set in the platform code. Is there a
> reason for this not to be in the framebuffer driver?
>

I can't reserve memory via memblock from the driver, and usual runtime
allocation functions can't handle it (need alignment to 4 megabytes in
8500, framebuffer sizes exceed 4 megabytes in 8505).

<snip>

>> +static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
>> +                                  unsigned offset)
>> +{
>> +     struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
>> +
>> +     writel(readl(regbase + vt8500_chip->regoff) |
>> +             (1 << vt8500_chip->shift << offset),
>> +             regbase + vt8500_chip->regoff);
>
> This would be more readable as:
>
>        unsigned val;
>
>        val  = readl(regbase + vt8500_chop->regoff);
>        val |= 1 << vt8500_chop->shift << offset;
>        writel(val, regbase + vt8500_chip->regoff);
>
> It's much clearer what is actually being done. Same goes for other
> functions in this file.
>

Ok.

<snip>

>> +static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
>> +     VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
>> +     VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
>> +     VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
>> +     VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
>> +     VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
>> +     VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
>
> Nitpick: Line up the columns to make these mpore readable.
>

That's true, will use tabs instead.

<snip>

>> diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
>> new file mode 100644
>> index 0000000..8dd55c8
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/include/mach/io.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + *  arch/arm/mach-vt8500/include/mach/io.h
>> + *
>> + *  Copyright (C) 2010 Alexey Charkov
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
>> + */
>> +#ifndef __ASM_ARM_ARCH_IO_H
>> +#define __ASM_ARM_ARCH_IO_H
>> +
>> +#define IO_SPACE_LIMIT 0xffff
>> +
>> +#define __io(a)              ((void __iomem *)((a) + 0xf0000000))
>
> Should use __typesafe_io.
>

Ok.

<snip>

>> diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
>> new file mode 100644
>> index 0000000..175f914
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/include/mach/memory.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + *  arch/arm/mach-vt8500/include/mach/memory.h
>> + *
>> + *  Copyright (C) 2003 ARM Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
>> + */
>> +#ifndef __ASM_ARCH_MEMORY_H
>> +#define __ASM_ARCH_MEMORY_H
>> +
>> +/*
>> + * Physical DRAM offset.
>> + */
>> +#define PHYS_OFFSET  UL(0x00000000)
>
> If you renamed this to PHYS_DRAM_OFFSET you wouldn't need the comment :-).
>

I'm not the one who chooses :)

<snip>

>> diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
>> new file mode 100644
>> index 0000000..cc7f25e
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
>> @@ -0,0 +1,31 @@
>> +/*
>> + *  VT8500/WM8505 Frame Buffer platform data definitions
>> + *
>> + *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef _VT8500FB_H
>> +#define _VT8500FB_H
>> +
>> +#include <linux/fb.h>
>> +
>> +struct vt8500fb_platform_data {
>> +     struct fb_videomode     mode;
>> +     __u32                   xres_virtual;
>> +     __u32                   yres_virtual;
>> +     __u32                   bpp;
>
> Is this struct exported to user space? If not, use u32 rather than __u32.
>

No it's not, will change.

>> +     unsigned long           video_mem_phys;
>> +     void                    *video_mem_virt;
>> +     unsigned long           video_mem_len;
>> +};
>> +
>> +#endif /* _VT8500FB_H */
>> diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
>> new file mode 100644
>> index 0000000..c042e85
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/irq.c
>> @@ -0,0 +1,179 @@
>> +/*
>> + *  arch/arm/mach-vt8500/irq.c
>> + *
>> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
>> + */
>> +
>> +#include <linux/io.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +
>> +#include <asm/irq.h>
>> +
>> +#include <mach/mmio_regs.h>
>> +#include <mach/irq_defs.h>
>> +
>> +#define VT8500_IC_DCTR               0x40            /* Destination control
>> +                                             register, 64*u8 */
>> +#define VT8500_INT_ENABLE    (1 << 3)
>> +#define VT8500_TRIGGER_HIGH  (0 << 4)
>> +#define VT8500_TRIGGER_RISING        (1 << 4)
>> +#define VT8500_TRIGGER_FALLING       (2 << 4)
>> +#define VT8500_IC_STATUS     0x80            /* Interrupt status, 2*u32 */
>> +
>> +static void __iomem *ic_regbase;
>> +static void __iomem *sic_regbase;
>> +
>> +static void vt8500_irq_mask(unsigned int irq)
>> +{
>> +     void __iomem *base = ic_regbase;
>> +     u8 edge;
>
> Nitpick: blank line between variable declarations and code.
>

Ok.

>> +     if (irq >= 64) {
>> +             base = sic_regbase;
>> +             irq -= 64;
>> +     }
>> +     edge = readb(base + VT8500_IC_DCTR + irq) & (3 << 4);
>
> What is (3 << 4)? Replace with a #define.
>
>> +     if (edge)
>> +             writel(readl(base
>> +                     + VT8500_IC_STATUS + (irq < 32 ? 0 : 4))
>> +                     | (1 << (irq & 0x1f)), base
>> +                     + VT8500_IC_STATUS + (irq & 0x20 ? 4 : 0));
>
> More unexplained magic numbers.
>

Ok, will add macros.

>> +     else
>> +             writeb(readb(base
>> +                     + VT8500_IC_DCTR + irq) & ~VT8500_INT_ENABLE,
>> +                     base + VT8500_IC_DCTR + irq);
>> +}
>> +
>> +static void vt8500_irq_unmask(unsigned int irq)
>> +{
>> +     void __iomem *base = ic_regbase;
>> +     if (irq >= 64) {
>> +             base = sic_regbase;
>> +             irq -= 64;
>> +     }
>> +     writeb(readb(base
>> +             + VT8500_IC_DCTR + irq) | VT8500_INT_ENABLE,
>> +             base + VT8500_IC_DCTR + irq);
>> +}
>> +
>> +static int vt8500_irq_set_wake(unsigned int irq, unsigned int on)
>> +{
>> +     return -EINVAL;
>> +}
>
> You don't have to provide this function.
>

In fact, there is some wakeup functionality, but it's not yet
implemented. I could re-add the functions when some useful code
appears, though.

>> +static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
>> +{
>> +     void __iomem *base = ic_regbase;
>> +     unsigned int orig_irq = irq;
>> +     if (irq >= 64) {
>> +             base = sic_regbase;
>> +             irq -= 64;
>> +     }
>> +     switch (flow_type) {
>> +     case IRQF_TRIGGER_LOW:
>> +             return -EINVAL;
>> +     case IRQF_TRIGGER_HIGH:
>> +             writeb((readb(base
>> +                     + VT8500_IC_DCTR + irq) & ~(3 << 4))
>> +                     | VT8500_TRIGGER_HIGH, base
>> +                     + VT8500_IC_DCTR + irq);
>> +             irq_desc[orig_irq].handle_irq = handle_level_irq;
>> +             break;
>> +     case IRQF_TRIGGER_FALLING:
>> +             writeb((readb(base
>> +                     + VT8500_IC_DCTR + irq) & ~(3 << 4))
>> +                     | VT8500_TRIGGER_FALLING, base
>> +                     + VT8500_IC_DCTR + irq);
>> +             irq_desc[orig_irq].handle_irq = handle_edge_irq;
>> +             break;
>> +     case IRQF_TRIGGER_RISING:
>> +             writeb((readb(base
>> +                     + VT8500_IC_DCTR + irq) & ~(3 << 4))
>> +                     | VT8500_TRIGGER_RISING, base
>> +                     + VT8500_IC_DCTR + irq);
>> +             irq_desc[orig_irq].handle_irq = handle_edge_irq;
>> +             break;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static struct irq_chip vt8500_irq_chip = {
>> +     .name      = "vt8500",
>> +     .ack       = vt8500_irq_mask,
>> +     .mask      = vt8500_irq_mask,
>> +     .unmask    = vt8500_irq_unmask,
>> +     .set_wake  = vt8500_irq_set_wake,
>
> Remove .set_wake.
>

Ok.

>> +     .set_type  = vt8500_irq_set_type,
>> +};
>> +
>> +void __init vt8500_init_irq(void)
>> +{
>> +     unsigned int i;
>> +
>> +     ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
>> +
>> +     if (ic_regbase) {
>> +             /* Enable rotating priority for IRQ */
>> +             writel((1 << 6), ic_regbase + 0x20);
>> +             writel(0, ic_regbase + 0x24);
>> +
>> +             for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
>> +                     /* Disable all interrupts and route them to IRQ */
>> +                     writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
>> +
>> +                     set_irq_chip(i, &vt8500_irq_chip);
>> +                     set_irq_handler(i, handle_level_irq);
>> +                     set_irq_flags(i, IRQF_VALID);
>> +             }
>> +     } else {
>> +             printk(KERN_ERR "Unable to remap the Interrupt Controller "
>> +                             "registers, not enabling IRQs!\n");
>
> printk strings should be on a single line (can be > 80 columns) to make
> grepping easier. You could also use the pr_ macros with pr_fmt set.
>

Well, checkpatch.pl complained about that in the first place, so I
split the line. Should I merge them back in all instances?

<snip>

>> diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
>> new file mode 100644
>> index 0000000..d1356a1
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/pwm.c
>
> I'm not sure what the state of the various efforts to provide a common
> pwm framework are, but you may want to check.
>

I did before starting to write this code, found nothing.

>> @@ -0,0 +1,254 @@
>> +/*
>> + * arch/arm/mach-vt8500/pwm.c
>> + *
>> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/pwm.h>
>> +
>> +#include <asm/div64.h>
>> +
>> +#define VT8500_NR_PWMS 4
>> +
>> +struct pwm_device {
>> +     struct list_head        node;
>> +     struct platform_device  *pdev;
>> +
>> +     const char      *label;
>> +
>> +     void __iomem    *regbase;
>> +
>> +     unsigned int    use_count;
>> +     unsigned int    pwm_id;
>> +};
>> +
>> +static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
>> +{
>> +     int loops = 1000;
>> +     while ((readb(reg) & bitmask) && --loops)
>> +             cpu_relax();
>
> Ugh. If you are going to busy wait, can't you delay for a known amount
> of time? Even better, can this be replaced with wait_event or some
> equivalent?
>

The delay should be on the order of several bus cycles, where udelay
actually busy-waits, too. wait_event would be longer than that to set
up, and there is no associated interrupt.

>> +}
>> +
>> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>> +{
>> +     unsigned long long c;
>> +     unsigned long period_cycles, prescale, pv, dc;
>> +
>> +     if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
>> +             return -EINVAL;
>> +
>> +     c = 25000000/2; /* wild guess --- need to implement clocks */
>> +     c = c * period_ns;
>> +     do_div(c, 1000000000);
>> +     period_cycles = c;
>
> This looks like it could be reworked to remove the do_div call.
>

I just followed PXA implementation in this regard. Are there any
specific suggestions? Note that c should not be a complie-time
constant eventually, as this is the guessed PWM base frequency (should
be read from the hardware, but the code for clocks is not yet in).

>> +
>> +     if (period_cycles < 1)
>> +             period_cycles = 1;
>> +     prescale = (period_cycles - 1) / 4096;
>> +     pv = period_cycles / (prescale + 1) - 1;
>> +     if (pv > 4095)
>> +             pv = 4095;
>> +
>> +     if (prescale > 1023)
>> +             return -EINVAL;
>> +
>> +     dc = pv * duty_ns / period_ns;
>> +
>> +     pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
>> +     writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
>> +
>> +     pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
>> +     writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
>> +
>> +     pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
>> +     writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
>> +
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(pwm_config);
>> +
>> +int pwm_enable(struct pwm_device *pwm)
>> +{
>> +     pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
>> +     writel(5, pwm->regbase + (pwm->pwm_id << 4));
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(pwm_enable);
>> +
>> +void pwm_disable(struct pwm_device *pwm)
>> +{
>> +     pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
>> +     writel(0, pwm->regbase + (pwm->pwm_id << 4));
>> +}
>> +EXPORT_SYMBOL(pwm_disable);
>> +
>> +static DEFINE_MUTEX(pwm_lock);
>> +static LIST_HEAD(pwm_list);
>
> These should be at the top of the file.
>

Ok.

>> +struct pwm_device *pwm_request(int pwm_id, const char *label)
>> +{
>> +     struct pwm_device *pwm;
>> +     int found = 0;
>> +
>> +     mutex_lock(&pwm_lock);
>> +
>> +     list_for_each_entry(pwm, &pwm_list, node) {
>> +             if (pwm->pwm_id == pwm_id) {
>> +                     found = 1;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     if (found) {
>> +             if (pwm->use_count == 0) {
>> +                     pwm->use_count++;
>> +                     pwm->label = label;
>> +             } else
>> +                     pwm = ERR_PTR(-EBUSY);
>> +     } else
>> +             pwm = ERR_PTR(-ENOENT);
>> +
>> +     mutex_unlock(&pwm_lock);
>> +     return pwm;
>> +}
>
> Maybe a bit clearer and more concise like this? Also replaces -ENOENT
> (No such file or directory) with -ENODEV (No such device):
>
>        pwm = ERR_PTR(-ENODEV);
>        mutex_lock(&pwm_lock);
>
>        list_for_each_entry(pwm, &pwm_list, node) {
>                if (pwm->pwm_id == pwm_id) {
>                        if (pwm->use_count != 0) {
>                                pwm = ERR_PTR(-EBUSY);
>                                break;
>                        }
>
>                        pwm->use_count++;
>                        pwm->label = label;
>                        break;
>                }
>        }
>
>        mutex_unlock(&pwm_lock);
>        return pwm;
>

Isn't pwm overwritten inside the loop? -ENODEV will then be lost with
this layout.

>> +EXPORT_SYMBOL(pwm_request);
>> +
>> +void pwm_free(struct pwm_device *pwm)
>> +{
>> +     mutex_lock(&pwm_lock);
>> +
>> +     if (pwm->use_count) {
>> +             pwm->use_count--;
>> +             pwm->label = NULL;
>> +     } else
>> +             pr_warning("PWM device already freed\n");
>> +
>
> Nitpick: Single line else should have braces if the if has braces
>

Ok.

>> +     mutex_unlock(&pwm_lock);
>> +}
>> +EXPORT_SYMBOL(pwm_free);
>> +
>> +static inline void __add_pwm(struct pwm_device *pwm)
>> +{
>> +     mutex_lock(&pwm_lock);
>> +     list_add_tail(&pwm->node, &pwm_list);
>> +     mutex_unlock(&pwm_lock);
>> +}
>> +
>> +static int __devinit pwm_probe(struct platform_device *pdev)
>> +{
>> +     struct pwm_device *pwms;
>> +     struct resource *r;
>> +     int ret = 0;
>> +     int i;
>> +
>> +     pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
>> +     if (pwms == NULL) {
>> +             dev_err(&pdev->dev, "failed to allocate memory\n");
>> +             return -ENOMEM;
>> +     }
>
> Devices should ideally be a single entity, so one platform device per pwm.
>

We have 4 pwm outputs that share status registers, so they are not
really separable.

<snip>

>> diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
>> new file mode 100644
>> index 0000000..ab4f7aa
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/timer.c
>> @@ -0,0 +1,154 @@
>> +/*
>> + *  arch/arm/mach-vt8500/timer.c
>> + *
>> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
>> + */
>> +
>> +#include <linux/io.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/clocksource.h>
>> +#include <linux/clockchips.h>
>> +
>> +#include <asm/mach/time.h>
>> +
>> +#include <mach/mmio_regs.h>
>> +#include <mach/irq_defs.h>
>> +
>> +#define VT8500_TIMER_OFFSET  0x0100
>> +#define TIMER_MATCH_VAL              0x0000
>> +#define TIMER_COUNT_VAL              0x0010
>> +#define TIMER_STATUS_VAL     0x0014
>> +#define TIMER_IER_VAL                0x001c          /* interrupt enable */
>> +#define TIMER_CTRL_VAL               0x0020
>> +#define TIMER_AS_VAL         0x0024          /* access status */
>> +#define TIMER_COUNT_R_ACTIVE (1 << 5)        /* not ready for read */
>> +#define TIMER_COUNT_W_ACTIVE (1 << 4)        /* not ready for write */
>> +#define TIMER_MATCH_W_ACTIVE (1 << 0)        /* not ready for write */
>> +#define VT8500_TIMER_HZ              3000000
>> +
>> +static void __iomem *regbase;
>> +
>> +static cycle_t vt8500_timer_read(struct clocksource *cs)
>> +{
>> +     int loops = 1000;
>> +     writel(3, regbase + TIMER_CTRL_VAL);
>> +     while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
>> +                                             && --loops)
>> +             cpu_relax();
>
> More loop counting? Surely there is a better solution?
>

None that I could come up with. Again, the delay should be on the
order of several bus cycles.

<snip>

>> diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
>> new file mode 100644
>> index 0000000..181ad6f
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/wm8505_7in.c
>> @@ -0,0 +1,81 @@
>> +/*
>> + *  arch/arm/mach-vt8500/wm8505_7in.c
>> + *
>> + *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
>> + */
>> +
>> +#include <linux/io.h>
>> +#include <linux/pm.h>
>> +
>> +#include <asm/mach-types.h>
>> +#include <asm/mach/arch.h>
>> +
>> +#include <mach/mmio_regs.h>
>> +#include "devices.h"
>> +
>> +static void __iomem *pmc_hiber;
>> +
>> +static struct platform_device *devices[] __initdata = {
>> +     &vt8500_device_uart0,
>> +     &vt8500_device_ehci,
>> +     &vt8500_device_wm8505_fb,
>> +     &vt8500_device_ge_rops,
>> +     &vt8500_device_pwm,
>> +     &vt8500_device_pwmbl,
>> +     &vt8500_device_rtc,
>> +};
>> +
>> +static void vt8500_power_off(void)
>> +{
>> +     local_irq_disable();
>
> Is this necessary?
>

Vendor's code disables interrupts. I believe my device refused to
actually switch off without this.

Thanks for the comments, Ryan!

Best regards,
Alexey

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 21:48                     ` Alexey Charkov
@ 2010-12-20 22:23                       ` Ryan Mallon
  2010-12-20 23:00                         ` Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Ryan Mallon @ 2010-12-20 22:23 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

On 12/21/2010 10:48 AM, Alexey Charkov wrote:
> 2010/12/20 Ryan Mallon <ryan@bluewatersys.com>:
>> On 12/21/2010 08:54 AM, Alexey Charkov wrote:
>>> This adds support for the family of Systems-on-Chip produced initially
>>> by VIA and now its subsidiary WonderMedia that have recently become
>>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>>
>>> Support is included for both VT8500 and WM8505, selectable by a
>>> configuration switch at kernel build time.
>>>
>>> Included are basic machine initialization files, register and
>>> interrupt definitions, support for the on-chip interrupt controller,
>>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>>> pulse-width-modulated outputs control, as well as platform device
>>> configurations for the specific drivers implemented elsewhere.
>>>
>>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>>
>> Hi Alexey,
>>
>> Quick review below.

> <snip>
>>> +void __init wmt_set_resources(void)
>>> +{
>>> +     resources_lcdc[0].start = wmt_current_regs->lcdc;
>>> +     resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
>>> +     resources_lcdc[1].start = wmt_current_irqs->lcdc;
>>> +     resources_lcdc[1].end = wmt_current_irqs->lcdc;
>>
>> Ah, this makes more sense. But why have all the indirection? The
>> wmt_regmaps table could just be replaced with #defines and then have
>> separate device files for the VT8500 and the WM8505. This would also
>> make clearer which variants have which peripherals.
>>
> 
> This was the way I implemented it originally. However, Arnd made quite
> a valid suggestion to allow runtime selection of the chip variant,
> thus registers and interrupts need to be held in an indexed data type
> instead of just compile-time macros. In addition, there is now some
> overall movement towards unification of binary kernel images for
> different ARM variants (as far as I can see), so this would be
> required in any case.
> 
> Furthermore, as with many unbranded Chinese products, it's somewhat
> difficult to reliably determine the exact chip version used in your
> netbook without disassembling it. Reading a hardware register for
> identification is easier :)

Okay, that makes sense. I still think there must be a better way than
having a massive indirect table with all the values. Why not detect the
variant in the core code and then have something like:

int init_devices(void)
{
	int board_type = detect_board_type();

	switch (board_type) {
	case BOARD_TYPE_VT8500:
		return vt8500_init_devices();

	case BOARD_TYPE_WM8505:
		return wm8500_init_devices();
	}

	pr_err("Unknown board type\n");
	BUG(); /* panic()? */
	while (1)
		;
}

Then you can have the peripheral setup for each of the variants in their
own files and use #defines. It may get tricky in a couple of places if
you need to be able to access some value directly which is different on
each of the variants, but that can be handled on a case by case basis.
The majority of the numbers will be passed into drivers via the resource
structs.

>>> +
>>> +void __init vt8500_map_io(void)
>>> +{
>>> +     wmt_current_regs = &wmt_regmaps[VT8500_INDEX];
>>> +     wmt_current_irqs = &wmt_irqs[VT8500_INDEX];
>>> +
>>> +     vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
>>> +     vt8500_io_desc[0].pfn =
>>> +                     __phys_to_pfn(wmt_current_regs->mmio_regs_start);
>>> +     vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
>>> +
>>> +     iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
>>> +}
>>> +
>>> +void __init wm8505_map_io(void)
>>> +{
>>> +     wmt_current_regs = &wmt_regmaps[WM8505_INDEX];
>>> +     wmt_current_irqs = &wmt_irqs[WM8505_INDEX];
>>> +
>>> +     vt8500_io_desc[0].virtual = wmt_current_regs->mmio_regs_virt;
>>> +     vt8500_io_desc[0].pfn =
>>> +                     __phys_to_pfn(wmt_current_regs->mmio_regs_start);
>>> +     vt8500_io_desc[0].length = wmt_current_regs->mmio_regs_length;
>>> +
>>> +     iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
>>> +}
>>
>> Separate files. If more variants get added, this file will become
>> unwieldy very quickly.
>>
> 
> Is it really worthwhile to create separate files for single 12-line
> functions? After all, WonderMedia does not release new chips every now
> and then :)

I meant if you have the full peripheral initialisation for each variant
in its own file.

>>> +
>>> +void __init vt8500_reserve_mem(void)
>>> +{
>>> +#ifdef CONFIG_FB_VT8500
>>> +     panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
>>> +     preallocate_fb(&panels[current_panel_idx], SZ_4M);
>>> +     vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
>>> +#endif
>>> +}
>>
>> Not sure if this should exist in the platform code or the framebuffer
>> driver. In the latter case it would automatically be CONFIG_FB_VT8500
>> and the platform_data can still be set in the platform code. Is there a
>> reason for this not to be in the framebuffer driver?
>>
> 
> I can't reserve memory via memblock from the driver, and usual runtime
> allocation functions can't handle it (need alignment to 4 megabytes in
> 8500, framebuffer sizes exceed 4 megabytes in 8505).

Can you use one of the initcalls from a driver to to the memblock
reserve? I don't know much about how memblock works. There are also the
various large page allocators in the works, but I don't think anything
has hit mainline yet.

>>> +#ifndef __ASM_ARCH_MEMORY_H
>>> +#define __ASM_ARCH_MEMORY_H
>>> +
>>> +/*
>>> + * Physical DRAM offset.
>>> + */
>>> +#define PHYS_OFFSET  UL(0x00000000)
>>
>> If you renamed this to PHYS_DRAM_OFFSET you wouldn't need the comment :-).
>>
> 
> I'm not the one who chooses :)

Oops, missed that :-).

>>> +void __init vt8500_init_irq(void)
>>> +{
>>> +     unsigned int i;
>>> +
>>> +     ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
>>> +
>>> +     if (ic_regbase) {
>>> +             /* Enable rotating priority for IRQ */
>>> +             writel((1 << 6), ic_regbase + 0x20);
>>> +             writel(0, ic_regbase + 0x24);
>>> +
>>> +             for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
>>> +                     /* Disable all interrupts and route them to IRQ */
>>> +                     writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
>>> +
>>> +                     set_irq_chip(i, &vt8500_irq_chip);
>>> +                     set_irq_handler(i, handle_level_irq);
>>> +                     set_irq_flags(i, IRQF_VALID);
>>> +             }
>>> +     } else {
>>> +             printk(KERN_ERR "Unable to remap the Interrupt Controller "
>>> +                             "registers, not enabling IRQs!\n");
>>
>> printk strings should be on a single line (can be > 80 columns) to make
>> grepping easier. You could also use the pr_ macros with pr_fmt set.
>>
> 
> Well, checkpatch.pl complained about that in the first place, so I
> split the line. Should I merge them back in all instances?

Yes. I think checkpatch has been changed to warn about spitting printk
strings across lines now.

> <snip>
> 
>>> diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
>>> new file mode 100644
>>> index 0000000..d1356a1
>>> --- /dev/null
>>> +++ b/arch/arm/mach-vt8500/pwm.c
>>
>> I'm not sure what the state of the various efforts to provide a common
>> pwm framework are, but you may want to check.
>>
> 
> I did before starting to write this code, found nothing.

Fair enough.

>>> +
>>> +static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
>>> +{
>>> +     int loops = 1000;
>>> +     while ((readb(reg) & bitmask) && --loops)
>>> +             cpu_relax();
>>
>> Ugh. If you are going to busy wait, can't you delay for a known amount
>> of time? Even better, can this be replaced with wait_event or some
>> equivalent?
>>
> 
> The delay should be on the order of several bus cycles, where udelay
> actually busy-waits, too. wait_event would be longer than that to set
> up, and there is no associated interrupt.

I meant if the hardware has some specific maximum wait time then you
could just delay that long. If there is no interrupt then wait_event and
friends probably aren't going to work.

Maybe convert this to a timed loop (i.e. 1 second timeout) using
jiffies. That way you are never dependent on cpu speed. You should
probably also emit a warning if the timeout is reached and the device
still claims to be busy.

>>> +}
>>> +
>>> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>>> +{
>>> +     unsigned long long c;
>>> +     unsigned long period_cycles, prescale, pv, dc;
>>> +
>>> +     if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
>>> +             return -EINVAL;
>>> +
>>> +     c = 25000000/2; /* wild guess --- need to implement clocks */
>>> +     c = c * period_ns;
>>> +     do_div(c, 1000000000);
>>> +     period_cycles = c;
>>
>> This looks like it could be reworked to remove the do_div call.
>>
> 
> I just followed PXA implementation in this regard. Are there any
> specific suggestions? Note that c should not be a complie-time
> constant eventually, as this is the guessed PWM base frequency (should
> be read from the hardware, but the code for clocks is not yet in).

I didn't have a particular solution in mind, but often by changing the
units used and rearranging the math a bit you can often avoid having to
do horrible multiplies and divides.

For now at least you could avoid the do_div by assigning period_cycles
directly.

>>> +struct pwm_device *pwm_request(int pwm_id, const char *label)
>>> +{
>>> +     struct pwm_device *pwm;
>>> +     int found = 0;
>>> +
>>> +     mutex_lock(&pwm_lock);
>>> +
>>> +     list_for_each_entry(pwm, &pwm_list, node) {
>>> +             if (pwm->pwm_id == pwm_id) {
>>> +                     found = 1;
>>> +                     break;
>>> +             }
>>> +     }
>>> +
>>> +     if (found) {
>>> +             if (pwm->use_count == 0) {
>>> +                     pwm->use_count++;
>>> +                     pwm->label = label;
>>> +             } else
>>> +                     pwm = ERR_PTR(-EBUSY);
>>> +     } else
>>> +             pwm = ERR_PTR(-ENOENT);
>>> +
>>> +     mutex_unlock(&pwm_lock);
>>> +     return pwm;
>>> +}
>>
>> Maybe a bit clearer and more concise like this? Also replaces -ENOENT
>> (No such file or directory) with -ENODEV (No such device):
>>
>>        pwm = ERR_PTR(-ENODEV);
>>        mutex_lock(&pwm_lock);
>>
>>        list_for_each_entry(pwm, &pwm_list, node) {
>>                if (pwm->pwm_id == pwm_id) {
>>                        if (pwm->use_count != 0) {
>>                                pwm = ERR_PTR(-EBUSY);
>>                                break;
>>                        }
>>
>>                        pwm->use_count++;
>>                        pwm->label = label;
>>                        break;
>>                }
>>        }
>>
>>        mutex_unlock(&pwm_lock);
>>        return pwm;
>>
> 
> Isn't pwm overwritten inside the loop? -ENODEV will then be lost with
> this layout.

Oops, yes. You would need a second temporary pwm for the list iterator.
It's not a big deal anyway, just though it could be made more concise by
having all the code inside the loop.

>>> +static int __devinit pwm_probe(struct platform_device *pdev)
>>> +{
>>> +     struct pwm_device *pwms;
>>> +     struct resource *r;
>>> +     int ret = 0;
>>> +     int i;
>>> +
>>> +     pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
>>> +     if (pwms == NULL) {
>>> +             dev_err(&pdev->dev, "failed to allocate memory\n");
>>> +             return -ENOMEM;
>>> +     }
>>
>> Devices should ideally be a single entity, so one platform device per pwm.
>>
> 
> We have 4 pwm outputs that share status registers, so they are not
> really separable.

Okay.

<snip>

>>> +static void vt8500_power_off(void)
>>> +{
>>> +     local_irq_disable();
>>
>> Is this necessary?
>>
> 
> Vendor's code disables interrupts. I believe my device refused to
> actually switch off without this.

Okay, fair enough.

> Thanks for the comments, Ryan!

No problem.

~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 22:23                       ` Ryan Mallon
@ 2010-12-20 23:00                         ` Alexey Charkov
  2010-12-20 23:22                           ` Ryan Mallon
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-12-20 23:00 UTC (permalink / raw)
  To: Ryan Mallon
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
> On 12/21/2010 10:48 AM, Alexey Charkov wrote:
>> 2010/12/20 Ryan Mallon <ryan@bluewatersys.com>:
>>> On 12/21/2010 08:54 AM, Alexey Charkov wrote:
>>>> This adds support for the family of Systems-on-Chip produced initially
>>>> by VIA and now its subsidiary WonderMedia that have recently become
>>>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>>>
>>>> Support is included for both VT8500 and WM8505, selectable by a
>>>> configuration switch at kernel build time.
>>>>
>>>> Included are basic machine initialization files, register and
>>>> interrupt definitions, support for the on-chip interrupt controller,
>>>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>>>> pulse-width-modulated outputs control, as well as platform device
>>>> configurations for the specific drivers implemented elsewhere.
>>>>
>>>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>>>
>>> Hi Alexey,
>>>
>>> Quick review below.
>
>> <snip>
>>>> +void __init wmt_set_resources(void)
>>>> +{
>>>> +     resources_lcdc[0].start = wmt_current_regs->lcdc;
>>>> +     resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
>>>> +     resources_lcdc[1].start = wmt_current_irqs->lcdc;
>>>> +     resources_lcdc[1].end = wmt_current_irqs->lcdc;
>>>
>>> Ah, this makes more sense. But why have all the indirection? The
>>> wmt_regmaps table could just be replaced with #defines and then have
>>> separate device files for the VT8500 and the WM8505. This would also
>>> make clearer which variants have which peripherals.
>>>
>>
>> This was the way I implemented it originally. However, Arnd made quite
>> a valid suggestion to allow runtime selection of the chip variant,
>> thus registers and interrupts need to be held in an indexed data type
>> instead of just compile-time macros. In addition, there is now some
>> overall movement towards unification of binary kernel images for
>> different ARM variants (as far as I can see), so this would be
>> required in any case.
>>
>> Furthermore, as with many unbranded Chinese products, it's somewhat
>> difficult to reliably determine the exact chip version used in your
>> netbook without disassembling it. Reading a hardware register for
>> identification is easier :)
>
> Okay, that makes sense. I still think there must be a better way than
> having a massive indirect table with all the values. Why not detect the
> variant in the core code and then have something like:
>
> int init_devices(void)
> {
>        int board_type = detect_board_type();
>
>        switch (board_type) {
>        case BOARD_TYPE_VT8500:
>                return vt8500_init_devices();
>
>        case BOARD_TYPE_WM8505:
>                return wm8500_init_devices();
>        }
>
>        pr_err("Unknown board type\n");
>        BUG(); /* panic()? */
>        while (1)
>                ;
> }
>
> Then you can have the peripheral setup for each of the variants in their
> own files and use #defines. It may get tricky in a couple of places if
> you need to be able to access some value directly which is different on
> each of the variants, but that can be handled on a case by case basis.
> The majority of the numbers will be passed into drivers via the resource
> structs.
>

This is more or less what I'm doing right now - except for the
separation between different files. I tried to avoid duplication of
similar things here. Is the indirect table really a big issue? I'm a
bit reluctant to copy about the whole devices.c for each chip variant,
which would be otherwise required. Further, it would add more
complexity to the timer, irq, gpio, i8042 and probably some other
places.

>>>> +
>>>> +void __init vt8500_reserve_mem(void)
>>>> +{
>>>> +#ifdef CONFIG_FB_VT8500
>>>> +     panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
>>>> +     preallocate_fb(&panels[current_panel_idx], SZ_4M);
>>>> +     vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
>>>> +#endif
>>>> +}
>>>
>>> Not sure if this should exist in the platform code or the framebuffer
>>> driver. In the latter case it would automatically be CONFIG_FB_VT8500
>>> and the platform_data can still be set in the platform code. Is there a
>>> reason for this not to be in the framebuffer driver?
>>>
>>
>> I can't reserve memory via memblock from the driver, and usual runtime
>> allocation functions can't handle it (need alignment to 4 megabytes in
>> 8500, framebuffer sizes exceed 4 megabytes in 8505).
>
> Can you use one of the initcalls from a driver to to the memblock
> reserve? I don't know much about how memblock works. There are also the
> various large page allocators in the works, but I don't think anything
> has hit mainline yet.
>

No, drivers are initialized too late, even if compiled statically.
Memblock can only be reserved through the designated machine callback,
as far as I have understood.

>>>> +void __init vt8500_init_irq(void)
>>>> +{
>>>> +     unsigned int i;
>>>> +
>>>> +     ic_regbase = ioremap(wmt_current_regs->ic0, SZ_64K);
>>>> +
>>>> +     if (ic_regbase) {
>>>> +             /* Enable rotating priority for IRQ */
>>>> +             writel((1 << 6), ic_regbase + 0x20);
>>>> +             writel(0, ic_regbase + 0x24);
>>>> +
>>>> +             for (i = 0; i < wmt_current_irqs->nr_irqs; i++) {
>>>> +                     /* Disable all interrupts and route them to IRQ */
>>>> +                     writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
>>>> +
>>>> +                     set_irq_chip(i, &vt8500_irq_chip);
>>>> +                     set_irq_handler(i, handle_level_irq);
>>>> +                     set_irq_flags(i, IRQF_VALID);
>>>> +             }
>>>> +     } else {
>>>> +             printk(KERN_ERR "Unable to remap the Interrupt Controller "
>>>> +                             "registers, not enabling IRQs!\n");
>>>
>>> printk strings should be on a single line (can be > 80 columns) to make
>>> grepping easier. You could also use the pr_ macros with pr_fmt set.
>>>
>>
>> Well, checkpatch.pl complained about that in the first place, so I
>> split the line. Should I merge them back in all instances?
>
> Yes. I think checkpatch has been changed to warn about spitting printk
> strings across lines now.
>

Ok, will then merge strings.

>>>> +
>>>> +static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
>>>> +{
>>>> +     int loops = 1000;
>>>> +     while ((readb(reg) & bitmask) && --loops)
>>>> +             cpu_relax();
>>>
>>> Ugh. If you are going to busy wait, can't you delay for a known amount
>>> of time? Even better, can this be replaced with wait_event or some
>>> equivalent?
>>>
>>
>> The delay should be on the order of several bus cycles, where udelay
>> actually busy-waits, too. wait_event would be longer than that to set
>> up, and there is no associated interrupt.
>
> I meant if the hardware has some specific maximum wait time then you
> could just delay that long. If there is no interrupt then wait_event and
> friends probably aren't going to work.
>
> Maybe convert this to a timed loop (i.e. 1 second timeout) using
> jiffies. That way you are never dependent on cpu speed. You should
> probably also emit a warning if the timeout is reached and the device
> still claims to be busy.
>

Agreed, will look into that. There is no definite maximum wait time,
though (at least none that I know about).

>>>> +}
>>>> +
>>>> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>>>> +{
>>>> +     unsigned long long c;
>>>> +     unsigned long period_cycles, prescale, pv, dc;
>>>> +
>>>> +     if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
>>>> +             return -EINVAL;
>>>> +
>>>> +     c = 25000000/2; /* wild guess --- need to implement clocks */
>>>> +     c = c * period_ns;
>>>> +     do_div(c, 1000000000);
>>>> +     period_cycles = c;
>>>
>>> This looks like it could be reworked to remove the do_div call.
>>>
>>
>> I just followed PXA implementation in this regard. Are there any
>> specific suggestions? Note that c should not be a complie-time
>> constant eventually, as this is the guessed PWM base frequency (should
>> be read from the hardware, but the code for clocks is not yet in).
>
> I didn't have a particular solution in mind, but often by changing the
> units used and rearranging the math a bit you can often avoid having to
> do horrible multiplies and divides.
>
> For now at least you could avoid the do_div by assigning period_cycles
> directly.
>

It depends on period_ns, which is passed in as an argument from
whatever uses PWM, so I'm not sure it can be assigned directly.

Thanks,
Alexey

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 23:00                         ` Alexey Charkov
@ 2010-12-20 23:22                           ` Ryan Mallon
  2010-12-20 23:49                             ` Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Ryan Mallon @ 2010-12-20 23:22 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

On 12/21/2010 12:00 PM, Alexey Charkov wrote:
> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>> On 12/21/2010 10:48 AM, Alexey Charkov wrote:
>>> 2010/12/20 Ryan Mallon <ryan@bluewatersys.com>:
>>>> On 12/21/2010 08:54 AM, Alexey Charkov wrote:
>>>>> This adds support for the family of Systems-on-Chip produced initially
>>>>> by VIA and now its subsidiary WonderMedia that have recently become
>>>>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>>>>
>>>>> Support is included for both VT8500 and WM8505, selectable by a
>>>>> configuration switch at kernel build time.
>>>>>
>>>>> Included are basic machine initialization files, register and
>>>>> interrupt definitions, support for the on-chip interrupt controller,
>>>>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>>>>> pulse-width-modulated outputs control, as well as platform device
>>>>> configurations for the specific drivers implemented elsewhere.
>>>>>
>>>>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>>>>
>>>> Hi Alexey,
>>>>
>>>> Quick review below.
>>
>>> <snip>
>>>>> +void __init wmt_set_resources(void)
>>>>> +{
>>>>> +     resources_lcdc[0].start = wmt_current_regs->lcdc;
>>>>> +     resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
>>>>> +     resources_lcdc[1].start = wmt_current_irqs->lcdc;
>>>>> +     resources_lcdc[1].end = wmt_current_irqs->lcdc;
>>>>
>>>> Ah, this makes more sense. But why have all the indirection? The
>>>> wmt_regmaps table could just be replaced with #defines and then have
>>>> separate device files for the VT8500 and the WM8505. This would also
>>>> make clearer which variants have which peripherals.
>>>>
>>>
>>> This was the way I implemented it originally. However, Arnd made quite
>>> a valid suggestion to allow runtime selection of the chip variant,
>>> thus registers and interrupts need to be held in an indexed data type
>>> instead of just compile-time macros. In addition, there is now some
>>> overall movement towards unification of binary kernel images for
>>> different ARM variants (as far as I can see), so this would be
>>> required in any case.
>>>
>>> Furthermore, as with many unbranded Chinese products, it's somewhat
>>> difficult to reliably determine the exact chip version used in your
>>> netbook without disassembling it. Reading a hardware register for
>>> identification is easier :)
>>
>> Okay, that makes sense. I still think there must be a better way than
>> having a massive indirect table with all the values. Why not detect the
>> variant in the core code and then have something like:
>>
>> int init_devices(void)
>> {
>>        int board_type = detect_board_type();
>>
>>        switch (board_type) {
>>        case BOARD_TYPE_VT8500:
>>                return vt8500_init_devices();
>>
>>        case BOARD_TYPE_WM8505:
>>                return wm8500_init_devices();
>>        }
>>
>>        pr_err("Unknown board type\n");
>>        BUG(); /* panic()? */
>>        while (1)
>>                ;
>> }
>>
>> Then you can have the peripheral setup for each of the variants in their
>> own files and use #defines. It may get tricky in a couple of places if
>> you need to be able to access some value directly which is different on
>> each of the variants, but that can be handled on a case by case basis.
>> The majority of the numbers will be passed into drivers via the resource
>> structs.
>>
> 
> This is more or less what I'm doing right now - except for the
> separation between different files. I tried to avoid duplication of
> similar things here. Is the indirect table really a big issue? I'm a
> bit reluctant to copy about the whole devices.c for each chip variant,
> which would be otherwise required. Further, it would add more
> complexity to the timer, irq, gpio, i8042 and probably some other
> places.

Yeah, agreed about the duplication. My approach would require the common
peripherals to be defined for each variant. I'm not entirely against the
indirect table (and am even starting to think it may be the best overall
solution), it's just that it can be a bit difficult to follow because to
add a device you need to do the following:

 - Add a partially empty resource/platform_device struct
 - Add resource entries to the resource table definition
 - Add resource values to the resource table
 - Add assignment of resource values to device init code

The indirection also makes it harder to quickly determine the IRQ number
of memory address of a peripheral.

The current solution using the indirect table is okay, but it would be
nice to find a solution which reduces some of this effort.

>>>>> +
>>>>> +void __init vt8500_reserve_mem(void)
>>>>> +{
>>>>> +#ifdef CONFIG_FB_VT8500
>>>>> +     panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
>>>>> +     preallocate_fb(&panels[current_panel_idx], SZ_4M);
>>>>> +     vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
>>>>> +#endif
>>>>> +}
>>>>
>>>> Not sure if this should exist in the platform code or the framebuffer
>>>> driver. In the latter case it would automatically be CONFIG_FB_VT8500
>>>> and the platform_data can still be set in the platform code. Is there a
>>>> reason for this not to be in the framebuffer driver?
>>>>
>>>
>>> I can't reserve memory via memblock from the driver, and usual runtime
>>> allocation functions can't handle it (need alignment to 4 megabytes in
>>> 8500, framebuffer sizes exceed 4 megabytes in 8505).
>>
>> Can you use one of the initcalls from a driver to to the memblock
>> reserve? I don't know much about how memblock works. There are also the
>> various large page allocators in the works, but I don't think anything
>> has hit mainline yet.
>>
> 
> No, drivers are initialized too late, even if compiled statically.
> Memblock can only be reserved through the designated machine callback,
> as far as I have understood.

Okay.

>>>>> +}
>>>>> +
>>>>> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>>>>> +{
>>>>> +     unsigned long long c;
>>>>> +     unsigned long period_cycles, prescale, pv, dc;
>>>>> +
>>>>> +     if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
>>>>> +             return -EINVAL;
>>>>> +
>>>>> +     c = 25000000/2; /* wild guess --- need to implement clocks */
>>>>> +     c = c * period_ns;
>>>>> +     do_div(c, 1000000000);
>>>>> +     period_cycles = c;
>>>>
>>>> This looks like it could be reworked to remove the do_div call.
>>>>
>>>
>>> I just followed PXA implementation in this regard. Are there any
>>> specific suggestions? Note that c should not be a complie-time
>>> constant eventually, as this is the guessed PWM base frequency (should
>>> be read from the hardware, but the code for clocks is not yet in).
>>
>> I didn't have a particular solution in mind, but often by changing the
>> units used and rearranging the math a bit you can often avoid having to
>> do horrible multiplies and divides.
>>
>> For now at least you could avoid the do_div by assigning period_cycles
>> directly.
>>
> 
> It depends on period_ns, which is passed in as an argument from
> whatever uses PWM, so I'm not sure it can be assigned directly.

Ah. How big a number is period_ns? Can you do something like this instead?

	period_cycles = ((250 / 2) * period_ns) / 10000;

and still safely avoid overflows?

~Ryan
	
-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 23:22                           ` Ryan Mallon
@ 2010-12-20 23:49                             ` Alexey Charkov
  2010-12-21  0:09                               ` Alexey Charkov
  2010-12-21  2:32                               ` Ryan Mallon
  0 siblings, 2 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-12-20 23:49 UTC (permalink / raw)
  To: Ryan Mallon
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
> On 12/21/2010 12:00 PM, Alexey Charkov wrote:
>> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>>> On 12/21/2010 10:48 AM, Alexey Charkov wrote:
>>>> 2010/12/20 Ryan Mallon <ryan@bluewatersys.com>:
>>>>> On 12/21/2010 08:54 AM, Alexey Charkov wrote:
>>>>>> This adds support for the family of Systems-on-Chip produced initially
>>>>>> by VIA and now its subsidiary WonderMedia that have recently become
>>>>>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>>>>>
>>>>>> Support is included for both VT8500 and WM8505, selectable by a
>>>>>> configuration switch at kernel build time.
>>>>>>
>>>>>> Included are basic machine initialization files, register and
>>>>>> interrupt definitions, support for the on-chip interrupt controller,
>>>>>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>>>>>> pulse-width-modulated outputs control, as well as platform device
>>>>>> configurations for the specific drivers implemented elsewhere.
>>>>>>
>>>>>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>>>>>
>>>>> Hi Alexey,
>>>>>
>>>>> Quick review below.
>>>
>>>> <snip>
>>>>>> +void __init wmt_set_resources(void)
>>>>>> +{
>>>>>> +     resources_lcdc[0].start = wmt_current_regs->lcdc;
>>>>>> +     resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
>>>>>> +     resources_lcdc[1].start = wmt_current_irqs->lcdc;
>>>>>> +     resources_lcdc[1].end = wmt_current_irqs->lcdc;
>>>>>
>>>>> Ah, this makes more sense. But why have all the indirection? The
>>>>> wmt_regmaps table could just be replaced with #defines and then have
>>>>> separate device files for the VT8500 and the WM8505. This would also
>>>>> make clearer which variants have which peripherals.
>>>>>
>>>>
>>>> This was the way I implemented it originally. However, Arnd made quite
>>>> a valid suggestion to allow runtime selection of the chip variant,
>>>> thus registers and interrupts need to be held in an indexed data type
>>>> instead of just compile-time macros. In addition, there is now some
>>>> overall movement towards unification of binary kernel images for
>>>> different ARM variants (as far as I can see), so this would be
>>>> required in any case.
>>>>
>>>> Furthermore, as with many unbranded Chinese products, it's somewhat
>>>> difficult to reliably determine the exact chip version used in your
>>>> netbook without disassembling it. Reading a hardware register for
>>>> identification is easier :)
>>>
>>> Okay, that makes sense. I still think there must be a better way than
>>> having a massive indirect table with all the values. Why not detect the
>>> variant in the core code and then have something like:
>>>
>>> int init_devices(void)
>>> {
>>>        int board_type = detect_board_type();
>>>
>>>        switch (board_type) {
>>>        case BOARD_TYPE_VT8500:
>>>                return vt8500_init_devices();
>>>
>>>        case BOARD_TYPE_WM8505:
>>>                return wm8500_init_devices();
>>>        }
>>>
>>>        pr_err("Unknown board type\n");
>>>        BUG(); /* panic()? */
>>>        while (1)
>>>                ;
>>> }
>>>
>>> Then you can have the peripheral setup for each of the variants in their
>>> own files and use #defines. It may get tricky in a couple of places if
>>> you need to be able to access some value directly which is different on
>>> each of the variants, but that can be handled on a case by case basis.
>>> The majority of the numbers will be passed into drivers via the resource
>>> structs.
>>>
>>
>> This is more or less what I'm doing right now - except for the
>> separation between different files. I tried to avoid duplication of
>> similar things here. Is the indirect table really a big issue? I'm a
>> bit reluctant to copy about the whole devices.c for each chip variant,
>> which would be otherwise required. Further, it would add more
>> complexity to the timer, irq, gpio, i8042 and probably some other
>> places.
>
> Yeah, agreed about the duplication. My approach would require the common
> peripherals to be defined for each variant. I'm not entirely against the
> indirect table (and am even starting to think it may be the best overall
> solution), it's just that it can be a bit difficult to follow because to
> add a device you need to do the following:
>
>  - Add a partially empty resource/platform_device struct
>  - Add resource entries to the resource table definition
>  - Add resource values to the resource table
>  - Add assignment of resource values to device init code
>

That's actually only one step more than what machines with static
resource definitions require (the last one). Flexibility does come at
a cost, so there should be a mathematical limit to optimization of
this thing :)

> The indirection also makes it harder to quickly determine the IRQ number
> of memory address of a peripheral.
>
> The current solution using the indirect table is okay, but it would be
> nice to find a solution which reduces some of this effort.
>
>>>>>> +}
>>>>>> +
>>>>>> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>>>>>> +{
>>>>>> +     unsigned long long c;
>>>>>> +     unsigned long period_cycles, prescale, pv, dc;
>>>>>> +
>>>>>> +     if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
>>>>>> +             return -EINVAL;
>>>>>> +
>>>>>> +     c = 25000000/2; /* wild guess --- need to implement clocks */
>>>>>> +     c = c * period_ns;
>>>>>> +     do_div(c, 1000000000);
>>>>>> +     period_cycles = c;
>>>>>
>>>>> This looks like it could be reworked to remove the do_div call.
>>>>>
>>>>
>>>> I just followed PXA implementation in this regard. Are there any
>>>> specific suggestions? Note that c should not be a complie-time
>>>> constant eventually, as this is the guessed PWM base frequency (should
>>>> be read from the hardware, but the code for clocks is not yet in).
>>>
>>> I didn't have a particular solution in mind, but often by changing the
>>> units used and rearranging the math a bit you can often avoid having to
>>> do horrible multiplies and divides.
>>>
>>> For now at least you could avoid the do_div by assigning period_cycles
>>> directly.
>>>
>>
>> It depends on period_ns, which is passed in as an argument from
>> whatever uses PWM, so I'm not sure it can be assigned directly.
>
> Ah. How big a number is period_ns? Can you do something like this instead?
>
>        period_cycles = ((250 / 2) * period_ns) / 10000;
>
> and still safely avoid overflows?
>

The only current in-kernel user of PWM is the backlight, and that
currently uses period_ns = 250000. At this value it does not overflow.
However, in a general case the base frequency will also be returned as
a large number (like 12500000) as per CLK infrastructure conventions
(once that part is implemented). Further, I can't see any built-in
reasons for period_ns to be bounded by anything below sizeof(int). The
hardware will work with up to 4096*1024*1000000000/base_frequency (see
the code for constraints), so it can in principle overflow with 32 bit
arithmetics.

Best regards,
Alexey

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 23:49                             ` Alexey Charkov
@ 2010-12-21  0:09                               ` Alexey Charkov
  2010-12-21  2:32                               ` Ryan Mallon
  1 sibling, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-12-21  0:09 UTC (permalink / raw)
  To: Ryan Mallon
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

2010/12/21 Alexey Charkov <alchark@gmail.com>:
> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>> On 12/21/2010 12:00 PM, Alexey Charkov wrote:
>>> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>>>> On 12/21/2010 10:48 AM, Alexey Charkov wrote:
>>>>> 2010/12/20 Ryan Mallon <ryan@bluewatersys.com>:
>>>>>> On 12/21/2010 08:54 AM, Alexey Charkov wrote:
>>>>>>> +}
>>>>>>> +
>>>>>>> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>>>>>>> +{
>>>>>>> +     unsigned long long c;
>>>>>>> +     unsigned long period_cycles, prescale, pv, dc;
>>>>>>> +
>>>>>>> +     if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
>>>>>>> +             return -EINVAL;
>>>>>>> +
>>>>>>> +     c = 25000000/2; /* wild guess --- need to implement clocks */
>>>>>>> +     c = c * period_ns;
>>>>>>> +     do_div(c, 1000000000);
>>>>>>> +     period_cycles = c;
>>>>>>
>>>>>> This looks like it could be reworked to remove the do_div call.
>>>>>>
>>>>>
>>>>> I just followed PXA implementation in this regard. Are there any
>>>>> specific suggestions? Note that c should not be a complie-time
>>>>> constant eventually, as this is the guessed PWM base frequency (should
>>>>> be read from the hardware, but the code for clocks is not yet in).
>>>>
>>>> I didn't have a particular solution in mind, but often by changing the
>>>> units used and rearranging the math a bit you can often avoid having to
>>>> do horrible multiplies and divides.
>>>>
>>>> For now at least you could avoid the do_div by assigning period_cycles
>>>> directly.
>>>>
>>>
>>> It depends on period_ns, which is passed in as an argument from
>>> whatever uses PWM, so I'm not sure it can be assigned directly.
>>
>> Ah. How big a number is period_ns? Can you do something like this instead?
>>
>>        period_cycles = ((250 / 2) * period_ns) / 10000;
>>
>> and still safely avoid overflows?
>>
>
> The only current in-kernel user of PWM is the backlight, and that
> currently uses period_ns = 250000. At this value it does not overflow.
> However, in a general case the base frequency will also be returned as
> a large number (like 12500000) as per CLK infrastructure conventions
> (once that part is implemented). Further, I can't see any built-in
> reasons for period_ns to be bounded by anything below sizeof(int). The
> hardware will work with up to 4096*1024*1000000000/base_frequency (see
> the code for constraints), so it can in principle overflow with 32 bit
> arithmetics.
>

This discussion led me to look closer at the duty counter calculation:
if period_ns and duty_ns are both large and close to each other (about
0.3 seconds, rare but possible use case for PWM), then (pv * duty_ns)
can overflow in 32 bit multiplication for permissible argument values.
Should I use do_div((u64)pv * (u64)duty_ns, period_ns) here?

Best regards,
Alexey

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-20 23:49                             ` Alexey Charkov
  2010-12-21  0:09                               ` Alexey Charkov
@ 2010-12-21  2:32                               ` Ryan Mallon
  2010-12-21 10:00                                 ` Alexey Charkov
  1 sibling, 1 reply; 91+ messages in thread
From: Ryan Mallon @ 2010-12-21  2:32 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

On 12/21/2010 12:49 PM, Alexey Charkov wrote:
> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>> On 12/21/2010 12:00 PM, Alexey Charkov wrote:
>>> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>>>> On 12/21/2010 10:48 AM, Alexey Charkov wrote:
>>>>> 2010/12/20 Ryan Mallon <ryan@bluewatersys.com>:
>>>>>> On 12/21/2010 08:54 AM, Alexey Charkov wrote:
>>>>>>> This adds support for the family of Systems-on-Chip produced initially
>>>>>>> by VIA and now its subsidiary WonderMedia that have recently become
>>>>>>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>>>>>>
>>>>>>> Support is included for both VT8500 and WM8505, selectable by a
>>>>>>> configuration switch at kernel build time.
>>>>>>>
>>>>>>> Included are basic machine initialization files, register and
>>>>>>> interrupt definitions, support for the on-chip interrupt controller,
>>>>>>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>>>>>>> pulse-width-modulated outputs control, as well as platform device
>>>>>>> configurations for the specific drivers implemented elsewhere.
>>>>>>>
>>>>>>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>>>>>>
>>>>>> Hi Alexey,
>>>>>>
>>>>>> Quick review below.
>>>>
>>>>> <snip>
>>>>>>> +void __init wmt_set_resources(void)
>>>>>>> +{
>>>>>>> +     resources_lcdc[0].start = wmt_current_regs->lcdc;
>>>>>>> +     resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
>>>>>>> +     resources_lcdc[1].start = wmt_current_irqs->lcdc;
>>>>>>> +     resources_lcdc[1].end = wmt_current_irqs->lcdc;
>>>>>>
>>>>>> Ah, this makes more sense. But why have all the indirection? The
>>>>>> wmt_regmaps table could just be replaced with #defines and then have
>>>>>> separate device files for the VT8500 and the WM8505. This would also
>>>>>> make clearer which variants have which peripherals.
>>>>>>
>>>>>
>>>>> This was the way I implemented it originally. However, Arnd made quite
>>>>> a valid suggestion to allow runtime selection of the chip variant,
>>>>> thus registers and interrupts need to be held in an indexed data type
>>>>> instead of just compile-time macros. In addition, there is now some
>>>>> overall movement towards unification of binary kernel images for
>>>>> different ARM variants (as far as I can see), so this would be
>>>>> required in any case.
>>>>>
>>>>> Furthermore, as with many unbranded Chinese products, it's somewhat
>>>>> difficult to reliably determine the exact chip version used in your
>>>>> netbook without disassembling it. Reading a hardware register for
>>>>> identification is easier :)
>>>>
>>>> Okay, that makes sense. I still think there must be a better way than
>>>> having a massive indirect table with all the values. Why not detect the
>>>> variant in the core code and then have something like:
>>>>
>>>> int init_devices(void)
>>>> {
>>>>        int board_type = detect_board_type();
>>>>
>>>>        switch (board_type) {
>>>>        case BOARD_TYPE_VT8500:
>>>>                return vt8500_init_devices();
>>>>
>>>>        case BOARD_TYPE_WM8505:
>>>>                return wm8500_init_devices();
>>>>        }
>>>>
>>>>        pr_err("Unknown board type\n");
>>>>        BUG(); /* panic()? */
>>>>        while (1)
>>>>                ;
>>>> }
>>>>
>>>> Then you can have the peripheral setup for each of the variants in their
>>>> own files and use #defines. It may get tricky in a couple of places if
>>>> you need to be able to access some value directly which is different on
>>>> each of the variants, but that can be handled on a case by case basis.
>>>> The majority of the numbers will be passed into drivers via the resource
>>>> structs.
>>>>
>>>
>>> This is more or less what I'm doing right now - except for the
>>> separation between different files. I tried to avoid duplication of
>>> similar things here. Is the indirect table really a big issue? I'm a
>>> bit reluctant to copy about the whole devices.c for each chip variant,
>>> which would be otherwise required. Further, it would add more
>>> complexity to the timer, irq, gpio, i8042 and probably some other
>>> places.
>>
>> Yeah, agreed about the duplication. My approach would require the common
>> peripherals to be defined for each variant. I'm not entirely against the
>> indirect table (and am even starting to think it may be the best overall
>> solution), it's just that it can be a bit difficult to follow because to
>> add a device you need to do the following:
>>
>>  - Add a partially empty resource/platform_device struct
>>  - Add resource entries to the resource table definition
>>  - Add resource values to the resource table
>>  - Add assignment of resource values to device init code
>>
> 
> That's actually only one step more than what machines with static
> resource definitions require (the last one). Flexibility does come at
> a cost, so there should be a mathematical limit to optimization of
> this thing :)

No it isn't. You don't have the massive table, which requires
modifications to both the definition and declaration, on machines with
static resource definitions.

How about using the resource structures directly rather than introducing
the table which is effectively holding the same information? Something
like this?

In vt8500_resources.c (and similarly for wm8505_resources.c):

static struct resource vt8500_resources_uart0[] __initdata = {
	[0] = {
		.flags	= IORESOURCE_MEM,
		.start  = VT8500_UART0_PHYS_BASE,
		.end	= VT8500_UART0_PHYS_BASE + 0xff,
	},
	[1] = {
		.flags	= IORESOURCE_IRQ,
		.start	= VT8500_UART0_IRQ,
		.end	= VT8500_UART0_IRQ,
	},
};

struct resource *vt8500_resources[] __initdata = {
	[VT8500_UART0] = &vt8500_resources_uart0,
	...
};

In devices.c:

extern struct resource *vt8500_resources;
extern struct resource *wm8505_resources;

/* Set this pointer according to board variant */
static struct resource *resources;

void __init wmt_set_resources(void)
{
	vt8500_device_uart0.resource = resources[VT8500_UART0];
	...
}

This way we only have a single externed resource structure per
board-variant, there is no additional table needed, and the resource
definitions can be read clearly. Alternatively the wmt_regmaps/wmt_irqs
tables could be modified to use struct resource rather than individual
fields which would simplify the assignments later.

~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-21  2:32                               ` Ryan Mallon
@ 2010-12-21 10:00                                 ` Alexey Charkov
  2010-12-21 12:05                                   ` Arnd Bergmann
  2010-12-21 19:39                                   ` Ryan Mallon
  0 siblings, 2 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-12-21 10:00 UTC (permalink / raw)
  To: Ryan Mallon
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
> On 12/21/2010 12:49 PM, Alexey Charkov wrote:
>> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>>> On 12/21/2010 12:00 PM, Alexey Charkov wrote:
>>>> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>>>>> On 12/21/2010 10:48 AM, Alexey Charkov wrote:
>>>>>> 2010/12/20 Ryan Mallon <ryan@bluewatersys.com>:
>>>>>>> On 12/21/2010 08:54 AM, Alexey Charkov wrote:
>>>>>>>> This adds support for the family of Systems-on-Chip produced initially
>>>>>>>> by VIA and now its subsidiary WonderMedia that have recently become
>>>>>>>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>>>>>>>
>>>>>>>> Support is included for both VT8500 and WM8505, selectable by a
>>>>>>>> configuration switch at kernel build time.
>>>>>>>>
>>>>>>>> Included are basic machine initialization files, register and
>>>>>>>> interrupt definitions, support for the on-chip interrupt controller,
>>>>>>>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>>>>>>>> pulse-width-modulated outputs control, as well as platform device
>>>>>>>> configurations for the specific drivers implemented elsewhere.
>>>>>>>>
>>>>>>>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>>>>>>>
>>>>>>> Hi Alexey,
>>>>>>>
>>>>>>> Quick review below.
>>>>>
>>>>>> <snip>
>>>>>>>> +void __init wmt_set_resources(void)
>>>>>>>> +{
>>>>>>>> +     resources_lcdc[0].start = wmt_current_regs->lcdc;
>>>>>>>> +     resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
>>>>>>>> +     resources_lcdc[1].start = wmt_current_irqs->lcdc;
>>>>>>>> +     resources_lcdc[1].end = wmt_current_irqs->lcdc;
>>>>>>>
>>>>>>> Ah, this makes more sense. But why have all the indirection? The
>>>>>>> wmt_regmaps table could just be replaced with #defines and then have
>>>>>>> separate device files for the VT8500 and the WM8505. This would also
>>>>>>> make clearer which variants have which peripherals.
>>>>>>>
>>>>>>
>>>>>> This was the way I implemented it originally. However, Arnd made quite
>>>>>> a valid suggestion to allow runtime selection of the chip variant,
>>>>>> thus registers and interrupts need to be held in an indexed data type
>>>>>> instead of just compile-time macros. In addition, there is now some
>>>>>> overall movement towards unification of binary kernel images for
>>>>>> different ARM variants (as far as I can see), so this would be
>>>>>> required in any case.
>>>>>>
>>>>>> Furthermore, as with many unbranded Chinese products, it's somewhat
>>>>>> difficult to reliably determine the exact chip version used in your
>>>>>> netbook without disassembling it. Reading a hardware register for
>>>>>> identification is easier :)
>>>>>
>>>>> Okay, that makes sense. I still think there must be a better way than
>>>>> having a massive indirect table with all the values. Why not detect the
>>>>> variant in the core code and then have something like:
>>>>>
>>>>> int init_devices(void)
>>>>> {
>>>>>        int board_type = detect_board_type();
>>>>>
>>>>>        switch (board_type) {
>>>>>        case BOARD_TYPE_VT8500:
>>>>>                return vt8500_init_devices();
>>>>>
>>>>>        case BOARD_TYPE_WM8505:
>>>>>                return wm8500_init_devices();
>>>>>        }
>>>>>
>>>>>        pr_err("Unknown board type\n");
>>>>>        BUG(); /* panic()? */
>>>>>        while (1)
>>>>>                ;
>>>>> }
>>>>>
>>>>> Then you can have the peripheral setup for each of the variants in their
>>>>> own files and use #defines. It may get tricky in a couple of places if
>>>>> you need to be able to access some value directly which is different on
>>>>> each of the variants, but that can be handled on a case by case basis.
>>>>> The majority of the numbers will be passed into drivers via the resource
>>>>> structs.
>>>>>
>>>>
>>>> This is more or less what I'm doing right now - except for the
>>>> separation between different files. I tried to avoid duplication of
>>>> similar things here. Is the indirect table really a big issue? I'm a
>>>> bit reluctant to copy about the whole devices.c for each chip variant,
>>>> which would be otherwise required. Further, it would add more
>>>> complexity to the timer, irq, gpio, i8042 and probably some other
>>>> places.
>>>
>>> Yeah, agreed about the duplication. My approach would require the common
>>> peripherals to be defined for each variant. I'm not entirely against the
>>> indirect table (and am even starting to think it may be the best overall
>>> solution), it's just that it can be a bit difficult to follow because to
>>> add a device you need to do the following:
>>>
>>>  - Add a partially empty resource/platform_device struct
>>>  - Add resource entries to the resource table definition
>>>  - Add resource values to the resource table
>>>  - Add assignment of resource values to device init code
>>>
>>
>> That's actually only one step more than what machines with static
>> resource definitions require (the last one). Flexibility does come at
>> a cost, so there should be a mathematical limit to optimization of
>> this thing :)
>
> No it isn't. You don't have the massive table, which requires
> modifications to both the definition and declaration, on machines with
> static resource definitions.
>
> How about using the resource structures directly rather than introducing
> the table which is effectively holding the same information? Something
> like this?
>
> In vt8500_resources.c (and similarly for wm8505_resources.c):
>
> static struct resource vt8500_resources_uart0[] __initdata = {
>        [0] = {
>                .flags  = IORESOURCE_MEM,
>                .start  = VT8500_UART0_PHYS_BASE,
>                .end    = VT8500_UART0_PHYS_BASE + 0xff,
>        },
>        [1] = {
>                .flags  = IORESOURCE_IRQ,
>                .start  = VT8500_UART0_IRQ,
>                .end    = VT8500_UART0_IRQ,
>        },
> };
>
> struct resource *vt8500_resources[] __initdata = {
>        [VT8500_UART0] = &vt8500_resources_uart0,
>        ...
> };
>
> In devices.c:
>
> extern struct resource *vt8500_resources;
> extern struct resource *wm8505_resources;
>
> /* Set this pointer according to board variant */
> static struct resource *resources;
>
> void __init wmt_set_resources(void)
> {
>        vt8500_device_uart0.resource = resources[VT8500_UART0];
>        ...
> }
>
> This way we only have a single externed resource structure per
> board-variant, there is no additional table needed, and the resource
> definitions can be read clearly. Alternatively the wmt_regmaps/wmt_irqs
> tables could be modified to use struct resource rather than individual
> fields which would simplify the assignments later.

This way we will again duplicate quite much: those files will mostly
differ in just the macro definitions of specific registers/irqs.

What if I just move all the initializations inside my runtime helper
function, and add a macro to save space and improve readability?
Something along the lines of:

static struct resource resources_lcdc[2] __initdata;

#define WMT_MMIO_RES(__start, __size) \
{\
        .flags = IORESOURCE_MEM,\
        .start = __start,\
        .end = __start + __size - 1,\
}
#define WMT_IRQ_RES(__irq) \
{\
        .flags = IORESOURCE_IRQ,\
        .start = __irq,\
        .end = __irq,\
}

void __init wmt_set_resources(void)
{
     resources_lcdc[0] = WMT_MMIO_RES(wmt_current_regs->lcdc, SZ_1K);
     resources_lcdc[1] = WMT_IRQ_RES(wmt_current_irqs->lcdc);
...
}

Then there will be no half-empty initializations scattered around
separate from the other assignments (which is probably the worst thing
in current configuration).

Best regards,
Alexey

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-21 10:00                                 ` Alexey Charkov
@ 2010-12-21 12:05                                   ` Arnd Bergmann
  2010-12-21 19:39                                   ` Ryan Mallon
  1 sibling, 0 replies; 91+ messages in thread
From: Arnd Bergmann @ 2010-12-21 12:05 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Alexey Charkov, Ryan Mallon, Eric Miao, linux-kernel,
	Uwe Kleine-König, vt8500-wm8505-linux-kernel,
	Russell King - ARM Linux, Albin Tonnerre

On Tuesday 21 December 2010 11:00:43 Alexey Charkov wrote:
> #define WMT_IRQ_RES(__irq) \
> {\
>         .flags = IORESOURCE_IRQ,\
>         .start = __irq,\
>         .end = __irq,\
> }
> 
> void __init wmt_set_resources(void)
> {
>      resources_lcdc[0] = WMT_MMIO_RES(wmt_current_regs->lcdc, SZ_1K);
>      resources_lcdc[1] = WMT_IRQ_RES(wmt_current_irqs->lcdc);
> ...
> }
> 
> Then there will be no half-empty initializations scattered around
> separate from the other assignments (which is probably the worst thing
> in current configuration).

If you use platform_device_add_resources() to add the resource
at a later point, or platform_device_register_simple, you don't
even need to statically allocate the resource structure, which may
also make this simpler because you can keep all the data structures
local to the function filling them out.

	Arnd

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

* Re: [PATCH 1/6 v9] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-21 10:00                                 ` Alexey Charkov
  2010-12-21 12:05                                   ` Arnd Bergmann
@ 2010-12-21 19:39                                   ` Ryan Mallon
  2010-12-22 21:18                                     ` [PATCH 1/6 v10] " Alexey Charkov
  1 sibling, 1 reply; 91+ messages in thread
From: Ryan Mallon @ 2010-12-21 19:39 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

On 12/21/2010 11:00 PM, Alexey Charkov wrote:
> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>   
>> On 12/21/2010 12:49 PM, Alexey Charkov wrote:
>>     
>>> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>>>       
>>>> On 12/21/2010 12:00 PM, Alexey Charkov wrote:
>>>>         
>>>>> 2010/12/21 Ryan Mallon <ryan@bluewatersys.com>:
>>>>>           
>>>>>> On 12/21/2010 10:48 AM, Alexey Charkov wrote:
>>>>>>             
>>>>>>> 2010/12/20 Ryan Mallon <ryan@bluewatersys.com>:
>>>>>>>               
>>>>>>>> On 12/21/2010 08:54 AM, Alexey Charkov wrote:
>>>>>>>>                 
>>>>>>>>> This adds support for the family of Systems-on-Chip produced initially
>>>>>>>>> by VIA and now its subsidiary WonderMedia that have recently become
>>>>>>>>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>>>>>>>>
>>>>>>>>> Support is included for both VT8500 and WM8505, selectable by a
>>>>>>>>> configuration switch at kernel build time.
>>>>>>>>>
>>>>>>>>> Included are basic machine initialization files, register and
>>>>>>>>> interrupt definitions, support for the on-chip interrupt controller,
>>>>>>>>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>>>>>>>>> pulse-width-modulated outputs control, as well as platform device
>>>>>>>>> configurations for the specific drivers implemented elsewhere.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>>>>>>>>>                   
>>>>>>>> Hi Alexey,
>>>>>>>>
>>>>>>>> Quick review below.
>>>>>>>>                 
>>>>>>             
>>>>>>> <snip>
>>>>>>>               
>>>>>>>>> +void __init wmt_set_resources(void)
>>>>>>>>> +{
>>>>>>>>> +     resources_lcdc[0].start = wmt_current_regs->lcdc;
>>>>>>>>> +     resources_lcdc[0].end = wmt_current_regs->lcdc + SZ_1K - 1;
>>>>>>>>> +     resources_lcdc[1].start = wmt_current_irqs->lcdc;
>>>>>>>>> +     resources_lcdc[1].end = wmt_current_irqs->lcdc;
>>>>>>>>>                   
>>>>>>>> Ah, this makes more sense. But why have all the indirection? The
>>>>>>>> wmt_regmaps table could just be replaced with #defines and then have
>>>>>>>> separate device files for the VT8500 and the WM8505. This would also
>>>>>>>> make clearer which variants have which peripherals.
>>>>>>>>
>>>>>>>>                 
>>>>>>> This was the way I implemented it originally. However, Arnd made quite
>>>>>>> a valid suggestion to allow runtime selection of the chip variant,
>>>>>>> thus registers and interrupts need to be held in an indexed data type
>>>>>>> instead of just compile-time macros. In addition, there is now some
>>>>>>> overall movement towards unification of binary kernel images for
>>>>>>> different ARM variants (as far as I can see), so this would be
>>>>>>> required in any case.
>>>>>>>
>>>>>>> Furthermore, as with many unbranded Chinese products, it's somewhat
>>>>>>> difficult to reliably determine the exact chip version used in your
>>>>>>> netbook without disassembling it. Reading a hardware register for
>>>>>>> identification is easier :)
>>>>>>>               
>>>>>> Okay, that makes sense. I still think there must be a better way than
>>>>>> having a massive indirect table with all the values. Why not detect the
>>>>>> variant in the core code and then have something like:
>>>>>>
>>>>>> int init_devices(void)
>>>>>> {
>>>>>>        int board_type = detect_board_type();
>>>>>>
>>>>>>        switch (board_type) {
>>>>>>        case BOARD_TYPE_VT8500:
>>>>>>                return vt8500_init_devices();
>>>>>>
>>>>>>        case BOARD_TYPE_WM8505:
>>>>>>                return wm8500_init_devices();
>>>>>>        }
>>>>>>
>>>>>>        pr_err("Unknown board type\n");
>>>>>>        BUG(); /* panic()? */
>>>>>>        while (1)
>>>>>>                ;
>>>>>> }
>>>>>>
>>>>>> Then you can have the peripheral setup for each of the variants in their
>>>>>> own files and use #defines. It may get tricky in a couple of places if
>>>>>> you need to be able to access some value directly which is different on
>>>>>> each of the variants, but that can be handled on a case by case basis.
>>>>>> The majority of the numbers will be passed into drivers via the resource
>>>>>> structs.
>>>>>>
>>>>>>             
>>>>> This is more or less what I'm doing right now - except for the
>>>>> separation between different files. I tried to avoid duplication of
>>>>> similar things here. Is the indirect table really a big issue? I'm a
>>>>> bit reluctant to copy about the whole devices.c for each chip variant,
>>>>> which would be otherwise required. Further, it would add more
>>>>> complexity to the timer, irq, gpio, i8042 and probably some other
>>>>> places.
>>>>>           
>>>> Yeah, agreed about the duplication. My approach would require the common
>>>> peripherals to be defined for each variant. I'm not entirely against the
>>>> indirect table (and am even starting to think it may be the best overall
>>>> solution), it's just that it can be a bit difficult to follow because to
>>>> add a device you need to do the following:
>>>>
>>>>  - Add a partially empty resource/platform_device struct
>>>>  - Add resource entries to the resource table definition
>>>>  - Add resource values to the resource table
>>>>  - Add assignment of resource values to device init code
>>>>
>>>>         
>>> That's actually only one step more than what machines with static
>>> resource definitions require (the last one). Flexibility does come at
>>> a cost, so there should be a mathematical limit to optimization of
>>> this thing :)
>>>       
>> No it isn't. You don't have the massive table, which requires
>> modifications to both the definition and declaration, on machines with
>> static resource definitions.
>>
>> How about using the resource structures directly rather than introducing
>> the table which is effectively holding the same information? Something
>> like this?
>>
>> In vt8500_resources.c (and similarly for wm8505_resources.c):
>>
>> static struct resource vt8500_resources_uart0[] __initdata = {
>>        [0] = {
>>                .flags  = IORESOURCE_MEM,
>>                .start  = VT8500_UART0_PHYS_BASE,
>>                .end    = VT8500_UART0_PHYS_BASE + 0xff,
>>        },
>>        [1] = {
>>                .flags  = IORESOURCE_IRQ,
>>                .start  = VT8500_UART0_IRQ,
>>                .end    = VT8500_UART0_IRQ,
>>        },
>> };
>>
>> struct resource *vt8500_resources[] __initdata = {
>>        [VT8500_UART0] = &vt8500_resources_uart0,
>>        ...
>> };
>>
>> In devices.c:
>>
>> extern struct resource *vt8500_resources;
>> extern struct resource *wm8505_resources;
>>
>> /* Set this pointer according to board variant */
>> static struct resource *resources;
>>
>> void __init wmt_set_resources(void)
>> {
>>        vt8500_device_uart0.resource = resources[VT8500_UART0];
>>        ...
>> }
>>
>> This way we only have a single externed resource structure per
>> board-variant, there is no additional table needed, and the resource
>> definitions can be read clearly. Alternatively the wmt_regmaps/wmt_irqs
>> tables could be modified to use struct resource rather than individual
>> fields which would simplify the assignments later.
>>     
> This way we will again duplicate quite much: those files will mostly
> differ in just the macro definitions of specific registers/irqs.
>
> What if I just move all the initializations inside my runtime helper
> function, and add a macro to save space and improve readability?
> Something along the lines of:
>
> static struct resource resources_lcdc[2] __initdata;
>
> #define WMT_MMIO_RES(__start, __size) \
> {\
>         .flags = IORESOURCE_MEM,\
>         .start = __start,\
>         .end = __start + __size - 1,\
> }
> #define WMT_IRQ_RES(__irq) \
> {\
>         .flags = IORESOURCE_IRQ,\
>         .start = __irq,\
>         .end = __irq,\
> }
>
> void __init wmt_set_resources(void)
> {
>      resources_lcdc[0] = WMT_MMIO_RES(wmt_current_regs->lcdc, SZ_1K);
>      resources_lcdc[1] = WMT_IRQ_RES(wmt_current_irqs->lcdc);
> ...
> }
>
> Then there will be no half-empty initializations scattered around
> separate from the other assignments (which is probably the worst thing
> in current configuration).
>
> Best regards,
> Alexey
>   
Sure, that works. In this case, can we remove the wmt_current_regs table
and just have a switch statement with the two board variants in place?
The latter will actually be half as much code because the struct
definition for the table is no longer needed and each resource
assignment is still one line. i.e.:

void __init wmt_set_resources(void)
{
	switch (board_type) {
	case BOARD_TYPE_VT8500:
		resources_lcdc[0] = WMT_MMIO_RES(VT8500_LCDC_BASE, SZ_1K);
		resources_lcdc[1] = WMT_IRQ_RES(VT8500_LCDC_IRQ);
		...
		break;

	case BOARD_TYPE_WM8505:
		resources_lcdc[0] = WMT_MMIO_RES(WM8505_LCDC_BASE, SZ_1K);
		resources_lcdc[1] = WMT_IRQ_RES(WM8505_LCDC_IRQ);
		...
		break;
	}
} 

~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751 
Fax:   +64 3 3779135			  USA 1800 261 2934


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

* [PATCH 1/6 v10] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-21 19:39                                   ` Ryan Mallon
@ 2010-12-22 21:18                                     ` Alexey Charkov
  2010-12-22 21:52                                       ` Ryan Mallon
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-12-22 21:18 UTC (permalink / raw)
  To: Ryan Mallon
  Cc: Alexey Charkov, Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505, selectable by a
configuration switch at kernel build time.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

Welcome the jubilee tenth revision of this patch ;-)

I've tried to incorporate the suggestions by Ryan and Arnd, hope that
there is nothing left out. There was a massive reorganization of code
to remove less-than-obvious magic with MMIO registers and irqs being
held in huge structs, now they are again macro definitions. Those
macros are, however, only included in single isolated files, and
actual values to use are chosen at runtime by calling the respective
routines at machine initialization. There are also stylistic changes
all around, where Ryan suggested.

As a result, i8042 should again be adjusted a bit to reflect the new
place to find respective register/irq definitions, that one will be
sent in the respective thread shortly.

Best regards,
Alexey

 arch/arm/Kconfig                                |   12 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 ++++
 arch/arm/mach-vt8500/Kconfig                    |   73 ++++++
 arch/arm/mach-vt8500/Makefile                   |    9 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   77 +++++++
 arch/arm/mach-vt8500/devices-vt8500.c           |  117 ++++++++++
 arch/arm/mach-vt8500/devices-wm8505.c           |  125 +++++++++++
 arch/arm/mach-vt8500/devices.c                  |  270 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   59 +++++
 arch/arm/mach-vt8500/gpio.c                     |  233 +++++++++++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 +++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 +++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/i8042.h       |   18 ++
 arch/arm/mach-vt8500/include/mach/io.h          |   28 +++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 +++
 arch/arm/mach-vt8500/include/mach/system.h      |   18 ++
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 +++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 +++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 ++
 arch/arm/mach-vt8500/include/mach/vt8500_irqs.h |   88 ++++++++
 arch/arm/mach-vt8500/include/mach/vt8500_regs.h |   79 +++++++
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 +++
 arch/arm/mach-vt8500/include/mach/wm8505_irqs.h |  115 ++++++++++
 arch/arm/mach-vt8500/include/mach/wm8505_regs.h |   78 +++++++
 arch/arm/mach-vt8500/irq.c                      |  177 +++++++++++++++
 arch/arm/mach-vt8500/pwm.c                      |  265 ++++++++++++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  155 +++++++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   77 +++++++
 34 files changed, 2372 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices-vt8500.c
 create mode 100644 arch/arm/mach-vt8500/devices-wm8505.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/i8042.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/wm8505_regs.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d56d21c..53052fa 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,16 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +983,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..2c20a34
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,73 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X576
+	bool "10-inch with 1024x576 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in CherryPal netbooks and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x576' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..81aedb7
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,9 @@
+obj-y += devices.o gpio.o irq.o timer.o
+
+obj-$(CONFIG_VTWM_VERSION_VT8500) += devices-vt8500.o
+obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..94a261d
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,77 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_gpio_base + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_pmc_base + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be remapped, not enabling power off!\n");
+
+	vt8500_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= vt8500_reserve_mem,
+	.map_io		= vt8500_map_io,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices-vt8500.c b/arch/arm/mach-vt8500/devices-vt8500.c
new file mode 100644
index 0000000..88384c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices-vt8500.c
@@ -0,0 +1,117 @@
+/* linux/arch/arm/mach-vt8500/devices-vt8500.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+#include <mach/vt8500_regs.h>
+#include <mach/vt8500_irqs.h>
+#include <mach/i8042.h>
+#include "devices.h"
+
+static inline struct resource WMT_MMIO_RES(u32 start, u32 size)
+{
+	struct resource tmp = {
+		.flags = IORESOURCE_MEM,
+		.start = start,
+		.end = start + size - 1,
+	};
+
+	return tmp;
+}
+
+static inline struct resource WMT_IRQ_RES(int irq)
+{
+	struct resource tmp = {
+		.flags = IORESOURCE_IRQ,
+		.start = irq,
+		.end = irq,
+	};
+
+	return tmp;
+}
+
+#define WMT_RES_ADD(__device, __resource, __num) \
+if (platform_device_add_resources(__device, __resource, __num)) \
+	pr_err("Failed to assign resources to __device##\n");
+
+void __init vt8500_set_resources(void)
+{
+	struct resource tmp[3];
+
+	tmp[0] = WMT_MMIO_RES(VT8500_LCDC_BASE, SZ_1K);
+	tmp[1] = WMT_IRQ_RES(IRQ_LCDC);
+	WMT_RES_ADD(&vt8500_device_lcdc, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(VT8500_UART0_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART0);
+	WMT_RES_ADD(&vt8500_device_uart0, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(VT8500_UART1_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART1);
+	WMT_RES_ADD(&vt8500_device_uart1, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(VT8500_UART2_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART2);
+	WMT_RES_ADD(&vt8500_device_uart2, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(VT8500_UART3_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART3);
+	WMT_RES_ADD(&vt8500_device_uart3, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(VT8500_EHCI_BASE, SZ_512);
+	tmp[1] = WMT_IRQ_RES(IRQ_EHCI);
+	WMT_RES_ADD(&vt8500_device_ehci, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(VT8500_GEGEA_BASE, SZ_256);
+	WMT_RES_ADD(&vt8500_device_ge_rops, tmp, 1);
+
+	tmp[0] = WMT_MMIO_RES(VT8500_PWM_BASE, 0x44);
+	WMT_RES_ADD(&vt8500_device_pwm, tmp, 1);
+
+	tmp[0] = WMT_MMIO_RES(VT8500_RTC_BASE, 0x2c);
+	tmp[1] = WMT_IRQ_RES(IRQ_RTC);
+	tmp[2] = WMT_IRQ_RES(IRQ_RTCSM);
+	WMT_RES_ADD(&vt8500_device_rtc, tmp, 3);
+}
+
+static void __init vt8500_set_externs(void)
+{
+	/* Non-resource-aware stuff */
+	wmt_ic_base = VT8500_IC_BASE;
+	wmt_gpio_base = VT8500_GPIO_BASE;
+	wmt_pmc_base = VT8500_PMC_BASE;
+	wmt_i8042_base = VT8500_PS2_BASE;
+
+	wmt_nr_irqs = VT8500_NR_IRQS;
+	wmt_timer_irq = IRQ_PMCOS0;
+	wmt_gpio_ext_irq[0] = IRQ_EXT0;
+	wmt_gpio_ext_irq[1] = IRQ_EXT1;
+	wmt_gpio_ext_irq[2] = IRQ_EXT2;
+	wmt_gpio_ext_irq[3] = IRQ_EXT3;
+	wmt_gpio_ext_irq[4] = IRQ_EXT4;
+	wmt_gpio_ext_irq[5] = IRQ_EXT5;
+	wmt_gpio_ext_irq[6] = IRQ_EXT6;
+	wmt_gpio_ext_irq[7] = IRQ_EXT7;
+	wmt_i8042_kbd_irq = IRQ_PS2KBD;
+	wmt_i8042_aux_irq = IRQ_PS2MOUSE;
+}
+
+void __init vt8500_map_io(void)
+{
+	iotable_init(wmt_io_desc, ARRAY_SIZE(wmt_io_desc));
+
+	/* Should be done before interrupts and timers are initialized */
+	vt8500_set_externs();
+}
diff --git a/arch/arm/mach-vt8500/devices-wm8505.c b/arch/arm/mach-vt8500/devices-wm8505.c
new file mode 100644
index 0000000..ad53e7d
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices-wm8505.c
@@ -0,0 +1,125 @@
+/* linux/arch/arm/mach-vt8500/devices-wm8505.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+#include <mach/wm8505_regs.h>
+#include <mach/wm8505_irqs.h>
+#include <mach/i8042.h>
+#include "devices.h"
+
+static inline struct resource WMT_MMIO_RES(u32 start, u32 size)
+{
+	struct resource tmp = {
+		.flags = IORESOURCE_MEM,
+		.start = start,
+		.end = start + size - 1,
+	};
+
+	return tmp;
+}
+
+static inline struct resource WMT_IRQ_RES(int irq)
+{
+	struct resource tmp = {
+		.flags = IORESOURCE_IRQ,
+		.start = irq,
+		.end = irq,
+	};
+
+	return tmp;
+}
+
+#define WMT_RES_ADD(__device, __resource, __num) \
+if (platform_device_add_resources(__device, __resource, __num)) \
+	pr_err("Failed to assign resources to __device##\n");
+
+void __init wm8505_set_resources(void)
+{
+	struct resource tmp[3];
+
+	tmp[0] = WMT_MMIO_RES(WM8505_GOVR_BASE, SZ_512);
+	WMT_RES_ADD(&vt8500_device_wm8505_fb, tmp, 1);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_UART0_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART0);
+	WMT_RES_ADD(&vt8500_device_uart0, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_UART1_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART1);
+	WMT_RES_ADD(&vt8500_device_uart1, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_UART2_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART2);
+	WMT_RES_ADD(&vt8500_device_uart2, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_UART3_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART3);
+	WMT_RES_ADD(&vt8500_device_uart3, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_UART4_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART4);
+	WMT_RES_ADD(&vt8500_device_uart4, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_UART5_BASE, 0x1040);
+	tmp[1] = WMT_IRQ_RES(IRQ_UART5);
+	WMT_RES_ADD(&vt8500_device_uart5, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_EHCI_BASE, SZ_512);
+	tmp[1] = WMT_IRQ_RES(IRQ_EHCI);
+	WMT_RES_ADD(&vt8500_device_ehci, tmp, 2);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_GEGEA_BASE, SZ_256);
+	WMT_RES_ADD(&vt8500_device_ge_rops, tmp, 1);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_PWM_BASE, 0x44);
+	WMT_RES_ADD(&vt8500_device_pwm, tmp, 1);
+
+	tmp[0] = WMT_MMIO_RES(WM8505_RTC_BASE, 0x2c);
+	tmp[1] = WMT_IRQ_RES(IRQ_RTC);
+	tmp[2] = WMT_IRQ_RES(IRQ_RTCSM);
+	WMT_RES_ADD(&vt8500_device_rtc, tmp, 3);
+}
+
+static void __init wm8505_set_externs(void)
+{
+	/* Non-resource-aware stuff */
+	wmt_ic_base = WM8505_IC_BASE;
+	wmt_sic_base = WM8505_SIC_BASE;
+	wmt_gpio_base = WM8505_GPIO_BASE;
+	wmt_pmc_base = WM8505_PMC_BASE;
+	wmt_i8042_base = WM8505_PS2_BASE;
+
+	wmt_nr_irqs = WM8505_NR_IRQS;
+	wmt_timer_irq = IRQ_PMCOS0;
+	wmt_gpio_ext_irq[0] = IRQ_EXT0;
+	wmt_gpio_ext_irq[1] = IRQ_EXT1;
+	wmt_gpio_ext_irq[2] = IRQ_EXT2;
+	wmt_gpio_ext_irq[3] = IRQ_EXT3;
+	wmt_gpio_ext_irq[4] = IRQ_EXT4;
+	wmt_gpio_ext_irq[5] = IRQ_EXT5;
+	wmt_gpio_ext_irq[6] = IRQ_EXT6;
+	wmt_gpio_ext_irq[7] = IRQ_EXT7;
+	wmt_i8042_kbd_irq = IRQ_PS2KBD;
+	wmt_i8042_aux_irq = IRQ_PS2MOUSE;
+}
+
+void __init wm8505_map_io(void)
+{
+	iotable_init(wmt_io_desc, ARRAY_SIZE(wmt_io_desc));
+
+	/* Should be done before interrupts and timers are initialized */
+	wm8505_set_externs();
+}
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..1fcdc36
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,270 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+
+#include <mach/vt8500fb.h>
+#include <mach/i8042.h>
+#include "devices.h"
+
+/* These can't use resources currently */
+unsigned long wmt_ic_base __initdata;
+unsigned long wmt_sic_base __initdata;
+unsigned long wmt_gpio_base __initdata;
+unsigned long wmt_pmc_base __initdata;
+unsigned long wmt_i8042_base __initdata;
+
+int wmt_nr_irqs __initdata;
+int wmt_timer_irq __initdata;
+int wmt_gpio_ext_irq[8] __initdata;
+
+/* Should remain accessible after init.
+ * i8042 driver desperately calls for attention...
+ */
+int wmt_i8042_kbd_irq;
+int wmt_i8042_aux_irq;
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X576
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 576 * 2,
+	.mode		= {
+		.name		= "1024x576",
+		.xres		= 1024,
+		.yres		= 576,
+		.left_margin	= 40,
+		.right_margin	= 24,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 96,
+		.vsync_len	= 2,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 66,
+		.right_margin	= 2,
+		.upper_margin	= 19,
+		.lower_margin	= 1,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		if (strcmp(panels[i].mode.name, str) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= -1,
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+};
+
+struct map_desc wmt_io_desc[] __initdata = {
+	/* SoC MMIO registers */
+	[0] = {
+		.virtual	= 0xf8000000,
+		.pfn		= __phys_to_pfn(0xd8000000),
+		.length		= 0x00390000, /* max of all chip variants */
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..ba0418b
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,59 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+#include <asm/mach/map.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init vt8500_gpio_init(void);
+void __init vt8500_set_resources(void);
+void __init wm8505_set_resources(void);
+
+extern unsigned long wmt_ic_base __initdata;
+extern unsigned long wmt_sic_base __initdata;
+extern unsigned long wmt_gpio_base __initdata;
+extern unsigned long wmt_pmc_base __initdata;
+
+extern int wmt_nr_irqs __initdata;
+extern int wmt_timer_irq __initdata;
+extern int wmt_gpio_ext_irq[8] __initdata;
+
+extern struct map_desc wmt_io_desc[2] __initdata;
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..445397e
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,233 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include "devices.h"
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + vt8500_chip->regoff);
+
+	val |= (1 << vt8500_chip->shift << offset);
+	writel(val, regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + vt8500_chip->regoff);
+
+	val &= ~(1 << vt8500_chip->shift << offset);
+	writel(val, regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + 0x20 + vt8500_chip->regoff);
+
+	val &= ~(1 << vt8500_chip->shift << offset);
+	writel(val, regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + 0x20 + vt8500_chip->regoff);
+
+	val |= (1 << vt8500_chip->shift << offset);
+	writel(val, regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value) {
+		val = readl(regbase + 0x40 + vt8500_chip->regoff);
+		val |= (1 << vt8500_chip->shift << offset);
+		writel(val, regbase + 0x40 + vt8500_chip->regoff);
+	}
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + 0x40 + vt8500_chip->regoff);
+
+	if (value)
+		val |= (1 << vt8500_chip->shift << offset);
+	else
+		val &= ~(1 << vt8500_chip->shift << offset);
+
+	writel(val, regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0",	0,	0x0,	8,	4),
+	VT8500_GPIO_BANK("uart1",	4,	0x0,	12,	4),
+	VT8500_GPIO_BANK("spi0",	8,	0x0,	16,	4),
+	VT8500_GPIO_BANK("spi1",	12,	0x0,	20,	4),
+	VT8500_GPIO_BANK("spi2",	16,	0x0,	24,	4),
+	VT8500_GPIO_BANK("pwmout",	24,	0x0,	28,	2),
+
+	VT8500_GPIO_BANK("sdmmc",	0,	0x4,	30,	11),
+	VT8500_GPIO_BANK("ms",		16,	0x4,	41,	7),
+	VT8500_GPIO_BANK("i2c0",	24,	0x4,	48,	2),
+	VT8500_GPIO_BANK("i2c1",	26,	0x4,	50,	2),
+
+	VT8500_GPIO_BANK("mii",		0,	0x8,	52,	20),
+	VT8500_GPIO_BANK("see",		20,	0x8,	72,	4),
+	VT8500_GPIO_BANK("ide",		24,	0x8,	76,	7),
+
+	VT8500_GPIO_BANK("ccir",	0,	0xc,	83,	19),
+
+	VT8500_GPIO_BANK("ts",		8,	0x10,	102,	11),
+
+	VT8500_GPIO_BANK("lcd",		0,	0x14,	113,	23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	unsigned val = readl(regbase + 0x3c);
+
+	val &= ~(1 << offset);
+	writel(val, regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	unsigned val = readl(regbase + 0x3c);
+
+	val |= (1 << offset);
+	writel(val, regbase + 0x3c);
+
+	if (value) {
+		val = readl(regbase + 0x5c);
+		val |= (1 << offset);
+		writel(val, regbase + 0x5c);
+	}
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	unsigned val = readl(regbase + 0x5c);
+
+	if (value)
+		val |= (1 << offset);
+	else
+		val &= ~(1 << offset);
+
+	writel(val, regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	for (i = 0; i < 8; i++)
+		gpio_to_irq_map[i] = wmt_gpio_ext_irq[i];
+
+	regbase = ioremap(wmt_gpio_base, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/i8042.h b/arch/arm/mach-vt8500/include/mach/i8042.h
new file mode 100644
index 0000000..cd7143c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/i8042.h
@@ -0,0 +1,18 @@
+/* arch/arm/mach-vt8500/include/mach/i8042.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+extern unsigned long wmt_i8042_base __initdata;
+extern int wmt_i8042_kbd_irq;
+extern int wmt_i8042_aux_irq;
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..9077239
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		__typesafe_io((a) + 0xf0000000)
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..d6c757e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,18 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR_VIRT	0xf8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	writel(1, VT8500_PMSR_VIRT);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..4642290
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END	0xd0000000UL
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h b/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
new file mode 100644
index 0000000..ecfee91
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
@@ -0,0 +1,88 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* VT8500 Interrupt Sources */
+
+#define IRQ_JPEGENC	0	/* JPEG Encoder */
+#define IRQ_JPEGDEC	1	/* JPEG Decoder */
+				/* Reserved */
+#define IRQ_PATA	3	/* PATA Controller */
+				/* Reserved */
+#define IRQ_DMA		5	/* DMA Controller */
+#define IRQ_EXT0	6	/* External Interrupt 0 */
+#define IRQ_EXT1	7	/* External Interrupt 1 */
+#define IRQ_GE		8	/* Graphic Engine */
+#define IRQ_GOV		9	/* Graphic Overlay Engine */
+#define IRQ_ETHER	10	/* Ethernet MAC */
+#define IRQ_MPEGTS	11	/* Transport Stream Interface */
+#define IRQ_LCDC	12	/* LCD Controller */
+#define IRQ_EXT2	13	/* External Interrupt 2 */
+#define IRQ_EXT3	14	/* External Interrupt 3 */
+#define IRQ_EXT4	15	/* External Interrupt 4 */
+#define IRQ_CIPHER	16	/* Cipher */
+#define IRQ_VPP		17	/* Video Post-Processor */
+#define IRQ_I2C1	18	/* I2C 1 */
+#define IRQ_I2C0	19	/* I2C 0 */
+#define IRQ_SDMMC	20	/* SD/MMC Controller */
+#define IRQ_SDMMC_DMA	21	/* SD/MMC Controller DMA */
+#define IRQ_PMC_WU	22	/* Power Management Controller Wakeup */
+				/* Reserved */
+#define IRQ_SPI0	24	/* SPI 0 */
+#define IRQ_SPI1	25	/* SPI 1 */
+#define IRQ_SPI2	26	/* SPI 2 */
+#define IRQ_LCDDF	27	/* LCD Data Formatter */
+#define IRQ_NAND	28	/* NAND Flash Controller */
+#define IRQ_NAND_DMA	29	/* NAND Flash Controller DMA */
+#define IRQ_MS		30	/* MemoryStick Controller */
+#define IRQ_MS_DMA	31	/* MemoryStick Controller DMA */
+#define IRQ_UART0	32	/* UART 0 */
+#define IRQ_UART1	33	/* UART 1 */
+#define IRQ_I2S		34	/* I2S */
+#define IRQ_PCM		35	/* PCM */
+#define IRQ_PMCOS0	36	/* PMC OS Timer 0 */
+#define IRQ_PMCOS1	37	/* PMC OS Timer 1 */
+#define IRQ_PMCOS2	38	/* PMC OS Timer 2 */
+#define IRQ_PMCOS3	39	/* PMC OS Timer 3 */
+#define IRQ_VPU		40	/* Video Processing Unit */
+#define IRQ_VID		41	/* Video Digital Input Interface */
+#define IRQ_AC97	42	/* AC97 Interface */
+#define IRQ_EHCI	43	/* USB */
+#define IRQ_NOR		44	/* NOR Flash Controller */
+#define IRQ_PS2MOUSE	45	/* PS/2 Mouse */
+#define IRQ_PS2KBD	46	/* PS/2 Keyboard */
+#define IRQ_UART2	47	/* UART 2 */
+#define IRQ_RTC		48	/* RTC Interrupt */
+#define IRQ_RTCSM	49	/* RTC Second/Minute Update Interrupt */
+#define IRQ_UART3	50	/* UART 3 */
+#define IRQ_ADC		51	/* ADC */
+#define IRQ_EXT5	52	/* External Interrupt 5 */
+#define IRQ_EXT6	53	/* External Interrupt 6 */
+#define IRQ_EXT7	54	/* External Interrupt 7 */
+#define IRQ_CIR		55	/* CIR */
+#define IRQ_DMA0	56	/* DMA Channel 0 */
+#define IRQ_DMA1	57	/* DMA Channel 1 */
+#define IRQ_DMA2	58	/* DMA Channel 2 */
+#define IRQ_DMA3	59	/* DMA Channel 3 */
+#define IRQ_DMA4	60	/* DMA Channel 4 */
+#define IRQ_DMA5	61	/* DMA Channel 5 */
+#define IRQ_DMA6	62	/* DMA Channel 6 */
+#define IRQ_DMA7	63	/* DMA Channel 7 */
+
+#define VT8500_NR_IRQS		64
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500_regs.h b/arch/arm/mach-vt8500/include/mach/vt8500_regs.h
new file mode 100644
index 0000000..29c63ec
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500_regs.h
@@ -0,0 +1,79 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vt8500_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_VT8500_REGS_H
+#define __ASM_ARM_ARCH_VT8500_REGS_H
+
+/* VT8500 Registers Map */
+
+#define VT8500_REGS_START_PHYS	0xd8000000	/* Start of MMIO registers */
+#define VT8500_REGS_START_VIRT	0xf8000000	/* Virtual mapping start */
+
+#define VT8500_DDR_BASE		0xd8000000	/* 1k	DDR/DDR2 Memory
+							Controller */
+#define VT8500_DMA_BASE		0xd8001000	/* 1k	DMA Controller */
+#define VT8500_SFLASH_BASE	0xd8002000	/* 1k	Serial Flash Memory
+							Controller */
+#define VT8500_ETHER_BASE	0xd8004000	/* 1k	Ethernet MAC 0 */
+#define VT8500_CIPHER_BASE	0xd8006000	/* 4k	Cipher */
+#define VT8500_USB_BASE		0xd8007800	/* 2k	USB OTG */
+# define VT8500_EHCI_BASE	0xd8007900	/*	EHCI */
+# define VT8500_UHCI_BASE	0xd8007b01	/*	UHCI */
+#define VT8500_PATA_BASE	0xd8008000	/* 512	PATA */
+#define VT8500_PS2_BASE		0xd8008800	/* 1k	PS/2 */
+#define VT8500_NAND_BASE	0xd8009000	/* 1k	NAND Controller */
+#define VT8500_NOR_BASE		0xd8009400	/* 1k	NOR Controller */
+#define VT8500_SDMMC_BASE	0xd800a000	/* 1k	SD/MMC Controller */
+#define VT8500_MS_BASE		0xd800b000	/* 1k	MS/MSPRO Controller */
+#define VT8500_LCDC_BASE	0xd800e400	/* 1k	LCD Controller */
+#define VT8500_VPU_BASE		0xd8050000	/* 256	VPU */
+#define VT8500_GOV_BASE		0xd8050300	/* 256	GOV */
+#define VT8500_GEGEA_BASE	0xd8050400	/* 768	GE/GE Alpha Mixing */
+#define VT8500_LCDF_BASE	0xd8050900	/* 256	LCD Formatter */
+#define VT8500_VID_BASE		0xd8050a00	/* 256	VID */
+#define VT8500_VPP_BASE		0xd8050b00	/* 256	VPP */
+#define VT8500_TSBK_BASE	0xd80f4000	/* 4k	TSBK */
+#define VT8500_JPEGDEC_BASE	0xd80fe000	/* 4k	JPEG Decoder */
+#define VT8500_JPEGENC_BASE	0xd80ff000	/* 4k	JPEG Encoder */
+#define VT8500_RTC_BASE		0xd8100000	/* 64k	RTC */
+#define VT8500_GPIO_BASE	0xd8110000	/* 64k	GPIO Configuration */
+#define VT8500_SCC_BASE		0xd8120000	/* 64k	System Configuration*/
+#define VT8500_PMC_BASE		0xd8130000	/* 64k	PMC Configuration */
+#define VT8500_IC_BASE		0xd8140000	/* 64k	Interrupt Controller*/
+#define VT8500_UART0_BASE	0xd8200000	/* 64k	UART 0 */
+#define VT8500_UART2_BASE	0xd8210000	/* 64k	UART 2 */
+#define VT8500_PWM_BASE		0xd8220000	/* 64k	PWM Configuration */
+#define VT8500_SPI0_BASE	0xd8240000	/* 64k	SPI 0 */
+#define VT8500_SPI1_BASE	0xd8250000	/* 64k	SPI 1 */
+#define VT8500_CIR_BASE		0xd8270000	/* 64k	CIR */
+#define VT8500_I2C0_BASE	0xd8280000	/* 64k	I2C 0 */
+#define VT8500_AC97_BASE	0xd8290000	/* 64k	AC97 */
+#define VT8500_SPI2_BASE	0xd82a0000	/* 64k	SPI 2 */
+#define VT8500_UART1_BASE	0xd82b0000	/* 64k	UART 1 */
+#define VT8500_UART3_BASE	0xd82c0000	/* 64k	UART 3 */
+#define VT8500_PCM_BASE		0xd82d0000	/* 64k	PCM */
+#define VT8500_I2C1_BASE	0xd8320000	/* 64k	I2C 1 */
+#define VT8500_I2S_BASE		0xd8330000	/* 64k	I2S */
+#define VT8500_ADC_BASE		0xd8340000	/* 64k	ADC */
+
+#define VT8500_REGS_END_PHYS	0xd834ffff	/* End of MMIO registers */
+#define VT8500_REGS_LENGTH	(VT8500_REGS_END_PHYS \
+				- VT8500_REGS_START_PHYS + 1)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..7f399c3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	u32			xres_virtual;
+	u32			yres_virtual;
+	u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h b/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
new file mode 100644
index 0000000..6128627
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
@@ -0,0 +1,115 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* WM8505 Interrupt Sources */
+
+#define IRQ_UHCI	0	/* UHC FS (UHCI?) */
+#define IRQ_EHCI	1	/* UHC HS */
+#define IRQ_UDCDMA	2	/* UDC DMA */
+				/* Reserved */
+#define IRQ_PS2MOUSE	4	/* PS/2 Mouse */
+#define IRQ_UDC		5	/* UDC */
+#define IRQ_EXT0	6	/* External Interrupt 0 */
+#define IRQ_EXT1	7	/* External Interrupt 1 */
+#define IRQ_KEYPAD	8	/* Keypad */
+#define IRQ_DMA		9	/* DMA Controller */
+#define IRQ_ETHER	10	/* Ethernet MAC */
+				/* Reserved */
+				/* Reserved */
+#define IRQ_EXT2	13	/* External Interrupt 2 */
+#define IRQ_EXT3	14	/* External Interrupt 3 */
+#define IRQ_EXT4	15	/* External Interrupt 4 */
+#define IRQ_APB		16	/* APB Bridge */
+#define IRQ_DMA0	17	/* DMA Channel 0 */
+#define IRQ_I2C1	18	/* I2C 1 */
+#define IRQ_I2C0	19	/* I2C 0 */
+#define IRQ_SDMMC	20	/* SD/MMC Controller */
+#define IRQ_SDMMC_DMA	21	/* SD/MMC Controller DMA */
+#define IRQ_PMC_WU	22	/* Power Management Controller Wakeup */
+#define IRQ_PS2KBD	23	/* PS/2 Keyboard */
+#define IRQ_SPI0	24	/* SPI 0 */
+#define IRQ_SPI1	25	/* SPI 1 */
+#define IRQ_SPI2	26	/* SPI 2 */
+#define IRQ_DMA1	27	/* DMA Channel 1 */
+#define IRQ_NAND	28	/* NAND Flash Controller */
+#define IRQ_NAND_DMA	29	/* NAND Flash Controller DMA */
+#define IRQ_UART5	30	/* UART 5 */
+#define IRQ_UART4	31	/* UART 4 */
+#define IRQ_UART0	32	/* UART 0 */
+#define IRQ_UART1	33	/* UART 1 */
+#define IRQ_DMA2	34	/* DMA Channel 2 */
+#define IRQ_I2S		35	/* I2S */
+#define IRQ_PMCOS0	36	/* PMC OS Timer 0 */
+#define IRQ_PMCOS1	37	/* PMC OS Timer 1 */
+#define IRQ_PMCOS2	38	/* PMC OS Timer 2 */
+#define IRQ_PMCOS3	39	/* PMC OS Timer 3 */
+#define IRQ_DMA3	40	/* DMA Channel 3 */
+#define IRQ_DMA4	41	/* DMA Channel 4 */
+#define IRQ_AC97	42	/* AC97 Interface */
+				/* Reserved */
+#define IRQ_NOR		44	/* NOR Flash Controller */
+#define IRQ_DMA5	45	/* DMA Channel 5 */
+#define IRQ_DMA6	46	/* DMA Channel 6 */
+#define IRQ_UART2	47	/* UART 2 */
+#define IRQ_RTC		48	/* RTC Interrupt */
+#define IRQ_RTCSM	49	/* RTC Second/Minute Update Interrupt */
+#define IRQ_UART3	50	/* UART 3 */
+#define IRQ_DMA7	51	/* DMA Channel 7 */
+#define IRQ_EXT5	52	/* External Interrupt 5 */
+#define IRQ_EXT6	53	/* External Interrupt 6 */
+#define IRQ_EXT7	54	/* External Interrupt 7 */
+#define IRQ_CIR		55	/* CIR */
+#define IRQ_SIC0	56	/* SIC IRQ0 */
+#define IRQ_SIC1	57	/* SIC IRQ1 */
+#define IRQ_SIC2	58	/* SIC IRQ2 */
+#define IRQ_SIC3	59	/* SIC IRQ3 */
+#define IRQ_SIC4	60	/* SIC IRQ4 */
+#define IRQ_SIC5	61	/* SIC IRQ5 */
+#define IRQ_SIC6	62	/* SIC IRQ6 */
+#define IRQ_SIC7	63	/* SIC IRQ7 */
+				/* Reserved */
+#define IRQ_JPEGDEC	65	/* JPEG Decoder */
+#define IRQ_SAE		66	/* SAE (?) */
+				/* Reserved */
+#define IRQ_VPU		79	/* Video Processing Unit */
+#define IRQ_VPP		80	/* Video Post-Processor */
+#define IRQ_VID		81	/* Video Digital Input Interface */
+#define IRQ_SPU		82	/* SPU (?) */
+#define IRQ_PIP		83	/* PIP Error */
+#define IRQ_GE		84	/* Graphic Engine */
+#define IRQ_GOV		85	/* Graphic Overlay Engine */
+#define IRQ_DVO		86	/* Digital Video Output */
+				/* Reserved */
+#define IRQ_DMA8	92	/* DMA Channel 8 */
+#define IRQ_DMA9	93	/* DMA Channel 9 */
+#define IRQ_DMA10	94	/* DMA Channel 10 */
+#define IRQ_DMA11	95	/* DMA Channel 11 */
+#define IRQ_DMA12	96	/* DMA Channel 12 */
+#define IRQ_DMA13	97	/* DMA Channel 13 */
+#define IRQ_DMA14	98	/* DMA Channel 14 */
+#define IRQ_DMA15	99	/* DMA Channel 15 */
+				/* Reserved */
+#define IRQ_GOVW	111	/* GOVW (?) */
+#define IRQ_GOVRSDSCD	112	/* GOVR SDSCD (?) */
+#define IRQ_GOVRSDMIF	113	/* GOVR SDMIF (?) */
+#define IRQ_GOVRHDSCD	114	/* GOVR HDSCD (?) */
+#define IRQ_GOVRHDMIF	115	/* GOVR HDMIF (?) */
+
+#define WM8505_NR_IRQS		116
diff --git a/arch/arm/mach-vt8500/include/mach/wm8505_regs.h b/arch/arm/mach-vt8500/include/mach/wm8505_regs.h
new file mode 100644
index 0000000..df15509
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/wm8505_regs.h
@@ -0,0 +1,78 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/wm8505_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_WM8505_REGS_H
+#define __ASM_ARM_ARCH_WM8505_REGS_H
+
+/* WM8505 Registers Map */
+
+#define WM8505_REGS_START_PHYS	0xd8000000	/* Start of MMIO registers */
+#define WM8505_REGS_START_VIRT	0xf8000000	/* Virtual mapping start */
+
+#define WM8505_DDR_BASE		0xd8000400	/* 1k	DDR/DDR2 Memory
+							Controller */
+#define WM8505_DMA_BASE		0xd8001800	/* 1k	DMA Controller */
+#define WM8505_VDMA_BASE	0xd8001c00	/* 1k	VDMA */
+#define WM8505_SFLASH_BASE	0xd8002000	/* 1k	Serial Flash Memory
+							Controller */
+#define WM8505_ETHER_BASE	0xd8004000	/* 1k	Ethernet MAC 0 */
+#define WM8505_CIPHER_BASE	0xd8006000	/* 4k	Cipher */
+#define WM8505_USB_BASE		0xd8007000	/* 2k	USB 2.0 Host */
+# define WM8505_EHCI_BASE	0xd8007100	/*	EHCI */
+# define WM8505_UHCI_BASE	0xd8007301	/*	UHCI */
+#define WM8505_PS2_BASE		0xd8008800	/* 1k	PS/2 */
+#define WM8505_NAND_BASE	0xd8009000	/* 1k	NAND Controller */
+#define WM8505_NOR_BASE		0xd8009400	/* 1k	NOR Controller */
+#define WM8505_SDMMC_BASE	0xd800a000	/* 1k	SD/MMC Controller */
+#define WM8505_VPU_BASE		0xd8050000	/* 256	VPU */
+#define WM8505_GOV_BASE		0xd8050300	/* 256	GOV */
+#define WM8505_GEGEA_BASE	0xd8050400	/* 768	GE/GE Alpha Mixing */
+#define WM8505_GOVR_BASE	0xd8050800	/* 512	GOVR (frambuffer) */
+#define WM8505_VID_BASE		0xd8050a00	/* 256	VID */
+#define WM8505_SCL_BASE		0xd8050d00	/* 256	SCL */
+#define WM8505_VPP_BASE		0xd8050f00	/* 256	VPP */
+#define WM8505_JPEGDEC_BASE	0xd80fe000	/* 4k	JPEG Decoder */
+#define WM8505_RTC_BASE		0xd8100000	/* 64k	RTC */
+#define WM8505_GPIO_BASE	0xd8110000	/* 64k	GPIO Configuration */
+#define WM8505_SCC_BASE		0xd8120000	/* 64k	System Configuration*/
+#define WM8505_PMC_BASE		0xd8130000	/* 64k	PMC Configuration */
+#define WM8505_IC_BASE		0xd8140000	/* 64k	Interrupt Controller*/
+#define WM8505_SIC_BASE		0xd8150000	/* 64k	Secondary IC */
+#define WM8505_UART0_BASE	0xd8200000	/* 64k	UART 0 */
+#define WM8505_UART2_BASE	0xd8210000	/* 64k	UART 2 */
+#define WM8505_PWM_BASE		0xd8220000	/* 64k	PWM Configuration */
+#define WM8505_SPI0_BASE	0xd8240000	/* 64k	SPI 0 */
+#define WM8505_SPI1_BASE	0xd8250000	/* 64k	SPI 1 */
+#define WM8505_KEYPAD_BASE	0xd8260000	/* 64k	Keypad control */
+#define WM8505_CIR_BASE		0xd8270000	/* 64k	CIR */
+#define WM8505_I2C0_BASE	0xd8280000	/* 64k	I2C 0 */
+#define WM8505_AC97_BASE	0xd8290000	/* 64k	AC97 */
+#define WM8505_SPI2_BASE	0xd82a0000	/* 64k	SPI 2 */
+#define WM8505_UART1_BASE	0xd82b0000	/* 64k	UART 1 */
+#define WM8505_UART3_BASE	0xd82c0000	/* 64k	UART 3 */
+#define WM8505_I2C1_BASE	0xd8320000	/* 64k	I2C 1 */
+#define WM8505_I2S_BASE		0xd8330000	/* 64k	I2S */
+#define WM8505_UART4_BASE	0xd8370000	/* 64k	UART 4 */
+#define WM8505_UART5_BASE	0xd8380000	/* 64k	UART 5 */
+
+#define WM8505_REGS_END_PHYS	0xd838ffff	/* End of MMIO registers */
+#define WM8505_REGS_LENGTH	(WM8505_REGS_END_PHYS \
+				- WM8505_REGS_START_PHYS + 1)
+
+#endif
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..5f4ddde
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,177 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include "devices.h"
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_EDGE		( VT8500_TRIGGER_RISING \
+				| VT8500_TRIGGER_FALLING)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & VT8500_EDGE;
+	if (edge) {
+		void __iomem *stat_reg = base + VT8500_IC_STATUS
+						+ (irq < 32 ? 0 : 4);
+		unsigned status = readl(stat_reg);
+
+		status |= (1 << (irq & 0x1f));
+		writel(status, stat_reg);
+	} else {
+		u8 dctr = readb(base + VT8500_IC_DCTR + irq);
+
+		dctr &= ~VT8500_INT_ENABLE;
+		writeb(dctr, base + VT8500_IC_DCTR + irq);
+	}
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 dctr;
+
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	dctr = readb(base + VT8500_IC_DCTR + irq);
+	dctr |= VT8500_INT_ENABLE;
+	writeb(dctr, base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	u8 dctr;
+
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+
+	dctr = readb(base + VT8500_IC_DCTR + irq);
+	dctr &= ~VT8500_EDGE;
+
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		dctr |= VT8500_TRIGGER_HIGH;
+		irq_desc[orig_irq].handle_irq = handle_level_irq;
+		break;
+	case IRQF_TRIGGER_FALLING:
+		dctr |= VT8500_TRIGGER_FALLING;
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	case IRQF_TRIGGER_RISING:
+		dctr |= VT8500_TRIGGER_RISING;
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	}
+	writeb(dctr, base + VT8500_IC_DCTR + irq);
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_ic_base, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_ic_base, SZ_64K);
+	sic_regbase = ioremap(wmt_sic_base, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR
+								+ i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..8ad825e
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,265 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+	int loops = msecs_to_loops(10);
+	while ((readb(reg) & bitmask) && --loops)
+		cpu_relax();
+
+	if (unlikely(!loops))
+		pr_warning("Waiting for status bits 0x%x to clear timed out\n",
+			   bitmask);
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	c = (unsigned long long)pv * duty_ns;
+	do_div(c, period_ns);
+	dc = c;
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else {
+			pwm = ERR_PTR(-EBUSY);
+		}
+	} else {
+		pwm = ERR_PTR(-ENOENT);
+	}
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else {
+		pr_warning("PWM device already freed\n");
+	}
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[0].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..d5376c5
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,155 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/delay.h>
+
+#include <asm/mach/time.h>
+
+#include "devices.h"
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+
+static void __iomem *regbase;
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	int loops = msecs_to_loops(10);
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	int loops = msecs_to_loops(10);
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_pmc_base + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register failed for %s\n",
+					clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_timer_irq, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq failed for %s\n",
+					clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..e73aadb
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,77 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_gpio_base + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_pmc_base + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be remapped, not enabling power off!\n");
+
+	wm8505_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= wm8505_reserve_mem,
+	.map_io		= wm8505_map_io,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.4


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

* [PATCH 3/6 v3] input: Add support for VIA VT8500 and compatibles in i8042
  2010-11-12 23:30     ` Dmitry Torokhov
  2010-11-13  0:00       ` Alexey Charkov
@ 2010-12-22 21:41       ` Alexey Charkov
  1 sibling, 0 replies; 91+ messages in thread
From: Alexey Charkov @ 2010-12-22 21:41 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Alexey Charkov, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Tony Lindgren, Feng Tang, Janusz Krzysztofik,
	Dmitry Eremin-Solenikov, linux-input, linux-kernel

VIA and WonderMedia Systems-on-Chip feature a standard i8042-compatible
keyboard and mouse controller. This adds necessary glue to enable use
of the standard driver with these systems.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This version of the patch introduces yet another trivial update to
follow changes in arch code (namely variables that hold the register
and interrupt numbers). Otherwise it is identical to v2.

Best regards,
Alexey

 drivers/input/serio/Kconfig        |    3 +-
 drivers/input/serio/i8042-vt8500.h |   73 ++++++++++++++++++++++++++++++++++++
 drivers/input/serio/i8042.h        |    2 +
 3 files changed, 77 insertions(+), 1 deletions(-)
 create mode 100644 drivers/input/serio/i8042-vt8500.h

diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 6256233..ff799f3 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -21,7 +21,8 @@ if SERIO
 config SERIO_I8042
 	tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
 	default y
-	depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
+	depends on !PARISC && \
+		  (!ARM || ARCH_SHARK || ARCH_VT8500 || FOOTBRIDGE_HOST) && \
 		   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
 	help
 	  i8042 is the chip over which the standard AT keyboard and PS/2
diff --git a/drivers/input/serio/i8042-vt8500.h b/drivers/input/serio/i8042-vt8500.h
new file mode 100644
index 0000000..a7e6673
--- /dev/null
+++ b/drivers/input/serio/i8042-vt8500.h
@@ -0,0 +1,73 @@
+#ifndef _I8042_VT8500_H
+#define _I8042_VT8500_H
+
+#include <mach/i8042.h>
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+static void __iomem *regbase;
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "vt8500ps2/serio0"
+#define I8042_AUX_PHYS_DESC "vt8500ps2/serio1"
+#define I8042_MUX_PHYS_DESC "vt8500ps2/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#define I8042_KBD_IRQ	(wmt_i8042_kbd_irq)
+#define I8042_AUX_IRQ	(wmt_i8042_aux_irq)
+
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG	(regbase + 0x4)
+#define I8042_STATUS_REG	(regbase + 0x4)
+#define I8042_DATA_REG		(regbase + 0x0)
+
+static inline int i8042_read_data(void)
+{
+	return readl(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+	return readl(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+	writel(val, I8042_DATA_REG);
+}
+
+static inline void i8042_write_command(int val)
+{
+	writel(val, I8042_COMMAND_REG);
+}
+
+static inline int i8042_platform_init(void)
+{
+	i8042_reset = true;
+	regbase = ioremap(wmt_i8042_base, SZ_1K);
+	if (!regbase)
+		return -ENODEV;
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+	iounmap(regbase);
+}
+
+#endif /* _I8042_VT8500_H */
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
index cbc1beb..bdb2aeb 100644
--- a/drivers/input/serio/i8042.h
+++ b/drivers/input/serio/i8042.h
@@ -16,6 +16,8 @@
 
 #if defined(CONFIG_MACH_JAZZ)
 #include "i8042-jazzio.h"
+#elif defined(CONFIG_ARCH_VT8500)
+#include "i8042-vt8500.h"
 #elif defined(CONFIG_SGI_HAS_I8042)
 #include "i8042-ip22io.h"
 #elif defined(CONFIG_SNI_RM)
-- 
1.7.3.4


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

* Re: [PATCH 1/6 v10] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-22 21:18                                     ` [PATCH 1/6 v10] " Alexey Charkov
@ 2010-12-22 21:52                                       ` Ryan Mallon
  2010-12-22 22:02                                         ` Alexey Charkov
  2010-12-22 22:25                                         ` [PATCH 1/6 v11] " Alexey Charkov
  0 siblings, 2 replies; 91+ messages in thread
From: Ryan Mallon @ 2010-12-22 21:52 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

On 12/23/2010 10:18 AM, Alexey Charkov wrote:
> This adds support for the family of Systems-on-Chip produced initially
> by VIA and now its subsidiary WonderMedia that have recently become
> widespread in lower-end Chinese ARM-based tablets and netbooks.
> 
> Support is included for both VT8500 and WM8505, selectable by a
> configuration switch at kernel build time.
> 
> Included are basic machine initialization files, register and
> interrupt definitions, support for the on-chip interrupt controller,
> high-precision OS timer, GPIO lines, necessary macros for early debug,
> pulse-width-modulated outputs control, as well as platform device
> configurations for the specific drivers implemented elsewhere.
> 
> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> ---
> 
> Welcome the jubilee tenth revision of this patch ;-)
> 
> I've tried to incorporate the suggestions by Ryan and Arnd, hope that
> there is nothing left out. There was a massive reorganization of code
> to remove less-than-obvious magic with MMIO registers and irqs being
> held in huge structs, now they are again macro definitions. Those
> macros are, however, only included in single isolated files, and
> actual values to use are chosen at runtime by calling the respective
> routines at machine initialization. There are also stylistic changes
> all around, where Ryan suggested.
> 
> As a result, i8042 should again be adjusted a bit to reflect the new
> place to find respective register/irq definitions, that one will be
> sent in the respective thread shortly.
> 
> Best regards,
> Alexey

<snip>

> --- /dev/null
> +++ b/arch/arm/mach-vt8500/devices-vt8500.c
> @@ -0,0 +1,117 @@
> +/* linux/arch/arm/mach-vt8500/devices-vt8500.c
> + *
> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/platform_device.h>
> +
> +#include <mach/vt8500_regs.h>
> +#include <mach/vt8500_irqs.h>
> +#include <mach/i8042.h>
> +#include "devices.h"
> +
> +static inline struct resource WMT_MMIO_RES(u32 start, u32 size)
> +{
> +	struct resource tmp = {
> +		.flags = IORESOURCE_MEM,
> +		.start = start,
> +		.end = start + size - 1,
> +	};
> +
> +	return tmp;
> +}

These functions can be marked __init (though I guess they already are if
marked inline?). They should also have lower case names since they are
proper functions.

Do these functions generate warnings about returning temporary values
off the stack? If so, they could be rewritten as:

static __init void wmt_mmio_res(struct resource *res,
				u32 start, u32 size)
{
	res->flags = IORESOURCE_MEM;
	res->start = start;
	res->end   = start + size - 1;
}

You could also make these functions static inline in devices.h to avoid
having to define them for each board.

> +
> +static inline struct resource WMT_IRQ_RES(int irq)
> +{
> +	struct resource tmp = {
> +		.flags = IORESOURCE_IRQ,
> +		.start = irq,
> +		.end = irq,
> +	};
> +
> +	return tmp;
> +}
> +
> +#define WMT_RES_ADD(__device, __resource, __num) \
> +if (platform_device_add_resources(__device, __resource, __num)) \
> +	pr_err("Failed to assign resources to __device##\n");

This could be written as a proper function. The resource add is unlikely
to fail. Maybe keep the warning but don't worry about printing the
device name?

~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934

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

* Re: [PATCH 1/6 v10] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-22 21:52                                       ` Ryan Mallon
@ 2010-12-22 22:02                                         ` Alexey Charkov
  2010-12-22 22:32                                           ` Ryan Mallon
  2010-12-22 22:25                                         ` [PATCH 1/6 v11] " Alexey Charkov
  1 sibling, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-12-22 22:02 UTC (permalink / raw)
  To: Ryan Mallon
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

2010/12/23 Ryan Mallon <ryan@bluewatersys.com>:
> On 12/23/2010 10:18 AM, Alexey Charkov wrote:
>> This adds support for the family of Systems-on-Chip produced initially
>> by VIA and now its subsidiary WonderMedia that have recently become
>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>
>> Support is included for both VT8500 and WM8505, selectable by a
>> configuration switch at kernel build time.
>>
>> Included are basic machine initialization files, register and
>> interrupt definitions, support for the on-chip interrupt controller,
>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>> pulse-width-modulated outputs control, as well as platform device
>> configurations for the specific drivers implemented elsewhere.
>>
>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>> ---
>>
>> Welcome the jubilee tenth revision of this patch ;-)
>>
>> I've tried to incorporate the suggestions by Ryan and Arnd, hope that
>> there is nothing left out. There was a massive reorganization of code
>> to remove less-than-obvious magic with MMIO registers and irqs being
>> held in huge structs, now they are again macro definitions. Those
>> macros are, however, only included in single isolated files, and
>> actual values to use are chosen at runtime by calling the respective
>> routines at machine initialization. There are also stylistic changes
>> all around, where Ryan suggested.
>>
>> As a result, i8042 should again be adjusted a bit to reflect the new
>> place to find respective register/irq definitions, that one will be
>> sent in the respective thread shortly.
>>
>> Best regards,
>> Alexey
>
> <snip>
>
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/devices-vt8500.c
>> @@ -0,0 +1,117 @@
>> +/* linux/arch/arm/mach-vt8500/devices-vt8500.c
>> + *
>> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + *
>> + */
>> +
>> +#include <linux/platform_device.h>
>> +
>> +#include <mach/vt8500_regs.h>
>> +#include <mach/vt8500_irqs.h>
>> +#include <mach/i8042.h>
>> +#include "devices.h"
>> +
>> +static inline struct resource WMT_MMIO_RES(u32 start, u32 size)
>> +{
>> +     struct resource tmp = {
>> +             .flags = IORESOURCE_MEM,
>> +             .start = start,
>> +             .end = start + size - 1,
>> +     };
>> +
>> +     return tmp;
>> +}
>
> These functions can be marked __init (though I guess they already are if
> marked inline?). They should also have lower case names since they are
> proper functions.
>

As inline functions are unrolled into the caller at compile time, and
the caller is __init, I would expect their code to be freed after init
as well. I could be wrong, though :)

> Do these functions generate warnings about returning temporary values
> off the stack? If so, they could be rewritten as:
>

Should those be compile-time or run-time? I did not see any.

> static __init void wmt_mmio_res(struct resource *res,
>                                u32 start, u32 size)
> {
>        res->flags = IORESOURCE_MEM;
>        res->start = start;
>        res->end   = start + size - 1;
> }
>
> You could also make these functions static inline in devices.h to avoid
> having to define them for each board.
>

Agreed.

>> +
>> +static inline struct resource WMT_IRQ_RES(int irq)
>> +{
>> +     struct resource tmp = {
>> +             .flags = IORESOURCE_IRQ,
>> +             .start = irq,
>> +             .end = irq,
>> +     };
>> +
>> +     return tmp;
>> +}
>> +
>> +#define WMT_RES_ADD(__device, __resource, __num) \
>> +if (platform_device_add_resources(__device, __resource, __num)) \
>> +     pr_err("Failed to assign resources to __device##\n");
>
> This could be written as a proper function. The resource add is unlikely
> to fail. Maybe keep the warning but don't worry about printing the
> device name?
>

There is memory allocation inside platform_device_add_resources, so
probably there is scope for failure. I could add unlikely(), though.

Thanks,
Alexey

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

* [PATCH 1/6 v11] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-22 21:52                                       ` Ryan Mallon
  2010-12-22 22:02                                         ` Alexey Charkov
@ 2010-12-22 22:25                                         ` Alexey Charkov
  2010-12-22 22:59                                           ` Ryan Mallon
  1 sibling, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-12-22 22:25 UTC (permalink / raw)
  To: Ryan Mallon
  Cc: Alexey Charkov, Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505, selectable by a
configuration switch at kernel build time.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This incorporates latest comments by Ryan regarding helper inlines
in devices-{vt8500,wm8505}.c

Best regards,
Alexey

 arch/arm/Kconfig                                |   12 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 ++++
 arch/arm/mach-vt8500/Kconfig                    |   73 ++++++
 arch/arm/mach-vt8500/Makefile                   |    9 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   77 +++++++
 arch/arm/mach-vt8500/devices-vt8500.c           |   91 ++++++++
 arch/arm/mach-vt8500/devices-wm8505.c           |   99 +++++++++
 arch/arm/mach-vt8500/devices.c                  |  270 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   88 ++++++++
 arch/arm/mach-vt8500/gpio.c                     |  233 +++++++++++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 +++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 +++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/i8042.h       |   18 ++
 arch/arm/mach-vt8500/include/mach/io.h          |   28 +++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 +++
 arch/arm/mach-vt8500/include/mach/system.h      |   18 ++
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 +++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 +++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 ++
 arch/arm/mach-vt8500/include/mach/vt8500_irqs.h |   88 ++++++++
 arch/arm/mach-vt8500/include/mach/vt8500_regs.h |   79 +++++++
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 +++
 arch/arm/mach-vt8500/include/mach/wm8505_irqs.h |  115 ++++++++++
 arch/arm/mach-vt8500/include/mach/wm8505_regs.h |   78 +++++++
 arch/arm/mach-vt8500/irq.c                      |  177 +++++++++++++++
 arch/arm/mach-vt8500/pwm.c                      |  265 ++++++++++++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  155 +++++++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   77 +++++++
 34 files changed, 2349 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices-vt8500.c
 create mode 100644 arch/arm/mach-vt8500/devices-wm8505.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/i8042.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/wm8505_regs.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d56d21c..53052fa 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,16 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +983,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..2c20a34
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,73 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X576
+	bool "10-inch with 1024x576 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in CherryPal netbooks and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x576' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..81aedb7
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,9 @@
+obj-y += devices.o gpio.o irq.o timer.o
+
+obj-$(CONFIG_VTWM_VERSION_VT8500) += devices-vt8500.o
+obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..94a261d
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,77 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_gpio_base + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_pmc_base + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be remapped, not enabling power off!\n");
+
+	vt8500_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= vt8500_reserve_mem,
+	.map_io		= vt8500_map_io,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices-vt8500.c b/arch/arm/mach-vt8500/devices-vt8500.c
new file mode 100644
index 0000000..19519ae
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices-vt8500.c
@@ -0,0 +1,91 @@
+/* linux/arch/arm/mach-vt8500/devices-vt8500.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+#include <mach/vt8500_regs.h>
+#include <mach/vt8500_irqs.h>
+#include <mach/i8042.h>
+#include "devices.h"
+
+void __init vt8500_set_resources(void)
+{
+	struct resource tmp[3];
+
+	tmp[0] = wmt_mmio_res(VT8500_LCDC_BASE, SZ_1K);
+	tmp[1] = wmt_irq_res(IRQ_LCDC);
+	wmt_res_add(&vt8500_device_lcdc, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_UART0_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART0);
+	wmt_res_add(&vt8500_device_uart0, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_UART1_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART1);
+	wmt_res_add(&vt8500_device_uart1, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_UART2_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART2);
+	wmt_res_add(&vt8500_device_uart2, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_UART3_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART3);
+	wmt_res_add(&vt8500_device_uart3, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_EHCI_BASE, SZ_512);
+	tmp[1] = wmt_irq_res(IRQ_EHCI);
+	wmt_res_add(&vt8500_device_ehci, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_GEGEA_BASE, SZ_256);
+	wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(VT8500_PWM_BASE, 0x44);
+	wmt_res_add(&vt8500_device_pwm, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(VT8500_RTC_BASE, 0x2c);
+	tmp[1] = wmt_irq_res(IRQ_RTC);
+	tmp[2] = wmt_irq_res(IRQ_RTCSM);
+	wmt_res_add(&vt8500_device_rtc, tmp, 3);
+}
+
+static void __init vt8500_set_externs(void)
+{
+	/* Non-resource-aware stuff */
+	wmt_ic_base = VT8500_IC_BASE;
+	wmt_gpio_base = VT8500_GPIO_BASE;
+	wmt_pmc_base = VT8500_PMC_BASE;
+	wmt_i8042_base = VT8500_PS2_BASE;
+
+	wmt_nr_irqs = VT8500_NR_IRQS;
+	wmt_timer_irq = IRQ_PMCOS0;
+	wmt_gpio_ext_irq[0] = IRQ_EXT0;
+	wmt_gpio_ext_irq[1] = IRQ_EXT1;
+	wmt_gpio_ext_irq[2] = IRQ_EXT2;
+	wmt_gpio_ext_irq[3] = IRQ_EXT3;
+	wmt_gpio_ext_irq[4] = IRQ_EXT4;
+	wmt_gpio_ext_irq[5] = IRQ_EXT5;
+	wmt_gpio_ext_irq[6] = IRQ_EXT6;
+	wmt_gpio_ext_irq[7] = IRQ_EXT7;
+	wmt_i8042_kbd_irq = IRQ_PS2KBD;
+	wmt_i8042_aux_irq = IRQ_PS2MOUSE;
+}
+
+void __init vt8500_map_io(void)
+{
+	iotable_init(wmt_io_desc, ARRAY_SIZE(wmt_io_desc));
+
+	/* Should be done before interrupts and timers are initialized */
+	vt8500_set_externs();
+}
diff --git a/arch/arm/mach-vt8500/devices-wm8505.c b/arch/arm/mach-vt8500/devices-wm8505.c
new file mode 100644
index 0000000..db4594e
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices-wm8505.c
@@ -0,0 +1,99 @@
+/* linux/arch/arm/mach-vt8500/devices-wm8505.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+#include <mach/wm8505_regs.h>
+#include <mach/wm8505_irqs.h>
+#include <mach/i8042.h>
+#include "devices.h"
+
+void __init wm8505_set_resources(void)
+{
+	struct resource tmp[3];
+
+	tmp[0] = wmt_mmio_res(WM8505_GOVR_BASE, SZ_512);
+	wmt_res_add(&vt8500_device_wm8505_fb, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART0_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART0);
+	wmt_res_add(&vt8500_device_uart0, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART1_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART1);
+	wmt_res_add(&vt8500_device_uart1, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART2_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART2);
+	wmt_res_add(&vt8500_device_uart2, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART3_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART3);
+	wmt_res_add(&vt8500_device_uart3, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART4_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART4);
+	wmt_res_add(&vt8500_device_uart4, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART5_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART5);
+	wmt_res_add(&vt8500_device_uart5, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_EHCI_BASE, SZ_512);
+	tmp[1] = wmt_irq_res(IRQ_EHCI);
+	wmt_res_add(&vt8500_device_ehci, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_GEGEA_BASE, SZ_256);
+	wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(WM8505_PWM_BASE, 0x44);
+	wmt_res_add(&vt8500_device_pwm, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(WM8505_RTC_BASE, 0x2c);
+	tmp[1] = wmt_irq_res(IRQ_RTC);
+	tmp[2] = wmt_irq_res(IRQ_RTCSM);
+	wmt_res_add(&vt8500_device_rtc, tmp, 3);
+}
+
+static void __init wm8505_set_externs(void)
+{
+	/* Non-resource-aware stuff */
+	wmt_ic_base = WM8505_IC_BASE;
+	wmt_sic_base = WM8505_SIC_BASE;
+	wmt_gpio_base = WM8505_GPIO_BASE;
+	wmt_pmc_base = WM8505_PMC_BASE;
+	wmt_i8042_base = WM8505_PS2_BASE;
+
+	wmt_nr_irqs = WM8505_NR_IRQS;
+	wmt_timer_irq = IRQ_PMCOS0;
+	wmt_gpio_ext_irq[0] = IRQ_EXT0;
+	wmt_gpio_ext_irq[1] = IRQ_EXT1;
+	wmt_gpio_ext_irq[2] = IRQ_EXT2;
+	wmt_gpio_ext_irq[3] = IRQ_EXT3;
+	wmt_gpio_ext_irq[4] = IRQ_EXT4;
+	wmt_gpio_ext_irq[5] = IRQ_EXT5;
+	wmt_gpio_ext_irq[6] = IRQ_EXT6;
+	wmt_gpio_ext_irq[7] = IRQ_EXT7;
+	wmt_i8042_kbd_irq = IRQ_PS2KBD;
+	wmt_i8042_aux_irq = IRQ_PS2MOUSE;
+}
+
+void __init wm8505_map_io(void)
+{
+	iotable_init(wmt_io_desc, ARRAY_SIZE(wmt_io_desc));
+
+	/* Should be done before interrupts and timers are initialized */
+	wm8505_set_externs();
+}
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..1fcdc36
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,270 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+
+#include <mach/vt8500fb.h>
+#include <mach/i8042.h>
+#include "devices.h"
+
+/* These can't use resources currently */
+unsigned long wmt_ic_base __initdata;
+unsigned long wmt_sic_base __initdata;
+unsigned long wmt_gpio_base __initdata;
+unsigned long wmt_pmc_base __initdata;
+unsigned long wmt_i8042_base __initdata;
+
+int wmt_nr_irqs __initdata;
+int wmt_timer_irq __initdata;
+int wmt_gpio_ext_irq[8] __initdata;
+
+/* Should remain accessible after init.
+ * i8042 driver desperately calls for attention...
+ */
+int wmt_i8042_kbd_irq;
+int wmt_i8042_aux_irq;
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X576
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 576 * 2,
+	.mode		= {
+		.name		= "1024x576",
+		.xres		= 1024,
+		.yres		= 576,
+		.left_margin	= 40,
+		.right_margin	= 24,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 96,
+		.vsync_len	= 2,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 66,
+		.right_margin	= 2,
+		.upper_margin	= 19,
+		.lower_margin	= 1,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		if (strcmp(panels[i].mode.name, str) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= -1,
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+};
+
+struct map_desc wmt_io_desc[] __initdata = {
+	/* SoC MMIO registers */
+	[0] = {
+		.virtual	= 0xf8000000,
+		.pfn		= __phys_to_pfn(0xd8000000),
+		.length		= 0x00390000, /* max of all chip variants */
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..188d4e1
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,88 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+#include <asm/mach/map.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init vt8500_gpio_init(void);
+void __init vt8500_set_resources(void);
+void __init wm8505_set_resources(void);
+
+extern unsigned long wmt_ic_base __initdata;
+extern unsigned long wmt_sic_base __initdata;
+extern unsigned long wmt_gpio_base __initdata;
+extern unsigned long wmt_pmc_base __initdata;
+
+extern int wmt_nr_irqs __initdata;
+extern int wmt_timer_irq __initdata;
+extern int wmt_gpio_ext_irq[8] __initdata;
+
+extern struct map_desc wmt_io_desc[2] __initdata;
+
+static inline struct resource wmt_mmio_res(u32 start, u32 size)
+{
+	struct resource tmp = {
+		.flags = IORESOURCE_MEM,
+		.start = start,
+		.end = start + size - 1,
+	};
+
+	return tmp;
+}
+
+static inline struct resource wmt_irq_res(int irq)
+{
+	struct resource tmp = {
+		.flags = IORESOURCE_IRQ,
+		.start = irq,
+		.end = irq,
+	};
+
+	return tmp;
+}
+
+static inline void wmt_res_add(struct platform_device *pdev,
+			       const struct resource *res, unsigned int num)
+{
+	if (unlikely(platform_device_add_resources(pdev, res, num)))
+		pr_err("Failed to assign resources\n");
+}
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..445397e
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,233 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include "devices.h"
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + vt8500_chip->regoff);
+
+	val |= (1 << vt8500_chip->shift << offset);
+	writel(val, regbase + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + vt8500_chip->regoff);
+
+	val &= ~(1 << vt8500_chip->shift << offset);
+	writel(val, regbase + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + 0x20 + vt8500_chip->regoff);
+
+	val &= ~(1 << vt8500_chip->shift << offset);
+	writel(val, regbase + 0x20 + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + 0x20 + vt8500_chip->regoff);
+
+	val |= (1 << vt8500_chip->shift << offset);
+	writel(val, regbase + 0x20 + vt8500_chip->regoff);
+
+	if (value) {
+		val = readl(regbase + 0x40 + vt8500_chip->regoff);
+		val |= (1 << vt8500_chip->shift << offset);
+		writel(val, regbase + 0x40 + vt8500_chip->regoff);
+	}
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + 0x60 + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + 0x40 + vt8500_chip->regoff);
+
+	if (value)
+		val |= (1 << vt8500_chip->shift << offset);
+	else
+		val &= ~(1 << vt8500_chip->shift << offset);
+
+	writel(val, regbase + 0x40 + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0",	0,	0x0,	8,	4),
+	VT8500_GPIO_BANK("uart1",	4,	0x0,	12,	4),
+	VT8500_GPIO_BANK("spi0",	8,	0x0,	16,	4),
+	VT8500_GPIO_BANK("spi1",	12,	0x0,	20,	4),
+	VT8500_GPIO_BANK("spi2",	16,	0x0,	24,	4),
+	VT8500_GPIO_BANK("pwmout",	24,	0x0,	28,	2),
+
+	VT8500_GPIO_BANK("sdmmc",	0,	0x4,	30,	11),
+	VT8500_GPIO_BANK("ms",		16,	0x4,	41,	7),
+	VT8500_GPIO_BANK("i2c0",	24,	0x4,	48,	2),
+	VT8500_GPIO_BANK("i2c1",	26,	0x4,	50,	2),
+
+	VT8500_GPIO_BANK("mii",		0,	0x8,	52,	20),
+	VT8500_GPIO_BANK("see",		20,	0x8,	72,	4),
+	VT8500_GPIO_BANK("ide",		24,	0x8,	76,	7),
+
+	VT8500_GPIO_BANK("ccir",	0,	0xc,	83,	19),
+
+	VT8500_GPIO_BANK("ts",		8,	0x10,	102,	11),
+
+	VT8500_GPIO_BANK("lcd",		0,	0x14,	113,	23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	unsigned val = readl(regbase + 0x3c);
+
+	val &= ~(1 << offset);
+	writel(val, regbase + 0x3c);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	unsigned val = readl(regbase + 0x3c);
+
+	val |= (1 << offset);
+	writel(val, regbase + 0x3c);
+
+	if (value) {
+		val = readl(regbase + 0x5c);
+		val |= (1 << offset);
+		writel(val, regbase + 0x5c);
+	}
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + 0x7c) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	unsigned val = readl(regbase + 0x5c);
+
+	if (value)
+		val |= (1 << offset);
+	else
+		val &= ~(1 << offset);
+
+	writel(val, regbase + 0x5c);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	for (i = 0; i < 8; i++)
+		gpio_to_irq_map[i] = wmt_gpio_ext_irq[i];
+
+	regbase = ioremap(wmt_gpio_base, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/i8042.h b/arch/arm/mach-vt8500/include/mach/i8042.h
new file mode 100644
index 0000000..cd7143c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/i8042.h
@@ -0,0 +1,18 @@
+/* arch/arm/mach-vt8500/include/mach/i8042.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+extern unsigned long wmt_i8042_base __initdata;
+extern int wmt_i8042_kbd_irq;
+extern int wmt_i8042_aux_irq;
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..9077239
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		__typesafe_io((a) + 0xf0000000)
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..d6c757e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,18 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR_VIRT	0xf8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	writel(1, VT8500_PMSR_VIRT);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..4642290
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END	0xd0000000UL
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h b/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
new file mode 100644
index 0000000..ecfee91
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
@@ -0,0 +1,88 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* VT8500 Interrupt Sources */
+
+#define IRQ_JPEGENC	0	/* JPEG Encoder */
+#define IRQ_JPEGDEC	1	/* JPEG Decoder */
+				/* Reserved */
+#define IRQ_PATA	3	/* PATA Controller */
+				/* Reserved */
+#define IRQ_DMA		5	/* DMA Controller */
+#define IRQ_EXT0	6	/* External Interrupt 0 */
+#define IRQ_EXT1	7	/* External Interrupt 1 */
+#define IRQ_GE		8	/* Graphic Engine */
+#define IRQ_GOV		9	/* Graphic Overlay Engine */
+#define IRQ_ETHER	10	/* Ethernet MAC */
+#define IRQ_MPEGTS	11	/* Transport Stream Interface */
+#define IRQ_LCDC	12	/* LCD Controller */
+#define IRQ_EXT2	13	/* External Interrupt 2 */
+#define IRQ_EXT3	14	/* External Interrupt 3 */
+#define IRQ_EXT4	15	/* External Interrupt 4 */
+#define IRQ_CIPHER	16	/* Cipher */
+#define IRQ_VPP		17	/* Video Post-Processor */
+#define IRQ_I2C1	18	/* I2C 1 */
+#define IRQ_I2C0	19	/* I2C 0 */
+#define IRQ_SDMMC	20	/* SD/MMC Controller */
+#define IRQ_SDMMC_DMA	21	/* SD/MMC Controller DMA */
+#define IRQ_PMC_WU	22	/* Power Management Controller Wakeup */
+				/* Reserved */
+#define IRQ_SPI0	24	/* SPI 0 */
+#define IRQ_SPI1	25	/* SPI 1 */
+#define IRQ_SPI2	26	/* SPI 2 */
+#define IRQ_LCDDF	27	/* LCD Data Formatter */
+#define IRQ_NAND	28	/* NAND Flash Controller */
+#define IRQ_NAND_DMA	29	/* NAND Flash Controller DMA */
+#define IRQ_MS		30	/* MemoryStick Controller */
+#define IRQ_MS_DMA	31	/* MemoryStick Controller DMA */
+#define IRQ_UART0	32	/* UART 0 */
+#define IRQ_UART1	33	/* UART 1 */
+#define IRQ_I2S		34	/* I2S */
+#define IRQ_PCM		35	/* PCM */
+#define IRQ_PMCOS0	36	/* PMC OS Timer 0 */
+#define IRQ_PMCOS1	37	/* PMC OS Timer 1 */
+#define IRQ_PMCOS2	38	/* PMC OS Timer 2 */
+#define IRQ_PMCOS3	39	/* PMC OS Timer 3 */
+#define IRQ_VPU		40	/* Video Processing Unit */
+#define IRQ_VID		41	/* Video Digital Input Interface */
+#define IRQ_AC97	42	/* AC97 Interface */
+#define IRQ_EHCI	43	/* USB */
+#define IRQ_NOR		44	/* NOR Flash Controller */
+#define IRQ_PS2MOUSE	45	/* PS/2 Mouse */
+#define IRQ_PS2KBD	46	/* PS/2 Keyboard */
+#define IRQ_UART2	47	/* UART 2 */
+#define IRQ_RTC		48	/* RTC Interrupt */
+#define IRQ_RTCSM	49	/* RTC Second/Minute Update Interrupt */
+#define IRQ_UART3	50	/* UART 3 */
+#define IRQ_ADC		51	/* ADC */
+#define IRQ_EXT5	52	/* External Interrupt 5 */
+#define IRQ_EXT6	53	/* External Interrupt 6 */
+#define IRQ_EXT7	54	/* External Interrupt 7 */
+#define IRQ_CIR		55	/* CIR */
+#define IRQ_DMA0	56	/* DMA Channel 0 */
+#define IRQ_DMA1	57	/* DMA Channel 1 */
+#define IRQ_DMA2	58	/* DMA Channel 2 */
+#define IRQ_DMA3	59	/* DMA Channel 3 */
+#define IRQ_DMA4	60	/* DMA Channel 4 */
+#define IRQ_DMA5	61	/* DMA Channel 5 */
+#define IRQ_DMA6	62	/* DMA Channel 6 */
+#define IRQ_DMA7	63	/* DMA Channel 7 */
+
+#define VT8500_NR_IRQS		64
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500_regs.h b/arch/arm/mach-vt8500/include/mach/vt8500_regs.h
new file mode 100644
index 0000000..29c63ec
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500_regs.h
@@ -0,0 +1,79 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vt8500_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_VT8500_REGS_H
+#define __ASM_ARM_ARCH_VT8500_REGS_H
+
+/* VT8500 Registers Map */
+
+#define VT8500_REGS_START_PHYS	0xd8000000	/* Start of MMIO registers */
+#define VT8500_REGS_START_VIRT	0xf8000000	/* Virtual mapping start */
+
+#define VT8500_DDR_BASE		0xd8000000	/* 1k	DDR/DDR2 Memory
+							Controller */
+#define VT8500_DMA_BASE		0xd8001000	/* 1k	DMA Controller */
+#define VT8500_SFLASH_BASE	0xd8002000	/* 1k	Serial Flash Memory
+							Controller */
+#define VT8500_ETHER_BASE	0xd8004000	/* 1k	Ethernet MAC 0 */
+#define VT8500_CIPHER_BASE	0xd8006000	/* 4k	Cipher */
+#define VT8500_USB_BASE		0xd8007800	/* 2k	USB OTG */
+# define VT8500_EHCI_BASE	0xd8007900	/*	EHCI */
+# define VT8500_UHCI_BASE	0xd8007b01	/*	UHCI */
+#define VT8500_PATA_BASE	0xd8008000	/* 512	PATA */
+#define VT8500_PS2_BASE		0xd8008800	/* 1k	PS/2 */
+#define VT8500_NAND_BASE	0xd8009000	/* 1k	NAND Controller */
+#define VT8500_NOR_BASE		0xd8009400	/* 1k	NOR Controller */
+#define VT8500_SDMMC_BASE	0xd800a000	/* 1k	SD/MMC Controller */
+#define VT8500_MS_BASE		0xd800b000	/* 1k	MS/MSPRO Controller */
+#define VT8500_LCDC_BASE	0xd800e400	/* 1k	LCD Controller */
+#define VT8500_VPU_BASE		0xd8050000	/* 256	VPU */
+#define VT8500_GOV_BASE		0xd8050300	/* 256	GOV */
+#define VT8500_GEGEA_BASE	0xd8050400	/* 768	GE/GE Alpha Mixing */
+#define VT8500_LCDF_BASE	0xd8050900	/* 256	LCD Formatter */
+#define VT8500_VID_BASE		0xd8050a00	/* 256	VID */
+#define VT8500_VPP_BASE		0xd8050b00	/* 256	VPP */
+#define VT8500_TSBK_BASE	0xd80f4000	/* 4k	TSBK */
+#define VT8500_JPEGDEC_BASE	0xd80fe000	/* 4k	JPEG Decoder */
+#define VT8500_JPEGENC_BASE	0xd80ff000	/* 4k	JPEG Encoder */
+#define VT8500_RTC_BASE		0xd8100000	/* 64k	RTC */
+#define VT8500_GPIO_BASE	0xd8110000	/* 64k	GPIO Configuration */
+#define VT8500_SCC_BASE		0xd8120000	/* 64k	System Configuration*/
+#define VT8500_PMC_BASE		0xd8130000	/* 64k	PMC Configuration */
+#define VT8500_IC_BASE		0xd8140000	/* 64k	Interrupt Controller*/
+#define VT8500_UART0_BASE	0xd8200000	/* 64k	UART 0 */
+#define VT8500_UART2_BASE	0xd8210000	/* 64k	UART 2 */
+#define VT8500_PWM_BASE		0xd8220000	/* 64k	PWM Configuration */
+#define VT8500_SPI0_BASE	0xd8240000	/* 64k	SPI 0 */
+#define VT8500_SPI1_BASE	0xd8250000	/* 64k	SPI 1 */
+#define VT8500_CIR_BASE		0xd8270000	/* 64k	CIR */
+#define VT8500_I2C0_BASE	0xd8280000	/* 64k	I2C 0 */
+#define VT8500_AC97_BASE	0xd8290000	/* 64k	AC97 */
+#define VT8500_SPI2_BASE	0xd82a0000	/* 64k	SPI 2 */
+#define VT8500_UART1_BASE	0xd82b0000	/* 64k	UART 1 */
+#define VT8500_UART3_BASE	0xd82c0000	/* 64k	UART 3 */
+#define VT8500_PCM_BASE		0xd82d0000	/* 64k	PCM */
+#define VT8500_I2C1_BASE	0xd8320000	/* 64k	I2C 1 */
+#define VT8500_I2S_BASE		0xd8330000	/* 64k	I2S */
+#define VT8500_ADC_BASE		0xd8340000	/* 64k	ADC */
+
+#define VT8500_REGS_END_PHYS	0xd834ffff	/* End of MMIO registers */
+#define VT8500_REGS_LENGTH	(VT8500_REGS_END_PHYS \
+				- VT8500_REGS_START_PHYS + 1)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..7f399c3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	u32			xres_virtual;
+	u32			yres_virtual;
+	u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h b/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
new file mode 100644
index 0000000..6128627
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
@@ -0,0 +1,115 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* WM8505 Interrupt Sources */
+
+#define IRQ_UHCI	0	/* UHC FS (UHCI?) */
+#define IRQ_EHCI	1	/* UHC HS */
+#define IRQ_UDCDMA	2	/* UDC DMA */
+				/* Reserved */
+#define IRQ_PS2MOUSE	4	/* PS/2 Mouse */
+#define IRQ_UDC		5	/* UDC */
+#define IRQ_EXT0	6	/* External Interrupt 0 */
+#define IRQ_EXT1	7	/* External Interrupt 1 */
+#define IRQ_KEYPAD	8	/* Keypad */
+#define IRQ_DMA		9	/* DMA Controller */
+#define IRQ_ETHER	10	/* Ethernet MAC */
+				/* Reserved */
+				/* Reserved */
+#define IRQ_EXT2	13	/* External Interrupt 2 */
+#define IRQ_EXT3	14	/* External Interrupt 3 */
+#define IRQ_EXT4	15	/* External Interrupt 4 */
+#define IRQ_APB		16	/* APB Bridge */
+#define IRQ_DMA0	17	/* DMA Channel 0 */
+#define IRQ_I2C1	18	/* I2C 1 */
+#define IRQ_I2C0	19	/* I2C 0 */
+#define IRQ_SDMMC	20	/* SD/MMC Controller */
+#define IRQ_SDMMC_DMA	21	/* SD/MMC Controller DMA */
+#define IRQ_PMC_WU	22	/* Power Management Controller Wakeup */
+#define IRQ_PS2KBD	23	/* PS/2 Keyboard */
+#define IRQ_SPI0	24	/* SPI 0 */
+#define IRQ_SPI1	25	/* SPI 1 */
+#define IRQ_SPI2	26	/* SPI 2 */
+#define IRQ_DMA1	27	/* DMA Channel 1 */
+#define IRQ_NAND	28	/* NAND Flash Controller */
+#define IRQ_NAND_DMA	29	/* NAND Flash Controller DMA */
+#define IRQ_UART5	30	/* UART 5 */
+#define IRQ_UART4	31	/* UART 4 */
+#define IRQ_UART0	32	/* UART 0 */
+#define IRQ_UART1	33	/* UART 1 */
+#define IRQ_DMA2	34	/* DMA Channel 2 */
+#define IRQ_I2S		35	/* I2S */
+#define IRQ_PMCOS0	36	/* PMC OS Timer 0 */
+#define IRQ_PMCOS1	37	/* PMC OS Timer 1 */
+#define IRQ_PMCOS2	38	/* PMC OS Timer 2 */
+#define IRQ_PMCOS3	39	/* PMC OS Timer 3 */
+#define IRQ_DMA3	40	/* DMA Channel 3 */
+#define IRQ_DMA4	41	/* DMA Channel 4 */
+#define IRQ_AC97	42	/* AC97 Interface */
+				/* Reserved */
+#define IRQ_NOR		44	/* NOR Flash Controller */
+#define IRQ_DMA5	45	/* DMA Channel 5 */
+#define IRQ_DMA6	46	/* DMA Channel 6 */
+#define IRQ_UART2	47	/* UART 2 */
+#define IRQ_RTC		48	/* RTC Interrupt */
+#define IRQ_RTCSM	49	/* RTC Second/Minute Update Interrupt */
+#define IRQ_UART3	50	/* UART 3 */
+#define IRQ_DMA7	51	/* DMA Channel 7 */
+#define IRQ_EXT5	52	/* External Interrupt 5 */
+#define IRQ_EXT6	53	/* External Interrupt 6 */
+#define IRQ_EXT7	54	/* External Interrupt 7 */
+#define IRQ_CIR		55	/* CIR */
+#define IRQ_SIC0	56	/* SIC IRQ0 */
+#define IRQ_SIC1	57	/* SIC IRQ1 */
+#define IRQ_SIC2	58	/* SIC IRQ2 */
+#define IRQ_SIC3	59	/* SIC IRQ3 */
+#define IRQ_SIC4	60	/* SIC IRQ4 */
+#define IRQ_SIC5	61	/* SIC IRQ5 */
+#define IRQ_SIC6	62	/* SIC IRQ6 */
+#define IRQ_SIC7	63	/* SIC IRQ7 */
+				/* Reserved */
+#define IRQ_JPEGDEC	65	/* JPEG Decoder */
+#define IRQ_SAE		66	/* SAE (?) */
+				/* Reserved */
+#define IRQ_VPU		79	/* Video Processing Unit */
+#define IRQ_VPP		80	/* Video Post-Processor */
+#define IRQ_VID		81	/* Video Digital Input Interface */
+#define IRQ_SPU		82	/* SPU (?) */
+#define IRQ_PIP		83	/* PIP Error */
+#define IRQ_GE		84	/* Graphic Engine */
+#define IRQ_GOV		85	/* Graphic Overlay Engine */
+#define IRQ_DVO		86	/* Digital Video Output */
+				/* Reserved */
+#define IRQ_DMA8	92	/* DMA Channel 8 */
+#define IRQ_DMA9	93	/* DMA Channel 9 */
+#define IRQ_DMA10	94	/* DMA Channel 10 */
+#define IRQ_DMA11	95	/* DMA Channel 11 */
+#define IRQ_DMA12	96	/* DMA Channel 12 */
+#define IRQ_DMA13	97	/* DMA Channel 13 */
+#define IRQ_DMA14	98	/* DMA Channel 14 */
+#define IRQ_DMA15	99	/* DMA Channel 15 */
+				/* Reserved */
+#define IRQ_GOVW	111	/* GOVW (?) */
+#define IRQ_GOVRSDSCD	112	/* GOVR SDSCD (?) */
+#define IRQ_GOVRSDMIF	113	/* GOVR SDMIF (?) */
+#define IRQ_GOVRHDSCD	114	/* GOVR HDSCD (?) */
+#define IRQ_GOVRHDMIF	115	/* GOVR HDMIF (?) */
+
+#define WM8505_NR_IRQS		116
diff --git a/arch/arm/mach-vt8500/include/mach/wm8505_regs.h b/arch/arm/mach-vt8500/include/mach/wm8505_regs.h
new file mode 100644
index 0000000..df15509
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/wm8505_regs.h
@@ -0,0 +1,78 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/wm8505_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_WM8505_REGS_H
+#define __ASM_ARM_ARCH_WM8505_REGS_H
+
+/* WM8505 Registers Map */
+
+#define WM8505_REGS_START_PHYS	0xd8000000	/* Start of MMIO registers */
+#define WM8505_REGS_START_VIRT	0xf8000000	/* Virtual mapping start */
+
+#define WM8505_DDR_BASE		0xd8000400	/* 1k	DDR/DDR2 Memory
+							Controller */
+#define WM8505_DMA_BASE		0xd8001800	/* 1k	DMA Controller */
+#define WM8505_VDMA_BASE	0xd8001c00	/* 1k	VDMA */
+#define WM8505_SFLASH_BASE	0xd8002000	/* 1k	Serial Flash Memory
+							Controller */
+#define WM8505_ETHER_BASE	0xd8004000	/* 1k	Ethernet MAC 0 */
+#define WM8505_CIPHER_BASE	0xd8006000	/* 4k	Cipher */
+#define WM8505_USB_BASE		0xd8007000	/* 2k	USB 2.0 Host */
+# define WM8505_EHCI_BASE	0xd8007100	/*	EHCI */
+# define WM8505_UHCI_BASE	0xd8007301	/*	UHCI */
+#define WM8505_PS2_BASE		0xd8008800	/* 1k	PS/2 */
+#define WM8505_NAND_BASE	0xd8009000	/* 1k	NAND Controller */
+#define WM8505_NOR_BASE		0xd8009400	/* 1k	NOR Controller */
+#define WM8505_SDMMC_BASE	0xd800a000	/* 1k	SD/MMC Controller */
+#define WM8505_VPU_BASE		0xd8050000	/* 256	VPU */
+#define WM8505_GOV_BASE		0xd8050300	/* 256	GOV */
+#define WM8505_GEGEA_BASE	0xd8050400	/* 768	GE/GE Alpha Mixing */
+#define WM8505_GOVR_BASE	0xd8050800	/* 512	GOVR (frambuffer) */
+#define WM8505_VID_BASE		0xd8050a00	/* 256	VID */
+#define WM8505_SCL_BASE		0xd8050d00	/* 256	SCL */
+#define WM8505_VPP_BASE		0xd8050f00	/* 256	VPP */
+#define WM8505_JPEGDEC_BASE	0xd80fe000	/* 4k	JPEG Decoder */
+#define WM8505_RTC_BASE		0xd8100000	/* 64k	RTC */
+#define WM8505_GPIO_BASE	0xd8110000	/* 64k	GPIO Configuration */
+#define WM8505_SCC_BASE		0xd8120000	/* 64k	System Configuration*/
+#define WM8505_PMC_BASE		0xd8130000	/* 64k	PMC Configuration */
+#define WM8505_IC_BASE		0xd8140000	/* 64k	Interrupt Controller*/
+#define WM8505_SIC_BASE		0xd8150000	/* 64k	Secondary IC */
+#define WM8505_UART0_BASE	0xd8200000	/* 64k	UART 0 */
+#define WM8505_UART2_BASE	0xd8210000	/* 64k	UART 2 */
+#define WM8505_PWM_BASE		0xd8220000	/* 64k	PWM Configuration */
+#define WM8505_SPI0_BASE	0xd8240000	/* 64k	SPI 0 */
+#define WM8505_SPI1_BASE	0xd8250000	/* 64k	SPI 1 */
+#define WM8505_KEYPAD_BASE	0xd8260000	/* 64k	Keypad control */
+#define WM8505_CIR_BASE		0xd8270000	/* 64k	CIR */
+#define WM8505_I2C0_BASE	0xd8280000	/* 64k	I2C 0 */
+#define WM8505_AC97_BASE	0xd8290000	/* 64k	AC97 */
+#define WM8505_SPI2_BASE	0xd82a0000	/* 64k	SPI 2 */
+#define WM8505_UART1_BASE	0xd82b0000	/* 64k	UART 1 */
+#define WM8505_UART3_BASE	0xd82c0000	/* 64k	UART 3 */
+#define WM8505_I2C1_BASE	0xd8320000	/* 64k	I2C 1 */
+#define WM8505_I2S_BASE		0xd8330000	/* 64k	I2S */
+#define WM8505_UART4_BASE	0xd8370000	/* 64k	UART 4 */
+#define WM8505_UART5_BASE	0xd8380000	/* 64k	UART 5 */
+
+#define WM8505_REGS_END_PHYS	0xd838ffff	/* End of MMIO registers */
+#define WM8505_REGS_LENGTH	(WM8505_REGS_END_PHYS \
+				- WM8505_REGS_START_PHYS + 1)
+
+#endif
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..5f4ddde
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,177 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include "devices.h"
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_EDGE		( VT8500_TRIGGER_RISING \
+				| VT8500_TRIGGER_FALLING)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & VT8500_EDGE;
+	if (edge) {
+		void __iomem *stat_reg = base + VT8500_IC_STATUS
+						+ (irq < 32 ? 0 : 4);
+		unsigned status = readl(stat_reg);
+
+		status |= (1 << (irq & 0x1f));
+		writel(status, stat_reg);
+	} else {
+		u8 dctr = readb(base + VT8500_IC_DCTR + irq);
+
+		dctr &= ~VT8500_INT_ENABLE;
+		writeb(dctr, base + VT8500_IC_DCTR + irq);
+	}
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 dctr;
+
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	dctr = readb(base + VT8500_IC_DCTR + irq);
+	dctr |= VT8500_INT_ENABLE;
+	writeb(dctr, base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	u8 dctr;
+
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+
+	dctr = readb(base + VT8500_IC_DCTR + irq);
+	dctr &= ~VT8500_EDGE;
+
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		dctr |= VT8500_TRIGGER_HIGH;
+		irq_desc[orig_irq].handle_irq = handle_level_irq;
+		break;
+	case IRQF_TRIGGER_FALLING:
+		dctr |= VT8500_TRIGGER_FALLING;
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	case IRQF_TRIGGER_RISING:
+		dctr |= VT8500_TRIGGER_RISING;
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	}
+	writeb(dctr, base + VT8500_IC_DCTR + irq);
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_ic_base, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_ic_base, SZ_64K);
+	sic_regbase = ioremap(wmt_sic_base, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR
+								+ i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..8ad825e
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,265 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+	int loops = msecs_to_loops(10);
+	while ((readb(reg) & bitmask) && --loops)
+		cpu_relax();
+
+	if (unlikely(!loops))
+		pr_warning("Waiting for status bits 0x%x to clear timed out\n",
+			   bitmask);
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	c = (unsigned long long)pv * duty_ns;
+	do_div(c, period_ns);
+	dc = c;
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else {
+			pwm = ERR_PTR(-EBUSY);
+		}
+	} else {
+		pwm = ERR_PTR(-ENOENT);
+	}
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else {
+		pr_warning("PWM device already freed\n");
+	}
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[0].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..d5376c5
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,155 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/delay.h>
+
+#include <asm/mach/time.h>
+
+#include "devices.h"
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+
+static void __iomem *regbase;
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	int loops = msecs_to_loops(10);
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	int loops = msecs_to_loops(10);
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_pmc_base + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register failed for %s\n",
+					clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_timer_irq, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq failed for %s\n",
+					clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..e73aadb
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,77 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_gpio_base + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_pmc_base + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be remapped, not enabling power off!\n");
+
+	wm8505_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= wm8505_reserve_mem,
+	.map_io		= wm8505_map_io,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.4


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

* Re: [PATCH 1/6 v10] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-22 22:02                                         ` Alexey Charkov
@ 2010-12-22 22:32                                           ` Ryan Mallon
  0 siblings, 0 replies; 91+ messages in thread
From: Ryan Mallon @ 2010-12-22 22:32 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

On 12/23/2010 11:02 AM, Alexey Charkov wrote:
> 2010/12/23 Ryan Mallon <ryan@bluewatersys.com>:
>> On 12/23/2010 10:18 AM, Alexey Charkov wrote:
>>> This adds support for the family of Systems-on-Chip produced initially
>>> by VIA and now its subsidiary WonderMedia that have recently become
>>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>>
>>> Support is included for both VT8500 and WM8505, selectable by a
>>> configuration switch at kernel build time.
>>>
>>> Included are basic machine initialization files, register and
>>> interrupt definitions, support for the on-chip interrupt controller,
>>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>>> pulse-width-modulated outputs control, as well as platform device
>>> configurations for the specific drivers implemented elsewhere.
>>>
>>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>>> ---
>>>
>>> Welcome the jubilee tenth revision of this patch ;-)
>>>
>>> I've tried to incorporate the suggestions by Ryan and Arnd, hope that
>>> there is nothing left out. There was a massive reorganization of code
>>> to remove less-than-obvious magic with MMIO registers and irqs being
>>> held in huge structs, now they are again macro definitions. Those
>>> macros are, however, only included in single isolated files, and
>>> actual values to use are chosen at runtime by calling the respective
>>> routines at machine initialization. There are also stylistic changes
>>> all around, where Ryan suggested.
>>>
>>> As a result, i8042 should again be adjusted a bit to reflect the new
>>> place to find respective register/irq definitions, that one will be
>>> sent in the respective thread shortly.
>>>
>>> Best regards,
>>> Alexey
>>
>> <snip>
>>
>>> --- /dev/null
>>> +++ b/arch/arm/mach-vt8500/devices-vt8500.c
>>> @@ -0,0 +1,117 @@
>>> +/* linux/arch/arm/mach-vt8500/devices-vt8500.c
>>> + *
>>> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * 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.
>>> + *
>>> + */
>>> +
>>> +#include <linux/platform_device.h>
>>> +
>>> +#include <mach/vt8500_regs.h>
>>> +#include <mach/vt8500_irqs.h>
>>> +#include <mach/i8042.h>
>>> +#include "devices.h"
>>> +
>>> +static inline struct resource WMT_MMIO_RES(u32 start, u32 size)
>>> +{
>>> +     struct resource tmp = {
>>> +             .flags = IORESOURCE_MEM,
>>> +             .start = start,
>>> +             .end = start + size - 1,
>>> +     };
>>> +
>>> +     return tmp;
>>> +}
>>
>> These functions can be marked __init (though I guess they already are if
>> marked inline?). They should also have lower case names since they are
>> proper functions.
> 
> As inline functions are unrolled into the caller at compile time, and
> the caller is __init, I would expect their code to be freed after init
> as well. I could be wrong, though :)

If the compiler decided not to inline them for whatever reason they
would stick around. They should also probably be marked __init to make
it clear that they are not permanent functions.

>> Do these functions generate warnings about returning temporary values
>> off the stack? If so, they could be rewritten as:
>>
> 
> Should those be compile-time or run-time? I did not see any.

I thought you might get compile time warnings. Returning things off the
stack in general is error prone. I think passing a pointer to the
resource into the function (as below) maybe a bit better.

>> static __init void wmt_mmio_res(struct resource *res,
>>                                u32 start, u32 size)
>> {
>>        res->flags = IORESOURCE_MEM;
>>        res->start = start;
>>        res->end   = start + size - 1;
>> }
>>
>> You could also make these functions static inline in devices.h to avoid
>> having to define them for each board.
>>
> 
> Agreed.
> 
>>> +
>>> +static inline struct resource WMT_IRQ_RES(int irq)
>>> +{
>>> +     struct resource tmp = {
>>> +             .flags = IORESOURCE_IRQ,
>>> +             .start = irq,
>>> +             .end = irq,
>>> +     };
>>> +
>>> +     return tmp;
>>> +}
>>> +
>>> +#define WMT_RES_ADD(__device, __resource, __num) \
>>> +if (platform_device_add_resources(__device, __resource, __num)) \
>>> +     pr_err("Failed to assign resources to __device##\n");
>>
>> This could be written as a proper function. The resource add is unlikely
>> to fail. Maybe keep the warning but don't worry about printing the
>> device name?
>>
> 
> There is memory allocation inside platform_device_add_resources, so
> probably there is scope for failure. I could add unlikely(), though.

I meant more in terms of losing the device name in the error which seems
to be the sole point of the macro. i.e it could be rewritten as:

static void wmt_add_resource(struct platform_device *pdev,
				const struct resource *res,
				unsigned num)
{
	if (platform_device_add_resources(pdev, res, num))
		pr_err("Failed to add device resource\n");
}

Because the add resource failing would indicate some serious problem you
may even want to BUG().

~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934

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

* Re: [PATCH 1/6 v11] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-22 22:25                                         ` [PATCH 1/6 v11] " Alexey Charkov
@ 2010-12-22 22:59                                           ` Ryan Mallon
  2010-12-22 23:38                                             ` [PATCH 1/6 v12] " Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Ryan Mallon @ 2010-12-22 22:59 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

On 12/23/2010 11:25 AM, Alexey Charkov wrote:
> This adds support for the family of Systems-on-Chip produced initially
> by VIA and now its subsidiary WonderMedia that have recently become
> widespread in lower-end Chinese ARM-based tablets and netbooks.
> 
> Support is included for both VT8500 and WM8505, selectable by a
> configuration switch at kernel build time.
> 
> Included are basic machine initialization files, register and
> interrupt definitions, support for the on-chip interrupt controller,
> high-precision OS timer, GPIO lines, necessary macros for early debug,
> pulse-width-modulated outputs control, as well as platform device
> configurations for the specific drivers implemented elsewhere.
> 
> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> ---
> 
> This incorporates latest comments by Ryan regarding helper inlines
> in devices-{vt8500,wm8505}.c
> 
> Best regards,
> Alexey

One final nitpick :-).

> +static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
> +				       unsigned offset)
> +{
> +	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
> +	unsigned val = readl(regbase + 0x20 + vt8500_chip->regoff);
> +
> +	val &= ~(1 << vt8500_chip->shift << offset);
> +	writel(val, regbase + 0x20 + vt8500_chip->regoff);

The magic numbers (0x20) in the gpio functions should probably be
#defined as register names. I'm guessing this is the input enable
register? Often people do helper functions for this, i.e:

static inline void gpio_write_reg(struct vt8500_gpio_chip *chip,
				  unsigned val, unsigned reg)
{
	writel(val, regbase + chip->regoff + reg);
}

Other than that, looks good.

Reviewed-by: Ryan Mallon <ryan@bluewatersys.com>

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934

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

* [PATCH 1/6 v12] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-22 22:59                                           ` Ryan Mallon
@ 2010-12-22 23:38                                             ` Alexey Charkov
  2010-12-28 14:52                                               ` Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-12-22 23:38 UTC (permalink / raw)
  To: Ryan Mallon
  Cc: Alexey Charkov, Russell King - ARM Linux, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

This adds support for the family of Systems-on-Chip produced initially
by VIA and now its subsidiary WonderMedia that have recently become
widespread in lower-end Chinese ARM-based tablets and netbooks.

Support is included for both VT8500 and WM8505, selectable by a
configuration switch at kernel build time.

Included are basic machine initialization files, register and
interrupt definitions, support for the on-chip interrupt controller,
high-precision OS timer, GPIO lines, necessary macros for early debug,
pulse-width-modulated outputs control, as well as platform device
configurations for the specific drivers implemented elsewhere.

Signed-off-by: Alexey Charkov <alchark@gmail.com>
---

This fixes magic numbers in gpio.c

Thanks,
Alexey

 arch/arm/Kconfig                                |   12 +
 arch/arm/Makefile                               |    1 +
 arch/arm/boot/compressed/Makefile               |    4 +
 arch/arm/boot/compressed/head-vt8500.S          |   46 ++++
 arch/arm/mach-vt8500/Kconfig                    |   73 ++++++
 arch/arm/mach-vt8500/Makefile                   |    9 +
 arch/arm/mach-vt8500/Makefile.boot              |    3 +
 arch/arm/mach-vt8500/bv07.c                     |   77 +++++++
 arch/arm/mach-vt8500/devices-vt8500.c           |   91 ++++++++
 arch/arm/mach-vt8500/devices-wm8505.c           |   99 +++++++++
 arch/arm/mach-vt8500/devices.c                  |  270 +++++++++++++++++++++++
 arch/arm/mach-vt8500/devices.h                  |   88 ++++++++
 arch/arm/mach-vt8500/gpio.c                     |  240 ++++++++++++++++++++
 arch/arm/mach-vt8500/include/mach/debug-macro.S |   31 +++
 arch/arm/mach-vt8500/include/mach/entry-macro.S |   32 +++
 arch/arm/mach-vt8500/include/mach/gpio.h        |    6 +
 arch/arm/mach-vt8500/include/mach/hardware.h    |   12 +
 arch/arm/mach-vt8500/include/mach/i8042.h       |   18 ++
 arch/arm/mach-vt8500/include/mach/io.h          |   28 +++
 arch/arm/mach-vt8500/include/mach/irqs.h        |   22 ++
 arch/arm/mach-vt8500/include/mach/memory.h      |   28 +++
 arch/arm/mach-vt8500/include/mach/system.h      |   18 ++
 arch/arm/mach-vt8500/include/mach/timex.h       |   26 +++
 arch/arm/mach-vt8500/include/mach/uncompress.h  |   37 +++
 arch/arm/mach-vt8500/include/mach/vmalloc.h     |   20 ++
 arch/arm/mach-vt8500/include/mach/vt8500_irqs.h |   88 ++++++++
 arch/arm/mach-vt8500/include/mach/vt8500_regs.h |   79 +++++++
 arch/arm/mach-vt8500/include/mach/vt8500fb.h    |   31 +++
 arch/arm/mach-vt8500/include/mach/wm8505_irqs.h |  115 ++++++++++
 arch/arm/mach-vt8500/include/mach/wm8505_regs.h |   78 +++++++
 arch/arm/mach-vt8500/irq.c                      |  177 +++++++++++++++
 arch/arm/mach-vt8500/pwm.c                      |  265 ++++++++++++++++++++++
 arch/arm/mach-vt8500/timer.c                    |  155 +++++++++++++
 arch/arm/mach-vt8500/wm8505_7in.c               |   77 +++++++
 34 files changed, 2356 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/boot/compressed/head-vt8500.S
 create mode 100644 arch/arm/mach-vt8500/Kconfig
 create mode 100644 arch/arm/mach-vt8500/Makefile
 create mode 100644 arch/arm/mach-vt8500/Makefile.boot
 create mode 100644 arch/arm/mach-vt8500/bv07.c
 create mode 100644 arch/arm/mach-vt8500/devices-vt8500.c
 create mode 100644 arch/arm/mach-vt8500/devices-wm8505.c
 create mode 100644 arch/arm/mach-vt8500/devices.c
 create mode 100644 arch/arm/mach-vt8500/devices.h
 create mode 100644 arch/arm/mach-vt8500/gpio.c
 create mode 100644 arch/arm/mach-vt8500/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-vt8500/include/mach/gpio.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/hardware.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/i8042.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/io.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/memory.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/system.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/timex.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vmalloc.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500_regs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/vt8500fb.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
 create mode 100644 arch/arm/mach-vt8500/include/mach/wm8505_regs.h
 create mode 100644 arch/arm/mach-vt8500/irq.c
 create mode 100644 arch/arm/mach-vt8500/pwm.c
 create mode 100644 arch/arm/mach-vt8500/timer.c
 create mode 100644 arch/arm/mach-vt8500/wm8505_7in.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d56d21c..53052fa 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -843,6 +843,16 @@ config PLAT_SPEAR
 	help
 	  Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx).
 
+config ARCH_VT8500
+	bool "VIA/WonderMedia 85xx"
+	select CPU_ARM926T
+	select GENERIC_GPIO
+	select ARCH_HAS_CPUFREQ
+	select GENERIC_CLOCKEVENTS
+	select ARCH_REQUIRE_GPIOLIB
+	select HAVE_PWM
+	help
+	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 endchoice
 
 #
@@ -973,6 +983,8 @@ source "arch/arm/mach-versatile/Kconfig"
 
 source "arch/arm/mach-vexpress/Kconfig"
 
+source "arch/arm/mach-vt8500/Kconfig"
+
 source "arch/arm/mach-w90x900/Kconfig"
 
 # Definitions to make life easier
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b87aed0..b0f219a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -189,6 +189,7 @@ machine-$(CONFIG_ARCH_U300)		:= u300
 machine-$(CONFIG_ARCH_U8500)		:= ux500
 machine-$(CONFIG_ARCH_VERSATILE)	:= versatile
 machine-$(CONFIG_ARCH_VEXPRESS)		:= vexpress
+machine-$(CONFIG_ARCH_VT8500)		:= vt8500
 machine-$(CONFIG_ARCH_W90X900)		:= w90x900
 machine-$(CONFIG_ARCH_NUC93X)		:= nuc93x
 machine-$(CONFIG_FOOTBRIDGE)		:= footbridge
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 65a7c1c..62cade4 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_ARCH_SA1100),y)
 OBJS		+= head-sa1100.o
 endif
 
+ifeq ($(CONFIG_ARCH_VT8500),y)
+OBJS		+= head-vt8500.o
+endif
+
 ifeq ($(CONFIG_CPU_XSCALE),y)
 OBJS		+= head-xscale.o
 endif
diff --git a/arch/arm/boot/compressed/head-vt8500.S b/arch/arm/boot/compressed/head-vt8500.S
new file mode 100644
index 0000000..1dc1e21
--- /dev/null
+++ b/arch/arm/boot/compressed/head-vt8500.S
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/boot/compressed/head-vt8500.S
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * VIA VT8500 specific tweaks. This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+		.section        ".start", "ax"
+
+__VT8500_start:
+	@ Compare the SCC ID register against a list of known values
+	ldr	r1, .SCCID
+	ldr	r3, [r1]
+
+	@ VT8500 override
+	ldr	r4, .VT8500SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_BV07
+	beq	.Lendvt8500
+
+	@ WM8505 override
+	ldr	r4, .WM8505SCC
+	cmp	r3, r4
+	ldreq	r7, .ID_8505
+	beq	.Lendvt8500
+
+	@ Otherwise, leave the bootloader's machine id untouched
+
+.SCCID:
+	.word	0xd8120000
+.VT8500SCC:
+	.word	0x34000102
+.WM8505SCC:
+	.word	0x34260103
+
+.ID_BV07:
+	.word	MACH_TYPE_BV07
+.ID_8505:
+	.word	MACH_TYPE_WM8505_7IN_NETBOOK
+
+.Lendvt8500:
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
new file mode 100644
index 0000000..2c20a34
--- /dev/null
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -0,0 +1,73 @@
+if ARCH_VT8500
+
+config VTWM_VERSION_VT8500
+	bool
+
+config VTWM_VERSION_WM8505
+	bool
+
+config MACH_BV07
+	bool "Benign BV07-8500 Mini Netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_VT8500
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a VIA VT8500 chip.
+
+config MACH_WM8505_7IN_NETBOOK
+	bool "WM8505 7-inch generic netbook"
+	depends on ARCH_VT8500
+	select VTWM_VERSION_WM8505
+	help
+	  Add support for the inexpensive 7-inch netbooks sold by many
+	  Chinese distributors under various names. Note that there are
+	  many hardware implementations in identical exterior, make sure
+	  that yours is indeed based on a WonderMedia WM8505 chip.
+
+comment "LCD panel size"
+
+config WMT_PANEL_800X480
+	bool "7-inch with 800x480 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	default y
+	help
+	  These are found in most of the netbooks in generic cases, as
+	  well as in Eken M001 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x480' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_800X600
+	bool "8-inch with 800x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M003 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=800x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X576
+	bool "10-inch with 1024x576 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in CherryPal netbooks and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x576' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+config WMT_PANEL_1024X600
+	bool "10-inch with 1024x600 resolution"
+	depends on (FB_VT8500 || FB_WM8505)
+	help
+	  These are found in Eken M006 tablets and possibly elsewhere.
+
+	  To select this panel at runtime, say y here and append
+	  'panel=1024x600' to your kernel command line. Otherwise, the
+	  largest one available will be used.
+
+endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
new file mode 100644
index 0000000..81aedb7
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile
@@ -0,0 +1,9 @@
+obj-y += devices.o gpio.o irq.o timer.o
+
+obj-$(CONFIG_VTWM_VERSION_VT8500) += devices-vt8500.o
+obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
+
+obj-$(CONFIG_MACH_BV07) += bv07.o
+obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
diff --git a/arch/arm/mach-vt8500/Makefile.boot b/arch/arm/mach-vt8500/Makefile.boot
new file mode 100644
index 0000000..a8acc4e
--- /dev/null
+++ b/arch/arm/mach-vt8500/Makefile.boot
@@ -0,0 +1,3 @@
+   zreladdr-y	:= 0x00008000
+params_phys-y	:= 0x00000100
+initrd_phys-y	:= 0x01000000
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
new file mode 100644
index 0000000..94a261d
--- /dev/null
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -0,0 +1,77 @@
+/*
+ *  arch/arm/mach-vt8500/bv07.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_lcdc,
+	&vt8500_device_ehci,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init bv07_init(void)
+{
+#ifdef CONFIG_FB_VT8500
+	void __iomem *gpio_mux_reg = ioremap(wmt_gpio_base + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_pmc_base + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be remapped, not enabling power off!\n");
+
+	vt8500_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(BV07, "Benign BV07 Mini Netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= vt8500_reserve_mem,
+	.map_io		= vt8500_map_io,
+	.init_irq	= vt8500_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= bv07_init,
+MACHINE_END
diff --git a/arch/arm/mach-vt8500/devices-vt8500.c b/arch/arm/mach-vt8500/devices-vt8500.c
new file mode 100644
index 0000000..19519ae
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices-vt8500.c
@@ -0,0 +1,91 @@
+/* linux/arch/arm/mach-vt8500/devices-vt8500.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+#include <mach/vt8500_regs.h>
+#include <mach/vt8500_irqs.h>
+#include <mach/i8042.h>
+#include "devices.h"
+
+void __init vt8500_set_resources(void)
+{
+	struct resource tmp[3];
+
+	tmp[0] = wmt_mmio_res(VT8500_LCDC_BASE, SZ_1K);
+	tmp[1] = wmt_irq_res(IRQ_LCDC);
+	wmt_res_add(&vt8500_device_lcdc, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_UART0_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART0);
+	wmt_res_add(&vt8500_device_uart0, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_UART1_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART1);
+	wmt_res_add(&vt8500_device_uart1, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_UART2_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART2);
+	wmt_res_add(&vt8500_device_uart2, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_UART3_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART3);
+	wmt_res_add(&vt8500_device_uart3, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_EHCI_BASE, SZ_512);
+	tmp[1] = wmt_irq_res(IRQ_EHCI);
+	wmt_res_add(&vt8500_device_ehci, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(VT8500_GEGEA_BASE, SZ_256);
+	wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(VT8500_PWM_BASE, 0x44);
+	wmt_res_add(&vt8500_device_pwm, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(VT8500_RTC_BASE, 0x2c);
+	tmp[1] = wmt_irq_res(IRQ_RTC);
+	tmp[2] = wmt_irq_res(IRQ_RTCSM);
+	wmt_res_add(&vt8500_device_rtc, tmp, 3);
+}
+
+static void __init vt8500_set_externs(void)
+{
+	/* Non-resource-aware stuff */
+	wmt_ic_base = VT8500_IC_BASE;
+	wmt_gpio_base = VT8500_GPIO_BASE;
+	wmt_pmc_base = VT8500_PMC_BASE;
+	wmt_i8042_base = VT8500_PS2_BASE;
+
+	wmt_nr_irqs = VT8500_NR_IRQS;
+	wmt_timer_irq = IRQ_PMCOS0;
+	wmt_gpio_ext_irq[0] = IRQ_EXT0;
+	wmt_gpio_ext_irq[1] = IRQ_EXT1;
+	wmt_gpio_ext_irq[2] = IRQ_EXT2;
+	wmt_gpio_ext_irq[3] = IRQ_EXT3;
+	wmt_gpio_ext_irq[4] = IRQ_EXT4;
+	wmt_gpio_ext_irq[5] = IRQ_EXT5;
+	wmt_gpio_ext_irq[6] = IRQ_EXT6;
+	wmt_gpio_ext_irq[7] = IRQ_EXT7;
+	wmt_i8042_kbd_irq = IRQ_PS2KBD;
+	wmt_i8042_aux_irq = IRQ_PS2MOUSE;
+}
+
+void __init vt8500_map_io(void)
+{
+	iotable_init(wmt_io_desc, ARRAY_SIZE(wmt_io_desc));
+
+	/* Should be done before interrupts and timers are initialized */
+	vt8500_set_externs();
+}
diff --git a/arch/arm/mach-vt8500/devices-wm8505.c b/arch/arm/mach-vt8500/devices-wm8505.c
new file mode 100644
index 0000000..db4594e
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices-wm8505.c
@@ -0,0 +1,99 @@
+/* linux/arch/arm/mach-vt8500/devices-wm8505.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+#include <mach/wm8505_regs.h>
+#include <mach/wm8505_irqs.h>
+#include <mach/i8042.h>
+#include "devices.h"
+
+void __init wm8505_set_resources(void)
+{
+	struct resource tmp[3];
+
+	tmp[0] = wmt_mmio_res(WM8505_GOVR_BASE, SZ_512);
+	wmt_res_add(&vt8500_device_wm8505_fb, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART0_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART0);
+	wmt_res_add(&vt8500_device_uart0, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART1_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART1);
+	wmt_res_add(&vt8500_device_uart1, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART2_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART2);
+	wmt_res_add(&vt8500_device_uart2, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART3_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART3);
+	wmt_res_add(&vt8500_device_uart3, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART4_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART4);
+	wmt_res_add(&vt8500_device_uart4, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_UART5_BASE, 0x1040);
+	tmp[1] = wmt_irq_res(IRQ_UART5);
+	wmt_res_add(&vt8500_device_uart5, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_EHCI_BASE, SZ_512);
+	tmp[1] = wmt_irq_res(IRQ_EHCI);
+	wmt_res_add(&vt8500_device_ehci, tmp, 2);
+
+	tmp[0] = wmt_mmio_res(WM8505_GEGEA_BASE, SZ_256);
+	wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(WM8505_PWM_BASE, 0x44);
+	wmt_res_add(&vt8500_device_pwm, tmp, 1);
+
+	tmp[0] = wmt_mmio_res(WM8505_RTC_BASE, 0x2c);
+	tmp[1] = wmt_irq_res(IRQ_RTC);
+	tmp[2] = wmt_irq_res(IRQ_RTCSM);
+	wmt_res_add(&vt8500_device_rtc, tmp, 3);
+}
+
+static void __init wm8505_set_externs(void)
+{
+	/* Non-resource-aware stuff */
+	wmt_ic_base = WM8505_IC_BASE;
+	wmt_sic_base = WM8505_SIC_BASE;
+	wmt_gpio_base = WM8505_GPIO_BASE;
+	wmt_pmc_base = WM8505_PMC_BASE;
+	wmt_i8042_base = WM8505_PS2_BASE;
+
+	wmt_nr_irqs = WM8505_NR_IRQS;
+	wmt_timer_irq = IRQ_PMCOS0;
+	wmt_gpio_ext_irq[0] = IRQ_EXT0;
+	wmt_gpio_ext_irq[1] = IRQ_EXT1;
+	wmt_gpio_ext_irq[2] = IRQ_EXT2;
+	wmt_gpio_ext_irq[3] = IRQ_EXT3;
+	wmt_gpio_ext_irq[4] = IRQ_EXT4;
+	wmt_gpio_ext_irq[5] = IRQ_EXT5;
+	wmt_gpio_ext_irq[6] = IRQ_EXT6;
+	wmt_gpio_ext_irq[7] = IRQ_EXT7;
+	wmt_i8042_kbd_irq = IRQ_PS2KBD;
+	wmt_i8042_aux_irq = IRQ_PS2MOUSE;
+}
+
+void __init wm8505_map_io(void)
+{
+	iotable_init(wmt_io_desc, ARRAY_SIZE(wmt_io_desc));
+
+	/* Should be done before interrupts and timers are initialized */
+	wm8505_set_externs();
+}
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
new file mode 100644
index 0000000..1fcdc36
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.c
@@ -0,0 +1,270 @@
+/* linux/arch/arm/mach-vt8500/devices.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/memblock.h>
+
+#include <asm/mach/arch.h>
+
+#include <mach/vt8500fb.h>
+#include <mach/i8042.h>
+#include "devices.h"
+
+/* These can't use resources currently */
+unsigned long wmt_ic_base __initdata;
+unsigned long wmt_sic_base __initdata;
+unsigned long wmt_gpio_base __initdata;
+unsigned long wmt_pmc_base __initdata;
+unsigned long wmt_i8042_base __initdata;
+
+int wmt_nr_irqs __initdata;
+int wmt_timer_irq __initdata;
+int wmt_gpio_ext_irq[8] __initdata;
+
+/* Should remain accessible after init.
+ * i8042 driver desperately calls for attention...
+ */
+int wmt_i8042_kbd_irq;
+int wmt_i8042_aux_irq;
+
+static u64 fb_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_lcdc = {
+	.name           = "vt8500-lcd",
+	.id             = 0,
+	.dev		= {
+		.dma_mask	= &fb_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};
+
+struct platform_device vt8500_device_wm8505_fb = {
+	.name           = "wm8505-fb",
+	.id             = 0,
+};
+
+/* Smallest to largest */
+static struct vt8500fb_platform_data panels[] = {
+#ifdef CONFIG_WMT_PANEL_800X480
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 480 * 2,
+	.mode		= {
+		.name		= "800x480",
+		.xres		= 800,
+		.yres		= 480,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_800X600
+{
+	.xres_virtual	= 800,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "800x600",
+		.xres		= 800,
+		.yres		= 600,
+		.left_margin	= 88,
+		.right_margin	= 40,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 0,
+		.vsync_len	= 1,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X576
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 576 * 2,
+	.mode		= {
+		.name		= "1024x576",
+		.xres		= 1024,
+		.yres		= 576,
+		.left_margin	= 40,
+		.right_margin	= 24,
+		.upper_margin	= 32,
+		.lower_margin	= 11,
+		.hsync_len	= 96,
+		.vsync_len	= 2,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+#ifdef CONFIG_WMT_PANEL_1024X600
+{
+	.xres_virtual	= 1024,
+	.yres_virtual	= 600 * 2,
+	.mode		= {
+		.name		= "1024x600",
+		.xres		= 1024,
+		.yres		= 600,
+		.left_margin	= 66,
+		.right_margin	= 2,
+		.upper_margin	= 19,
+		.lower_margin	= 1,
+		.hsync_len	= 23,
+		.vsync_len	= 8,
+		.vmode		= FB_VMODE_NONINTERLACED,
+	},
+},
+#endif
+};
+
+static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
+
+static int __init panel_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(panels); i++) {
+		if (strcmp(panels[i].mode.name, str) == 0) {
+			current_panel_idx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+early_param("panel", panel_setup);
+
+static inline void preallocate_fb(struct vt8500fb_platform_data *p,
+				  unsigned long align) {
+	p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
+			(p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
+					(8 / p->bpp) + 1));
+	p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
+							  align);
+	p->video_mem_virt = phys_to_virt(p->video_mem_phys);
+}
+
+struct platform_device vt8500_device_uart0 = {
+	.name		= "vt8500_serial",
+	.id		= 0,
+};
+
+struct platform_device vt8500_device_uart1 = {
+	.name		= "vt8500_serial",
+	.id		= 1,
+};
+
+struct platform_device vt8500_device_uart2 = {
+	.name		= "vt8500_serial",
+	.id		= 2,
+};
+
+struct platform_device vt8500_device_uart3 = {
+	.name		= "vt8500_serial",
+	.id		= 3,
+};
+
+struct platform_device vt8500_device_uart4 = {
+	.name		= "vt8500_serial",
+	.id		= 4,
+};
+
+struct platform_device vt8500_device_uart5 = {
+	.name		= "vt8500_serial",
+	.id		= 5,
+};
+
+static u64 ehci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_ehci = {
+	.name		= "vt8500-ehci",
+	.id		= 0,
+	.dev		= {
+		.dma_mask	= &ehci_dma_mask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};
+
+struct platform_device vt8500_device_ge_rops = {
+	.name		= "wmt_ge_rops",
+	.id		= -1,
+};
+
+struct platform_device vt8500_device_pwm = {
+	.name		= "vt8500-pwm",
+	.id		= 0,
+};
+
+static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
+	.pwm_id		= 0,
+	.max_brightness	= 128,
+	.dft_brightness = 70,
+	.pwm_period_ns	= 250000, /* revisit when clocks are implemented */
+};
+
+struct platform_device vt8500_device_pwmbl = {
+	.name		= "pwm-backlight",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &vt8500_pwmbl_data,
+	},
+};
+
+struct platform_device vt8500_device_rtc = {
+	.name		= "vt8500-rtc",
+	.id		= 0,
+};
+
+struct map_desc wmt_io_desc[] __initdata = {
+	/* SoC MMIO registers */
+	[0] = {
+		.virtual	= 0xf8000000,
+		.pfn		= __phys_to_pfn(0xd8000000),
+		.length		= 0x00390000, /* max of all chip variants */
+		.type		= MT_DEVICE
+	},
+	/* PCI I/O space, numbers tied to those in <mach/io.h> */
+	[1] = {
+		.virtual	= 0xf0000000,
+		.pfn		= __phys_to_pfn(0xc0000000),
+		.length		= SZ_64K,
+		.type		= MT_DEVICE
+	},
+};
+
+void __init vt8500_reserve_mem(void)
+{
+#ifdef CONFIG_FB_VT8500
+	panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
+	preallocate_fb(&panels[current_panel_idx], SZ_4M);
+	vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
+
+void __init wm8505_reserve_mem(void)
+{
+#if defined CONFIG_FB_WM8505
+	panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
+	preallocate_fb(&panels[current_panel_idx], 32);
+	vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
+#endif
+}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
new file mode 100644
index 0000000..188d4e1
--- /dev/null
+++ b/arch/arm/mach-vt8500/devices.h
@@ -0,0 +1,88 @@
+/* linux/arch/arm/mach-vt8500/devices.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_VT8500_DEVICES_H
+#define __ARCH_ARM_MACH_VT8500_DEVICES_H
+
+#include <linux/platform_device.h>
+#include <asm/mach/map.h>
+
+void __init vt8500_init_irq(void);
+void __init wm8505_init_irq(void);
+void __init vt8500_map_io(void);
+void __init wm8505_map_io(void);
+void __init vt8500_reserve_mem(void);
+void __init wm8505_reserve_mem(void);
+void __init vt8500_gpio_init(void);
+void __init vt8500_set_resources(void);
+void __init wm8505_set_resources(void);
+
+extern unsigned long wmt_ic_base __initdata;
+extern unsigned long wmt_sic_base __initdata;
+extern unsigned long wmt_gpio_base __initdata;
+extern unsigned long wmt_pmc_base __initdata;
+
+extern int wmt_nr_irqs __initdata;
+extern int wmt_timer_irq __initdata;
+extern int wmt_gpio_ext_irq[8] __initdata;
+
+extern struct map_desc wmt_io_desc[2] __initdata;
+
+static inline struct resource wmt_mmio_res(u32 start, u32 size)
+{
+	struct resource tmp = {
+		.flags = IORESOURCE_MEM,
+		.start = start,
+		.end = start + size - 1,
+	};
+
+	return tmp;
+}
+
+static inline struct resource wmt_irq_res(int irq)
+{
+	struct resource tmp = {
+		.flags = IORESOURCE_IRQ,
+		.start = irq,
+		.end = irq,
+	};
+
+	return tmp;
+}
+
+static inline void wmt_res_add(struct platform_device *pdev,
+			       const struct resource *res, unsigned int num)
+{
+	if (unlikely(platform_device_add_resources(pdev, res, num)))
+		pr_err("Failed to assign resources\n");
+}
+
+extern struct sys_timer vt8500_timer;
+
+extern struct platform_device vt8500_device_uart0;
+extern struct platform_device vt8500_device_uart1;
+extern struct platform_device vt8500_device_uart2;
+extern struct platform_device vt8500_device_uart3;
+extern struct platform_device vt8500_device_uart4;
+extern struct platform_device vt8500_device_uart5;
+
+extern struct platform_device vt8500_device_lcdc;
+extern struct platform_device vt8500_device_wm8505_fb;
+extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_ge_rops;
+extern struct platform_device vt8500_device_pwm;
+extern struct platform_device vt8500_device_pwmbl;
+extern struct platform_device vt8500_device_rtc;
+#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
new file mode 100644
index 0000000..2bcc0ec
--- /dev/null
+++ b/arch/arm/mach-vt8500/gpio.c
@@ -0,0 +1,240 @@
+/* linux/arch/arm/mach-vt8500/gpio.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include "devices.h"
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+#define ENABLE_REGS	0x0
+#define DIRECTION_REGS	0x20
+#define OUTVALUE_REGS	0x40
+#define INVALUE_REGS	0x60
+
+#define EXT_REGOFF	0x1c
+
+static void __iomem *regbase;
+
+struct vt8500_gpio_chip {
+	struct gpio_chip	chip;
+	unsigned int		shift;
+	unsigned int		regoff;
+};
+
+static int gpio_to_irq_map[8];
+
+static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
+				     unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + ENABLE_REGS + vt8500_chip->regoff);
+
+	val |= (1 << vt8500_chip->shift << offset);
+	writel(val, regbase + ENABLE_REGS + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
+				   unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + ENABLE_REGS + vt8500_chip->regoff);
+
+	val &= ~(1 << vt8500_chip->shift << offset);
+	writel(val, regbase + ENABLE_REGS + vt8500_chip->regoff);
+}
+
+static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + DIRECTION_REGS + vt8500_chip->regoff);
+
+	val &= ~(1 << vt8500_chip->shift << offset);
+	writel(val, regbase + DIRECTION_REGS + vt8500_chip->regoff);
+
+	return 0;
+}
+
+static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + DIRECTION_REGS + vt8500_chip->regoff);
+
+	val |= (1 << vt8500_chip->shift << offset);
+	writel(val, regbase + DIRECTION_REGS + vt8500_chip->regoff);
+
+	if (value) {
+		val = readl(regbase + OUTVALUE_REGS + vt8500_chip->regoff);
+		val |= (1 << vt8500_chip->shift << offset);
+		writel(val, regbase + OUTVALUE_REGS + vt8500_chip->regoff);
+	}
+	return 0;
+}
+
+static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return (readl(regbase + INVALUE_REGS + vt8500_chip->regoff)
+		>> vt8500_chip->shift >> offset) & 1;
+}
+
+static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+	unsigned val = readl(regbase + INVALUE_REGS + vt8500_chip->regoff);
+
+	if (value)
+		val |= (1 << vt8500_chip->shift << offset);
+	else
+		val &= ~(1 << vt8500_chip->shift << offset);
+
+	writel(val, regbase + INVALUE_REGS + vt8500_chip->regoff);
+}
+
+#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num)		\
+{									\
+	.chip = {							\
+		.label			= __name,			\
+		.request		= vt8500_muxed_gpio_request,	\
+		.free			= vt8500_muxed_gpio_free,	\
+		.direction_input  = vt8500_muxed_gpio_direction_input,	\
+		.direction_output = vt8500_muxed_gpio_direction_output,	\
+		.get			= vt8500_muxed_gpio_get_value,	\
+		.set			= vt8500_muxed_gpio_set_value,	\
+		.can_sleep		= 0,				\
+		.base			= __base,			\
+		.ngpio			= __num,			\
+	},								\
+	.shift		= __shift,					\
+	.regoff		= __off,					\
+}
+
+static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
+	VT8500_GPIO_BANK("uart0",	0,	0x0,	8,	4),
+	VT8500_GPIO_BANK("uart1",	4,	0x0,	12,	4),
+	VT8500_GPIO_BANK("spi0",	8,	0x0,	16,	4),
+	VT8500_GPIO_BANK("spi1",	12,	0x0,	20,	4),
+	VT8500_GPIO_BANK("spi2",	16,	0x0,	24,	4),
+	VT8500_GPIO_BANK("pwmout",	24,	0x0,	28,	2),
+
+	VT8500_GPIO_BANK("sdmmc",	0,	0x4,	30,	11),
+	VT8500_GPIO_BANK("ms",		16,	0x4,	41,	7),
+	VT8500_GPIO_BANK("i2c0",	24,	0x4,	48,	2),
+	VT8500_GPIO_BANK("i2c1",	26,	0x4,	50,	2),
+
+	VT8500_GPIO_BANK("mii",		0,	0x8,	52,	20),
+	VT8500_GPIO_BANK("see",		20,	0x8,	72,	4),
+	VT8500_GPIO_BANK("ide",		24,	0x8,	76,	7),
+
+	VT8500_GPIO_BANK("ccir",	0,	0xc,	83,	19),
+
+	VT8500_GPIO_BANK("ts",		8,	0x10,	102,	11),
+
+	VT8500_GPIO_BANK("lcd",		0,	0x14,	113,	23),
+};
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	unsigned val = readl(regbase + DIRECTION_REGS + EXT_REGOFF);
+
+	val &= ~(1 << offset);
+	writel(val, regbase + DIRECTION_REGS + EXT_REGOFF);
+	return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	unsigned val = readl(regbase + DIRECTION_REGS + EXT_REGOFF);
+
+	val |= (1 << offset);
+	writel(val, regbase + DIRECTION_REGS + EXT_REGOFF);
+
+	if (value) {
+		val = readl(regbase + OUTVALUE_REGS + EXT_REGOFF);
+		val |= (1 << offset);
+		writel(val, regbase + OUTVALUE_REGS + EXT_REGOFF);
+	}
+	return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	return (readl(regbase + INVALUE_REGS + EXT_REGOFF) >> offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	unsigned val = readl(regbase + OUTVALUE_REGS + EXT_REGOFF);
+
+	if (value)
+		val |= (1 << offset);
+	else
+		val &= ~(1 << offset);
+
+	writel(val, regbase + OUTVALUE_REGS + EXT_REGOFF);
+}
+
+static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset > 7)
+		return -EINVAL;
+
+	return gpio_to_irq_map[offset];
+}
+
+static struct gpio_chip vt8500_external_gpios = {
+	.label			= "extgpio",
+	.direction_input	= vt8500_gpio_direction_input,
+	.direction_output	= vt8500_gpio_direction_output,
+	.get			= vt8500_gpio_get_value,
+	.set			= vt8500_gpio_set_value,
+	.to_irq			= vt8500_gpio_to_irq,
+	.can_sleep		= 0,
+	.base			= 0,
+	.ngpio			= 8,
+};
+
+void __init vt8500_gpio_init(void)
+{
+	int i;
+
+	for (i = 0; i < 8; i++)
+		gpio_to_irq_map[i] = wmt_gpio_ext_irq[i];
+
+	regbase = ioremap(wmt_gpio_base, SZ_64K);
+	if (!regbase) {
+		printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
+		return;
+	}
+
+	gpiochip_add(&vt8500_external_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
+		gpiochip_add(&vt8500_muxed_gpios[i].chip);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/debug-macro.S b/arch/arm/mach-vt8500/include/mach/debug-macro.S
new file mode 100644
index 0000000..f119162
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/debug-macro.S
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/debug-macro.S
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+	.macro	addruart, rp, rv
+	mov	\rp,      #0x00200000
+	orr	\rv, \rp, #0xf8000000
+	orr	\rp, \rp, #0xd8000000
+	.endm
+
+	.macro	senduart,rd,rx
+	strb	\rd, [\rx, #0]
+	.endm
+
+	.macro	busyuart,rd,rx
+1001:	ldr	\rd, [\rx, #0x1c]
+	ands	\rd, \rd, #0x2
+	bne	1001b
+	.endm
+
+	.macro	waituart,rd,rx
+	.endm
diff --git a/arch/arm/mach-vt8500/include/mach/entry-macro.S b/arch/arm/mach-vt8500/include/mach/entry-macro.S
new file mode 100644
index 0000000..92684c7
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/entry-macro.S
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for VIA VT8500
+ *
+ * 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.
+ */
+
+	.macro	disable_fiq
+	.endm
+
+	.macro  get_irqnr_preamble, base, tmp
+	@ physical 0xd8140000 is virtual 0xf8140000
+	mov	\base, #0xf8000000
+	orr	\base, \base, #0x00140000
+	.endm
+
+	.macro  arch_ret_to_user, tmp1, tmp2
+	.endm
+
+	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp
+	ldr	\irqnr, [\base]
+	cmp	\irqnr, #63 @ may be false positive, check interrupt status
+	bne	1001f
+	ldr	\irqstat, [\base, #0x84]
+	ands	\irqstat, #0x80000000
+	moveq	\irqnr, #0
+1001:
+	.endm
+
diff --git a/arch/arm/mach-vt8500/include/mach/gpio.h b/arch/arm/mach-vt8500/include/mach/gpio.h
new file mode 100644
index 0000000..94ff276
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
diff --git a/arch/arm/mach-vt8500/include/mach/hardware.h b/arch/arm/mach-vt8500/include/mach/hardware.h
new file mode 100644
index 0000000..db4163f
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/* arch/arm/mach-vt8500/include/mach/hardware.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-vt8500/include/mach/i8042.h b/arch/arm/mach-vt8500/include/mach/i8042.h
new file mode 100644
index 0000000..cd7143c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/i8042.h
@@ -0,0 +1,18 @@
+/* arch/arm/mach-vt8500/include/mach/i8042.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+extern unsigned long wmt_i8042_base __initdata;
+extern int wmt_i8042_kbd_irq;
+extern int wmt_i8042_aux_irq;
diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
new file mode 100644
index 0000000..9077239
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/io.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/io.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)		__typesafe_io((a) + 0xf0000000)
+#define __mem_pci(a)	(a)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/irqs.h b/arch/arm/mach-vt8500/include/mach/irqs.h
new file mode 100644
index 0000000..a129fd1
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/irqs.h
@@ -0,0 +1,22 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This value is just to make the core happy, never used otherwise */
+#define NR_IRQS 128
diff --git a/arch/arm/mach-vt8500/include/mach/memory.h b/arch/arm/mach-vt8500/include/mach/memory.h
new file mode 100644
index 0000000..175f914
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/memory.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET	UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/system.h b/arch/arm/mach-vt8500/include/mach/system.h
new file mode 100644
index 0000000..d6c757e
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/system.h
@@ -0,0 +1,18 @@
+/*
+ * arch/arm/mach-vt8500/include/mach/system.h
+ *
+ */
+#include <asm/io.h>
+
+/* PM Software Reset request register */
+#define VT8500_PMSR_VIRT	0xf8130060
+
+static inline void arch_idle(void)
+{
+	cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+	writel(1, VT8500_PMSR_VIRT);
+}
diff --git a/arch/arm/mach-vt8500/include/mach/timex.h b/arch/arm/mach-vt8500/include/mach/timex.h
new file mode 100644
index 0000000..8487e4c
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/timex.h
@@ -0,0 +1,26 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/timex.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MACH_TIMEX_H
+#define MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE		(3000000)
+
+#endif /* MACH_TIMEX_H */
diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h b/arch/arm/mach-vt8500/include/mach/uncompress.h
new file mode 100644
index 0000000..bb9e2d2
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
@@ -0,0 +1,37 @@
+/* arch/arm/mach-vt8500/include/mach/uncompress.h
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on arch/arm/mach-dove/include/mach/uncompress.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define UART0_PHYS 0xd8200000
+#include <asm/io.h>
+
+static void putc(const char c)
+{
+	while (readb(UART0_PHYS + 0x1c) & 0x2)
+		/* Tx busy, wait and poll */;
+
+	writeb(c, UART0_PHYS);
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-vt8500/include/mach/vmalloc.h b/arch/arm/mach-vt8500/include/mach/vmalloc.h
new file mode 100644
index 0000000..4642290
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define VMALLOC_END	0xd0000000UL
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h b/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
new file mode 100644
index 0000000..ecfee91
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
@@ -0,0 +1,88 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* VT8500 Interrupt Sources */
+
+#define IRQ_JPEGENC	0	/* JPEG Encoder */
+#define IRQ_JPEGDEC	1	/* JPEG Decoder */
+				/* Reserved */
+#define IRQ_PATA	3	/* PATA Controller */
+				/* Reserved */
+#define IRQ_DMA		5	/* DMA Controller */
+#define IRQ_EXT0	6	/* External Interrupt 0 */
+#define IRQ_EXT1	7	/* External Interrupt 1 */
+#define IRQ_GE		8	/* Graphic Engine */
+#define IRQ_GOV		9	/* Graphic Overlay Engine */
+#define IRQ_ETHER	10	/* Ethernet MAC */
+#define IRQ_MPEGTS	11	/* Transport Stream Interface */
+#define IRQ_LCDC	12	/* LCD Controller */
+#define IRQ_EXT2	13	/* External Interrupt 2 */
+#define IRQ_EXT3	14	/* External Interrupt 3 */
+#define IRQ_EXT4	15	/* External Interrupt 4 */
+#define IRQ_CIPHER	16	/* Cipher */
+#define IRQ_VPP		17	/* Video Post-Processor */
+#define IRQ_I2C1	18	/* I2C 1 */
+#define IRQ_I2C0	19	/* I2C 0 */
+#define IRQ_SDMMC	20	/* SD/MMC Controller */
+#define IRQ_SDMMC_DMA	21	/* SD/MMC Controller DMA */
+#define IRQ_PMC_WU	22	/* Power Management Controller Wakeup */
+				/* Reserved */
+#define IRQ_SPI0	24	/* SPI 0 */
+#define IRQ_SPI1	25	/* SPI 1 */
+#define IRQ_SPI2	26	/* SPI 2 */
+#define IRQ_LCDDF	27	/* LCD Data Formatter */
+#define IRQ_NAND	28	/* NAND Flash Controller */
+#define IRQ_NAND_DMA	29	/* NAND Flash Controller DMA */
+#define IRQ_MS		30	/* MemoryStick Controller */
+#define IRQ_MS_DMA	31	/* MemoryStick Controller DMA */
+#define IRQ_UART0	32	/* UART 0 */
+#define IRQ_UART1	33	/* UART 1 */
+#define IRQ_I2S		34	/* I2S */
+#define IRQ_PCM		35	/* PCM */
+#define IRQ_PMCOS0	36	/* PMC OS Timer 0 */
+#define IRQ_PMCOS1	37	/* PMC OS Timer 1 */
+#define IRQ_PMCOS2	38	/* PMC OS Timer 2 */
+#define IRQ_PMCOS3	39	/* PMC OS Timer 3 */
+#define IRQ_VPU		40	/* Video Processing Unit */
+#define IRQ_VID		41	/* Video Digital Input Interface */
+#define IRQ_AC97	42	/* AC97 Interface */
+#define IRQ_EHCI	43	/* USB */
+#define IRQ_NOR		44	/* NOR Flash Controller */
+#define IRQ_PS2MOUSE	45	/* PS/2 Mouse */
+#define IRQ_PS2KBD	46	/* PS/2 Keyboard */
+#define IRQ_UART2	47	/* UART 2 */
+#define IRQ_RTC		48	/* RTC Interrupt */
+#define IRQ_RTCSM	49	/* RTC Second/Minute Update Interrupt */
+#define IRQ_UART3	50	/* UART 3 */
+#define IRQ_ADC		51	/* ADC */
+#define IRQ_EXT5	52	/* External Interrupt 5 */
+#define IRQ_EXT6	53	/* External Interrupt 6 */
+#define IRQ_EXT7	54	/* External Interrupt 7 */
+#define IRQ_CIR		55	/* CIR */
+#define IRQ_DMA0	56	/* DMA Channel 0 */
+#define IRQ_DMA1	57	/* DMA Channel 1 */
+#define IRQ_DMA2	58	/* DMA Channel 2 */
+#define IRQ_DMA3	59	/* DMA Channel 3 */
+#define IRQ_DMA4	60	/* DMA Channel 4 */
+#define IRQ_DMA5	61	/* DMA Channel 5 */
+#define IRQ_DMA6	62	/* DMA Channel 6 */
+#define IRQ_DMA7	63	/* DMA Channel 7 */
+
+#define VT8500_NR_IRQS		64
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500_regs.h b/arch/arm/mach-vt8500/include/mach/vt8500_regs.h
new file mode 100644
index 0000000..29c63ec
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500_regs.h
@@ -0,0 +1,79 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/vt8500_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_VT8500_REGS_H
+#define __ASM_ARM_ARCH_VT8500_REGS_H
+
+/* VT8500 Registers Map */
+
+#define VT8500_REGS_START_PHYS	0xd8000000	/* Start of MMIO registers */
+#define VT8500_REGS_START_VIRT	0xf8000000	/* Virtual mapping start */
+
+#define VT8500_DDR_BASE		0xd8000000	/* 1k	DDR/DDR2 Memory
+							Controller */
+#define VT8500_DMA_BASE		0xd8001000	/* 1k	DMA Controller */
+#define VT8500_SFLASH_BASE	0xd8002000	/* 1k	Serial Flash Memory
+							Controller */
+#define VT8500_ETHER_BASE	0xd8004000	/* 1k	Ethernet MAC 0 */
+#define VT8500_CIPHER_BASE	0xd8006000	/* 4k	Cipher */
+#define VT8500_USB_BASE		0xd8007800	/* 2k	USB OTG */
+# define VT8500_EHCI_BASE	0xd8007900	/*	EHCI */
+# define VT8500_UHCI_BASE	0xd8007b01	/*	UHCI */
+#define VT8500_PATA_BASE	0xd8008000	/* 512	PATA */
+#define VT8500_PS2_BASE		0xd8008800	/* 1k	PS/2 */
+#define VT8500_NAND_BASE	0xd8009000	/* 1k	NAND Controller */
+#define VT8500_NOR_BASE		0xd8009400	/* 1k	NOR Controller */
+#define VT8500_SDMMC_BASE	0xd800a000	/* 1k	SD/MMC Controller */
+#define VT8500_MS_BASE		0xd800b000	/* 1k	MS/MSPRO Controller */
+#define VT8500_LCDC_BASE	0xd800e400	/* 1k	LCD Controller */
+#define VT8500_VPU_BASE		0xd8050000	/* 256	VPU */
+#define VT8500_GOV_BASE		0xd8050300	/* 256	GOV */
+#define VT8500_GEGEA_BASE	0xd8050400	/* 768	GE/GE Alpha Mixing */
+#define VT8500_LCDF_BASE	0xd8050900	/* 256	LCD Formatter */
+#define VT8500_VID_BASE		0xd8050a00	/* 256	VID */
+#define VT8500_VPP_BASE		0xd8050b00	/* 256	VPP */
+#define VT8500_TSBK_BASE	0xd80f4000	/* 4k	TSBK */
+#define VT8500_JPEGDEC_BASE	0xd80fe000	/* 4k	JPEG Decoder */
+#define VT8500_JPEGENC_BASE	0xd80ff000	/* 4k	JPEG Encoder */
+#define VT8500_RTC_BASE		0xd8100000	/* 64k	RTC */
+#define VT8500_GPIO_BASE	0xd8110000	/* 64k	GPIO Configuration */
+#define VT8500_SCC_BASE		0xd8120000	/* 64k	System Configuration*/
+#define VT8500_PMC_BASE		0xd8130000	/* 64k	PMC Configuration */
+#define VT8500_IC_BASE		0xd8140000	/* 64k	Interrupt Controller*/
+#define VT8500_UART0_BASE	0xd8200000	/* 64k	UART 0 */
+#define VT8500_UART2_BASE	0xd8210000	/* 64k	UART 2 */
+#define VT8500_PWM_BASE		0xd8220000	/* 64k	PWM Configuration */
+#define VT8500_SPI0_BASE	0xd8240000	/* 64k	SPI 0 */
+#define VT8500_SPI1_BASE	0xd8250000	/* 64k	SPI 1 */
+#define VT8500_CIR_BASE		0xd8270000	/* 64k	CIR */
+#define VT8500_I2C0_BASE	0xd8280000	/* 64k	I2C 0 */
+#define VT8500_AC97_BASE	0xd8290000	/* 64k	AC97 */
+#define VT8500_SPI2_BASE	0xd82a0000	/* 64k	SPI 2 */
+#define VT8500_UART1_BASE	0xd82b0000	/* 64k	UART 1 */
+#define VT8500_UART3_BASE	0xd82c0000	/* 64k	UART 3 */
+#define VT8500_PCM_BASE		0xd82d0000	/* 64k	PCM */
+#define VT8500_I2C1_BASE	0xd8320000	/* 64k	I2C 1 */
+#define VT8500_I2S_BASE		0xd8330000	/* 64k	I2S */
+#define VT8500_ADC_BASE		0xd8340000	/* 64k	ADC */
+
+#define VT8500_REGS_END_PHYS	0xd834ffff	/* End of MMIO registers */
+#define VT8500_REGS_LENGTH	(VT8500_REGS_END_PHYS \
+				- VT8500_REGS_START_PHYS + 1)
+
+#endif
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500fb.h b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
new file mode 100644
index 0000000..7f399c3
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/vt8500fb.h
@@ -0,0 +1,31 @@
+/*
+ *  VT8500/WM8505 Frame Buffer platform data definitions
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _VT8500FB_H
+#define _VT8500FB_H
+
+#include <linux/fb.h>
+
+struct vt8500fb_platform_data {
+	struct fb_videomode	mode;
+	u32			xres_virtual;
+	u32			yres_virtual;
+	u32			bpp;
+	unsigned long		video_mem_phys;
+	void			*video_mem_virt;
+	unsigned long		video_mem_len;
+};
+
+#endif /* _VT8500FB_H */
diff --git a/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h b/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
new file mode 100644
index 0000000..6128627
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
@@ -0,0 +1,115 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* WM8505 Interrupt Sources */
+
+#define IRQ_UHCI	0	/* UHC FS (UHCI?) */
+#define IRQ_EHCI	1	/* UHC HS */
+#define IRQ_UDCDMA	2	/* UDC DMA */
+				/* Reserved */
+#define IRQ_PS2MOUSE	4	/* PS/2 Mouse */
+#define IRQ_UDC		5	/* UDC */
+#define IRQ_EXT0	6	/* External Interrupt 0 */
+#define IRQ_EXT1	7	/* External Interrupt 1 */
+#define IRQ_KEYPAD	8	/* Keypad */
+#define IRQ_DMA		9	/* DMA Controller */
+#define IRQ_ETHER	10	/* Ethernet MAC */
+				/* Reserved */
+				/* Reserved */
+#define IRQ_EXT2	13	/* External Interrupt 2 */
+#define IRQ_EXT3	14	/* External Interrupt 3 */
+#define IRQ_EXT4	15	/* External Interrupt 4 */
+#define IRQ_APB		16	/* APB Bridge */
+#define IRQ_DMA0	17	/* DMA Channel 0 */
+#define IRQ_I2C1	18	/* I2C 1 */
+#define IRQ_I2C0	19	/* I2C 0 */
+#define IRQ_SDMMC	20	/* SD/MMC Controller */
+#define IRQ_SDMMC_DMA	21	/* SD/MMC Controller DMA */
+#define IRQ_PMC_WU	22	/* Power Management Controller Wakeup */
+#define IRQ_PS2KBD	23	/* PS/2 Keyboard */
+#define IRQ_SPI0	24	/* SPI 0 */
+#define IRQ_SPI1	25	/* SPI 1 */
+#define IRQ_SPI2	26	/* SPI 2 */
+#define IRQ_DMA1	27	/* DMA Channel 1 */
+#define IRQ_NAND	28	/* NAND Flash Controller */
+#define IRQ_NAND_DMA	29	/* NAND Flash Controller DMA */
+#define IRQ_UART5	30	/* UART 5 */
+#define IRQ_UART4	31	/* UART 4 */
+#define IRQ_UART0	32	/* UART 0 */
+#define IRQ_UART1	33	/* UART 1 */
+#define IRQ_DMA2	34	/* DMA Channel 2 */
+#define IRQ_I2S		35	/* I2S */
+#define IRQ_PMCOS0	36	/* PMC OS Timer 0 */
+#define IRQ_PMCOS1	37	/* PMC OS Timer 1 */
+#define IRQ_PMCOS2	38	/* PMC OS Timer 2 */
+#define IRQ_PMCOS3	39	/* PMC OS Timer 3 */
+#define IRQ_DMA3	40	/* DMA Channel 3 */
+#define IRQ_DMA4	41	/* DMA Channel 4 */
+#define IRQ_AC97	42	/* AC97 Interface */
+				/* Reserved */
+#define IRQ_NOR		44	/* NOR Flash Controller */
+#define IRQ_DMA5	45	/* DMA Channel 5 */
+#define IRQ_DMA6	46	/* DMA Channel 6 */
+#define IRQ_UART2	47	/* UART 2 */
+#define IRQ_RTC		48	/* RTC Interrupt */
+#define IRQ_RTCSM	49	/* RTC Second/Minute Update Interrupt */
+#define IRQ_UART3	50	/* UART 3 */
+#define IRQ_DMA7	51	/* DMA Channel 7 */
+#define IRQ_EXT5	52	/* External Interrupt 5 */
+#define IRQ_EXT6	53	/* External Interrupt 6 */
+#define IRQ_EXT7	54	/* External Interrupt 7 */
+#define IRQ_CIR		55	/* CIR */
+#define IRQ_SIC0	56	/* SIC IRQ0 */
+#define IRQ_SIC1	57	/* SIC IRQ1 */
+#define IRQ_SIC2	58	/* SIC IRQ2 */
+#define IRQ_SIC3	59	/* SIC IRQ3 */
+#define IRQ_SIC4	60	/* SIC IRQ4 */
+#define IRQ_SIC5	61	/* SIC IRQ5 */
+#define IRQ_SIC6	62	/* SIC IRQ6 */
+#define IRQ_SIC7	63	/* SIC IRQ7 */
+				/* Reserved */
+#define IRQ_JPEGDEC	65	/* JPEG Decoder */
+#define IRQ_SAE		66	/* SAE (?) */
+				/* Reserved */
+#define IRQ_VPU		79	/* Video Processing Unit */
+#define IRQ_VPP		80	/* Video Post-Processor */
+#define IRQ_VID		81	/* Video Digital Input Interface */
+#define IRQ_SPU		82	/* SPU (?) */
+#define IRQ_PIP		83	/* PIP Error */
+#define IRQ_GE		84	/* Graphic Engine */
+#define IRQ_GOV		85	/* Graphic Overlay Engine */
+#define IRQ_DVO		86	/* Digital Video Output */
+				/* Reserved */
+#define IRQ_DMA8	92	/* DMA Channel 8 */
+#define IRQ_DMA9	93	/* DMA Channel 9 */
+#define IRQ_DMA10	94	/* DMA Channel 10 */
+#define IRQ_DMA11	95	/* DMA Channel 11 */
+#define IRQ_DMA12	96	/* DMA Channel 12 */
+#define IRQ_DMA13	97	/* DMA Channel 13 */
+#define IRQ_DMA14	98	/* DMA Channel 14 */
+#define IRQ_DMA15	99	/* DMA Channel 15 */
+				/* Reserved */
+#define IRQ_GOVW	111	/* GOVW (?) */
+#define IRQ_GOVRSDSCD	112	/* GOVR SDSCD (?) */
+#define IRQ_GOVRSDMIF	113	/* GOVR SDMIF (?) */
+#define IRQ_GOVRHDSCD	114	/* GOVR HDSCD (?) */
+#define IRQ_GOVRHDMIF	115	/* GOVR HDMIF (?) */
+
+#define WM8505_NR_IRQS		116
diff --git a/arch/arm/mach-vt8500/include/mach/wm8505_regs.h b/arch/arm/mach-vt8500/include/mach/wm8505_regs.h
new file mode 100644
index 0000000..df15509
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/wm8505_regs.h
@@ -0,0 +1,78 @@
+/*
+ *  arch/arm/mach-vt8500/include/mach/wm8505_regs.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_WM8505_REGS_H
+#define __ASM_ARM_ARCH_WM8505_REGS_H
+
+/* WM8505 Registers Map */
+
+#define WM8505_REGS_START_PHYS	0xd8000000	/* Start of MMIO registers */
+#define WM8505_REGS_START_VIRT	0xf8000000	/* Virtual mapping start */
+
+#define WM8505_DDR_BASE		0xd8000400	/* 1k	DDR/DDR2 Memory
+							Controller */
+#define WM8505_DMA_BASE		0xd8001800	/* 1k	DMA Controller */
+#define WM8505_VDMA_BASE	0xd8001c00	/* 1k	VDMA */
+#define WM8505_SFLASH_BASE	0xd8002000	/* 1k	Serial Flash Memory
+							Controller */
+#define WM8505_ETHER_BASE	0xd8004000	/* 1k	Ethernet MAC 0 */
+#define WM8505_CIPHER_BASE	0xd8006000	/* 4k	Cipher */
+#define WM8505_USB_BASE		0xd8007000	/* 2k	USB 2.0 Host */
+# define WM8505_EHCI_BASE	0xd8007100	/*	EHCI */
+# define WM8505_UHCI_BASE	0xd8007301	/*	UHCI */
+#define WM8505_PS2_BASE		0xd8008800	/* 1k	PS/2 */
+#define WM8505_NAND_BASE	0xd8009000	/* 1k	NAND Controller */
+#define WM8505_NOR_BASE		0xd8009400	/* 1k	NOR Controller */
+#define WM8505_SDMMC_BASE	0xd800a000	/* 1k	SD/MMC Controller */
+#define WM8505_VPU_BASE		0xd8050000	/* 256	VPU */
+#define WM8505_GOV_BASE		0xd8050300	/* 256	GOV */
+#define WM8505_GEGEA_BASE	0xd8050400	/* 768	GE/GE Alpha Mixing */
+#define WM8505_GOVR_BASE	0xd8050800	/* 512	GOVR (frambuffer) */
+#define WM8505_VID_BASE		0xd8050a00	/* 256	VID */
+#define WM8505_SCL_BASE		0xd8050d00	/* 256	SCL */
+#define WM8505_VPP_BASE		0xd8050f00	/* 256	VPP */
+#define WM8505_JPEGDEC_BASE	0xd80fe000	/* 4k	JPEG Decoder */
+#define WM8505_RTC_BASE		0xd8100000	/* 64k	RTC */
+#define WM8505_GPIO_BASE	0xd8110000	/* 64k	GPIO Configuration */
+#define WM8505_SCC_BASE		0xd8120000	/* 64k	System Configuration*/
+#define WM8505_PMC_BASE		0xd8130000	/* 64k	PMC Configuration */
+#define WM8505_IC_BASE		0xd8140000	/* 64k	Interrupt Controller*/
+#define WM8505_SIC_BASE		0xd8150000	/* 64k	Secondary IC */
+#define WM8505_UART0_BASE	0xd8200000	/* 64k	UART 0 */
+#define WM8505_UART2_BASE	0xd8210000	/* 64k	UART 2 */
+#define WM8505_PWM_BASE		0xd8220000	/* 64k	PWM Configuration */
+#define WM8505_SPI0_BASE	0xd8240000	/* 64k	SPI 0 */
+#define WM8505_SPI1_BASE	0xd8250000	/* 64k	SPI 1 */
+#define WM8505_KEYPAD_BASE	0xd8260000	/* 64k	Keypad control */
+#define WM8505_CIR_BASE		0xd8270000	/* 64k	CIR */
+#define WM8505_I2C0_BASE	0xd8280000	/* 64k	I2C 0 */
+#define WM8505_AC97_BASE	0xd8290000	/* 64k	AC97 */
+#define WM8505_SPI2_BASE	0xd82a0000	/* 64k	SPI 2 */
+#define WM8505_UART1_BASE	0xd82b0000	/* 64k	UART 1 */
+#define WM8505_UART3_BASE	0xd82c0000	/* 64k	UART 3 */
+#define WM8505_I2C1_BASE	0xd8320000	/* 64k	I2C 1 */
+#define WM8505_I2S_BASE		0xd8330000	/* 64k	I2S */
+#define WM8505_UART4_BASE	0xd8370000	/* 64k	UART 4 */
+#define WM8505_UART5_BASE	0xd8380000	/* 64k	UART 5 */
+
+#define WM8505_REGS_END_PHYS	0xd838ffff	/* End of MMIO registers */
+#define WM8505_REGS_LENGTH	(WM8505_REGS_END_PHYS \
+				- WM8505_REGS_START_PHYS + 1)
+
+#endif
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
new file mode 100644
index 0000000..5f4ddde
--- /dev/null
+++ b/arch/arm/mach-vt8500/irq.c
@@ -0,0 +1,177 @@
+/*
+ *  arch/arm/mach-vt8500/irq.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include "devices.h"
+
+#define VT8500_IC_DCTR		0x40		/* Destination control
+						register, 64*u8 */
+#define VT8500_INT_ENABLE	(1 << 3)
+#define VT8500_TRIGGER_HIGH	(0 << 4)
+#define VT8500_TRIGGER_RISING	(1 << 4)
+#define VT8500_TRIGGER_FALLING	(2 << 4)
+#define VT8500_EDGE		( VT8500_TRIGGER_RISING \
+				| VT8500_TRIGGER_FALLING)
+#define VT8500_IC_STATUS	0x80		/* Interrupt status, 2*u32 */
+
+static void __iomem *ic_regbase;
+static void __iomem *sic_regbase;
+
+static void vt8500_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 edge;
+
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	edge = readb(base + VT8500_IC_DCTR + irq) & VT8500_EDGE;
+	if (edge) {
+		void __iomem *stat_reg = base + VT8500_IC_STATUS
+						+ (irq < 32 ? 0 : 4);
+		unsigned status = readl(stat_reg);
+
+		status |= (1 << (irq & 0x1f));
+		writel(status, stat_reg);
+	} else {
+		u8 dctr = readb(base + VT8500_IC_DCTR + irq);
+
+		dctr &= ~VT8500_INT_ENABLE;
+		writeb(dctr, base + VT8500_IC_DCTR + irq);
+	}
+}
+
+static void vt8500_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ic_regbase;
+	u8 dctr;
+
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+	dctr = readb(base + VT8500_IC_DCTR + irq);
+	dctr |= VT8500_INT_ENABLE;
+	writeb(dctr, base + VT8500_IC_DCTR + irq);
+}
+
+static int vt8500_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+	void __iomem *base = ic_regbase;
+	unsigned int orig_irq = irq;
+	u8 dctr;
+
+	if (irq >= 64) {
+		base = sic_regbase;
+		irq -= 64;
+	}
+
+	dctr = readb(base + VT8500_IC_DCTR + irq);
+	dctr &= ~VT8500_EDGE;
+
+	switch (flow_type) {
+	case IRQF_TRIGGER_LOW:
+		return -EINVAL;
+	case IRQF_TRIGGER_HIGH:
+		dctr |= VT8500_TRIGGER_HIGH;
+		irq_desc[orig_irq].handle_irq = handle_level_irq;
+		break;
+	case IRQF_TRIGGER_FALLING:
+		dctr |= VT8500_TRIGGER_FALLING;
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	case IRQF_TRIGGER_RISING:
+		dctr |= VT8500_TRIGGER_RISING;
+		irq_desc[orig_irq].handle_irq = handle_edge_irq;
+		break;
+	}
+	writeb(dctr, base + VT8500_IC_DCTR + irq);
+
+	return 0;
+}
+
+static struct irq_chip vt8500_irq_chip = {
+	.name      = "vt8500",
+	.ack       = vt8500_irq_mask,
+	.mask      = vt8500_irq_mask,
+	.unmask    = vt8500_irq_unmask,
+	.set_type  = vt8500_irq_set_type,
+};
+
+void __init vt8500_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_ic_base, SZ_64K);
+
+	if (ic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+
+		for (i = 0; i < wmt_nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n");
+	}
+}
+
+void __init wm8505_init_irq(void)
+{
+	unsigned int i;
+
+	ic_regbase = ioremap(wmt_ic_base, SZ_64K);
+	sic_regbase = ioremap(wmt_sic_base, SZ_64K);
+
+	if (ic_regbase && sic_regbase) {
+		/* Enable rotating priority for IRQ */
+		writel((1 << 6), ic_regbase + 0x20);
+		writel(0, ic_regbase + 0x24);
+		writel((1 << 6), sic_regbase + 0x20);
+		writel(0, sic_regbase + 0x24);
+
+		for (i = 0; i < wmt_nr_irqs; i++) {
+			/* Disable all interrupts and route them to IRQ */
+			if (i < 64)
+				writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+			else
+				writeb(0x00, sic_regbase + VT8500_IC_DCTR
+								+ i - 64);
+
+			set_irq_chip(i, &vt8500_irq_chip);
+			set_irq_handler(i, handle_level_irq);
+			set_irq_flags(i, IRQF_VALID);
+		}
+	} else {
+		printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n");
+	}
+}
diff --git a/arch/arm/mach-vt8500/pwm.c b/arch/arm/mach-vt8500/pwm.c
new file mode 100644
index 0000000..8ad825e
--- /dev/null
+++ b/arch/arm/mach-vt8500/pwm.c
@@ -0,0 +1,265 @@
+/*
+ * arch/arm/mach-vt8500/pwm.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device {
+	struct list_head	node;
+	struct platform_device	*pdev;
+
+	const char	*label;
+
+	void __iomem	*regbase;
+
+	unsigned int	use_count;
+	unsigned int	pwm_id;
+};
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+	int loops = msecs_to_loops(10);
+	while ((readb(reg) & bitmask) && --loops)
+		cpu_relax();
+
+	if (unlikely(!loops))
+		pr_warning("Waiting for status bits 0x%x to clear timed out\n",
+			   bitmask);
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	unsigned long long c;
+	unsigned long period_cycles, prescale, pv, dc;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	c = 25000000/2; /* wild guess --- need to implement clocks */
+	c = c * period_ns;
+	do_div(c, 1000000000);
+	period_cycles = c;
+
+	if (period_cycles < 1)
+		period_cycles = 1;
+	prescale = (period_cycles - 1) / 4096;
+	pv = period_cycles / (prescale + 1) - 1;
+	if (pv > 4095)
+		pv = 4095;
+
+	if (prescale > 1023)
+		return -EINVAL;
+
+	c = (unsigned long long)pv * duty_ns;
+	do_div(c, period_ns);
+	dc = c;
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 1));
+	writel(prescale, pwm->regbase + 0x4 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 2));
+	writel(pv, pwm->regbase + 0x8 + (pwm->pwm_id << 4));
+
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 3));
+	writel(dc, pwm->regbase + 0xc + (pwm->pwm_id << 4));
+
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(5, pwm->regbase + (pwm->pwm_id << 4));
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm_busy_wait(pwm->regbase + 0x40 + pwm->pwm_id, (1 << 0));
+	writel(0, pwm->regbase + (pwm->pwm_id << 4));
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	int found = 0;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else {
+			pwm = ERR_PTR(-EBUSY);
+		}
+	} else {
+		pwm = ERR_PTR(-ENOENT);
+	}
+
+	mutex_unlock(&pwm_lock);
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+
+	if (pwm->use_count) {
+		pwm->use_count--;
+		pwm->label = NULL;
+	} else {
+		pr_warning("PWM device already freed\n");
+	}
+
+	mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static inline void __add_pwm(struct pwm_device *pwm)
+{
+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+}
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int ret = 0;
+	int i;
+
+	pwms = kzalloc(sizeof(struct pwm_device) * VT8500_NR_PWMS, GFP_KERNEL);
+	if (pwms == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < VT8500_NR_PWMS; i++) {
+		pwms[i].use_count = 0;
+		pwms[i].pwm_id = i;
+		pwms[i].pdev = pdev;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	pwms[0].regbase = ioremap(r->start, resource_size(r));
+	if (pwms[0].regbase == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	for (i = 1; i < VT8500_NR_PWMS; i++)
+		pwms[i].regbase = pwms[0].regbase;
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		__add_pwm(&pwms[i]);
+
+	platform_set_drvdata(pdev, pwms);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free:
+	kfree(pwms);
+	return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwms;
+	struct resource *r;
+	int i;
+
+	pwms = platform_get_drvdata(pdev);
+	if (pwms == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm_lock);
+
+	for (i = 0; i < VT8500_NR_PWMS; i++)
+		list_del(&pwms[i].node);
+	mutex_unlock(&pwm_lock);
+
+	iounmap(pwms[0].regbase);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+
+	kfree(pwms);
+	return 0;
+}
+
+static struct platform_driver pwm_driver = {
+	.driver		= {
+		.name	= "vt8500-pwm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pwm_probe,
+	.remove		= __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+	return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+	platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
new file mode 100644
index 0000000..d5376c5
--- /dev/null
+++ b/arch/arm/mach-vt8500/timer.c
@@ -0,0 +1,155 @@
+/*
+ *  arch/arm/mach-vt8500/timer.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/delay.h>
+
+#include <asm/mach/time.h>
+
+#include "devices.h"
+
+#define VT8500_TIMER_OFFSET	0x0100
+#define TIMER_MATCH_VAL		0x0000
+#define TIMER_COUNT_VAL		0x0010
+#define TIMER_STATUS_VAL	0x0014
+#define TIMER_IER_VAL		0x001c		/* interrupt enable */
+#define TIMER_CTRL_VAL		0x0020
+#define TIMER_AS_VAL		0x0024		/* access status */
+#define TIMER_COUNT_R_ACTIVE	(1 << 5)	/* not ready for read */
+#define TIMER_COUNT_W_ACTIVE	(1 << 4)	/* not ready for write */
+#define TIMER_MATCH_W_ACTIVE	(1 << 0)	/* not ready for write */
+#define VT8500_TIMER_HZ		3000000
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+
+static void __iomem *regbase;
+
+static cycle_t vt8500_timer_read(struct clocksource *cs)
+{
+	int loops = msecs_to_loops(10);
+	writel(3, regbase + TIMER_CTRL_VAL);
+	while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	return readl(regbase + TIMER_COUNT_VAL);
+}
+
+struct clocksource clocksource = {
+	.name           = "vt8500_timer",
+	.rating         = 200,
+	.read           = vt8500_timer_read,
+	.mask           = CLOCKSOURCE_MASK(32),
+	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int vt8500_timer_set_next_event(unsigned long cycles,
+				    struct clock_event_device *evt)
+{
+	int loops = msecs_to_loops(10);
+	cycle_t alarm = clocksource.read(&clocksource) + cycles;
+	while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
+						&& --loops)
+		cpu_relax();
+	writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
+
+	if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+		return -ETIME;
+
+	writel(1, regbase + TIMER_IER_VAL);
+
+	return 0;
+}
+
+static void vt8500_timer_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_RESUME:
+	case CLOCK_EVT_MODE_PERIODIC:
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		writel(readl(regbase + TIMER_CTRL_VAL) | 1,
+			regbase + TIMER_CTRL_VAL);
+		writel(0, regbase + TIMER_IER_VAL);
+		break;
+	}
+}
+
+struct clock_event_device clockevent = {
+	.name           = "vt8500_timer",
+	.features       = CLOCK_EVT_FEAT_ONESHOT,
+	.rating         = 200,
+	.set_next_event = vt8500_timer_set_next_event,
+	.set_mode       = vt8500_timer_set_mode,
+};
+
+static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+struct irqaction irq = {
+	.name    = "vt8500_timer",
+	.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler = vt8500_timer_interrupt,
+	.dev_id  = &clockevent,
+};
+
+static void __init vt8500_timer_init(void)
+{
+	regbase = ioremap(wmt_pmc_base + VT8500_TIMER_OFFSET, 0x28);
+	if (!regbase)
+		printk(KERN_ERR "vt8500_timer_init: failed to map MMIO registers\n");
+
+	writel(1, regbase + TIMER_CTRL_VAL);
+	writel(0xf, regbase + TIMER_STATUS_VAL);
+	writel(~0, regbase + TIMER_MATCH_VAL);
+
+	if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
+		printk(KERN_ERR "vt8500_timer_init: clocksource_register failed for %s\n",
+					clocksource.name);
+
+	clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
+
+	/* copy-pasted from mach-msm; no idea */
+	clockevent.max_delta_ns =
+		clockevent_delta2ns(0xf0000000, &clockevent);
+	clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
+	clockevent.cpumask = cpumask_of(0);
+
+	if (setup_irq(wmt_timer_irq, &irq))
+		printk(KERN_ERR "vt8500_timer_init: setup_irq failed for %s\n",
+					clockevent.name);
+	clockevents_register_device(&clockevent);
+}
+
+struct sys_timer vt8500_timer = {
+	.init = vt8500_timer_init
+};
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
new file mode 100644
index 0000000..e73aadb
--- /dev/null
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -0,0 +1,77 @@
+/*
+ *  arch/arm/mach-vt8500/wm8505_7in.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "devices.h"
+
+static void __iomem *pmc_hiber;
+
+static struct platform_device *devices[] __initdata = {
+	&vt8500_device_uart0,
+	&vt8500_device_ehci,
+	&vt8500_device_wm8505_fb,
+	&vt8500_device_ge_rops,
+	&vt8500_device_pwm,
+	&vt8500_device_pwmbl,
+	&vt8500_device_rtc,
+};
+
+static void vt8500_power_off(void)
+{
+	local_irq_disable();
+	writew(5, pmc_hiber);
+	asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init wm8505_7in_init(void)
+{
+#ifdef CONFIG_FB_WM8505
+	void __iomem *gpio_mux_reg = ioremap(wmt_gpio_base + 0x200, 4);
+	if (gpio_mux_reg) {
+		writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
+		iounmap(gpio_mux_reg);
+	} else {
+		printk(KERN_ERR "Could not remap the GPIO mux register, display may not work properly!\n");
+	}
+#endif
+	pmc_hiber = ioremap(wmt_pmc_base + 0x12, 2);
+	if (pmc_hiber)
+		pm_power_off = &vt8500_power_off;
+	else
+		printk(KERN_ERR "PMC Hibernation register could not be remapped, not enabling power off!\n");
+
+	wm8505_set_resources();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+	vt8500_gpio_init();
+}
+
+MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
+	.boot_params	= 0x00000100,
+	.reserve	= wm8505_reserve_mem,
+	.map_io		= wm8505_map_io,
+	.init_irq	= wm8505_init_irq,
+	.timer		= &vt8500_timer,
+	.init_machine	= wm8505_7in_init,
+MACHINE_END
-- 
1.7.3.4


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

* Re: [PATCH 1/6 v12] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-22 23:38                                             ` [PATCH 1/6 v12] " Alexey Charkov
@ 2010-12-28 14:52                                               ` Alexey Charkov
  2011-01-15  0:53                                                 ` Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2010-12-28 14:52 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, Ryan Mallon, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

2010/12/23 Alexey Charkov <alchark@gmail.com>:
> This adds support for the family of Systems-on-Chip produced initially
> by VIA and now its subsidiary WonderMedia that have recently become
> widespread in lower-end Chinese ARM-based tablets and netbooks.
>
> Support is included for both VT8500 and WM8505, selectable by a
> configuration switch at kernel build time.
>
> Included are basic machine initialization files, register and
> interrupt definitions, support for the on-chip interrupt controller,
> high-precision OS timer, GPIO lines, necessary macros for early debug,
> pulse-width-modulated outputs control, as well as platform device
> configurations for the specific drivers implemented elsewhere.
>
> Signed-off-by: Alexey Charkov <alchark@gmail.com>

Russell, will you take this for .38? Please note that the last
revision of this patch was also submitted to the ARM Linux patch
tracking system:

http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6597/1

Best regards,
Alexey

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

* Re: [PATCH 1/6 v12] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-28 14:52                                               ` Alexey Charkov
@ 2011-01-15  0:53                                                 ` Alexey Charkov
  2011-01-15  0:58                                                   ` Russell King - ARM Linux
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2011-01-15  0:53 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Alexey Charkov, Ryan Mallon, linux-arm-kernel,
	vt8500-wm8505-linux-kernel, Eric Miao, Uwe Kleine-König,
	Albin Tonnerre, linux-kernel

2010/12/28 Alexey Charkov <alchark@gmail.com>:
> 2010/12/23 Alexey Charkov <alchark@gmail.com>:
>> This adds support for the family of Systems-on-Chip produced initially
>> by VIA and now its subsidiary WonderMedia that have recently become
>> widespread in lower-end Chinese ARM-based tablets and netbooks.
>>
>> Support is included for both VT8500 and WM8505, selectable by a
>> configuration switch at kernel build time.
>>
>> Included are basic machine initialization files, register and
>> interrupt definitions, support for the on-chip interrupt controller,
>> high-precision OS timer, GPIO lines, necessary macros for early debug,
>> pulse-width-modulated outputs control, as well as platform device
>> configurations for the specific drivers implemented elsewhere.
>>
>> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>
> Russell, will you take this for .38? Please note that the last
> revision of this patch was also submitted to the ARM Linux patch
> tracking system:
>
> http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6597/1
>
> Best regards,
> Alexey
>

Russell, I realize that there are quite a lot of other things to worry
about currently in the ARM Linux world, but could you please still
consider merging this patch before .38 window closes?

It has been (at different stages):
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Ryan Mallon <ryan@bluewatersys.com>

There's also a patch to the i8042 input controller that Dmitry
Torokhov wished to be merged together with this code. That one is:
Acked-by: Dmitry Torokhov <dtor@mail.ru>
and available at:
http://lists.arm.linux.org.uk/lurker/message/20101222.214141.c745816a.en.html

Best regards,
Alexey

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

* Re: [PATCH 1/6 v12] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2011-01-15  0:53                                                 ` Alexey Charkov
@ 2011-01-15  0:58                                                   ` Russell King - ARM Linux
  2011-01-15  1:13                                                     ` Alexey Charkov
  0 siblings, 1 reply; 91+ messages in thread
From: Russell King - ARM Linux @ 2011-01-15  0:58 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: Ryan Mallon, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Sat, Jan 15, 2011 at 03:53:56AM +0300, Alexey Charkov wrote:
> 2010/12/28 Alexey Charkov <alchark@gmail.com>:
> > 2010/12/23 Alexey Charkov <alchark@gmail.com>:
> >> This adds support for the family of Systems-on-Chip produced initially
> >> by VIA and now its subsidiary WonderMedia that have recently become
> >> widespread in lower-end Chinese ARM-based tablets and netbooks.
> >>
> >> Support is included for both VT8500 and WM8505, selectable by a
> >> configuration switch at kernel build time.
> >>
> >> Included are basic machine initialization files, register and
> >> interrupt definitions, support for the on-chip interrupt controller,
> >> high-precision OS timer, GPIO lines, necessary macros for early debug,
> >> pulse-width-modulated outputs control, as well as platform device
> >> configurations for the specific drivers implemented elsewhere.
> >>
> >> Signed-off-by: Alexey Charkov <alchark@gmail.com>
> >
> > Russell, will you take this for .38? Please note that the last
> > revision of this patch was also submitted to the ARM Linux patch
> > tracking system:
> >
> > http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6597/1
> >
> > Best regards,
> > Alexey
> >
> 
> Russell, I realize that there are quite a lot of other things to worry
> about currently in the ARM Linux world, but could you please still
> consider merging this patch before .38 window closes?

It got lost in the patch system in the middle of the SPEAR deluge -
and after that deluge I did say on list that it would result in things
being missed.  I've basically been ignoring everything in there since
beyond the top few newest entries.

In any case, I believe it's too late in this merge window with only
four days left to start merging large amounts of code.  I'd get
moaned at by Linus.  Sorry.

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

* Re: [PATCH 1/6 v12] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2011-01-15  0:58                                                   ` Russell King - ARM Linux
@ 2011-01-15  1:13                                                     ` Alexey Charkov
  2011-01-21 10:43                                                       ` Russell King - ARM Linux
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2011-01-15  1:13 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Ryan Mallon, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

<previous response went off-list by mistake, sorry for that>

2011/1/15 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Sat, Jan 15, 2011 at 03:53:56AM +0300, Alexey Charkov wrote:
>> 2010/12/28 Alexey Charkov <alchark@gmail.com>:
>> > 2010/12/23 Alexey Charkov <alchark@gmail.com>:
>> >> This adds support for the family of Systems-on-Chip produced initially
>> >> by VIA and now its subsidiary WonderMedia that have recently become
>> >> widespread in lower-end Chinese ARM-based tablets and netbooks.
>> >>
>> >> Support is included for both VT8500 and WM8505, selectable by a
>> >> configuration switch at kernel build time.
>> >>
>> >> Included are basic machine initialization files, register and
>> >> interrupt definitions, support for the on-chip interrupt controller,
>> >> high-precision OS timer, GPIO lines, necessary macros for early debug,
>> >> pulse-width-modulated outputs control, as well as platform device
>> >> configurations for the specific drivers implemented elsewhere.
>> >>
>> >> Signed-off-by: Alexey Charkov <alchark@gmail.com>
>> >
>> > Russell, will you take this for .38? Please note that the last
>> > revision of this patch was also submitted to the ARM Linux patch
>> > tracking system:
>> >
>> > http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6597/1
>> >
>> > Best regards,
>> > Alexey
>> >
>>
>> Russell, I realize that there are quite a lot of other things to worry
>> about currently in the ARM Linux world, but could you please still
>> consider merging this patch before .38 window closes?
>
> It got lost in the patch system in the middle of the SPEAR deluge -
> and after that deluge I did say on list that it would result in things
> being missed.  I've basically been ignoring everything in there since
> beyond the top few newest entries.
>
> In any case, I believe it's too late in this merge window with only
> four days left to start merging large amounts of code.  I'd get
> moaned at by Linus.  Sorry.
>

Ok, life is life. Could you then please take these to your tree for .39?

Best regards,
Alexey

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

* Re: [PATCH 1/6 v12] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2011-01-15  1:13                                                     ` Alexey Charkov
@ 2011-01-21 10:43                                                       ` Russell King - ARM Linux
  0 siblings, 0 replies; 91+ messages in thread
From: Russell King - ARM Linux @ 2011-01-21 10:43 UTC (permalink / raw)
  To: Alexey Charkov, Lennert Buytenhek
  Cc: Ryan Mallon, linux-arm-kernel, vt8500-wm8505-linux-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Sat, Jan 15, 2011 at 04:13:48AM +0300, Alexey Charkov wrote:
> Ok, life is life. Could you then please take these to your tree for .39?

Taken.  However, Lennert has been converting ARM to use the new irq_*
irq_chip methods, which your code has missed out on.  Any chance of a
follow-up patch to switch it over?

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

* Re: [PATCH 1/6 v8] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2010-12-19 17:40             ` [PATCH 1/6 v8] " Alexey Charkov
  2010-12-20 18:15               ` Arnd Bergmann
  2010-12-20 19:15               ` Russell King - ARM Linux
@ 2011-07-06 12:34               ` Russell King - ARM Linux
  2011-07-07  7:13                 ` Alexey Charkov
  2 siblings, 1 reply; 91+ messages in thread
From: Russell King - ARM Linux @ 2011-07-06 12:34 UTC (permalink / raw)
  To: Alexey Charkov
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Sun, Dec 19, 2010 at 08:40:17PM +0300, Alexey Charkov wrote:
> +static struct map_desc vt8500_io_desc[] __initdata = {
> +	/* SoC MMIO registers, to be filled in later */
> +	[0] = {
> +		.type		= MT_DEVICE
> +	},
> +	/* PCI I/O space, numbers tied to those in <mach/io.h> */
> +	[1] = {
> +		.virtual	= 0xf0000000,
> +		.pfn		= __phys_to_pfn(0xc0000000),
> +		.length		= SZ_64K,
> +		.type		= MT_DEVICE
> +	},
> +};
...
> diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
> new file mode 100644
> index 0000000..8dd55c8
> --- /dev/null
> +++ b/arch/arm/mach-vt8500/include/mach/io.h
> +#ifndef __ASM_ARM_ARCH_IO_H
> +#define __ASM_ARM_ARCH_IO_H
> +
> +#define IO_SPACE_LIMIT 0xffff
> +
> +#define __io(a)		((void __iomem *)((a) + 0xf0000000))
> +#define __mem_pci(a)	(a)

Can you explain why you seem to have PCI IO space, yet don't have CONFIG_PCI
selected?  Do you have any drivers which use this space without CONFIG_PCI=y?

Thanks.

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

* Re: [PATCH 1/6 v8] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2011-07-06 12:34               ` [PATCH 1/6 v8] " Russell King - ARM Linux
@ 2011-07-07  7:13                 ` Alexey Charkov
  2011-07-07  7:54                   ` Arnd Bergmann
  0 siblings, 1 reply; 91+ messages in thread
From: Alexey Charkov @ 2011-07-07  7:13 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-arm-kernel, vt8500-wm8505-linux-kernel, Eric Miao,
	Uwe Kleine-König, Albin Tonnerre, linux-kernel

2011/7/6 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Sun, Dec 19, 2010 at 08:40:17PM +0300, Alexey Charkov wrote:
>> +static struct map_desc vt8500_io_desc[] __initdata = {
>> +     /* SoC MMIO registers, to be filled in later */
>> +     [0] = {
>> +             .type           = MT_DEVICE
>> +     },
>> +     /* PCI I/O space, numbers tied to those in <mach/io.h> */
>> +     [1] = {
>> +             .virtual        = 0xf0000000,
>> +             .pfn            = __phys_to_pfn(0xc0000000),
>> +             .length         = SZ_64K,
>> +             .type           = MT_DEVICE
>> +     },
>> +};
> ...
>> diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
>> new file mode 100644
>> index 0000000..8dd55c8
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/include/mach/io.h
>> +#ifndef __ASM_ARM_ARCH_IO_H
>> +#define __ASM_ARM_ARCH_IO_H
>> +
>> +#define IO_SPACE_LIMIT 0xffff
>> +
>> +#define __io(a)              ((void __iomem *)((a) + 0xf0000000))
>> +#define __mem_pci(a) (a)
>
> Can you explain why you seem to have PCI IO space, yet don't have CONFIG_PCI
> selected?  Do you have any drivers which use this space without CONFIG_PCI=y?

We have not yet created support for the PCI bridge (or even verified
its existence on the chip). These numbers are derived bit by bit from
vendor-published kernel sources and rather intended as a reference in
case somebody wishes to try and implement PCI support.

All the peripherals on these chips that I know of are statically
mapped platform devices programmed via MMIO registers. There may be
some boards which have support for external PCI devices, but I haven't
seen them.

Best,
Alexey

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

* Re: [PATCH 1/6 v8] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2011-07-07  7:13                 ` Alexey Charkov
@ 2011-07-07  7:54                   ` Arnd Bergmann
  2011-07-07  7:59                     ` Russell King - ARM Linux
  0 siblings, 1 reply; 91+ messages in thread
From: Arnd Bergmann @ 2011-07-07  7:54 UTC (permalink / raw)
  To: vt8500-wm8505-linux-kernel
  Cc: Alexey Charkov, Russell King - ARM Linux, linux-arm-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Thursday 07 July 2011 09:13:26 Alexey Charkov wrote:
> >> diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
> >> new file mode 100644
> >> index 0000000..8dd55c8
> >> --- /dev/null
> >> +++ b/arch/arm/mach-vt8500/include/mach/io.h
> >> +#ifndef __ASM_ARM_ARCH_IO_H
> >> +#define __ASM_ARM_ARCH_IO_H
> >> +
> >> +#define IO_SPACE_LIMIT 0xffff
> >> +
> >> +#define __io(a)              ((void __iomem *)((a) + 0xf0000000))
> >> +#define __mem_pci(a) (a)
> >
> > Can you explain why you seem to have PCI IO space, yet don't have CONFIG_PCI
> > selected?  Do you have any drivers which use this space without CONFIG_PCI=y?
> 
> We have not yet created support for the PCI bridge (or even verified
> its existence on the chip). These numbers are derived bit by bit from
> vendor-published kernel sources and rather intended as a reference in
> case somebody wishes to try and implement PCI support.
> 
> All the peripherals on these chips that I know of are statically
> mapped platform devices programmed via MMIO registers. There may be
> some boards which have support for external PCI devices, but I haven't
> seen them.

Actually, we did discuss this when I first reviewed your patches. FWIW,
WM8505, WM8510 and VT8500 don't seem to have PCI, but VT8430 has PCI,
according to the data sheet and the patch provided by VIA.

What made this a little hard to understand is the way that VIA provided
an emulated PCI bus even on those platforms that didn't have the hardware,
apparently because it didn't occur to them to add platform device
support to the network driver (via velocity, iirc). They just faked a
configuration space for the USB and network devices to make them work
with the regular PCI drivers.

	Arnd

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

* Re: [PATCH 1/6 v8] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2011-07-07  7:54                   ` Arnd Bergmann
@ 2011-07-07  7:59                     ` Russell King - ARM Linux
  2011-07-07  8:05                       ` Arnd Bergmann
  0 siblings, 1 reply; 91+ messages in thread
From: Russell King - ARM Linux @ 2011-07-07  7:59 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, linux-arm-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Thu, Jul 07, 2011 at 09:54:00AM +0200, Arnd Bergmann wrote:
> On Thursday 07 July 2011 09:13:26 Alexey Charkov wrote:
> > >> diff --git a/arch/arm/mach-vt8500/include/mach/io.h b/arch/arm/mach-vt8500/include/mach/io.h
> > >> new file mode 100644
> > >> index 0000000..8dd55c8
> > >> --- /dev/null
> > >> +++ b/arch/arm/mach-vt8500/include/mach/io.h
> > >> +#ifndef __ASM_ARM_ARCH_IO_H
> > >> +#define __ASM_ARM_ARCH_IO_H
> > >> +
> > >> +#define IO_SPACE_LIMIT 0xffff
> > >> +
> > >> +#define __io(a)              ((void __iomem *)((a) + 0xf0000000))
> > >> +#define __mem_pci(a) (a)
> > >
> > > Can you explain why you seem to have PCI IO space, yet don't have CONFIG_PCI
> > > selected?  Do you have any drivers which use this space without CONFIG_PCI=y?
> > 
> > We have not yet created support for the PCI bridge (or even verified
> > its existence on the chip). These numbers are derived bit by bit from
> > vendor-published kernel sources and rather intended as a reference in
> > case somebody wishes to try and implement PCI support.
> > 
> > All the peripherals on these chips that I know of are statically
> > mapped platform devices programmed via MMIO registers. There may be
> > some boards which have support for external PCI devices, but I haven't
> > seen them.
> 
> Actually, we did discuss this when I first reviewed your patches. FWIW,
> WM8505, WM8510 and VT8500 don't seem to have PCI, but VT8430 has PCI,
> according to the data sheet and the patch provided by VIA.
> 
> What made this a little hard to understand is the way that VIA provided
> an emulated PCI bus even on those platforms that didn't have the hardware,
> apparently because it didn't occur to them to add platform device
> support to the network driver (via velocity, iirc). They just faked a
> configuration space for the USB and network devices to make them work
> with the regular PCI drivers.

Ok, so it can use the default value I've created for IO_SPACE_LIMIT
based on whether CONFIG_PCI is enabled or not.

Thanks.

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

* Re: [PATCH 1/6 v8] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's
  2011-07-07  7:59                     ` Russell King - ARM Linux
@ 2011-07-07  8:05                       ` Arnd Bergmann
  0 siblings, 0 replies; 91+ messages in thread
From: Arnd Bergmann @ 2011-07-07  8:05 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: vt8500-wm8505-linux-kernel, Alexey Charkov, linux-arm-kernel,
	Eric Miao, Uwe Kleine-König, Albin Tonnerre, linux-kernel

On Thursday 07 July 2011 09:59:54 Russell King - ARM Linux wrote:
> > Actually, we did discuss this when I first reviewed your patches. FWIW,
> > WM8505, WM8510 and VT8500 don't seem to have PCI, but VT8430 has PCI,
> > according to the data sheet and the patch provided by VIA.
> > 
> > What made this a little hard to understand is the way that VIA provided
> > an emulated PCI bus even on those platforms that didn't have the hardware,
> > apparently because it didn't occur to them to add platform device
> > support to the network driver (via velocity, iirc). They just faked a
> > configuration space for the USB and network devices to make them work
> > with the regular PCI drivers.
> 
> Ok, so it can use the default value I've created for IO_SPACE_LIMIT
> based on whether CONFIG_PCI is enabled or not.
> 

Yes, correct.

	Arnd

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

end of thread, other threads:[~2011-07-07  8:06 UTC | newest]

Thread overview: 91+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-11-07 16:28 [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Alexey Charkov
2010-11-07 16:28 ` [PATCH 2/6 v2] serial: Add support for UART on VIA VT8500 and compatibles Alexey Charkov
2010-11-07 23:08   ` Alan Cox
2010-11-08  0:58     ` [PATCH 2/6 v3] " Alexey Charkov
2010-11-08 10:46       ` Alan Cox
2010-11-08 17:33         ` [PATCH 2/6 v4] " Alexey Charkov
2010-11-07 16:28 ` [PATCH 3/6 v2] input: Add support for VIA VT8500 and compatibles in i8042 Alexey Charkov
2010-11-12 22:54   ` Alexey Charkov
2010-11-12 23:30     ` Dmitry Torokhov
2010-11-13  0:00       ` Alexey Charkov
2010-12-22 21:41       ` [PATCH 3/6 v3] " Alexey Charkov
2010-11-07 16:28 ` [PATCH 4/6 v2] usb: Add support for VIA VT8500 and compatibles in EHCI HCD Alexey Charkov
2010-11-07 16:28 ` [PATCH 5/6 v2] rtc: Add support for the RTC in VIA VT8500 and compatibles Alexey Charkov
2010-11-12 22:53   ` Alexey Charkov
2010-11-13 12:14   ` Lars-Peter Clausen
2010-11-13 23:56     ` [PATCH 5/6 v3] " Alexey Charkov
2010-11-14 15:50       ` Lars-Peter Clausen
2010-11-14 17:00         ` [PATCH 5/6 v4] " Alexey Charkov
2010-11-23 19:17           ` Alexey Charkov
2010-11-24 19:23             ` Lars-Peter Clausen
2010-11-24 22:47               ` [PATCH 5/6 v5] " Alexey Charkov
2010-11-07 16:28 ` [PATCH 6/6 v2] ARM: Add support for the display controllers in VT8500 and WM8505 Alexey Charkov
2010-11-08  4:17   ` Paul Mundt
2010-11-08 12:56     ` Alexey Charkov
2010-11-08 14:14     ` [PATCH 6/6 v3] " Alexey Charkov
2010-11-08 20:43       ` Paul Mundt
2010-11-08 21:15         ` Alexey Charkov
2010-11-08 21:30           ` Paul Mundt
2010-11-08 23:42             ` [PATCH 6/6 v4] " Alexey Charkov
2010-11-08 23:54               ` Paul Mundt
2010-11-09  0:03                 ` Alexey Charkov
2010-11-09  7:36                   ` Guennadi Liakhovetski
2010-11-09  9:39                   ` Paul Mundt
2010-11-09  9:49                     ` Alexey Charkov
2010-11-09 10:33         ` [PATCH 6/6 v3] " Russell King - ARM Linux
2010-11-09 10:51           ` Paul Mundt
2010-11-09 11:04             ` Russell King - ARM Linux
2010-11-09 13:02               ` Geert Uytterhoeven
2010-11-09 13:33                 ` Arnd Bergmann
2010-11-09 16:20                   ` Paul Mundt
2010-11-08  8:47   ` [PATCH 6/6 v2] " Arnd Bergmann
2010-11-09 10:23     ` Alexey Charkov
2010-11-09 15:03       ` Arnd Bergmann
2010-11-07 16:57 ` [PATCH 1/6 v2] ARM: Add basic architecture support for VIA/WonderMedia 85xx SoC's Russell King - ARM Linux
2010-11-07 17:08   ` Alexey Charkov
2010-11-07 17:17     ` Russell King - ARM Linux
2010-11-07 18:25       ` [PATCH 1/6 v3] " Alexey Charkov
2010-11-08 17:19       ` [PATCH 1/6 v4] " Alexey Charkov
2010-11-10 15:16         ` saeed bishara
2010-11-10 15:18           ` Russell King - ARM Linux
2010-11-10 15:20             ` saeed bishara
2010-11-11 21:23         ` [PATCH 1/6 v5] " Alexey Charkov
2010-11-11 23:49           ` Russell King - ARM Linux
2010-11-12 16:54             ` [PATCH 1/6 v6] " Alexey Charkov
2010-11-12 20:14               ` Alexey Charkov
2010-11-23 19:50             ` [PATCH 1/6 v7] " Alexey Charkov
2010-12-19 17:40             ` [PATCH 1/6 v8] " Alexey Charkov
2010-12-20 18:15               ` Arnd Bergmann
2010-12-20 19:15               ` Russell King - ARM Linux
2010-12-20 19:26                 ` Alexey Charkov
2010-12-20 19:54                 ` [PATCH 1/6 v9] " Alexey Charkov
2010-12-20 20:50                   ` Ryan Mallon
2010-12-20 21:48                     ` Alexey Charkov
2010-12-20 22:23                       ` Ryan Mallon
2010-12-20 23:00                         ` Alexey Charkov
2010-12-20 23:22                           ` Ryan Mallon
2010-12-20 23:49                             ` Alexey Charkov
2010-12-21  0:09                               ` Alexey Charkov
2010-12-21  2:32                               ` Ryan Mallon
2010-12-21 10:00                                 ` Alexey Charkov
2010-12-21 12:05                                   ` Arnd Bergmann
2010-12-21 19:39                                   ` Ryan Mallon
2010-12-22 21:18                                     ` [PATCH 1/6 v10] " Alexey Charkov
2010-12-22 21:52                                       ` Ryan Mallon
2010-12-22 22:02                                         ` Alexey Charkov
2010-12-22 22:32                                           ` Ryan Mallon
2010-12-22 22:25                                         ` [PATCH 1/6 v11] " Alexey Charkov
2010-12-22 22:59                                           ` Ryan Mallon
2010-12-22 23:38                                             ` [PATCH 1/6 v12] " Alexey Charkov
2010-12-28 14:52                                               ` Alexey Charkov
2011-01-15  0:53                                                 ` Alexey Charkov
2011-01-15  0:58                                                   ` Russell King - ARM Linux
2011-01-15  1:13                                                     ` Alexey Charkov
2011-01-21 10:43                                                       ` Russell King - ARM Linux
2011-07-06 12:34               ` [PATCH 1/6 v8] " Russell King - ARM Linux
2011-07-07  7:13                 ` Alexey Charkov
2011-07-07  7:54                   ` Arnd Bergmann
2011-07-07  7:59                     ` Russell King - ARM Linux
2011-07-07  8:05                       ` Arnd Bergmann
2010-11-07 17:00 ` [PATCH 1/6 v2] " Russell King - ARM Linux
2010-11-07 17:16   ` Alexey Charkov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).